Commit Graph

438 Commits

Author SHA1 Message Date
Chiguyong d74b41b0aa Merge: fix(iq) P1 trigger_reason + P2 autonomy UX 修复
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
Deploy to Production / deploy (push) Has been cancelled Details
2026-07-06 21:24:34 +08:00
Chiguyong f63082fb9c fix(iq): apply code review fixes — P1 trigger_reason + P2 autonomy UX
P1-1: _rebalance 路径传入 trigger_reason="loop_detection",使
retrieve_prompt_reflection 在重新规划时检索历史反思(原先两个
调用方都不传 trigger_reason,导致 retrieve_prompt_reflection
在生产中是死代码)。verify_retry / schema_validation 触发路径
待 verify/schema 重试路径与 team re-planning 集成后接入。

P2-1: 会话切换时如果有 autonomyPaused,先发送 cancel 消息通知
后端取消暂停的执行,避免 _resume_handler future 挂起最多 30 分钟。

P2-2: resumeAutonomy 中更新 "自主执行已暂停" milestone 状态为
"success",避免 streaming step 列表残留 "running" 状态。
2026-07-06 21:20:49 +08:00
Chiguyong 584592005a chore: 添加 uv.lock 锁文件 + 异步生成器死锁解决方案文档
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
- uv.lock: uv 包管理器锁文件,保证可复现构建
- docs/solutions/runtime-errors/async-generator-yield-before-await-deadlock.md:
  记录 ReActEngine autonomy pause 死锁问题的根因与修复方案
- components.d.ts: 新增 AutonomyPausedCard 组件类型声明(自动生成)
- .gitignore: 添加 .agentkit-data/ 忽略本地运行时数据
2026-07-06 20:40:33 +08:00
Chiguyong 8714056c46 Merge PR: fix(iq): 补齐 PR #27 的 5 个实现缺口 + 2 个 P2 residuals
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-06 20:38:12 +08:00
Chiguyong 5e29f35b6c Merge PR: fix(bitable): 修复表格头部与数据行之间的幽灵空白 2026-07-06 20:37:49 +08:00
Chiguyong bcfa4fb5f6 fix(iq): 补齐 PR #27 的 5 个实现缺口 + 2 个 P2 residuals
F1: 前端 autonomy_paused 事件处理
  - 新增 AutonomyPausedCard.vue 组件(reason + progress + resume 按钮)
  - chatStore/chatStream 接收 autonomy_paused 事件并渲染暂停卡片
  - ChatView 集成暂停卡片显示

F2: cleanup_expired 定时调用
  - episodic.py 的 prompt_reflection 记录 30 天过期清理接入

F3: SharedWorkspace.lock() TOCTOU 防护
  - _phase_executor.py 并行子任务写入用 lock() 防护
  - shared_workspace.py 补充锁实现

F4: retrieve_prompt_reflection KTD5 门控
  - react.py/reflexion.py 按 verify 二次失败/循环检测条件门控

F5: configs/agentkit.yaml.example 配置示例
  - 包含 dangerous_tools 配置示例

F6: resume Future 注册顺序消除竞态

F7: 用户拒绝危险工具不计入 _consecutive_failures

测试:112 个单元测试全部通过
2026-07-06 20:31:50 +08:00
Chiguyong fede996dcb fix(bitable): 修复表格头部与数据行之间的幽灵空白
- BitableGrid.vue: 小数据量时移除 height 属性,让 vxe-grid 按内容高度渲染
- 为 .bitable-grid-scope__group-grid 添加 flex:1/min-height:0,避免 vxe-table 回退到 16777216px 的异常高度
- 小数据量时禁用固定列,避免固定列容器错位导致底部出现重复表头
- 隐藏 vxe-table 内置 empty-placeholder,防止有数据时仍显示“暂无数据”
- 隐藏小数据量下 vxe-table 错误的 .vxe-body--y-space 占位,消除表头与首行之间的 132px 间隙
- test_user_service.py: 中文化错误信息断言同步更新
2026-07-06 20:27:04 +08:00
Chiguyong 99a512c3f7 feat(bitable): 表格复选框、字段图标与单元格样式
- BitableGrid 添加 checkbox 选择列,seq 列改窄为 40px
- 单元格高度 44px、表头高度 40px、行 hover 背景、列分隔线
- 字段类型图标加入 ColumnHeaderMenu
- date 字段使用 DateDisplay,统一 YYYY-MM-DD 格式
- SelectDisplay chip 圆角与行高微调
2026-07-06 18:46:21 +08:00
Chiguyong 729047433d feat(bitable): Twenty 风格工具栏、视图切换器与视图配置
- BitableFileDetailView 重构顶部工具栏:视图切换器、记录数徽章、新建记录/筛选/排序/选项
- ViewSwitcher 从标签页改为下拉菜单
- bitable store 新增 createRecord 方法
- ViewConfigPanel 支持 initialTab 从按钮直达对应配置页
- 新增 DateDisplay 组件与 bitable-grid-guidelines UX 规范
2026-07-06 18:43:49 +08:00
Chiguyong 96c15146d5 fix(auth): 登录失败、错误提示中文化、记住我 checkbox 样式
- 重置 admin 密码哈希为 Admin@123
- local.py / auth.py / dependencies.py 面向用户的错误提示改为中文
- LoginView 为记住我 checkbox 添加显式方形/主色样式
2026-07-06 18:43:49 +08:00
Chiguyong 306c6031a5 Merge branch 'fix/conversation-delete-reappears' into main
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
Fixes private-board conversation deletion reappearing after refresh.
- board_started now marks conversation is_local=false (server persisted)
- loadConversations explicitly sets is_local=false as safety net
2026-07-06 16:14:03 +08:00
Chiguyong f368d703dd fix(frontend): conversation deletion reappears after refresh for board meetings
Root cause: private board meetings are persisted server-side as soon as
board_started is emitted (portal intercepts and saves the message), but
the frontend left the conversation marked is_local=true. deleteConversation
skipped the server DELETE for local conversations, so only the in-memory
list was updated; the next refresh reloaded the conversation from SQLite.

Changes:
- chatStream.ts: on board_started, mark the conversation is_local=false
  so the server delete is called.
