Commit Graph

284 Commits

Author SHA1 Message Date
chiguyong 3fdd29d152 build(bitable): rebuild frontend index.html for JS hash alignment
Test / backend-test (pull_request) Has been cancelled Details
Test / frontend-unit (pull_request) Has been cancelled Details
Test / api-e2e (pull_request) Has been cancelled Details
Test / frontend-e2e (pull_request) Has been cancelled Details
Rebuild index.html after U1-U6 frontend changes. JS bundle hash updated
(index-CHtvprqX.js -> index-agwA6wam.js) to match new build output.
CI runs unit/e2e only and does not rebuild static assets, so the committed
hash must match the bundled JS.
2026-07-04 00:39:24 +08:00
chiguyong 137bda0361 refactor(bitable): simplify code after ce-simplify-code pass
Behavior-preserving simplifications (net -22 lines):
- useResponsiveBreakpoint: remove createHandler factory, share single sync fn
- RecordDetailDrawer: remove isEditable wrapper, call isFieldEditable directly
- ViewConfigPanel: merge duplicate saveGrouping/saveConditionalFormat into saveU5Config
- groupingRulesUtils: use Array.find instead of for-loop, simplify Number.isFinite check
- GroupingEditor: simplify filter callback to single-expression arrow

Verified: typecheck + build:frontend + ruff all pass.

Refs: ce-simplify-code (LFG Step 3)
2026-07-04 00:28:28 +08:00
chiguyong 229dc0b2f3 feat(bitable): U6 R15a BitableTool 4 new actions + DELETE /views endpoint
Extend BitableTool from 6 to 10 actions (create_view, update_view,
update_field, delete_view) and add the DELETE /views/{view_id} backend
endpoint with 404-before-403 ownership, 409 last-view protection, and
X-Internal-Token passthrough (KTD11).

Backend:
- repository.py: add delete_view() — DELETE row by view_id, returns rowcount > 0
- service.py: add LastViewDeletionError domain exception + delete_view()
  with last-view guard (siblings <= 1 → raise → route maps to 409)
- routes/bitable.py: add DELETE /views/{view_id} (204 No Content),
  404-before-403 ownership pattern, 409 on LastViewDeletionError,
  X-Internal-Token passthrough via require_bitable_auth
- tools/bitable_tool.py: add 4 new actions (_create_view, _update_view,
  _update_field, _delete_view), register in BOTH handlers dict AND
  input_schema.action.enum (KTD10 — 10 actions each)

Frontend:
- api/bitable.ts: add deleteView(viewId): Promise<void>
- stores/bitable.ts: add deleteView action — removes from local state,
  switches to first remaining view if active was deleted, 409 warning
- ViewSwitcher.vue: add delete button (a-popconfirm "确认删除此视图?"),
  hidden when views.length <= 1 (preempt last-view 409)
- BitableFileDetailView.vue: handle @delete event from ViewSwitcher

Tests:
- test_routes.py: 6 new DELETE /views tests (204, 404 missing, 404
  non-owner, 409 last-view, internal-token passthrough, internal-token 404)
- test_bitable_tool.py: 13 new tests (action count = 10, handlers = 10,
  4 action happy paths, missing-field errors, 409 last-view, R3/R4
  config parity, X-Internal-Token passthrough on all 4 new actions)
- e2e/bitable-agent-parity.spec.ts: 10 scenarios (P1-P10) covering
  delete button visibility, popconfirm, 204/409/404 flows, tab removal,
  view switch after delete, create view adds tab

Verification:
- ruff check: all files pass
- pytest: 62 passed, 12 pre-existing failures (unchanged from e931fbe baseline)
- typecheck: pass (EXIT_CODE=0)
- build:frontend: pass (BUILD_EXIT=0)
- action count: ENUM=10, HANDLERS=10, delete_view in both
- no blue hex colors in ViewSwitcher.vue

Pre-existing test failures (12, unchanged from e931fbe):
test_create_table_success, test_create_field_success, test_list_fields,
test_create_records_batch, test_upsert_inserts_then_updates,
test_upsert_preserves_user_columns, test_create_view_success,
test_batch_upsert_1200_records, test_resume_from_partial_failure,
test_query_records, test_query_records_with_limit, test_collect_api

Constraints honored:
- No emojis, no `any` type, no blue hex colors, no pyproject.toml changes
- 404-before-403 for non-owned resources (Pattern 4)
- X-Internal-Token transparent passthrough (KTD11)
- KTD10: actions registered in both handlers dict AND enum
2026-07-03 23:13:46 +08:00
chiguyong e931fbef2d feat(bitable): U5 R4 grouping (max 3 fields) + conditional formatting (7 operators)
- GroupingEditor: multi-select field picker (max 3), per-level direction
  toggle, reorder buttons, "已知限制:不支持跨分组多选" note, empty state
- ConditionalFormatEditor: per-rule enable/field/operator/value/color/bold,
  8 color keys, WCAG 1.4.1 bold default true, first-match-wins footer legend
- BitableGrid: unified section rendering (grouped/ungrouped via single
  vxe-grid declaration), group headers as separate divs (CF only on data
  cells), CF via row-config.className, multi-grid instance map for refresh
- groupingRulesUtils: pure functions for CF matching (7 operators), group
  tree builder, SUM/AVG aggregation, CSS var mappers, self-check on load
- view_config.py: Pydantic v2 validation (MAX_GROUP_BY_FIELDS=3, 7
  operators, 8 color keys, extra="forbid" on sub-models)
- routes/bitable.py: validate_view_config on PATCH (HTTP 422 on error)
- stores/bitable.ts: updateViewConfig action (merges U5 sub-keys, preserves
  filters/sort/hidden_fields)
- ViewConfigPanel: grouping + conditional-format tabs
- E2E: 8 scenarios (G1-G8: single/multi grouping, collapse/expand, CF
  equals/between, combined, aggregation)
- Tests: 54 unit tests (19 grouping + 35 CF), 2 PG-marked skipped
2026-07-03 22:33:18 +08:00
chiguyong f280627da1 feat(bitable): U4 view type switcher with 5 types (grid enabled, others disabled)
- Add viewSwitcherUtils.ts (5 view types metadata: label/icon/disabled/tooltip)
- Refactor ViewSwitcher: button -> dropdown with 5 types, disabled items show "规划中" tooltip
- Update BitableFileDetailView.handleCreateView to accept viewType parameter (no more hardcoded grid)
- Bind :creating=viewCreating to ViewSwitcher for loading/disabled state during POST
- Extend store createView + API createView to pass view_type field (already in prior commits)
- Add loading/disabled state on create button to prevent duplicate clicks
- Extend e2e/bitable-view.spec.ts with 5 view type scenarios (E1-E5)

