Merge branch 'worktree/refactor-tool-cli-skill-ui-schema' into dev

This commit is contained in:
qzl
2026-04-23 16:04:13 +08:00
151 changed files with 6725 additions and 5216 deletions
@@ -0,0 +1,15 @@
{"file": ".opencode/commands/trellis/finish-work.md", "reason": "Completion checklist for cross-layer work"}
{"file": ".opencode/commands/trellis/check-backend.md", "reason": "Backend verification checklist"}
{"file": ".opencode/commands/trellis/check-frontend.md", "reason": "Frontend verification checklist"}
{"file": "AGENTS.md", "reason": "Project-level constraints and protocol source of truth"}
{"file": "backend/AGENTS.md", "reason": "Backend runtime/tool contract rules"}
{"file": "apps/AGENTS.md", "reason": "Frontend AG-UI and renderer constraints"}
{"file": "backend/src/core/agentscope/tools/toolkit.py", "reason": "Verify tool registration moved off direct Python bindings as intended"}
{"file": "backend/src/schemas/agent/runtime_models.py", "reason": "Verify worker output no longer requires ui_hints"}
{"file": "backend/src/core/agentscope/runtime/stage_emitter.py", "reason": "Verify worker event payload shape after ui_hints removal"}
{"file": "backend/src/v1/agent/utils.py", "reason": "Verify history payload no longer depends on runtime ui_hints compilation"}
{"file": "apps/lib/core/chat/ag_ui_event.dart", "reason": "Verify frontend event/history parsing matches new payload contract"}
{"file": "apps/lib/shared/widgets/ui_schema/ui_schema_renderer.dart", "reason": "Verify renderer consumes tool-schema derived UI contract"}
{"file": "docs/protocols/ui/ui-schema.md", "reason": "Verify protocol docs match implementation after refactor"}
{"file": "docs/protocols/ui/data-flow.md", "reason": "Verify documented data flow no longer depends on worker ui_hints"}
{"file": "docs/protocols/agent/sse-events.md", "reason": "Verify SSE contract documents updated worker/tool payloads"}
@@ -0,0 +1,20 @@
{"file": ".trellis/workflow.md", "reason": "Trellis workflow and task execution rules"}
{"file": ".trellis/spec/backend/index.md", "reason": "Backend development constraints and verification baseline"}
{"file": ".trellis/spec/frontend/index.md", "reason": "Frontend rendering and app-layer conventions"}
{"file": ".trellis/spec/guides/cross-layer-thinking-guide.md", "reason": "Cross-layer contract checklist for protocol/backend/frontend changes"}
{"file": "AGENTS.md", "reason": "Project routing, protocol-source-of-truth, and git safety constraints"}
{"file": "backend/AGENTS.md", "reason": "Backend schema/tooling/runtime rules"}
{"file": "apps/AGENTS.md", "reason": "Frontend AG-UI and shared rendering rules"}
{"file": "backend/src/core/agentscope/tools/tool_config.py", "reason": "Current tool registry and tool name mapping"}
{"file": "backend/src/core/agentscope/tools/toolkit.py", "reason": "Current direct Python tool registration path"}
{"file": "backend/src/v1/agent/system_agents_config.py", "reason": "Worker enabled_tools loading from system agent config"}
{"file": "backend/src/schemas/agent/runtime_models.py", "reason": "Current worker output contract includes ui_hints"}
{"file": "backend/src/core/agentscope/runtime/stage_emitter.py", "reason": "Current TEXT_MESSAGE_END payload still emits ui_hints"}
{"file": "backend/src/v1/agent/utils.py", "reason": "History path still compiles ui_hints into ui_schema"}
{"file": "apps/lib/core/chat/ag_ui_event.dart", "reason": "Frontend event/history parser consumes ui_schema from backend"}
{"file": "apps/lib/shared/widgets/ui_schema/ui_schema_renderer.dart", "reason": "Current UI renderer consumes compiled ui_schema"}
{"file": "docs/protocols/ui/ui-schema.md", "reason": "UI schema protocol source of truth"}
{"file": "docs/protocols/ui/data-flow.md", "reason": "Current worker ui_hints -> ui_schema data-flow documentation"}
{"file": "docs/protocols/agent/sse-events.md", "reason": "SSE payload contract for worker and tool events"}
{"file": "docs/protocols/agent/run-agent-input.md", "reason": "Run-agent contract and ui_schema references"}
{"file": "docs/protocols/agent/api-endpoints.md", "reason": "HTTP endpoint contract for history and events payloads"}
@@ -0,0 +1,410 @@
# AgentScope Skill + CLI Tool Refactor Implementation Checklist
## Purpose
This file is the execution checklist for implementing the PRD in:
- `.trellis/tasks/04-20-refactor-tool-cli-skill-ui-schema/prd.md`
Use this document as the working guide during implementation.
Do not mark an item complete until the code, docs, and verification for that item are actually done.
## Required Standards Read Before Backend Changes
- [x] Read `backend/AGENTS.md`
- [x] Read `.trellis/spec/backend/index.md`
- [x] Read `.trellis/spec/backend/database-guidelines.md`
- [x] Read `.trellis/spec/backend/error-handling.md`
- [x] Read `.trellis/spec/backend/logging-guidelines.md`
- [x] Read `.trellis/spec/backend/quality-guidelines.md`
- [x] Confirm `.trellis/spec/backend/type-safety.md` does not exist; use current backend schema/type rules from `backend/AGENTS.md` and repository code as the effective type-safety baseline
## Non-Negotiable Constraints
- [x] Protocol docs are updated before implementation changes that alter contracts
- [ ] New backend runtime code reads configuration only through `core.config.settings`
- [ ] New backend runtime code uses project logging, never `print()`
- [ ] New backend errors follow RFC 7807 with stable `code`
- [ ] Any new or changed error codes are updated in `docs/protocols/common/http-error-codes.md`
- [ ] Repository/service layering remains intact
- [ ] `owner_id` is never treated as a credential
- [ ] No new error swallowing is introduced
- [x] `ToolAgentOutput.result` remains the canonical machine-oriented tool result field
## Execution Order
- [ ] Phase 0 completed before any runtime contract change is implemented
- [x] Phase 1 completed before replacing tool execution with CLI-backed wrappers
- [ ] Phase 2 completed before auth credential transport is wired into queue/runtime
- [ ] Phase 3 completed before frontend contract alignment begins
- [ ] Phase 4 completed before cleanup is considered done
- [ ] Phase 5 verification completed before task is marked finished
## Phase 0: Protocol Docs First
### 0.1 Define the tool protocol source of truth
- [x] Add `docs/protocols/agent/tool-protocol.md`
- [x] Document that CLI execution produces structured `result` as the source payload
- [x] Document that `ToolResponse` only carries the text projection of `result`
- [x] Document that runtime tool post-processing reconstructs full `ToolAgentOutput`
- [x] Document that tool post-processing is responsible for `status`, `error`, and `ui_hints`
- [x] Document that `message.content` is the full JSON text projection of `ToolAgentOutput.result`
- [x] Document that `ToolAgentOutput` is used for SSE, persistence, history recovery, and context rebuild
- [x] Document CLI input channel split: `argv` primary, `stdin` secondary, environment variables for controlled auth injection
- [x] Document stdout JSON shape and non-zero exit semantics
- [x] Document that shell execution is not exposed; router is whitelist-only
### 0.2 Remove `ui_schema` from active protocol
- [x] Update `docs/protocols/ui/data-flow.md`
- [x] Replace worker-driven UI source descriptions with tool-driven `ui_hints`
- [x] Explicitly document that worker output no longer includes `ui_hints`
- [x] Explicitly document that history tool UI recovery reads `metadata.tool_agent_output.ui_hints` and compiles to `ui_schema`
- [x] Update `docs/protocols/ui/ui-schema.md`
- [x] Clarify that `ui_hints` is the descriptive UI representation (source), `ui_schema` is the rendered format (wire format)
- [x] Clarify that frontend renderer continues to consume `ui_schema`
- [x] Document that `ui_hints → ui_schema` compilation path remains unchanged, only `ui_hints` source changes
### 0.3 Update SSE and HTTP contracts
- [x] Update `docs/protocols/agent/sse-events.md`
- [x] Remove worker `ui_hints` from `TEXT_MESSAGE_END`
- [x] Define `TOOL_CALL_RESULT` payload with `ui_schema` (compiled from `ui_hints`)
- [x] Document that `ui_hints → ui_schema` compilation happens in backend codec
- [x] Provide examples where `result` is object-shaped instead of string-shaped
- [x] Update `docs/protocols/agent/api-endpoints.md`
- [x] Define `/history` response contract for tool UI replay from `metadata.tool_agent_output.ui_hints` compiled to `ui_schema`
- [x] Remove any statement that `/history` is assistant-only UI-wise if tool UI replay is now supported
- [x] Update `docs/protocols/agent/run-agent-input.md`
- [x] Clarify that frontend does not submit auth token as tool arg
- [x] Clarify that backend-controlled tool registration remains backend-owned
### 0.4 Update auth and automation protocol docs
- [x] Update `docs/protocols/models/auth.md`
- [x] Define controlled credential purpose, TTL, scope, and audit expectations
- [x] Define relationship between normal bearer issuer and automation credential issuer
- [x] Update `docs/protocols/models/automation-jobs.md`
- [x] State that `owner_id` is only an identity reference, not a credential
- [x] Document automation credential issuance path before queue/runtime execution
- [x] Update `docs/protocols/common/http-error-codes.md` if new codes are introduced for CLI/runtime/credential failures
### 0.5 Phase 0 verification
- [x] Confirm protocol docs no longer describe worker `ui_hints` as UI source
- [x] Confirm protocol docs explicitly document `ui_hints → ui_schema` compilation path
- [x] Confirm docs explicitly define ToolResponse vs ToolAgentOutput responsibility split
- [x] Confirm docs explicitly define `/history` tool UI replay path (from `ui_hints` compiled to `ui_schema`)
- [x] Confirm docs explicitly define controlled credential transport and TTL
## Phase 1: Backend Contract Models And Persistence Path
### 1.1 Refactor runtime schemas
- [x] Update `backend/src/schemas/agent/runtime_models.py`
- [x] Remove `WorkerAgentOutputRich.ui_hints`
- [x] Remove `AgentOutput` inheritance that depends on worker UI payload
- [x] Make `resolve_worker_output_model()` return the non-UI worker output model path
- [x] Change `ToolAgentOutput.result` from `str` to JSON-native structured payload type
- [x] Add `ui_hints` to `ToolAgentOutput`
- [x] Keep `ToolAgentOutput` strict with `extra="forbid"`
- [x] Review any validator changes required to keep result deterministic and JSON-native
### 1.2 Update chat message metadata schema consumers
- [x] Review `backend/src/schemas/domain/chat_message.py`
- [x] Ensure `tool_agent_output` accepts the updated structured `ToolAgentOutput`
- [x] Confirm metadata serialization remains compatible with persistence and context cache usage
### 1.3 Separate ToolResponse from ToolAgentOutput
- [x] Update `backend/src/core/agentscope/tools/utils/tool_response_builder.py`
- [x] Stop serializing full `ToolAgentOutput` directly into `ToolResponse.content`
- [x] Make `build_tool_response()` emit only the text projection of `result`
- [x] Decide and implement the helper that projects structured `result` to stable JSON text
- [x] Update error response builder to follow the same split cleanly
### 1.4 Add tool post-processing path
- [x] Introduce a runtime tool post-processing module in backend tool/runtime layer
- [x] Define the post-processor input contract from raw tool execution result
- [x] Define the post-processor output as full `ToolAgentOutput`
- [x] Ensure post-processor is the only place generating `ui_hints` for tools
- [x] Ensure worker code does not generate tool UI fields anymore
### 1.5 Update parsing and stage emission
- [x] Update `backend/src/core/agentscope/utils/parsing.py`
- [x] Stop assuming text blocks contain full serialized `ToolAgentOutput`
- [x] Add helpers to parse the text projection back into structured result where required
- [x] Update `backend/src/core/agentscope/runtime/stage_emitter.py`
- [x] Remove worker `ui_hints` emission from final text events
- [x] Emit `TOOL_CALL_RESULT` based on full post-processed `ToolAgentOutput`
- [x] Ensure emitted tool payload carries structured `result` and `ui_hints`
### 1.6 Update AG-UI codec and event storage
- [x] Update `backend/src/core/agentscope/events/agui_codec.py`
- [x] Remove worker `ui_hints -> ui_schema` compilation path
- [x] Remove `ui_schema`-specific output shaping
- [x] Ensure tool events pass through tool-derived `ui_hints`
- [x] Update `backend/src/core/agentscope/events/store.py`
- [x] Persist tool message `content` as the JSON text projection of `result`
- [x] Persist full post-processed `ToolAgentOutput` in metadata
- [x] Ensure worker metadata no longer expects `ui_hints`
### 1.7 Unify cold/hot runtime paths
- [x] Update `backend/src/core/agentscope/runtime/tasks.py`
- [x] Replace `_serialize_tool_agent_output()` assumptions that rely on old `ToolAgentOutput` shape
- [x] Ensure context rebuild uses the same content projection rule as hot-path execution
- [x] Stop rebuilding tool context from legacy string-only result assumptions
- [x] Review `backend/src/core/agentscope/caches/context_messages_cache.py`
- [x] Define whether old cache payloads are backward-read compatible or intentionally invalidated
- [x] Ensure runtime cold path and hot path see the same tool message shape
### 1.8 Update `/history` backend shaping
- [x] Update `backend/src/v1/agent/utils.py`
- [x] Remove worker `ui_hints` compilation logic
- [x] Stop returning `ui_schema`
- [x] Add tool UI replay logic from `metadata.tool_agent_output.ui_hints`
- [x] Keep user attachment handling intact
- [x] Update `backend/src/v1/agent/schemas.py`
- [x] Remove `UiSchemaRenderer` dependency from `HistoryMessage`
- [x] Redefine history response shape to carry tool UI replay payload
- [x] Update role constraints if tool-derived history items need explicit representation
- [x] Review `backend/src/v1/agent/repository.py` for any history query assumptions that prevent tool UI replay
### 1.9 Phase 1 verification
- [x] Unit tests cover `ToolAgentOutput.result` as structured payload
- [x] Unit tests confirm worker output schema no longer includes `ui_hints`
- [x] Unit tests confirm ToolResponse no longer embeds full ToolAgentOutput
- [x] Unit tests confirm event store persists full ToolAgentOutput metadata and projected content separately
- [x] Unit tests confirm `/history` shaping no longer emits `ui_schema`
- [x] Unit tests confirm tool UI replay uses `metadata.tool_agent_output.ui_hints`
## Phase 2: CLI-Backed Tools And Skill Registration
### 2.1 Replace direct Python tool registration
- [x] Update `backend/src/core/agentscope/tools/tool_config.py`
- [x] Replace function-name-centric mapping with CLI capability/wrapper-centric mapping
- [x] Unify config and runtime skill selection on `enabled_skills`
- [x] Keep approval config support aligned with the new tool names
- [x] Update `backend/src/core/agentscope/tools/toolkit.py`
- [x] Remove direct imports of `custom/calendar.py`, `custom/memory.py`, `custom/user_lookup.py`
- [x] Register CLI-backed wrappers instead of Python business functions
- [x] Preserve `enabled_skills` filtering behavior
### 2.2 Add CLI adapter, router, and entrypoint
- [x] Add a CLI adapter module in `backend/src/core/agentscope/tools/`
- [x] Adapter must invoke only the project CLI entrypoint
- [x] Adapter must pass args via `argv` primarily and `stdin` secondarily where required
- [x] Adapter must inject auth credential only via controlled environment variables
- [x] Adapter must parse stdout JSON and map failures to structured errors
- [x] Add a command router module in `backend/src/core/agentscope/tools/`
- [x] Router must be whitelist-only
- [x] Router must map commands to Python handlers
- [x] Router must not expose generic shell execution
- [x] Add a Python console entrypoint module in `backend/src/core/agentscope/tools/`
- [x] Update `pyproject.toml` with the console script entry
### 2.3 Migrate tool implementations to CLI handlers
- [x] Replace old `backend/src/core/agentscope/tools/custom/*.py` direct runtime tools with CLI handler implementations
- [x] Remove old direct AgentScope tool-function implementations from final runtime wiring
- [x] Ensure new handlers only call allowed internal services/repositories
- [x] Ensure handler boundaries follow schema -> repository -> service layering
- [x] Ensure handlers raise typed errors instead of transport exceptions where applicable
### 2.4 Register AgentScope skills
- [x] Populate `backend/src/core/agentscope/tools/custom` with skill assets using AgentScope-native layout
- [x] Add required `SKILL.md` files
- [x] Ensure skill content explains when to use each tool and how to compose them
- [x] Register skills through AgentScope-native registration path in toolkit/runtime setup
- [x] Ensure skill assets are included in runtime/deployment packaging
### 2.5 Update runner and middleware linkages
- [x] Update `backend/src/core/agentscope/runtime/runner.py`
- [x] Build toolkit from CLI-backed wrappers instead of Python functions
- [x] Keep `enabled_skills` and stage-based selection behavior intact
- [x] Update `backend/src/core/agentscope/tools/tool_middleware.py`
- [x] Ensure middleware name resolution still works with the new tool registration path
- [x] Update `backend/src/core/agentscope/prompts/agent_prompt.py`
- [x] Remove any prompt assumptions that still act as pseudo-skill behavior
- [x] Keep prompt aligned with skill-driven disclosure instead of duplicating the full tool contract
### 2.6 Phase 2 verification
- [x] Unit tests cover CLI adapter success path
- [x] Unit tests cover CLI adapter malformed stdout path
- [x] Unit tests cover CLI adapter non-zero exit path
- [x] Unit tests confirm toolkit only registers enabled CLI-backed tools
- [x] Unit tests confirm middleware still recognizes the active tool names
- [x] Smoke test confirms AgentScope skill registration succeeds from project skill assets
## Phase 3: Controlled Credential And Queue Transport
### 3.1 Define backend auth runtime objects
- [x] Review `backend/src/core/auth/models.py`
- [x] Add any missing auth runtime model needed for controlled credential transport
- [x] Keep `CurrentUser` as identity model if still appropriate, but do not overload it as credential carrier without an explicit design
### 3.2 Add controlled credential issuance path
- [x] Add a credential issuer service under `backend/src/core/auth/` or another appropriate auth module
- [x] Keep issuer in the same trust boundary as current bearer token issuing system
- [x] Ensure issued credential is short-lived according to PRD target
- [x] Ensure issuer encodes only the minimal scope required for tool execution
- [x] Ensure logs do not expose raw credentials
### 3.3 Wire chat enqueue path
- [x] Update `backend/src/v1/agent/service.py`
- [x] Stop enqueueing only `owner_id` for runtime auth purposes
- [x] Enqueue the controlled credential or resolvable credential handle required by worker runtime
- [x] Ensure queue payload does not expose raw token in model-visible fields
- [x] Keep session ownership checks intact
### 3.4 Wire automation dispatch path
- [x] Update `backend/src/core/automation/scheduler.py`
- [x] Stop creating runtime auth solely as `CurrentUser(id=owner_id)`
- [x] Issue or obtain automation controlled credential before enqueueing run
- [x] Ensure `owner_id` remains only a lookup/reference input
- [x] Ensure automation runtime uses the same CLI auth injection mechanism as chat runtime
### 3.5 Update task runtime injection
- [x] Update `backend/src/core/agentscope/runtime/tasks.py`
- [x] Read controlled credential from queued command payload
- [x] Inject controlled credential into CLI runtime environment variables
- [x] Remove any path that implicitly depends on `owner_id` as execution credential
- [x] Keep user-context loading behavior explicit and separate from auth credential handling
### 3.6 Add settings and error mapping
- [x] Update `backend/src/core/config/settings.py` for any new CLI/credential configuration
- [x] Keep new config values typed and centralized
- [x] Update error handling paths to use stable problem codes for credential/CLI failures
- [x] Update docs/protocols/common/http-error-codes.md if these codes are new
### 3.7 Phase 3 verification
- [x] Unit tests confirm chat enqueue includes required controlled credential transport data
- [x] Unit tests confirm automation dispatch no longer relies on `owner_id` as credential
- [x] Unit tests confirm task runtime injects controlled credential only via env vars
- [x] Unit tests confirm credential issuance TTL and scope constraints
- [x] Logs and error payloads do not expose raw credentials
## Phase 4: Frontend Contract Alignment
### 4.1 Update event parsing
- [x] Update `apps/lib/core/chat/ag_ui_event.dart`
- [x] Remove active wire parsing paths that depend on `ui_schema`
- [x] Parse tool event `ui_hints` directly from updated payload contract
- [x] Parse structured `result` instead of string-only assumptions
### 4.2 Update history parsing and cache
- [x] Update `apps/lib/core/chat/chat_history_repository.dart`
- [x] Align cached history format with the new backend history response shape
- [x] Ensure history replay can rebuild tool UI items from backend-provided tool metadata/UI payload
### 4.3 Update chat service and item models
- [x] Update `apps/lib/core/chat/ag_ui_service.dart`
- [x] Ensure SSE handling matches the new tool event contract
- [x] Update `apps/lib/core/chat/chat_list_item.dart`
- [x] Remove item model assumptions that a rendered UI payload must be named `uiSchema`
### 4.4 Update rendering path
- [x] Update `apps/lib/features/chat/presentation/bloc/chat_bloc_events.dart`
- [x] Ensure tool results become visible UI items through direct tool payloads
- [x] Update `apps/lib/features/home/presentation/widgets/home_chat_item_renderer.dart`
- [x] Continue reusing the existing renderer component if it still fits the new input shape
- [x] Update `apps/lib/shared/widgets/ui_schema/ui_schema_renderer.dart` only as needed to accept the new direct tool UI input contract
### 4.5 Phase 4 verification
- [x] Frontend tests confirm SSE tool event parsing without `ui_schema`
- [x] Frontend tests confirm history replay rebuilds tool UI correctly
- [x] Frontend tests confirm refresh/reload still shows prior tool UI consistently
## Phase 5: Cleanup, Regression Tests, And Final Validation
### 5.1 Backend test updates
- [x] Update `backend/tests/unit/core/agentscope/events/test_store.py`
- [x] Update `backend/tests/unit/core/agentscope/events/test_agui_codec.py`
- [x] Update `backend/tests/unit/core/agentscope/runtime/test_stage_emitter.py`
- [x] Update `backend/tests/unit/core/agentscope/runtime/test_tasks.py`
- [x] Update `backend/tests/unit/v1/agent/test_utils.py`
- [x] Update `backend/tests/unit/schemas/agent/test_runtime_models.py`
- [x] Add tests for CLI adapter, command router, and tool post-processing
- [x] Add tests for controlled credential issuance and queue transport
### 5.2 Frontend test updates
- [x] Update `apps/test/core/chat/ag_ui_event_test.dart`
- [x] Update `apps/test/features/chat/presentation/bloc/chat_bloc_test.dart`
- [x] Add tests for history repository if needed by the new replay contract
### 5.3 Remove obsolete code paths
- [x] Remove worker `ui_hints` usage from runtime/event/history code paths
- [x] Remove active `ui_schema` contract usage from backend response shaping (N/A - ui_schema is still used as wire format)
- [x] Remove old direct `custom/*.py` tool runtime wiring
- [x] Remove any parsing logic that assumes `ToolResponse` carries full ToolAgentOutput JSON
- [x] Remove dead compatibility helpers only after replacement path is verified
### 5.4 Run verification commands
- [x] Run relevant backend unit tests with `uv run pytest ...`
- [x] Run relevant frontend tests
- [x] Run backend lint checks required for touched files
- [x] Run backend type checks required for touched files
- [x] If skill registration/package wiring changed, run a focused smoke check of the CLI-backed tool path
### 5.5 Final acceptance audit against PRD
- [x] `ui_hints → ui_schema` compilation path is preserved (only `ui_hints` source changes from worker to tool)
- [x] `WorkerAgentOutput` no longer has `ui_hints`
- [x] `/history` tool UI replay compiles `metadata.tool_agent_output.ui_hints` to `ui_schema`
- [x] `ToolResponse` carries only projected result text
- [x] Tool post-processor generates full `ToolAgentOutput`
- [x] `ToolAgentOutput.result` is structured and machine-oriented
- [x] `message.content` is the full JSON text projection of `result`
- [x] CLI uses whitelist router and no shell execution path
- [x] Chat and automation both use controlled credential injection, not `owner_id` as credential
- [x] AgentScope skills are registered from project skill assets
- [x] Hot path and cold path tool context are unified
- [x] Frontend receives `ui_schema` from `TOOL_CALL_RESULT` and history
- [x] Relevant docs, tests, lint, and type checks are updated
## Suggested First Implementation Slice
- [ ] Complete Phase 0 only
- [ ] Do not start backend runtime refactor until Phase 0 contract text is committed and reviewed
## Progress Log
- [x] Phase 0 complete
- [x] Phase 1 complete
- [x] Phase 2 complete
- [x] Phase 3 complete
- [x] Phase 4 complete
- [x] Phase 5 complete
- [x] 2026-04-23: finished frontend cleanup for legacy tool-call interim events/cards; tool UI render path is now `TOOL_CALL_RESULT` + history replay only
- [x] 2026-04-23: documented `messages.content` decision to remain `text` (structured payload stays in metadata)
- [x] 2026-04-23: removed CLI alias compatibility and switched to canonical subcommands (`calendar.create/read/update/delete/share`, `contacts.read`, `memory.update`)
- [x] 2026-04-23: expanded protocol and postprocessor policy so canonical CRUD commands emit `ui_hints` (`calendar.create/read/update/delete`, `contacts.read`, `memory.update`)
@@ -0,0 +1,738 @@
# AgentScope Skill + CLI Tool Protocol Refactor PRD
## 1. Goal
本次重构的目标不是发明一套新的 agent 工具体系,而是把当前项目的工具执行、skill 使用、UI 生成这三条链路收敛到一条一致的协议路径上:
1. 以 AgentScope 原生 `tools``skills` 能力为基础。
2. 将当前项目工具包装为符合 AgentScope 工具协议的 CLI 工具。
3. skill 不负责执行工具,而是负责向 agent 渐进式披露"有哪些能力、什么时候用、怎么用"。
4. CLI 工具的 runtime 认证边界以用户登录 token 为主,默认通过受控环境变量注入,而不是直接注入数据库 session / owner_id。
5. worker agent output 不再输出 `ui_hints`
6. tool output 继续保留独立结构化协议对象,并可携带 `ui_hints` 给前端渲染使用。
7. worker 在 ReAct loop 中消费的工具信息应收敛为 tool message `content`,而不是完整 `ToolAgentOutput` 元数据对象。
8. `message.content` 继续保持文本投影,不升级为通用 schema 字段;结构化结果进入 `ToolAgentOutput.result`
9. `ToolResponse` 只返回 `result` 文本投影;再由 tool 后处理器把 `result` 解析/补全为完整 `ToolAgentOutput`,包括 `ui_hints` 等字段。
本次改造的核心收益:
- 减少 worker 输出 token 成本
- 消除 UI 在 worker / backend compiler / frontend renderer 三处漂移
- 让工具边界对 AgentScope、skill、前端渲染都更稳定
## 2. Corrected Design Principles
以下原则是本 PRD 的前提,必须严格遵守:
1. AgentScope 原生支持 `skills`
- 官方 README 明确列出 built-in support for `tools, skills`
- 官方示例提供 `toolkit.register_agent_skill(...)`
2. skill 是给 agent 的能力说明与工作流知识,不是业务执行 transport。
3. 如果工具改成 CLICLI 仍然必须服从 AgentScope 的工具协议,而不是项目自己随意定义。
4. preset/runtime 参数应该以认证凭证为边界,当前项目应使用 Bearer token,并默认通过受控环境变量注入 CLI 执行环境,而不是把 DB session 直接暴露给工具调用接口。
5. worker output 和 tool output 的协议职责必须分离,不能混成同一套字段语义。
6. 协议文档先行,`docs/protocols/**` 仍是项目单一协议真源。
7. 本项目的"CLI"是受限命令行工具,不是通用 bash/shell 执行能力。
## 3. Confirmed External Facts
### 3.1 AgentScope 对 Skills 的官方支持
已确认的外部事实:
- AgentScope 官方 README 明确写有 built-in support for `skills`
- 官方示例 `examples/functionality/agent_skill/main.py` 使用:
- `toolkit.register_agent_skill(...)`
- 官方示例同时说明:
- 使用 agent skill 时,agent 需要具备查看文本文件等基础工具能力
- skill 目录通过 `SKILL.md` 进行渐进式披露
已确认的 skill 示例结构:
- `SKILL.md` 包含 frontmatter`name``description`
- skill 内容描述:
- 适用场景
- 推荐工作流
- 需要执行的脚本/命令
- 何时读取补充材料
结论:本项目不应绕开 AgentScope skill 能力自行设计一套"伪 skill"机制。
### 3.2 AgentScope 工具协议的确认边界
已确认的 AgentScope 工具使用方式:
- `Toolkit.register_tool_function(...)` 注册 callable function
- tool 返回 `ToolResponse`
- `ToolResponse.content` 可承载 `TextBlock`
- toolkit 支持 `preset_kwargs`
这意味着如果本项目改成 CLI 路径,落点应是:
- 对 agent 来说,固定暴露为单一 AgentScope tool`project_cli`
- 对 tool 的内部实现来说,它可以调用 CLI
- CLI 的输入输出要能被稳定转换为 AgentScope `ToolResponse`
## 4. Confirmed Repository Facts
### 4.1 当前工具执行方式
当前项目仍然是 Python 函数直接注册多个工具:
- `backend/src/core/agentscope/tools/toolkit.py`
- `TOOL_FUNCTIONS` 直接绑定:
- `calendar_read`
- `calendar_write`
- `calendar_share`
- `user_lookup`
- `memory_write`
- `memory_forget`
- `build_toolkit()` 通过 `toolkit.register_tool_function(...)` 注册这些函数
- `backend/src/core/agentscope/tools/tool_config.py`
- 现统一使用 `enabled_skills``calendar|contacts|memory`)做能力白名单
- `backend/src/core/agentscope/runtime/runner.py`
- `_build_toolkit()` 根据 `enabled_skills` 选择技能白名单
结论:当前实现尚未接入 AgentScope skill,也尚未接入 CLI 工具边界。
### 4.2 当前项目中的认证边界
当前项目已有明确的 Bearer token 用户解析路径:
- `backend/src/v1/users/dependencies.py`
- `get_current_user()``Authorization: Bearer <token>` 解析 token
- 先走 JWT verifier,再 fallback 到 Supabase user lookup
- 最终产出 `CurrentUser`
- `backend/src/core/auth/models.py`
- `CurrentUser` 当前字段:`id``phone``role`
结论:如果工具改为 CLI,runtime 的合理边界应是 token 或 auth credential,而不是 DB session / owner_id。token 默认通过受控环境变量注入 CLI 运行环境,CLI 内部再根据 token 推导用户身份和数据访问上下文。
补充约束:
- 不能仅凭 `owner_id` 直接构造用户 Bearer token`owner_id` 不是签名凭证。
- automation 场景由当前 Bearer token 的签发方负责基于 `automation_jobs.owner_id` 签发短期受控凭证;若现有签发链路不支持,则补一个服务端签发函数。
- 当前假设该签发方与用户登录 Bearer token 的签发体系一致;现有若仅支持"邮箱 + 验证码"登录签发,则在同一信任边界下新增 automation 凭证签发能力。
- automation 受控凭证生命周期目标为 5-10 分钟,只覆盖 agent 调用工具所需窗口。
- 该凭证的签发方式、有效期、权限边界和审计规则必须在协议文档中写清。
### 4.3 当前 worker output 与 UI 路径
当前 worker output 仍然包含 UI 描述:
- `backend/src/schemas/agent/runtime_models.py`
- `WorkerAgentOutputRich.ui_hints`
当前 runtime 会把 `ui_hints` 带入最终文本事件:
- `backend/src/core/agentscope/runtime/stage_emitter.py`
- `emit_final_text_end()``ui_hints` 写入 payload
当前对外 AG-UI payload 会把 `ui_hints` 编译为 `ui_schema`
- `backend/src/core/agentscope/events/agui_codec.py`
- `TEXT_MESSAGE_END` 上执行 `compile_ui_hints(...)`
当前 history 回放同样依赖 `ui_hints -> ui_schema`
- `backend/src/v1/agent/utils.py`
- `backend/src/v1/agent/schemas.py`
结论:当前 `ui_schema` 的真源是 worker 自身输出的 `ui_hints`。本次改造将把 `ui_hints` 的来源从 worker output 改为 tool output`ui_hints → ui_schema` 的编译链路保持不变。
### 4.4 当前前端消费方式
前端当前消费的是后端下发的编译后 `ui_schema`
- `apps/lib/core/chat/ag_ui_event.dart`
- `apps/lib/core/chat/chat_history_repository.dart`
- `apps/lib/shared/widgets/ui_schema/ui_schema_renderer.dart`
- `apps/lib/features/home/presentation/widgets/home_chat_item_renderer.dart`
结论:前端目前理解的是后端已编译好的 `ui_schema`。本次改造目标不是改变前端消费方式,而是改变 `ui_hints` 的来源(从 worker 改为 tool),`ui_hints → ui_schema` 编译链路保持不变。
### 4.5 当前 tool result 结构
当前 tool result 已经走 machine-oriented 路线,但结构还不完整:
- `backend/src/core/agentscope/tools/utils/tool_response_builder.py`
- tool 最终返回 `ToolResponse`
- 当前 `ToolResponse` 中仍混入完整 tool 结构,不符合目标边界
- `backend/src/core/agentscope/utils/parsing.py`
- 从 ToolResponse text block 中恢复 `ToolAgentOutput`
- `backend/src/core/agentscope/runtime/stage_emitter.py`
- `TOOL_CALL_RESULT` payload 包含 `tool_name``tool_call_args``status``result`
当前仓库已经存在"给 agent 的文本内容"和"给事件/持久化的完整结构化对象"分离的模式:
- `backend/src/core/agentscope/events/store.py`
- tool 消息持久化时:`content = tool_output.result`
- 完整结构化对象进入 `metadata.tool_agent_output`
- `backend/tests/unit/core/agentscope/events/test_store.py`
- 明确校验 tool message `content` 等于文本结果
- 同时完整 `tool_agent_output` 保存在 metadata 中
结论:当前仓库已经证明一个正确的职责分层:
- worker/上下文消费的应是 tool message `content`
- AG-UI 事件、持久化和前端渲染可以保留完整 `ToolAgentOutput`
但当前仍有三个缺口:
- `ToolAgentOutput` 还没有承载前端所需 `ui_hints`
- `ToolAgentOutput.result` 仍是字符串,还没有升级为结构化对象字段
- `ToolResponse` 还没有收敛为只承载 `result` 文本投影,再交给 tool 后处理器生成完整 `ToolAgentOutput`
此外,history/replay 的正确恢复方向也已经比较明确:
- 可以像 worker output 一样,从 `message.metadata` 中恢复结构化对象
- 对 tool 来说,恢复源应是 `metadata.tool_agent_output.ui_hints`
## 5. Problem Statement
当前实现存在 5 个核心问题:
1. 工具实现直接耦合在 Python runtime 函数注册层,缺少独立协议边界。
2. skill 尚未成为 agent 的原生能力入口,工具知识仍主要依赖 prompt 和函数 docstring。
3. worker 仍承担 UI 描述职责,导致 `ui_hints` 占 token,且与前后端实现容易漂移。
4. tool result 已经拥有独立结构化对象,但没有成为 UI 渲染主路径。
5. 当前 tool output 协议没有明确区分"给 worker 推理的 content"和"给前端渲染的完整结构化输出"。
6. 当前 CLI 工具边界尚未定义为受限命令行工具,容易和通用 shell 执行概念混淆。
## 6. Target Architecture
### 6.1 总体方向
目标架构应遵循下面的职责分层:
1. AgentScope skill
- 负责告诉 agent
- 有哪些工具能力
- 什么时候调用
- 调用前后如何决策
- 不直接承担业务执行
2. AgentScope tool
- agent 固定只看到一个工具入口:`project_cli`
- 内部执行方式为结构化 `command + subcommand + args` -> 调用项目 CLI -> 适配回 ToolResponse
3. CLI tool
- 是业务执行边界
- 输入输出必须稳定、结构化、可验证
- 采用经典命令树:`command + subcommand + args`
- 不按 `skill + action` 建模 CLI
- 必须符合 AgentScope 工具协议适配需要
- 必须是受限项目命令行工具,不提供通用 bash/shell 能力
4. Tool output protocol
- 保留独立的 `ToolAgentOutput` 类协议对象
- 对 AG-UI 事件、持久化元数据、前端渲染负责
- 可包含 `ui_hints` 这类前端渲染字段
5. UI contract
- 由 tool output 中的 `ui_hints` 编译生成 `ui_schema`
- `ui_schema` 成为前端渲染的输入
- worker output 不再输出 `ui_hints`
### 6.2 Token / 受控凭证作为 runtime 注入参数
本项目要求将 Bearer token 或受控服务端凭证作为 tool runtime 注入参数,而不是 DB 级参数。
目标边界:
- AgentScope tool 被调用时,runtime 将认证 token 或受控服务端凭证注入 CLI 执行环境
- CLI 接收到 token 后:
- 验证 token
- 解析用户身份
- 推导可访问数据范围
- 再进入 service / repository / DB 层
禁止继续扩大的错误边界:
- 不应把 `AsyncSession` 直接暴露到 CLI 工具调用边界
- 不应把 `owner_id` 当作唯一可信的输入
- 不应让 agent 直接控制数据库上下文对象
当前优先方案:
- token 不作为模型可见的业务参数字段进入 tool args schema
- token 由 runtime 以默认环境变量方式注入到 CLI 子进程
- CLI 只读取受控环境变量,不从自然语言或 tool args 中接受任意 token 字符串
- automation 不直接伪造用户 Bearer token;由当前 token 签发方基于 `automation_jobs.owner_id` 获取或签发 5-10 分钟短期受控凭证后注入
### 6.3 ToolResponse 与 ToolAgentOutput 的边界
本次协议边界明确如下:
- CLI/Tool 原始执行结果先产出结构化 `result`
- AgentScope `ToolResponse` 只承载给 agent 使用的 `result` 文本投影
- 不再把完整 `ToolAgentOutput` 直接塞进 `ToolResponse.content`
- runtime 内增加 tool 后处理器:基于 `result` 解析/补全完整 `ToolAgentOutput`
- 该后处理器负责补齐:
- `tool_name`
- `tool_call_id`
- `tool_call_args`
- `status`
- `result`
- `error`
- `ui_hints`
这样职责分层固定为:
- agent 只看到 `message.content` / `ToolResponse` 中的 `result` 文本投影
- runtime / SSE / history / persistence 使用完整 `ToolAgentOutput`
### 6.4 Worker 与 Tool 输出职责分离
根据当前仓库已存在的运行时与持久化逻辑,本次重构必须明确保持以下分层:
1. worker output
- 面向 worker 最终回答
- 不再包含 `ui_hints`
2. tool output
- 使用 `ToolAgentOutput` 作为结构化协议对象
- 面向 AG-UI 事件、历史持久化、前端渲染
- 可以包含 `ui_hints`
3. tool message content
- 面向 agent ReAct loop 上下文
- 应是工具结构化结果的稳定文本投影
- 不应把完整 `ToolAgentOutput` 字段直接作为下一轮 worker 输入上下文
当前仓库中的支持证据:
- `backend/src/core/agentscope/events/store.py`
- tool message `content = tool_output.result`
- metadata 保留完整 `tool_agent_output`
因此正确方向不是删除 `ToolAgentOutput`,而是:
- 保留 `ToolAgentOutput` 作为独立 tool 协议对象
- 只让 worker 主要消费 tool message `content`
### 6.4 `message.content` 字段策略
本次不建议把 `message.content` 从文本改成通用 schema 字段。
原因:
1. AgentScope/Msg 本身是通用消息抽象,不同 role 的 content 类型已经存在差异,但当前项目的 worker 推理、历史持久化、事件处理都默认把文本内容当作主信息载体。
2. 当前仓库已经有一条可工作的分层:
- `message.content` 给 worker 推理
- `metadata.tool_agent_output` 给结构化消费
3. 若把 `message.content` 直接升级为 schema,会把:
- ReAct loop
- memory/context assembly
- event store
- history replay
- frontend parsing
一起拉进大范围协议改动,收益不成比例。
因此推荐策略是:
- `message.content` 继续保持文本
- 文本内容由 `ToolAgentOutput.result` 结构化对象稳定投影生成
- 结构化真源放在 `ToolAgentOutput.result`
也就是说,后续语义应变成:
- `message.content`: 给 worker 的完整 JSON 文本投影
- `ToolAgentOutput.result`: 给系统使用的结构化对象真源
### 6.5 UI 真源迁移方向
本次改造的目标不是"前端直接裸渲染任意 CLI 参数 schema",而是:
1. 先把 tool output 协议收敛为稳定的结构化 schema
2. 在 tool output 协议中保留前端所需 `ui_hints`
3. 再从该协议导出 AG-UI / history / frontend 统一 contract
4. AG-UI 传输时:后端 codec 将 `ui_hints` 编译为 `ui_schema` 后传输
5. 前端渲染 `ui_schema`(复用现有 `UiSchemaRenderer`
**关键设计决策**
- `ui_hints` 是 tool 输出的描述性 UI 表示(真源)
- `ui_schema` 是编译后的渲染性 UI 表示(传输格式)
- 编译链路 `ui_hints → ui_schema` 保持不变,仅改变 `ui_hints` 的来源
- 前端继续使用现有 `UiSchemaRenderer` 渲染 `ui_schema`
因此本次必须避免两个错误方向:
1. 继续保留 `worker -> ui_hints -> ui_schema`(应改为 `tool -> ui_hints -> ui_schema`
2. 让前端直接绑定 `ui_hints` 而不是 `ui_schema`
3. 把完整 `ToolAgentOutput` 直接塞回 worker 推理输入,而不是让 worker 主要消费 tool message `content`
4. 把"项目 CLI"实现成可执行任意 shell 的通用命令入口
## 7. Required Refactor Requirements
### 7.1 Skill 接入要求
必须:
- 基于 AgentScope 原生 `register_agent_skill(...)` 接入 skill
- 提供项目内 skill 目录与 `SKILL.md`
- skill 内容必须包含:
- 工具能力说明
- 何时调用哪个工具
- 参数构造规则
- 组合调用规则
- 失败后的恢复策略
不允许:
- 用单纯 prompt 文本替代 skill
- 自定义一套与 AgentScope skill 不兼容的 skill 机制
### 7.2 CLI Tool Protocol 要求
CLI 工具必须满足:
- 能被 AgentScope tool wrapper 稳定调用
- 输入参数可从当前 AgentScope tool schema 映射得到
- 输出结果可稳定映射回 `ToolResponse`
- 输出至少包含:
- 状态
- 结构化结果
- 错误信息
- 前端渲染所需的稳定字段
CLI 输出在进入 AgentScope tool wrapper 后,最终需要映射成两层信息:
1. `message.content`
- 给 worker ReAct loop 使用
- 应为结构化 `result` 的稳定文本投影
- 由结构化 `result` 稳定投影生成
- 默认保留完整 JSON 文本投影,不做阉割式摘要裁剪
2. `ToolAgentOutput`
- 给 AG-UI 事件、持久化、前端渲染使用
- 至少包含 `tool_name``tool_call_id``tool_call_args``status``result``error`
- 本次应扩展支持 `ui_hints`
这里的实现约束已经确定:
- `ToolResponse` 只返回 `result` 文本投影
- 完整 `ToolAgentOutput` 由 tool 后处理器在 runtime 内生成
- `ui_hints` 由 tool 后处理器生成并写入 `ToolAgentOutput`
- `ui_hints` 经后端 codec 编译为 `ui_schema` 后传输给前端
- worker 不参与 tool UI 生成
需要显式设计:
- stdin / argv / env 何者承载输入
- token 放在哪个输入通道
- stdout 返回 JSON 的格式
- 非 0 exit code 的错误语义
当前已确认的输入通道策略:
- CLI 输入通道采用"两者结合"
- 默认以 `argv` 为主、`stdin` 为辅
- token 继续通过受控环境变量注入,不进入模型可控参数
### 7.3 Tool Configuration 保留要求
以下配置能力必须保留:
- `enabled_skills` from system agents config
- `enabled_skills` from automation jobs config
允许变化的部分:
- `enabled_skills` 不再映射到 Python 函数名
- 改为映射到 CLI-backed tool capability
### 7.4 Worker Output 精简要求
worker output 必须移除:
- `ui_hints`
worker output 保留:
- `status`
- `answer`
- `suggested_actions`
- `error`
是否增加新的结构化字段,必须以协议文档先定义为前提;禁止再次引入新的"隐式 UI 描述字段"。
### 7.5 Tool Output 协议重构要求
`ToolAgentOutput` 不应被删除,而应被重构为更完整的 tool 协议对象。
至少应满足:
- 保留当前已有字段:
- `tool_name`
- `tool_call_id`
- `tool_call_args`
- `status`
- `result`
- `error`
- 扩展支持:
- `ui_hints`
其中 `result` 应升级为结构化对象字段,不再以字符串为真源。
同时需要新增一条投影规则:
- 从结构化 `result` 生成稳定文本投影,写入 tool message `content`
- 默认使用完整 JSON 投影,保持与 `result` 信息量一致,避免因摘要裁剪削弱模型对结构化数据的读取能力
同时需要固定 tool 后处理规则:
- 后处理器以 tool 原始 `result` 为真源
- 根据 result 解析/补齐 `ToolAgentOutput`
- `ui_hints` 由后处理器生成并写入 `ToolAgentOutput`
- worker 不参与 tool UI 生成
这样可以满足两条不同消费链路:
- worker 继续消费由 `result` 投影生成的 `message.content`
- AG-UI / history / frontend 消费完整 `ToolAgentOutput`
### 7.6 UI Contract 重构要求
必须完成:
- `TEXT_MESSAGE_END` 不再依赖 worker `ui_hints` 编译
- history assistant message 不再依赖 `metadata.agent_output.ui_hints` 编译
- tool result 成为 UI 合约的主要输入之一
- `TOOL_CALL_RESULT` / tool metadata / history replay 能保留 tool `ui_hints`
AG-UI 传递策略调整为:
- tool 事件内部携带 `ui_hints`
- 后端 codec 将 `ui_hints` 编译为 `ui_schema`
- AG-UI 对前端传输 `ui_schema`
- 前端继续复用现有 UI 渲染器渲染 `ui_schema`
- history 恢复时优先从 `message.metadata.tool_agent_output.ui_hints` 读取并编译为 `ui_schema` 后渲染
这里的设计结论已经确定:
- `ui_hints` 是 tool 输出的 UI 描述(真源)
- `ui_schema` 是编译后的渲染格式(传输格式)
- 编译链路 `ui_hints → ui_schema` 保持不变
- 前端继续使用现有 `UiSchemaRenderer` 渲染 `ui_schema`
## 8. Scope Of Changes
### 8.1 Protocol Docs
必须更新:
- `docs/protocols/ui/ui-schema.md`
- `docs/protocols/ui/data-flow.md`
- `docs/protocols/agent/sse-events.md`
- `docs/protocols/agent/run-agent-input.md`
- `docs/protocols/agent/api-endpoints.md`
建议新增:
- 一个工具协议文档,明确:
- CLI 输入输出约定
- token preset 约定
- tool result 到 UI contract 的映射边界
### 8.2 Backend
主要改动点:
- `backend/src/core/agentscope/tools/toolkit.py`
- `backend/src/core/agentscope/tools/tool_config.py`
- `backend/src/core/agentscope/runtime/runner.py`
- `backend/src/core/agentscope/runtime/stage_emitter.py`
- `backend/src/core/agentscope/events/agui_codec.py`
- `backend/src/core/agentscope/events/store.py`
- `backend/src/core/agentscope/runtime/tasks.py`
- `backend/src/core/agentscope/utils/parsing.py`
- `backend/src/schemas/agent/runtime_models.py`
- `backend/src/v1/agent/utils.py`
- `backend/src/v1/agent/schemas.py`
- 新增 skill 资产与 CLI adapter
- 新增 Python console entrypoint 与内嵌 command router
skill 资产目录精确落点:
- `backend/src/core/agentscope/tools/custom`
skill 部署方式:
- 参考 AgentScope skill 提供的原生装载/注册方法进入运行时与部署产物
- 现有 `custom/*.py` 不再保留为最终工具实现,后续由 CLI handler / router 重写替换
### 8.3 Cleanup / 收尾清理
本次改造完成后,必须明确收尾清理以下代码与路径,避免旧协议残留:
1. 删除或下线 worker `ui_hints` 主路径
- `backend/src/schemas/agent/runtime_models.py` 中 worker rich output 的旧定义
- `backend/src/core/agentscope/runtime/stage_emitter.py` 中向 `TEXT_MESSAGE_END` 写入 worker `ui_hints` 的逻辑
- `backend/src/core/agentscope/events/agui_codec.py``TEXT_MESSAGE_END``compile_ui_hints(...)` 主路径
- `backend/src/v1/agent/utils.py` 中 assistant history 读取 worker `ui_hints` 并编译的逻辑
2. 清理旧文档语义
- `docs/protocols/ui/data-flow.md` 中"worker ui_hints 是 UI 真源"的描述
- `docs/protocols/ui/ui-schema.md` 中把 UI compiler 绑定到 worker `ui_hints` 的描述
- `docs/protocols/agent/sse-events.md` / `api-endpoints.md` / `run-agent-input.md` 中与旧 worker UI 路径冲突的描述
3. 清理测试中的旧假设
- 依赖 worker `ui_hints` 的 runtime/event/history 单测
- 依赖 `ToolAgentOutput.result` 为字符串真源的测试
4. 清理旧前端假设
- 只接受 assistant `ui_schema` 而不理解 tool `ui_schema` 的渲染逻辑
- 历史/事件解析中把 assistant UI 当唯一 UI 来源的路径
5. 清理旧工具调用边界
- 继续把 Python runtime 函数当作唯一稳定工具边界的 registry 代码
- 任何会把 DB session / owner_id 直接注入到 CLI 边界的适配代码
-`custom/*.py` 直接工具实现
6. 清理第二条 runtime 冷路径
- `backend/src/core/agentscope/runtime/tasks.py` 中与新 tool output/context rebuild 不一致的旧序列化路径
- 所有会导致热路径、冷路径看到不同 tool 上下文的兼容逻辑
### 8.4 Frontend
主要改动点:
- `apps/lib/core/chat/ag_ui_event.dart`
- `apps/lib/core/chat/chat_history_repository.dart`
- `apps/lib/shared/widgets/ui_schema/ui_schema_renderer.dart`(保持不变,继续渲染 `ui_schema`
- `apps/lib/features/home/presentation/widgets/home_chat_item_renderer.dart`
## 9. Phased Plan
### Phase 1. Protocol Correction
先修正文档中的错误前提:
- 明确 AgentScope skill 是原生能力
- 明确 CLI 是工具实现载体,不是替代 AgentScope tool/skill
- 明确 token 环境变量注入边界
- 明确仅 worker `ui_hints` 将被移除,tool `ui_hints` 保留在独立协议中
- 明确 `ui_hints → ui_schema` 编译链路保持不变
### Phase 2. Skill And Tool Boundary Refactor
- 接入项目内 skill
- 定义单一 `project_cli` AgentScope tool wrapper
- 使用 Pydantic 化的 `enabled_skills` 作为唯一配置字段
- CLI 输入通道采用"两者结合",默认 `argv` 为主、`stdin` 为辅
- 定义 Python console entrypoint 作为外部 CLI 入口
- 定义内嵌 command router 作为唯一分发核心
- router 只注册白名单 `command + subcommand`
- 每个命令绑定 Python handler
- handler 只调用允许的内部能力
- 默认不开放 shell 执行能力
- 移除旧多工具暴露实现,改为 skills 手册 + 单一 CLI 入口
### Phase 3. Auth-Preset Refactor
- 将 tool preset 从 runtime DB context 改为 auth token / 受控凭证
- CLI 内部完成身份解析与数据作用域推导
- token 默认通过受控环境变量进入 CLI,而不是模型可控参数
- automation 基于 `automation_jobs.owner_id` 获取或签发受控凭证,不直接伪造用户 Bearer token
### Phase 4. UI Source Refactor
- 删除 worker `ui_hints` 输出
- 删除 worker `ui_hints -> ui_schema` 编译主路径
- 将 tool `ui_hints` 纳入 `ToolAgentOutput` 协议
-`ToolAgentOutput.result` 升级为结构化对象
- 建立 `result -> message.content` 的稳定完整 JSON 文本投影规则
- 建立 tool output `ui_hints → ui_schema` 的编译路径
### Phase 4.5 Runtime Path Unification
- 改造 `backend/src/core/agentscope/runtime/tasks.py`
- 确保热路径、冷路径、context rebuild 都使用同一套:
- `ToolAgentOutput.result` 结构化对象真源
- `message.content` 完整 JSON 文本投影
- `metadata.tool_agent_output` 作为历史恢复真源
- 移除第二条旧路径,避免同一线程在不同路径下看到不同 tool 上下文
### Phase 5. Frontend Alignment
- 调整事件解析和 history 解析,从 `TOOL_CALL_RESULT.ui_schema` 读取
- 渲染逻辑保持不变,继续使用 `UiSchemaRenderer` 渲染 `ui_schema`
## 10. Acceptance Criteria
- [ ] PRD 与协议文档不再错误描述 AgentScope skill 能力
- [ ] skill 通过 AgentScope 原生能力接入,而不是自定义伪 skill 机制
- [ ] 单一 `project_cli` 能通过 AgentScope tool protocol 被稳定调用
- [ ] 工具 runtime 以 Bearer token / 受控服务端凭证为边界
- [ ] token/凭证 默认以受控环境变量注入 CLI 执行环境
- [ ] CLI 能基于 token/凭证 解析用户身份与数据访问范围
- [ ] automation 能基于 `owner_id` 由当前 token 签发方签发 5-10 分钟受控凭证,但不直接伪造用户 Bearer token
- [ ] `enabled_skills` 能驱动技能手册和允许命令域配置
- [ ] worker output 不再包含 `ui_hints`
- [ ] `ToolAgentOutput` 保留为独立 tool 协议对象,并支持 `ui_hints`
- [ ] worker 在上下文中主要消费 tool message `content/result`,而不是完整 `ToolAgentOutput`
- [ ] `message.content` 保持文本,不升级为通用 schema 字段
- [ ] `ToolAgentOutput.result` 升级为结构化对象真源
- [ ] `message.content` 采用完整 JSON 文本投影,而不是裁剪摘要
- [ ] `ToolResponse` 只承载 `result` 文本投影
- [ ] tool 后处理器能基于 `result` 生成完整 `ToolAgentOutput`(含 `ui_hints`
- [ ] `TEXT_MESSAGE_END` 和 assistant history 不再依赖 worker `ui_hints` 编译 UI
- [ ] history 能直接从 `metadata.tool_agent_output.ui_hints` 恢复 tool UI
- [ ] `runtime/tasks.py` 等冷/热路径已统一,不再存在第二条旧路径
- [ ] 前端继续通过现有 UI 渲染器消费 `ui_schema` 渲染内容
- [ ] `ui_hints → ui_schema` 编译链路保持不变,仅改变 `ui_hints` 来源
- [ ] skill 资产目录已落在 `backend/src/core/agentscope/tools/skills`
- [ ] skill 部署/装载方式与 AgentScope 原生方法保持一致
- [ ] CLI 输入通道已按 `argv` 主、`stdin` 辅的组合模式落地
- [ ]`custom/*.py` 直接工具实现已被 CLI handler / router 替换
## 11. Open Questions
当前 open questions 已收口,不再保留架构级未决项。
已确认结论:
1. `ui_hints` 是 tool 输出的 UI 描述(真源),`ui_schema` 是编译后的渲染格式(传输格式)
2. skill 资产目录最终精确落点为 `backend/src/core/agentscope/tools/custom`
3. skill 部署方式参考 AgentScope skill 提供的原生方法
4. CLI 输入通道采用"两者结合",默认 `argv` 为主、`stdin` 为辅
5. history route 的 tool UI 回放从 `metadata.tool_agent_output.ui_hints` 读取并编译为 `ui_schema`
6.`custom/*.py` 不再保留,工具实现改为 CLI handler / router
7. `ToolResponse` 只返回 `result` 文本投影,完整 `ToolAgentOutput` 由 tool 后处理器生成
## 12. Risks
1. 如果只把工具换成 CLI,但不先定义稳定工具协议,最终仍会把旧耦合问题原样搬过去。
2. 如果前端直接消费 `ui_hints` 而不是 `ui_schema`,会破坏现有渲染链路。
3. 如果把"项目 CLI"误实现为通用 shell/命令执行器,会引入提示词注入和越权风险。
4. 如果 token/凭证 注入边界设计不严谨,容易把认证逻辑散落到多个工具实现中。
5. 当前代码里仍有多处 broad catch,迁移时错误可能被掩盖:
- `backend/src/core/agentscope/events/agui_codec.py`
- `backend/src/v1/agent/utils.py`
- `apps/lib/core/chat/ag_ui_service.dart`
## 13. Definition Of Done
当以下条件全部满足时,本任务完成:
1. 协议文档修正完毕并成为实现依据。
2. AgentScope skill 已在项目内落地并承担工具使用知识披露。
3. 当前工具已切换为单一 `project_cli` + skills 手册架构。
4. token/受控凭证 已通过受控环境变量成为 CLI runtime 的认证边界。
5. worker output 已移除 `ui_hints`
6. `ToolAgentOutput` 已成为独立完整的 tool 协议对象,并承载前端所需 UI hints。
7. `ToolAgentOutput.result` 已升级为结构化对象真源。
8. worker 仅通过完整 JSON 文本投影的 tool message `content` 获取工具结果主信息。
9. history 能通过 `metadata.tool_agent_output.ui_hints` 编译为 `ui_schema` 恢复 tool UI。
10. `runtime/tasks.py` 等冷/热路径已统一,不再存在第二条旧路径。
11. AG-UI 对 tool UI 编译 `ui_hints → ui_schema` 后传输,前端继续复用现有 UI 渲染器。
12. 旧 worker UI 编译路径和相关清理项已完成收尾。
13. 前后端事件/history/渲染链路验证通过。
14. `ui_hints → ui_schema` 编译链路保持不变,仅改变 `ui_hints` 来源。
15. skill 资产已按约定落在 `backend/src/core/agentscope/tools/skills`,并按 AgentScope 原生方式装载。
16. CLI 输入通道已按 `argv` 主、`stdin` 辅的组合模式落地。
17. history route 已从 `metadata.tool_agent_output.ui_hints` 编译为 `ui_schema` 回放 tool UI。
18.`custom/*.py` 直接工具实现已被 CLI handler / router 替换。
19. `ToolResponse` 已收敛为只返回 `result` 文本投影,完整 `ToolAgentOutput` 由 tool 后处理器生成。
@@ -0,0 +1,96 @@
# Debug Session: Tool Credential Injection Issue
## Date
2026-04-22
## Context
After completing the skills+CLI refactor, running live integration tests revealed tool credential injection issues.
## Commits Made
1. `4d55df4` - refactor: unify skills+cli runtime and streamline ag-ui flow
2. `ef931ee` - chore: clean up legacy tool/UI code paths and remove unused events
3. `91077a9` - fix: pass tool_call_id to parse_tool_agent_output for proper payload resolution
## Test Execution
```bash
CLI_SKILLS_LIVE_TEST=1 TEST_USER_ID="f6f4bc6b-f525-434e-81b6-38eeef9b89a8" \
AGENT_LIVE_BASE_URL="http://localhost:5775" \
uv run pytest backend/tests/integration/test_cli_skills_live.py::test_calendar_read_skill_queries_db -v -s
```
## Error Found
From `logs/errors/worker-agent.error.log`:
```
"error": "tool credential not found in runtime context",
"tool_name": "project_cli"
```
Full stack trace shows:
1. `invoke_cli_tool` calls `_resolve_owner_id()`
2. `_resolve_owner_id()` calls `get_tool_credential()`
3. `get_tool_credential()` returns `None`
4. Raises `TokenValidationError("tool credential not found in runtime context")`
## Root Cause Analysis
The tool credential is set via context variable `tool_credential` but is not being injected into the runtime context before tool execution.
### Key Files
- `backend/src/core/auth/tool_credential_context.py` - ContextVar for tool credential
- `backend/src/core/agentscope/tools/cli/adapter.py` - Calls `get_tool_credential()`
- `backend/src/core/agentscope/runtime/runner.py` - Should inject credential before tool execution
### Expected Flow
1. Runner receives run request with `owner_id`
2. Runner creates tool credential using `ToolCredentialIssuer`
3. Runner sets credential via `set_tool_credential(credential)`
4. Tool execution reads via `get_tool_credential()`
5. After execution, credential is cleared
### Missing Implementation
The credential injection logic needs to be added to `runner.py` around the worker stage execution.
## Secondary Error
When tool credential fails, the error response causes a DB insert error:
```
invalid input for query argument $5: {'status': 'failure', ...} (expected str, got dict)
```
This is because `content` field receives a dict instead of str. Fixed in `store.py` by ensuring proper serialization, but the root cause is the missing credential.
## Next Steps
1. Find where tool credential should be set in runtime
2. Add credential issuance in runner before tool execution
3. Ensure credential is passed through task queue or generated in worker
4. Restart backend service with new code
5. Re-run integration tests
## Database State
- `system_agents.config.enabled_skills`: Correctly uses `["calendar", "contacts"]`
- `automation_jobs.config`: No longer has `enabled_tools`
- User ID for testing: `f6f4bc6b-f525-434e-81b6-38eeef9b89a8`
## Files Modified
- `backend/src/core/agentscope/runtime/stage_emitter.py` - Fixed `tool_call_id` passing
- `backend/tests/integration/test_cli_skills_live.py` - Added live integration tests
## Remaining Work
- [ ] Fix tool credential injection in runtime
- [ ] Verify calendar read/write works end-to-end
- [ ] Verify contacts lookup works end-to-end
- [ ] Verify memory write via automation works
- [ ] Run full test suite after fixes
@@ -0,0 +1,86 @@
{
"id": "refactor-tool-cli-skill-ui-schema",
"name": "refactor-tool-cli-skill-ui-schema",
"title": "Refactor agent tools into CLI + skill-driven UI schema flow",
"description": "Wrap current AgentScope tools as CLI commands, add a skill that teaches the agent how to invoke the CLI, replace ui_hints-driven UI generation with tool-schema-driven rendering, and remove worker ui_hints output.",
"status": "in_progress",
"dev_type": "fullstack",
"scope": "cross-domain",
"priority": "P1",
"creator": "qzl",
"assignee": "qzl",
"createdAt": "2026-04-20",
"completedAt": null,
"branch": "worktree/refactor-tool-cli-skill-ui-schema",
"base_branch": "dev",
"worktree_path": ".worktrees/refactor-tool-cli-skill-ui-schema",
"current_phase": 5,
"next_action": [
{
"phase": 5,
"action": "check"
},
{
"phase": 5,
"action": "implement"
}
],
"commit": null,
"pr_url": null,
"subtasks": [
{
"name": "Implement CLI router and handlers",
"status": "completed"
},
{
"name": "Create skill documentation (calendar, contacts, memory)",
"status": "completed"
},
{
"name": "Update protocol docs (tool-protocol.md, ui-schema.md)",
"status": "completed"
},
{
"name": "Update frontend ag_ui_event.dart for tool UI flow",
"status": "completed"
},
{
"name": "Write backend integration tests for CLI tool flow",
"status": "completed"
},
{
"name": "Write frontend tests for UI schema rendering",
"status": "completed"
},
{
"name": "Verify end-to-end agent -> CLI -> UI flow",
"status": "pending"
},
{
"name": "Remove CLI alias compatibility and enforce canonical subcommands",
"status": "completed"
},
{
"name": "Expand ui_hints policy to canonical CRUD commands and refactor postprocessor framework",
"status": "completed"
}
],
"children": [],
"parent": null,
"relatedFiles": [
"backend/src/core/agentscope/tools/tool_config.py",
"backend/src/core/agentscope/tools/toolkit.py",
"backend/src/core/agentscope/runtime/stage_emitter.py",
"backend/src/schemas/agent/runtime_models.py",
"backend/src/v1/agent/utils.py",
"apps/lib/core/chat/ag_ui_event.dart",
"apps/lib/shared/widgets/ui_schema/ui_schema_renderer.dart",
"docs/protocols/ui/ui-schema.md",
"docs/protocols/ui/data-flow.md",
"docs/protocols/agent/sse-events.md"
],
"notes": "Core refactor is complete and protocol/docs/tests are aligned. CLI now uses canonical subcommands only (no alias compatibility). ui_hints policy follows canonical CRUD commands with a common postprocessor framework.",
"meta": {
"feature_summary": "tool refactor + CLI wrapping + skill guidance + tool-schema rendered UI + worker output simplification"
}
}