- chatStore.ts: loadConversations() now explicitly sets is_local=false
  for anything returned from the server as a safety net.

Type-check passes (existing unrelated test-file node module errors remain).
2026-07-06 16:13:29 +08:00
Chiguyong 5c0021e4bd Merge branch 'fix/private-board-llm' into main
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
Fixes 2 private board bugs:
1. board_orchestrator: chat_stream first-chunk hang (commit 36b0296 regression)
   - Add 30s first-chunk timeout to _stream_expert_speech
   - Fallback to non-streaming chat() + replay_stream on timeout
   - Fix mid-stream break double-broadcast (P2, review finding)
2. config_driven: _handle_llm_generate prefers _llm_gateway over legacy
   _llm_client (AgentPool only injects gateway)

3 commits, 4 files changed, 5 new tests.
E2E verified: 5-expert board completes in ~277s with DashScope.
2026-07-06 15:59:40 +08:00
Chiguyong 1c33859904 Merge remote-tracking branch 'origin/main' into fix/private-board-llm 2026-07-06 15:58:22 +08:00
Chiguyong af2de993d8 docs(solutions): document board chat_stream first-chunk hang fix
Captures the private board streaming hang regression (commit 36b0296)
and its fix (commits ed1e289, 0daa4d2, d0fe661). Key learnings:
- All `async for chunk in stream` calls need first-chunk timeout
  (DashScope/DeepSeek via LiteLLM occasionally never emit first chunk)
- Mid-stream break must not trigger full-content re-broadcast
  (return partial content instead of double-broadcasting)
- Diagnostic pattern: monkey-patch + per-stage timeout to locate hang

Track: bug
Category: runtime-errors
Overlap: low (related streaming docs exist but different root cause)
2026-07-06 15:55:37 +08:00
Chiguyong 0daa4d21b0 fix(review): avoid double broadcast when stream breaks mid-way
Code review finding (P2): when chat_stream successfully emits the first
chunk (broadcasted to UI) but then breaks during subsequent chunks, the
fallback path called gateway.chat() and broadcasted the FULL content
via _replay_stream — causing the UI to see partial streamed content
followed by duplicated full content.

Fix: if `total` already contains partial content from first_chunk,
return it directly without calling gateway.chat(). Partial content is
better than duplicated content. The non-streaming fallback only runs
when no chunks were broadcasted (total is empty).

Add test_stream_breaks_after_first_chunk_returns_partial covering this
edge case: first chunk yields "部分内容", second __anext__ raises
RuntimeError. Asserts result is the partial content, gateway.chat() is
never called, and broadcast_event is called exactly once (for the
first chunk only).
2026-07-06 15:53:27 +08:00
Chiguyong ed1e289785 fix(board): add first-chunk timeout to _stream_expert_speech
Commit 36b0296 changed expert speech generation from gateway.chat()
(non-streaming) to gateway.chat_stream() (streaming) for progressive
UI output. However, DashScope/DeepSeek via LiteLLM occasionally accept
the streaming request but never emit the first SSE chunk — the async
generator hangs indefinitely with no error and no timeout, freezing
the entire board.

Root cause: `async for chunk in gateway.chat_stream(...)` blocks
forever when the provider silently stalls. The moderator path
(gateway.chat) still works because it's synchronous — that's why
the moderator opening always succeeds but experts hang.

Fix: pull the first chunk via `__anext__()` wrapped in
`asyncio.wait_for(timeout=30s)`. If no chunk arrives within 30s,
close the stream and fall back to non-streaming gateway.chat() +
_replay_stream() (which splits the response into small chunks for
progressive UI rendering). This preserves the "逐个输出" UX while
guaranteeing the board never hangs on a stalled streaming provider.

Add 4 unit tests covering:
- Normal streaming works (existing path)
- First-chunk timeout → fallback to chat()
- Empty stream → fallback to chat()
- chat_stream raising → fallback to chat()

E2E verification: 5-expert board with DashScope completes in ~277s
(status=completed), all experts produce content, no hang.