Closes R3 (P0): view type selection in UI, backend already supports view_type.

Refs: docs/plans/2026-07-03-001-feat-bitable-p0-ux-and-agent-parity-plan.md U4
2026-07-03 21:43:51 +08:00
chiguyong 5baaeb489d feat(bitable): U3 record detail drawer with full field type rendering
- Add RecordDetailDrawer.vue (480px/640px drawer, sticky header, full field type render)
- Add recordDrawerUtils.ts (value formatter, attachment/image extractors, drawer width calc)
- Add currentRecord state + openRecordDetail/closeRecordDetail/fetchRecordDetail actions to store
- Wire BitableGrid row click to open drawer
- Add e2e/bitable-record-drawer.spec.ts with 7 scenarios
- Loading/Error/404/empty states use U1 LoadingState/ErrorState per Open Question
- useResponsiveBreakpoint consumed: isMobile -> 100vw full-screen overlay
- user-owned fields editable, agent-owned fields read-only, upsert preserves agent columns

Closes R2 (P0): grid row click -> detail drawer with all field types visualized.

Refs: docs/plans/2026-07-03-001-feat-bitable-p0-ux-and-agent-parity-plan.md U3
2026-07-03 15:57:33 +08:00
chiguyong f0c993a0d9 feat(bitable): U2 inline field configuration in column header menu
- Add InlineFieldConfigurator.vue (inline panel reusing FieldConfigForm logic)
- Add fieldRenderUtils.ts (type conversion compatibility check)
- Refactor ColumnHeaderMenu: edit -> inline expand, batch -> open FieldManagePanel
- Integrate InlineFieldConfigurator in BitableGrid header slot
- Add batch-management banner to FieldManagePanel
- Add submitting loading state to prevent duplicate clicks
- Extend e2e/bitable-field-ops.spec.ts with inline edit scenarios

Closes R1 (P0): column header menu inline edit, no more drawer jump.

Refs: docs/plans/2026-07-03-001-feat-bitable-p0-ux-and-agent-parity-plan.md U2
2026-07-03 15:12:17 +08:00
chiguyong e1cf073693 feat(bitable): U1 add design token system + vxe-table dependency declaration
- Add bitable-tokens.css with 4 token categories (color/spacing/radius/font/drawer-width)
- Add FieldTypeIcon.vue mapping 9 field types to Ant Design Outlined icons
- Add useResponsiveBreakpoint composable (768/1024/1440 breakpoints)
- Add LoadingState (skeleton) and ErrorState (inline alert + retry) components
- Token化 9 bitable components/views (replace hardcoded hex with var())
- Declare vxe-table dependency explicitly (resolve ghost dependency)
- Upgrade SelectDisplay chip palette to 8-color token with WCAG AA contrast

Phase 1 foundation for Phase 2 UX work (U2-U5).

