Merge branch 'master' into worktree/llm-mock-fault-server

This commit is contained in:
Yichen Jiang
2026-07-25 22:27:29 +08:00
committed by GitHub
870 changed files with 21704 additions and 28791 deletions

View File

@@ -2,5 +2,5 @@
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write
architecture.md: 76c58e03282ef6d736da7d65b05c534c05c4c318
architecture.zh.md: dddccf1e9238e617a453395731ee3f620ba5749d
architecture.md: 38a0b704e3ed8be11b59743a0f76f7ce09c06323
architecture.zh.md: e0e381a81d7d1ac8e18d2b8fac28b1d3d8903ff5

View File

@@ -167,7 +167,7 @@ Exceptions combine layers: LLM interface/consumer; filesystem policy; web regist
### Bundles And Apps
`dsh-agent-spine-demo` bundles a spine and optional goals. App packages own the TUI, one-shot CLI, and ACP/JSON-RPC front doors ([README](../packages/examples/agent-spine-demo/README.md), [ui/](../packages/ui/README.md)). `dsh-jsonrpc-agent` boots external `cordis.yml`; the Python SDK supplies a default only without explicit config ([Python SDK](../python/README.md)). Thin deployments use swappable backends and optional tools ([examples/](../examples/AGENTS.md), [runnable wirings](cookbook/extension-cookbook.md#runnable-wirings), [graph atlas](graph-atlas.md)).
`dsh-agent-spine-demo` bundles a spine and optional goals. App packages own TUI, CLI, ACP automation, and JSON-RPC front doors ([README](../packages/examples/agent-spine-demo/README.md), [acp/](../packages/acp/README.md), [ui/](../packages/ui/README.md)). `dsh-jsonrpc-agent` boots external `cordis.yml`; the Python SDK supplies a default only without explicit config ([Python SDK](../python/README.md)). Thin deployments use swappable backends and optional tools ([examples/](../examples/AGENTS.md), [runnable wirings](cookbook/extension-cookbook.md#runnable-wirings), [graph atlas](graph-atlas.md)).
### Where New Behavior Goes

View File

@@ -167,7 +167,7 @@ forever:
### 组合包与应用
`dsh-agent-spine-demo` 组合一套主干和可选目标。应用包负责 TUI、单次运行的 CLI命令行界面以及 ACP/JSON-RPC 入口([README](../packages/examples/agent-spine-demo/README.md)、[ui/](../packages/ui/README.md))。`dsh-jsonrpc-agent` 启动外部 `cordis.yml`Python SDK 仅在没有显式配置时提供默认项([Python SDK](../python/README.md))。轻量部署使用可替换后端和可选工具([examples/](../examples/AGENTS.md)、[可运行接线](cookbook/extension-cookbook.md#runnable-wirings)、[图谱](graph-atlas.md))。
`dsh-agent-spine-demo` 组合一套主干和可选目标。应用包负责 TUI、CLI命令行界面、ACP 自动化入口和 JSON-RPC 入口([README](../packages/examples/agent-spine-demo/README.md)、[acp/](../packages/acp/README.md)、[ui/](../packages/ui/README.md))。`dsh-jsonrpc-agent` 启动外部 `cordis.yml`Python SDK 仅在没有显式配置时提供默认项([Python SDK](../python/README.md))。轻量部署使用可替换后端和可选工具([examples/](../examples/AGENTS.md)、[可运行接线](cookbook/extension-cookbook.md#runnable-wirings)、[图谱](graph-atlas.md))。
### 新行为的归属位置

View File

@@ -35,7 +35,15 @@ flowchart LR
pkg_tool_bash["tool-bash"]
pkg_hooks_claude["hooks-claude"]
pkg_hooks_codex["hooks-codex"]
pkg_acp["acp"]
pkg_storage["storage"]
svc_storage["ctx.storage<br/>Non-session storage hub"]
pkg_storage_json["storage-json"]
pkg_storage_sqlite["storage-sqlite"]
pkg_storage_domain["storage-domain"]
svc_storageDomain["ctx.storageDomain<br/>Domain data facility"]
pkg_workspace["workspace"]
svc_workspace["ctx.workspace<br/>Workspace entity registry"]
pkg_apiproxy["apiproxy"]
svc_sessionQuery["ctx.sessionQuery<br/>Session reads, traces, filters, and search"]
pkg_session_reference["session-reference"]
svc_sessionReferences["ctx.sessionReferences<br/>Cross-session snapshot preparation"]
@@ -67,6 +75,7 @@ flowchart LR
svc_skills["ctx.skills<br/>Skill provider registry"]
pkg_skill_local["skill-local"]
svc_agents["ctx.agents<br/>Agent service"]
pkg_acp["acp"]
pkg_tui_demo["tui-demo"]
svc_agentLoop["ctx.agentLoop<br/>Concrete loop driver"]
pkg_agent_spine_demo["agent-spine-demo"]
@@ -118,12 +127,17 @@ flowchart LR
svc_spillStore["ctx.spillStore<br/>Spill storage seam"]
pkg_spill_local["spill-local"]
pkg_spill_policy["spill-policy"]
pkg_webserver["webserver"]
svc_httpServer["ctx.httpServer<br/>HTTP route registration"]
pkg_connection["connection"]
pkg_modules["modules"]
pkg_hmr["hmr"]
svc_clientModuleHost["ctx.clientModuleHost<br/>Client plugin graph host"]
pkg_workflow["workflow"]
svc_workflows["ctx.workflows<br/>Workflow script engine"]
pkg_workflow_workerthread["workflow-workerthread"]
pkg_tool_workflow["tool-workflow"]
pkg_acp --> svc_approval
pkg_acp --> svc_userInteraction
pkg_agent --> svc_agents
pkg_agent_loop --> svc_agentLoop
pkg_approval --> svc_approval
@@ -145,6 +159,7 @@ flowchart LR
pkg_llm_deepseek --> svc_llm
pkg_llm_pi_ai --> svc_llm
pkg_llm_replay --> svc_llm
pkg_modules --> svc_clientModuleHost
pkg_permission --> svc_permission
pkg_plan_mode --> svc_planMode
pkg_pty --> svc_pty
@@ -166,6 +181,10 @@ flowchart LR
pkg_skill_local --> svc_skills
pkg_spill --> svc_spillStore
pkg_spill_local --> svc_spillStore
pkg_storage --> svc_storage
pkg_storage_domain --> svc_storageDomain
pkg_storage_json --> svc_storage
pkg_storage_sqlite --> svc_storage
pkg_subagent --> svc_subagents
pkg_subagent_acp --> svc_subagents
pkg_subagent_fork --> svc_subagents
@@ -183,8 +202,10 @@ flowchart LR
pkg_web_search_deepseek --> svc_web
pkg_web_search_exa --> svc_web
pkg_web_search_perplexity --> svc_web
pkg_webserver --> svc_httpServer
pkg_workflow --> svc_workflows
pkg_workflow_workerthread --> svc_workflows
pkg_workspace --> svc_workspace
svc_agentLoop --> pkg_agent_spine_demo
svc_agents --> pkg_acp
svc_agents --> pkg_agent_loop
@@ -196,26 +217,26 @@ flowchart LR
svc_bash --> pkg_hooks_claude
svc_bash --> pkg_hooks_codex
svc_bash --> pkg_tool_bash
svc_clientModuleHost --> pkg_hmr
svc_codeRuntime --> pkg_tools
svc_commands --> pkg_acp
svc_commands --> pkg_tui
svc_compact --> pkg_compact_basic
svc_fs --> pkg_tool_fs
svc_httpServer --> pkg_connection
svc_httpServer --> pkg_hmr
svc_httpServer --> pkg_modules
svc_invariants --> pkg_agent
svc_invariants --> pkg_agent_loop
svc_invariants --> pkg_scope
svc_invariants --> pkg_session
svc_llm --> pkg_agent_loop
svc_llm --> pkg_compact_basic
svc_permission --> pkg_acp
svc_planMode --> pkg_acp
svc_pty --> pkg_tool_pty
svc_sandbox --> pkg_bash_sandbox
svc_sandbox --> pkg_pty_local
svc_sandboxPolicy --> pkg_bash_sandbox
svc_sandboxPolicy --> pkg_fs_sandbox
svc_sandboxPolicy --> pkg_pty_local
svc_sessionPersistence --> pkg_acp
svc_sessionPersistence --> pkg_agent_loop
svc_sessionPersistence --> pkg_hooks_claude
svc_sessionPersistence --> pkg_hooks_codex
@@ -223,7 +244,6 @@ flowchart LR
svc_sessionPersistence --> pkg_session_query_sqlite
svc_sessionPersistence --> pkg_tool_bash
svc_sessionQuery --> pkg_session_reference
svc_sessionReferences --> pkg_acp
svc_sessionReferences --> pkg_tui
svc_sessions --> pkg_agent
svc_sessions --> pkg_agent_loop
@@ -235,6 +255,8 @@ flowchart LR
svc_sessions --> pkg_subagent_inprocess
svc_skills --> pkg_tool_skill
svc_spillStore --> pkg_spill_policy
svc_storage --> pkg_storage_domain
svc_storageDomain --> pkg_workspace
svc_subagents --> pkg_tool_ralph
svc_subagents --> pkg_tool_subagent
svc_systemPrompt --> pkg_agent_loop
@@ -248,7 +270,6 @@ flowchart LR
svc_tasks --> pkg_tool_tasks
svc_tokenMeter --> pkg_compact_basic
svc_toolResultPrune --> pkg_compact_basic
svc_tools --> pkg_acp
svc_tools --> pkg_agent_loop
svc_tools --> pkg_tool_ask_user
svc_tools --> pkg_tool_bash
@@ -259,12 +280,12 @@ flowchart LR
svc_tools --> pkg_tool_subagent
svc_tools --> pkg_tool_todo
svc_tools --> pkg_tool_web
svc_userInteraction --> pkg_acp
svc_userInteraction --> pkg_tool_ask_user
svc_userInteraction --> pkg_tui
svc_web --> pkg_tool_web
svc_workflows --> pkg_tool_ralph
svc_workflows --> pkg_tool_workflow
svc_workspace --> pkg_apiproxy
svc_fs -. event gate .-> pkg_fs_policy
```
@@ -275,18 +296,21 @@ flowchart LR
| `ctx.toolResultPrune` | `core` | [`compact-tool-result-prune`](../packages/compact/compact-tool-result-prune) | - | [`compact-basic`](../packages/compact/compact-basic) | - | Rewrites oversized current tool results through replayable single-node surface replacements before summary compaction. |
| `ctx.sessions` | `core` | [`session`](../packages/core/session) | - | [`agent-loop`](../packages/core/agent-loop), [`agent`](../packages/core/agent), [`cli-demo`](../packages/examples/cli-demo), [`session-persistence`](../packages/session-persistence/session-persistence), [`session-query`](../packages/session-query/session-query), [`session-query-sqlite`](../packages/session-query/session-query-sqlite), [`subagent-inprocess`](../packages/subagent/subagent-inprocess), [`invariants`](../packages/support/invariants) | - | Owns append-only Session instances and emits the durable session event feed. |
| `ctx.invariants` | `core` | [`invariants`](../packages/support/invariants) | - | [`session`](../packages/core/session), [`agent`](../packages/core/agent), [`scope`](../packages/core/scope), [`agent-loop`](../packages/core/agent-loop) | - | Companion subpaths register owner-local checks; the service owns selection, uniqueness, child fibers, and package-attributed failures. |
| `ctx.sessionPersistence` | `seam` | [`session-persistence`](../packages/session-persistence/session-persistence) | [`session-persistence-jsonl`](../packages/session-persistence/session-persistence-jsonl), [`session-persistence-sqlite`](../packages/session-persistence/session-persistence-sqlite) | [`agent-loop`](../packages/core/agent-loop), [`tool-bash`](../packages/bash/tool-bash), [`hooks-claude`](../packages/hooks/hooks-claude), [`hooks-codex`](../packages/hooks/hooks-codex), [`acp`](../packages/ui/acp), [`session-query`](../packages/session-query/session-query), [`session-query-sqlite`](../packages/session-query/session-query-sqlite) | - | Backends persist the same SessionEvent vocabulary; apps choose a backend at composition time. |
| `ctx.sessionPersistence` | `seam` | [`session-persistence`](../packages/session-persistence/session-persistence) | [`session-persistence-jsonl`](../packages/session-persistence/session-persistence-jsonl), [`session-persistence-sqlite`](../packages/session-persistence/session-persistence-sqlite) | [`agent-loop`](../packages/core/agent-loop), [`tool-bash`](../packages/bash/tool-bash), [`hooks-claude`](../packages/hooks/hooks-claude), [`hooks-codex`](../packages/hooks/hooks-codex), [`session-query`](../packages/session-query/session-query), [`session-query-sqlite`](../packages/session-query/session-query-sqlite) | - | Backends persist the same SessionEvent vocabulary; apps choose a backend at composition time. |
| `ctx.storage` | `seam` | [`storage`](../packages/storage/storage) | [`storage-json`](../packages/storage/storage-json), [`storage-sqlite`](../packages/storage/storage-sqlite) | [`storage-domain`](../packages/storage/storage-domain) | - | Backends register side by side under names; data forms (domain first) mount on the hub and translate typed operations into opaque KV-unit primitives. |
| `ctx.storageDomain` | `core` | [`storage-domain`](../packages/storage/storage-domain) | - | [`workspace`](../packages/workspace/workspace) | - | Waits for every configured backend, then publishes the domain form as one lifecycle-bound service for typed durable state. |
| `ctx.workspace` | `core` | [`workspace`](../packages/workspace/workspace) | - | `apiproxy` | - | Owns WorkspaceId-branded records over the domain facility; stable sessionIds accounts drive Host RPC and GUI projections. |
| `ctx.sessionQuery` | `seam` | [`session-query`](../packages/session-query/session-query) | [`session-query-sqlite`](../packages/session-query/session-query-sqlite) | [`session-reference`](../packages/context/session-reference) | - | The interface supplies exact reads, filters, and traces; its concrete backend adds full-text reconciliation, ranking, snippets, and cursor generations on the same service. |
| `ctx.sessionReferences` | `core` | [`session-reference`](../packages/context/session-reference) | - | [`tui`](../packages/ui/tui), [`acp`](../packages/ui/acp) | - | Projects bounded current-surface conversation snapshots into durable untrusted message context; host adapters own mention syntax. |
| `ctx.sessionReferences` | `core` | [`session-reference`](../packages/context/session-reference) | - | [`tui`](../packages/ui/tui) | - | Projects bounded current-surface conversation snapshots into durable untrusted message context; host adapters own mention syntax. |
| `ctx.sessionTitle` | `seam` | [`session-title`](../packages/session-title/session-title) | [`session-title-first-message-llm`](../packages/session-title/session-title-first-message-llm), [`session-title-all-messages-llm`](../packages/session-title/session-title-all-messages-llm) | - | - | Owns the deterministic fallback, latest-title fold, and sole optional asynchronous provider registration. |
| `ctx.systemPrompt` | `core` | [`system-prompt`](../packages/core/system-prompt) | - | [`agent-loop`](../packages/core/agent-loop), [`tools`](../packages/core/tools), [`tool-fs`](../packages/fs/tool-fs), [`tool-pty`](../packages/pty/tool-pty), [`tool-web`](../packages/web/tool-web) | - | Collects prompt sections and model-facing tool schemas for each step. |
| `ctx.tools` | `core` | [`tools`](../packages/core/tools) | - | [`agent-loop`](../packages/core/agent-loop), [`tool-ask-user`](../packages/ui/tool-ask-user), [`tool-bash`](../packages/bash/tool-bash), [`tool-cordis`](../packages/cordis/tool-cordis), [`tool-fs`](../packages/fs/tool-fs), [`tool-pty`](../packages/pty/tool-pty), [`tool-skill`](../packages/skill/tool-skill), [`tool-subagent`](../packages/subagent/tool-subagent), [`tool-todo`](../packages/todo/tool-todo), [`tool-web`](../packages/web/tool-web), [`acp`](../packages/ui/acp) | - | Registers capabilities, owns Code Mode transport, and routes calls through pre-policy, monotonic guards, around dispatch, post-policy, and final-result observation. |
| `ctx.userInteraction` | `seam` | [`user-interaction`](../packages/ui/user-interaction) | [`tui`](../packages/ui/tui), [`acp`](../packages/ui/acp) | [`tool-ask-user`](../packages/ui/tool-ask-user), [`tui`](../packages/ui/tui), [`acp`](../packages/ui/acp) | - | UI front doors provide the active human-answer provider; tool-ask-user pauses a tool call on the provider-neutral ask() promise. |
| `ctx.planMode` | `core` | [`plan-mode`](../packages/plan/plan-mode) | - | [`acp`](../packages/ui/acp) | - | Folds logged plan/mode state, flushes user selections at turn boundaries, renders deployment-owned guidance, registers /plan, and keeps the plan-exit schema stable across transitions. |
| `ctx.commands` | `core` | [`commands`](../packages/ui/commands) | - | [`tui`](../packages/ui/tui), [`acp`](../packages/ui/acp) | - | Plugins register direct human commands; TUI and ACP consume the same effective per-agent catalog without sending invocations to the model. |
| `ctx.tools` | `core` | [`tools`](../packages/core/tools) | - | [`agent-loop`](../packages/core/agent-loop), [`tool-ask-user`](../packages/ui/tool-ask-user), [`tool-bash`](../packages/bash/tool-bash), [`tool-cordis`](../packages/cordis/tool-cordis), [`tool-fs`](../packages/fs/tool-fs), [`tool-pty`](../packages/pty/tool-pty), [`tool-skill`](../packages/skill/tool-skill), [`tool-subagent`](../packages/subagent/tool-subagent), [`tool-todo`](../packages/todo/tool-todo), [`tool-web`](../packages/web/tool-web) | - | Registers capabilities, owns Code Mode transport, and routes calls through pre-policy, monotonic guards, around dispatch, post-policy, and final-result observation. |
| `ctx.userInteraction` | `seam` | [`user-interaction`](../packages/ui/user-interaction) | [`tui`](../packages/ui/tui) | [`tool-ask-user`](../packages/ui/tool-ask-user), [`tui`](../packages/ui/tui) | - | UI front doors provide the active human-answer provider; tool-ask-user pauses a tool call on the provider-neutral ask() promise. |
| `ctx.planMode` | `core` | [`plan-mode`](../packages/plan/plan-mode) | - | - | - | Folds logged plan/mode state, flushes user selections at turn boundaries, renders deployment-owned guidance, registers /plan, and keeps the plan-exit schema stable across transitions. |
| `ctx.commands` | `core` | [`commands`](../packages/ui/commands) | - | [`tui`](../packages/ui/tui) | - | Plugins register direct human commands; TUI consumes the effective per-agent catalog without sending invocations to the model. |
| `ctx.tui` | `bundle` | [`tui`](../packages/ui/tui) | - | - | - | One TUI front door provides a FIFO overlay host; injected plugins receive caller-fiber ownership without access to pi-tui or terminal lifecycle state. |
| `ctx.skills` | `seam` | [`skill`](../packages/skill/skill) | [`skill-local`](../packages/skill/skill-local) | [`tool-skill`](../packages/skill/tool-skill) | - | Merges provider skill catalogs; tool-skill renders the session-prefix catalog and loads complete skill bodies. |
| `ctx.agents` | `core` | [`agent`](../packages/core/agent) | - | [`agent-loop`](../packages/core/agent-loop), [`acp`](../packages/ui/acp), [`cli-demo`](../packages/examples/cli-demo), [`subagent-inprocess`](../packages/subagent/subagent-inprocess), [`tui-demo`](../packages/examples/tui-demo) | - | Owns live Agent handles, the create/resume factory seam, and process-local initiator propagation. |
| `ctx.agents` | `core` | [`agent`](../packages/core/agent) | - | [`agent-loop`](../packages/core/agent-loop), [`acp`](../packages/acp/acp), [`cli-demo`](../packages/examples/cli-demo), [`subagent-inprocess`](../packages/subagent/subagent-inprocess), [`tui-demo`](../packages/examples/tui-demo) | - | Owns live Agent handles, the create/resume factory seam, and process-local initiator propagation. |
| `ctx.agentLoop` | `bundle` | [`agent-loop`](../packages/core/agent-loop) | - | [`agent-spine-demo`](../packages/examples/agent-spine-demo) | - | The one concrete loop plugin; extension packages depend on dsh-agent events and services, not on this package. |
| `ctx.goals` | `core` | [`goal`](../packages/goal/goal) | - | - | - | Folds revisioned objective state from the session log and keeps live continuation activation process-local. |
| `ctx.bash` | `seam` | [`bash`](../packages/bash/bash) | [`bash-local`](../packages/bash/bash-local), [`bash-sandbox`](../packages/bash/bash-sandbox) | [`tool-bash`](../packages/bash/tool-bash), [`hooks-claude`](../packages/hooks/hooks-claude), [`hooks-codex`](../packages/hooks/hooks-codex) | - | The model-facing bash tools and hook bridges consume this seam; sandboxed or remote executors replace bash-local without touching them. |
@@ -294,8 +318,8 @@ flowchart LR
| `ctx.pty` | `seam` | [`pty`](../packages/pty/pty) | [`pty-local`](../packages/pty/pty-local) | [`tool-pty`](../packages/pty/tool-pty) | - | The registry owns exact-Agent session identity and cleanup; backends own terminal mechanics, while tool-pty exposes the owner-scoped model surface. |
| `ctx.sandbox` | `seam` | [`sandbox`](../packages/sandbox/sandbox) | [`sandbox-local`](../packages/sandbox/sandbox-local) | [`bash-sandbox`](../packages/bash/bash-sandbox), [`pty-local`](../packages/pty/pty-local) | - | Consumers hand over the exact argv they are about to spawn; same-world backends wrap it under a per-call policy and report enforcement. |
| `ctx.sandboxPolicy` | `core` | [`sandbox-policy`](../packages/sandbox/sandbox-policy) | - | [`bash-sandbox`](../packages/bash/bash-sandbox), [`fs-sandbox`](../packages/fs/fs-sandbox), [`pty-local`](../packages/pty/pty-local) | - | The one home for the deployment default mode + workspace root; only the sandboxed executor and provider read the service (the tool layers use the pure `sandbox/mode` fold it also exports). Both enforcing families read it so bash and fs cannot confine to different roots. |
| `ctx.approval` | `seam` | `approval` | [`acp`](../packages/ui/acp) | [`tools`](../packages/core/tools), [`tool-bash`](../packages/bash/tool-bash) | - | One-shot permission decisions dispatched over the `approval/request` waterfall; answerers are listeners (the ACP bridge for its own agents), absence fails closed to `unavailable`. |
| `ctx.permission` | `core` | [`permission`](../packages/ui/permission) | - | [`acp`](../packages/ui/acp) | - | User-facing preset table (`workspace-write`/`danger-full-access`) bundling the sandbox-mode and approval-policy knobs; a switch writes one `permission/preset` event through to both knob events. |
| `ctx.approval` | `seam` | `approval` | [`acp`](../packages/acp/acp) | [`tools`](../packages/core/tools), [`tool-bash`](../packages/bash/tool-bash) | - | One-shot permission decisions dispatched over the `approval/request` waterfall; answerers are listeners (the ACP bridge for its own agents), absence fails closed to `unavailable`. |
| `ctx.permission` | `core` | [`permission`](../packages/ui/permission) | - | - | - | User-facing preset table (`workspace-write`/`danger-full-access`) bundling the sandbox-mode and approval-policy knobs; a switch writes one `permission/preset` event through to both knob events. |
| `ctx.codeRuntime` | `seam` | [`code-runtime`](../packages/code-runtime/code-runtime) | [`code-runtime-worker`](../packages/code-runtime/code-runtime-worker) | [`tools`](../packages/core/tools) | - | Runs one model-written program against host-provided async bindings; backends differ by substrate and language (the tool registry consumes it for Code Mode). |
| `ctx.fs` | `seam` | [`fs`](../packages/fs/fs) | [`fs-local`](../packages/fs/fs-local), [`fs-sandbox`](../packages/fs/fs-sandbox) | [`tool-fs`](../packages/fs/tool-fs) | [`fs-policy`](../packages/fs/fs-policy) | tool-fs executes read/write/edit through ctx.fs; fs-sandbox fences mutations by the shared sandbox mode; fs-policy contributes observed-state checks through the fs/* event gate. |
| `ctx.compact` | `seam` | [`compact`](../packages/compact/compact) | [`compact-basic`](../packages/compact/compact-basic) | [`compact-basic`](../packages/compact/compact-basic) | - | The basic backend consumes post-step pressure and request-error recovery events; a model-facing compact tool remains deferred. |
@@ -303,6 +327,8 @@ flowchart LR
| `ctx.tasks` | `core` | [`tasks`](../packages/tasks/tasks) | - | [`tool-bash`](../packages/bash/tool-bash), [`tool-pty`](../packages/pty/tool-pty), [`tool-subagent`](../packages/subagent/tool-subagent), [`tool-tasks`](../packages/tasks/tool-tasks) | - | Producers (background bash, PTY sends, and subagent delegations) register running work; tool-tasks is the model-facing control surface that reads, lists, and kills it. |
| `ctx.web` | `seam` | [`web`](../packages/web/web) | [`web-search-exa`](../packages/web/web-search-exa), [`web-search-perplexity`](../packages/web/web-search-perplexity), [`web-search-deepseek`](../packages/web/web-search-deepseek), [`web-fetch-local`](../packages/web/web-fetch-local) | [`tool-web`](../packages/web/tool-web) | - | Search and fetch providers register into one ctx.web seam; tool-web owns the stable model-facing names. |
| `ctx.spillStore` | `seam` | [`spill`](../packages/spill/spill) | [`spill-local`](../packages/spill/spill-local) | [`spill-policy`](../packages/spill/spill-policy) | - | The backend saves oversized tool text and returns a model-facing locator plus retrieval hint; spill-policy is the tools/post-execute consumer that decides when to spill. |
| `ctx.httpServer` | `core` | `webserver` | - | `connection`, `modules`, `hmr` | - | Plain node:http carrier: named-route registry, index transform taps, and the static dist fallback; web-transport plugins register their own routes. |
| `ctx.clientModuleHost` | `core` | `modules` | - | `hmr` | - | Composes the __DSH_BOOT__ entry graph from an incremental dshClient scan, serves plugin bundles, and notifies rebuilt/graph-changed subscribers. |
| `ctx.workflows` | `seam` | [`workflow`](../packages/workflow/workflow) | [`workflow-workerthread`](../packages/workflow/workflow-workerthread) | [`tool-workflow`](../packages/workflow/tool-workflow), [`tool-ralph`](../packages/workflow/tool-ralph) | - | One engine per context (bash shape, no named-provider registry); the general workflow and fixed Ralph consumers start runs whose agent() calls fan out through ctx.subagents. |
Maintenance mode: hybrid: services are discovered from Cordis declarations; interface/implementation/consumer roles are classified in `scripts/gen-doc-graphs.ts` with a completeness guard.

View File

@@ -11,14 +11,14 @@ A `Requires:` line lists the service keys the plugin `inject`s: its `cordis.yml`
## `@deepseek-ai/dsh-acp`
Requires: `agents` · `commands` · `sessionPersistence` · `sessionQuery` · `tools` · `userInteraction` · `llm` · `systemPrompt`
Requires: `agents`
```ts config-catalog
/** Plugin config: the agent template ACP sessions are created from. */
/** Plugin config: the provider/model target used for each ACP-created agent. */
export interface AcpConfig {
/** Provider route for created agents. */
provider?: string
/** Model name for created agents (must have a registered adapter). */
/** Model name for created agents. */
model?: string
/** Runtime-only transport override; production uses stdio. */
stream?: Stream
@@ -27,15 +27,14 @@ export interface AcpConfig {
Depends on: `Stream` (`@agentclientprotocol/sdk`)
Source: [`packages/ui/acp/src/index.ts:285`](../packages/ui/acp/src/index.ts)
Source: [`packages/acp/acp/src/index.ts:56`](../packages/acp/acp/src/index.ts)
## `@deepseek-ai/dsh-acp-demo`
```ts config-catalog
/**
* App config: the swappable per-deployment values. `provider` and `model` configure the
* agent template the ACP bridge creates each session's agent from (NOT a
* pre-created agent — ACP creates agents at `session/new`); `persona` is the
* App config: the swappable per-deployment values. `provider` and `model` configure
* each agent the ACP bridge creates at `session/new`; `persona` is the
* deployment persona (forwarded to the system-prompt plugin); `toolOrder` is
* the explicit model-facing tool order (forwarded to the system-prompt plugin);
* `tools` is the tool registry's config (its presentation `mode`, forwarded
@@ -58,14 +57,12 @@ export interface Config {
dshHome?: string
/** Fallback session-title limits forwarded through agent-spine-demo. */
sessionTitle?: NonNullable<agentCore.Config['sessionTitle']>
/** Directory for JSONL sessions and the derived query index. Defaults to `./.sessions`. */
/** Directory for JSONL sessions. Defaults to `./.sessions`. */
persistenceRoot?: string
/** Write delta-chunk runs as packed storage rows (the JSONL backend's `packChunks`). Defaults to `false`. */
packChunks?: boolean
/** JSONL artifact encoding; defaults to checksummed Zstandard frames. */
persistenceCompression?: JsonlCompression
/** Cross-session reference discovery and snapshot byte budgets. */
sessionReferences?: SessionReferenceConfig
/** Controls automatic AGENTS.md/CLAUDE.md loading; configure a byte budget or set `false`. */
workspaceContext: agentCore.Config['workspaceContext']
/** Skill registry, local-provider, and model-facing consumer config forwarded to agent-spine-demo. */
@@ -74,16 +71,16 @@ export interface Config {
toolBash?: NonNullable<agentCore.Config['toolBash']>
/** Generic background-task controls forwarded through agent-core; set false to omit their tool surface. */
toolTasks?: NonNullable<agentCore.Config['toolTasks']>
/** Persisted same-session goals; owner defaults enable them, or false disables the stack and command. */
/** Persisted same-session goals; owner defaults enable them, or false disables the stack and tools. */
goals?: agentCore.GoalConfig | false
/** Bounded transient model-request retry policy forwarded through agent-core. */
llmRetry?: NonNullable<agentCore.Config['llmRetry']>
}
```
Depends on: [`agentCore`](../packages/examples/agent-spine-demo/src/index.ts) · [`JsonlCompression`](../packages/session-persistence/session-persistence-jsonl/src/index.ts) · [`SessionReferenceConfig`](#deepseek-aidsh-session-reference) · [`ToolsConfig`](#deepseek-aidsh-tools)
Depends on: [`agentCore`](../packages/examples/agent-spine-demo/src/index.ts) · [`JsonlCompression`](../packages/session-persistence/session-persistence-jsonl/src/index.ts) · [`ToolsConfig`](#deepseek-aidsh-tools)
Source: [`packages/examples/acp-demo/src/index.ts:44`](../packages/examples/acp-demo/src/index.ts)
Source: [`packages/examples/acp-demo/src/index.ts:37`](../packages/examples/acp-demo/src/index.ts)
## `@deepseek-ai/dsh-agent-loop`
@@ -276,6 +273,20 @@ Depends on: [`agentCore`](../packages/examples/agent-spine-demo/src/index.ts) ·
Source: [`packages/examples/cli-demo/src/index.ts:26`](../packages/examples/cli-demo/src/index.ts)
## `@deepseek-ai/dsh-client-hmr`
Requires: `clientModuleHost` · `httpServer`
```ts config-catalog
/** Plugin config, validated by the same-named schemastery schema. */
export interface Config {
/** Bundle stat-poll interval in milliseconds (default 500, the build-side watcher's polling default). */
pollIntervalMs?: number
}
```
Source: [`packages/client/hmr/src/index.ts:29`](../packages/client/hmr/src/index.ts)
## `@deepseek-ai/dsh-code-runtime-worker`
```ts config-catalog
@@ -474,6 +485,40 @@ export interface Config {
Source: [`packages/hooks/hooks-codex/src/index.ts:42`](../packages/hooks/hooks-codex/src/index.ts)
## `@deepseek-ai/dsh-host-apiproxy`
Requires: `agents` · `sessions` · `tools` · `userInteraction` · `workspace`
```ts config-catalog
/** Gateway plugin config: host-level agent routing and Workspace creation root. */
export interface Config {
/** Default provider route for created/resumed agents. */
provider: string
/** Default model id. */
model: string
/** Parent directory for name-created Workspaces; defaults to the Host cwd. */
workspaceRoot?: string
}
```
Source: [`packages/host/apiproxy/src/index.ts:33`](../packages/host/apiproxy/src/index.ts)
## `@deepseek-ai/dsh-host-webserver`
```ts config-catalog
/** Gateway config: listen address plus the static dist anchor (injected by the composing app, never self-resolved). */
export interface Config {
/** Listen host; the two supported values are loopback and all-interfaces. */
host: '127.0.0.1' | '0.0.0.0'
/** Listen port; zero requests an OS-assigned port. */
port: number
/** Absolute path of index.html inside the static root (dist location is workspace knowledge of the app). */
distIndex: string
}
```
Source: [`packages/host/webserver/src/index.ts:39`](../packages/host/webserver/src/index.ts)
## `@deepseek-ai/dsh-invariants`
```ts config-catalog
@@ -624,7 +669,7 @@ export interface ReplayProviderConfig {
id: string
/** Selector label; defaults to {@link id}. */
name?: string
/** Advisory models exposed to clients such as ACP editors. */
/** Advisory models exposed to replay scenarios that exercise discovery. */
models?: ReplayModelConfig[]
}
@@ -641,7 +686,7 @@ export interface ReplayModelConfig {
}
```
Source: [`packages/support/llm-replay/src/index.ts:387`](../packages/support/llm-replay/src/index.ts)
Source: [`packages/support/llm-replay/src/index.ts:392`](../packages/support/llm-replay/src/index.ts)
## `@deepseek-ai/dsh-llm-retry`
@@ -710,12 +755,12 @@ Source: [`packages/lsp/lsp-local/src/index.ts:85`](../packages/lsp/lsp-local/src
Requires: `tools`
```ts config-catalog
/** Discriminated union of all supported MCP transport configurations. */
/** Configuration for one stdio or Streamable HTTP MCP server. */
export type Config = StdioConfig | StreamableHttpConfig
/** Config for connecting to an MCP server via a spawned child process over stdio. */
export interface StdioConfig {
/** Transport type: spawn a child process and communicate over stdio. */
/** Selects child-process stdio transport. */
transport: 'stdio'
/**
* Stable local namespace for this server's model-facing tool names
@@ -723,21 +768,21 @@ export interface StdioConfig {
* unique across live mcp-client instances.
*/
serverName: string
/** Executable to spawn. */
/** Executable used to start the server. */
command: string
/** Arguments passed to the command. */
/** Arguments passed directly, without shell interpolation. */
args: string[]
/** Extra env vars merged on top of scrubbed ambient env. */
env: Record<string, string>
/** Working directory for the child process. */
cwd: string
/** Timeout per callTool invocation (ms). */
/** Per-tool-call timeout in milliseconds. */
toolCallTimeoutMs: number
}
/** Config for connecting to an MCP server over Streamable HTTP (SSE). */
export interface StreamableHttpConfig {
/** Transport type: connect to an MCP server over Streamable HTTP (SSE). */
/** Selects Streamable HTTP transport. */
transport: 'streamable-http'
/**
* Stable local namespace for this server's model-facing tool names
@@ -745,11 +790,11 @@ export interface StreamableHttpConfig {
* unique across live mcp-client instances.
*/
serverName: string
/** MCP server URL. */
/** MCP endpoint URL. */
url: string
/** Extra headers (e.g. auth tokens). */
/** Additional headers attached to MCP requests. */
headers: Record<string, string>
/** Timeout per callTool invocation (ms). */
/** Per-tool-call timeout in milliseconds. */
toolCallTimeoutMs: number
}
```
@@ -935,9 +980,9 @@ export interface Config {
/**
* Root directory for all session files. Required (no default): a default of
* `process.cwd()` would scatter session files as the process's cwd changes
* (bash calls, subprocesses). Sessions group under per-cwd subdirectories. An
* existing root must be a readable directory; an absent root is created on
* first materialization.
* (bash calls, subprocesses). Sessions group under human-readable project
* directories, then per-session directories. An existing root must be a
* readable directory; an absent root is created on first materialization.
*/
root: string
/**
@@ -1155,6 +1200,84 @@ export interface Config {
Source: [`packages/spill/spill-policy/src/index.ts:51`](../packages/spill/spill-policy/src/index.ts)
## `@deepseek-ai/dsh-storage-domain`
Requires: `storage`
```ts config-catalog
/**
* Plugin config. Which backend serves which domain is decided here, not
* globally on the hub: `backend` is the default route and `routes` overrides
* it per domain name. A route naming an unregistered backend fails loud at
* `open` with `backend-not-found`.
*/
export interface Config {
/** Default backend name for every domain without an explicit route. Required: there is no universally correct medium. */
backend: string
/** Per-domain overrides: domain name → backend name. */
routes?: Record<string, string>
}
```
Source: [`packages/storage/storage-domain/src/index.ts:52`](../packages/storage/storage-domain/src/index.ts)
## `@deepseek-ai/dsh-storage-json`
Requires: `storage`
```ts config-catalog
/**
* Plugin configuration.
* `root` has NO default on purpose: a `process.cwd()` fallback would scatter
* unit files wherever the process happens to start; assemblies state the
* location explicitly.
*/
export interface Config {
/** Directory holding one `<unit>.json` file per unit. */
root: string
}
```
Source: [`packages/storage/storage-json/src/index.ts:27`](../packages/storage/storage-json/src/index.ts)
## `@deepseek-ai/dsh-storage-sqlite`
Requires: `storage`
```ts config-catalog
/** Plugin configuration. */
export interface Config {
/**
* Filesystem path to the SQLite database file. The special value `:memory:`
* opens an in-process database (tests). On filesystems with POSIX modes,
* missing directories and databases are created owner-only; existing path
* modes are preserved. Filesystem setup errors other than an existing
* database fail the open. The backend does not protect confidentiality or
* integrity when another principal can replace the database entry in its
* parent directory.
*/
path: string
/**
* SQLite `journal_mode` pragma. `wal` (the default) suits local disks; pick
* a rollback-journal mode (`delete`/`truncate`/`persist`) on filesystems
* where WAL's shared-memory files do not work (network mounts). See
* {@link JournalMode}.
*/
journalMode?: JournalMode
}
/**
* Journal modes the backend will run under. `wal` is the default; the
* rollback-journal modes (`delete`/`truncate`/`persist`) exist for
* filesystems where WAL's shared-memory files do not work (network mounts).
* `memory`/`off` are excluded: dropping journal durability silently
* contradicts the durability clause of the KV backend contract.
*/
export type JournalMode = 'wal' | 'delete' | 'truncate' | 'persist'
```
Source: [`packages/storage/storage-sqlite/src/index.ts:24`](../packages/storage/storage-sqlite/src/index.ts)
## `@deepseek-ai/dsh-subagent-acp`
Requires: `subagents`
@@ -1893,9 +2016,9 @@ Source: [`packages/context/workspace-context/src/config.ts:17`](../packages/cont
These load from a `cordis.yml` entry with no `config:` block; they declare no config surface.
- `@deepseek-ai/dsh-agent` ([`packages/core/agent/src/index.ts`](../packages/core/agent/src/index.ts))
- `@deepseek-ai/dsh-client-connection` ([`packages/client/connection/src/index.ts`](../packages/client/connection/src/index.ts))
- `@deepseek-ai/dsh-client-hmr` ([`packages/client/hmr/src/index.ts`](../packages/client/hmr/src/index.ts))
- `@deepseek-ai/dsh-client-connection` — requires `httpServer` · `apiProxy` ([`packages/client/connection/src/index.ts`](../packages/client/connection/src/index.ts))
- `@deepseek-ai/dsh-client-i18n` ([`packages/client/i18n/src/index.ts`](../packages/client/i18n/src/index.ts))
- `@deepseek-ai/dsh-client-modules` — requires `httpServer` · `loader` ([`packages/client/modules/src/index.ts`](../packages/client/modules/src/index.ts))
- `@deepseek-ai/dsh-client-runtime` ([`packages/client/runtime/src/index.ts`](../packages/client/runtime/src/index.ts))
- `@deepseek-ai/dsh-client-ui-conversation` ([`packages/client/ui-conversation/src/index.ts`](../packages/client/ui-conversation/src/index.ts))
- `@deepseek-ai/dsh-client-ui-layout` ([`packages/client/ui-layout/src/index.ts`](../packages/client/ui-layout/src/index.ts))
@@ -1903,6 +2026,7 @@ These load from a `cordis.yml` entry with no `config:` block; they declare no co
- `@deepseek-ai/dsh-client-ui-sidebar` ([`packages/client/ui-sidebar/src/index.ts`](../packages/client/ui-sidebar/src/index.ts))
- `@deepseek-ai/dsh-client-ui-theme` ([`packages/client/ui-theme/src/index.ts`](../packages/client/ui-theme/src/index.ts))
- `@deepseek-ai/dsh-client-ui-trajectory` ([`packages/client/ui-trajectory/src/index.ts`](../packages/client/ui-trajectory/src/index.ts))
- `@deepseek-ai/dsh-client-ui-workspace` ([`packages/client/ui-workspace/src/index.ts`](../packages/client/ui-workspace/src/index.ts))
- `@deepseek-ai/dsh-command-goal` — requires `commands` · `goals` ([`packages/goal/command-goal/src/index.ts`](../packages/goal/command-goal/src/index.ts))
- `@deepseek-ai/dsh-commands` ([`packages/ui/commands/src/index.ts`](../packages/ui/commands/src/index.ts))
- `@deepseek-ai/dsh-fs-policy` ([`packages/fs/fs-policy/src/index.ts`](../packages/fs/fs-policy/src/index.ts))
@@ -1912,12 +2036,14 @@ These load from a `cordis.yml` entry with no `config:` block; they declare no co
- `@deepseek-ai/dsh-pty` ([`packages/pty/pty/src/index.ts`](../packages/pty/pty/src/index.ts))
- `@deepseek-ai/dsh-session` ([`packages/core/session/src/index.ts`](../packages/core/session/src/index.ts))
- `@deepseek-ai/dsh-session-checkpoint-policy` — requires `llm` · `sessionPersistence` · `sessions` · `tools` ([`packages/session-persistence/session-checkpoint-policy/src/index.ts`](../packages/session-persistence/session-checkpoint-policy/src/index.ts))
- `@deepseek-ai/dsh-storage` ([`packages/storage/storage/src/index.ts`](../packages/storage/storage/src/index.ts))
- `@deepseek-ai/dsh-subagent` ([`packages/subagent/subagent/src/index.ts`](../packages/subagent/subagent/src/index.ts))
- `@deepseek-ai/dsh-tasks` ([`packages/tasks/tasks/src/index.ts`](../packages/tasks/tasks/src/index.ts))
- `@deepseek-ai/dsh-timeout-policy` — requires `tools` ([`packages/timeout/timeout-policy/src/index.ts`](../packages/timeout/timeout-policy/src/index.ts))
- `@deepseek-ai/dsh-tool-ask-user` — requires `tools` · `userInteraction` ([`packages/ui/tool-ask-user/src/index.ts`](../packages/ui/tool-ask-user/src/index.ts))
- `@deepseek-ai/dsh-tool-todo` — requires `tools` ([`packages/todo/tool-todo/src/index.ts`](../packages/todo/tool-todo/src/index.ts))
- `@deepseek-ai/dsh-user-interaction` ([`packages/ui/user-interaction/src/index.ts`](../packages/ui/user-interaction/src/index.ts))
- `@deepseek-ai/dsh-workspace` — requires `storageDomain` · `sessionPersistence` ([`packages/workspace/workspace/src/index.ts`](../packages/workspace/workspace/src/index.ts))
## Seam packages (not directly loadable)
@@ -1942,16 +2068,12 @@ Imported as libraries by other packages; a `cordis.yml` cannot load them.
- `@deepseek-ai/dsh-agent-loop-testkit` ([`packages/support/agent-loop-testkit/src/index.ts`](../packages/support/agent-loop-testkit/src/index.ts))
- `@deepseek-ai/dsh-app-boot` ([`packages/ui/app-boot/src/index.ts`](../packages/ui/app-boot/src/index.ts))
- `@deepseek-ai/dsh-brand` ([`packages/util/brand/src/index.ts`](../packages/util/brand/src/index.ts))
- `@deepseek-ai/dsh-client-modules` ([`packages/client/modules/src/index.ts`](../packages/client/modules/src/index.ts))
- `@deepseek-ai/dsh-client-ui-primitives` ([`packages/client/ui-primitives/src/index.ts`](../packages/client/ui-primitives/src/index.ts))
- `@deepseek-ai/dsh-client-ui-slots` ([`packages/client/ui-slots/src/index.ts`](../packages/client/ui-slots/src/index.ts))
- `@deepseek-ai/dsh-client-web` ([`packages/client/web/src/index.ts`](../packages/client/web/src/index.ts))
- `@deepseek-ai/dsh-client-web-react` ([`packages/client/web-react/src/index.ts`](../packages/client/web-react/src/index.ts))
- `@deepseek-ai/dsh-helper` ([`packages/sdk/helper/src/index.ts`](../packages/sdk/helper/src/index.ts))
- `@deepseek-ai/dsh-hook-protocol` ([`packages/hooks/hook-protocol/src/index.ts`](../packages/hooks/hook-protocol/src/index.ts))
- `@deepseek-ai/dsh-host-apiproxy` ([`packages/host/apiproxy/src/index.ts`](../packages/host/apiproxy/src/index.ts))
- `@deepseek-ai/dsh-host-runtime` ([`packages/host/runtime/src/index.ts`](../packages/host/runtime/src/index.ts))
- `@deepseek-ai/dsh-host-webserver` ([`packages/host/webserver/src/index.ts`](../packages/host/webserver/src/index.ts))
- `@deepseek-ai/dsh-jsonrpc-demo` ([`packages/examples/jsonrpc-demo/src/index.ts`](../packages/examples/jsonrpc-demo/src/index.ts))
- `@deepseek-ai/dsh-llm-mock-server` ([`packages/support/llm-mock-server/src/index.ts`](../packages/support/llm-mock-server/src/index.ts))
- `@deepseek-ai/dsh-loader-smoke` ([`packages/support/loader-smoke/src/index.ts`](../packages/support/loader-smoke/src/index.ts))

View File

@@ -2,5 +2,5 @@
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write
adding-a-tool.md: 788c967cffa9df06e8d96e190930d69b9f2182ed
adding-a-tool.zh.md: 4ff9ce25a66625435ccba4454f2ac4b5472e5668
adding-a-tool.md: c4deed8e13afcdc8e1a714364b086b8b0da018c1
adding-a-tool.zh.md: 103c92b596a90192aae5a6b4d1e42fbcf052cf66

View File

@@ -64,9 +64,9 @@ In [Code Mode](../../packages/core/tools/README.md), every visible registered to
Design `output.schema` as a useful programmatic API: return handles and fields directly, allow scalar/array/null roots when they are the honest value, and keep human explanation in `output.render`. Intermediate values are execution-local, are not persisted or prompt-truncated, and have no byte cap, so the producer's truthful acquisition bounds and process memory still matter. Only the outer `run_code` logs/result cross the configurable output cap and model-facing spill pipeline.
## How your tool renders in an editor (ACP presentation)
## How your tool renders in a UI
Your tool's `output.render` returns model-facing content; its **editor card** is a separate concern declared through pure presentation projections and optional `presentCall` / `presentResult` methods. Design these alongside the canonical value—an editor (Zed, over the ACP bridge) shows the card, and a tool with no UI presentation falls back to a generic card (title = tool name, raw args as input).
Your tool's `output.render` returns model-facing content; its **UI card** is a separate concern declared through pure presentation projections and optional `presentCall` / `presentResult` methods. Design these alongside the canonical value. A tool with no UI presentation falls back to a generic card (title = tool name, raw args as input).
Both methods return a **`card`-tagged render intent** — pick the card kind that matches what your tool does:
@@ -76,17 +76,17 @@ Both methods return a **`card`-tagged render intent** — pick the card kind tha
- `{ card: 'diff', title, diffs, locations? }` — your call creates or modifies a file. `diffs: [{ path, oldText, newText }]` (`oldText: null` for a new file) renders as an inline diff card. (tool-fs `write`/`edit`.)
- `presentResult(args, { content, isError, meta? })` returns the completed card:
- `generic` supplies an optional title and content.
- `terminal` supplies raw output and optional exit metadata; the bridge renders the capability-specific or fenced fallback view.
- `diff` supplies applied hunks, often derived by `output.presentationMeta` and carried in persisted `result.meta` so replay reproduces them. Mutation tools keep a diff result because an ACP update replaces the pending card's content.
- `terminal` supplies raw output and optional exit metadata; each UI renders its capable or fallback view.
- `diff` supplies applied hunks, often derived by `output.presentationMeta` and carried in persisted `result.meta` so replay reproduces them. Mutation tools keep a diff result because the completed view replaces the pending card.
Hard rules (they bite if broken):
- **Purity.** These run on live streaming AND on session-log REPLAY, so they must be pure functions of `args` (+ the result) — NO I/O, NO reading session state, NO clock/random. A diff is derived from the args (`write` uses `oldText: null` because a call-time presenter has no prior file content); the BRIDGE, not the tool, fills the session cwd and relativizes a display-path title. If you find yourself wanting the file's old content or the working directory inside `presentCall`, stop — that belongs on the bridge or a future result-event shape, not the presenter.
- **UI-only formatting stays out of the model result.** A fenced ` ```console ` block, a diff, a relativized path—none of these belongs in the canonical value or Native content merely to serve an editor. `output.render` owns model-facing prose; `presentationMeta` plus the card presenters own replayable UI state. A `terminal` result view carries raw output and the bridge adds fences.
- **Purity.** These run on live streaming AND on session-log REPLAY, so they must be pure functions of `args` (+ the result) — NO I/O, NO reading session state, NO clock/random. A diff is derived from the args (`write` uses `oldText: null` because a call-time presenter has no prior file content); the UI adapter, not the tool, supplies session context. If you find yourself wanting the file's old content or the working directory inside `presentCall`, stop — that belongs in durable result metadata or the adapter, not the presenter.
- **UI-only formatting stays out of the model result.** A fenced ` ```console ` block, a diff, a relativized path—none of these belongs in the canonical value or Native content merely to serve a UI. `output.render` owns model-facing prose; `presentationMeta` plus the card presenters own replayable UI state. A `terminal` result view carries raw output and the adapter adds any fallback framing.
- **`defineTool` soft-validates the display path.** A malformed/older logged arg shape makes the wrapper return `undefined` (a generic fallback) rather than throw — display must never crash a replay.
The neutral vocabulary lives in `dsh-tools` (never import an ACP type into a tool); the ACP bridge maps each `card` to the wire. The design and the why are in [the render-intent-union Agent Note](../../.agents/notes/implemented/architecture/2026-07-02-tool-render-intent-union.md); `dsh-tool-fs` (generic/diff) and `dsh-tool-bash` (terminal) are the reference implementations.
The neutral vocabulary lives in `dsh-tools`; tools never import a UI or transport type. The TUI and host/client runtime map each `card` into their own view. The design and the why are in [the render-intent-union Agent Note](../../.agents/notes/implemented/architecture/2026-07-02-tool-render-intent-union.md); `dsh-tool-fs` (generic/diff) and `dsh-tool-bash` (terminal) are the reference implementations.
## Tests every tool needs
Cover argument rejection, every canonical value and Native rendering shape, output-schema rejection, and HMR disposal. For a side-effecting tool, drive the real tool through the agent loop with a scripted `MockAdapter` and assert its `tool/call` and projected `tool/result` session events; prove the canonical value itself is not persisted. For an editor card, assert the exact `presentCall` and `presentResult` views and add an [ACP snapshot](../../.agents/notes/implemented/testing/2026-06-19-acp-snapshot-tests.md) through the real bridge; a terminal card's scenario sets `terminalOutput: true` to exercise the capable-client path.
Cover argument rejection, every canonical value and Native rendering shape, output-schema rejection, and HMR disposal. For a side-effecting tool, drive the real tool through the agent loop with a scripted `MockAdapter` and assert its `tool/call` and projected `tool/result` session events; prove the canonical value itself is not persisted. For a UI card, assert the exact `presentCall` and `presentResult` views and exercise the owning TUI or host/client projection. Add an assembled snapshot for the shipped model or UI behavior the tool changes.

View File

@@ -64,9 +64,9 @@ producer 提供同步的 `cancel`、在资源清理后 settle 且不 reject 的
请把 `output.schema` 设计为实用的程序化 API直接返回句柄与字段当标量、数组或 null 确实就是结果时,允许采用相应的根类型;将面向人类的解释放入 `output.render`。中间值只存在于执行期间,不会被持久化或按提示词上限截断,也不设字节上限,因此生产方如实声明的采集边界和进程内存仍然重要。只有外层 `run_code` 日志/结果会受到可配置输出上限和面向模型的输出落盘流水线约束。
## 工具在编辑器中的渲染方式ACP 展示)
## 工具在 UI 中的渲染方式
工具的 `output.render` 返回模型可见的内容;其**编辑器卡片**是另一项独立关注点,通过纯展示投影以及可选的 `presentCall``presentResult` 方法声明。请将这些内容与规范值一并设计:编辑器(如 Zed通过 ACPAgent Client Protocol桥接会展示该卡片没有 UI 展示方法的工具回退到通用卡片(标题 = 工具名,原始 args 作为输入)。
工具的 `output.render` 返回模型可见的内容;其 **UI 卡片** 是另一项独立关注点,通过纯展示投影以及可选的 `presentCall``presentResult` 方法声明。请将这些内容与规范值一并设计没有 UI 展示方法的工具回退到通用卡片(标题 = 工具名,原始 args 作为输入)。
两个方法都返回一个 **`card` 标签的渲染意图**——选择与你的工具行为匹配的卡片类型:
@@ -76,17 +76,17 @@ producer 提供同步的 `cancel`、在资源清理后 settle 且不 reject 的
- `{ card: 'diff', title, diffs, locations? }`——你的调用创建或修改文件。`diffs: [{ path, oldText, newText }]`(新文件时 `oldText: null`)渲染为内联 diff 卡片。tool-fs `write`/`edit`。)
- `presentResult(args, { content, isError, meta? })` 返回完成后的卡片:
- `generic` 提供可选的标题和内容。
- `terminal` 提供原始输出和可选的退出元数据;桥接层渲染能力特定或围栏回退视图。
- `diff` 提供已应用的 hunk通常由 `output.presentationMeta` 派生并通过持久化的 `result.meta` 携带,使回放能重现它们。变更类工具保留 diff 结果,因为 ACP 更新会替换 pending 卡片的内容
- `terminal` 提供原始输出和可选的退出元数据;各 UI 根据自身能力渲染对应视图或回退视图。
- `diff` 提供已应用的 hunk通常由 `output.presentationMeta` 派生并通过持久化的 `result.meta` 携带,使回放能重现它们。变更类工具保留 diff 结果,因为完成后的视图会替换 pending 卡片。
硬性规则(违反会出问题):
- **纯函数。** 这些方法在实时流式输出和会话日志回放时都会运行,因此必须是 `args`(加 result的纯函数——不做 I/O、不读会话状态、不用时钟/随机数。diff 从 args 派生(`write` 使用 `oldText: null`,因为调用时的展示器没有文件先前内容);**桥接层**(而非工具)填充会话 cwd 并相对化展示路径标题。如果你发现自己想在 `presentCall` 内获取文件旧内容或工作目录,请停下——那属于桥接层或未来的 result-event 形态,不属于展示器。
- **UI 格式不进入模型结果。** 围栏 ` ```console ` 块、diff、相对化路径均不应仅为服务编辑器而进入规范值或 Native 内容。`output.render` 负责模型可见的自然语言;`presentationMeta` 和卡片展示器负责可回放的 UI 状态。`terminal` 结果视图携带原始输出,由桥接层添加围栏
- **纯函数。** 这些方法在实时流式输出和会话日志回放时都会运行,因此必须是 `args`(加 result的纯函数——不做 I/O、不读会话状态、不用时钟/随机数。diff 从 args 派生(`write` 使用 `oldText: null`,因为调用时的展示器没有文件先前内容);会话上下文由 UI 适配器而非工具提供。如果你发现自己想在 `presentCall` 内获取文件旧内容或工作目录,请停下那属于持久结果元数据或适配器,不属于展示器。
- **UI 格式不进入模型结果。** 围栏 ` ```console ` 块、diff、相对化路径均不应仅为服务 UI 而进入规范值或 Native 内容。`output.render` 负责模型可见的自然语言;`presentationMeta` 和卡片展示器负责可回放的 UI 状态。`terminal` 结果视图携带原始输出,由适配器按需添加回退格式
- **`defineTool` 对展示路径做软校验。** 格式错误或旧版日志中的 arg 形态会使包装器返回 `undefined`(通用回退)而非抛异常——展示绝不能导致回放崩溃。
中性词汇定义在 `dsh-tools`(绝不在工具中导入 ACP 类型ACP 桥接层将每个 `card` 映射到协议格式wire format。设计与原因见[渲染意图联合体 Agent Note](../../.agents/notes/implemented/architecture/2026-07-02-tool-render-intent-union.md)`dsh-tool-fs`generic/diff`dsh-tool-bash`terminal是参考实现。
中性词汇定义在 `dsh-tools`;工具绝不导入 UI 或传输类型。TUI 和 host/client 运行时将每个 `card` 映射到各自的视图。设计与原因见[渲染意图联合体 Agent Note](../../.agents/notes/implemented/architecture/2026-07-02-tool-render-intent-union.md)`dsh-tool-fs`generic/diff`dsh-tool-bash`terminal是参考实现。
## 每个工具必须的测试
覆盖参数拒绝、每种规范值和 Native 渲染形态、输出 schema 拒绝以及 HMR dispose。对于有副作用的工具使用脚本化的 `MockAdapter` 驱动真实工具通过 agent loop智能体循环并断言其 `tool/call` 和投影后的 `tool/result` 会话事件;同时证明规范值本身未被持久化。对于编辑器卡片,断言 `presentCall``presentResult` 的精确视图,并通过真实桥接层添加一个 [ACP 快照](../../.agents/notes/implemented/testing/2026-06-19-acp-snapshot-tests.md);终端卡片的场景设置 `terminalOutput: true` 以覆盖 capable-client 路径
覆盖参数拒绝、每种规范值和 Native 渲染形态、输出 schema 拒绝以及 HMR dispose。对于有副作用的工具使用脚本化的 `MockAdapter` 驱动真实工具通过 agent loop智能体循环并断言其 `tool/call` 和投影后的 `tool/result` 会话事件;同时证明规范值本身未被持久化。对于 UI 卡片,断言 `presentCall``presentResult` 的精确视图,并实际运行所属 TUI 或 host/client 投影。如果工具改变了已交付的模型或 UI 行为,请添加组装应用快照

View File

@@ -2,5 +2,5 @@
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write
extension-cookbook.md: c13b46e06a3b34512cd371e6a4868a6e932a575f
extension-cookbook.zh.md: ef099ff318488ba26a3ba4fe736c5f320bdce4f3
extension-cookbook.md: 0ab337377518f832c0649bc80cf2941751cb934f
extension-cookbook.zh.md: f7c2572d0b91867589636249f29896848f8aec82

View File

@@ -58,11 +58,11 @@ export function apply(ctx: Context) {
}
```
## A client-driver plugin (external protocol bridge)
## An external protocol driver
A *client driver* is a UI plugin for a wire-protocol peer. It owns stdio, so stdout logging must be disabled, creates or resumes agents through the factory, maps harness events to protocol messages, and maps requests to `followup()` or `cancel()`. Settle each request exactly once from durable `turn/end`, even if rendering fails, and tear agents down with `AgentHandle.dispose()` so disposal reaches quiescence.
A *protocol driver* adapts a wire peer to `ctx.agents`; it may serve a UI or an automation client. A stdio driver owns stdout, creates or resumes agents through the factory, maps the protocol's requests to `followup()` or `cancel()`, and settles each request exactly once from durable `turn/end`. Tear agents down with `AgentHandle.dispose()` so disposal reaches quiescence.
`packages/ui/acp` is the worked example: it bridges the agent to the Agent Client Protocol (JSON-RPC over stdio) so Zed and other ACP editors can drive it. See its README for the full method surface and the permission-prompt answerer it registers on the approval seam.
[`packages/acp/acp`](../../packages/acp/acp) is the automation-only worked example: it exposes fresh text sessions over Agent Client Protocol JSON-RPC stdio, emits committed assistant text, and registers a one-shot machine permission answerer for agents it owns. Its [README](../../packages/acp/acp/README.md) owns the exact method and lifecycle contract.
```ts
import type { Context } from 'cordis'

View File

@@ -58,11 +58,11 @@ export function apply(ctx: Context) {
}
```
## 客户端驱动插件(外部协议桥接)
## 外部协议驱动
*客户端驱动*是面向协议格式wire format对端的 UI 插件。它拥有 stdio因此必须禁用 stdout 日志;通过工厂创建或恢复 agent智能体;将 harness 事件映射为协议消息;将请求映射为 `followup()``cancel()`。每个请求从持久的 `turn/end` 恰好结算一次(即使渲染失败),并通过 `AgentHandle.dispose()` 拆除 agent确保 dispose资源释放流程完全停稳。
*协议驱动*将协议对端接入 `ctx.agents`;它可以服务于 UI 或自动化客户端。stdio 驱动拥有 stdout通过工厂创建或恢复 agent智能体,将协议请求映射为 `followup()``cancel()`,并根据持久的 `turn/end` 对每个请求恰好结算一次通过 `AgentHandle.dispose()` 拆除 agent以使 dispose资源释放达到完全停稳。
`packages/ui/acp` 是完整的工作示例:它将 agent 桥接到 ACPAgent Client Protocol基于 stdio 的 JSON-RPC使 Zed 及其他 ACP 编辑器能够驱动它。其 README 描述了完整的方法接口以及它在审批 seam 上注册的权限提示应答器
[`packages/acp/acp`](../../packages/acp/acp) 是仅面向自动化的完整示例:它通过 ACPAgent Client ProtocolJSON-RPC stdio 提供全新文本会话,发出已提交的助手文本,并为其拥有的 agent 注册一次性机器权限应答器。其 [README](../../packages/acp/acp/README.md) 拥有精确的方法和生命周期契约
```ts
import type { Context } from 'cordis'

View File

@@ -489,6 +489,26 @@ A command was registered or unregistered. This is an unfiltered registry notific
Source: [`packages/ui/commands/src/index.ts:103`](../../packages/ui/commands/src/index.ts)
## `domain/*`
### `domain/changed` — emit
A domain record or the global singleton changed, emitted once per write strictly after the backend acknowledged durability. Events of one domain arrive in its write-chain order.
```ts cordis-catalog
/**
* A domain record or the global singleton changed, emitted once per write
* strictly after the backend acknowledged durability. Events of one
* domain arrive in its write-chain order.
* @param change - domain, table (`''` for global), key (`''` for global),
* operation discriminant, and on `put` the new snapshot.
* @mode emit
*/
'domain/changed'(change: DomainChanged): void
```
Source: [`packages/storage/storage-domain/src/events.ts:46`](../../packages/storage/storage-domain/src/events.ts)
## `fs/*`
### `fs/edit-intent` — waterfall

View File

@@ -319,6 +319,50 @@ Types: [DshEnvironment](../core-data-structures/bash.md) · [ToolExecution](../c
Source: [`packages/bash/tool-bash/src/index.ts:104`](../../packages/bash/tool-bash/src/index.ts)
## `ctx.clientModuleHost` — `ClientModuleHostService`
The web plugin table service: incremental dshClient scan + wire composition + bundle route + index tap. Construction runs the activation scan synchronously — a malformed declaration or missing bundle among the already-loaded entries aggregates into one loud throw (FAILED fiber; the boot sweep reports it).
```ts cordis-catalog
/**
* Current composed entry graph (stable object between changes).
* @returns the graph served as `window.__DSH_BOOT__`.
*/
graph(): WebBootGraph
/**
* Absolute path of an entry's client bundle.
* @param id - entry id (package name).
* @returns the path, or undefined for an unknown id.
*/
clientPath(id: string): string | undefined
/**
* Re-hash one bundle (the HMR watch's registration hook — the only entry
* point through which bundle content changes reach the graph).
* @param id - entry id (package name).
* @returns the new rev, or undefined for an unknown id.
*/
rebuilt(id: string): string | undefined
/**
* Subscribe to bundle rebuilds; fires only when the re-hash changed the rev.
* @param listener - receives the entry id and its new bundle rev.
* @returns the unsubscriber.
*/
onRebuilt(listener: (id: string, rev: string) => void): () => void
/**
* Fires after any flush that recomposed the graph (row added/removed, or a
* rebuilt rev change). Pull model: listeners re-read {@link graph}.
* @param listener - notified with no payload.
* @returns the unsubscriber.
*/
onGraphChanged(listener: () => void): () => void
```
Source: [`packages/client/modules/src/index.ts:143`](../../packages/client/modules/src/index.ts)
## `ctx.codeRuntime` — `CodeRuntime` (abstract seam)
Registers one `ctx.codeRuntime` implementation. Program, budget, abort, and substrate failures resolve in CodeRunResult; only seam misuse rejects. Implementations bridge structured-cloneable bindings, materialize each declared namespace rejection class, treat programs as hostile peers, isolate runs from one another, and terminate and await in-flight runs during disposal.
@@ -614,6 +658,30 @@ Types: [Agent](../core-data-structures/core.md) · [CreateGoalRequest](../core-d
Source: [`packages/goal/goal/src/index.ts:135`](../../packages/goal/goal/src/index.ts)
## `ctx.httpServer` — `HttpServerService`
The web-shape HTTP carrier service. Activation listens immediately (route registration order carries no request-facing semantics: named routes are composed to be disjoint, and the static dist fallback answers anything not yet claimed during the boot window). A listen failure throws out of init — a FAILED fiber the boot's fail-loud sweep reports.
```ts cordis-catalog
/**
* Register a named route. Duplicate (kind, path) throws — route patterns are
* a composition-level contract, so a collision is a misconfiguration.
* @param route - kind, path, and the owning handler.
* @returns the disposer removing the route.
*/
register(route: WebRoute): () => void
/**
* Register an index.html transform, applied to every index response in
* registration order.
* @param transform - pure html-to-html function.
* @returns the disposer removing the transform.
*/
tapIndex(transform: (html: string) => string): () => void
```
Source: [`packages/host/webserver/src/index.ts:55`](../../packages/host/webserver/src/index.ts)
## `ctx.invariants` — `InvariantService`
Package-owned invariant registry with global and regex-based selection.
@@ -1353,6 +1421,73 @@ Types: [SaveTextSpill](../core-data-structures/spill.md) · [SpillRef](../core-d
Source: [`packages/spill/spill/src/index.ts:45`](../../packages/spill/spill/src/index.ts)
## `ctx.storage` — `Storage`
The storage hub service. Backends register under `backend`; data forms mount under their `StorageForms` key and are reached as `ctx.storage.<form>`.
```ts cordis-catalog
/**
* Mount a data-form facility on the hub. Mounting is an effect: the
* returned disposer unmounts the form.
* @param form - Form key declared in {@link StorageForms}.
* @param facility - The facility instance to expose.
* @returns the disposer that unmounts the form.
*/
mount<K extends keyof StorageForms>(form: K, facility: StorageForms[K]): () => void
/**
* Resolve a mounted data form.
* @param form - Form key declared in {@link StorageForms}.
* @returns the mounted facility.
*/
form<K extends keyof StorageForms>(form: K): StorageForms[K]
```
Source: [`packages/storage/storage/src/index.ts:47`](../../packages/storage/storage/src/index.ts)
## `ctx.storageDomain` — `DomainFacility`
The mounted domain facility. Opens declared domains over routed backends; one facility instance owns the open-domain table and enforces single-open per domain name.
```ts cordis-catalog
/**
* Open one declared domain. Steps, each failing the whole call: reject a
* name that is already open (`already-open`); resolve the backend route
* (`backend-not-found` passes through from the hub); require its `kv` facet
* (`facet-unsupported`); open the unit projected from the spec (backend
* `version-mismatch`/`malformed-medium` pass through); load and validate
* every stored record against the spec's zod schemas (`invalid-record`
* with the offending table and key); construct the domain.
*
* Lifecycle: the CALLER owns the returned handle and closes it via
* `Domain.close()` (typically as its own `ctx.effect` disposer) — the
* facility does not tie the domain to any consumer fiber. Domains still
* open when the facility unmounts are closed by the plugin disposer.
* @param spec - The domain declaration, typically from `defineDomain`.
* @returns the opened domain handle, typed by the spec.
*/
async open<S extends DomainSpec>(spec: S): Promise<Domain<S>>
/**
* Look up an open domain by name, untyped. Diagnostic surface (the package
* invariant cross-checks change events against live domain state); typed
* consumers hold the handle returned by {@link open}.
* @param name - Domain name.
* @returns the open domain runtime, or `undefined` when not open.
*/
get(name: string): DomainImpl | undefined
/**
* Close every domain still open on this facility. The unmount path for
* consumers that never called `Domain.close()` themselves; closing is
* idempotent, so double-closing an already-closed domain is harmless.
* @returns resolution after every unit is released.
*/
async closeAll(): Promise<void>
```
Source: [`packages/storage/storage-domain/src/index.ts:69`](../../packages/storage/storage-domain/src/index.ts)
## `ctx.subagents` — `SubagentService`
Named provider registry and capability-checked start surface.
@@ -1813,6 +1948,62 @@ Types: [WorkflowRun](../core-data-structures/workflow.md) · [WorkflowStartReque
Source: [`packages/workflow/workflow/src/index.ts:159`](../../packages/workflow/workflow/src/index.ts)
## `ctx.workspace` — `WorkspaceRegistry`
Durable workspace registry. Startup waits for `sessionPersistence`, builds one canonical-cwd header index, and completes the one-time history bootstrap before the service becomes active. The persistence dependency is mandatory so an unavailable peer can never be mistaken for an empty history and commit the initialized marker.
```ts cordis-catalog
/**
* Create or reuse a workspace for an existing directory. The path is
* canonicalized through `fs.realpath`; a nonexistent path rejects with the
* original error and a non-directory rejects. Repeated calls for the same
* canonical path return the existing entity without changing its title.
* A newly created workspace is prepended to the durable registry order.
* A different canonical path cannot create a duplicate display title.
* @param path - Existing directory to own, in any path spelling.
* @param title - Display title used only when a new record is created.
* @returns the existing or newly durable workspace.
*/
async create(path: string, title?: string): Promise<Workspace>
/**
* Look up a workspace by id.
* @param id - Workspace id.
* @returns the workspace, or `undefined` when unknown.
*/
get(id: WorkspaceId): Workspace | undefined
/**
* Synchronous workspace projection in durable registry order. Every
* entity's `sessionIds` getter is already filtered by the startup/live
* canonical-cwd header index; this method performs no persistence reads.
* @returns a fresh ordered array of workspace entities.
*/
list(): Workspace[]
/**
* Move one accounted, cwd-validated session to the front of its workspace.
* Ungrouped sessions and candidates filtered by the header check are
* no-ops. The owning workspace's relative position never changes.
* @param sessionId - Session whose activity was observed.
* @returns resolution after the possible record write.
*/
async touchSession(sessionId: SessionId): Promise<void>
/**
* Resolve by canonical directory path without creating or mutating a
* workspace. A missing path rejects during `realpath`; an existing unowned
* directory returns `undefined`.
* @param path - Existing directory path in any spelling.
* @returns the workspace owning the canonical path, when one exists.
*/
async resolveByPath(path: string): Promise<Workspace | undefined>
```
Types: [SessionId](../core-data-structures/core.md)
Source: [`packages/workspace/workspace/src/index.ts:75`](../../packages/workspace/workspace/src/index.ts)
## Inherited `ctx` members (cordis core + loader/hmr/timer)
The framework `ctx` surface every plugin also sees, beyond the harness services above. This is pinned vendor source ([vendoring policy](../../vendor/README.md)); it is summarized here so the page is a complete picture of what `ctx` offers, without elevating framework internals to the harness tier's prominence.

View File

@@ -2,5 +2,5 @@
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write
approval.md: c9b411e38508fc7e8ac2e2dd923d9c8cc2f475a3
approval.zh.md: c3d67b195a0c04186911285ccb76cd89eceb421b
approval.md: 0de13eb1504b5ecc4ac0eafefefa2c87562c89b4
approval.zh.md: b7ba10449fccfdb0dd5b52374e14038e3ec76ff8

View File

@@ -2,7 +2,7 @@
English | [中文](approval.zh.md)
The user-approval seam of [dsh-user-approval](../../packages/ui/user-approval) answers one question: may this specific action proceed? It owns the shared request/outcome vocabulary, the `ctx.approval` dispatch service, the `approval/request` answerer waterfall, the log-only audit pair, and the per-session `ask`/`never` policy. UI channels such as [dsh-acp](../../packages/ui/acp) provide answerers; callers such as [dsh-tools](../../packages/core/tools) and [dsh-tool-bash](../../packages/bash/tool-bash) consume the closed outcome and fail closed unless it is `allowed-once`.
The user-approval seam of [dsh-user-approval](../../packages/ui/user-approval) answers one question: may this specific action proceed? It owns the shared request/outcome vocabulary, the `ctx.approval` dispatch service, the `approval/request` answerer waterfall, the log-only audit pair, and the per-session `ask`/`never` policy. UI channels may provide human answerers; the [ACP automation bridge](../../packages/acp/acp) provides one-shot machine decisions for its own agents. Callers such as [dsh-tools](../../packages/core/tools) and [dsh-tool-bash](../../packages/bash/tool-bash) consume the closed outcome and fail closed unless it is `allowed-once`.
Source: [`packages/ui/user-approval/src/index.ts`](../../packages/ui/user-approval/src/index.ts)
@@ -48,7 +48,7 @@ type ApprovalOutcome = 'allowed-once' | 'rejected' | 'cancelled' | 'unavailable'
type ApprovalPolicy = 'ask' | 'never'
```
The prompt section states the deterministic `never` behavior and records either policy with a source-owned marker. The pre-step narrator reads that marker from the logged request header after restart; it does not infer state from deployment persona prose. An idle ACP switch is held in the bridge until the next `turn/start`, because approval audit and policy events must remain turn-enclosed for durable replay.
The prompt section states the deterministic `never` behavior and records either policy with a source-owned marker. The pre-step narrator reads that marker from the logged request header after restart; it does not infer state from deployment persona prose.
## Approval request

View File

@@ -2,7 +2,7 @@
[English](approval.md) | 中文
[dsh-user-approval](../../packages/ui/user-approval) 的用户审批 seam 回答一个问题:这个具体操作是否可以继续?它拥有共享的请求/结果词汇、`ctx.approval` 分发服务、`approval/request` 应答者 waterfall瀑布式事件、仅记录日志的审计事件对以及按会话的 `ask`/`never` 策略。UI 通道如 [dsh-acp](../../packages/ui/acp) 提供应答者;调用方如 [dsh-tools](../../packages/core/tools) 和 [dsh-tool-bash](../../packages/bash/tool-bash) 消费闭合的结果,除非结果为 `allowed-once`,否则一律拒绝。
[dsh-user-approval](../../packages/ui/user-approval) 的用户审批 seam 回答一个问题:这个具体操作是否可以继续?它拥有共享的请求/结果词汇、`ctx.approval` 分发服务、`approval/request` 应答者 waterfall瀑布式事件、仅记录日志的审计事件对以及按会话的 `ask`/`never` 策略。UI 通道可以提供人类应答者;[ACPAgent Client Protocol自动化桥接层](../../packages/acp/acp)为其拥有的 agent 提供一次性机器决策。调用方如 [dsh-tools](../../packages/core/tools) 和 [dsh-tool-bash](../../packages/bash/tool-bash) 消费闭合的结果,除非结果为 `allowed-once`,否则一律拒绝。
源码:[`packages/ui/user-approval/src/index.ts`](../../packages/ui/user-approval/src/index.ts)
@@ -48,7 +48,7 @@ type ApprovalOutcome = 'allowed-once' | 'rejected' | 'cancelled' | 'unavailable'
type ApprovalPolicy = 'ask' | 'never'
```
提示词段落会声明 `never` 的确定性行为,并以服务自有的标记记录当前策略。重启后,步骤前叙述器从已记录的请求头中读取该标记,而非从部署 persona 行文中推断状态。ACPAgent Client Protocol空闲切换会在 bridge 中保持,直到下一个 `turn/start`,因为审批审计和策略事件必须保持在轮次内,以确保持久回放的正确性。
提示词段落会声明 `never` 的确定性行为,并以服务自有的标记记录当前策略。重启后,步骤前叙述器从已记录的请求头中读取该标记,而非从部署 persona 行文中推断状态。
## 审批请求

View File

@@ -1,15 +1,15 @@
# Human Commands
The human-command seam of [`dsh-commands`](../../packages/ui/commands). TUI and ACP adapters use it to discover and directly execute plugin-owned commands for an exact agent without creating a model message. The [command Agent Note](../../.agents/notes/implemented/feature/2026-07-19-plugin-command-registration.md) owns dispatch and lifecycle rationale; the [package README](../../packages/ui/commands/README.md) owns composition and limitations.
The human-command seam of [`dsh-commands`](../../packages/ui/commands). Interactive adapters use it to discover and directly execute plugin-owned commands for an exact agent without creating a model message. The [command Agent Note](../../.agents/notes/implemented/feature/2026-07-19-plugin-command-registration.md) owns dispatch and lifecycle rationale; the [package README](../../packages/ui/commands/README.md) owns composition and limitations.
Source: [`packages/ui/commands/src/index.ts`](../../packages/ui/commands/src/index.ts)
## Input metadata
ACP currently exposes one unstructured-input hint. Command availability follows plugin composition: every adapter consuming the registry sees every effective definition.
The seam exposes one optional unstructured-input hint. Command availability follows plugin composition: every adapter consuming the registry sees every effective definition.
```ts type-equiv
/** Immutable command input metadata compatible with ACP unstructured input. */
/** Immutable metadata for a command's optional unstructured input. */
interface CommandInputDescriptor {
/** Placeholder shown before the user supplies free-form input. */
readonly hint: string

View File

@@ -2,5 +2,5 @@
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write
llm-streaming.md: cb99c935aea2dc9cc769e3056fdb98a2e5c9eacb
llm-streaming.zh.md: 740fa1f796088e63d0195cfbecf975adb236381c
llm-streaming.md: fb97e74a9ec01e62ba940295112fb157cc73bd1d
llm-streaming.zh.md: fda59a64aeef1c037372d69dbc15ee0de48de222

View File

@@ -63,6 +63,7 @@ Every adapter MUST obey these, and every consumer may rely on them:
- **One adapter call is one provider attempt.** Adapters disable library retries. Agent-level recovery opens another durable numbered step; direct `ctx.llm.stream()` callers remain single-attempt.
- **Provider stalls are bounded at the transport.** Both shipping remote adapters expose positive finite `streamIdleTimeoutMs` with a five-minute default. The watchdog arms only while iterator `next()` is outstanding, uses one stable signal for the whole request, maps its own expiry to `TIMEOUT`, and keeps an earlier caller abort as `ABORTED`.
- **Context overflow has one canonical code.** Both DeepSeek adapters classify explicit provider detail through `isContextWindowExceededError()` and surface `CONTEXT_WINDOW_EXCEEDED`, whether the failure arrives as a thrown HTTP `LlmError` or an in-band finish error. Consumers route on the code, never provider text.
- **An empty completion is a retryable error, not a silent success.** Both adapters map a terminal `stop` finish that carried no content blocks to `finish {kind:'error'}` with the canonical `EMPTY_RESPONSE` code, and `dsh-llm-retry` retries it by default; see [empty model responses are retryable](../../.agents/notes/implemented/bug-fix/2026-07-24-empty-model-response-is-retryable.md).
- **Every provider HTTP request carries the app-attribution header.** Adapters send `attributionHeaders()` (below) - the `User-Agent` baseline - and prove it with a wire-level test (mock server asserting the received header, or the library's header hook for a library-backed adapter).
- **Replay state is adapter-owned.** A successful `finish` may carry lossless-JSON state needed to reconstruct a native provider response. The loop stores it with the assembled assistant message unless an `agent/step-result` listener rewrote the content. On a later request, `LlmService` passes the state only when the historical provider and target provider are currently registered to the exact same adapter instance. That adapter validates the state and owns any cross-model or cross-provider conversion; other adapters receive the provider-neutral content and provenance without the private state.

View File

@@ -63,6 +63,7 @@ interface LlmFailure {
- **一次适配器调用就是一次提供方尝试。** 适配器禁用库重试。agent 层恢复会打开另一个持久、带编号的步骤;直接调用 `ctx.llm.stream()` 的调用方仍然只尝试一次。
- **提供方停顿在传输层受到时限约束。** 两个已交付的远程适配器都暴露正数且有限的 `streamIdleTimeoutMs`默认五分钟。watchdog 只在 iterator `next()` 尚未完成时启动,整个请求使用同一个稳定 signal把自身到期映射为 `TIMEOUT`,并把更早发生的调用方中止保留为 `ABORTED`。
- **上下文溢出只有一个规范 code。** 两个 DeepSeek 适配器都通过 `isContextWindowExceededError()` 对提供方的显式细节分类并暴露 `CONTEXT_WINDOW_EXCEEDED`,无论失败以抛出的 HTTP `LlmError` 还是带内 finish error 到达。消费方按 code 路由,绝不依赖提供方文本。
- **空 completion 是可重试错误,而不是静默的成功结果。** 两个适配器都把没有携带任何内容块的终止性 `stop` 结束映射为携带规范 `EMPTY_RESPONSE` code 的 `finish {kind:'error'}``dsh-llm-retry` 默认会重试它;详见[空模型响应可重试](../../.agents/notes/implemented/bug-fix/2026-07-24-empty-model-response-is-retryable.md)。
- **每个提供方 HTTP 请求都携带应用归属头。** 适配器发送 `attributionHeaders()`(见下文)作为 `User-Agent` 基线并通过协议级测试加以证明mock 服务器断言收到的 header或对基于库的适配器使用库的 header 钩子)。
- **回放状态归适配器所有。** 成功的 `finish` 可以携带重建提供方原生响应所需的无损 JSON 状态。除非 `agent/step-result` listener 改写了内容,否则循环会将其与组装后的 assistant 消息一起存储。后续请求中,仅当历史提供方与目标提供方当前注册到完全相同的适配器实例时,`LlmService` 才会传递该状态。该适配器负责校验状态并拥有所有跨模型或跨提供方转换;其他适配器只会收到提供方无关的内容与 provenance不会收到私有状态。

View File

@@ -2,5 +2,5 @@
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write
persistence.md: dc497fd85f44660c0a981579351b5cfbe0040a4d
persistence.zh.md: 5236f4fe2ba8ad1be7e74bffafebfea19014d7aa
persistence.md: b03cc07d2e514b3900d4035ea386f31c761470a7
persistence.zh.md: 3030ff2fe949cb02385331800d826df227e3d6cd

View File

@@ -20,7 +20,7 @@ Repair applies only to cold sessions. For a live id, `SessionPersistence.load(id
## `SessionLocation` — optional per-session artifact target
`SessionPersistence.locate(meta)` synchronously resolves a backend-owned independent artifact without reading, creating, or flushing it. JSONL returns its absolute target path; SQLite returns `undefined` because sessions share one database. A returned path can therefore name a file that does not yet exist or lacks the current unflushed turn; it is a location hint, not authorization or a freshness guarantee.
`SessionPersistence.locate(meta)` synchronously resolves a backend-owned independent artifact without reading, creating, or flushing it. JSONL returns the absolute transcript path inside its project/session directory; SQLite returns `undefined` because sessions share one database. A returned path can therefore name a file that does not yet exist or lacks the current unflushed turn; it is a location hint, not authorization or a freshness guarantee.
```ts type-equiv
/**

View File

@@ -20,7 +20,7 @@
## `SessionLocation`——可选的逐会话产物目标
`SessionPersistence.locate(meta)` 会同步解析一个归后端所有的独立产物,而不会读取、创建或 flush 它。JSONL 返回其绝对目标路径SQLite 因各会话共享一个数据库而返回 `undefined`。因此,返回的路径可能指向尚不存在、或还不包含当前尚未 flush 的轮次;它是位置提示,不是授权或新鲜度保证。
`SessionPersistence.locate(meta)` 会同步解析一个归后端所有的独立产物,而不会读取、创建或 flush 它。JSONL 返回其项目/会话目录内 transcript文本记录绝对路径SQLite 因各会话共享一个数据库而返回 `undefined`。因此,返回的路径可能指向尚不存在、或还不包含当前尚未 flush 的轮次;它是位置提示,不是授权或新鲜度保证。
```ts type-equiv
/**

View File

@@ -2,5 +2,5 @@
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write
session.md: 7a9ae919fecef7db19f3ca67feb128bcba1db4f0
session.zh.md: 9e1e9a4f39ed6919416ff5cebf345b43bbc450ea
session.md: d789ffcabb5cb0c744e265b61e322831c1d8a04f
session.zh.md: f4f102861db7403520e9f38cb56613e430718cbe

View File

@@ -143,7 +143,7 @@ interface OutOfBandSessionEventMap {}
### `TodoItem` — one todo-list entry
The unit of the `todo/write` event's whole-list snapshot. Deliberately minimal — a `content` line and a three-state `status` (no id, priority, or `activeForm`): the list is replaced wholesale on every write, so entries need no stable identity, and the status triple is exactly the ACP `PlanEntryStatus`, so a UI bridge can map a todo list onto an ACP `plan` 1:1 (synthesizing the priority ACP additionally requires). See the [todo_write Agent Note](../../.agents/notes/implemented/feature/2026-06-29-todo-write-tool.md).
The unit of the `todo/write` event's whole-list snapshot. Deliberately minimal — a `content` line and a three-state `status` (no id, priority, or `activeForm`): the list is replaced wholesale on every write, so entries need no stable identity. See the [todo_write Agent Note](../../.agents/notes/implemented/feature/2026-06-29-todo-write-tool.md).
```ts type-equiv
/**
@@ -152,10 +152,9 @@ The unit of the `todo/write` event's whole-list snapshot. Deliberately minimal
*
* Deliberately minimal: a human-readable `content` line and a three-state
* `status`. No id, priority, or `activeForm` — the list is replaced wholesale
* on every write (last-write-wins), so entries need no stable identity, and the
* status triple is exactly the ACP `PlanEntryStatus`, so a UI bridge can map a
* todo list onto an ACP `plan` 1:1 (synthesizing the priority ACP additionally
* requires).
* on every write (last-write-wins), so entries need no stable identity. The
* three statuses describe the complete portable lifecycle needed by model and
* UI consumers.
*/
interface TodoItem {
/** What this task is — a short imperative line shown in the UI. */

View File

@@ -143,7 +143,7 @@ interface OutOfBandSessionEventMap {}
### `TodoItem`:一条待办项
这是 `todo/write` 事件全量列表快照中的单元。它有意保持精简:一行 `content` 加一个三态 `status`(没有 id、优先级或 `activeForm`);列表在每次写入时整体替换,因此条目无需稳定标识,而这三个状态值恰好对应 ACP 的 `PlanEntryStatus`,所以 UI 桥接层可以将待办列表一一映射为 ACP `plan`(并合成 ACP 额外要求的优先级)。见 [todo_write Agent Noteagent 决策记录)](../../.agents/notes/implemented/feature/2026-06-29-todo-write-tool.md)。
这是 `todo/write` 事件全量列表快照中的单元。它有意保持精简:一行 `content` 加一个三态 `status`(没有 id、优先级或 `activeForm`);列表在每次写入时整体替换,因此条目无需稳定标识。见 [todo_write Agent Noteagent 决策记录)](../../.agents/notes/implemented/feature/2026-06-29-todo-write-tool.md)。
```ts type-equiv
/**
@@ -152,10 +152,9 @@ interface OutOfBandSessionEventMap {}
*
* Deliberately minimal: a human-readable `content` line and a three-state
* `status`. No id, priority, or `activeForm` — the list is replaced wholesale
* on every write (last-write-wins), so entries need no stable identity, and the
* status triple is exactly the ACP `PlanEntryStatus`, so a UI bridge can map a
* todo list onto an ACP `plan` 1:1 (synthesizing the priority ACP additionally
* requires).
* on every write (last-write-wins), so entries need no stable identity. The
* three statuses describe the complete portable lifecycle needed by model and
* UI consumers.
*/
interface TodoItem {
/** What this task is — a short imperative line shown in the UI. */

View File

@@ -2,5 +2,5 @@
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write
subagent.md: 0335a3f0780ae17b57ae730f5a49a269261c8073
subagent.zh.md: dac48b624f6e0cfc28737e3e1a2774ba2d97e85b
subagent.md: 2497dbab9cfc8304eb7aaeba7109404ac614bbff
subagent.zh.md: 2d96e9bc635951746e72ed58a7c3638dc2598cc2

View File

@@ -19,16 +19,13 @@ A provider advertises its **start-time** features on a static descriptor the ser
* is rejected with a typed error rather than accepted-then-ignored (the "fail loud, no silent
* degradation" rule). These static flags cover features needed before a run exists; runtime
* capabilities such as steering and resume are optional {@link SubagentRun} methods whose presence
* is the capability.
* is the capability. Each flag corresponds one-to-one to a {@link SubagentStartRequest} option:
* `depthLimit` to `maxDepth`; the other names match.
*/
interface SubagentCapabilities {
/** Honor {@link SubagentStartRequest.outputSchema} (structured final output). */
readonly outputSchema: boolean
/** Enforce {@link SubagentStartRequest.maxDepth} (recursion cap). */
readonly depthLimit: boolean
/** Enforce {@link SubagentStartRequest.toolFilter} (child tool scoping). */
readonly toolFilter: boolean
/** Honor {@link SubagentStartRequest.persona} (a per-child persona). */
readonly persona: boolean
}
```
@@ -45,16 +42,12 @@ The tool layer builds this request from the model input and its own config; the
* passes it to {@link SubagentProvider.start}.
*/
interface SubagentStartRequest {
/** The task/prompt for the child agent (a user message in the child session). */
/** Content delivered as the child's user message. */
readonly prompt: ContentBlock[]
/**
* The spawning ("parent") agent — the one whose tool call started this
* subagent. REQUIRED: in-process backends read `parent.session.header` for
* the working directory, the `parentSession` lineage to stamp on the child,
* and the parent's delegation depth. The out-of-process backend (ACP) reads
* exactly one field — the session header's cwd, the child's workspace when
* no deployment `cwd` override is configured; nothing else crosses the
* process boundary.
* The spawning agent. In-process providers derive workspace, lineage, and
* delegation depth from its durable session state. ACP reads only its cwd,
* and only when no deployment `cwd` override is configured.
*/
readonly parent: Agent
/**
@@ -65,7 +58,6 @@ interface SubagentStartRequest {
* afterward.
*/
readonly signal: AbortSignal
/** Per-child agent options (model and plugin-defined extension fields). */
readonly agentOptions?: AgentOptions
/**
* Object-rooted JSON Schema within `assertObjectJsonSchema`'s enforced subset. Start rejects
@@ -137,9 +129,9 @@ interface SubagentResult {
interface SubagentStopReasonMap {
/** The child finished its turn normally. */
completed: 'completed'
/** The run was cancelled by its request signal or by disposal. */
/** Cancelled through the request signal or disposal. */
aborted: 'aborted'
/** The child failed (model error, transport error). */
/** Model or transport failure. */
error: 'error'
/** The child hit its token ceiling before finishing. */
'max-tokens': 'max-tokens'
@@ -180,9 +172,8 @@ interface SubagentRun {
*/
readonly result: Promise<SubagentResult>
/**
* Cancel remaining work, reach child quiescence, and release the run's
* resources (in-process: dispose the owned agent and remove its session;
* ACP: kill and reap the subprocess). Idempotent.
* Cancel remaining work, reach child quiescence, and release resources.
* Idempotent.
*/
dispose(): Promise<void>
/**
@@ -206,12 +197,9 @@ Each provider is a named child-agent transport, and multiple providers may coexi
```ts type-equiv
/**
* A subagent backend: one transport for running a child agent (in-process
* spawn/fork, ACP to another process, …). Implementations register under a
* unique name via {@link SubagentService.registerProvider}; multiple providers
* coexist in one context (unlike the single-implementation bash seam). The
* Providers are trusted same-process implementations; callers treat their
* descriptors and returned values as borrowed immutable data.
* One registered transport for running child agents. Providers are trusted
* same-process implementations; callers treat descriptors and returned values
* as borrowed immutable data.
*/
interface SubagentProvider {
/** Unique registry name (e.g. `spawn`, `fork`, `acp`). */

View File

@@ -19,16 +19,13 @@ subagent seam一个 agent智能体将工作委派给子 agent。与 [ba
* is rejected with a typed error rather than accepted-then-ignored (the "fail loud, no silent
* degradation" rule). These static flags cover features needed before a run exists; runtime
* capabilities such as steering and resume are optional {@link SubagentRun} methods whose presence
* is the capability.
* is the capability. Each flag corresponds one-to-one to a {@link SubagentStartRequest} option:
* `depthLimit` to `maxDepth`; the other names match.
*/
interface SubagentCapabilities {
/** Honor {@link SubagentStartRequest.outputSchema} (structured final output). */
readonly outputSchema: boolean
/** Enforce {@link SubagentStartRequest.maxDepth} (recursion cap). */
readonly depthLimit: boolean
/** Enforce {@link SubagentStartRequest.toolFilter} (child tool scoping). */
readonly toolFilter: boolean
/** Honor {@link SubagentStartRequest.persona} (a per-child persona). */
readonly persona: boolean
}
```
@@ -45,16 +42,12 @@ interface SubagentCapabilities {
* passes it to {@link SubagentProvider.start}.
*/
interface SubagentStartRequest {
/** The task/prompt for the child agent (a user message in the child session). */
/** Content delivered as the child's user message. */
readonly prompt: ContentBlock[]
/**
* The spawning ("parent") agent — the one whose tool call started this
* subagent. REQUIRED: in-process backends read `parent.session.header` for
* the working directory, the `parentSession` lineage to stamp on the child,
* and the parent's delegation depth. The out-of-process backend (ACP) reads
* exactly one field — the session header's cwd, the child's workspace when
* no deployment `cwd` override is configured; nothing else crosses the
* process boundary.
* The spawning agent. In-process providers derive workspace, lineage, and
* delegation depth from its durable session state. ACP reads only its cwd,
* and only when no deployment `cwd` override is configured.
*/
readonly parent: Agent
/**
@@ -65,7 +58,6 @@ interface SubagentStartRequest {
* afterward.
*/
readonly signal: AbortSignal
/** Per-child agent options (model and plugin-defined extension fields). */
readonly agentOptions?: AgentOptions
/**
* Object-rooted JSON Schema within `assertObjectJsonSchema`'s enforced subset. Start rejects
@@ -137,9 +129,9 @@ interface SubagentResult {
interface SubagentStopReasonMap {
/** The child finished its turn normally. */
completed: 'completed'
/** The run was cancelled by its request signal or by disposal. */
/** Cancelled through the request signal or disposal. */
aborted: 'aborted'
/** The child failed (model error, transport error). */
/** Model or transport failure. */
error: 'error'
/** The child hit its token ceiling before finishing. */
'max-tokens': 'max-tokens'
@@ -182,9 +174,8 @@ interface SubagentRun {
*/
readonly result: Promise<SubagentResult>
/**
* Cancel remaining work, reach child quiescence, and release the run's
* resources (in-process: dispose the owned agent and remove its session;
* ACP: kill and reap the subprocess). Idempotent.
* Cancel remaining work, reach child quiescence, and release resources.
* Idempotent.
*/
dispose(): Promise<void>
/**
@@ -208,12 +199,9 @@ interface SubagentRun {
```ts type-equiv
/**
* A subagent backend: one transport for running a child agent (in-process
* spawn/fork, ACP to another process, …). Implementations register under a
* unique name via {@link SubagentService.registerProvider}; multiple providers
* coexist in one context (unlike the single-implementation bash seam). The
* Providers are trusted same-process implementations; callers treat their
* descriptors and returned values as borrowed immutable data.
* One registered transport for running child agents. Providers are trusted
* same-process implementations; callers treat descriptors and returned values
* as borrowed immutable data.
*/
interface SubagentProvider {
/** Unique registry name (e.g. `spawn`, `fork`, `acp`). */

View File

@@ -2,5 +2,5 @@
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write
tools.md: 43d91e72d83dce0bec99417ca90f9394f4c2169e
tools.zh.md: b92f6d3d99fda7dc28d10556adcceee3946a9571
tools.md: 4a8081154a12a0d30e90f8b4df059ddc4257bba0
tools.zh.md: 74f543548a54e532d1a858dee33bed906e82f4b3

View File

@@ -407,8 +407,8 @@ type ObjectJsonSchema = JsonSchemaNode & { type: 'object' }
How a tool wants its call shown in a UI (an editor tool-call card, a CLI log line), provider-neutral so a tool describes itself without depending on any client protocol. `presentCall`/`presentResult` return a **`card`-tagged render intent** — a discriminated union a UI bridge switches on:
- `ToolCallView` (pending): `{ card: 'generic', title, kind?, rawInput?, content?, locations? }` (the default card; `locations` is `{ path, line? }[]` files the call reads/modifies, for editor follow-along), `{ card: 'terminal', title, description?, cwd? }` (a shell command → a terminal card), or `{ card: 'diff', title, diffs, locations? }` (a file create/modify → an inline diff card; `diffs` is `{ path, oldText, newText }[]`, `oldText: null` for a new file).
- `ToolResultView` (completed): `{ card: 'generic', title?, content? }`, `{ card: 'terminal', title?, output?, exitCode?, signal? }` (the captured run output + exit; a capable UI shows an exit-status pill, an incapable one gets a fenced ` ```console ` fallback the bridge derives from `output`), or `{ card: 'diff', title?, diffs }` (a completed file mutation → the change to show, typically the applied hunks with context lines computed from the before/after content, or a whole-file diff when there is no before-image — e.g. a file create. A `tool_call_update`'s content REPLACES the call's content, so a mutation tool returns this even when it duplicates the call-time snippet, to keep the result from clobbering the diff with result text).
- `ToolResultView` (completed): `{ card: 'generic', title?, content? }`, `{ card: 'terminal', title?, output?, exitCode?, signal? }` (the captured run output + exit; a capable UI shows an exit-status pill, while another may derive a fenced ` ```console ` fallback), or `{ card: 'diff', title?, diffs }` (a completed file mutation → the change to show, typically the applied hunks with context lines computed from the before/after content, or a whole-file diff when there is no before-image). Completed views replace pending views, so mutation tools return a diff result even when it duplicates the call-time snippet.
`ToolCallKind` (`'read' | 'edit' | 'delete' | 'move' | 'search' | 'execute' | 'fetch' | 'other'`) picks an icon on a generic card. `FileLocation` (`{ path, line? }`) and `FileDiff` (`{ path, oldText, newText }`) are the shared file-card vocabulary. The design is pinned in [the render-intent-union Agent Note](../../.agents/notes/implemented/architecture/2026-07-02-tool-render-intent-union.md); the ACP bridge maps a `diff` card to a `{ type: 'diff' }` content block, a `terminal` card to the `_meta` terminal convention, and relativizes a file card's title against the session cwd.
`ToolCallKind` (`'read' | 'edit' | 'delete' | 'move' | 'search' | 'execute' | 'fetch' | 'other'`) picks an icon on a generic card. `FileLocation` (`{ path, line? }`) and `FileDiff` (`{ path, oldText, newText }`) are the shared file-card vocabulary. The design is pinned in [the render-intent-union Agent Note](../../.agents/notes/implemented/architecture/2026-07-02-tool-render-intent-union.md); the TUI and host/client runtime project this neutral vocabulary into their own views.
The full presentation field docs live in [`packages/core/tools/src/presentation.ts`](../../packages/core/tools/src/presentation.ts). The `bash` schema and executor are on [bash.md](bash.md); generic background controls are on [tasks.md](tasks.md).

View File

@@ -407,8 +407,8 @@ type ObjectJsonSchema = JsonSchemaNode & { type: 'object' }
工具希望其调用在 UI 中如何呈现编辑器工具调用卡片、CLI命令行界面日志行提供方无关使工具在不依赖任何客户端协议的情况下描述自身。`presentCall`/`presentResult` 返回一个 **`card` 标签的渲染意图**——一个可辨识联合类型UI 桥接层据此分发:
- `ToolCallView`(待执行):`{ card: 'generic', title, kind?, rawInput?, content?, locations? }`(默认卡片;`locations` 是 `{ path, line? }[]`,表示调用读取/修改的文件,供编辑器跟随)、`{ card: 'terminal', title, description?, cwd? }`shell 命令→终端卡片)、或 `{ card: 'diff', title, diffs, locations? }`(文件创建/修改→行内 diff 卡片;`diffs` 是 `{ path, oldText, newText }[]`,新文件时 `oldText: null`)。
- `ToolResultView`(已完成):`{ card: 'generic', title?, content? }`、`{ card: 'terminal', title?, output?, exitCode?, signal? }`(捕获的运行输出 + 退出状态;有能力的 UI 显示退出状态标签,无能力的 UI 获得桥接层从 `output` 派生围栏 ` ```console ` 回退)、或 `{ card: 'diff', title?, diffs }`(已完成的文件变更→要展示的变更,通常是从变更前后内容计算出带上下文行的已应用 hunk或在没有前像时的整文件 diff——例如文件创建。`tool_call_update` 的内容会替换调用的内容,因此变更工具即使与调用时的片段重复也要返回此卡片,以防结果文本覆盖 diff
- `ToolResultView`(已完成):`{ card: 'generic', title?, content? }`、`{ card: 'terminal', title?, output?, exitCode?, signal? }`(捕获的运行输出 + 退出状态;有能力的 UI 显示退出状态标签,其他 UI 可以派生围栏 ` ```console ` 回退)、或 `{ card: 'diff', title?, diffs }`(已完成的文件变更→要展示的变更,通常是从变更前后内容计算出带上下文行的已应用 hunk或在没有前像时的整文件 diff)。已完成视图会替换待执行视图,因此变更工具即使与调用时的片段重复也要返回 diff 结果
`ToolCallKind``'read' | 'edit' | 'delete' | 'move' | 'search' | 'execute' | 'fetch' | 'other'`)用于为通用卡片选择图标。`FileLocation``{ path, line? }`)与 `FileDiff``{ path, oldText, newText }`)是共享的文件卡片词汇。该设计由[渲染意图联合类型 Agent Noteagent 决策记录)](../../.agents/notes/implemented/architecture/2026-07-02-tool-render-intent-union.md)固定;ACPAgent Client Protocol桥接层将 `diff` 卡片映射为 `{ type: 'diff' }` 内容块,将 `terminal` 卡片映射为 `_meta` 终端约定,并根据会话 cwd 将文件卡片标题转换为相对路径
`ToolCallKind``'read' | 'edit' | 'delete' | 'move' | 'search' | 'execute' | 'fetch' | 'other'`)用于为通用卡片选择图标。`FileLocation``{ path, line? }`)与 `FileDiff``{ path, oldText, newText }`)是共享的文件卡片词汇。该设计由[渲染意图联合类型 Agent Noteagent 决策记录)](../../.agents/notes/implemented/architecture/2026-07-02-tool-render-intent-union.md)固定;TUI 和 host/client 运行时将这套中性词汇投影为各自的视图
完整的展示字段文档见 [`packages/core/tools/src/presentation.ts`](../../packages/core/tools/src/presentation.ts)。`bash` schema 与执行器见 [bash.md](bash.md);通用后台控制见 [tasks.md](tasks.md)。

View File

@@ -2,5 +2,5 @@
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write
user-interaction.md: 7ddc2f46bdbbe25f2a8979b029d0c6b958f85b09
user-interaction.zh.md: 75b9b7795a36e0706672f0d17ba0b399c20cc644
user-interaction.md: 798a9790f424683775284a98421be08e6e1399e3
user-interaction.zh.md: 12bfcffe4fe4caaacb54e90126eac55e333d64a5

View File

@@ -2,7 +2,7 @@
English | [中文](user-interaction.zh.md)
The user-interaction seam of [dsh-user-interaction](../../packages/ui/user-interaction). It is the provider-neutral vocabulary a tool or permission plugin uses when it needs the human to answer before the agent can continue. UI surfaces provide the active `UserInteractionProvider`: `dsh-tui` uses keyboard-driven overlays, and `dsh-acp` maps questions to ACP form elicitations.
The user-interaction seam of [dsh-user-interaction](../../packages/ui/user-interaction). It is the provider-neutral vocabulary a tool or permission plugin uses when it needs the human to answer before the agent can continue. UI surfaces provide the active `UserInteractionProvider`; `dsh-tui` uses keyboard-driven overlays and the host runtime relays requests to its connected client.
Source: [`packages/ui/user-interaction/src/index.ts`](../../packages/ui/user-interaction/src/index.ts)
@@ -95,7 +95,7 @@ interface UserInteractionProvider {
## Errors
`UserInteractionError` extends `HarnessError`, so `ctx.tools.execute()` preserves `{ name, code }` for model-facing tool failures such as `EMPTY_QUESTIONS`, `NO_PROVIDER`, `ASK_ABORTED`, or ACP-side cancellation.
`UserInteractionError` extends `HarnessError`, so `ctx.tools.execute()` preserves `{ name, code }` for model-facing tool failures such as `EMPTY_QUESTIONS`, `NO_PROVIDER`, `ASK_ABORTED`, or UI-side cancellation.
```ts type-equiv
/** Stable error taxonomy for user-interaction failures. */

View File

@@ -2,7 +2,7 @@
[English](user-interaction.md) | 中文
[dsh-user-interaction](../../packages/ui/user-interaction) 的用户交互 seam。它是工具或权限插件需要人类回答后 agent智能体才能继续时所使用的、提供方无关的词汇。UI surface 提供活跃的 `UserInteractionProvider``dsh-tui` 使用键盘驱动的 overlay`dsh-acp` 则把问题映射为 ACPAgent Client Protocol表单 elicitation
[dsh-user-interaction](../../packages/ui/user-interaction) 的用户交互 seam。它是工具或权限插件需要人类回答后 agent智能体才能继续时所使用的、提供方无关的词汇。UI surface 提供活跃的 `UserInteractionProvider``dsh-tui` 使用键盘驱动的 overlayhost 运行时把请求转发给它连接的客户端
源码:[`packages/ui/user-interaction/src/index.ts`](../../packages/ui/user-interaction/src/index.ts)
@@ -95,7 +95,7 @@ interface UserInteractionProvider {
## 错误
`UserInteractionError` 继承 `HarnessError`,因此 `ctx.tools.execute()` 会保留 `{ name, code }`,用于面向模型的工具失败,如 `EMPTY_QUESTIONS`、`NO_PROVIDER`、`ASK_ABORTED` 或 ACP 侧取消。
`UserInteractionError` 继承 `HarnessError`,因此 `ctx.tools.execute()` 会保留 `{ name, code }`,用于面向模型的工具失败,如 `EMPTY_QUESTIONS`、`NO_PROVIDER`、`ASK_ABORTED` 或 UI 侧取消。
```ts type-equiv
/** Stable error taxonomy for user-interaction failures. */

View File

@@ -2,5 +2,5 @@
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write
development.md: 4294038e40aa774a006874e6641ca63eea44beeb
development.zh.md: 1f07c95dd60d0554b945c29e6e3ba8bc6ca9841a
development.md: f706d54764bbf79d1f13ccb4c412e7b5717b1edb
development.zh.md: 15f5ae0c514ac412c302a99cb0a662acce510c44

View File

@@ -9,7 +9,7 @@ This onboarding guide helps project contributors get started with the local envi
- Node.js supports 22.19+ and 24+. CI covers 22.19, 24, and 26; see the [Node engine floor Agent Note](../.agents/notes/implemented/process/2026-07-06-node-engine-floor.md).
- Corepack-enabled pnpm. The repo pins `pnpm@11.7.0` in `package.json`; run `corepack enable` if `pnpm --version` does not resolve through Corepack.
- Git.
- Optional: a DeepSeek API key for the TUI/Headless/ACP agent demos and real-API e2e tests.
- Optional: a DeepSeek API key for the TUI, headless, and ACP automation demos and real-API e2e tests.
## First-time setup
@@ -142,7 +142,7 @@ The self-referential cordis-agent demo can inspect and modify its live plugin ru
pnpm run demo:cordis
```
The ACP server agent demo exposes the agent over JSON-RPC stdio and also needs `DEEPSEEK_API_KEY`:
The ACP automation server exposes fresh agent sessions over JSON-RPC stdio and also needs `DEEPSEEK_API_KEY`:
```sh
pnpm run demo:acp

View File

@@ -9,7 +9,7 @@
- Node.js 支持 22.19+ 与 24+。CI 覆盖 22.19、24 和 26见 [Node 引擎下限 Agent Note](../.agents/notes/implemented/process/2026-07-06-node-engine-floor.md)。
- 启用了 Corepack 的 pnpm。仓库在 `package.json` 中固定使用 `pnpm@11.7.0`;如果 `pnpm --version` 无法通过 Corepack 解析,请先运行 `corepack enable`
- Git。
- 可选:一个 DeepSeek API key用于 TUI/Headless/ACPAgent Client Protocol agent智能体演示真实 API 的 e2e 测试。
- 可选:一个 DeepSeek API key用于 TUI、headlessACPAgent Client Protocol自动化 agent智能体演示以及真实 API 的 e2e 测试。
## 首次搭建
@@ -142,7 +142,7 @@ pnpm run demo:tui
pnpm run demo:cordis
```
ACP 服务器 agent 演示通过 JSON-RPC stdio 暴露 agent同样需要 `DEEPSEEK_API_KEY`
ACP 自动化服务器通过 JSON-RPC stdio 提供全新 agent 会话,同样需要 `DEEPSEEK_API_KEY`
```sh
pnpm run demo:acp

View File

@@ -11,31 +11,32 @@ This matrix shows which packages dispatch each harness-owned event and which pac
| `agent/cancel-requested` | `emit` | [`packages/core/agent/src/types.ts:350`](../packages/core/agent/src/types.ts) | [`agent-loop`](../packages/core/agent-loop) (`emit`) | [`goal-session`](../packages/goal/goal-session) |
| `agent/created` | `emit` | [`packages/core/agent/src/types.ts:285`](../packages/core/agent/src/types.ts) | [`agent`](../packages/core/agent) (`events.dispatch`) | [`goal-session`](../packages/goal/goal-session), [`tui`](../packages/ui/tui) |
| `agent/disposed` | `emit` | [`packages/core/agent/src/types.ts:294`](../packages/core/agent/src/types.ts) | [`agent`](../packages/core/agent) (`events.dispatch`) | [`agent-loop`](../packages/core/agent-loop), [`goal-session`](../packages/goal/goal-session), [`tui`](../packages/ui/tui) |
| `agent/error` | `emit` | [`packages/core/agent/src/types.ts:498`](../packages/core/agent/src/types.ts) | [`agent-loop`](../packages/core/agent-loop) (`emit`) | [`goal-session`](../packages/goal/goal-session), `runtime`, [`tui`](../packages/ui/tui) |
| `agent/error` | `emit` | [`packages/core/agent/src/types.ts:498`](../packages/core/agent/src/types.ts) | [`agent-loop`](../packages/core/agent-loop) (`emit`) | `apiproxy`, [`goal-session`](../packages/goal/goal-session), [`tui`](../packages/ui/tui) |
| `agent/inbox/dequeue` | `emit` | [`packages/core/agent/src/types.ts:326`](../packages/core/agent/src/types.ts) | [`agent-loop`](../packages/core/agent-loop) (`emit`) | [`agent`](../packages/core/agent) |
| `agent/inbox/discard` | `emit` | [`packages/core/agent/src/types.ts:340`](../packages/core/agent/src/types.ts) | [`agent-loop`](../packages/core/agent-loop) (`emit`) | [`agent`](../packages/core/agent) |
| `agent/inbox/enqueue` | `emit` | [`packages/core/agent/src/types.ts:316`](../packages/core/agent/src/types.ts) | [`agent-loop`](../packages/core/agent-loop) (`emit`) | [`agent`](../packages/core/agent), [`goal-session`](../packages/goal/goal-session), [`tui`](../packages/ui/tui) |
| `agent/post-step` | `serial` | [`packages/core/agent/src/types.ts:448`](../packages/core/agent/src/types.ts) | [`agent-loop`](../packages/core/agent-loop) (`serial`) | [`compact-basic`](../packages/compact/compact-basic), [`session-checkpoint-policy`](../packages/session-persistence/session-checkpoint-policy) |
| `agent/pre-step` | `serial` | [`packages/core/agent/src/types.ts:379`](../packages/core/agent/src/types.ts) | [`agent-loop`](../packages/core/agent-loop) (`serial`) | [`time-context`](../packages/context/time-context), [`user-approval`](../packages/ui/user-approval) |
| `agent/prompt-submit` | `waterfall` | [`packages/core/agent/src/types.ts:395`](../packages/core/agent/src/types.ts) | [`agent-loop`](../packages/core/agent-loop) (`waterfall`) | [`acp`](../packages/ui/acp), [`goal-session`](../packages/goal/goal-session), [`hooks-claude`](../packages/hooks/hooks-claude), [`hooks-codex`](../packages/hooks/hooks-codex), [`plan-mode`](../packages/plan/plan-mode), [`repeat-tool-guard`](../packages/guard/repeat-tool-guard) |
| `agent/prompt-submit` | `waterfall` | [`packages/core/agent/src/types.ts:395`](../packages/core/agent/src/types.ts) | [`agent-loop`](../packages/core/agent-loop) (`waterfall`) | [`goal-session`](../packages/goal/goal-session), [`hooks-claude`](../packages/hooks/hooks-claude), [`hooks-codex`](../packages/hooks/hooks-codex), [`plan-mode`](../packages/plan/plan-mode), [`repeat-tool-guard`](../packages/guard/repeat-tool-guard) |
| `agent/request` | `waterfall` | [`packages/core/agent/src/types.ts:409`](../packages/core/agent/src/types.ts) | [`agent-loop`](../packages/core/agent-loop) (`waterfall`) | [`agent`](../packages/core/agent) |
| `agent/request-error` | `waterfall` | [`packages/core/agent/src/types.ts:463`](../packages/core/agent/src/types.ts) | [`agent-loop`](../packages/core/agent-loop) (`waterfall`) | [`compact-basic`](../packages/compact/compact-basic), [`llm-retry`](../packages/llm/llm-retry), [`plan-mode`](../packages/plan/plan-mode) |
| `agent/session-prefix` | `waterfall` | [`packages/core/agent/src/types.ts:424`](../packages/core/agent/src/types.ts) | [`agent-loop`](../packages/core/agent-loop) (`waterfall`) | [`tool-skill`](../packages/skill/tool-skill), [`workspace-context`](../packages/context/workspace-context) |
| `agent/session-start` | `emit` | [`packages/core/agent/src/types.ts:363`](../packages/core/agent/src/types.ts) | [`agent-loop`](../packages/core/agent-loop) (`emit`) | [`goal`](../packages/goal/goal), [`goal-session`](../packages/goal/goal-session), [`hooks-claude`](../packages/hooks/hooks-claude), [`hooks-codex`](../packages/hooks/hooks-codex) |
| `agent/status` | `emit` | [`packages/core/agent/src/types.ts:303`](../packages/core/agent/src/types.ts) | [`agent-loop`](../packages/core/agent-loop) (`emit`) | [`agent`](../packages/core/agent), [`goal-session`](../packages/goal/goal-session), `runtime`, [`tui`](../packages/ui/tui) |
| `agent/status` | `emit` | [`packages/core/agent/src/types.ts:303`](../packages/core/agent/src/types.ts) | [`agent-loop`](../packages/core/agent-loop) (`emit`) | [`agent`](../packages/core/agent), `apiproxy`, [`goal-session`](../packages/goal/goal-session), [`tui`](../packages/ui/tui) |
| `agent/step-result` | `waterfall` | [`packages/core/agent/src/types.ts:436`](../packages/core/agent/src/types.ts) | [`agent-loop`](../packages/core/agent-loop) (`waterfall`) | - |
| `agent/turn-continuation` | `waterfall` | [`packages/core/agent/src/types.ts:474`](../packages/core/agent/src/types.ts) | [`agent-loop`](../packages/core/agent-loop) (`waterfall`) | [`hooks-claude`](../packages/hooks/hooks-claude), [`hooks-codex`](../packages/hooks/hooks-codex), [`plan-mode`](../packages/plan/plan-mode) |
| `agent/turn-stop` | `serial` | [`packages/core/agent/src/types.ts:485`](../packages/core/agent/src/types.ts) | [`agent-loop`](../packages/core/agent-loop) (`serial`) | [`subagent-inprocess`](../packages/subagent/subagent-inprocess), [`tool-goal`](../packages/goal/tool-goal) |
| `approval/request` | `waterfall` | [`packages/ui/user-approval/src/index.ts:30`](../packages/ui/user-approval/src/index.ts) | [`user-approval`](../packages/ui/user-approval) (`waterfall`) | [`acp`](../packages/ui/acp) |
| `commands/change` | `emit` | [`packages/ui/commands/src/index.ts:103`](../packages/ui/commands/src/index.ts) | [`commands`](../packages/ui/commands) (`events.dispatch`) | [`acp`](../packages/ui/acp), [`tui`](../packages/ui/tui) |
| `approval/request` | `waterfall` | [`packages/ui/user-approval/src/index.ts:30`](../packages/ui/user-approval/src/index.ts) | [`user-approval`](../packages/ui/user-approval) (`waterfall`) | [`acp`](../packages/acp/acp) |
| `commands/change` | `emit` | [`packages/ui/commands/src/index.ts:103`](../packages/ui/commands/src/index.ts) | [`commands`](../packages/ui/commands) (`events.dispatch`) | [`tui`](../packages/ui/tui) |
| `domain/changed` | `emit` | [`packages/storage/storage-domain/src/events.ts:46`](../packages/storage/storage-domain/src/events.ts) | [`storage-domain`](../packages/storage/storage-domain) (`emit`) | `apiproxy`, [`storage-domain`](../packages/storage/storage-domain), [`workspace`](../packages/workspace/workspace) |
| `fs/edit-intent` | `waterfall` | [`packages/fs/fs/src/index.ts:62`](../packages/fs/fs/src/index.ts) | [`tool-fs`](../packages/fs/tool-fs) (`waterfall`) | [`fs-policy`](../packages/fs/fs-policy) |
| `fs/observed` | `emit` | [`packages/fs/fs/src/index.ts:71`](../packages/fs/fs/src/index.ts) | [`tool-fs`](../packages/fs/tool-fs) (`emit`) | [`fs-policy`](../packages/fs/fs-policy) |
| `fs/write-intent` | `waterfall` | [`packages/fs/fs/src/index.ts:54`](../packages/fs/fs/src/index.ts) | [`tool-fs`](../packages/fs/tool-fs) (`waterfall`) | [`fs-policy`](../packages/fs/fs-policy) |
| `goal/changed` | `emit` | [`packages/goal/goal/src/types.ts:167`](../packages/goal/goal/src/types.ts) | [`goal`](../packages/goal/goal) (`emit`) | [`goal-session`](../packages/goal/goal-session) |
| `llm/stream` | `waterfall` | [`packages/llm/llm/src/index.ts:52`](../packages/llm/llm/src/index.ts) | [`llm`](../packages/llm/llm) (`waterfall`) | [`agent-loop`](../packages/core/agent-loop), [`llm`](../packages/llm/llm), [`llm-replay`](../packages/support/llm-replay), [`session-checkpoint-policy`](../packages/session-persistence/session-checkpoint-policy), [`session-title`](../packages/session-title/session-title) |
| `session/created` | `emit` | [`packages/core/session/src/index.ts:79`](../packages/core/session/src/index.ts) | [`session`](../packages/core/session) (`events.dispatch`) | [`compact`](../packages/compact/compact), [`goal`](../packages/goal/goal), [`hook-protocol`](../packages/hooks/hook-protocol), [`jsonrpc`](../packages/ui/jsonrpc), [`llm-retry`](../packages/llm/llm-retry), `runtime`, [`session`](../packages/core/session), [`session-persistence`](../packages/session-persistence/session-persistence), [`user-approval`](../packages/ui/user-approval) |
| `session/disposed` | `emit` | [`packages/core/session/src/index.ts:89`](../packages/core/session/src/index.ts) | [`session`](../packages/core/session) (`events.dispatch`) | [`agent-loop`](../packages/core/agent-loop), `runtime`, [`session-persistence`](../packages/session-persistence/session-persistence), [`session-title`](../packages/session-title/session-title) |
| `session/event` | `emit` | [`packages/core/session/src/index.ts:101`](../packages/core/session/src/index.ts) | [`session`](../packages/core/session) (`events.dispatch`) | [`acp`](../packages/ui/acp), [`cli-demo`](../packages/examples/cli-demo), [`compact`](../packages/compact/compact), [`goal`](../packages/goal/goal), [`goal-session`](../packages/goal/goal-session), [`hook-protocol`](../packages/hooks/hook-protocol), [`jsonrpc`](../packages/ui/jsonrpc), `runtime`, [`session`](../packages/core/session), [`session-persistence`](../packages/session-persistence/session-persistence), [`session-title`](../packages/session-title/session-title), [`token-meter`](../packages/llm/token-meter), [`tui`](../packages/ui/tui), [`user-approval`](../packages/ui/user-approval), [`workspace-context`](../packages/context/workspace-context) |
| `session/created` | `emit` | [`packages/core/session/src/index.ts:79`](../packages/core/session/src/index.ts) | [`session`](../packages/core/session) (`events.dispatch`) | `apiproxy`, [`compact`](../packages/compact/compact), [`goal`](../packages/goal/goal), [`hook-protocol`](../packages/hooks/hook-protocol), [`jsonrpc`](../packages/ui/jsonrpc), [`llm-retry`](../packages/llm/llm-retry), [`session`](../packages/core/session), [`session-persistence`](../packages/session-persistence/session-persistence), [`user-approval`](../packages/ui/user-approval) |
| `session/disposed` | `emit` | [`packages/core/session/src/index.ts:89`](../packages/core/session/src/index.ts) | [`session`](../packages/core/session) (`events.dispatch`) | [`agent-loop`](../packages/core/agent-loop), `apiproxy`, [`session-persistence`](../packages/session-persistence/session-persistence), [`session-title`](../packages/session-title/session-title) |
| `session/event` | `emit` | [`packages/core/session/src/index.ts:101`](../packages/core/session/src/index.ts) | [`session`](../packages/core/session) (`events.dispatch`) | [`acp`](../packages/acp/acp), `apiproxy`, [`cli-demo`](../packages/examples/cli-demo), [`compact`](../packages/compact/compact), [`goal`](../packages/goal/goal), [`goal-session`](../packages/goal/goal-session), [`hook-protocol`](../packages/hooks/hook-protocol), [`jsonrpc`](../packages/ui/jsonrpc), [`session`](../packages/core/session), [`session-persistence`](../packages/session-persistence/session-persistence), [`session-title`](../packages/session-title/session-title), [`token-meter`](../packages/llm/token-meter), [`tui`](../packages/ui/tui), [`user-approval`](../packages/ui/user-approval), [`workspace`](../packages/workspace/workspace), [`workspace-context`](../packages/context/workspace-context) |
| `session/flush` | `parallel` | [`packages/core/session/src/index.ts:111`](../packages/core/session/src/index.ts) | [`session`](../packages/core/session) (`events.dispatch`) | [`session-persistence`](../packages/session-persistence/session-persistence) |
| `subagent/end` | `emit` | [`packages/subagent/subagent/src/index.ts:139`](../packages/subagent/subagent/src/index.ts) | [`subagent`](../packages/subagent/subagent) (`events.dispatch`) | [`hooks-claude`](../packages/hooks/hooks-claude), [`jsonrpc`](../packages/ui/jsonrpc), [`subagent`](../packages/subagent/subagent) |
| `subagent/provider-added` | `emit` | [`packages/subagent/subagent/src/index.ts:113`](../packages/subagent/subagent/src/index.ts) | [`subagent`](../packages/subagent/subagent) (`emit`) | [`subagent`](../packages/subagent/subagent), [`tool-subagent`](../packages/subagent/tool-subagent) |
@@ -60,7 +61,7 @@ This matrix shows which packages dispatch each harness-owned event and which pac
| Event string | Dispatchers | Listeners |
| --- | --- | --- |
| `internal/dispatch` | - | [`compact`](../packages/compact/compact), [`fs`](../packages/fs/fs), [`goal`](../packages/goal/goal), [`goal-session`](../packages/goal/goal-session), [`hook-protocol`](../packages/hooks/hook-protocol), [`llm-retry`](../packages/llm/llm-retry), [`permission`](../packages/ui/permission), [`plan-mode`](../packages/plan/plan-mode), [`pty-local`](../packages/pty/pty-local), `runtime`, [`sandbox-policy`](../packages/sandbox/sandbox-policy), [`scope`](../packages/core/scope), [`session`](../packages/core/session), [`subagent`](../packages/subagent/subagent), [`time-context`](../packages/context/time-context), [`tool-todo`](../packages/todo/tool-todo), [`tools`](../packages/core/tools), [`user-approval`](../packages/ui/user-approval), [`workflow`](../packages/workflow/workflow) |
| `internal/plugin` | - | `webserver` |
| `internal/plugin` | - | `hmr`, `modules`, `webserver` |
| `internal/status` | - | [`agent`](../packages/core/agent) |
| `slots/changed` | `runtime` (`emit`) | - |

View File

@@ -19,7 +19,6 @@ The process decision behind this index is recorded in [the documentation graph A
| [event producer/consumer matrix](event-producer-consumer.md) | `hybrid generated` |
| [agent turn and step lifecycle](agent-lifecycle.md) | `curated` |
| [tool execution pipeline](tool-execution-pipeline.md) | `curated` |
| [ACP snapshot replay](../packages/ui/acp/snapshot-replay.md) | `curated` |
Regenerate with `pnpm run gen-doc-graphs`; verify freshness with `pnpm run verify-doc-graphs`.

View File

@@ -120,8 +120,10 @@ flowchart TD
pkg_llm_replay["llm-replay"]
pkg_loader_smoke["loader-smoke"]
end
subgraph group_ui["packages/ui"]
subgraph group_acp["packages/acp"]
pkg_acp["acp"]
end
subgraph group_ui["packages/ui"]
pkg_app_boot["app-boot"]
pkg_commands["commands"]
pkg_jsonrpc["jsonrpc"]
@@ -145,6 +147,7 @@ flowchart TD
pkg_client_ui_slots["client-ui-slots"]
pkg_client_ui_theme["client-ui-theme"]
pkg_client_ui_trajectory["client-ui-trajectory"]
pkg_client_ui_workspace["client-ui-workspace"]
pkg_client_web["client-web"]
pkg_client_web_react["client-web-react"]
end
@@ -169,7 +172,6 @@ flowchart TD
end
subgraph group_host["packages/host"]
pkg_host_apiproxy["host-apiproxy"]
pkg_host_runtime["host-runtime"]
pkg_host_webserver["host-webserver"]
end
subgraph group_lsp["packages/lsp"]
@@ -195,6 +197,12 @@ flowchart TD
pkg_scripts["scripts"]
pkg_telemetry["telemetry"]
end
subgraph group_storage["packages/storage"]
pkg_storage["storage"]
pkg_storage_domain["storage-domain"]
pkg_storage_json["storage-json"]
pkg_storage_sqlite["storage-sqlite"]
end
subgraph group_tasks["packages/tasks"]
pkg_tasks["tasks"]
pkg_tool_tasks["tool-tasks"]
@@ -205,6 +213,9 @@ flowchart TD
pkg_workflow["workflow"]
pkg_workflow_workerthread["workflow-workerthread"]
end
subgraph group_workspace["packages/workspace"]
pkg_workspace["workspace"]
end
pkg_brand --> pkg_invariants
pkg_paths --> pkg_invariants
pkg_retention --> pkg_invariants
@@ -214,7 +225,6 @@ flowchart TD
pkg_subagent_subprocess --> pkg_invariants
pkg_acp_snapshot --> pkg_invariants
pkg_loader_smoke --> pkg_invariants
pkg_client_connection --> pkg_invariants
pkg_client_i18n --> pkg_invariants
pkg_client_modules --> pkg_invariants
pkg_client_runtime --> pkg_invariants
@@ -228,11 +238,14 @@ flowchart TD
pkg_code_runtime --> pkg_invariants
pkg_jsonrpc_demo --> pkg_invariants
pkg_host_apiproxy --> pkg_invariants
pkg_host_runtime --> pkg_invariants
pkg_host_webserver --> pkg_invariants
pkg_storage --> pkg_invariants
pkg_llm --> pkg_brand
pkg_llm --> pkg_invariants
pkg_client_connection --> pkg_host_webserver
pkg_client_connection --> pkg_invariants
pkg_client_hmr --> pkg_client_modules
pkg_client_hmr --> pkg_host_webserver
pkg_client_hmr --> pkg_invariants
pkg_client_ui_conversation --> pkg_client_runtime
pkg_client_ui_conversation --> pkg_client_ui_primitives
@@ -245,11 +258,21 @@ flowchart TD
pkg_client_ui_sidebar --> pkg_client_ui_primitives
pkg_client_ui_sidebar --> pkg_client_ui_slots
pkg_client_ui_sidebar --> pkg_invariants
pkg_client_ui_workspace --> pkg_client_runtime
pkg_client_ui_workspace --> pkg_client_ui_primitives
pkg_client_ui_workspace --> pkg_client_ui_slots
pkg_client_ui_workspace --> pkg_invariants
pkg_helper --> pkg_brand
pkg_helper --> pkg_invariants
pkg_telemetry --> pkg_brand
pkg_telemetry --> pkg_invariants
pkg_telemetry --> pkg_paths
pkg_storage_domain --> pkg_invariants
pkg_storage_domain --> pkg_storage
pkg_storage_json --> pkg_invariants
pkg_storage_json --> pkg_storage
pkg_storage_sqlite --> pkg_invariants
pkg_storage_sqlite --> pkg_storage
pkg_llm_deepseek --> pkg_invariants
pkg_llm_deepseek --> pkg_llm
pkg_llm_deepseek --> pkg_timeout
@@ -413,6 +436,12 @@ flowchart TD
pkg_workflow --> pkg_invariants
pkg_workflow --> pkg_llm
pkg_workflow --> pkg_session
pkg_workspace --> pkg_brand
pkg_workspace --> pkg_invariants
pkg_workspace --> pkg_session
pkg_workspace --> pkg_session_persistence
pkg_workspace --> pkg_storage
pkg_workspace --> pkg_storage_domain
pkg_tools --> pkg_agent
pkg_tools --> pkg_code_runtime
pkg_tools --> pkg_invariants
@@ -453,6 +482,10 @@ flowchart TD
pkg_session_title_first_message_llm --> pkg_session
pkg_session_title_first_message_llm --> pkg_session_title
pkg_session_title_first_message_llm --> pkg_session_title_llm
pkg_acp --> pkg_agent
pkg_acp --> pkg_invariants
pkg_acp --> pkg_session
pkg_acp --> pkg_user_approval
pkg_permission --> pkg_bash
pkg_permission --> pkg_invariants
pkg_permission --> pkg_sandbox
@@ -646,24 +679,6 @@ flowchart TD
pkg_hooks_claude --> pkg_session_persistence
pkg_hooks_claude --> pkg_subagent
pkg_hooks_claude --> pkg_tools
pkg_acp --> pkg_agent
pkg_acp --> pkg_bash
pkg_acp --> pkg_commands
pkg_acp --> pkg_invariants
pkg_acp --> pkg_llm
pkg_acp --> pkg_llm_retry
pkg_acp --> pkg_permission
pkg_acp --> pkg_plan_mode
pkg_acp --> pkg_sandbox
pkg_acp --> pkg_session
pkg_acp --> pkg_session_persistence
pkg_acp --> pkg_session_query
pkg_acp --> pkg_session_reference
pkg_acp --> pkg_session_title
pkg_acp --> pkg_system_prompt
pkg_acp --> pkg_tools
pkg_acp --> pkg_user_approval
pkg_acp --> pkg_user_interaction
pkg_jsonrpc --> pkg_agent
pkg_jsonrpc --> pkg_invariants
pkg_jsonrpc --> pkg_llm
@@ -735,16 +750,10 @@ flowchart TD
pkg_acp_demo --> pkg_acp
pkg_acp_demo --> pkg_agent_spine_demo
pkg_acp_demo --> pkg_app_boot
pkg_acp_demo --> pkg_command_goal
pkg_acp_demo --> pkg_commands
pkg_acp_demo --> pkg_invariants
pkg_acp_demo --> pkg_session_checkpoint_policy
pkg_acp_demo --> pkg_session_persistence_jsonl
pkg_acp_demo --> pkg_session_query
pkg_acp_demo --> pkg_session_query_sqlite
pkg_acp_demo --> pkg_session_reference
pkg_acp_demo --> pkg_tools
pkg_acp_demo --> pkg_user_interaction
pkg_acp_demo --> pkg_workspace_context
pkg_cli_demo --> pkg_agent
pkg_cli_demo --> pkg_agent_spine_demo
@@ -789,7 +798,6 @@ flowchart TD
| [`subagent-subprocess`](../packages/subagent/subagent-subprocess) | `subagent` | [`invariants`](../packages/support/invariants) |
| [`acp-snapshot`](../packages/support/acp-snapshot) | `support` | [`invariants`](../packages/support/invariants) |
| [`loader-smoke`](../packages/support/loader-smoke) | `support` | [`invariants`](../packages/support/invariants) |
| [`client-connection`](../packages/client/connection) | `client` | [`invariants`](../packages/support/invariants) |
| [`client-i18n`](../packages/client/i18n) | `client` | [`invariants`](../packages/support/invariants) |
| [`client-modules`](../packages/client/modules) | `client` | [`invariants`](../packages/support/invariants) |
| [`client-runtime`](../packages/client/runtime) | `client` | [`invariants`](../packages/support/invariants) |
@@ -803,15 +811,20 @@ flowchart TD
| [`code-runtime`](../packages/code-runtime/code-runtime) | `code-runtime` | [`invariants`](../packages/support/invariants) |
| [`jsonrpc-demo`](../packages/examples/jsonrpc-demo) | `examples` | [`invariants`](../packages/support/invariants) |
| [`host-apiproxy`](../packages/host/apiproxy) | `host` | [`invariants`](../packages/support/invariants) |
| [`host-runtime`](../packages/host/runtime) | `host` | [`invariants`](../packages/support/invariants) |
| [`host-webserver`](../packages/host/webserver) | `host` | [`invariants`](../packages/support/invariants) |
| [`storage`](../packages/storage/storage) | `storage` | [`invariants`](../packages/support/invariants) |
| [`llm`](../packages/llm/llm) | `llm` | [`brand`](../packages/util/brand), [`invariants`](../packages/support/invariants) |
| [`client-hmr`](../packages/client/hmr) | `client` | [`client-modules`](../packages/client/modules), [`invariants`](../packages/support/invariants) |
| [`client-connection`](../packages/client/connection) | `client` | [`host-webserver`](../packages/host/webserver), [`invariants`](../packages/support/invariants) |
| [`client-hmr`](../packages/client/hmr) | `client` | [`client-modules`](../packages/client/modules), [`host-webserver`](../packages/host/webserver), [`invariants`](../packages/support/invariants) |
| [`client-ui-conversation`](../packages/client/ui-conversation) | `client` | [`client-runtime`](../packages/client/runtime), [`client-ui-primitives`](../packages/client/ui-primitives), [`client-ui-slots`](../packages/client/ui-slots), [`invariants`](../packages/support/invariants) |
| [`client-ui-layout`](../packages/client/ui-layout) | `client` | [`client-runtime`](../packages/client/runtime), [`client-ui-slots`](../packages/client/ui-slots), [`invariants`](../packages/support/invariants) |
| [`client-ui-sidebar`](../packages/client/ui-sidebar) | `client` | [`client-runtime`](../packages/client/runtime), [`client-ui-primitives`](../packages/client/ui-primitives), [`client-ui-slots`](../packages/client/ui-slots), [`invariants`](../packages/support/invariants) |
| [`client-ui-workspace`](../packages/client/ui-workspace) | `client` | [`client-runtime`](../packages/client/runtime), [`client-ui-primitives`](../packages/client/ui-primitives), [`client-ui-slots`](../packages/client/ui-slots), [`invariants`](../packages/support/invariants) |
| [`helper`](../packages/sdk/helper) | `sdk` | [`brand`](../packages/util/brand), [`invariants`](../packages/support/invariants) |
| [`telemetry`](../packages/sdk/telemetry) | `sdk` | [`brand`](../packages/util/brand), [`invariants`](../packages/support/invariants), [`paths`](../packages/util/paths) |
| [`storage-domain`](../packages/storage/storage-domain) | `storage` | [`invariants`](../packages/support/invariants), [`storage`](../packages/storage/storage) |
| [`storage-json`](../packages/storage/storage-json) | `storage` | [`invariants`](../packages/support/invariants), [`storage`](../packages/storage/storage) |
| [`storage-sqlite`](../packages/storage/storage-sqlite) | `storage` | [`invariants`](../packages/support/invariants), [`storage`](../packages/storage/storage) |
| [`llm-deepseek`](../packages/llm/llm-deepseek) | `llm` | [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`timeout`](../packages/util/timeout) |
| [`llm-pi-ai`](../packages/llm/llm-pi-ai) | `llm` | [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`timeout`](../packages/util/timeout) |
| [`session`](../packages/core/session) | `core` | [`brand`](../packages/util/brand), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`scope`](../packages/core/scope) |
@@ -859,6 +872,7 @@ flowchart TD
| [`scripts`](../packages/sdk/scripts) | `sdk` | [`app-boot`](../packages/ui/app-boot), [`invariants`](../packages/support/invariants) |
| [`tasks`](../packages/tasks/tasks) | `tasks` | [`agent`](../packages/core/agent), [`brand`](../packages/util/brand), [`invariants`](../packages/support/invariants), [`session`](../packages/core/session), [`timeout`](../packages/util/timeout) |
| [`workflow`](../packages/workflow/workflow) | `workflow` | [`agent`](../packages/core/agent), [`brand`](../packages/util/brand), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session) |
| [`workspace`](../packages/workspace/workspace) | `workspace` | [`brand`](../packages/util/brand), [`invariants`](../packages/support/invariants), [`session`](../packages/core/session), [`session-persistence`](../packages/session-persistence/session-persistence), [`storage`](../packages/storage/storage), [`storage-domain`](../packages/storage/storage-domain) |
| [`tools`](../packages/core/tools) | `core` | [`agent`](../packages/core/agent), [`code-runtime`](../packages/code-runtime/code-runtime), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`scope`](../packages/core/scope), [`session`](../packages/core/session), [`system-prompt`](../packages/core/system-prompt), [`user-approval`](../packages/ui/user-approval) |
| [`command-goal`](../packages/goal/command-goal) | `goal` | [`commands`](../packages/ui/commands), [`goal`](../packages/goal/goal), [`invariants`](../packages/support/invariants) |
| [`goal-session`](../packages/goal/goal-session) | `goal` | [`agent`](../packages/core/agent), [`goal`](../packages/goal/goal), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session) |
@@ -867,6 +881,7 @@ flowchart TD
| [`session-query-sqlite`](../packages/session-query/session-query-sqlite) | `session-query` | [`invariants`](../packages/support/invariants), [`session`](../packages/core/session), [`session-persistence`](../packages/session-persistence/session-persistence), [`session-query`](../packages/session-query/session-query) |
| [`session-title-all-messages-llm`](../packages/session-title/session-title-all-messages-llm) | `session-title` | [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`session-title`](../packages/session-title/session-title), [`session-title-llm`](../packages/session-title/session-title-llm) |
| [`session-title-first-message-llm`](../packages/session-title/session-title-first-message-llm) | `session-title` | [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`session-title`](../packages/session-title/session-title), [`session-title-llm`](../packages/session-title/session-title-llm) |
| [`acp`](../packages/acp/acp) | `acp` | [`agent`](../packages/core/agent), [`invariants`](../packages/support/invariants), [`session`](../packages/core/session), [`user-approval`](../packages/ui/user-approval) |
| [`permission`](../packages/ui/permission) | `ui` | [`bash`](../packages/bash/bash), [`invariants`](../packages/support/invariants), [`sandbox`](../packages/sandbox/sandbox), [`sandbox-policy`](../packages/sandbox/sandbox-policy), [`session`](../packages/core/session), [`user-approval`](../packages/ui/user-approval) |
| [`session-reference`](../packages/context/session-reference) | `context` | [`agent`](../packages/core/agent), [`compact`](../packages/compact/compact), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`retention`](../packages/util/retention), [`session`](../packages/core/session), [`session-query`](../packages/session-query/session-query) |
| [`pty-local`](../packages/pty/pty-local) | `pty` | [`agent`](../packages/core/agent), [`invariants`](../packages/support/invariants), [`pty`](../packages/pty/pty), [`sandbox`](../packages/sandbox/sandbox), [`sandbox-policy`](../packages/sandbox/sandbox-policy), [`session`](../packages/core/session) |
@@ -898,7 +913,6 @@ flowchart TD
| [`subagent-inprocess`](../packages/subagent/subagent-inprocess) | `subagent` | [`agent`](../packages/core/agent), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`subagent`](../packages/subagent/subagent), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools) |
| [`tool-subagent`](../packages/subagent/tool-subagent) | `subagent` | [`agent`](../packages/core/agent), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`subagent`](../packages/subagent/subagent), [`tasks`](../packages/tasks/tasks), [`tools`](../packages/core/tools) |
| [`hooks-claude`](../packages/hooks/hooks-claude) | `hooks` | [`agent`](../packages/core/agent), [`hook-protocol`](../packages/hooks/hook-protocol), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`session-persistence`](../packages/session-persistence/session-persistence), [`subagent`](../packages/subagent/subagent), [`tools`](../packages/core/tools) |
| [`acp`](../packages/ui/acp) | `ui` | [`agent`](../packages/core/agent), [`bash`](../packages/bash/bash), [`commands`](../packages/ui/commands), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`llm-retry`](../packages/llm/llm-retry), [`permission`](../packages/ui/permission), [`plan-mode`](../packages/plan/plan-mode), [`sandbox`](../packages/sandbox/sandbox), [`session`](../packages/core/session), [`session-persistence`](../packages/session-persistence/session-persistence), [`session-query`](../packages/session-query/session-query), [`session-reference`](../packages/context/session-reference), [`session-title`](../packages/session-title/session-title), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools), [`user-approval`](../packages/ui/user-approval), [`user-interaction`](../packages/ui/user-interaction) |
| [`jsonrpc`](../packages/ui/jsonrpc) | `ui` | [`agent`](../packages/core/agent), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`llm-deepseek`](../packages/llm/llm-deepseek), [`scope`](../packages/core/scope), [`session`](../packages/core/session), [`subagent`](../packages/subagent/subagent) |
| [`tui`](../packages/ui/tui) | `ui` | [`agent`](../packages/core/agent), [`agent-loop`](../packages/core/agent-loop), [`commands`](../packages/ui/commands), [`goal`](../packages/goal/goal), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`llm-retry`](../packages/llm/llm-retry), [`session`](../packages/core/session), [`session-persistence`](../packages/session-persistence/session-persistence), [`session-query`](../packages/session-query/session-query), [`session-reference`](../packages/context/session-reference), [`session-title`](../packages/session-title/session-title), [`skill`](../packages/skill/skill), [`system-prompt`](../packages/core/system-prompt), [`token-meter`](../packages/llm/token-meter), [`tools`](../packages/core/tools), [`user-interaction`](../packages/ui/user-interaction) |
| [`agent-spine-demo`](../packages/examples/agent-spine-demo) | `examples` | [`agent`](../packages/core/agent), [`agent-loop`](../packages/core/agent-loop), [`goal`](../packages/goal/goal), [`goal-session`](../packages/goal/goal-session), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`llm-retry`](../packages/llm/llm-retry), [`paths`](../packages/util/paths), [`scope`](../packages/core/scope), [`session`](../packages/core/session), [`session-title`](../packages/session-title/session-title), [`skill`](../packages/skill/skill), [`skill-local`](../packages/skill/skill-local), [`system-prompt`](../packages/core/system-prompt), [`tasks`](../packages/tasks/tasks), [`tool-bash`](../packages/bash/tool-bash), [`tool-goal`](../packages/goal/tool-goal), [`tool-skill`](../packages/skill/tool-skill), [`tool-tasks`](../packages/tasks/tool-tasks), [`tools`](../packages/core/tools), [`workspace-context`](../packages/context/workspace-context) |
@@ -906,6 +920,6 @@ flowchart TD
| [`workflow-workerthread`](../packages/workflow/workflow-workerthread) | `workflow` | [`agent`](../packages/core/agent), [`brand`](../packages/util/brand), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`subagent`](../packages/subagent/subagent), [`tools`](../packages/core/tools), [`workflow`](../packages/workflow/workflow) |
| [`subagent-fork`](../packages/subagent/subagent-fork) | `subagent` | [`agent`](../packages/core/agent), [`invariants`](../packages/support/invariants), [`session`](../packages/core/session), [`subagent`](../packages/subagent/subagent), [`subagent-inprocess`](../packages/subagent/subagent-inprocess) |
| [`subagent-spawn`](../packages/subagent/subagent-spawn) | `subagent` | [`invariants`](../packages/support/invariants), [`subagent`](../packages/subagent/subagent), [`subagent-inprocess`](../packages/subagent/subagent-inprocess) |
| [`acp-demo`](../packages/examples/acp-demo) | `examples` | [`acp`](../packages/ui/acp), [`agent-spine-demo`](../packages/examples/agent-spine-demo), [`app-boot`](../packages/ui/app-boot), [`command-goal`](../packages/goal/command-goal), [`commands`](../packages/ui/commands), [`invariants`](../packages/support/invariants), [`session-checkpoint-policy`](../packages/session-persistence/session-checkpoint-policy), [`session-persistence-jsonl`](../packages/session-persistence/session-persistence-jsonl), [`session-query`](../packages/session-query/session-query), [`session-query-sqlite`](../packages/session-query/session-query-sqlite), [`session-reference`](../packages/context/session-reference), [`tools`](../packages/core/tools), [`user-interaction`](../packages/ui/user-interaction), [`workspace-context`](../packages/context/workspace-context) |
| [`acp-demo`](../packages/examples/acp-demo) | `examples` | [`acp`](../packages/acp/acp), [`agent-spine-demo`](../packages/examples/agent-spine-demo), [`app-boot`](../packages/ui/app-boot), [`invariants`](../packages/support/invariants), [`session-checkpoint-policy`](../packages/session-persistence/session-checkpoint-policy), [`session-persistence-jsonl`](../packages/session-persistence/session-persistence-jsonl), [`tools`](../packages/core/tools), [`workspace-context`](../packages/context/workspace-context) |
| [`cli-demo`](../packages/examples/cli-demo) | `examples` | [`agent`](../packages/core/agent), [`agent-spine-demo`](../packages/examples/agent-spine-demo), [`app-boot`](../packages/ui/app-boot), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`session-checkpoint-policy`](../packages/session-persistence/session-checkpoint-policy), [`session-persistence-jsonl`](../packages/session-persistence/session-persistence-jsonl), [`tools`](../packages/core/tools), [`workspace-context`](../packages/context/workspace-context) |
| [`tui-demo`](../packages/examples/tui-demo) | `examples` | [`agent`](../packages/core/agent), [`agent-loop`](../packages/core/agent-loop), [`agent-spine-demo`](../packages/examples/agent-spine-demo), [`app-boot`](../packages/ui/app-boot), [`command-goal`](../packages/goal/command-goal), [`commands`](../packages/ui/commands), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`session-checkpoint-policy`](../packages/session-persistence/session-checkpoint-policy), [`session-persistence-jsonl`](../packages/session-persistence/session-persistence-jsonl), [`session-query`](../packages/session-query/session-query), [`session-query-sqlite`](../packages/session-query/session-query-sqlite), [`session-reference`](../packages/context/session-reference), [`tool-ask-user`](../packages/ui/tool-ask-user), [`tools`](../packages/core/tools), [`tui`](../packages/ui/tui), [`user-interaction`](../packages/ui/user-interaction), [`workspace-context`](../packages/context/workspace-context) |