Same branch as the earlier _handle_llm_gateway fix (commit d0fe661).
2026-07-06 15:35:15 +08:00
Fischer 8fc6bb0395 feat(iq): Agent IQ Boost — parallel sub-agents + autonomy + prompt self-tuning (#27)
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
Implements U1-U7 across 3 IQ-boost dimensions (R1-R15). 137 unit tests pass, ruff clean. Review fixes: P0 deadlock, P1 gate/tracking/filter, P2 score semantics.
2026-07-06 15:13:29 +08:00
Chiguyong d0fe6611c7 fix(core): prefer _llm_gateway over _llm_client in _handle_llm_generate
AgentPool.create_agent() injects only llm_gateway (v2 path) into
ConfigDrivenAgent and never sets the legacy _llm_client field. The
previous _handle_llm_generate implementation gated the entire LLM
call on `self._llm_client is None`, so every expert/board agent
created via the pool silently fell through to the
`llm_generate_no_client` placeholder — which is why the private
board moderator/experts reported "LLM 不可用" even when the gateway
and providers were healthy.

Mirror the same precedence used by `_handle_direct`: prefer
`_llm_gateway` (with agent_name/task_type for usage tracking), fall
back to `_llm_client` for backward compatibility, and only return
the no-client placeholder when both are unavailable.

Add a unit test that constructs an agent with `llm_gateway` only
(_llm_client is None) and asserts the gateway.chat path is taken.
2026-07-06 15:09:55 +08:00
Chiguyong aacec29948 fix(iq): apply code review fixes — P0 deadlock + P1 gate/tracking/filter
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
P0 #1: Fix autonomy_paused event deadlock — split _check_autonomy_pause
into _detect_autonomy_pause (non-blocking) + _await_autonomy_resume
(blocking). Caller now yields the pause event BEFORE awaiting resume,
so the frontend receives it and the resume handler doesn't deadlock.

P1 #2+#6: Fix retrieve_prompt_reflection field mapping — read
output_summary/reflection/quality_score from MemoryItem.value dict
(matching EpisodicMemory.search shape) instead of metadata. Score
filtering uses stored quality_score, not search relevance score.

P1 #3: Add optional task_type filter to cleanup_expired so
prompt_reflection TTL cleanup doesn't delete all episodic records.

P1 #4: Disable parallel tool execution in autonomy mode — dangerous
tools must go through _check_autonomy_gate, which only runs in the
serial path.

P1 #5: Add _track_tool_result_for_autonomy to parallel result loop
so tool failures are counted toward the consecutive_failures threshold.

Tests: adapt test_autonomy_paused.py to new detect/await interface;
fix test_lead_reflection_retrieval.py mock shape (fields in value dict).
137 IQ-boost tests pass, ruff clean.
2026-07-06 15:07:59 +08:00
Chiguyong fb86d4e51b refactor(iq): ce-simplify-code fixes — P1 bug + P2 quality
P1 (bug fix):
- store_prompt_reflection: bypass store() and write ORM entry directly
  so task_hash/version/score land in metadata_ JSONB column. Previously
  store() dropped these keys, breaking list_prompt_reflections_by_hash
  filtering (U7 would always return empty).

P2 (quality):
- DangerousToolsConfig: pre-compile regex patterns in __post_init__
  (is_dangerous runs on per-tool hot path in autonomy mode).
- ABTester.compare_prompt_versions: extract _no_data_result helper,
  remove redundant isinstance check after value type guard.
- orchestrator: fix misleading "transient TeamPlan" comment.

Tests updated to match new store_prompt_reflection implementation
(direct ORM write instead of store() delegation). 137 tests pass.
2026-07-06 14:20:42 +08:00
Chiguyong 4e2c7c5cac feat(iq): U7 ABTester prompt-version offline comparison (R14)
- EpisodicMemory.list_prompt_reflections_by_hash(task_hash): exact
  query for all prompt_reflection records matching task_hash.
  ponytail: O(N) scan with N<100 typical; GIN index upgrade path noted.
- ABTester.__init__: accepts optional episodic_memory parameter.
- ABTester.compare_prompt_versions(task_hash) -> dict: retrieves all
  versions for a task_hash, sorts by score descending, returns
  {versions, best_version, recommendation, total_versions}.
  Offline-only — no online bandit. Non-blocking on retrieval failure.
- 8 unit tests covering no-episodic, multi-version sort, single version,
  empty result, retrieval failure, field completeness, low-score
  retention, task_hash echo.
2026-07-06 14:02:14 +08:00
Chiguyong a2deeac0d6 feat(iq): U6 Lead planning-time reflection retrieval (R12, R13)
- ReflexionEngine.retrieve_prompt_reflection(): searches EpisodicMemory
  for historical reflections on similar task_input, returns best version
  by score (defaults min_score=0.5). Non-blocking: failure → None.
- TeamOrchestrator._decompose_task: prepends historical reflection hint
  to Lead's planning prompt when reflexion_engine is wired and a
  high-score reflection exists. Default prompt preserved on miss/failure.
- 12 unit tests covering retrieve path (7) + decompose integration (5).
2026-07-06 13:57:51 +08:00
Chiguyong 9653b1d5f7 feat(memory): U5 ReflexionEngine reflection persistence to EpisodicMemory (R11, R15)
Cross-task reflection persistence for prompt self-tuning:
- EpisodicMemory.store_prompt_reflection(): persists reflection via existing
  store() with task_type="prompt_reflection" discriminator. Key format:
  prompt_reflection:{task_hash}:{version}. Non-raising on failure.
- EpisodicMemory.search_prompt_reflections(): semantic search with
  task_type filter. Returns [] on failure.
- EpisodicMemory.cleanup_expired(): TTL cleanup (default 30 days).
- ReflexionEngine.__init__: optional episodic_memory param (None=backward compat)
- ReflexionEngine._reflect: persists reflection+improved_prompt+score after
  generation. Non-blocking — persistence failure doesn't block in-task retry.

Tests: 16 new tests (store/search/cleanup + _reflect persistence paths +
multi-version coexistence). All pass.
2026-07-06 13:50:10 +08:00
Chiguyong 81a35dac27 feat(experts): U4 TeamOrchestrator parallel independent subtasks (R1-R5)
Add explicit support for parallel execution of dependency-free subtasks:
- TeamPlan.get_independent_subtasks(): returns phases with depends_on==[]
  (introspection entry — topological_sort already groups them in layer 0)
- TeamOrchestrator.MAX_INDEPENDENT_SUBTASKS=10: aligns with router.MAX_EXPERTS
- _rebalance_independent_subtasks(): when Lead over-decomposes (>10
  independent subtasks), re-decompose once with merge hint. Fallbacks:
  no gateway → keep original; LLM error → keep original; single-phase
  fallback → keep original (don't collapse 11→1); still over → return
  new (MAX_PHASES truncation handles it)

Tests: 15 new tests covering get_independent_subtasks, topological_sort
layer contract, SharedWorkspace path uniqueness, rebalance (5 paths),
and full execute() integration. Existing TeamOrchestrator tests pass.
2026-07-06 13:43:31 +08:00
Fischer 8063f4efe0 feat(bitable): formula relations v2 — U1-U8 + review fixes (#26)
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
PR #26: bitable v2 module

- Formula engine: 10→64 functions
- Cross-table formula syntax
- Relation CRUD (1:1/1:N/N:N + bidirectional + self-ref)
- Lookup & Rollup field types with recalc worker
- Cross-table dependency graph
- Automation engine (3 triggers + 5 actions + retries + Webhook)
- 11 REST API endpoints + 17 BitableTool actions

Bug fixes (review + AE1):
- SSRF guard for outbound webhooks
- Bidirectional association reverse field cleanup
- Target table ownership checks
- Typed AutomationUpdate models
- Decimal handling for JSONB numbers
- merge_record_values (preserve fields on link update)
- enqueue_recalc ON CONFLICT DO UPDATE (reset done→pending)
- _trigger_cross_table_recalc on record update

Tests: 267 unit passed, AE1 e2e 3/3 passed, ruff clean
2026-07-06 13:35:31 +08:00
Chiguyong 7627403a8a feat(react): U3 PLAN_EXEC autonomy pause + autonomy_paused event (R10)
Add autonomy pause mechanism for PLAN_EXEC mode: when execution exceeds
timeout (default 30min) or hits consecutive failures (default 3), engine
emits `autonomy_paused` event and awaits user resume via resume_handler.

- DangerousToolsConfig: add autonomy_timeout_minutes + max_consecutive_failures
- ReActEngine: add _check_autonomy_pause() + _track_tool_result_for_autonomy()
  Called in serial + text-parsed tool paths (between tool_call event and
  autonomy gate). resume_handler=None → auto-resume (REST/testing).
- chat.py: WebSocket `resume` message handler + _resume_handler closure
  (Future-based, 30min hard timeout). pending_autonomy_resumes cleanup
  in finally block. autonomy_paused event forwarded to frontend.

Tests: 21 new tests (track_result + no_trigger + timeout + failures +
config parsing + handler exception). All pass; U1/U2 no regression.
2026-07-06 13:34:31 +08:00
Chiguyong e35b2289ce fix(bitable): cross-table recalc + merge_record_values + SSRF tests
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
Fixes 4 bugs blocking AE1 acceptance scenario (rollup returns 0):

1. Decimal not recognized as numeric: PostgreSQL JSONB returns numbers
   as Decimal; _evaluate_rollup's isinstance(v, (int,float)) missed them.
   Added _to_float() helper (also excludes bool, an int subclass).
   Applied to _evaluate_lookup for JSON serialization safety.

2. add_relation_link clobbers record values: update_record_values does
   full-replace, so setting the relation field wiped amount/etc.
   Added merge_record_values (|| operator) and switched all relation
   link mutations to use it (add + remove + reverse cleanup).

3. enqueue_recalc ON CONFLICT DO NOTHING swallowed re-enqueues: once a
   task reached 'done', the unique constraint silently dropped new
   requests for the same (record_id, field_id) — field showed stale
   data forever. Changed to ON CONFLICT DO UPDATE: reset done/error
   tasks to pending; leave pending/calculating as-is (no double-process).

4. No cross-table recalc on record update: update_record_values only
   triggered recalc on the record's own table; rollup on the target
   table never updated when source values changed. Added
   _trigger_cross_table_recalc() — finds relation fields on the
   record's table, triggers recalc on linked target records.

Tests:
- AE1 acceptance: 3 e2e tests (SUM on insert, update-triggered recalc,
  AVG aggregation) — all pass against real PG.
- Bidirectional relation deletion: verifies _cleanup_reverse_link
  removes source from target's reverse field.
- SSRF guard: 3 tests blocking loopback, private IP, non-HTTP scheme.
- Updated test_recalc_deduplication for new ON CONFLICT DO UPDATE
  semantics (returns existing task, not None).
- Fixed test_crash_recovery to use stale_threshold=0 (was always broken
  with default 600s threshold).
2026-07-06 13:29:54 +08:00
Chiguyong 4729e20bbf feat(react): U2 PLAN_EXEC autonomy mode + dangerous-tools gate (R6-R9)
ReActEngine now accepts dangerous_tools_config + autonomy_mode. In PLAN_EXEC
autonomy mode, dangerous tools (per DangerousToolsConfig whitelist) trigger
confirmation_request BEFORE first execution; non-dangerous tools execute
directly without confirmation. _build_phase_engine injects the config and
sets autonomy_mode=True when PLAN_EXEC is engaged.

- _check_autonomy_gate: pre-execution gate (serial/parallel/text-parsed paths)
- Approved dangerous tool re-executes with _skip_dangerous_check=True
- Rejected/handler-failure → permission_denied result (exit_code=126)
- Default autonomy_mode=False → transparent no-op (backward compat)
- 19 new tests (14 autonomy gate + 5 PLAN_EXEC wiring)
2026-07-06 13:23:06 +08:00
Chiguyong b6f7d82ff5 feat(config): U1 add DangerousToolsConfig for PLAN_EXEC autonomy (KTD2)
- New DangerousToolsConfig dataclass with regex-based tool_patterns
- Default whitelist: rm/rmdir/deploy/kubectl/helm/git_push_force/alembic/migrate/payment_*
- is_dangerous(tool_name) method for autonomy mode gate
- ServerConfig.from_dict parses dangerous_tools section
- 10 unit tests covering defaults, matching, disabled, custom patterns
2026-07-06 13:12:34 +08:00
Chiguyong 9fe1810497 docs(plan): add agent-iq-boost implementation-ready plan (R1-R15, U1-U7) 2026-07-06 13:12:34 +08:00
Chiguyong fae76b7cca merge: resolve conflict with origin/main (PR #24/#25)
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
Conflict in src/agentkit/bitable/service.py delete_view():
- Adopted main's MAINT-2 fix (delete_view_if_not_last atomic op + race check)
- Worktree's simpler version replaced by main's race-safe implementation

No conflict markers remain. 277 tests pass, ruff clean.
2026-07-06 13:04:53 +08:00
Chiguyong 6d5a34ada2 feat(bitable): formula relations v2 — U1-U8 + review fixes
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
Implements bitable v2: formula engine expansion (10->64 functions),
cross-table formula references, relation CRUD (1:1/1:N/N:N + bidirectional),
lookup/rollup fields, cross-table dependency graph, automation engine
(3 triggers + 5 actions + retry + webhook), and REST API + BitableTool actions.

Implementation units:
- U1: Schema V3 migration (junction table, automations, cross_table_deps)
- U2: Formula functions 10->64 (datetime/text/logical/aggregate/lookup)
- U3: Cross-table formula {rel.target} syntax (parser 3-tuple, engine resolve)
- U4: Relation CRUD + bidirectional + self-reference
- U5: Lookup & Rollup field types with recalc worker routing
- U6: Cross-table dependency graph + recalc trigger
- U7: Automation engine (asyncio queue, 3x retry, execution logs)
- U8: REST API (11 endpoints) + BitableTool (17 actions)

Review fixes applied (ce-code-review mode:agent):
- P1: SSRF guard on outbound webhook (reuse _assert_safe_host from ingestion)
- P1: Bidirectional relation removal now cleans reverse field (symmetric with add)
- P2: Ownership check on target_table_id in relation field creation (IDOR fix)
- P2: Typed AutomationUpdate Pydantic model (prevents table_id injection)
- P3: Tracked automation fire-and-forget tasks (error visibility)
- P3: Public FormulaEngine.get_cross_table_mapping (remove private attr access)

Tests: 267 passed, 153 skipped (PG). Ruff clean.
Deferred: generate_formula/generate_automation LLM actions, frontend components.
2026-07-06 12:57:07 +08:00
root f0fc34bfee Merge pull request 'fix(enterprise): P3 residual batch' (#25) from feat/p3-residual-fixes into main
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-06 12:24:44 +08:00
Chiguyong 04ad5966d0 fix(enterprise): ce-debug P3 residual batch — SEC-2 OIDC 验签 + STAND-3 SCIM /scim/v2 + MIG-2 schema 迁移
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
13 项 P3 residual findings 全部清零:

安全/合规
- SEC-2: OIDC id_token 用 JWKS 验签 + IDToken claims_cls 校验 iss/aud/exp(fail-closed,无 jwks_uri 时拒绝回退)
- STAND-3: SCIM router 改挂 /scim/v2(RFC 7644 标准路径)+ 修复双重前缀 bug(router 内部 prefix 移除,由挂载点控制)
- API-12/14: SCIM PATCH 严格性 — 不支持的 op/path 返回 400 noTarget + path==None 时遍历 value keys

性能/可观测
- PERF-6/7: gateway cache metric 修正 — None usage 早退 + 仅对 anthropic/claude model 记录 cache hit/miss

部署/运维
- DEPLOY-8: key-rotation-runbook 拆分 Slot 10(Redis)/Slot 11(PostgreSQL)
- DEPLOY-9: Helm chart 支持 image.digest pinning(@sha256:<digest> 覆盖 tag)

迁移/维护
- MIG-2: auth DB schema 迁移注册表(_SCHEMA_MIGRATIONS dict + _run_schema_migrations 按 version 差值执行)
- MAINT-2: bitable delete_view 三态错误信息(不存在 vs 最后一视图 vs 并发竞争)
- MAINT-3: chatStream 提取 findStreamingSynthesisMilestone helper(去重 team_synthesis_chunk/team_synthesis 谓词)
- MAINT-4 + STAND-2: 前端测试 token 正则 helper + replace 加 g 标志(移除所有 :root 块)

验证:ruff check + format(P3 文件全过)| pytest 526 passed(calendar webhook SSRF 失败为 pre-existing)| helm lint 通过
2026-07-06 12:18:42 +08:00
Fischer a406f59bdf feat(enterprise): U1-U6 enterprise agent platform (#24)
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
Merge PR #24 — U1-U6 enterprise agent platform

ce-debug 批量修复:
- PERF-1: PII 脱敏接入 gateway 生产路径
- DEPLOY-1: 部署名引用修正(label selector)
- DEPLOY-2: Redis 鉴权(11 处 from_url 消费 REDIS_PASSWORD)
- DEPLOY-4: 移除死 env POSTGRES_PASSWORD
- DEPLOY-5: OTel token 格式 Authorization=Bearer
- SCIM RFC 7643/7644 合规
- API-4/8/10: SSO token 原子化 + provider 区分 + response_model
2026-07-06 11:42:29 +08:00
Chiguyong cb278bb5f0 fix(enterprise): ce-debug batch — PERF-1 PII filter + DEPLOY-1/2/4/5 + SCIM RFC 7643/7644
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-code-review 发现的全部 P1/P2 residual findings:

P1:
- PERF-1: PII 脱敏接入 gateway chat()/chat_stream() 生产路径
  (env AGENTKIT_PII_FILTER_ENABLED=true 启用,fail-closed)
- API-1/2/5/6/7/9/11/13 + STAND-1 + SEC-3: SCIM router 全面重写
  - API-1: totalResults 用 SELECT COUNT(*) 返回全量总数
  - API-2: 异常处理器转 RFC 7644 错误格式(scope 到 /scim/v2)
  - API-5: create_user 写 user_idp_links(idp_name='scim')
  - API-6: POST /Users 返回 Location 头
  - API-7: PATCH userName UNIQUE 冲突 → 409
  - API-9: 不支持的 filter → 400 invalidFilter
  - API-11: displayName=None
  - API-13: filter 大小写不敏感
  - STAND-1: dict[str, Any] → dict[str, object]
  - SEC-3: active true→false 写审计(延后到事务提交后避免 SQLite 锁竞争)
- API-4: _sso_issue_token_pair 用 secrets.token_urlsafe(32) 生成真实 refresh_token
  (移除 __pending_sso__ 占位符 + 后续 UPDATE 的非原子两步写入)
- API-8: _sso_issue_token_pair 增加 provider_type/provider_name 参数
- API-10: deprovision_user/change_user_role 加 response_model
- DEPLOY-1: install.sh + docs 用 instance 标签选择器替代硬编码 fullname
- DEPLOY-2: 11 处 Redis 客户端构造显式传入 REDIS_PASSWORD env var

P2:
- DEPLOY-4: 移除 agentkit deployment 中死 env POSTGRES_PASSWORD
  (应用通过 DATABASE_URL Slot 3 内嵌密码连接,不读此 env)
- DEPLOY-5: OTel token 格式改为 Authorization=Bearer <token>
  (符合 OTEL_EXPORTER_OTLP_HEADERS key=value 规范)

测试:
- ruff check + format 全部通过
- pytest tests/unit/test_scim_router.py 9 passed
- pytest tests/unit/test_llm_gateway.py + test_handoff.py + test_event_queue_integration.py 70 passed, 5 skipped
- helm lint + helm template 验证 POSTGRES_PASSWORD env 已移除
2026-07-06 11:41:06 +08:00
Chiguyong 444667e2f0 docs(review): record residual 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
2026-07-06 10:40:06 +08:00
Chiguyong 3cf29f4633 fix(enterprise): ce-code-review P1 fixes — SSO is_active + span leak + Helm chart integration
Stage 5c applied safe P1/P2/P3 fixes from multi-agent code review:

- SEC-1 (P1, security): SSO 登录绕过 is_active 检查 — _sso_issue_token_pair
  入口加显式校验,与本地 login(auth.py:377)一致。已停用用户无法再通过
  IDP 回调拿 JWT,封堵 R1b deprovisioning 绕过。

- PERF-2/3 (P2, reliability): gateway chat() span 泄漏 + 异常未记录 — try 块
  扩展到覆盖整个 span 生命周期(含 cache 设置),新增 except 分支调用
  record_exception + set_status(ERROR),失败调用在 trace 中不再显示为成功。

- API-3 (P1, test isolation): SCIM router 使用 import-time 捕获的
  DEFAULT_AUTH_DB_PATH — 改为 resolve_auth_db_path() 在调用时读取 env var,
  让测试 monkeypatch.setenv 可见。

- MAINT-1 (P3, dead code): 删除 BitableRepository.delete_view(被
  delete_view_if_not_last 取代,无生产调用方,仅 docs 残留引用)。

- PERF-4/5 (P3, performance): pii_filter._record_pii_metrics 的 from import
  提升至模块顶部 + except: pass 改为 logger.debug 记录失败原因。

- DEPLOY-3 (P1, chart integration): ConfigMap 不可达 — deployment.yaml
  args 增加 --config /etc/agentkit/agentkit.yaml,让应用能加载 chart 渲染
  的非密配置。

- DEPLOY-6/7 (P3, doc/chart consistency): values.yaml 注释残留旧 slot 名
  redisPgPasswords → redisPassword;slot 计数 10 → 11(5 处 chart + 3 处
  deployment docs 同步修正)。

Verification:
- ruff check + format clean (7 modified .py files)
- pytest tests/unit/{auth,bitable} + test_{pii_filter,oidc_provider,
  saml_provider,scim_router,auth_audit,telemetry,prompt_cache_layers}.py
  → 336 passed, 145 skipped
- helm lint + helm template 验证 secret key 一致性

Residual findings (deferred to follow-up):
- PERF-1: PII filter 未接入 gateway 生产路径(manual, 需配置接入决策)
- API-1/2/4/5: SCIM RFC 7644 合规性(totalResults/error format/Location
  header/SSO 预配联动)— manual, 需设计决策
- DEPLOY-1/2/4: Helm chart ↔ app 集成断裂(部署名引用/Redis 鉴权/PG 密码双源)
  — manual, 需 app+chart 联动修改
2026-07-06 08:36:59 +08:00
Chiguyong 86bce129a4 refactor(enterprise): simplify U1-U6 — fix span leak, dedup helpers, stdlib parsing
lfg Step 3 (ce-simplify-code) — 16 files, -89 net lines.

P1 fixes (functional):
- audit.py / scim/router.py: fix OTel span leak — start_span() returned a
  context manager but was used as a plain object, so spans never ended
  and never exported. Wrap in `with`.
- helm chart: split combined `redisPgPasswords` slot (single JSON key)
  into `redisPassword` + `postgresPassword` slots — the combined `key`
  field was dead config never referenced by any template; deployment.yaml
  hardcoded `redis-password`/`postgres-password` keys were inconsistent
  with values.yaml schema.

P2 dedup (reuse):
- Promote `resolve_auth_db_path()` to models.py (canonical); remove 3
  duplicate `_resolve_db_path` defs in oidc.py / saml.py / audit.py.
- Reuse `_now_iso` from models.py; remove 5 duplicates across audit /
  oidc / saml / scim / local providers.
- Reuse `_row_to_user` from local.py in oidc.py / saml.py (verbatim
  copies removed).
- pii_filter._redact: collapse redundant `finditer` + `sub` (2 regex
  passes + 2x sha256 per match on hot path) into a single `sub` with a
  callback that collects matches and computes hashes once.

P3 simplifications:
- RoleChangeRequest.role: `str` + manual `if not in (...)` → `Literal`
  (Pydantic validates automatically).
- Extract `_record_pii_metrics(status, redacted_count)` helper to dedup
  3 nearly-identical OTel metric emission blocks.
- Extract `LLMGateway._record_prompt_cache_metric(usage, model)` to dedup
  non-stream + stream cache hit/miss counters; switch to use the
  `TokenUsage.cache_hit` property (previously added but unused).
- middleware: split `WHITELIST_PATHS` + `PREFIX_MATCH_PATHS` (redundant
  cross-reference) into `WHITELIST_PATHS` (exact) + `PREFIX_WHITELIST_PATHS`
  (prefix) — single source of truth per kind.
- OIDC token exchange: replace f-string form body with `urlencode` to
  correctly escape `&` / `=` in code / redirect_uri.
- auth.py OIDC redirect route: replace hand-rolled `url.split("state=")`
  with `urlparse + parse_qs`.
- PII module docstring: clarify that `PIIMatch.original` is in-memory
  only (must not be logged / persisted) — the previous "仅 hash, 不含
  原文" wording was misleading.

Skipped findings (false-positive or not worth):
- OIDC/SAML `_find_or_create_user` JIT 60-line dedup — high blast
  radius across SQL + transaction boundaries; defers to dedicated refactor.
- SAML `_extract_host` hand-rolled split — already has ponytail comment;
  stdlib `urlparse` swap is behavior-equivalent for current inputs but
  changes OneLogin `http_host` field semantics; conservative keep.
- gateway.py department-quota / usage-record sequential await —
  concurrency gain unclear when dept count is typically ≤2.

Verification:
- ruff check (scoped to 12 touched files) clean
- ruff format (1 file reformatted)
- pytest (PII + OIDC + SAML + SCIM + audit + bitable + team_orchestrator):
  265 passed, 145 skipped (PG/Docker), 0 failures
- helm lint clean; helm template renders correctly for both
  sealed-secrets + external-secrets backends
2026-07-06 08:02:40 +08:00
Chiguyong de02ac8a92 docs(compliance): U6 — 等保三级文档准备(6 维度 + 控制点清单)
U6 启动等保三级认证文档编写,按 GB/T 22239-2019 6 维度组织:

00-overview — 总览:认证目标、范围、AgentKit 架构摘要、各维度状态。
01-physical — 物理安全:客户机房部署假设。
02-network — 网络安全:Ingress TLS + NetworkPolicy + OTel mTLS。
03-host — 主机安全:非 root 容器 + distroless + 漏洞扫描。
04-application — 应用安全:SSO 多 IDP + RBAC 3 级 + 6 层终端安全 + OTel 审计。
05-data — 数据安全:PII 脱敏 + 国密 P1 参考 + PG 备份。
06-management — 管理安全:密钥轮换 runbook + break-glass 告警。

control-points.yaml — 控制点清单 + AgentKit 实现映射(已实现/P0/P1/待评估)。
2026-07-06 07:32:30 +08:00
Chiguyong af9cb9496c feat(deploy): U5 — K8s Helm Chart + Sealed Secrets + 离线安装包
U5 产出政企生产级 K8s 部署 artifacts:

Helm Chart — deploy/helm/agentkit/ 含 Chart.yaml + values.yaml +
8 个 templates(deployment/service/ingress/configmap/sealed-secret/
external-secret/serviceaccount/_helpers)。

values.yaml 显式列出 10 个 secret slot(KTD-4):JWT 签名密钥 /
API Key / DB 凭据 / IDP 签名证书 / 第三方 API Key / TLS 服务器证书 /
mTLS 客户端证书 / KMS-HSM PIN / OTel exporter token / Redis-PG 密码。

Sealed Secrets + External Secrets Operator 双模板,禁用 plain
Kubernetes Secret + Git 提交。serviceaccount 配置 projected token
注入支持 KMS/HSM workload identity。

离线安装包 — build-images.sh 构建镜像 tarball +
package-chart.sh 打包 chart tarball + install.sh 离线安装,
覆盖原生 K8s 1.28+ / Helm 3.x。

运维文档 — k8s-install.md 部署指南 + key-rotation-runbook.md
密钥轮换 runbook(10 slot)+ secret-inventory.md 密钥清单。
2026-07-06 07:21:41 +08:00
Chiguyong d3bd0d3b5f feat(auth): U4 — SSO 集成 (OIDC + SAML + SCIM + RBAC 审计)
实现 U4 SSO 集成,按 R1a 多 IDP 信任边界(按 (idp_name, idp_subject)
主键关联,禁止邮箱合并)、R1b 过渡期手动 deprovisioning、R1c 单租户假设。

- OIDC 适配器 (authlib):redirect flow + JIT 用户创建(KTD-2)
  GET  /auth/oauth/{provider}/redirect → IDP authorize
  GET  /auth/oauth/{provider}/callback  → token + userinfo + 关联本地用户
- SAML 适配器 (python3-saml):ACS endpoint
  POST /auth/saml/{provider}/acs → 解析 NameID + JIT 用户创建
- IDPRegistry (idp_registry.py):多 IDP 注册 + 热证书切换 + 健康隔离
- SCIM 2.0 minimal:bearer token 认证 + push 失败告警
  POST  /scim/v2/Users      (create)
  PATCH /scim/v2/Users/{id} (disable)
  GET   /scim/v2/Users       (filter/list)
- 手动 deprovisioning (R1b):operator/admin RBAC + OTel 审计
  POST /admin/users/{user_id}/deprovision
  POST /admin/users/{user_id}/role        (RBAC 角色变更审计 KTD-8)
- AuthDB schema V5:user_idp_links + auth_audit_log(additive)
- middleware 白名单:auth/oauth/、auth/saml/、scim/v2/ 走前缀匹配

测试:31 个单元测试全部通过(OIDC 8 + SAML 7 + SCIM 9 + Audit 6 + 1 schema)
ponytail 简化:
- _StateCache 进程内 dict + TTL 5min(多实例需 Redis 共享 state)
- OneLogin_Saml2_Auth 同步解析走 asyncio.to_thread 避免阻塞
- SCIM alert 走 OTel span event(无独立告警通道)
- 邮箱冲突时使用 fallback 邮箱(@sso.{provider}.local)保留 UNIQUE 约束
2026-07-06 04:15:02 +08:00
Chiguyong c60e96ac32 fix(bitable/team): U3 — Residual P2 findings (DR-1/3/4/5 + #2/#3)
U3 修复 8 项 P2 design review 与 streaming 残留问题:

DR-1 SQL 注入防御 — repository.py 添加 _validate_field_id UUID 校验,
拒绝非 UUID 字符串进入 jsonb_set path 与 WHERE 子句的字符串插值。
ceiling 注释标明仅覆盖 field_id,其余 SQL 结构由 service 层枚举白名单守护。

DR-3 design token 契约 — 新增 bitable-tokens.test.ts (31 tests),
解析 CSS :root 块并断言所有 --bitable-* token 在 :root 定义,
防止 token 被硬编码到组件外或遗漏定义。

DR-4 TOCTOU 修复 — repository.delete_view_if_not_last 用
SELECT ... FOR UPDATE 单事务原子化 check + delete,
消除 list_views → count → delete 三步分离的竞态。
service.delete_view 委托原子方法,失败抛 LastViewDeletionError。

DR-5 _update_field 静默失败 — type 参数被 Pydantic extra=ignore
静默丢弃,用户以为改了类型实际无效。显式返回
UNSUPPORTED_FIELD_TYPE_CHANGE 错误,避免静默失败的 UX 陷阱。

#2 team_synthesis 孤儿 milestone — 补 error + cancelled 路径测试,
验证内层 except 广播终结事件后 re-raise,外层 except 捕获后 fallback。

#3 synthesis_id 去重附身 — chatStream.ts 移除
|| m.synthesis_id === undefined 兜底匹配,避免新 milestone
附身到上一次孤儿 streaming 占位。

测试:DR-1 (7) + DR-4 repo (3) + DR-4 service (3) + DR-5 (2) +
#2 (2) + #8 frontend (31) = 48 项通过;U2 无回归 (29 项)。
2026-07-06 03:48:14 +08:00
Chiguyong 60a58a0fdb feat(llm): U2 — Prompt Cache 全链路监控 + PII 脱敏 hook
U2/R3 — Anthropic prompt cache 命中率监控 + 等保合规 PII 脱敏。

变更:
- protocol.py: TokenUsage 添加 cache_read/creation_input_tokens + cache_hit property
- providers/anthropic.py: 非流式 + 流式两处解析 cache 字段(cache_read_input_tokens / cache_creation_input_tokens)
- gateway.py: chat() / chat_stream() emit prompt_cache.hit / prompt_cache.miss OTel counter + span attributes
- telemetry/metrics.py: 新增 4 个 metric instruments(prompt_cache.hit/miss + pii_filter.coverage/redacted)
- llm/pii_filter.py(新文件): 正则版 PII 脱敏(手机/邮箱/身份证/银行卡)+ ner_hook + fail-closed + sha256 hash 审计
- agentkit.yaml: 添加 fallbacks + pii_filter 配置模板(默认 disabled,等保场景按需启用)
- tests: test_prompt_cache_layers.py +4 cache metric 测试;test_pii_filter.py 新增 PII 脱敏测试(29 测试通过)

设计决策:
- PII 脱敏用 stdlib(re + hashlib),不引入 NER 依赖;ner_hook 接入客户内网 NER 模型
- 正则顺序 id_card 在 phone 之前(避免身份证号内 11 位数字子串被手机号误匹配)
- ner_hook 异常不吞掉:传播到 filter_pii 由 fail_closed 决定(True→PIIFilterError;False→降级正则版重试)
- fail-closed:脱敏失败拒绝发送(不降级到原文),等保合规要求
2026-07-06 00:32:35 +08:00
Chiguyong d998c0344c feat(telemetry): U1 — OTel 默认启用,从 optional 升级为 required 依赖
- pyproject.toml: 添加 opentelemetry-api/sdk/exporter-otlp-proto-grpc/
  instrumentation-fastapi 到 [project] dependencies(KTD-3)
- telemetry/{tracer,metrics,tracing,setup}.py: 移除 try/except ImportError
  静默回退 — 缺包现在是硬安装错误,不再是监控盲区。保留 except Exception
  处理运行时错误(OTLP 连接失败),保留 enabled=false 配置开关
- setup.py: 修复已存在 bug — MeterProvider 的 readers kwarg 在 OTel SDK 1.16+
  改名为 metric_readers(被 ImportError fallback 掩盖)
- agentkit.yaml: 取消 telemetry 段注释,设 enabled=true + otlp_endpoint +
  service_name + export_traces/metrics
- test_telemetry.py: 更新 test_missing_opentelemetry → test_enabled_initializes_otel_tracer;
  新增 TestSetupTelemetry smoke test(disabled/none 是 no-op,enabled instrument FastAPI);
  清理 unused imports/vars

Verification:
- pytest tests/unit/test_telemetry.py: 21 passed
- pytest tests/unit/test_react_compression.py: 22 passed(_OTEL_AVAILABLE patch 兼容)
- runtime check: enabled=True → OTelTracer, enabled=False → NoOpTracer
2026-07-06 00:18:09 +08:00
Fischer 8633f60831 feat: complex-task-quality-loop (R1-R12) — 11 P1 blockers fixed (#22)
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
Merge feat/complex-task-quality-loop into main.

Includes U1-U9/R1-R12 implementation + 11 P1 blocker fixes from ce-code-review.

P1 fixes: trace_outcome propagation, portal execute_stream routing, network_block reentrancy, spec review gate wiring, max_reflections threading, phase budgets, plan aggregation, failure status mapping, evolution drain timeout, portal spec_review_reply, spec_review persistence.
2026-07-05 22:31:21 +08:00
chiguyong e5e76697a9 fix(review): resolve 11 P1 blockers from ce-code-review
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
P1#1  config_driven: propagate trace_outcome into output_data so
      lifecycle._is_failure_path() detects non-success outcomes
P1#2  portal: route through ConfigDrivenAgent.execute_stream (not
      react_engine.execute_stream directly) so evolution hooks fire
      and trace_outcome propagates; add pre-built messages support in
      _build_llm_messages
P1#3  sandbox: make network_block reentrant via module-level reference
      counter + threading.Lock - concurrent VERIFICATION phases no
      longer permanently block all new connections
P1#4  chat: replace dead isinstance(_PlanExecEngine) check with
      hasattr(_spec_review_handler) to wire the spec review gate
P1#5  plan_exec_engine: complete max_reflections threading chain
      (PlanExecEngine + ReActStepExecutor constructors)
P1#6  plan_exec_engine: enforce phase budgets (max_steps from
      phase_budgets, not hardcoded 5)
P1#7  plan_exec_engine: use current plan (not stale plan var) in
      aggregation after replan
P1#8  plan_exec_engine: map failure to failed status (not success)
P1#9  app: add drain timeout for pending evolution tasks on shutdown
P1#10 portal: handle spec_review_reply in WS handler
P1#11 chat: persist spec_review_request/reply/timeout to conversation
      store so reload can reconstruct gate state

Tests: 116 related tests pass; 26 pre-existing failures unchanged
(stash-verified). ruff lint clean.
2026-07-04 01:10:01 +08:00
Fischer 454a50b5a8 feat: Bitable P0 UX Polish + Agent Parity (#23)
Deploy to Production / deploy (push) Failing after 7s Details
Test / backend-test (push) Has been cancelled Details
Test / frontend-unit (push) Has been cancelled Details
Test / api-e2e (push) Has been cancelled Details
Test / frontend-e2e (push) Has been cancelled Details
Merged via LFG pipeline after final ce-code-review (Ready to merge) + ce-compound documentation.
2026-07-04 01:05:04 +08:00
chiguyong 826b766af0 docs(solutions): record bitable agent tool parity patterns + final review findings
Add docs/solutions/architecture-patterns/bitable-agent-tool-parity-patterns.md
capturing three architecture patterns from U6 (R15a):
- Dual-sync action registration (KTD10): handlers dict + input_schema.enum
- 404-before-403 ownership check (KTD9): prevent existence leak via DELETE
- 409 last-view protection: prevent invalid zero-view table state

Update residual findings with DR-4 (TOCTOU race in delete_view) and DR-5
(_update_field silent type drop) surfaced in final pre-merge ce-code-review
pass. Both P2, neither blocks merge. Documented in the solutions doc under
Known Limitations with concrete fix paths.
2026-07-04 01:04:46 +08:00