Refs: docs/plans/2026-07-03-001-feat-bitable-p0-ux-and-agent-parity-plan.md U1
2026-07-03 14:40:57 +08:00
Fischer 00b2dad36e feat(compressor): CJK-aware token estimation + linear compress flow (#21)
Deploy to Production / deploy (push) Waiting to run Details
Test / backend-test (push) Waiting to run Details
Test / frontend-unit (push) Waiting to run Details
Test / api-e2e (push) Waiting to run Details
Test / frontend-e2e (push) Waiting to run Details
Squash merge PR #21: CJK-aware token estimation + linear compress flow + solution doc
2026-07-03 09:40:28 +08:00
Fischer 2296d0b209 refactor: remove all emoji from source code (#20)
Deploy to Production / deploy (push) Waiting to run Details
Test / backend-test (push) Waiting to run Details
Test / frontend-unit (push) Waiting to run Details
Test / api-e2e (push) Waiting to run Details
Test / frontend-e2e (push) Waiting to run Details
Replace emoji/glyph characters with Ant Design Vue Outlined icons (frontend), text labels with ANSI colors (CLI/shell), and ASCII art (docstrings). Add pre-commit guard (scripts/check-no-emoji.sh) and style guide to prevent regression.

Closes: docs/plans/2026-07-02-001-refactor-remove-all-emoji-plan.md
2026-07-03 02:46:40 +08:00
chiguyong cc6634b2ab feat(ui): private board restrictions + scheme B assistant/user bubbles
Test / backend-test (pull_request) Has been cancelled Details
Test / frontend-unit (pull_request) Has been cancelled Details
Test / api-e2e (pull_request) Has been cancelled Details
Test / frontend-e2e (pull_request) Has been cancelled Details
U1: ChatInput @board button blocks existing-conversation board creation
    with modal — enforces "one board per conversation" constraint.
U2: BoardBannerCard simplified to plain title + round meta
    (no icons/bars/progress/expert chips).
U3: MessageShell assistant bubble (方案B neutral grayscale) with
    F4-A card-type exclusion + G1 empty-bubble hide.
U4: UserBubble dark text bubble for plain text
    (command card/file keep light bg).

Code review fixes (ce-code-review step 5):
- P1: UserBubble focus-visible --accent-primary → --color-primary
  (dark mode visibility fix).
- P2: CARD_BEARING_TYPES adds 'error' (ErrorCard double-bubble regression).
- P2: Remove dead expertColor prop (scheme B leftover).
- P0/P1: Extract bubbleUtils.ts pure functions + add 42 tests
  covering G1/F4-A/U4/U2 key decisions.

Tests: 180/181 pass (1 pre-existing tauri-auth failure unrelated).
Typecheck: clean.
2026-07-03 01:47:37 +08:00
chiguyong 23160be055 fix(types): resolve 3 pre-existing typecheck errors in transient-state test
Test / backend-test (pull_request) Has been cancelled Details
Test / frontend-unit (pull_request) Has been cancelled Details
Test / api-e2e (pull_request) Has been cancelled Details
Test / frontend-e2e (pull_request) Has been cancelled Details
message_type: 'board_started' as const (line 93) fixes TS2322 on lines
107 and 122 — TypeScript was inferring message_type as string instead
of the literal 'board_started'.

boardState local variable: replace 'as never' with proper shape +
'status: discussing' as const (line 159-160) fixes TS2339 on line 168
where .topic was accessed on type 'never'.

All 5 transient-state tests still pass. vue-tsc --noEmit now clean.
2026-07-02 22:13:28 +08:00
chiguyong 44f4f1c46f fix: add null check for chatStore.conversations in StickyModeHeader
Optional chaining prevents TypeError when test mocks don't provide conversations array.
2026-07-02 21:48:41 +08:00
chiguyong 8188e8861d feat(ui): scheme B neutral grayscale for board messages + assistant bubbles
expertIdentity.ts PALETTE -> neutral grayscale; useMessageRenderer.ts removes assistant fallback for board_* events; BoardRoundCard/MessageShell apply GitHub-style gray; chatStream.ts prefers event-provided moderator avatar/color; StickyModeHeader/Scene4/LoginView/types aligned.
2026-07-02 21:26:22 +08:00
chiguyong 32746652aa fix(board): persist moderator avatar/color in round_summary events
board_orchestrator.py: include moderator_avatar and moderator_color in
the round_summary event payload so downstream consumers have the
moderator's identity metadata.

chat.py: persist expert_avatar and expert_color from the event data into
the board_summary message metadata, ensuring avatar/color survive page
reload instead of falling back to defaults.
2026-07-02 21:24:13 +08:00
chiguyong 484b7ddb95 fix(dev): isolate dev environment ports and fix env loading
- docker-compose.yaml: production mode uses expose (container-only) for
  Redis/PostgreSQL instead of ports (host-mapped)
- docker-compose.dev.yml: dev override maps Redis 6381 and PostgreSQL 5435
  to avoid conflicts with other projects (pms-redis 6379, geo_redis 6380,
  geo_db 5433)
- config.py: fix empty env var handling — only skip .env override when
  os.environ[key] is non-empty; load .env, .env.dev, .env.local in sequence
- scripts/dev-start.sh: manage agentkit-specific Docker containers
- .gitignore: add .env.dev and .env.local (contain API keys)
2026-07-02 21:23:50 +08:00
chiguyong 7376005868 fix: 修复 transient state 重置口径 + ReAct 工具调用规则
Test / backend-test (pull_request) Has been cancelled Details
Test / frontend-unit (pull_request) Has been cancelled Details
Test / api-e2e (pull_request) Has been cancelled Details
Test / frontend-e2e (pull_request) Has been cancelled Details
Bug 1: chatStore 三个 action 重置 boardState/debateState/collaborationState
- createConversation: 新增三态重置(原缺失,旧私董会状态泄漏到新会话)
- selectConversation: 统一为条件重置(prevConvId !== id),避免 force-reload 误清空
- deleteConversation: 补全 collaborationState 重置
- 附带:selectConversation 中 board_speech/board_summary 消息缺失
  expert_avatar/expert_color 时从 boardState.experts 兜底补全

Bug 2: ReAct _build_tool_use_prompt L0 规则调整
- 新增规则 1:涉及外部信息/实时数据/多步骤分析/不确定事实时必须使用工具
- 原规则 3 降为规则 4,收窄为仅在确实无需工具时可直接回答
- base_prompt 与工具描述不动(L1/L2 拆为独立 plan)

测试:5 前端 transient-state reset matrix + 6 后端 prompt rules 断言

Plan: docs/plans/2026-07-02-002-fix-transient-state-reset-and-react-tool-guidance-plan.md
2026-07-02 20:51:57 +08:00
chiguyong 78a7faa17b refactor: remove all emoji from agentkit
Deploy to Production / deploy (push) Waiting to run Details
Test / backend-test (push) Waiting to run Details
Test / frontend-unit (push) Waiting to run Details
Test / api-e2e (push) Waiting to run Details
Test / frontend-e2e (push) Waiting to run Details
Test / backend-test (pull_request) Has been cancelled Details
Test / frontend-unit (pull_request) Has been cancelled Details
Test / api-e2e (pull_request) Has been cancelled Details
Test / frontend-e2e (pull_request) Has been cancelled Details
Replace emoji across codebase: YAML avatars -> first char, frontend banners -> Ant Design Vue components, CLI status -> OK/FAIL/WARN labels, terminal -> [WARN]/[OK]/[PENDING], Bitable DB default -> table, App.vue font cleanup, test fixtures -> first char letters. shell.avatar type upgraded to string | Component.
2026-07-02 01:33:28 +08:00
chiguyong 36b0296730 fix: 私董会数据持久化修复 + emoji 移除计划
- 修复 board_started/expert_speech/round_summary/board_concluded 事件持久化
- 添加 is_board 标记到会话列表和详情接口
- 实现 restoreBoardStateFromMessages 从持久化消息恢复 boardState
- 添加 ChatSidebar 私董会徽章
- 添加 emoji 移除计划文档 (docs/plans/2026-07-02-001)
2026-07-02 01:07:12 +08:00
chiguyong 47a437c5e3 fix(experts): resolve residual review findings from PR #13
Test / backend-test (pull_request) Has been cancelled Details
Test / frontend-unit (pull_request) Has been cancelled Details
Test / api-e2e (pull_request) Has been cancelled Details
Test / frontend-e2e (pull_request) Has been cancelled Details
Addresses 4 actionable findings (1 P1 + 3 P2) from ce-code-review of
feat/ui-ue-enhancement (PR #13), now merged to main (8066e0b).

P1 — expert_step payload alignment (_phase_executor.py)
  The thinking/tool_call/tool_result event payloads were missing the
  fields the frontend WsServerMessage contract requires
  (expert_name/expert_color/content/step). Frontend code consuming these
  events silently degraded. Now all expert_step broadcasts carry the
  full contract; tool_call/tool_result keep step_data for the raw payload.

P2 #1 — execute_stream CancellationToken registration (config_driven.py)
  execute_stream() bypassed BaseAgent.execute() and never registered a
  CancellationToken, so cancel_task() could not cooperatively cancel a
  streaming task. Now registers the token and cleans it up in finally.

P2 #2 — team_synthesis orphan milestone cleanup (orchestrator.py)
  If synthesis streaming was interrupted (cancel/exception), no terminal
  team_synthesis event was emitted, leaving the frontend streaming
  milestone spinning forever. Now an inner try/except emits a terminal
  team_synthesis with status=cancelled|error before re-raising, so the
  frontend can finalize the milestone. The success path also carries
  the synthesis_id.

P2 #3 — synthesis_id dedup (orchestrator.py + types.ts + chatStream.ts)
  Without an identifier, the frontend could not precisely match a
  team_synthesis terminal event to its streaming milestone (especially
  across retries/concurrent teams). The backend now injects a stable
  synthesis_id (`{plan.id}:synthesis`) into both team_synthesis_chunk
  and team_synthesis events; the frontend uses it for exact milestone
  matching and treats error/cancelled status as terminal.

Test updates
  - Updated test_thinking_events_forwarded_as_expert_step to assert the
    new payload contract (expert_id/name/color/content/step).
  - Added test_tool_call_events_forwarded_as_expert_step covering
    tool_call/tool_result payload shape (content=tool_name摘要 +
    step_data=原始 payload).

Verification
  - ruff check: clean
  - pytest tests/unit/experts/test_phase_executor_streaming.py: 14/14
  - npm run typecheck: clean
  - vitest: 126/127 (1 unrelated baseline failure in tauri-auth.test.ts)

Residuals doc: docs/residual-review-findings/feat-ui-ue-enhancement.md
2026-07-01 13:26:19 +08:00
chiguyong f872a3fac6 feat: UI/UE enhancement — streaming, sticky header, hover actions, calendar tokens
Test / backend-test (pull_request) Has been cancelled Details
Test / frontend-unit (pull_request) Has been cancelled Details
Test / api-e2e (pull_request) Has been cancelled Details
Test / frontend-e2e (pull_request) Has been cancelled Details
U1 ThinkingBlock: streaming cursor + auto-collapse to summary bar
U2 StickyModeHeader: new component replacing ExpertTeamView + BoardStatusView
U3 Backend _phase_executor: execute_stream() with token/thinking/final_answer forwarding
U4 Frontend chatStream: expert_result_chunk/team_synthesis_chunk token accumulation
U5 AssistantText: routing tag hover fade-in
U6 UserBubble: hover actions (copy/delete/refill)
U7 CalendarGrid: token-based color redesign

Review fixes (ce-code-review):
- P0: _VALID_TEAM_EVENT_TYPES whitelist adds 3 new streaming event types
- P0: final_answer no longer double-accumulates token content
- P2: exception handling expanded to except Exception for LLMProviderError etc.

Simplification (ce-simplify-code):
- _synthesizer.py: O(n²) concat -> list+join, _concat_results extraction
- config_driven.py: 4 duplicate _handle_*_stream -> _wrap_sync_as_stream
- chatStream.ts: 5x [...messages].reverse().find() -> findLastMessage helper

Tests: pytest 13/13, vitest 126/127 (1 baseline), typecheck pass, ruff clean
2026-07-01 12:51:45 +08:00
Fischer c005642851 refactor: tech debt Wave 3+4 (tools/skills/mcp/rag/calendar/auth/cli/quality/channels/telemetry/session/bus/documents Any 治理) (#11)
Deploy to Production / deploy (push) Waiting to run Details
Test / backend-test (push) Waiting to run Details
Test / frontend-unit (push) Waiting to run Details
Test / api-e2e (push) Waiting to run Details
Test / frontend-e2e (push) Waiting to run Details
2026-07-01 08:08:36 +08:00
Fischer a778f816c5 refactor: tech debt Wave 1+2 (except Exception 收尾 + core/experts Any 治理) (#10)
Deploy to Production / deploy (push) Waiting to run Details
Test / backend-test (push) Waiting to run Details
Test / frontend-unit (push) Waiting to run Details
Test / api-e2e (push) Waiting to run Details
Test / frontend-e2e (push) Waiting to run Details
2026-07-01 03:54:53 +08:00
Fischer 838a05772e refactor: follow-up tech debt cleanup (except Exception + Any 治理) (#9)
Deploy to Production / deploy (push) Waiting to run Details
Test / backend-test (push) Waiting to run Details
Test / frontend-unit (push) Waiting to run Details
Test / api-e2e (push) Waiting to run Details
Test / frontend-e2e (push) Waiting to run Details
2026-07-01 03:03:02 +08:00
chiguyong ec9a0a1f70 refactor(frontend): split chat.ts (2025 lines) into chatStore/chatSocket/chatStream (U5)
Test / backend-test (pull_request) Has been cancelled Details
Test / frontend-unit (pull_request) Has been cancelled Details
Test / api-e2e (pull_request) Has been cancelled Details
Test / frontend-e2e (pull_request) Has been cancelled Details
chatStore.ts (498 lines, <=500 target met): Pinia store entry composing useChatSocket + useChatStream; retains all actions + backward-compat export aliases.

chatSocket.ts (165 lines): resolveIncomingConvId pure fn + useChatSocket composable (connect/disconnect/heartbeat/reconnect).

chatStream.ts (1557 lines): dispatchWsEvent pure fn for 30+ WS event types + useChatStream composable. Exceeds plan ~300 estimate due to discriminated union breadth (each case 30-50 lines); core testability goal met.

8 components + chat-phase.test.ts migrated from @/stores/chat to @/stores/chatStore.

vitest: 35 new tests (chatStream 19 + chatSocket 13 + chat-phase 3) all green; typecheck passes.
2026-06-30 22:32:48 +08:00
chiguyong 1033346913 refactor(bitable,tools): replace Any with concrete types + Protocol (U4)
BitableRecord/FormulaResult/SessionState TypeAlias replace dict[str, Any]; _redis/_engine/_session_factory typed as object | None with TYPE_CHECKING Protocol (_RedisLike, _RecalcWorker); Coroutine[Any, Any, Any] retained as legitimate type param.

Baseline 40 : Any occurrences -> 0 across 6 in-scope files (target <=5). Deferred: repository.py/recalc_worker.py/ingestion/* (10 occurrences, separate PR).

ruff clean; 367 passed + 116 skipped (bitable + pipeline_state + tools).
2026-06-30 22:32:30 +08:00
chiguyong be5c4e09f8 refactor(core,experts): classify except Exception + structured ReviewResult (U3)
ReviewResult dataclass (passed/degraded/feedback) replaces tuple+[DEGRADED] prefix in _review_phase_output; 3 review_result WS payloads now carry degraded field (AE3).

except Exception narrowed to specific types across 10 files (core/react, rewoo, base, orchestrator, dispatcher, plan_exec_engine + experts/orchestrator, _phase_executor, _review_gate + orchestrator/pipeline_engine). Baseline 140 -> 66 occurrences (>=50% reduction).

Fix RuntimeError regression: review-gate + compression paths now catch RuntimeError (LLM/provider internal errors) to preserve degradation semantics. Test side_effect switched to functional form to avoid StopIteration on list exhaustion.

ruff clean; 135 key + 469 experts + 163 core tests pass.
2026-06-30 18:03:58 +08:00
chiguyong 47ee2449df refactor(experts): split TeamOrchestrator god class into 7 mixins (U2)
- Split 2085-line orchestrator.py into main class (592 lines) + 7 responsibility-focused mixins: PhaseExecutor, DebateRunner, ReviewGate, DivergenceDetector, RollbackHandler, Synthesizer, InterventionHandler.

- Mixin pattern preserves self access to shared state (_experts/_workspace/_broadcast_event); method bodies moved verbatim to minimize regression risk. Each mixin declares TYPE_CHECKING Protocol for shared state.

- Split _execute_execution_phase (~290 lines) into _prepare_phase_context/_run_agent_steps/_finalize_phase (each <=100 lines).

- All mixins <=400 lines, main class <=600 lines. [DEGRADED] prefix annotations preserved in ReviewGateMixin.

- 60 team_orchestrator tests pass (behavior unchanged), 469 experts tests pass, ruff clean.
2026-06-30 16:47:20 +08:00
chiguyong e61f98898f refactor(core): unify ReActEngine execute/execute_stream via async generator (U1)
- Convert _execute_loop to async generator yielding ReActEvent; both execute and execute_stream delegate to it, eliminating ~760 lines of duplicated loop logic (execute_stream 813 -> 53 lines).

- Add 'final_result' event_type carrying ReActResult; execute extracts result from final event, execute_stream forwards events (backward-compatible 'final_answer' retained).

- Unify _drain_phase_violations across both paths.

- Add 14 golden-trajectory characterization tests.

- Fix test_execute_stream_with_compressor mock gateway (chat_stream test-infra gap). 130 react tests pass, 762 core+experts pass, no regressions.
2026-06-30 16:07:00 +08:00
chiguyong a3cecd4b50 fix(review): apply P0/P2 findings from dual-agent review
- Dockerfile: split ENTRYPOINT/CMD to align with docker-compose serve
- test_termbase: guard jieba import with pytest.importorskip
- orchestrator: mark silent review-degradation with [DEGRADED] prefix
- chat.py: accurate ExecutionMode log message
- agentkit.yaml: document OTel exporter config
- skill_routing: replace 12 Any with object/typed (AGENTS.md compliance)
- AssistantText.vue: add aria-live/role for a11y
2026-06-30 14:27:46 +08:00
chiguyong 8627777f87 fix(review): apply ce-code-review findings
Test / backend-test (pull_request) Has been cancelled Details
Test / frontend-unit (pull_request) Has been cancelled Details
Test / api-e2e (pull_request) Has been cancelled Details
Test / frontend-e2e (pull_request) Has been cancelled Details
Six safe fixes from Stage 5c review:

phase.py: delete dead _DEFAULT_BASH_FILTER constant (no references after U1)
chat.py: drop Any from _build_phase_engine params (AGENTS.md prohibits any)
chat.ts: delete stale comment about phase_changed emission
chat-phase.test.ts: rename misleading 'capped at 5' test name
test_chat_plan_exec_ws.py: tighten test_rest_react_mode_still_works assertion
test_plan_exec_e2e.py: clarify test_auto_advance assertion comment

Known limitations documented in PR description (not fixed): loop detector + advance_phase (P1), parallel path phase_violation ordering (P2), REST cancellation_token (P2), Callable filter exceptions (P3).
2026-06-30 12:42:15 +08:00
chiguyong cbbe937940 chore(shell): fix ruff F401/F841 + apply ruff format
Pre-existing ruff errors surfaced during Wave 4 QC:
- F401: drop unused `TerminalSession` import (only `TerminalSessionManager` is used)
- F841: drop unused `start = time.monotonic()` local in `_execute_standalone`

`ruff format` then reformatted a few long lines in the same file
(frozenset literal, curl exfiltration regex, pipe operators, session.env
call). No behavior change — formatting only.

Why now: shell.py was already touched by U1 (widen
`bash_command_filter`). Leaving known ruff failures in a file this PR
modifies would make future CI gates noisy.
2026-06-30 11:52:51 +08:00
chiguyong 2abe7c9e49 feat(U4): frontend phase_violation handling + PhaseIndicator component
Extend the frontend to surface PLAN_EXEC phase lifecycle events to the
user:

- WsServerMessage union (types.ts) gains two branches:
  `phase_changed` and `phase_violation` (matching backend U2 emission).
- chat.ts Pinia store gains a phase state slice:
  `currentPhase`, `phaseViolations` (capped at 5), `isPlanExec`
  computed, and `resetPlanExecState()`.
- handleWsMessage adds `case "phase_changed"` (sets currentPhase +
  appends a milestone step) and `case "phase_violation"` (sets
  currentPhase from violation data, appends to violations, fires an
  ant-design-vue message.warning toast, appends an error step).
- `result` handler calls `resetPlanExecState()` to clear the
  indicator when the conversation completes.
- New `PhaseIndicator.vue` component: compact badge + 4 dots
  (PLANNING/BUILDING/VERIFICATION/DELIVERY) with the current phase
  highlighted + violation counter. Renders nothing when
  `!isPlanExec` (graceful degradation).
- Mounted in `ChatView.vue` alongside ExpertTeamView and
  BoardStatusView.

Tests:
- New `tests/unit/stores/chat-phase.test.ts` verifies the phase state
  slice is exposed with correct initial values and `isPlanExec`
  derives from `currentPhase`.
- `npm run typecheck` clean.
- Pre-existing `tauri-auth.test.ts` failure is unrelated (fails in
  isolation on main).
2026-06-30 11:11:03 +08:00
chiguyong b032e08866 feat(U3): extract _build_phase_engine helper + wire REST PLAN_EXEC
Extract the WS path's inline phase_policy construction into a shared
_build_phase_engine helper so the REST send_message endpoint can reuse
it. Replace the former 501 stub with actual PLAN_EXEC execution:

- REST POST /chat/sessions/{id}/messages with execution_mode=plan_exec
  now builds a phase-policy-backed ReActEngine, calls execute()
  (non-streaming), and returns a MessageResponse.
- KTD5: PLAN_EXEC bypasses execute_with_fallback_chain — phase policy
  and fallback chain are mutually exclusive.
- When plan_exec.enabled=False, REST falls through to the REACT path
  (matching WS behavior).
- WS path refactored to call the same helper; behavior unchanged.

Tests:
- Replace TestRestPlanExec501 with TestRestPlanExec (happy path, bad
  config → 500, disabled → falls through to REACT, REACT mode unchanged).
- Add TestBuildPhaseEngineHelper covering all return branches:
  not-PLAN_EXEC, disabled, empty-config, invalid-config, tool append,
  default-policy fallback.
- All 109 tests pass across the three PLAN_EXEC test files.
2026-06-30 10:59:43 +08:00
chiguyong 4dc58c24bc feat(U2): emit phase_violation WS event alongside LLM reinjection
Wave 3 only injected the violation error dict back to the LLM as a tool
result. Wave 4 U2 adds a parallel WS event so the frontend PhaseIndicator
can surface violations to the user.

- ReActEngine: add _phase_violations accumulator (list[dict]). Cleared in
  reset(). _check_phase_permission appends a structured violation dict
  (with new violation_kind field: tool_not_allowed | bash_command_blocked)
  before returning the error.
- Add _drain_phase_violations(step) helper that pops pending violations
  and returns ReActEvent(event_type="phase_violation", ...) list. Events
  carry a shallow copy of the violation dict so callers can't mutate the
  accumulator.
- execute_stream: drain after each tool_result yield at all 3 tool
  execution sites (parallel, serial-with-confirmation, parsed_calls).
  Non-streaming execute() ignores the accumulator (the LLM reinjection
  via the error dict is the only signal there).
- chat.py WS handler: new elif branch forwards phase_violation ReActEvents
  to the client as {"type": "phase_violation", "data": ...} WS messages.
- Tests: 11 new tests covering accumulator lifecycle, drain semantics,
  shallow-copy isolation, and execute_stream event emission for both
  tool_block and bash_block paths. 2 new WS forwarding tests pin the
  chat.py path (forward + characterization for REACT mode).
2026-06-30 10:48:35 +08:00
chiguyong 9e28ab315e feat(U1): widen PhasePolicy bash_command_filter to accept Callable
Reuses ShellTool._is_dangerous as the default bash filter for PLANNING
and VERIFICATION phases, closing the regex ceiling documented in Wave 3.

- Convert ShellTool._is_dangerous and _is_single_command_dangerous to
  @staticmethod (backward-compatible; instance calls still work via
  Python's descriptor protocol).
- Widen PhasePolicy.bash_command_filter field type to
  dict[PhaseState, Callable[[str], bool] | re.Pattern | None].
- is_bash_command_allowed dispatches on callable vs pattern at call time.
  Empty commands short-circuit to allowed (Wave 3 contract; ShellTool
  emits the clearer empty-command error).
- to_dict serializes callables as <callable> for log readability.
- default_policy() now wires ShellTool._is_dangerous for PLANNING and
  VERIFICATION. _DEFAULT_BASH_FILTER kept for backward compat with
  configs that pass a re.Pattern.
- Tests: characterization tests pin Wave 3 behavior (rm/mv/cp/echo >
  still blocked) plus new edge-case coverage for ceiling closed
  (dd of=/dev/sda, :>file, chain operators, pipe segments).
2026-06-30 10:39:44 +08:00
Fischer 2b8a7d8909 feat(agent): Wave 3 strategic coupling (G5/G6) (#6)
Deploy to Production / deploy (push) Waiting to run Details
Test / backend-test (push) Waiting to run Details
Test / frontend-unit (push) Waiting to run Details
Test / api-e2e (push) Waiting to run Details
Test / frontend-e2e (push) Waiting to run Details
2026-06-30 09:17:19 +08:00
Fischer a2dcde01b8 feat(agent): Wave 2 medium coupling (G4/G7/G9) (#5)
Deploy to Production / deploy (push) Waiting to run Details
Test / backend-test (push) Waiting to run Details
Test / frontend-unit (push) Waiting to run Details
Test / api-e2e (push) Waiting to run Details
Test / frontend-e2e (push) Waiting to run Details
2026-06-30 09:09:33 +08:00
chiguyong d7ca6e8065 fix(review): W1 ServerConfig from_dict wiring, W3 internal kwargs filter, N3 status docstring
Test / backend-test (pull_request) Has been cancelled Details
Test / frontend-unit (pull_request) Has been cancelled Details
Test / api-e2e (pull_request) Has been cancelled Details
Test / frontend-e2e (pull_request) Has been cancelled Details
Code review fixes for Wave 1:
- W1: ServerConfig.from_dict now wires prompt_cache/streaming/verification sections
  from YAML to constructor (previously these params existed but were never read)
- W3: Tool._validate_input filters _-prefixed kwargs (e.g. _skip_dangerous_check)
  before jsonschema.validate, preventing additionalProperties:false schemas from
  rejecting internal control parameters
- N3: ReActResult.status docstring now lists "empty_fallback" and "verify_failed"

Added test test_internal_kwargs_underscore_prefixed_skipped_by_validation for W3.
2026-06-29 21:58:40 +08:00
chiguyong cd211c6cd9 feat(U4): G1 verify 失败回灌 ReAct
- ReActEngine 新增 max_reinjections 构造参数(默认 1,=0 等价原行为)
- execute()/execute_stream() verify 块从循环后移到循环内 final-answer 检测点:
  - verify 通过 → 正常 break
  - verify 失败 + reinjections < max + step < max_steps → errors 作为 user 消息回灌 conversation, continue 让 LLM 自纠正
  - verify 失败 + 达到 max_reinjections 或 max_steps → 记录 verify log 到 trajectory, trace_outcome="verify_failed", break
- execute_stream 的 final_answer 事件在 verify 通过后才 yield,避免客户端过早收到完成信号
- ReActResult.status 现在传递 trace_outcome(原默认 "success")
- ServerConfig.verification 配置项(max_reinjections)
- test_verify_reinjection.py 10 测试:characterization(max=0)+ 新行为(R1/R2/R3/R14)
2026-06-29 21:35:08 +08:00
chiguyong 0f3f0a7550 feat(U3): G8 delta_flush_interval 调速
- ReActEngine 新增 flush_interval_ms 构造参数(默认 0 = 逐 chunk yield 向后兼容)
- execute_stream chunk 循环用 time.monotonic 节流,累积 _flush_buffer 批量 yield
- flush_interval_ms=0 条件短路为 True 逐 chunk yield 保当前行为
- 流结束 mid-interval 最终 flush 剩余 buffer 不丢字符
- ServerConfig.streaming 配置项(flush_interval_ms)
- test_delta_flush.py 覆盖 R11/R12/R14
2026-06-29 20:49:52 +08:00
chiguyong c4aaef05aa feat(U2): G2 prompt cache 双块结构
- ReActEngine 新增 _build_system_message(stable+volatile) 双块构造
- Anthropic provider 返回 content blocks,stable 块带 cache_control
- 非 Anthropic provider 返回字符串拼接,依赖 stable 前缀命中自动前缀缓存
- execute_stream/execute 记忆注入从 system_prompt 末尾移到 volatile 层
- LLMGateway.get_provider_name_for_model 暴露 provider 检测能力
- anthropic.py _convert_messages 支持 list-type system content 透传
- ServerConfig.prompt_cache 配置项(默认 enable=True)
- ReActEngine.prompt_cache_enable 构造参数(默认 True 保当前行为)
- test_prompt_cache_layers.py 覆盖 R4-R7/R13
2026-06-29 20:47:23 +08:00
chiguyong c66a7773b5 feat(U1): G3 工具调用 schema 校验
- base.py 新增 ToolValidationError(error_code/details)与 _validate_input
- safe_execute 在 execute 前用 jsonschema.validate 校验 kwargs
- input_schema=None 跳过校验保持向后兼容
- _execute_tool 优先捕获 ToolValidationError 保留 error_code
- function_tool._infer_schema 修复 VAR_KEYWORD/VAR_POSITIONAL 误入 schema
- test_tool_schema_validation.py 覆盖 R8-R10
2026-06-29 20:34:14 +08:00
chiguyong 2747bb4e64 chore(prior): malformed tool call handling, auth whitelist, dev scripts, wave1 plan 2026-06-29 20:25:03 +08:00
chiguyong a6e1bf5884 feat(bitable): 多维表格文件层 + 默认字段 + 表内字段操作 + ce-code-review 修复 (Stage 1)
Test / backend-test (pull_request) Has been cancelled Details
Test / frontend-unit (pull_request) Has been cancelled Details
Test / api-e2e (pull_request) Has been cancelled Details
Test / frontend-e2e (pull_request) Has been cancelled Details
实现多维表格 UI 完整性 Stage 1(U1-U6),补齐飞书/twenty 对齐缺失的文件层、
默认字段与表内字段操作能力,并修复 ce-code-review 走查发现的 P0/P1 级问题。

后端(U1-U2):
- 新增 BitableFile 实体(models/db/repository/service/routes),三级层级:文件→数据表→字段/记录
- Schema V2 迁移:bitable_files 表 + tables.file_id 列,幂等(IF NOT EXISTS),保留 V1 孤儿表
- 新建数据表自动创建 5 个默认字段(标题/状态/日期/创建人/创建时间)
- agent-owned 字段在 create_record 时自动填充(按 type+owner 匹配,传 actor_user_id)
- 7 个文件 REST 端点 + IDOR ownership 检查(404-before-403,internal token 旁路)

前端(U3-U5):
- 文件列表页(FileCard 网格 + 新建/重命名/删除)+ 文件详情页(侧栏表格列表 + vxe-table 网格)
- Vue Router 嵌套路由 /bitable → /bitable/:fileId → /bitable/:fileId/:tableId
- 列头菜单(编辑/隐藏/删除字段)+ 末尾 + 列新增字段
- select/multiselect 字段自定义单元格编辑器 + Tag 展示
- Pinia store 扩展 file 状态与动作,深链直访回退 getFile,fileId 切换 watch

测试(U6):
- 文件 CRUD(12 例)+ 默认字段(10 例)单元测试
- 3 个 E2E spec(视图加载、文件流、字段操作),后端不可用时优雅跳过

ce-code-review 修复(P0/P1):
- P0 路由冲突:GET /files/{file_id} 遮蔽下载端点 → 下载改 /uploads/{filename}
- P0 IDOR:update/delete field/record/view 五端点补 ownership 检查
- P1 is_initialized property 缺失致二次初始化崩溃
- P1 直接 URL 导航失效(files 数组为空)→ selectFile 回退 getFile
- P1 fileId 切换不重载 → 增加 watch
- P1 轮询丢弃最终公式值(wasCalculating 守卫)+ 复用视图 filters
- P1 测试断言 200→201;test_db 无 URL 用例解除 postgres 标记得以执行
- P2 _check_table_ownership 403→404;输入长度校验;upload field-table 一致性校验
- P2 multiselect 浅比较 → 深比较;E2E bitable-view 补 waitForServer 守卫

验证:ruff check 通过;pytest 91 passed/116 skipped;vue-tsc --noEmit 通过。
2026-06-29 04:07:45 +08:00
chiguyong 5c15238a5a fix(calendar): 修复 agent 创建日历事件后 UI 不刷新 + 文档化三根因三部曲
Test / backend-test (pull_request) Has been cancelled Details
Test / frontend-unit (pull_request) Has been cancelled Details
Test / api-e2e (pull_request) Has been cancelled Details
Test / frontend-e2e (pull_request) Has been cancelled Details
代码修复 (ce-debug):
- CalendarService.create_event 注入 notify_callback,成功后广播 calendar_event_created WS 消息
- app.py 调整 _calendar_ws_sender 闭包定义顺序,注入 CalendarService(与 ReminderScheduler 共享)
- tauri-auth.ts keychain fallback 修复(localStorage 始终作为备份)
- 新增 2 个广播回归测试

文档 (ce-compound + ce-compound-refresh):
- 新增 docs/solutions/ui-bugs/calendar-agent-create-no-refresh.md(第三根因:WS 广播缺失)
- 更新 calendar-capability-and-ui-fixes.md:刷新 test count + 加 Related Issues 前向引用
- 更新 jwt-secret-dev-mode-user-id-mismatch.md:扩展 e2e bullet + 加第三个根因引用
- CONCEPTS.md 新增 Service Broadcast Callback 条目 (Real-Time Fan-Out 节)

测试:
- 新增 E2E 测试套件 (admin/auth-persistence/bitable/calendar/conversation/documents/evolution/settings/skills)
- 新增 tests/e2e/test_api_coverage.py
- CI: .gitea/.github workflows/test.yml
2026-06-29 02:20:33 +08:00
chiguyong d27681a93c fix(portal-auth): 修复 dev mode JWT 验证误激活 + README 文档同步
## Portal 401 根因修复

问题:AGENTKIT_JWT_SECRET 未设置时,jwt_utils 生成 ephemeral 非空 secret,
该 secret 被传给 AuthMiddleware 后 _is_dev_mode() 返回 False(not "" = False),
导致无 JWT/API key 的请求被拒为 401(17 个 portal 测试失败)。

修复:分离 explicit_jwt_secret 与 jwt_secret —
- explicit_jwt_secret = get_jwt_secret()  # None when env unset
- jwt_secret = explicit_jwt_secret or get_or_create_jwt_secret()  # for signing
- AuthMiddleware(jwt_secret=explicit_jwt_secret or "")  # only explicit activates JWT verify

ephemeral secret 仅供 token 签名 routes,不激活 middleware 的 JWT 验证。
生产环境(AGENTKIT_JWT_SECRET 已设置)行为不变。

验证:
- _is_dev_mode(): False → True
- GET /api/v1/portal/conversations: 401 → 200
- 27 个 portal 测试全部通过(之前 17 失败)
- 232 个测试通过 (portal + auth + calendar),0 失败

## README 文档同步

代码中 CostAwareRouter / RegexRules / HeuristicClassifier / SemanticRouter / LLMClassifier
类已完全删除,仅 RequestPreprocessor 存在。README.md 6 处过时引用同步:

- 第 4 节"意图路由"改为引用 RequestPreprocessor(详见第 7 节)
- 第 7 节重写为"请求预处理(RequestPreprocessor)",按 AGENTS.md 架构描述
- 第 8 节"语义路由"删除(合并入第 7 节历史说明)
- 架构图 CostAwareRouter → RequestPreprocessor,22→28 路由模块
- 模块详解 chat/skill_routing + chat/semantic_router 合并为 chat/request_preprocessor
- 模块详解 router/intent 描述更新为"未接入 chat 流程"
- 目录注释 CostAwareRouter → RequestPreprocessor
- 章节重新编号 1-16 连续(原 1-17 跳过 9)
2026-06-28 15:26:42 +08:00
chiguyong c9ce15fa4b fix(code-review): 修复走查发现的 13 High + Medium 安全/可靠性问题
代码修复(8 High + 9 Medium):
- portal.py — C1 IDOR 文档 / C2 类型修复 / C3 WS 连接上限 16 / C4 ws_user_id 早初始化 / M silent swallow 日志化
- auth/middleware.py — C5 WS sid 补齐
- calendar_tool.py — C6 偏移量 ±43200 双向校验 + reminder_channels 类型/白名单校验
- sqlite_conversation_store.py — C7 DELETE 事务回滚
- chat.ts (Pinia) — C8 deleteConversation 清理 pending 缓存
- app.py — M except: pass → logger.debug(exc_info=True)
- Scene6Error.vue — M onUnmounted 清理 setTimeout
- DocumentsTab.vue — M Invalid Date 守卫
- ChatSidebar/RightPanel/TopNav.vue — M aria-label 无障碍标签
- SystemMonitorPanel.vue — M v-else 兜底 + active 边框色 + tablist 键盘导航
- CalendarDrawer.vue — M overflow-y: auto
- CalendarGrid.vue — M ResizeObserver 反馈循环防护
- SkillsTab.vue — M onMounted 始终 fetchSkills

文档修复(5 High + 6 Medium):
- portal-platform-security-reliability-fixes.md — D2 测试路径 / D3 Root Cause+Impact 章节 / D4 severity: mixed / 标题中文化 / 12 处绝对路径转相对 / P2 #12 数字口径
- AGENTS.md — D5 路由表 22→28 / 专家模板 5→15 / LiteLLM U15 迁移 / 配置查找 fallback
- README.md — 8 处端口 8000→8001

新增测试:
- tests/unit/calendar/test_calendar_tool.py — ponytail 自检断言

验证:
- ruff check (5 文件) — All checks passed
- vue-tsc --noEmit — exit 0
- git stash baseline 验证 — portal 17 个 401 失败为预存在问题

已知限制(预存在):
- 17 个 portal 测试 401 失败 — 需另起 ce-debug 调查
- README.md 7 处 CostAwareRouter 引用过时 — 文档同步另起任务
2026-06-28 15:06:41 +08:00
chiguyong 43e9025c6d fix(calendar): 日历能力缺失修复 + UI 布局优化 + 会话404处理
P0: calendar_tool reminder_rules 未传入 create_event,提醒功能完全失效。P1: chat.ts deleteConversation 未清理 pending + 404 递归保护。P2: app.py 系统提示重复段落 + gui_mode F821 + SystemMonitorPanel flex 布局。P3: portal send_json 快照 + WS connected 清除 is_local + 移除死代码。验证: ruff+pytest 98passed+typecheck 通过。
2026-06-28 14:24:58 +08:00
chiguyong 31c65e01b8 fix(security): P0 安全加固 + 多实例部署一致性 (U1-U4 + U5c)
Deploy to Production / deploy (push) Has been cancelled Details
U1: LLM gateway KB 缓存 fail-closed — 异常时默认禁用缓存防止 KB 数据泄漏
U2: MCP 危险工具黑名单过滤 — 6+1 端点覆盖,防止绕过 chat confirmation
U3: SecretsStore Redis 迁移 — 多 worker 共享凭证,内存降级保留开发模式
U4: channels webhook Redis 状态 — ZSET 滑动窗口限流 + nonce dedup + backpressure
U5c: ce-code-review 修复批次:
  - P0: 统一 MCP 黑名单与 publisher.py 一致 (terminal_execute -> terminal, +file_read)
  - P1: ZSET 限流 member 加 uuid 后缀避免同时间戳碰撞
  - P1: SecretsStore redis 参数 Any -> aioredis.Redis | None (AGENTS.md 合规)
  - P1: Redis client 添加 socket_timeout 防止单点故障请求挂死

测试: 171 scoped tests pass, ruff clean
2026-06-26 04:05:33 +08:00