View File

@@ -78,7 +78,7 @@ export type SessionEvent<T extends SessionEventType = SessionEventType> = {
}[T]
```
Sources: [`packages/core/session/src/types.ts:325`](../packages/core/session/src/types.ts) · [`packages/core/session/src/types.ts:338`](../packages/core/session/src/types.ts) · [`packages/core/session/src/types.ts:367`](../packages/core/session/src/types.ts) · [`packages/core/session/src/types.ts:399`](../packages/core/session/src/types.ts)
Sources: [`packages/core/session/src/types.ts:324`](../packages/core/session/src/types.ts) · [`packages/core/session/src/types.ts:337`](../packages/core/session/src/types.ts) · [`packages/core/session/src/types.ts:366`](../packages/core/session/src/types.ts) · [`packages/core/session/src/types.ts:398`](../packages/core/session/src/types.ts)
## Events
@@ -150,7 +150,7 @@ Source: [`packages/ui/user-approval/src/index.ts:67`](../packages/ui/user-approv
Types: [StreamChunk](core-data-structures/llm-streaming.md)
Source: [`packages/core/session/src/types.ts:271`](../packages/core/session/src/types.ts)
Source: [`packages/core/session/src/types.ts:270`](../packages/core/session/src/types.ts)
#### `assistant/message` — surface
@@ -166,7 +166,7 @@ Source: [`packages/core/session/src/types.ts:271`](../packages/core/session/src/
Types: [ContentBlock](core-data-structures/core.md) · [TokenUsage](core-data-structures/llm-streaming.md)
Source: [`packages/core/session/src/types.ts:278`](../packages/core/session/src/types.ts)
Source: [`packages/core/session/src/types.ts:277`](../packages/core/session/src/types.ts)
### `compact/*`
@@ -329,7 +329,7 @@ Source: [`packages/plan/plan-mode/src/index.ts:41`](../packages/plan/plan-mode/s
Types: [ContentBlock](core-data-structures/core.md) · [MessageSource](core-data-structures/core.md)
Source: [`packages/core/session/src/types.ts:269`](../packages/core/session/src/types.ts)
Source: [`packages/core/session/src/types.ts:268`](../packages/core/session/src/types.ts)
### `request/*`
@@ -343,7 +343,7 @@ Source: [`packages/core/session/src/types.ts:269`](../packages/core/session/src/
'request/header': { header: EpochHeader; reason: RequestHeaderReason }
```
Source: [`packages/core/session/src/types.ts:313`](../packages/core/session/src/types.ts)
Source: [`packages/core/session/src/types.ts:312`](../packages/core/session/src/types.ts)
### `sandbox/*`
@@ -399,7 +399,7 @@ Source: [`packages/session-title/session-title-llm/src/index.ts:44`](../packages
'steering/message': PromptMessageData & { turn: number }
```
Source: [`packages/core/session/src/types.ts:306`](../packages/core/session/src/types.ts)
Source: [`packages/core/session/src/types.ts:305`](../packages/core/session/src/types.ts)
### `step/*`
@@ -410,7 +410,7 @@ Source: [`packages/core/session/src/types.ts:306`](../packages/core/session/src/
'step/end': { turn: number; step: number }
```
Source: [`packages/core/session/src/types.ts:254`](../packages/core/session/src/types.ts)
Source: [`packages/core/session/src/types.ts:253`](../packages/core/session/src/types.ts)
#### `step/start` — log-only
@@ -419,7 +419,7 @@ Source: [`packages/core/session/src/types.ts:254`](../packages/core/session/src/
'step/start': { turn: number; step: number }
```
Source: [`packages/core/session/src/types.ts:252`](../packages/core/session/src/types.ts)
Source: [`packages/core/session/src/types.ts:251`](../packages/core/session/src/types.ts)
### `todo/*`
@@ -432,7 +432,7 @@ Source: [`packages/core/session/src/types.ts:252`](../packages/core/session/src/
Types: [TodoItem](core-data-structures/session.md)
Source: [`packages/core/session/src/types.ts:308`](../packages/core/session/src/types.ts)
Source: [`packages/core/session/src/types.ts:307`](../packages/core/session/src/types.ts)
### `tool/*`
@@ -449,7 +449,7 @@ Source: [`packages/core/session/src/types.ts:308`](../packages/core/session/src/
Types: [CallId](core-data-structures/core.md)
Source: [`packages/core/session/src/types.ts:284`](../packages/core/session/src/types.ts)
Source: [`packages/core/session/src/types.ts:283`](../packages/core/session/src/types.ts)
#### `tool/code-dispatch` — log-only
@@ -503,7 +503,7 @@ Source: [`packages/core/tools/src/code-mode.ts:34`](../packages/core/tools/src/c
Types: [CallId](core-data-structures/core.md) · [ContentBlock](core-data-structures/core.md)
Source: [`packages/core/session/src/types.ts:296`](../packages/core/session/src/types.ts)
Source: [`packages/core/session/src/types.ts:295`](../packages/core/session/src/types.ts)
### `turn/*`
@@ -521,7 +521,7 @@ Source: [`packages/core/session/src/types.ts:296`](../packages/core/session/src/
Types: [TurnEndReason](core-data-structures/session.md)
Source: [`packages/core/session/src/types.ts:250`](../packages/core/session/src/types.ts)
Source: [`packages/core/session/src/types.ts:249`](../packages/core/session/src/types.ts)
#### `turn/start` — log-only
@@ -537,7 +537,7 @@ Source: [`packages/core/session/src/types.ts:250`](../packages/core/session/src/
Types: [TurnTrigger](core-data-structures/session.md)
Source: [`packages/core/session/src/types.ts:243`](../packages/core/session/src/types.ts)
Source: [`packages/core/session/src/types.ts:242`](../packages/core/session/src/types.ts)
### `user/*`
@@ -556,4 +556,4 @@ Source: [`packages/core/session/src/types.ts:243`](../packages/core/session/src/
'user/message': PromptMessageData
```
Source: [`packages/core/session/src/types.ts:264`](../packages/core/session/src/types.ts)
Source: [`packages/core/session/src/types.ts:263`](../packages/core/session/src/types.ts)

View File

@@ -2,5 +2,5 @@
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write
0001-acp-default-export-drops-inject.md: ab3efc880cb5290dc149b6bacb276ccf581968c1
0001-acp-default-export-drops-inject.zh.md: 763b2e6230cf42dcd6985df3d7e2da8766176fd5
0001-acp-default-export-drops-inject.md: 2d36f24fa54814e39345d7fe68792023c2cf0194
0001-acp-default-export-drops-inject.zh.md: c528f8be04013803274e80e51970754e92a935ae

View File

@@ -26,7 +26,7 @@ The ACP server could not create or load a single session — the two RPCs an edi
## Root cause #1 — `export default apply` drops the plugin's `inject` (broke `session/new`)
`packages/ui/acp/src/index.ts` is a *namespace plugin*: it exports `name`, `inject`, `Config`, and `apply` as separate named exports — the same shape as every other plugin in the repo (`invariants`, `llm-deepseek`, `tool-bash`, `tui`, …). But it *also* ended with one extra line no other plugin had:
`packages/acp/acp/src/index.ts` is a *namespace plugin*: it exports `name`, `inject`, `Config`, and `apply` as separate named exports — the same shape as every other plugin in the repo (`invariants`, `llm-deepseek`, `tool-bash`, `tui`, …). But it *also* ended with one extra line no other plugin had:
```ts ignore-check
export const name = 'acp'
@@ -99,7 +99,7 @@ Both bugs share one root process gap: **no test exercised the plugin through its
## Guardrails added
- **Removed `export default apply`** (`packages/ui/acp/src/index.ts`) — the Bug #1 fix.
- **Removed `export default apply`** (`packages/acp/acp/src/index.ts`) — the Bug #1 fix.
- **`AgentLoop.resume` reads `this.ctx.get('sessionPersistence')`** (`packages/core/agent-loop/src/index.ts`) — the Bug #2 fix, with a comment explaining the shadow-walk trap.
- **No-key `session/new` e2e over real stdio** (`examples/acp-agent/tests/acp.e2e.ts`): boots the example as a subprocess through the real Loader and asserts `session/new` resolves. This fails loudly on Bug #1 with no API key. Verified it fails when `export default apply` is restored.
- **`TSX_TSCONFIG_PATH` in the e2e spawn**: the subprocess runs from a temp cwd, where tsx cannot find the repo-root tsconfig `paths` map by searching upward — so dsh-* imports silently fell back to built `lib/`. Pointing tsx at the repo tsconfig makes resolution cwd-independent and ensures the test runs *source*, not a possibly-stale build.

View File

@@ -26,7 +26,7 @@ ACP 服务器无法创建或加载任何一个会话——而这正是编辑器
## 根因 #1——`export default apply` 丢弃了插件的 `inject`(导致 `session/new` 崩溃)
`packages/ui/acp/src/index.ts` 是一个*命名空间插件*:它将 `name``inject``Config``apply` 作为独立的命名导出——与仓库中其他所有插件(`invariants``llm-deepseek``tool-bash``tui` 等)形状相同。但它*还*多了一行其他插件都没有的代码:
`packages/acp/acp/src/index.ts` 是一个*命名空间插件*:它将 `name``inject``Config``apply` 作为独立的命名导出——与仓库中其他所有插件(`invariants``llm-deepseek``tool-bash``tui` 等)形状相同。但它*还*多了一行其他插件都没有的代码:
```ts ignore-check
export const name = 'acp'
@@ -99,7 +99,7 @@ if (!ctx.fiber.runtime) return ctx.reflect.get(prop, false) // ← direct glob
## 新增的防护措施
- **删除 `export default apply`**`packages/ui/acp/src/index.ts`——Bug #1 的修复。
- **删除 `export default apply`**`packages/acp/acp/src/index.ts`——Bug #1 的修复。
- **`AgentLoop.resume` 使用 `this.ctx.get('sessionPersistence')`**`packages/core/agent-loop/src/index.ts`——Bug #2 的修复,附注释说明 shadow 遍历陷阱。
- **无需 key 的 `session/new` e2e通过真实 stdio 运行**`examples/acp-agent/tests/acp.e2e.ts`):以子进程方式通过真实 Loader 启动示例,并断言 `session/new` 正常返回。无需 API key 即可在 Bug #1 上大声失败。已验证恢复 `export default apply` 时测试失败。
- **e2e spawn 中设置 `TSX_TSCONFIG_PATH`**:子进程从临时 cwd 运行tsx 无法通过向上搜索找到仓库根的 tsconfig `paths` 映射——因此 dsh-* 的 import 静默回退到已构建的 `lib/`。将 tsx 指向仓库 tsconfig 使解析不依赖 cwd确保测试运行的是*源码*而非可能陈旧的构建产物。

View File

@@ -2,5 +2,5 @@
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write
testing.md: fd38fb7b20d76ef48c81c86badcf501f7c0dbd4e
testing.zh.md: 4584492350aefd5d72692093b08c0dcd4910a8af
testing.md: ac5324c6ad2cc0718d9ad4f2d699120abb033374
testing.zh.md: 54e7a91b69480893a871d73b95696b10ffd69926

View File

@@ -9,7 +9,7 @@ How this repo tests, tier by tier, and the rules that keep a green suite meaning
- **Unit** (`pnpm run test`): vitest over package and example specs under their `tests/**` directories plus repository script specs under `scripts/**/*.spec.ts`; tests stay with the code area they exercise. Every registry gets an HMR-safety test (dispose the contributing fiber, assert cleanup). Prefer edge cases, error paths, event ordering, concurrency races, and permanent contract regressions (see `packages/core/agent-loop/tests/contract-regressions.spec.ts`).
- **Coverage gate** (`pnpm run test:coverage`): the gating run, per-file 100% on `packages/*/*/src`. An uncovered line is often dead code the gate is correctly flagging for deletion, not a missing test to bolt on. Line coverage is necessary, never sufficient — it proves lines ran, not that the feature works as shipped.
- **Real-API e2e** (`pnpm run test:e2e`): with-key tests against live provider APIs — the DeepSeek model plus provider-specific smokes that gate on their own keys (`EXA_API_KEY`, `PERPLEXITY_API_KEY`, …); each suite self-skips without its key so keyless CI stays green ([real-API e2e Agent Note](../.agents/notes/implemented/testing/2026-06-19-real-api-e2e-ci.md)).
- **Snapshot** (`pnpm run test:snapshot`): keyless expected outputs cover external presentation. ACP boots the real example, replays a recorded session, and diffs normalized JSON-RPC plus the re-persisted log ([ACP snapshot Agent Note](../.agents/notes/implemented/testing/2026-06-19-acp-snapshot-tests.md)); headless pins `stream-json` through its real one-shot process. TUI journeys replay primary/child JSONL through the real loop and tools, then project ANSI into semantic terminal-state outputs; package snapshots retain transient states and a real PTY covers the process boundary ([TUI snapshot Agent Note](../.agents/notes/implemented/testing/2026-07-18-tui-terminal-state-snapshots.md)). Use `pnpm run test:snapshot:record` when a model transcript changes and `pnpm run test:snapshot:refresh` when replay input remains valid; review every JSONL and expected-output diff. One ACP scenario (`text-turn`) pins full system-prompt/tool-schema content; other fixtures tokenize it so an edit churns one line ([pinned-header Agent Note](../.agents/notes/implemented/testing/2026-07-06-pin-request-header-content-in-one-scenario.md)).
- **Snapshot** (`pnpm run test:snapshot`): keyless expected outputs cover external behavior — transport contracts and presentation, while persisted logs pin assembled backend behavior. ACP boots the real automation-server example, replays a recorded session, and diffs normalized JSON-RPC plus the re-persisted log ([ACP snapshot Agent Note](../.agents/notes/implemented/testing/2026-06-19-acp-snapshot-tests.md)); headless pins `stream-json` through its real one-shot process. TUI journeys replay primary/child JSONL through the real loop and tools, then project ANSI into semantic terminal-state outputs; package snapshots retain transient states and a real PTY covers the process boundary ([TUI snapshot Agent Note](../.agents/notes/implemented/testing/2026-07-18-tui-terminal-state-snapshots.md)). Use `pnpm run test:snapshot:record` when a model transcript changes and `pnpm run test:snapshot:refresh` when replay input remains valid; review every JSONL and expected-output diff. One ACP scenario (`text-turn`) pins full system-prompt/tool-schema content; other fixtures tokenize it so an edit churns one line ([pinned-header Agent Note](../.agents/notes/implemented/testing/2026-07-06-pin-request-header-content-in-one-scenario.md)).
## The with-key policy: inference is cheap here
@@ -43,4 +43,4 @@ An e2e assertion re-runs the command or re-reads the file externally; a keyword
## When a snapshot test is required
Every non-trivial model- or human-visible change adds or updates a keyless scenario in the same PR through a runnable example's owning snapshot suite. Package tests, e2e assertions, mock/test-only compositions, and PR rationale do not replace the assembled transcript; extend the harness when needed. ACP surfaces use `examples/<name>/tests/snapshots/`, a scenario table over the [`dsh-acp-snapshot`](../packages/support/acp-snapshot/README.md) suite factory (`examples/acp-agent` is primary); `examples/headless-agent` owns the `stream-json` snapshot and replay fixtures. Completed interactive-terminal journeys use JSONL-driven scenarios under `examples/tui-agent/tests/snapshots/`; transient presentation uses the package-local semantic matrix, with a PTY case when input, Loader selection, or terminal teardown changes. New capability seams, lifecycle shapes, or transcript surfaces name every coverage tier at plan time and verify the harness can express it before implementation.
Every non-trivial model-, protocol-, or human-visible change adds or updates a keyless scenario in the same PR through a runnable example's owning snapshot suite. Package tests, e2e assertions, mock/test-only compositions, and PR rationale do not replace the assembled transcript; extend the harness when needed. ACP automation scenarios use `examples/<name>/tests/snapshots/`, a scenario table over the [`dsh-acp-snapshot`](../packages/support/acp-snapshot/README.md) suite factory (`examples/acp-agent` is primary); `examples/headless-agent` owns the `stream-json` snapshot and replay fixtures. Completed interactive-terminal journeys use JSONL-driven scenarios under `examples/tui-agent/tests/snapshots/`; transient presentation uses the package-local semantic matrix, with a PTY case when input, Loader selection, or terminal teardown changes. New capability seams, lifecycle shapes, or transcript surfaces name every coverage tier at plan time and verify the harness can express it before implementation.

View File

@@ -9,7 +9,7 @@
- **单元测试**`pnpm run test`vitest 运行包package和示例各自的 `tests/**` 目录下的测试,以及匹配 `scripts/**/*.spec.ts` 的仓库脚本测试;测试文件与其所覆盖的代码区域放在一起。每个注册表都有一个 HMR热模块替换安全测试dispose资源释放贡献的 fiber断言清理完成。优先覆盖边界情况、错误路径、事件顺序、并发竞态以及永久性契约回归`packages/core/agent-loop/tests/contract-regressions.spec.ts`)。
- **覆盖率门禁**`pnpm run test:coverage`):门禁级运行,对 `packages/*/*/src` 按文件 100% 覆盖。未覆盖的行往往是门禁正确标记出的死代码(应删除),而非需要补写的测试。行覆盖率是必要条件,但永远不是充分条件:它证明行被执行过,不证明功能按交付预期工作。
- **真实 API e2e**`pnpm run test:e2e`):带密钥测试调用真实提供方 API包括 DeepSeek 模型以及各提供方特有的冒烟测试;这些测试各自由自己的密钥控制(`EXA_API_KEY``PERPLEXITY_API_KEY` 等),缺少密钥时套件会自动跳过,使 keyless CI 保持绿色([真实 API e2e Agent Note](../.agents/notes/implemented/testing/2026-06-19-real-api-e2e-ci.md))。
- **快照**`pnpm run test:snapshot`):无密钥预期输出覆盖对外呈现。ACP 启动真实示例、回放录制会话,并对归一化 JSON-RPC 与重新持久化的日志执行 diff[ACP 快照 Agent Note](../.agents/notes/implemented/testing/2026-06-19-acp-snapshot-tests.md)headless 通过真实单次运行进程固定 `stream-json`。TUI 旅程通过真实循环与工具回放主会话与子会话 JSONL再将 ANSI 投影为语义化终端状态输出;包级快照保留瞬态状态,真实 PTY 覆盖进程边界([TUI 快照 Agent Note](../.agents/notes/implemented/testing/2026-07-18-tui-terminal-state-snapshots.md))。当模型 transcript文本记录发生变化时使用 `pnpm run test:snapshot:record`,回放输入仍然有效时使用 `pnpm run test:snapshot:refresh`;请审查每一处 JSONL 与预期输出差异。一个 ACP 场景(`text-turn`)固定完整的系统提示词与工具 schema 内容;其他 fixture测试前置数据将其 token 化,因此修改只会扰动一行([pinned-header Agent Note](../.agents/notes/implemented/testing/2026-07-06-pin-request-header-content-in-one-scenario.md))。
- **快照**`pnpm run test:snapshot`):无密钥预期输出覆盖对外行为(传输契约与呈现),持久化日志则固定组装后的后端行为。ACP 启动真实的自动化服务器示例、回放录制会话,并对归一化 JSON-RPC 与重新持久化的日志执行 diff[ACP 快照 Agent Note](../.agents/notes/implemented/testing/2026-06-19-acp-snapshot-tests.md)headless 通过真实单次运行进程固定 `stream-json`。TUI 旅程通过真实循环与工具回放主会话与子会话 JSONL再将 ANSI 投影为语义化终端状态输出;包级快照保留瞬态状态,真实 PTY 覆盖进程边界([TUI 快照 Agent Note](../.agents/notes/implemented/testing/2026-07-18-tui-terminal-state-snapshots.md))。当模型 transcript文本记录发生变化时使用 `pnpm run test:snapshot:record`,回放输入仍然有效时使用 `pnpm run test:snapshot:refresh`;请审查每一处 JSONL 与预期输出差异。一个 ACP 场景(`text-turn`)固定完整的系统提示词与工具 schema 内容;其他 fixture测试前置数据将其 token 化,因此修改只会扰动一行([pinned-header Agent Note](../.agents/notes/implemented/testing/2026-07-06-pin-request-header-content-in-one-scenario.md))。
## 带密钥策略:推理在这里很便宜
@@ -43,4 +43,4 @@ e2e 断言应重新运行命令或从外部重新读取文件;对 agent 自身
## 何时需要快照测试
每项非平凡的模型可见或人类可见变更,都必须在同一 PR 中通过可运行示例所属的快照套件添加或更新无密钥场景。包测试、e2e 断言、mock 与仅测试组合、PR 理由都不能取代组装后的 transcript必要时应扩展 harness。ACP 接口使用 `examples/<name>/tests/snapshots/`,即基于 [`dsh-acp-snapshot`](../packages/support/acp-snapshot/README.md) 套件工厂的场景表(`examples/acp-agent` 为主套件);`examples/headless-agent` 拥有 `stream-json` 快照与回放 fixture。已完成的交互式终端旅程使用 `examples/tui-agent/tests/snapshots/` 下由 JSONL 驱动的场景瞬态呈现使用包内语义矩阵输入、Loader 选择或终端清理发生变化时还要添加 PTY 用例。新的能力 seam、生命周期形态或 transcript 呈现接口在计划阶段就要列出每个覆盖层级,并在实现前验证 harness 能够表达它们。
每项非平凡的模型可见、协议可见或人类可见变更,都必须在同一 PR 中通过可运行示例所属的快照套件添加或更新无密钥场景。包测试、e2e 断言、mock 与仅测试组合、PR 理由都不能取代组装后的 transcript必要时应扩展 harness。ACP 自动化场景使用 `examples/<name>/tests/snapshots/`,即基于 [`dsh-acp-snapshot`](../packages/support/acp-snapshot/README.md) 套件工厂的场景表(`examples/acp-agent` 为主套件);`examples/headless-agent` 拥有 `stream-json` 快照与回放 fixture。已完成的交互式终端旅程使用 `examples/tui-agent/tests/snapshots/` 下由 JSONL 驱动的场景瞬态呈现使用包内语义矩阵输入、Loader 选择或终端清理发生变化时还要添加 PTY 用例。新的能力 seam、生命周期形态或 transcript 呈现接口在计划阶段就要列出每个覆盖层级,并在实现前验证 harness 能够表达它们。

View File

@@ -29,7 +29,7 @@ This table connects model-visible tool names to the plugin package and service s
| `@deepseek-ai/dsh-tool-skill` | `skill` | `ctx.tools`, `ctx.skills` | `tool/call`, `tool/result` | - | - |
| `@deepseek-ai/dsh-tool-subagent` | `subagent` | `ctx.tools`, `ctx.subagents` | `tool/call`, `tool/result`, `child session events through the chosen provider` | `subagent`, `subagent_fork` | The registered tool name is the load-time `toolName` config (default `subagent`); the schema above is that default. The shipped example agents load this package once per subagent backend, so the model additionally sees `subagent_fork` (bound to the fork backend) with an identical schema — see `examples/tui-agent/cordis.yml` and `examples/acp-agent/cordis.yml`. |
| `@deepseek-ai/dsh-tool-tasks` | `task_kill`, `task_list`, `task_output` | `ctx.tools`, `ctx.tasks`, `ctx.systemPrompt` | `tool/call`, `tool/result`, `user/message via agent.inject() for background completion notices` | - | The kind-agnostic background-task control surface: background bash commands, PTY sends, and subagents are read, listed, and killed through the same three tools. Loading the plugin attaches the control surface that arms producers' `ctx.tasks.start()`. |
| `@deepseek-ai/dsh-tool-todo` | `todo_write` | `ctx.tools`, `owning Agent session` | `tool/call`, `todo/write`, `tool/result` | - | todo_write is session-owned state; UIs render the latest todo/write event as a checklist or ACP plan. |
| `@deepseek-ai/dsh-tool-todo` | `todo_write` | `ctx.tools`, `owning Agent session` | `tool/call`, `todo/write`, `tool/result` | - | todo_write is session-owned state; UIs render the latest todo/write event as a checklist. |
| `@deepseek-ai/dsh-tool-workflow` | `workflow` | `ctx.tools`, `ctx.workflows`, `ctx.systemPrompt`, `a calling Agent (exec.agent parents the script children)` | `tool/call`, `tool/result` | - | - |
| `@deepseek-ai/dsh-tool-web` | `web_fetch`, `web_search` | `ctx.tools`, `ctx.web`, `ctx.systemPrompt` | `tool/call`, `tool/result` | - | web_search and web_fetch keep provider selection behind ctx.web so model-visible schemas stay stable across backend swaps. |
@@ -929,7 +929,7 @@ Record and update a structured task list for the current work. Send the ENTIRE l
Source: [`packages/todo/tool-todo/src/index.ts`](../packages/todo/tool-todo/src/index.ts)
todo_write is session-owned state; UIs render the latest todo/write event as a checklist or ACP plan.
todo_write is session-owned state; UIs render the latest todo/write event as a checklist.
## `@deepseek-ai/dsh-tool-workflow`

View File

@@ -2,5 +2,5 @@
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write
tool.md: 7b211cfef54306f7c316dc08da1df759dcbf1b06
tool.zh.md: 214b35b28de0c647737bc8297b13b4997947b52e
tool.md: 0d7cbc3f0b86f88fb67aeff6aa61181dff2912ee
tool.zh.md: 30cc871d7b417bdf7f33025b22e3f0965e2b8805

View File

@@ -158,7 +158,7 @@ Do not repeat type validation inside `execute`.
## Presentation
A tool can define UI presentation methods for terminal and ACP clients:
A tool can define transport-neutral presentation methods for terminal and web clients:
```ts ignore-check
defineTool({

View File

@@ -158,7 +158,7 @@ async execute(args) {
## 展示层 (Presentation)
Tool 可以定义 UI 渲染方法,用于在终端或 ACP 客户端中展示 tool call 和 result
Tool 可以定义与传输方式无关的展示方法,供终端和 Web 客户端使用
```ts ignore-check
defineTool({

View File

@@ -2,5 +2,5 @@
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write
config.md: 8958729d04224215ca420c3103d253a8a5783405
config.zh.md: 530f2b335453d5064acdac28a60d7df51cd915f0
config.md: a884cb9c2ec31bd4b12a31cce6290df1d134cf9a
config.zh.md: 3fb9ce69e5f7cb6b92f055ef18595e5ff07d9bbf

View File

@@ -10,7 +10,7 @@ The repository examples are runnable configurations and the most reliable starti
- [tui-agent](../../../examples/tui-agent/cordis.yml) combines the DeepSeek model, Bash, filesystem, compaction, subagents, workflows, and the interactive TUI.
- [headless-agent](../../../examples/headless-agent/cordis.yml) exposes the coding composition as a one-shot task.
- [acp-agent](../../../examples/acp-agent/cordis.yml) connects to editor clients over ACP.
- [acp-agent](../../../examples/acp-agent/cordis.yml) exposes fresh sessions to programmatic ACP clients.
A minimal configuration is a list of plugin entries:

View File

@@ -10,7 +10,7 @@ Harness 使用 `cordis.yml` 描述 Agent 加载哪些插件以及每个插件的
- [tui-agent](../../../examples/tui-agent/cordis.yml) 组合 DeepSeek 模型、Bash、文件系统、压缩、子代理、工作流和交互式 TUI。
- [headless-agent](../../../examples/headless-agent/cordis.yml) 以单次任务形式暴露 coding 组装。
- [acp-agent](../../../examples/acp-agent/cordis.yml) 通过 ACP 接入编辑器客户端
- [acp-agent](../../../examples/acp-agent/cordis.yml) 向程序化 ACPAgent Client Protocol客户端提供全新会话
最小配置由一组插件条目组成: