docs: revise graph docs from review

This commit is contained in:
Tianyi Cui
2026-07-05 01:25:58 +08:00
parent fc6b7ccf1e
commit 1316022cc8
34 changed files with 882 additions and 1143 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
README.md: 11db682a995c0327134493c7178bc09e393b44e9
README.zh.md: f2cd9da367c6c7ec3ba3fcd71d1b9547987555fa
README.md: 53dd3896eb15800125673e7c44f7de02daca9376
README.zh.md: 5de4c5b6804648f061647d9e315c08a32b42b39b

View File

@@ -15,6 +15,6 @@ pnpm run demo:repl # REPL agent demo (needs DEEPSEEK_API_KEY)
pnpm run demo:acp # ACP server agent demo (needs DEEPSEEK_API_KEY)
```
For humans, start with the [development guide](docs/development.md) for local setup, hooks, environment variables, and quality gates, then read the [architecture design](docs/architecture.md) and [documentation graph atlas](docs/graphs/README.md) before package work. Local context lives in [packages/](packages/) and [vendor/](vendor/).
For humans, start with the [development guide](docs/development.md) for local setup, hooks, environment variables, and quality gates, then read the [architecture design](docs/architecture.md) and [documentation graph index](docs/graph-atlas.md) before package work. Local context lives in [packages/](packages/) and [vendor/](vendor/).
For agents, follow [AGENTS.md](AGENTS.md).

View File

@@ -15,6 +15,6 @@ pnpm run demo:repl # REPL agent demo (needs DEEPSEEK_API_KEY)
pnpm run demo:acp # ACP server agent demo (needs DEEPSEEK_API_KEY)
```
面向人类读者:先读[开发指南](docs/development.md)了解本地环境搭建、钩子、环境变量与质量门禁,动手改 package 之前再读[架构设计](docs/architecture.md)和 [documentation graph atlas](docs/graphs/README.md)。局部上下文见 [packages/](packages/) 与 [vendor/](vendor/)。
面向人类读者:先读[开发指南](docs/development.md)了解本地环境搭建、钩子、环境变量与质量门禁,动手改 package 之前再读[架构设计](docs/architecture.md)和[文档关系图索引](docs/graph-atlas.md)。局部上下文见 [packages/](packages/) 与 [vendor/](vendor/)。
面向 agent遵循 [AGENTS.md](AGENTS.md)。

View File

@@ -0,0 +1,67 @@
<!-- Generated by scripts/gen-doc-graphs.ts - do not edit by hand.
Run `pnpm run gen-doc-graphs` to regenerate. -->
# ACP Agent App Composition
Maintenance mode: hybrid: the leaf plugin list is parsed from its `cordis.yml`; app package expansion is curated from package source.
The ACP demo exposes the same agent spine over JSON-RPC stdio, with no stdout logger and no pre-created agent; clients create sessions through the ACP bridge.
```mermaid
flowchart LR
cfg["examples/acp-agent<br/>cordis.yml"]
plugin_acp_llm_deepseek["llm-deepseek<br/>@deepseek-ai/dsh-llm-deepseek"]
cfg --> plugin_acp_llm_deepseek
plugin_acp_bash["bash<br/>@deepseek-ai/dsh-bash-local"]
cfg --> plugin_acp_bash
plugin_acp_acp_agent["acp-agent<br/>@deepseek-ai/dsh-acp-agent"]
cfg --> plugin_acp_acp_agent
plugin_acp_acp_agent --> bundle_agent_core["@deepseek-ai/dsh-agent-core"]
plugin_acp_acp_agent --> bundle_jsonl["@deepseek-ai/dsh-session-persistence-jsonl"]
plugin_acp_acp_agent --> frontdoor_acp["@deepseek-ai/dsh-acp<br/>JSON-RPC stdio bridge<br/>sessions created by client"]
bundle_agent_core --> spine_llm["ctx.llm"]
bundle_agent_core --> spine_sessions["ctx.sessions"]
bundle_agent_core --> spine_tools["ctx.tools + tool-bash"]
bundle_agent_core --> spine_loop["ctx.agents + ctx.agentLoop"]
plugin_acp_subagent["subagent<br/>@deepseek-ai/dsh-subagent"]
cfg --> plugin_acp_subagent
plugin_acp_subagent_spawn["subagent-spawn<br/>@deepseek-ai/dsh-subagent-spawn"]
cfg --> plugin_acp_subagent_spawn
plugin_acp_subagent_fork["subagent-fork<br/>@deepseek-ai/dsh-subagent-fork"]
cfg --> plugin_acp_subagent_fork
plugin_acp_tool_subagent["tool-subagent<br/>@deepseek-ai/dsh-tool-subagent"]
cfg --> plugin_acp_tool_subagent
plugin_acp_tool_subagent_fork["tool-subagent-fork<br/>@deepseek-ai/dsh-tool-subagent"]
cfg --> plugin_acp_tool_subagent_fork
plugin_acp_tool_todo["tool-todo<br/>@deepseek-ai/dsh-tool-todo"]
cfg --> plugin_acp_tool_todo
plugin_acp_fs_local["fs-local<br/>@deepseek-ai/dsh-fs-local"]
cfg --> plugin_acp_fs_local
plugin_acp_fs_policy["fs-policy<br/>@deepseek-ai/dsh-fs-policy"]
cfg --> plugin_acp_fs_policy
plugin_acp_tool_fs["tool-fs<br/>@deepseek-ai/dsh-tool-fs"]
cfg --> plugin_acp_tool_fs
plugin_acp_hooks_claude["hooks-claude<br/>@deepseek-ai/dsh-hooks-claude"]
cfg --> plugin_acp_hooks_claude
plugin_acp_hooks_codex["hooks-codex<br/>@deepseek-ai/dsh-hooks-codex"]
cfg --> plugin_acp_hooks_codex
```
| Plugin id | Package / module |
| --- | --- |
| `llm-deepseek` | `@deepseek-ai/dsh-llm-deepseek` |
| `bash` | `@deepseek-ai/dsh-bash-local` |
| `acp-agent` | `@deepseek-ai/dsh-acp-agent` |
| `subagent` | `@deepseek-ai/dsh-subagent` |
| `subagent-spawn` | `@deepseek-ai/dsh-subagent-spawn` |
| `subagent-fork` | `@deepseek-ai/dsh-subagent-fork` |
| `tool-subagent` | `@deepseek-ai/dsh-tool-subagent` |
| `tool-subagent-fork` | `@deepseek-ai/dsh-tool-subagent` |
| `tool-todo` | `@deepseek-ai/dsh-tool-todo` |
| `fs-local` | `@deepseek-ai/dsh-fs-local` |
| `fs-policy` | `@deepseek-ai/dsh-fs-policy` |
| `tool-fs` | `@deepseek-ai/dsh-tool-fs` |
| `hooks-claude` | `@deepseek-ai/dsh-hooks-claude` |
| `hooks-codex` | `@deepseek-ai/dsh-hooks-codex` |
Source config: [`examples/acp-agent/cordis.yml`](../examples/acp-agent/cordis.yml).

View File

@@ -18,7 +18,7 @@ sequenceDiagram
Recorder->>Fixture: session.jsonl + workspace inputs
Fixture->>Workspace: seed files and hook configs
Fixture->>Replay: recorded StreamChunk script
Replay->>ACP: deterministic llm/stream chunks
Replay->>ACP: deterministic <code>llm/stream</code> chunks
ACP->>Workspace: bash, fs, and hook side effects
ACP->>Golden: normalized sessionUpdate stream
Golden-->>ACP: diff must be empty

49
docs/agent-lifecycle.md Normal file
View File

@@ -0,0 +1,49 @@
<!-- Generated by scripts/gen-doc-graphs.ts - do not edit by hand.
Run `pnpm run gen-doc-graphs` to regenerate. -->
# Agent Turn And Step Lifecycle
Maintenance mode: curated Mermaid sequence; exact event signatures live in the generated Cordis catalog.
This sequence is the visual companion to [architecture.md](architecture.md#loop-lifecycle-session--turn--step). It keeps durable replay facts on `session/event` and live control/status on `agent/*`.
```mermaid
sequenceDiagram
participant User
participant Agent
participant Driver
participant Hooks as hook listeners
participant Prompt as ctx.systemPrompt
participant LLM as ctx.llm
participant Tools as ctx.tools
participant Session
participant Persistence
participant SDK as UI or SDK listener
User->>Agent: send(content)
Agent-->>SDK: <code>agent/queued</code>
Agent->>Driver: queued work wakes driver
Driver-->>SDK: <code>agent/status</code> running
Driver->>Session: <code>turn/start</code>
Driver->>Hooks: <code>agent/prompt-submit</code> waterfall
Hooks-->>Driver: allow, block, or add context
Driver->>Session: <code>user/message</code> or rejected <code>turn/end</code>
Driver->>Prompt: <code>system-prompt/assemble</code> waterfall
Driver-->>Driver: <code>agent/pre-step</code> serial checkpoint
Driver->>Session: <code>step/start</code>
Driver->>LLM: <code>agent/request</code> waterfall, then <code>llm/stream</code> waterfall
LLM-->>Driver: StreamChunk*
Driver->>Session: <code>assistant/chunk</code>*
Session-->>SDK: <code>session/event</code> <code>assistant/chunk</code>*
Driver->>Hooks: <code>agent/step-result</code> waterfall
Driver->>Session: <code>assistant/message</code>
Driver->>Session: <code>tool/call</code>
Driver->>Tools: execute through pre and post waterfalls
Tools-->>Session: tool-owned events when applicable
Driver->>Session: <code>tool/result</code> and <code>step/end</code>
Driver->>Hooks: <code>agent/turn-continuation</code> waterfall
Driver->>Session: <code>turn/end</code>
Driver->>Persistence: <code>session/flush</code> parallel checkpoint
Driver-->>SDK: <code>agent/status</code> idle
```
SDK users that need replayable transcript data should consume `session/event`; `agent/*` is the live coordination surface for queue/status, prompt interception, request shaping, steering, continuation, and errors.

View File

@@ -2,7 +2,7 @@
This document describes the architecture of the DeepSeek Harness — the foundation of **DeepSeek Code**. The governing principle, from the [microkernel design discussion][microkernel-doc]: **everything is a plugin**. The core is deliberately tiny — a handful of abstract services plus one concrete loop plugin (`dsh-agent-loop`) — and every product feature is a plugin against the extension surface described here, without modifying the loop.
This document covers **behavior**; type shapes live in [core-data-structures/](core-data-structures/core.md), the per-event/service reference in the [generated catalog](cordis-catalog/events-and-services.md), visual relationship maps in the [documentation graph atlas](graphs/README.md), and per-package contracts in the package READMEs ([map](../packages/README.md)). Requirement context: [Coding Harness MVP 需求分析][mvp-doc].
This document covers **behavior**; type shapes live in [core-data-structures/](core-data-structures/core.md), the per-event/service reference in the [generated catalog](cordis-catalog/events-and-services.md), visual relationship maps in the [documentation graph index](graph-atlas.md), and per-package contracts in the package READMEs ([map](../packages/README.md)). Requirement context: [Coding Harness MVP 需求分析][mvp-doc].
[microkernel-doc]: https://trtgsjkv6r.feishu.cn/wiki/VS9Lw1kQki6mDJk2UHocyuphnsc
[mvp-doc]: https://trtgsjkv6r.feishu.cn/wiki/ZwK6wfBE9i91V6kzMGYcgRGanxg

View File

@@ -128,15 +128,15 @@ flowchart LR
| ctx key | Role | Owner | Implementations | Direct consumers | Companion plugins | Note |
| --- | --- | --- | --- | --- | --- | --- |
| `ctx.llm` | `seam` | [`llm`](../../packages/llm/llm) | [`llm-deepseek`](../../packages/llm/llm-deepseek), [`llm-pi-ai`](../../packages/llm/llm-pi-ai), [`llm-replay`](../../packages/support/llm-replay) | [`agent-loop`](../../packages/core/agent-loop), [`compact-basic`](../../packages/compact/compact-basic) | - | Adapters register provider implementations; the loop and compaction call the provider-neutral stream service. |
| `ctx.sessions` | `core` | [`session`](../../packages/core/session) | - | [`agent-loop`](../../packages/core/agent-loop), [`agent`](../../packages/core/agent), [`session-persistence`](../../packages/session-persistence/session-persistence), [`subagent-inprocess`](../../packages/subagent/subagent-inprocess), [`invariants`](../../packages/support/invariants) | - | Owns append-only Session instances and emits the durable session event feed. |
| `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), [`acp`](../../packages/ui/acp) | - | Backends persist the same SessionEvent vocabulary; apps choose a backend at composition time. |
| `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-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-bash`](../../packages/bash/tool-bash), [`tool-fs`](../../packages/fs/tool-fs), [`tool-subagent`](../../packages/subagent/tool-subagent), [`tool-todo`](../../packages/todo/tool-todo), [`tool-web`](../../packages/web/tool-web), [`acp`](../../packages/ui/acp) | - | Registers tool definitions, exposes schemas to the prompt, and routes calls through tools/pre-execute and tools/post-execute. |
| `ctx.agents` | `core` | [`agent`](../../packages/core/agent) | - | [`agent-loop`](../../packages/core/agent-loop), [`acp`](../../packages/ui/acp), [`subagent-inprocess`](../../packages/subagent/subagent-inprocess), [`stdio-agent`](../../packages/ui/stdio-agent), [`invariants`](../../packages/support/invariants) | - | Owns live Agent handles and the create/resume factory seam. |
| `ctx.agentLoop` | `bundle` | [`agent-loop`](../../packages/core/agent-loop) | - | [`agent-core`](../../packages/core/agent-core) | - | The one concrete loop plugin; extension packages depend on dsh-agent events and services, not on this package. |
| `ctx.bash` | `seam` | [`bash`](../../packages/bash/bash) | [`bash-local`](../../packages/bash/bash-local) | [`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 can replace bash-local. |
| `ctx.fs` | `seam` | [`fs`](../../packages/fs/fs) | [`fs-local`](../../packages/fs/fs-local) | [`tool-fs`](../../packages/fs/tool-fs) | [`fs-policy`](../../packages/fs/fs-policy) | tool-fs executes read/write/edit through ctx.fs; 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 currently consumes the pre-step event directly; a model-facing compact tool remains deferred. |
| `ctx.subagents` | `seam` | [`subagent`](../../packages/subagent/subagent) | [`subagent-spawn`](../../packages/subagent/subagent-spawn), [`subagent-fork`](../../packages/subagent/subagent-fork), [`subagent-acp`](../../packages/subagent/subagent-acp), [`subagent-mock`](../../packages/support/subagent-mock) | [`tool-subagent`](../../packages/subagent/tool-subagent) | - | Providers implement transports; tool-subagent exposes one configured provider as a model-facing tool name. |
| `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.llm` | `seam` | [`llm`](../packages/llm/llm) | [`llm-deepseek`](../packages/llm/llm-deepseek), [`llm-pi-ai`](../packages/llm/llm-pi-ai), [`llm-replay`](../packages/support/llm-replay) | [`agent-loop`](../packages/core/agent-loop), [`compact-basic`](../packages/compact/compact-basic) | - | Adapters register provider implementations; the loop and compaction call the provider-neutral stream service. |
| `ctx.sessions` | `core` | [`session`](../packages/core/session) | - | [`agent-loop`](../packages/core/agent-loop), [`agent`](../packages/core/agent), [`session-persistence`](../packages/session-persistence/session-persistence), [`subagent-inprocess`](../packages/subagent/subagent-inprocess), [`invariants`](../packages/support/invariants) | - | Owns append-only Session instances and emits the durable session event feed. |
| `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), [`acp`](../packages/ui/acp) | - | Backends persist the same SessionEvent vocabulary; apps choose a backend at composition time. |
| `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-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-bash`](../packages/bash/tool-bash), [`tool-fs`](../packages/fs/tool-fs), [`tool-subagent`](../packages/subagent/tool-subagent), [`tool-todo`](../packages/todo/tool-todo), [`tool-web`](../packages/web/tool-web), [`acp`](../packages/ui/acp) | - | Registers tool definitions, exposes schemas to the prompt, and routes calls through tools/pre-execute and tools/post-execute. |
| `ctx.agents` | `core` | [`agent`](../packages/core/agent) | - | [`agent-loop`](../packages/core/agent-loop), [`acp`](../packages/ui/acp), [`subagent-inprocess`](../packages/subagent/subagent-inprocess), [`stdio-agent`](../packages/ui/stdio-agent), [`invariants`](../packages/support/invariants) | - | Owns live Agent handles and the create/resume factory seam. |
| `ctx.agentLoop` | `bundle` | [`agent-loop`](../packages/core/agent-loop) | - | [`agent-core`](../packages/core/agent-core) | - | The one concrete loop plugin; extension packages depend on dsh-agent events and services, not on this package. |
| `ctx.bash` | `seam` | [`bash`](../packages/bash/bash) | [`bash-local`](../packages/bash/bash-local) | [`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 can replace bash-local. |
| `ctx.fs` | `seam` | [`fs`](../packages/fs/fs) | [`fs-local`](../packages/fs/fs-local) | [`tool-fs`](../packages/fs/tool-fs) | [`fs-policy`](../packages/fs/fs-policy) | tool-fs executes read/write/edit through ctx.fs; 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 currently consumes the pre-step event directly; a model-facing compact tool remains deferred. |
| `ctx.subagents` | `seam` | [`subagent`](../packages/subagent/subagent) | [`subagent-spawn`](../packages/subagent/subagent-spawn), [`subagent-fork`](../packages/subagent/subagent-fork), [`subagent-acp`](../packages/subagent/subagent-acp), [`subagent-mock`](../packages/support/subagent-mock) | [`tool-subagent`](../packages/subagent/tool-subagent) | - | Providers implement transports; tool-subagent exposes one configured provider as a model-facing tool name. |
| `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. |

View File

@@ -0,0 +1,67 @@
<!-- Generated by scripts/gen-doc-graphs.ts - do not edit by hand.
Run `pnpm run gen-doc-graphs` to regenerate. -->
# Coding Agent App Composition
Maintenance mode: hybrid: the leaf plugin list is parsed from its `cordis.yml`; app package expansion is curated from package source.
The coding REPL demo adds the real DeepSeek adapter, filesystem tools, todo_write, compaction, and both subagent transports on top of the stdio app package.
```mermaid
flowchart LR
cfg["examples/coding-agent<br/>cordis.yml"]
plugin_coding_hmr["hmr<br/>@cordisjs/plugin-hmr"]
cfg --> plugin_coding_hmr
plugin_coding_llm_deepseek["llm-deepseek<br/>@deepseek-ai/dsh-llm-deepseek"]
cfg --> plugin_coding_llm_deepseek
plugin_coding_bash["bash<br/>@deepseek-ai/dsh-bash-local"]
cfg --> plugin_coding_bash
plugin_coding_stdio_agent["stdio-agent<br/>@deepseek-ai/dsh-stdio-agent"]
cfg --> plugin_coding_stdio_agent
plugin_coding_stdio_agent --> bundle_agent_core["@deepseek-ai/dsh-agent-core"]
plugin_coding_stdio_agent --> bundle_jsonl["@deepseek-ai/dsh-session-persistence-jsonl"]
plugin_coding_stdio_agent --> frontdoor_stdio["readline UI<br/>console logger<br/>pre-created main agent"]
bundle_agent_core --> spine_llm["ctx.llm"]
bundle_agent_core --> spine_sessions["ctx.sessions"]
bundle_agent_core --> spine_tools["ctx.tools + tool-bash"]
bundle_agent_core --> spine_loop["ctx.agents + ctx.agentLoop"]
plugin_coding_compact_basic["compact-basic<br/>@deepseek-ai/dsh-compact-basic"]
cfg --> plugin_coding_compact_basic
plugin_coding_subagent["subagent<br/>@deepseek-ai/dsh-subagent"]
cfg --> plugin_coding_subagent
plugin_coding_subagent_spawn["subagent-spawn<br/>@deepseek-ai/dsh-subagent-spawn"]
cfg --> plugin_coding_subagent_spawn
plugin_coding_subagent_fork["subagent-fork<br/>@deepseek-ai/dsh-subagent-fork"]
cfg --> plugin_coding_subagent_fork
plugin_coding_tool_subagent["tool-subagent<br/>@deepseek-ai/dsh-tool-subagent"]
cfg --> plugin_coding_tool_subagent
plugin_coding_tool_subagent_fork["tool-subagent-fork<br/>@deepseek-ai/dsh-tool-subagent"]
cfg --> plugin_coding_tool_subagent_fork
plugin_coding_tool_todo["tool-todo<br/>@deepseek-ai/dsh-tool-todo"]
cfg --> plugin_coding_tool_todo
plugin_coding_fs_local["fs-local<br/>@deepseek-ai/dsh-fs-local"]
cfg --> plugin_coding_fs_local
plugin_coding_fs_policy["fs-policy<br/>@deepseek-ai/dsh-fs-policy"]
cfg --> plugin_coding_fs_policy
plugin_coding_tool_fs["tool-fs<br/>@deepseek-ai/dsh-tool-fs"]
cfg --> plugin_coding_tool_fs
```
| Plugin id | Package / module |
| --- | --- |
| `hmr` | `@cordisjs/plugin-hmr` |
| `llm-deepseek` | `@deepseek-ai/dsh-llm-deepseek` |
| `bash` | `@deepseek-ai/dsh-bash-local` |
| `stdio-agent` | `@deepseek-ai/dsh-stdio-agent` |
| `compact-basic` | `@deepseek-ai/dsh-compact-basic` |
| `subagent` | `@deepseek-ai/dsh-subagent` |
| `subagent-spawn` | `@deepseek-ai/dsh-subagent-spawn` |
| `subagent-fork` | `@deepseek-ai/dsh-subagent-fork` |
| `tool-subagent` | `@deepseek-ai/dsh-tool-subagent` |
| `tool-subagent-fork` | `@deepseek-ai/dsh-tool-subagent` |
| `tool-todo` | `@deepseek-ai/dsh-tool-todo` |
| `fs-local` | `@deepseek-ai/dsh-fs-local` |
| `fs-policy` | `@deepseek-ai/dsh-fs-policy` |
| `tool-fs` | `@deepseek-ai/dsh-tool-fs` |
Source config: [`examples/coding-agent/cordis.yml`](../examples/coding-agent/cordis.yml).

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: 18b379170118efed6e4af07b7fdbe606fa3e3d82
development.zh.md: 7005ad24be386781113984d535723ff40dd74eb2
development.md: af8d13e08bdb9789016aa1ad8a1f10f9ecfb783e
development.zh.md: 3163c7a21e8cd0e7896983dc721d550de9f1f5a8

View File

@@ -98,8 +98,8 @@ pnpm run lint:fix # eslint . --fix
pnpm run doc-typecheck # compile checked TypeScript snippets in Markdown docs
pnpm run gen-cordis-catalog # regenerate docs/cordis-catalog/events-and-services.md from source
pnpm run verify-cordis-catalog # fail if the cordis events/services catalog is stale
pnpm run gen-doc-graphs # regenerate docs/graphs/*.md from source and curated graph definitions
pnpm run verify-doc-graphs # fail if docs/graphs/*.md is stale
pnpm run gen-doc-graphs # regenerate generated relationship docs from source and curated graph definitions
pnpm run verify-doc-graphs # fail if generated relationship docs are stale
pnpm run gen-rfc-index # regenerate the docs/rfc/README.md index tables from the RFC tree
pnpm run verify-md-wrap # fail on hard-wrapped prose paragraphs in docs/README markdown
pnpm run verify-mermaid # fail if a ```mermaid diagram has invalid Mermaid syntax

View File

@@ -98,8 +98,8 @@ pnpm run lint:fix # eslint . --fix
pnpm run doc-typecheck # compile checked TypeScript snippets in Markdown docs
pnpm run gen-cordis-catalog # regenerate docs/cordis-catalog/events-and-services.md from source
pnpm run verify-cordis-catalog # fail if the cordis events/services catalog is stale
pnpm run gen-doc-graphs # regenerate docs/graphs/*.md from source and curated graph definitions
pnpm run verify-doc-graphs # fail if docs/graphs/*.md is stale
pnpm run gen-doc-graphs # regenerate generated relationship docs from source and curated graph definitions
pnpm run verify-doc-graphs # fail if generated relationship docs are stale
pnpm run gen-rfc-index # regenerate the docs/rfc/README.md index tables from the RFC tree
pnpm run verify-md-wrap # fail on hard-wrapped prose paragraphs in docs/README markdown
pnpm run verify-mermaid # fail if a ```mermaid diagram has invalid Mermaid syntax

View File

@@ -0,0 +1,40 @@
<!-- Generated by scripts/gen-doc-graphs.ts - do not edit by hand.
Run `pnpm run gen-doc-graphs` to regenerate. -->
# Echo Agent App Composition
Maintenance mode: hybrid: the leaf plugin list is parsed from its `cordis.yml`; app package expansion is curated from package source.
The echo demo swaps in a local mock LLM and teaching echo tool, then loads the stdio app package for the shared spine and terminal front door.
```mermaid
flowchart LR
cfg["examples/echo-agent<br/>cordis.yml"]
plugin_echo_hmr["hmr<br/>@cordisjs/plugin-hmr"]
cfg --> plugin_echo_hmr
plugin_echo_mock_llm["mock-llm<br/>./src/mock-llm.ts"]
cfg --> plugin_echo_mock_llm
plugin_echo_echo_tool["echo-tool<br/>./src/echo-tool.ts"]
cfg --> plugin_echo_echo_tool
plugin_echo_bash["bash<br/>@deepseek-ai/dsh-bash-local"]
cfg --> plugin_echo_bash
plugin_echo_stdio_agent["stdio-agent<br/>@deepseek-ai/dsh-stdio-agent"]
cfg --> plugin_echo_stdio_agent
plugin_echo_stdio_agent --> bundle_agent_core["@deepseek-ai/dsh-agent-core"]
plugin_echo_stdio_agent --> bundle_jsonl["@deepseek-ai/dsh-session-persistence-jsonl"]
plugin_echo_stdio_agent --> frontdoor_stdio["readline UI<br/>console logger<br/>pre-created main agent"]
bundle_agent_core --> spine_llm["ctx.llm"]
bundle_agent_core --> spine_sessions["ctx.sessions"]
bundle_agent_core --> spine_tools["ctx.tools + tool-bash"]
bundle_agent_core --> spine_loop["ctx.agents + ctx.agentLoop"]
```
| Plugin id | Package / module |
| --- | --- |
| `hmr` | `@cordisjs/plugin-hmr` |
| `mock-llm` | `./src/mock-llm.ts` |
| `echo-tool` | `./src/echo-tool.ts` |
| `bash` | `@deepseek-ai/dsh-bash-local` |
| `stdio-agent` | `@deepseek-ai/dsh-stdio-agent` |
Source config: [`examples/echo-agent/cordis.yml`](../examples/echo-agent/cordis.yml).

View File

@@ -0,0 +1,38 @@
<!-- Generated by scripts/gen-doc-graphs.ts - do not edit by hand.
Run `pnpm run gen-doc-graphs` to regenerate. -->
# Event Producer And Consumer Matrix
Maintenance mode: hybrid generated: Cordis event declarations and most producer/listener edges are AST-scanned; dynamic dispatch sites are classified in `scripts/gen-doc-graphs.ts`.
This matrix shows which packages dispatch each harness-owned event and which packages listen to it. It is intentionally a table rather than one large graph: events are many-to-many, and dense relation data is easier to review in rows. Dynamic dispatch overrides cover sites that deliberately bypass `ctx.emit`, such as subagent lifecycle containment.
| Event | Mode | Declared in | Dispatchers | Listeners |
| --- | --- | --- | --- | --- |
| `agent/created` | `emit` | [`packages/core/agent/src/types.ts:234`](../packages/core/agent/src/types.ts) | [`agent`](../packages/core/agent) (`emit`) | [`stdio-agent`](../packages/ui/stdio-agent) |
| `agent/disposed` | `emit` | [`packages/core/agent/src/types.ts:241`](../packages/core/agent/src/types.ts) | [`agent`](../packages/core/agent) (`emit`) | [`stdio-agent`](../packages/ui/stdio-agent) |
| `agent/error` | `emit` | [`packages/core/agent/src/types.ts:389`](../packages/core/agent/src/types.ts) | [`agent-loop`](../packages/core/agent-loop) (`emit`) | - |
| `agent/pre-step` | `serial` | [`packages/core/agent/src/types.ts:319`](../packages/core/agent/src/types.ts) | [`agent-loop`](../packages/core/agent-loop) (`serial`) | [`compact-basic`](../packages/compact/compact-basic) |
| `agent/prompt-submit` | `waterfall` | [`packages/core/agent/src/types.ts:332`](../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) |
| `agent/queued` | `emit` | [`packages/core/agent/src/types.ts:259`](../packages/core/agent/src/types.ts) | [`agent-loop`](../packages/core/agent-loop) (`emit`) | - |
| `agent/request` | `waterfall` | [`packages/core/agent/src/types.ts:345`](../packages/core/agent/src/types.ts) | [`agent-loop`](../packages/core/agent-loop) (`waterfall`), [`compact-basic`](../packages/compact/compact-basic) (`waterfall`) | - |
| `agent/session-start` | `emit` | [`packages/core/agent/src/types.ts:274`](../packages/core/agent/src/types.ts) | [`agent-loop`](../packages/core/agent-loop) (`emit`) | [`hooks-claude`](../packages/hooks/hooks-claude), [`hooks-codex`](../packages/hooks/hooks-codex) |
| `agent/status` | `emit` | [`packages/core/agent/src/types.ts:250`](../packages/core/agent/src/types.ts) | [`agent-loop`](../packages/core/agent-loop) (`emit`) | [`acp`](../packages/ui/acp), [`invariants`](../packages/support/invariants), [`stdio-agent`](../packages/ui/stdio-agent) |
| `agent/steering` | `emit` | [`packages/core/agent/src/types.ts:379`](../packages/core/agent/src/types.ts) | [`agent-loop`](../packages/core/agent-loop) (`emit`) | - |
| `agent/step-result` | `waterfall` | [`packages/core/agent/src/types.ts:355`](../packages/core/agent/src/types.ts) | [`agent-loop`](../packages/core/agent-loop) (`waterfall`) | - |
| `agent/turn-continuation` | `waterfall` | [`packages/core/agent/src/types.ts:368`](../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) |
| `fs/edit-intent` | `waterfall` | [`packages/fs/fs/src/index.ts:123`](../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:138`](../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:109`](../packages/fs/fs/src/index.ts) | [`tool-fs`](../packages/fs/tool-fs) (`waterfall`) | [`fs-policy`](../packages/fs/fs-policy) |
| `llm/stream` | `waterfall` | [`packages/llm/llm/src/index.ts:32`](../packages/llm/llm/src/index.ts) | [`llm`](../packages/llm/llm) (`waterfall`) | [`llm-replay`](../packages/support/llm-replay) |
| `session/created` | `emit` | [`packages/core/session/src/index.ts:36`](../packages/core/session/src/index.ts) | [`session`](../packages/core/session) (`emit`) | [`invariants`](../packages/support/invariants), [`session-persistence`](../packages/session-persistence/session-persistence) |
| `session/event` | `emit` | [`packages/core/session/src/index.ts:44`](../packages/core/session/src/index.ts) | [`session`](../packages/core/session) (`emit`) | [`acp`](../packages/ui/acp), [`invariants`](../packages/support/invariants), [`session-persistence`](../packages/session-persistence/session-persistence), [`stdio-agent`](../packages/ui/stdio-agent) |
| `session/flush` | `parallel` | [`packages/core/session/src/index.ts:54`](../packages/core/session/src/index.ts) | [`agent-loop`](../packages/core/agent-loop) (`parallel`) | [`session-persistence`](../packages/session-persistence/session-persistence) |
| `subagent/end` | `emit` | [`packages/subagent/subagent/src/index.ts:77`](../packages/subagent/subagent/src/index.ts) | [`subagent`](../packages/subagent/subagent) (`events.dispatch`) | [`hooks-claude`](../packages/hooks/hooks-claude) |
| `subagent/start` | `emit` | [`packages/subagent/subagent/src/index.ts:70`](../packages/subagent/subagent/src/index.ts) | [`subagent`](../packages/subagent/subagent) (`events.dispatch`) | [`hooks-claude`](../packages/hooks/hooks-claude) |
| `system-prompt/assemble` | `waterfall` | [`packages/core/system-prompt/src/index.ts:26`](../packages/core/system-prompt/src/index.ts) | [`system-prompt`](../packages/core/system-prompt) (`waterfall`) | - |
| `system-prompt/change` | `emit` | [`packages/core/system-prompt/src/index.ts:32`](../packages/core/system-prompt/src/index.ts) | [`system-prompt`](../packages/core/system-prompt) (`emit`) | - |
| `tools/change` | `emit` | [`packages/core/tools/src/index.ts:87`](../packages/core/tools/src/index.ts) | [`tools`](../packages/core/tools) (`emit`) | - |
| `tools/post-execute` | `waterfall` | [`packages/core/tools/src/index.ts:82`](../packages/core/tools/src/index.ts) | [`tools`](../packages/core/tools) (`waterfall`) | [`hooks-claude`](../packages/hooks/hooks-claude), [`hooks-codex`](../packages/hooks/hooks-codex) |
| `tools/pre-execute` | `waterfall` | [`packages/core/tools/src/index.ts:66`](../packages/core/tools/src/index.ts) | [`tools`](../packages/core/tools) (`waterfall`) | [`hooks-claude`](../packages/hooks/hooks-claude), [`hooks-codex`](../packages/hooks/hooks-codex) |
| `web/providers-change` | `emit` | [`packages/web/web/src/index.ts:65`](../packages/web/web/src/index.ts) | [`web`](../packages/web/web) (`emit`) | - |

25
docs/graph-atlas.md Normal file
View File

@@ -0,0 +1,25 @@
<!-- Generated by scripts/gen-doc-graphs.ts - do not edit by hand.
Run `pnpm run gen-doc-graphs` to regenerate. -->
# Documentation Graph Index
Maintenance mode: mixed: each linked page declares generated, hybrid, or curated mode.
These diagrams are the relationship layer above the generated catalogs. Use them to navigate package topology, capability seams, event flow, model-facing tools, app composition, and runtime lifecycle paths. Exact signatures and type shapes still live in [cordis-catalog/](cordis-catalog/events-and-services.md), [tool-catalog/](tool-catalog/tools.md), and [core-data-structures/](core-data-structures/core.md).
The process decision behind this index is recorded in [the documentation graph RFC](rfc/implemented/process/2026-07-03-documentation-graph-atlas.md).
| Graph | Mode |
| --- | --- |
| [module dependency graph](module-graph.md) | `generated` |
| [tool schema catalog and package map](tool-catalog/tools.md) | `generated` |
| [capability seams and core services](capability-seams.md) | `hybrid generated` |
| [echo-agent app composition](echo-agent-composition.md) | `hybrid generated` |
| [coding-agent app composition](coding-agent-composition.md) | `hybrid generated` |
| [acp-agent app composition](acp-agent-composition.md) | `hybrid generated` |
| [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](acp/snapshot-replay.md) | `curated` |
Regenerate with `pnpm run gen-doc-graphs`; verify freshness with `pnpm run verify-doc-graphs`.

View File

@@ -1,26 +0,0 @@
<!-- Generated by scripts/gen-doc-graphs.ts - do not edit by hand.
Run `pnpm run gen-doc-graphs` to regenerate. -->
# Documentation Graph Atlas
Maintenance mode: mixed: each linked page declares generated, hybrid, or curated mode.
The graph atlas is the relationship layer above the generated catalogs. Use it to navigate package topology, capability seams, event flow, model-facing tools, and runtime lifecycle paths. Exact signatures and type shapes still live in [cordis-catalog/](../cordis-catalog/events-and-services.md), [tool-catalog/](../tool-catalog/tools.md), and [core-data-structures/](../core-data-structures/core.md).
The process decision behind this atlas is recorded in [the documentation graph atlas RFC](../rfc/implemented/process/2026-07-03-documentation-graph-atlas.md).
| Graph | Mode |
| --- | --- |
| [package topology by group](package-topology.md) | `generated` |
| [capability seams and core services](capability-seams.md) | `hybrid generated` |
| [app composition](app-composition.md) | `hybrid generated` |
| [event producer/consumer matrix](event-producer-consumer.md) | `hybrid generated` |
| [tool affordance map](tool-affordance-map.md) | `hybrid generated` |
| [agent turn and step lifecycle](agent-lifecycle.md) | `curated` |
| [tool execution pipeline](tool-execution-pipeline.md) | `curated` |
| [session surface and message projection](session-surface.md) | `curated` |
| [subagent and session lineage](subagent-lineage.md) | `curated` |
| [plugin disposal and hot reload ownership](hot-reload-disposal.md) | `curated` |
| [ACP snapshot replay](snapshot-replay.md) | `curated` |
Regenerate with `pnpm run gen-doc-graphs`; verify freshness with `pnpm run verify-doc-graphs`.

View File

@@ -1,49 +0,0 @@
<!-- Generated by scripts/gen-doc-graphs.ts - do not edit by hand.
Run `pnpm run gen-doc-graphs` to regenerate. -->
# Agent Turn And Step Lifecycle
Maintenance mode: curated Mermaid sequence; exact event signatures live in the generated Cordis catalog.
This sequence is the visual companion to [architecture.md](../architecture.md#loop-lifecycle-session--turn--step). It keeps durable replay facts on `session/event` and live control/status on `agent/*`.
```mermaid
sequenceDiagram
participant User
participant Agent
participant Driver
participant Hooks as hook listeners
participant Prompt as ctx.systemPrompt
participant LLM as ctx.llm
participant Tools as ctx.tools
participant Session
participant Persistence
participant SDK as UI or SDK listener
User->>Agent: send(content)
Agent-->>SDK: agent/queued
Agent->>Driver: queued work wakes driver
Driver-->>SDK: agent/status running
Driver->>Session: turn/start
Driver->>Hooks: agent/prompt-submit waterfall
Hooks-->>Driver: allow, block, or add context
Driver->>Session: user/message or rejected turn/end
Driver->>Prompt: system-prompt/assemble waterfall
Driver-->>Driver: agent/pre-step serial checkpoint
Driver->>Session: step/start
Driver->>LLM: agent/request waterfall, then llm/stream waterfall
LLM-->>Driver: StreamChunk*
Driver->>Session: assistant/chunk*
Session-->>SDK: session/event assistant/chunk*
Driver->>Hooks: agent/step-result waterfall
Driver->>Session: assistant/message
Driver->>Session: tool/call
Driver->>Tools: execute through pre and post waterfalls
Tools-->>Session: tool-owned events when applicable
Driver->>Session: tool/result and step/end
Driver->>Hooks: agent/turn-continuation waterfall
Driver->>Session: turn/end
Driver->>Persistence: session/flush parallel checkpoint
Driver-->>SDK: agent/status idle
```
SDK users that need replayable transcript data should consume `session/event`; `agent/*` is the live coordination surface for queue/status, prompt interception, request shaping, steering, continuation, and errors.

View File

@@ -1,106 +0,0 @@
<!-- Generated by scripts/gen-doc-graphs.ts - do not edit by hand.
Run `pnpm run gen-doc-graphs` to regenerate. -->
# App Composition
Maintenance mode: hybrid: leaf plugin lists are parsed from `examples/*/cordis.yml`; bundle expansions are curated from app package source.
This graph is for SDK users asking which pieces a runnable agent loads. Leaf configs choose adapters and optional product tools; app packages provide the front door; `dsh-agent-core` bundles the providerless spine.
```mermaid
flowchart LR
subgraph example_echo["examples/echo-agent"]
cfg_echo["cordis.yml"]
plugin_echo_hmr["hmr<br/>@cordisjs/plugin-hmr"]
cfg_echo --> plugin_echo_hmr
plugin_echo_mock_llm["mock-llm<br/>./src/mock-llm.ts"]
cfg_echo --> plugin_echo_mock_llm
plugin_echo_echo_tool["echo-tool<br/>./src/echo-tool.ts"]
cfg_echo --> plugin_echo_echo_tool
plugin_echo_bash["bash<br/>@deepseek-ai/dsh-bash-local"]
cfg_echo --> plugin_echo_bash
plugin_echo_stdio_agent["stdio-agent<br/>@deepseek-ai/dsh-stdio-agent"]
cfg_echo --> plugin_echo_stdio_agent
plugin_echo_stdio_agent --> bundle_stdio
end
subgraph example_coding["examples/coding-agent"]
cfg_coding["cordis.yml"]
plugin_coding_hmr["hmr<br/>@cordisjs/plugin-hmr"]
cfg_coding --> plugin_coding_hmr
plugin_coding_llm_deepseek["llm-deepseek<br/>@deepseek-ai/dsh-llm-deepseek"]
cfg_coding --> plugin_coding_llm_deepseek
plugin_coding_bash["bash<br/>@deepseek-ai/dsh-bash-local"]
cfg_coding --> plugin_coding_bash
plugin_coding_stdio_agent["stdio-agent<br/>@deepseek-ai/dsh-stdio-agent"]
cfg_coding --> plugin_coding_stdio_agent
plugin_coding_stdio_agent --> bundle_stdio
plugin_coding_compact_basic["compact-basic<br/>@deepseek-ai/dsh-compact-basic"]
cfg_coding --> plugin_coding_compact_basic
plugin_coding_subagent["subagent<br/>@deepseek-ai/dsh-subagent"]
cfg_coding --> plugin_coding_subagent
plugin_coding_subagent_spawn["subagent-spawn<br/>@deepseek-ai/dsh-subagent-spawn"]
cfg_coding --> plugin_coding_subagent_spawn
plugin_coding_subagent_fork["subagent-fork<br/>@deepseek-ai/dsh-subagent-fork"]
cfg_coding --> plugin_coding_subagent_fork
plugin_coding_tool_subagent["tool-subagent<br/>@deepseek-ai/dsh-tool-subagent"]
cfg_coding --> plugin_coding_tool_subagent
plugin_coding_tool_subagent_fork["tool-subagent-fork<br/>@deepseek-ai/dsh-tool-subagent"]
cfg_coding --> plugin_coding_tool_subagent_fork
plugin_coding_tool_todo["tool-todo<br/>@deepseek-ai/dsh-tool-todo"]
cfg_coding --> plugin_coding_tool_todo
plugin_coding_fs_local["fs-local<br/>@deepseek-ai/dsh-fs-local"]
cfg_coding --> plugin_coding_fs_local
plugin_coding_fs_policy["fs-policy<br/>@deepseek-ai/dsh-fs-policy"]
cfg_coding --> plugin_coding_fs_policy
plugin_coding_tool_fs["tool-fs<br/>@deepseek-ai/dsh-tool-fs"]
cfg_coding --> plugin_coding_tool_fs
end
subgraph example_acp["examples/acp-agent"]
cfg_acp["cordis.yml"]
plugin_acp_llm_deepseek["llm-deepseek<br/>@deepseek-ai/dsh-llm-deepseek"]
cfg_acp --> plugin_acp_llm_deepseek
plugin_acp_bash["bash<br/>@deepseek-ai/dsh-bash-local"]
cfg_acp --> plugin_acp_bash
plugin_acp_acp_agent["acp-agent<br/>@deepseek-ai/dsh-acp-agent"]
cfg_acp --> plugin_acp_acp_agent
plugin_acp_acp_agent --> bundle_acp_agent
plugin_acp_subagent["subagent<br/>@deepseek-ai/dsh-subagent"]
cfg_acp --> plugin_acp_subagent
plugin_acp_subagent_spawn["subagent-spawn<br/>@deepseek-ai/dsh-subagent-spawn"]
cfg_acp --> plugin_acp_subagent_spawn
plugin_acp_subagent_fork["subagent-fork<br/>@deepseek-ai/dsh-subagent-fork"]
cfg_acp --> plugin_acp_subagent_fork
plugin_acp_tool_subagent["tool-subagent<br/>@deepseek-ai/dsh-tool-subagent"]
cfg_acp --> plugin_acp_tool_subagent
plugin_acp_tool_subagent_fork["tool-subagent-fork<br/>@deepseek-ai/dsh-tool-subagent"]
cfg_acp --> plugin_acp_tool_subagent_fork
plugin_acp_tool_todo["tool-todo<br/>@deepseek-ai/dsh-tool-todo"]
cfg_acp --> plugin_acp_tool_todo
plugin_acp_fs_local["fs-local<br/>@deepseek-ai/dsh-fs-local"]
cfg_acp --> plugin_acp_fs_local
plugin_acp_fs_policy["fs-policy<br/>@deepseek-ai/dsh-fs-policy"]
cfg_acp --> plugin_acp_fs_policy
plugin_acp_tool_fs["tool-fs<br/>@deepseek-ai/dsh-tool-fs"]
cfg_acp --> plugin_acp_tool_fs
plugin_acp_hooks_claude["hooks-claude<br/>@deepseek-ai/dsh-hooks-claude"]
cfg_acp --> plugin_acp_hooks_claude
plugin_acp_hooks_codex["hooks-codex<br/>@deepseek-ai/dsh-hooks-codex"]
cfg_acp --> plugin_acp_hooks_codex
end
bundle_stdio["@deepseek-ai/dsh-stdio-agent"] --> bundle_agent_core["@deepseek-ai/dsh-agent-core"]
bundle_stdio --> bundle_jsonl["@deepseek-ai/dsh-session-persistence-jsonl"]
bundle_stdio --> bundle_ui_stdio["@deepseek-ai/dsh-ui-stdio"]
bundle_acp_agent["@deepseek-ai/dsh-acp-agent"] --> bundle_agent_core
bundle_acp_agent --> bundle_jsonl
bundle_acp_agent --> bundle_acp["@deepseek-ai/dsh-acp"]
bundle_agent_core --> spine_llm["ctx.llm"]
bundle_agent_core --> spine_sessions["ctx.sessions"]
bundle_agent_core --> spine_tools["ctx.tools + tool-bash"]
bundle_agent_core --> spine_loop["ctx.agents + ctx.agentLoop"]
```
| Example | Parsed plugin ids | Config |
| --- | --- | --- |
| `examples/echo-agent` | `hmr`, `mock-llm`, `echo-tool`, `bash`, `stdio-agent` | [`examples/echo-agent/cordis.yml`](../../examples/echo-agent/cordis.yml) |
| `examples/coding-agent` | `hmr`, `llm-deepseek`, `bash`, `stdio-agent`, `compact-basic`, `subagent`, `subagent-spawn`, `subagent-fork`, `tool-subagent`, `tool-subagent-fork`, `tool-todo`, `fs-local`, `fs-policy`, `tool-fs` | [`examples/coding-agent/cordis.yml`](../../examples/coding-agent/cordis.yml) |
| `examples/acp-agent` | `llm-deepseek`, `bash`, `acp-agent`, `subagent`, `subagent-spawn`, `subagent-fork`, `tool-subagent`, `tool-subagent-fork`, `tool-todo`, `fs-local`, `fs-policy`, `tool-fs`, `hooks-claude`, `hooks-codex` | [`examples/acp-agent/cordis.yml`](../../examples/acp-agent/cordis.yml) |

View File

@@ -1,38 +0,0 @@
<!-- Generated by scripts/gen-doc-graphs.ts - do not edit by hand.
Run `pnpm run gen-doc-graphs` to regenerate. -->
# Event Producer And Consumer Matrix
Maintenance mode: hybrid generated: Cordis event declarations and most producer/listener edges are AST-scanned; dynamic dispatch sites are classified in `scripts/gen-doc-graphs.ts`.
This matrix shows which packages dispatch each harness-owned event and which packages listen to it. It is intentionally a table rather than one large graph: events are many-to-many, and dense relation data is easier to review in rows. Dynamic dispatch overrides cover sites that deliberately bypass `ctx.emit`, such as subagent lifecycle containment.
| Event | Mode | Declared in | Dispatchers | Listeners |
| --- | --- | --- | --- | --- |
| `agent/created` | `emit` | [`packages/core/agent/src/types.ts:234`](../../packages/core/agent/src/types.ts) | [`agent`](../../packages/core/agent) (`emit`) | [`stdio-agent`](../../packages/ui/stdio-agent) |
| `agent/disposed` | `emit` | [`packages/core/agent/src/types.ts:241`](../../packages/core/agent/src/types.ts) | [`agent`](../../packages/core/agent) (`emit`) | [`stdio-agent`](../../packages/ui/stdio-agent) |
| `agent/error` | `emit` | [`packages/core/agent/src/types.ts:389`](../../packages/core/agent/src/types.ts) | [`agent-loop`](../../packages/core/agent-loop) (`emit`) | - |
| `agent/pre-step` | `serial` | [`packages/core/agent/src/types.ts:319`](../../packages/core/agent/src/types.ts) | [`agent-loop`](../../packages/core/agent-loop) (`serial`) | [`compact-basic`](../../packages/compact/compact-basic) |
| `agent/prompt-submit` | `waterfall` | [`packages/core/agent/src/types.ts:332`](../../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) |
| `agent/queued` | `emit` | [`packages/core/agent/src/types.ts:259`](../../packages/core/agent/src/types.ts) | [`agent-loop`](../../packages/core/agent-loop) (`emit`) | - |
| `agent/request` | `waterfall` | [`packages/core/agent/src/types.ts:345`](../../packages/core/agent/src/types.ts) | [`agent-loop`](../../packages/core/agent-loop) (`waterfall`), [`compact-basic`](../../packages/compact/compact-basic) (`waterfall`) | - |
| `agent/session-start` | `emit` | [`packages/core/agent/src/types.ts:274`](../../packages/core/agent/src/types.ts) | [`agent-loop`](../../packages/core/agent-loop) (`emit`) | [`hooks-claude`](../../packages/hooks/hooks-claude), [`hooks-codex`](../../packages/hooks/hooks-codex) |
| `agent/status` | `emit` | [`packages/core/agent/src/types.ts:250`](../../packages/core/agent/src/types.ts) | [`agent-loop`](../../packages/core/agent-loop) (`emit`) | [`acp`](../../packages/ui/acp), [`invariants`](../../packages/support/invariants), [`stdio-agent`](../../packages/ui/stdio-agent) |
| `agent/steering` | `emit` | [`packages/core/agent/src/types.ts:379`](../../packages/core/agent/src/types.ts) | [`agent-loop`](../../packages/core/agent-loop) (`emit`) | - |
| `agent/step-result` | `waterfall` | [`packages/core/agent/src/types.ts:355`](../../packages/core/agent/src/types.ts) | [`agent-loop`](../../packages/core/agent-loop) (`waterfall`) | - |
| `agent/turn-continuation` | `waterfall` | [`packages/core/agent/src/types.ts:368`](../../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) |
| `fs/edit-intent` | `waterfall` | [`packages/fs/fs/src/index.ts:123`](../../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:138`](../../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:109`](../../packages/fs/fs/src/index.ts) | [`tool-fs`](../../packages/fs/tool-fs) (`waterfall`) | [`fs-policy`](../../packages/fs/fs-policy) |
| `llm/stream` | `waterfall` | [`packages/llm/llm/src/index.ts:32`](../../packages/llm/llm/src/index.ts) | [`llm`](../../packages/llm/llm) (`waterfall`) | [`llm-replay`](../../packages/support/llm-replay) |
| `session/created` | `emit` | [`packages/core/session/src/index.ts:36`](../../packages/core/session/src/index.ts) | [`session`](../../packages/core/session) (`emit`) | [`invariants`](../../packages/support/invariants), [`session-persistence`](../../packages/session-persistence/session-persistence) |
| `session/event` | `emit` | [`packages/core/session/src/index.ts:44`](../../packages/core/session/src/index.ts) | [`session`](../../packages/core/session) (`emit`) | [`acp`](../../packages/ui/acp), [`invariants`](../../packages/support/invariants), [`session-persistence`](../../packages/session-persistence/session-persistence), [`stdio-agent`](../../packages/ui/stdio-agent) |
| `session/flush` | `parallel` | [`packages/core/session/src/index.ts:54`](../../packages/core/session/src/index.ts) | [`agent-loop`](../../packages/core/agent-loop) (`parallel`) | [`session-persistence`](../../packages/session-persistence/session-persistence) |
| `subagent/end` | `emit` | [`packages/subagent/subagent/src/index.ts:77`](../../packages/subagent/subagent/src/index.ts) | [`subagent`](../../packages/subagent/subagent) (`events.dispatch`) | [`hooks-claude`](../../packages/hooks/hooks-claude) |
| `subagent/start` | `emit` | [`packages/subagent/subagent/src/index.ts:70`](../../packages/subagent/subagent/src/index.ts) | [`subagent`](../../packages/subagent/subagent) (`events.dispatch`) | [`hooks-claude`](../../packages/hooks/hooks-claude) |
| `system-prompt/assemble` | `waterfall` | [`packages/core/system-prompt/src/index.ts:26`](../../packages/core/system-prompt/src/index.ts) | [`system-prompt`](../../packages/core/system-prompt) (`waterfall`) | - |
| `system-prompt/change` | `emit` | [`packages/core/system-prompt/src/index.ts:32`](../../packages/core/system-prompt/src/index.ts) | [`system-prompt`](../../packages/core/system-prompt) (`emit`) | - |
| `tools/change` | `emit` | [`packages/core/tools/src/index.ts:87`](../../packages/core/tools/src/index.ts) | [`tools`](../../packages/core/tools) (`emit`) | - |
| `tools/post-execute` | `waterfall` | [`packages/core/tools/src/index.ts:82`](../../packages/core/tools/src/index.ts) | [`tools`](../../packages/core/tools) (`waterfall`) | [`hooks-claude`](../../packages/hooks/hooks-claude), [`hooks-codex`](../../packages/hooks/hooks-codex) |
| `tools/pre-execute` | `waterfall` | [`packages/core/tools/src/index.ts:66`](../../packages/core/tools/src/index.ts) | [`tools`](../../packages/core/tools) (`waterfall`) | [`hooks-claude`](../../packages/hooks/hooks-claude), [`hooks-codex`](../../packages/hooks/hooks-codex) |
| `web/providers-change` | `emit` | [`packages/web/web/src/index.ts:65`](../../packages/web/web/src/index.ts) | [`web`](../../packages/web/web) (`emit`) | - |

View File

@@ -1,24 +0,0 @@
<!-- Generated by scripts/gen-doc-graphs.ts - do not edit by hand.
Run `pnpm run gen-doc-graphs` to regenerate. -->
# Plugin Disposal And Hot Reload Ownership
Maintenance mode: curated Mermaid flow based on Cordis fiber/effect conventions.
This graph is a maintainer checklist for plugin authors: registrations are effects, service injection gates activation, and owned handles must be disposed by their owner.
```mermaid
flowchart TD
plugin["ctx.plugin(plugin) creates fiber"]
inject["static inject gates activation"]
service["ctx.provide / Service constructor"]
effects["ctx.effect registrations<br/>events, tools, adapters, timers"]
reload["HMR / fiber.dispose()"]
disposers["Run disposers in owner fiber"]
quiescence["Owned AgentHandle.dispose()<br/>or service teardown awaits quiescence"]
plugin --> inject --> service
inject --> effects
reload --> disposers --> quiescence
```
Hook bridges and SDK plugins increase the number of long-lived listeners, so this ownership graph should stay small and visible.

View File

@@ -1,247 +0,0 @@
<!-- Generated by scripts/gen-doc-graphs.ts - do not edit by hand.
Run `pnpm run gen-doc-graphs` to regenerate. -->
# Package Topology By Group
Maintenance mode: generated from `packages/*/*/package.json` peer dependencies plus package group paths.
This graph complements [module-graph.md](../module-graph.md): it keeps the same canonical peer-dependency edge source, but clusters packages by the `packages/<group>/<pkg>` hierarchy so layering and capability families are easier to scan.
```mermaid
flowchart TD
subgraph group_util["packages/util"]
pkg_brand["brand"]
end
subgraph group_llm["packages/llm"]
pkg_llm["llm"]
pkg_llm_deepseek["llm-deepseek"]
pkg_llm_pi_ai["llm-pi-ai"]
end
subgraph group_core["packages/core"]
pkg_agent["agent"]
pkg_agent_core["agent-core"]
pkg_agent_loop["agent-loop"]
pkg_session["session"]
pkg_system_prompt["system-prompt"]
pkg_tools["tools"]
end
subgraph group_bash["packages/bash"]
pkg_bash["bash"]
pkg_bash_local["bash-local"]
pkg_tool_bash["tool-bash"]
end
subgraph group_fs["packages/fs"]
pkg_fs["fs"]
pkg_fs_local["fs-local"]
pkg_fs_policy["fs-policy"]
pkg_tool_fs["tool-fs"]
end
subgraph group_compact["packages/compact"]
pkg_compact["compact"]
pkg_compact_basic["compact-basic"]
end
subgraph group_subagent["packages/subagent"]
pkg_subagent["subagent"]
pkg_subagent_acp["subagent-acp"]
pkg_subagent_fork["subagent-fork"]
pkg_subagent_inprocess["subagent-inprocess"]
pkg_subagent_spawn["subagent-spawn"]
pkg_tool_subagent["tool-subagent"]
end
subgraph group_web["packages/web"]
pkg_tool_web["tool-web"]
pkg_web["web"]
pkg_web_fetch_local["web-fetch-local"]
pkg_web_search_deepseek["web-search-deepseek"]
pkg_web_search_exa["web-search-exa"]
pkg_web_search_perplexity["web-search-perplexity"]
end
subgraph group_todo["packages/todo"]
pkg_tool_todo["tool-todo"]
end
subgraph group_hooks["packages/hooks"]
pkg_hook_protocol["hook-protocol"]
pkg_hooks_claude["hooks-claude"]
pkg_hooks_codex["hooks-codex"]
end
subgraph group_session_persistence["packages/session-persistence"]
pkg_session_persistence["session-persistence"]
pkg_session_persistence_jsonl["session-persistence-jsonl"]
pkg_session_persistence_sqlite["session-persistence-sqlite"]
end
subgraph group_support["packages/support"]
pkg_invariants["invariants"]
pkg_llm_replay["llm-replay"]
pkg_subagent_mock["subagent-mock"]
end
subgraph group_ui["packages/ui"]
pkg_acp["acp"]
pkg_acp_agent["acp-agent"]
pkg_stdio_agent["stdio-agent"]
end
pkg_llm --> pkg_brand
pkg_bash --> pkg_brand
pkg_llm_deepseek --> pkg_llm
pkg_llm_pi_ai --> pkg_llm
pkg_session --> pkg_brand
pkg_session --> pkg_llm
pkg_system_prompt --> pkg_llm
pkg_bash_local --> pkg_bash
pkg_fs --> pkg_brand
pkg_fs --> pkg_llm
pkg_web --> pkg_llm
pkg_agent --> pkg_brand
pkg_agent --> pkg_llm
pkg_agent --> pkg_session
pkg_fs_local --> pkg_fs
pkg_fs_policy --> pkg_fs
pkg_compact --> pkg_llm
pkg_compact --> pkg_session
pkg_web_fetch_local --> pkg_web
pkg_web_search_deepseek --> pkg_web
pkg_web_search_exa --> pkg_web
pkg_web_search_perplexity --> pkg_web
pkg_hook_protocol --> pkg_bash
pkg_hook_protocol --> pkg_session
pkg_session_persistence --> pkg_session
pkg_llm_replay --> pkg_llm
pkg_llm_replay --> pkg_session
pkg_tools --> pkg_agent
pkg_tools --> pkg_llm
pkg_tools --> pkg_system_prompt
pkg_compact_basic --> pkg_agent
pkg_compact_basic --> pkg_compact
pkg_compact_basic --> pkg_llm
pkg_compact_basic --> pkg_session
pkg_session_persistence_jsonl --> pkg_session
pkg_session_persistence_jsonl --> pkg_session_persistence
pkg_session_persistence_sqlite --> pkg_session
pkg_session_persistence_sqlite --> pkg_session_persistence
pkg_invariants --> pkg_agent
pkg_invariants --> pkg_llm
pkg_invariants --> pkg_session
pkg_agent_loop --> pkg_agent
pkg_agent_loop --> pkg_llm
pkg_agent_loop --> pkg_session
pkg_agent_loop --> pkg_session_persistence
pkg_agent_loop --> pkg_system_prompt
pkg_agent_loop --> pkg_tools
pkg_tool_bash --> pkg_agent
pkg_tool_bash --> pkg_bash
pkg_tool_bash --> pkg_llm
pkg_tool_bash --> pkg_tools
pkg_tool_fs --> pkg_fs
pkg_tool_fs --> pkg_llm
pkg_tool_fs --> pkg_session
pkg_tool_fs --> pkg_system_prompt
pkg_tool_fs --> pkg_tools
pkg_subagent --> pkg_agent
pkg_subagent --> pkg_llm
pkg_subagent --> pkg_tools
pkg_tool_web --> pkg_llm
pkg_tool_web --> pkg_system_prompt
pkg_tool_web --> pkg_tools
pkg_tool_web --> pkg_web
pkg_tool_todo --> pkg_agent
pkg_tool_todo --> pkg_session
pkg_tool_todo --> pkg_tools
pkg_hooks_codex --> pkg_agent
pkg_hooks_codex --> pkg_hook_protocol
pkg_hooks_codex --> pkg_llm
pkg_hooks_codex --> pkg_session
pkg_hooks_codex --> pkg_tools
pkg_acp --> pkg_agent
pkg_acp --> pkg_llm
pkg_acp --> pkg_session
pkg_acp --> pkg_session_persistence
pkg_acp --> pkg_tools
pkg_agent_core --> pkg_agent
pkg_agent_core --> pkg_agent_loop
pkg_agent_core --> pkg_invariants
pkg_agent_core --> pkg_llm
pkg_agent_core --> pkg_session
pkg_agent_core --> pkg_system_prompt
pkg_agent_core --> pkg_tool_bash
pkg_agent_core --> pkg_tools
pkg_subagent_acp --> pkg_agent
pkg_subagent_acp --> pkg_llm
pkg_subagent_acp --> pkg_subagent
pkg_subagent_inprocess --> pkg_agent
pkg_subagent_inprocess --> pkg_llm
pkg_subagent_inprocess --> pkg_session
pkg_subagent_inprocess --> pkg_subagent
pkg_tool_subagent --> pkg_agent
pkg_tool_subagent --> pkg_llm
pkg_tool_subagent --> pkg_subagent
pkg_tool_subagent --> pkg_tools
pkg_hooks_claude --> pkg_agent
pkg_hooks_claude --> pkg_hook_protocol
pkg_hooks_claude --> pkg_llm
pkg_hooks_claude --> pkg_session
pkg_hooks_claude --> pkg_subagent
pkg_hooks_claude --> pkg_tools
pkg_subagent_mock --> pkg_agent
pkg_subagent_mock --> pkg_llm
pkg_subagent_mock --> pkg_subagent
pkg_subagent_fork --> pkg_agent
pkg_subagent_fork --> pkg_session
pkg_subagent_fork --> pkg_subagent
pkg_subagent_fork --> pkg_subagent_inprocess
pkg_subagent_spawn --> pkg_subagent
pkg_subagent_spawn --> pkg_subagent_inprocess
pkg_acp_agent --> pkg_acp
pkg_acp_agent --> pkg_agent_core
pkg_acp_agent --> pkg_session_persistence_jsonl
pkg_stdio_agent --> pkg_agent
pkg_stdio_agent --> pkg_agent_core
pkg_stdio_agent --> pkg_llm
pkg_stdio_agent --> pkg_session
pkg_stdio_agent --> pkg_session_persistence_jsonl
```
| Package | Group | Depends on |
| --- | --- | --- |
| [`brand`](../../packages/util/brand) | `util` | - |
| [`llm`](../../packages/llm/llm) | `llm` | [`brand`](../../packages/util/brand) |
| [`bash`](../../packages/bash/bash) | `bash` | [`brand`](../../packages/util/brand) |
| [`llm-deepseek`](../../packages/llm/llm-deepseek) | `llm` | [`llm`](../../packages/llm/llm) |
| [`llm-pi-ai`](../../packages/llm/llm-pi-ai) | `llm` | [`llm`](../../packages/llm/llm) |
| [`session`](../../packages/core/session) | `core` | [`brand`](../../packages/util/brand), [`llm`](../../packages/llm/llm) |
| [`system-prompt`](../../packages/core/system-prompt) | `core` | [`llm`](../../packages/llm/llm) |
| [`bash-local`](../../packages/bash/bash-local) | `bash` | [`bash`](../../packages/bash/bash) |
| [`fs`](../../packages/fs/fs) | `fs` | [`brand`](../../packages/util/brand), [`llm`](../../packages/llm/llm) |
| [`web`](../../packages/web/web) | `web` | [`llm`](../../packages/llm/llm) |
| [`agent`](../../packages/core/agent) | `core` | [`brand`](../../packages/util/brand), [`llm`](../../packages/llm/llm), [`session`](../../packages/core/session) |
| [`fs-local`](../../packages/fs/fs-local) | `fs` | [`fs`](../../packages/fs/fs) |
| [`fs-policy`](../../packages/fs/fs-policy) | `fs` | [`fs`](../../packages/fs/fs) |
| [`compact`](../../packages/compact/compact) | `compact` | [`llm`](../../packages/llm/llm), [`session`](../../packages/core/session) |
| [`web-fetch-local`](../../packages/web/web-fetch-local) | `web` | [`web`](../../packages/web/web) |
| [`web-search-deepseek`](../../packages/web/web-search-deepseek) | `web` | [`web`](../../packages/web/web) |
| [`web-search-exa`](../../packages/web/web-search-exa) | `web` | [`web`](../../packages/web/web) |
| [`web-search-perplexity`](../../packages/web/web-search-perplexity) | `web` | [`web`](../../packages/web/web) |
| [`hook-protocol`](../../packages/hooks/hook-protocol) | `hooks` | [`bash`](../../packages/bash/bash), [`session`](../../packages/core/session) |
| [`session-persistence`](../../packages/session-persistence/session-persistence) | `session-persistence` | [`session`](../../packages/core/session) |
| [`llm-replay`](../../packages/support/llm-replay) | `support` | [`llm`](../../packages/llm/llm), [`session`](../../packages/core/session) |
| [`tools`](../../packages/core/tools) | `core` | [`agent`](../../packages/core/agent), [`llm`](../../packages/llm/llm), [`system-prompt`](../../packages/core/system-prompt) |
| [`compact-basic`](../../packages/compact/compact-basic) | `compact` | [`agent`](../../packages/core/agent), [`compact`](../../packages/compact/compact), [`llm`](../../packages/llm/llm), [`session`](../../packages/core/session) |
| [`session-persistence-jsonl`](../../packages/session-persistence/session-persistence-jsonl) | `session-persistence` | [`session`](../../packages/core/session), [`session-persistence`](../../packages/session-persistence/session-persistence) |
| [`session-persistence-sqlite`](../../packages/session-persistence/session-persistence-sqlite) | `session-persistence` | [`session`](../../packages/core/session), [`session-persistence`](../../packages/session-persistence/session-persistence) |
| [`invariants`](../../packages/support/invariants) | `support` | [`agent`](../../packages/core/agent), [`llm`](../../packages/llm/llm), [`session`](../../packages/core/session) |
| [`agent-loop`](../../packages/core/agent-loop) | `core` | [`agent`](../../packages/core/agent), [`llm`](../../packages/llm/llm), [`session`](../../packages/core/session), [`session-persistence`](../../packages/session-persistence/session-persistence), [`system-prompt`](../../packages/core/system-prompt), [`tools`](../../packages/core/tools) |
| [`tool-bash`](../../packages/bash/tool-bash) | `bash` | [`agent`](../../packages/core/agent), [`bash`](../../packages/bash/bash), [`llm`](../../packages/llm/llm), [`tools`](../../packages/core/tools) |
| [`tool-fs`](../../packages/fs/tool-fs) | `fs` | [`fs`](../../packages/fs/fs), [`llm`](../../packages/llm/llm), [`session`](../../packages/core/session), [`system-prompt`](../../packages/core/system-prompt), [`tools`](../../packages/core/tools) |
| [`subagent`](../../packages/subagent/subagent) | `subagent` | [`agent`](../../packages/core/agent), [`llm`](../../packages/llm/llm), [`tools`](../../packages/core/tools) |
| [`tool-web`](../../packages/web/tool-web) | `web` | [`llm`](../../packages/llm/llm), [`system-prompt`](../../packages/core/system-prompt), [`tools`](../../packages/core/tools), [`web`](../../packages/web/web) |
| [`tool-todo`](../../packages/todo/tool-todo) | `todo` | [`agent`](../../packages/core/agent), [`session`](../../packages/core/session), [`tools`](../../packages/core/tools) |
| [`hooks-codex`](../../packages/hooks/hooks-codex) | `hooks` | [`agent`](../../packages/core/agent), [`hook-protocol`](../../packages/hooks/hook-protocol), [`llm`](../../packages/llm/llm), [`session`](../../packages/core/session), [`tools`](../../packages/core/tools) |
| [`acp`](../../packages/ui/acp) | `ui` | [`agent`](../../packages/core/agent), [`llm`](../../packages/llm/llm), [`session`](../../packages/core/session), [`session-persistence`](../../packages/session-persistence/session-persistence), [`tools`](../../packages/core/tools) |
| [`agent-core`](../../packages/core/agent-core) | `core` | [`agent`](../../packages/core/agent), [`agent-loop`](../../packages/core/agent-loop), [`invariants`](../../packages/support/invariants), [`llm`](../../packages/llm/llm), [`session`](../../packages/core/session), [`system-prompt`](../../packages/core/system-prompt), [`tool-bash`](../../packages/bash/tool-bash), [`tools`](../../packages/core/tools) |
| [`subagent-acp`](../../packages/subagent/subagent-acp) | `subagent` | [`agent`](../../packages/core/agent), [`llm`](../../packages/llm/llm), [`subagent`](../../packages/subagent/subagent) |
| [`subagent-inprocess`](../../packages/subagent/subagent-inprocess) | `subagent` | [`agent`](../../packages/core/agent), [`llm`](../../packages/llm/llm), [`session`](../../packages/core/session), [`subagent`](../../packages/subagent/subagent) |
| [`tool-subagent`](../../packages/subagent/tool-subagent) | `subagent` | [`agent`](../../packages/core/agent), [`llm`](../../packages/llm/llm), [`subagent`](../../packages/subagent/subagent), [`tools`](../../packages/core/tools) |
| [`hooks-claude`](../../packages/hooks/hooks-claude) | `hooks` | [`agent`](../../packages/core/agent), [`hook-protocol`](../../packages/hooks/hook-protocol), [`llm`](../../packages/llm/llm), [`session`](../../packages/core/session), [`subagent`](../../packages/subagent/subagent), [`tools`](../../packages/core/tools) |
| [`subagent-mock`](../../packages/support/subagent-mock) | `support` | [`agent`](../../packages/core/agent), [`llm`](../../packages/llm/llm), [`subagent`](../../packages/subagent/subagent) |
| [`subagent-fork`](../../packages/subagent/subagent-fork) | `subagent` | [`agent`](../../packages/core/agent), [`session`](../../packages/core/session), [`subagent`](../../packages/subagent/subagent), [`subagent-inprocess`](../../packages/subagent/subagent-inprocess) |
| [`subagent-spawn`](../../packages/subagent/subagent-spawn) | `subagent` | [`subagent`](../../packages/subagent/subagent), [`subagent-inprocess`](../../packages/subagent/subagent-inprocess) |
| [`acp-agent`](../../packages/ui/acp-agent) | `ui` | [`acp`](../../packages/ui/acp), [`agent-core`](../../packages/core/agent-core), [`session-persistence-jsonl`](../../packages/session-persistence/session-persistence-jsonl) |
| [`stdio-agent`](../../packages/ui/stdio-agent) | `ui` | [`agent`](../../packages/core/agent), [`agent-core`](../../packages/core/agent-core), [`llm`](../../packages/llm/llm), [`session`](../../packages/core/session), [`session-persistence-jsonl`](../../packages/session-persistence/session-persistence-jsonl) |

View File

@@ -1,25 +0,0 @@
<!-- Generated by scripts/gen-doc-graphs.ts - do not edit by hand.
Run `pnpm run gen-doc-graphs` to regenerate. -->
# Session Surface And Message Projection
Maintenance mode: curated Mermaid dataflow; exact event/type shapes live in core-data-structures.
This graph separates the append-only log from the derived message surface the next model request sees.
```mermaid
flowchart LR
append["Session.append(type, data)"]
log["Append-only SessionEvent log"]
surface["SurfaceManager linked list<br/>surfaceOp + sourceEventSeqs"]
derive["deriveMessages()"]
model["GenerateOptions.messages"]
persist["JSONL / SQLite persistence"]
replay["load / replay / fork seed"]
append --> log
log --> surface
surface --> derive --> model
log --> persist --> replay --> log
```
See [core-data-structures/session.md](../core-data-structures/session.md) for the full `SessionEventMap`, surface operations, and turn-enclosure invariant.

View File

@@ -1,27 +0,0 @@
<!-- Generated by scripts/gen-doc-graphs.ts - do not edit by hand.
Run `pnpm run gen-doc-graphs` to regenerate. -->
# Subagent And Session Lineage
Maintenance mode: curated Mermaid flow; provider inventory is visible in the generated capability seam graph.
This graph keeps delegation semantics separate from hook observation. A subagent backend creates an ordinary child agent/session through the shared provider registry.
```mermaid
flowchart TD
parent["Parent Agent + Session"]
tool["tool-subagent<br/>model-facing name"]
registry["ctx.subagents provider registry"]
spawn["spawn provider<br/>fresh child session"]
fork["fork provider<br/>seeded from completed-turn prefix"]
acp["ACP provider<br/>out-of-process child"]
child["Child AgentHandle<br/>ordinary Agent lifecycle"]
result["SubagentResult returned to tool"]
parent --> tool --> registry
registry --> spawn --> child
registry --> fork --> child
registry --> acp --> child
child --> result --> parent
```
The hooks stack adds richer lifecycle observation around child runs; the core ownership rule stays the same: the provider owns the child handle and must dispose it.

View File

@@ -1,50 +0,0 @@
<!-- Generated by scripts/gen-doc-graphs.ts - do not edit by hand.
Run `pnpm run gen-doc-graphs` to regenerate. -->
# Tool Affordance Map
Maintenance mode: hybrid: tool names/schemas are boot-harvested from shipped tool plugins; required services and shipped aliases are classified in `scripts/gen-doc-graphs.ts` with a completeness guard.
This page connects the model-visible tools to the plugin packages and service seams behind them. For exact JSON Schemas, see [tool-catalog/tools.md](../tool-catalog/tools.md).
```mermaid
flowchart LR
model["Model request tools[]"]
toolpkg__deepseek_ai_dsh_tool_bash["tool-bash<br/>bash, bash_kill, bash_output"]
model --> toolpkg__deepseek_ai_dsh_tool_bash
requires_ctx_tools["ctx.tools"]
toolpkg__deepseek_ai_dsh_tool_bash --> requires_ctx_tools
requires_ctx_bash["ctx.bash"]
toolpkg__deepseek_ai_dsh_tool_bash --> requires_ctx_bash
toolpkg__deepseek_ai_dsh_tool_fs["tool-fs<br/>edit, read, write"]
model --> toolpkg__deepseek_ai_dsh_tool_fs
toolpkg__deepseek_ai_dsh_tool_fs --> requires_ctx_tools
requires_ctx_fs["ctx.fs"]
toolpkg__deepseek_ai_dsh_tool_fs --> requires_ctx_fs
requires_ctx_systemPrompt["ctx.systemPrompt"]
toolpkg__deepseek_ai_dsh_tool_fs --> requires_ctx_systemPrompt
toolpkg__deepseek_ai_dsh_tool_subagent["tool-subagent<br/>subagent"]
model --> toolpkg__deepseek_ai_dsh_tool_subagent
toolpkg__deepseek_ai_dsh_tool_subagent --> requires_ctx_tools
requires_ctx_subagents["ctx.subagents"]
toolpkg__deepseek_ai_dsh_tool_subagent --> requires_ctx_subagents
toolpkg__deepseek_ai_dsh_tool_todo["tool-todo<br/>todo_write"]
model --> toolpkg__deepseek_ai_dsh_tool_todo
toolpkg__deepseek_ai_dsh_tool_todo --> requires_ctx_tools
requires_owning_Agent_session["owning Agent session"]
toolpkg__deepseek_ai_dsh_tool_todo --> requires_owning_Agent_session
toolpkg__deepseek_ai_dsh_tool_web["tool-web<br/>web_fetch, web_search"]
model --> toolpkg__deepseek_ai_dsh_tool_web
toolpkg__deepseek_ai_dsh_tool_web --> requires_ctx_tools
requires_ctx_web["ctx.web"]
toolpkg__deepseek_ai_dsh_tool_web --> requires_ctx_web
toolpkg__deepseek_ai_dsh_tool_web --> requires_ctx_systemPrompt
```
| Tool package | Model-visible names | Requires | Writes / affects | Shipped aliases | Note |
| --- | --- | --- | --- | --- | --- |
| `@deepseek-ai/dsh-tool-bash` | `bash`, `bash_kill`, `bash_output` | `ctx.tools`, `ctx.bash` | `tool/call`, `tool/result`, `context/message via agent.inject() for background completion notices` | - | The bash/bash_output/bash_kill tools are model-facing consumers of the bash executor seam. |
| `@deepseek-ai/dsh-tool-fs` | `edit`, `read`, `write` | `ctx.tools`, `ctx.fs`, `ctx.systemPrompt` | `tool/call`, `fs/write-intent or fs/edit-intent for mutations`, `fs/observed after successful file operations`, `tool/result` | - | read/write/edit are the model-facing filesystem tools; read windowing lives here, while read-before-edit policy is supplied by fs-policy through fs/* events. |
| `@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 default package schema registers subagent; shipped coding/acp configs load it twice to expose spawn and fork backends. |
| `@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-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. |

View File

@@ -3,173 +3,243 @@
# Module dependency graph
Inter-package dependencies among the `@deepseek-ai/dsh-*` harness packages, derived from each package's `peerDependencies` (the canonical runtime-dependency signal). An edge `a --> b` means package `a` depends on package `b`. Names have the `@deepseek-ai/dsh-` prefix stripped.
Inter-package dependencies among the `@deepseek-ai/dsh-*` harness packages, derived from each package's `peerDependencies` (the canonical runtime-dependency signal) and grouped by the `packages/<group>/<pkg>` hierarchy. An edge `a --> b` means package `a` depends on package `b`. Names have the `@deepseek-ai/dsh-` prefix stripped.
```mermaid
graph TD
bash --> brand
llm --> brand
bash-local --> bash
fs --> brand
fs --> llm
llm-deepseek --> llm
llm-pi-ai --> llm
session --> brand
session --> llm
system-prompt --> llm
web --> llm
agent --> brand
agent --> llm
agent --> session
compact --> llm
compact --> session
fs-local --> fs
fs-policy --> fs
hook-protocol --> bash
hook-protocol --> session
llm-replay --> llm
llm-replay --> session
session-persistence --> session
web-fetch-local --> web
web-search-deepseek --> web
web-search-exa --> web
web-search-perplexity --> web
compact-basic --> agent
compact-basic --> compact
compact-basic --> llm
compact-basic --> session
invariants --> agent
invariants --> llm
invariants --> session
session-persistence-jsonl --> session
session-persistence-jsonl --> session-persistence
session-persistence-sqlite --> session
session-persistence-sqlite --> session-persistence
tools --> agent
tools --> llm
tools --> system-prompt
acp --> agent
acp --> llm
acp --> session
acp --> session-persistence
acp --> tools
agent-loop --> agent
agent-loop --> llm
agent-loop --> session
agent-loop --> session-persistence
agent-loop --> system-prompt
agent-loop --> tools
hooks-codex --> agent
hooks-codex --> hook-protocol
hooks-codex --> llm
hooks-codex --> session
hooks-codex --> tools
subagent --> agent
subagent --> llm
subagent --> tools
tool-bash --> agent
tool-bash --> bash
tool-bash --> llm
tool-bash --> tools
tool-fs --> fs
tool-fs --> llm
tool-fs --> session
tool-fs --> system-prompt
tool-fs --> tools
tool-todo --> agent
tool-todo --> session
tool-todo --> tools
tool-web --> llm
tool-web --> system-prompt
tool-web --> tools
tool-web --> web
agent-core --> agent
agent-core --> agent-loop
agent-core --> invariants
agent-core --> llm
agent-core --> session
agent-core --> system-prompt
agent-core --> tool-bash
agent-core --> tools
hooks-claude --> agent
hooks-claude --> hook-protocol
hooks-claude --> llm
hooks-claude --> session
hooks-claude --> subagent
hooks-claude --> tools
subagent-acp --> agent
subagent-acp --> llm
subagent-acp --> subagent
subagent-inprocess --> agent
subagent-inprocess --> llm
subagent-inprocess --> session
subagent-inprocess --> subagent
subagent-mock --> agent
subagent-mock --> llm
subagent-mock --> subagent
tool-subagent --> agent
tool-subagent --> llm
tool-subagent --> subagent
tool-subagent --> tools
acp-agent --> acp
acp-agent --> agent-core
acp-agent --> session-persistence-jsonl
stdio-agent --> agent
stdio-agent --> agent-core
stdio-agent --> llm
stdio-agent --> session
stdio-agent --> session-persistence-jsonl
subagent-fork --> agent
subagent-fork --> session
subagent-fork --> subagent
subagent-fork --> subagent-inprocess
subagent-spawn --> subagent
subagent-spawn --> subagent-inprocess
flowchart TD
subgraph group_util["packages/util"]
pkg_brand["brand"]
end
subgraph group_llm["packages/llm"]
pkg_llm["llm"]
pkg_llm_deepseek["llm-deepseek"]
pkg_llm_pi_ai["llm-pi-ai"]
end
subgraph group_core["packages/core"]
pkg_agent["agent"]
pkg_agent_core["agent-core"]
pkg_agent_loop["agent-loop"]
pkg_session["session"]
pkg_system_prompt["system-prompt"]
pkg_tools["tools"]
end
subgraph group_bash["packages/bash"]
pkg_bash["bash"]
pkg_bash_local["bash-local"]
pkg_tool_bash["tool-bash"]
end
subgraph group_fs["packages/fs"]
pkg_fs["fs"]
pkg_fs_local["fs-local"]
pkg_fs_policy["fs-policy"]
pkg_tool_fs["tool-fs"]
end
subgraph group_compact["packages/compact"]
pkg_compact["compact"]
pkg_compact_basic["compact-basic"]
end
subgraph group_subagent["packages/subagent"]
pkg_subagent["subagent"]
pkg_subagent_acp["subagent-acp"]
pkg_subagent_fork["subagent-fork"]
pkg_subagent_inprocess["subagent-inprocess"]
pkg_subagent_spawn["subagent-spawn"]
pkg_tool_subagent["tool-subagent"]
end
subgraph group_web["packages/web"]
pkg_tool_web["tool-web"]
pkg_web["web"]
pkg_web_fetch_local["web-fetch-local"]
pkg_web_search_deepseek["web-search-deepseek"]
pkg_web_search_exa["web-search-exa"]
pkg_web_search_perplexity["web-search-perplexity"]
end
subgraph group_todo["packages/todo"]
pkg_tool_todo["tool-todo"]
end
subgraph group_hooks["packages/hooks"]
pkg_hook_protocol["hook-protocol"]
pkg_hooks_claude["hooks-claude"]
pkg_hooks_codex["hooks-codex"]
end
subgraph group_session_persistence["packages/session-persistence"]
pkg_session_persistence["session-persistence"]
pkg_session_persistence_jsonl["session-persistence-jsonl"]
pkg_session_persistence_sqlite["session-persistence-sqlite"]
end
subgraph group_support["packages/support"]
pkg_invariants["invariants"]
pkg_llm_replay["llm-replay"]
pkg_subagent_mock["subagent-mock"]
end
subgraph group_ui["packages/ui"]
pkg_acp["acp"]
pkg_acp_agent["acp-agent"]
pkg_stdio_agent["stdio-agent"]
end
pkg_llm --> pkg_brand
pkg_bash --> pkg_brand
pkg_llm_deepseek --> pkg_llm
pkg_llm_pi_ai --> pkg_llm
pkg_session --> pkg_brand
pkg_session --> pkg_llm
pkg_system_prompt --> pkg_llm
pkg_bash_local --> pkg_bash
pkg_fs --> pkg_brand
pkg_fs --> pkg_llm
pkg_web --> pkg_llm
pkg_agent --> pkg_brand
pkg_agent --> pkg_llm
pkg_agent --> pkg_session
pkg_fs_local --> pkg_fs
pkg_fs_policy --> pkg_fs
pkg_compact --> pkg_llm
pkg_compact --> pkg_session
pkg_web_fetch_local --> pkg_web
pkg_web_search_deepseek --> pkg_web
pkg_web_search_exa --> pkg_web
pkg_web_search_perplexity --> pkg_web
pkg_hook_protocol --> pkg_bash
pkg_hook_protocol --> pkg_session
pkg_session_persistence --> pkg_session
pkg_llm_replay --> pkg_llm
pkg_llm_replay --> pkg_session
pkg_tools --> pkg_agent
pkg_tools --> pkg_llm
pkg_tools --> pkg_system_prompt
pkg_compact_basic --> pkg_agent
pkg_compact_basic --> pkg_compact
pkg_compact_basic --> pkg_llm
pkg_compact_basic --> pkg_session
pkg_session_persistence_jsonl --> pkg_session
pkg_session_persistence_jsonl --> pkg_session_persistence
pkg_session_persistence_sqlite --> pkg_session
pkg_session_persistence_sqlite --> pkg_session_persistence
pkg_invariants --> pkg_agent
pkg_invariants --> pkg_llm
pkg_invariants --> pkg_session
pkg_agent_loop --> pkg_agent
pkg_agent_loop --> pkg_llm
pkg_agent_loop --> pkg_session
pkg_agent_loop --> pkg_session_persistence
pkg_agent_loop --> pkg_system_prompt
pkg_agent_loop --> pkg_tools
pkg_tool_bash --> pkg_agent
pkg_tool_bash --> pkg_bash
pkg_tool_bash --> pkg_llm
pkg_tool_bash --> pkg_tools
pkg_tool_fs --> pkg_fs
pkg_tool_fs --> pkg_llm
pkg_tool_fs --> pkg_session
pkg_tool_fs --> pkg_system_prompt
pkg_tool_fs --> pkg_tools
pkg_subagent --> pkg_agent
pkg_subagent --> pkg_llm
pkg_subagent --> pkg_tools
pkg_tool_web --> pkg_llm
pkg_tool_web --> pkg_system_prompt
pkg_tool_web --> pkg_tools
pkg_tool_web --> pkg_web
pkg_tool_todo --> pkg_agent
pkg_tool_todo --> pkg_session
pkg_tool_todo --> pkg_tools
pkg_hooks_codex --> pkg_agent
pkg_hooks_codex --> pkg_hook_protocol
pkg_hooks_codex --> pkg_llm
pkg_hooks_codex --> pkg_session
pkg_hooks_codex --> pkg_tools
pkg_acp --> pkg_agent
pkg_acp --> pkg_llm
pkg_acp --> pkg_session
pkg_acp --> pkg_session_persistence
pkg_acp --> pkg_tools
pkg_agent_core --> pkg_agent
pkg_agent_core --> pkg_agent_loop
pkg_agent_core --> pkg_invariants
pkg_agent_core --> pkg_llm
pkg_agent_core --> pkg_session
pkg_agent_core --> pkg_system_prompt
pkg_agent_core --> pkg_tool_bash
pkg_agent_core --> pkg_tools
pkg_subagent_acp --> pkg_agent
pkg_subagent_acp --> pkg_llm
pkg_subagent_acp --> pkg_subagent
pkg_subagent_inprocess --> pkg_agent
pkg_subagent_inprocess --> pkg_llm
pkg_subagent_inprocess --> pkg_session
pkg_subagent_inprocess --> pkg_subagent
pkg_tool_subagent --> pkg_agent
pkg_tool_subagent --> pkg_llm
pkg_tool_subagent --> pkg_subagent
pkg_tool_subagent --> pkg_tools
pkg_hooks_claude --> pkg_agent
pkg_hooks_claude --> pkg_hook_protocol
pkg_hooks_claude --> pkg_llm
pkg_hooks_claude --> pkg_session
pkg_hooks_claude --> pkg_subagent
pkg_hooks_claude --> pkg_tools
pkg_subagent_mock --> pkg_agent
pkg_subagent_mock --> pkg_llm
pkg_subagent_mock --> pkg_subagent
pkg_subagent_fork --> pkg_agent
pkg_subagent_fork --> pkg_session
pkg_subagent_fork --> pkg_subagent
pkg_subagent_fork --> pkg_subagent_inprocess
pkg_subagent_spawn --> pkg_subagent
pkg_subagent_spawn --> pkg_subagent_inprocess
pkg_acp_agent --> pkg_acp
pkg_acp_agent --> pkg_agent_core
pkg_acp_agent --> pkg_session_persistence_jsonl
pkg_stdio_agent --> pkg_agent
pkg_stdio_agent --> pkg_agent_core
pkg_stdio_agent --> pkg_llm
pkg_stdio_agent --> pkg_session
pkg_stdio_agent --> pkg_session_persistence_jsonl
```
| Package | Depends on |
| --- | --- |
| `brand` | — |
| `bash` | `brand` |
| `llm` | `brand` |
| `bash-local` | `bash` |
| `fs` | `brand`, `llm` |
| `llm-deepseek` | `llm` |
| `llm-pi-ai` | `llm` |
| `session` | `brand`, `llm` |
| `system-prompt` | `llm` |
| `web` | `llm` |
| `agent` | `brand`, `llm`, `session` |
| `compact` | `llm`, `session` |
| `fs-local` | `fs` |
| `fs-policy` | `fs` |
| `hook-protocol` | `bash`, `session` |
| `llm-replay` | `llm`, `session` |
| `session-persistence` | `session` |
| `web-fetch-local` | `web` |
| `web-search-deepseek` | `web` |
| `web-search-exa` | `web` |
| `web-search-perplexity` | `web` |
| `compact-basic` | `agent`, `compact`, `llm`, `session` |
| `invariants` | `agent`, `llm`, `session` |
| `session-persistence-jsonl` | `session`, `session-persistence` |
| `session-persistence-sqlite` | `session`, `session-persistence` |
| `tools` | `agent`, `llm`, `system-prompt` |
| `acp` | `agent`, `llm`, `session`, `session-persistence`, `tools` |
| `agent-loop` | `agent`, `llm`, `session`, `session-persistence`, `system-prompt`, `tools` |
| `hooks-codex` | `agent`, `hook-protocol`, `llm`, `session`, `tools` |
| `subagent` | `agent`, `llm`, `tools` |
| `tool-bash` | `agent`, `bash`, `llm`, `tools` |
| `tool-fs` | `fs`, `llm`, `session`, `system-prompt`, `tools` |
| `tool-todo` | `agent`, `session`, `tools` |
| `tool-web` | `llm`, `system-prompt`, `tools`, `web` |
| `agent-core` | `agent`, `agent-loop`, `invariants`, `llm`, `session`, `system-prompt`, `tool-bash`, `tools` |
| `hooks-claude` | `agent`, `hook-protocol`, `llm`, `session`, `subagent`, `tools` |
| `subagent-acp` | `agent`, `llm`, `subagent` |
| `subagent-inprocess` | `agent`, `llm`, `session`, `subagent` |
| `subagent-mock` | `agent`, `llm`, `subagent` |
| `tool-subagent` | `agent`, `llm`, `subagent`, `tools` |
| `acp-agent` | `acp`, `agent-core`, `session-persistence-jsonl` |
| `stdio-agent` | `agent`, `agent-core`, `llm`, `session`, `session-persistence-jsonl` |
| `subagent-fork` | `agent`, `session`, `subagent`, `subagent-inprocess` |
| `subagent-spawn` | `subagent`, `subagent-inprocess` |
| Package | Group | Depends on |
| --- | --- | --- |
| [`brand`](../packages/util/brand) | `util` | — |
| [`llm`](../packages/llm/llm) | `llm` | [`brand`](../packages/util/brand) |
| [`bash`](../packages/bash/bash) | `bash` | [`brand`](../packages/util/brand) |
| [`llm-deepseek`](../packages/llm/llm-deepseek) | `llm` | [`llm`](../packages/llm/llm) |
| [`llm-pi-ai`](../packages/llm/llm-pi-ai) | `llm` | [`llm`](../packages/llm/llm) |
| [`session`](../packages/core/session) | `core` | [`brand`](../packages/util/brand), [`llm`](../packages/llm/llm) |
| [`system-prompt`](../packages/core/system-prompt) | `core` | [`llm`](../packages/llm/llm) |
| [`bash-local`](../packages/bash/bash-local) | `bash` | [`bash`](../packages/bash/bash) |
| [`fs`](../packages/fs/fs) | `fs` | [`brand`](../packages/util/brand), [`llm`](../packages/llm/llm) |
| [`web`](../packages/web/web) | `web` | [`llm`](../packages/llm/llm) |
| [`agent`](../packages/core/agent) | `core` | [`brand`](../packages/util/brand), [`llm`](../packages/llm/llm), [`session`](../packages/core/session) |
| [`fs-local`](../packages/fs/fs-local) | `fs` | [`fs`](../packages/fs/fs) |
| [`fs-policy`](../packages/fs/fs-policy) | `fs` | [`fs`](../packages/fs/fs) |
| [`compact`](../packages/compact/compact) | `compact` | [`llm`](../packages/llm/llm), [`session`](../packages/core/session) |
| [`web-fetch-local`](../packages/web/web-fetch-local) | `web` | [`web`](../packages/web/web) |
| [`web-search-deepseek`](../packages/web/web-search-deepseek) | `web` | [`web`](../packages/web/web) |
| [`web-search-exa`](../packages/web/web-search-exa) | `web` | [`web`](../packages/web/web) |
| [`web-search-perplexity`](../packages/web/web-search-perplexity) | `web` | [`web`](../packages/web/web) |
| [`hook-protocol`](../packages/hooks/hook-protocol) | `hooks` | [`bash`](../packages/bash/bash), [`session`](../packages/core/session) |
| [`session-persistence`](../packages/session-persistence/session-persistence) | `session-persistence` | [`session`](../packages/core/session) |
| [`llm-replay`](../packages/support/llm-replay) | `support` | [`llm`](../packages/llm/llm), [`session`](../packages/core/session) |
| [`tools`](../packages/core/tools) | `core` | [`agent`](../packages/core/agent), [`llm`](../packages/llm/llm), [`system-prompt`](../packages/core/system-prompt) |
| [`compact-basic`](../packages/compact/compact-basic) | `compact` | [`agent`](../packages/core/agent), [`compact`](../packages/compact/compact), [`llm`](../packages/llm/llm), [`session`](../packages/core/session) |
| [`session-persistence-jsonl`](../packages/session-persistence/session-persistence-jsonl) | `session-persistence` | [`session`](../packages/core/session), [`session-persistence`](../packages/session-persistence/session-persistence) |
| [`session-persistence-sqlite`](../packages/session-persistence/session-persistence-sqlite) | `session-persistence` | [`session`](../packages/core/session), [`session-persistence`](../packages/session-persistence/session-persistence) |
| [`invariants`](../packages/support/invariants) | `support` | [`agent`](../packages/core/agent), [`llm`](../packages/llm/llm), [`session`](../packages/core/session) |
| [`agent-loop`](../packages/core/agent-loop) | `core` | [`agent`](../packages/core/agent), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`session-persistence`](../packages/session-persistence/session-persistence), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools) |
| [`tool-bash`](../packages/bash/tool-bash) | `bash` | [`agent`](../packages/core/agent), [`bash`](../packages/bash/bash), [`llm`](../packages/llm/llm), [`tools`](../packages/core/tools) |
| [`tool-fs`](../packages/fs/tool-fs) | `fs` | [`fs`](../packages/fs/fs), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools) |
| [`subagent`](../packages/subagent/subagent) | `subagent` | [`agent`](../packages/core/agent), [`llm`](../packages/llm/llm), [`tools`](../packages/core/tools) |
| [`tool-web`](../packages/web/tool-web) | `web` | [`llm`](../packages/llm/llm), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools), [`web`](../packages/web/web) |
| [`tool-todo`](../packages/todo/tool-todo) | `todo` | [`agent`](../packages/core/agent), [`session`](../packages/core/session), [`tools`](../packages/core/tools) |
| [`hooks-codex`](../packages/hooks/hooks-codex) | `hooks` | [`agent`](../packages/core/agent), [`hook-protocol`](../packages/hooks/hook-protocol), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`tools`](../packages/core/tools) |
| [`acp`](../packages/ui/acp) | `ui` | [`agent`](../packages/core/agent), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`session-persistence`](../packages/session-persistence/session-persistence), [`tools`](../packages/core/tools) |
| [`agent-core`](../packages/core/agent-core) | `core` | [`agent`](../packages/core/agent), [`agent-loop`](../packages/core/agent-loop), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`system-prompt`](../packages/core/system-prompt), [`tool-bash`](../packages/bash/tool-bash), [`tools`](../packages/core/tools) |
| [`subagent-acp`](../packages/subagent/subagent-acp) | `subagent` | [`agent`](../packages/core/agent), [`llm`](../packages/llm/llm), [`subagent`](../packages/subagent/subagent) |
| [`subagent-inprocess`](../packages/subagent/subagent-inprocess) | `subagent` | [`agent`](../packages/core/agent), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`subagent`](../packages/subagent/subagent) |
| [`tool-subagent`](../packages/subagent/tool-subagent) | `subagent` | [`agent`](../packages/core/agent), [`llm`](../packages/llm/llm), [`subagent`](../packages/subagent/subagent), [`tools`](../packages/core/tools) |
| [`hooks-claude`](../packages/hooks/hooks-claude) | `hooks` | [`agent`](../packages/core/agent), [`hook-protocol`](../packages/hooks/hook-protocol), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`subagent`](../packages/subagent/subagent), [`tools`](../packages/core/tools) |
| [`subagent-mock`](../packages/support/subagent-mock) | `support` | [`agent`](../packages/core/agent), [`llm`](../packages/llm/llm), [`subagent`](../packages/subagent/subagent) |
| [`subagent-fork`](../packages/subagent/subagent-fork) | `subagent` | [`agent`](../packages/core/agent), [`session`](../packages/core/session), [`subagent`](../packages/subagent/subagent), [`subagent-inprocess`](../packages/subagent/subagent-inprocess) |
| [`subagent-spawn`](../packages/subagent/subagent-spawn) | `subagent` | [`subagent`](../packages/subagent/subagent), [`subagent-inprocess`](../packages/subagent/subagent-inprocess) |
| [`acp-agent`](../packages/ui/acp-agent) | `ui` | [`acp`](../packages/ui/acp), [`agent-core`](../packages/core/agent-core), [`session-persistence-jsonl`](../packages/session-persistence/session-persistence-jsonl) |
| [`stdio-agent`](../packages/ui/stdio-agent) | `ui` | [`agent`](../packages/core/agent), [`agent-core`](../packages/core/agent-core), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`session-persistence-jsonl`](../packages/session-persistence/session-persistence-jsonl) |

View File

@@ -169,7 +169,7 @@ Do NOT write one for a mechanical or local choice (a variable name, a one-file r
| [Classify RFCs by kind via path-encoded subdirectories](implemented/process/2026-06-20-rfc-classification.md) | 2026-06-20 |
| [Bilingual documentation via paired sibling files and a pairing gate](implemented/process/2026-07-02-bilingual-docs-and-pairing-gate.md) | 2026-07-02 |
| [Generated tool-schema catalog (boot-and-harvest)](implemented/process/2026-07-02-tool-schema-catalog.md) | 2026-07-02 |
| [Documentation graph atlas for maintainers and SDK users](implemented/process/2026-07-03-documentation-graph-atlas.md) | 2026-07-03 |
| [Documentation graph index for maintainers and SDK users](implemented/process/2026-07-03-documentation-graph-atlas.md) | 2026-07-03 |
| [JSDoc completeness gate for the cordis surface](implemented/process/2026-07-04-cordis-jsdoc-completeness-gate.md) | 2026-07-04 |
| [Documentation tiers, budgets, and the ceiling gate](implemented/process/2026-07-04-doc-tiers-and-budgets.md) | 2026-07-04 |
| [Generate the RFC index tables](implemented/process/2026-07-04-generate-rfc-index-tables.md) | 2026-07-04 |

View File

@@ -1,4 +1,4 @@
# RFC: Documentation graph atlas for maintainers and SDK users
# RFC: Documentation graph index for maintainers and SDK users
Status: implemented (accepted 2026-07-03)
@@ -12,9 +12,9 @@ The pressure is already visible in the open stacks even though this implementati
## Decision
Add a generated graph atlas under [docs/graphs/](../../../graphs/README.md), produced by `scripts/gen-doc-graphs.ts` and verified by `pnpm run verify-doc-graphs` as part of `doc-sync`.
Add generated relationship graph docs, indexed at [docs/graph-atlas.md](../../../graph-atlas.md), produced by focused generators and verified by `pnpm run verify-doc-graphs` / existing catalog freshness checks as part of `doc-sync`.
The atlas is a relationship layer above the existing catalogs. It does not replace exact references; instead, it links to them and explains how their pieces fit together.
The index is a relationship layer above the existing catalogs. It does not replace exact references; instead, it links to them and explains how their pieces fit together.
### Maintenance modes
@@ -22,36 +22,36 @@ Every graph page declares one maintenance mode:
- **Generated**: all nodes and edges are discovered from source; `--check` fails if the committed artifact is stale.
- **Hybrid generated**: source discovers the inventory, a small manifest classifies irreducible policy, and a completeness guard fails if discovered items are unclassified.
- **Curated**: the diagram explains design intent, temporal order, or ownership; it is emitted by the generator so the atlas remains a single regenerated unit, but the content is deliberately authored.
- **Curated**: the diagram explains design intent, temporal order, or ownership; it is emitted by the generator so the graph docs remain a regenerated unit, but the content is deliberately authored.
### First shipped atlas
### First shipped index
The first atlas ships twelve files: the index plus eleven graph pages.
The first index links ten relationship surfaces. Package topology and tool-package affordances live in the existing generated catalogs that already own those facts; the remaining focused diagrams are generated by `scripts/gen-doc-graphs.ts`.
| Graph | Maintenance mode | Source of truth |
|---|---|---|
| [package topology by group](../../../graphs/package-topology.md) | generated | `packages/*/*/package.json` peer dependencies plus package group paths |
| [capability seams and core services](../../../graphs/capability-seams.md) | hybrid generated | Cordis service declarations plus a role manifest in `gen-doc-graphs.ts` |
| [app composition](../../../graphs/app-composition.md) | hybrid generated | `examples/*/cordis.yml` plugin lists plus curated app/bundle expansions |
| [event producer/consumer matrix](../../../graphs/event-producer-consumer.md) | hybrid generated | Cordis event declarations, AST-scanned `ctx.on/emit/parallel/serial/waterfall` sites, and explicit dynamic dispatch overrides |
| [tool affordance map](../../../graphs/tool-affordance-map.md) | hybrid generated | boot-harvested tool catalog plus a manifest of required services and shipped aliases |
| [agent turn and step lifecycle](../../../graphs/agent-lifecycle.md) | curated | architecture.md loop lifecycle, Cordis catalog links, and session event semantics |
| [tool execution pipeline](../../../graphs/tool-execution-pipeline.md) | curated | tool pipeline semantics and the `tools/execute` waterfall |
| [session surface and message projection](../../../graphs/session-surface.md) | curated | session surface/event-sourcing docs |
| [subagent and session lineage](../../../graphs/subagent-lineage.md) | curated | subagent seam docs and replay/fork semantics |
| [plugin disposal and hot reload ownership](../../../graphs/hot-reload-disposal.md) | curated | Cordis fiber/effect ownership conventions |
| [ACP snapshot replay](../../../graphs/snapshot-replay.md) | curated | snapshot harness behavior |
| [module dependency graph](../../../module-graph.md) | generated | `packages/*/*/package.json` peer dependencies plus package group paths |
| [tool schema catalog and package map](../../../tool-catalog/tools.md) | generated | boot-harvested tool schemas plus tool-package service/effect metadata |
| [capability seams and core services](../../../capability-seams.md) | hybrid generated | Cordis service declarations plus a role manifest in `gen-doc-graphs.ts` |
| [echo-agent app composition](../../../echo-agent-composition.md) | hybrid generated | `examples/echo-agent/cordis.yml` plugin list plus curated app/bundle expansion |
| [coding-agent app composition](../../../coding-agent-composition.md) | hybrid generated | `examples/coding-agent/cordis.yml` plugin list plus curated app/bundle expansion |
| [acp-agent app composition](../../../acp-agent-composition.md) | hybrid generated | `examples/acp-agent/cordis.yml` plugin list plus curated app/bundle expansion |
| [event producer/consumer matrix](../../../event-producer-consumer.md) | hybrid generated | Cordis event declarations, AST-scanned `ctx.on/emit/parallel/serial/waterfall` sites, and explicit dynamic dispatch overrides |
| [agent turn and step lifecycle](../../../agent-lifecycle.md) | curated | architecture.md loop lifecycle, Cordis catalog links, and session event semantics |
| [tool execution pipeline](../../../tool-execution-pipeline.md) | curated | tool pipeline semantics and the `tools/execute` waterfall |
| [ACP snapshot replay](../../../acp/snapshot-replay.md) | curated | snapshot harness behavior |
### Why one generator
### Why generators own the docs
Keeping the atlas behind one generator gives reviewers one freshness gate and keeps cross-page terminology synchronized. The tradeoff is that curated diagrams are edited in TypeScript string blocks rather than directly in Markdown. That is acceptable for this first cut because the user-facing artifact is still plain Markdown/Mermaid, and a future change can split the curated pages out if authorship ergonomics matter more than one-command regeneration.
Package topology stays in `gen-module-graph.ts`, and tool-package affordances stay in `gen-tool-catalog.ts`, because those generators already own the canonical facts and freshness gates. `gen-doc-graphs.ts` owns the remaining relationship pages and the index. The tradeoff is that curated diagrams are edited in TypeScript string blocks rather than directly in Markdown. That is acceptable for this first cut because the user-facing artifact is still plain Markdown/Mermaid, and a future change can split the curated pages out if authorship ergonomics matter more than regeneration.
### Completeness guards
The hybrid pages must fail loud when their manifests are stale:
- The module graph reads every package's `peerDependencies` and groups each package by its `packages/<group>/<pkg>` path.
- The tool catalog boot-harvests shipped tools and renders the package/service/effect map from the same manifest that its completeness guard already checks.
- The capability seam graph imports the Cordis service collector and asserts every discovered harness `ctx.<key>` is classified in `SERVICE_ROLES`, and every classified key still exists.
- The tool affordance graph boot-harvests the shipped tool catalog and asserts every tool package has `TOOL_PACKAGE_META`.
- The event producer/consumer matrix labels itself hybrid because subagent lifecycle events deliberately use `ctx.events.dispatch` for per-listener containment; those dynamic edges are explicit overrides rather than invisible omissions.
- `verify-mermaid` parses every repo-authored ` ```mermaid ` fence with Mermaid's own parser, so syntax errors fail `doc-sync` locally and in CI instead of showing up as broken GitHub-rendered diagrams.
@@ -61,7 +61,7 @@ Use Mermaid for committed diagrams because GitHub renders it in Markdown and it
## Consequences
- Maintainers get visual entry points for topology, seams, event flow, lifecycle, session replay, and snapshot behavior.
- Maintainers get visual entry points for topology, seams, event flow, lifecycle, app composition, and snapshot behavior.
- SDK users get a path from use case to package composition instead of only bottom-up package references.
- `doc-sync` now includes `verify-doc-graphs` and `verify-mermaid`, so graph drift and Mermaid syntax errors are caught with the other doc freshness gates.
- Future fs and hooks work has a concrete place to land new complexity: fs should expand the capability and tool graphs, while hooks should expand the event matrix and tool execution pipeline.
- Future fs and hooks work has a concrete place to land new complexity: fs should expand the capability docs and tool catalog, while hooks should expand the event matrix and tool execution pipeline.

View File

@@ -9,6 +9,18 @@ This file is GENERATED and verified fresh by `pnpm run verify-tool-catalog` (par
Scope: shipped product tools under `packages/*/tool-*`, each booted with its DEFAULT config. The registered tool NAME can be a load-time config (e.g. `tool-subagent`'s `toolName`), so a deployment may surface a package under a different or additional name — a per-package note records those shipped aliases where they exist. The `examples/` demo tools (e.g. `echo`) are excluded, matching the cordis catalog's packages-only scope.
## Tool Package Map
This table connects model-visible tool names to the plugin package and service seams behind them. Exact JSON Schemas follow in the package sections below.
| Tool package | Model-visible names | Requires | Writes / affects | Shipped aliases | Deployment note |
| --- | --- | --- | --- | --- | --- |
| `@deepseek-ai/dsh-tool-bash` | `bash`, `bash_kill`, `bash_output` | `ctx.tools`, `ctx.bash` | `tool/call`, `tool/result`, `context/message via agent.inject() for background completion notices` | - | The bash/bash_output/bash_kill tools are model-facing consumers of the bash executor seam. |
| `@deepseek-ai/dsh-tool-fs` | `edit`, `read`, `write` | `ctx.tools`, `ctx.fs`, `ctx.systemPrompt` | `tool/call`, `fs/write-intent or fs/edit-intent for mutations`, `fs/observed after successful file operations`, `tool/result` | - | The read-before-write/edit policy is added by `@deepseek-ai/dsh-fs-policy` (an `fs/*` event-gate plugin, no schema change); a deployment that loads these tools is expected to also load it. The tool schemas above are identical with or without the policy plugin. |
| `@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/coding-agent/cordis.yml` and `examples/acp-agent/cordis.yml`. |
| `@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-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. |
## `@deepseek-ai/dsh-tool-bash`
### `bash`
@@ -91,6 +103,8 @@ Read new output from a background bash task started with `bash` + `run_in_backgr
Source: [`packages/bash/tool-bash/src/index.ts`](../../packages/bash/tool-bash/src/index.ts)
The bash/bash_output/bash_kill tools are model-facing consumers of the bash executor seam.
## `@deepseek-ai/dsh-tool-fs`
### `edit`
@@ -260,6 +274,8 @@ 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.
## `@deepseek-ai/dsh-tool-web`
### `web_fetch`
@@ -307,3 +323,5 @@ Search the web for current information. Returns an optional summary answer and a
```
Source: [`packages/web/tool-web/src/index.ts`](../../packages/web/tool-web/src/index.ts)
web_search and web_fetch keep provider selection behind ctx.web so model-visible schemas stay stable across backend swaps.

View File

@@ -10,16 +10,16 @@ This graph shows where policy, hooks, sandboxing, filesystem guards, result rewr
```mermaid
flowchart TD
model["Assistant message contains tool-call block"]
toolCall["Session event: tool/call<br/>logged before execution"]
toolCall["Session event: <code>tool/call</code><br/>logged before execution"]
presentCall["UI pending card<br/>presentCall(args)"]
pre["tools/pre-execute waterfall<br/>hooks, permission, sandbox"]
pre["<code>tools/pre-execute</code> waterfall<br/>hooks, permission, sandbox"]
denied["deny or ask<br/>tool body skipped"]
toolBody["Registered tool execute() body"]
fsGate["fs/write-intent or fs/edit-intent<br/>tool-fs mutations only"]
owned["Tool-owned session events<br/>todo/write, fs/observed, hook/invoked, hook/result"]
post["tools/post-execute waterfall<br/>accept, block, replace, add context"]
fsGate["<code>fs/write-intent</code> or <code>fs/edit-intent</code><br/>tool-fs mutations only"]
owned["Tool-owned session events<br/><code>todo/write</code>, <code>fs/observed</code>, <code>hook/invoked</code>, <code>hook/result</code>"]
post["<code>tools/post-execute</code> waterfall<br/>accept, block, replace, add context"]
context["Buffered additionalContext<br/>context/message after all tool results"]
toolResult["Session event: tool/result<br/>single model-facing outcome"]
toolResult["Session event: <code>tool/result</code><br/>single model-facing outcome"]
presentResult["UI completed card<br/>presentResult(args, result)"]
model --> toolCall
toolCall --> presentCall

View File

@@ -93,10 +93,13 @@ describe('gen-tool-catalog render', () => {
{
pkg: '@deepseek-ai/dsh-tool-demo',
source: 'packages/demo/tool-demo/src/index.ts',
requires: ['ctx.tools'],
writes: ['tool/result'],
schemas: [{ name: 'demo', description: 'A demo tool.', parameters: { type: 'object', properties: {} } }],
},
]
const md = render(catalog)
expect(md).toContain('| `@deepseek-ai/dsh-tool-demo` | `demo` | `ctx.tools` | `tool/result` |')
expect(md).toContain('## `@deepseek-ai/dsh-tool-demo`')
expect(md).toContain('### `demo`')
expect(md).toContain('A demo tool.')
@@ -109,6 +112,8 @@ describe('gen-tool-catalog render', () => {
{
pkg: '@deepseek-ai/dsh-tool-demo',
source: 'packages/demo/tool-demo/src/index.ts',
requires: ['ctx.tools'],
writes: ['tool/result'],
schemas: [{ name: 'demo', description: '', parameters: { type: 'object', properties: {} }, strict: true }],
},
]

View File

@@ -1,20 +1,20 @@
/**
* Generate (and verify) the documentation graph atlas in docs/graphs/.
* Generate (and verify) the relationship-diagram docs.
*
* This is the relationship layer above the existing catalogs:
* - module-graph.md answers "which packages depend on which packages?"
* - cordis-catalog/ answers "which events and services exist?"
* - tool-catalog/ answers "which tools does the model see?"
* - docs/graphs/ answers "how do those pieces fit together?"
* - docs/*.md relationship diagrams answer "how do those pieces fit together?"
*
* Generated pages discover the enumerable facts from source. Hybrid pages use
* discovered inventory plus small manifests for policy that source cannot infer
* (for example, whether a package is an implementation or consumer in a seam).
* Curated pages are still emitted here so the atlas is one regenerated unit,
* Curated pages are still emitted here so the graph docs are one regenerated unit,
* but their diagrams intentionally explain flow and ownership rather than
* pretending to enumerate every source edge.
*
* `tsx scripts/gen-doc-graphs.ts` -> write docs/graphs/*.md
* `tsx scripts/gen-doc-graphs.ts` -> write generated diagram docs
* `tsx scripts/gen-doc-graphs.ts --check` -> exit 1 if any file is stale
*/
@@ -22,10 +22,8 @@ import { existsSync, globSync, mkdirSync, readFileSync, writeFileSync } from 'no
import { dirname, resolve } from 'node:path'
import ts from 'typescript'
import { collectEvents, collectServices } from './gen-cordis-catalog.ts'
import { collectToolCatalog } from './gen-tool-catalog.ts'
const root = resolve(import.meta.dirname, '..')
const OUT_DIR = 'docs/graphs'
const SCOPE = '@deepseek-ai/dsh-'
interface PkgJson {
@@ -67,13 +65,6 @@ interface EventRelation {
listeners: Set<string>
}
interface ToolPackageMeta {
requires: string[]
writes: string[]
shippedNames?: string[]
note: string
}
const GROUP_ORDER = [
'util',
'llm',
@@ -197,35 +188,6 @@ const SERVICE_ROLES: ServiceRole[] = [
},
]
const TOOL_PACKAGE_META: Record<string, ToolPackageMeta> = {
'@deepseek-ai/dsh-tool-bash': {
requires: ['ctx.tools', 'ctx.bash'],
writes: ['tool/call', 'tool/result', 'context/message via agent.inject() for background completion notices'],
note: 'The bash/bash_output/bash_kill tools are model-facing consumers of the bash executor seam.',
},
'@deepseek-ai/dsh-tool-fs': {
requires: ['ctx.tools', 'ctx.fs', 'ctx.systemPrompt'],
writes: ['tool/call', 'fs/write-intent or fs/edit-intent for mutations', 'fs/observed after successful file operations', 'tool/result'],
note: 'read/write/edit are the model-facing filesystem tools; read windowing lives here, while read-before-edit policy is supplied by fs-policy through fs/* events.',
},
'@deepseek-ai/dsh-tool-subagent': {
requires: ['ctx.tools', 'ctx.subagents'],
writes: ['tool/call', 'tool/result', 'child session events through the chosen provider'],
shippedNames: ['subagent', 'subagent_fork'],
note: 'The default package schema registers subagent; shipped coding/acp configs load it twice to expose spawn and fork backends.',
},
'@deepseek-ai/dsh-tool-todo': {
requires: ['ctx.tools', 'owning Agent session'],
writes: ['tool/call', 'todo/write', 'tool/result'],
note: 'todo_write is session-owned state; UIs render the latest todo/write event as a checklist or ACP plan.',
},
'@deepseek-ai/dsh-tool-web': {
requires: ['ctx.tools', 'ctx.web', 'ctx.systemPrompt'],
writes: ['tool/call', 'tool/result'],
note: 'web_search and web_fetch keep provider selection behind ctx.web so model-visible schemas stay stable across backend swaps.',
},
}
const DYNAMIC_EVENT_DISPATCHERS: Array<{ event: string; pkg: string; method: string }> = [
// Subagent lifecycle events intentionally bypass ctx.emit and call
// ctx.events.dispatch directly so one throwing listener cannot starve later
@@ -302,8 +264,20 @@ function escLabel(value: string): string {
return value.replace(/"/g, '\\"')
}
function pkgLink(pkg: Pkg | undefined, fallback: string): string {
return pkg ? `[\`${pkg.short}\`](../../${pkg.rel})` : `\`${fallback}\``
function mermaidCode(value: string): string {
return `<code>${value.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;')}</code>`
}
function repoLink(path: string, label: string, up = '..'): string {
return `[${label}](${up}/${path})`
}
function sourceLink(source: string, up = '..'): string {
return repoLink(source.split(':')[0] ?? source, `\`${source}\``, up)
}
function pkgLink(pkg: Pkg | undefined, fallback: string, up = '..'): string {
return pkg ? repoLink(pkg.rel, `\`${pkg.short}\``, up) : `\`${fallback}\``
}
function pkgList(names: string[] | undefined, pkgsByShort: Map<string, Pkg>): string {
@@ -311,52 +285,10 @@ function pkgList(names: string[] | undefined, pkgsByShort: Map<string, Pkg>): st
return names.map(name => pkgLink(pkgsByShort.get(name), name)).join(', ')
}
function codeList(values: string[]): string {
return values.length ? values.map(v => `\`${v}\``).join(', ') : '-'
}
function tableCell(value: string): string {
return value.replace(/\|/g, '\\|').replace(/\n/g, '<br>')
}
function renderMermaidPackageNode(pkg: Pkg): string {
return ` ${nodeId('pkg', pkg.short)}["${escLabel(pkg.short)}"]`
}
function renderPackageTopology(pkgs: Pkg[]): string {
const lines = generatedHeader('Package Topology By Group', 'generated from `packages/*/*/package.json` peer dependencies plus package group paths')
lines.push(
'This graph complements [module-graph.md](../module-graph.md): it keeps the same canonical peer-dependency edge source, but clusters packages by the `packages/<group>/<pkg>` hierarchy so layering and capability families are easier to scan.',
'',
'```mermaid',
'flowchart TD',
)
const groups = [...new Set(pkgs.map(pkg => pkg.group))].sort((a, b) => {
const ia = GROUP_ORDER.indexOf(a)
const ib = GROUP_ORDER.indexOf(b)
const na = ia === -1 ? Number.MAX_SAFE_INTEGER : ia
const nb = ib === -1 ? Number.MAX_SAFE_INTEGER : ib
return na - nb || a.localeCompare(b)
})
for (const group of groups) {
lines.push(` subgraph ${nodeId('group', group)}["packages/${escLabel(group)}"]`)
for (const pkg of pkgs.filter(p => p.group === group).sort((a, b) => a.short.localeCompare(b.short))) {
lines.push(renderMermaidPackageNode(pkg))
}
lines.push(' end')
}
for (const pkg of pkgs) {
for (const dep of pkg.deps) lines.push(` ${nodeId('pkg', pkg.short)} --> ${nodeId('pkg', dep)}`)
}
lines.push('```', '', '| Package | Group | Depends on |', '| --- | --- | --- |')
const byShort = new Map(pkgs.map(pkg => [pkg.short, pkg]))
for (const pkg of pkgs) {
lines.push(`| ${pkgLink(pkg, pkg.short)} | \`${pkg.group}\` | ${pkg.deps.length ? pkg.deps.map(dep => pkgLink(byShort.get(dep), dep)).join(', ') : '-'} |`)
}
lines.push('')
return lines.join('\n')
}
function assertServiceRolesComplete(): void {
const discovered = new Set(collectServices().map(service => service.key))
const classified = new Set(SERVICE_ROLES.map(role => role.key))
@@ -440,56 +372,81 @@ function stripYamlScalar(value: string): string {
return value.trim().replace(/^['"]|['"]$/g, '')
}
function renderAppComposition(): string {
const examples = [
{ id: 'echo', label: 'examples/echo-agent', config: 'examples/echo-agent/cordis.yml' },
{ id: 'coding', label: 'examples/coding-agent', config: 'examples/coding-agent/cordis.yml' },
{ id: 'acp', label: 'examples/acp-agent', config: 'examples/acp-agent/cordis.yml' },
]
const lines = generatedHeader('App Composition', 'hybrid: leaf plugin lists are parsed from `examples/*/cordis.yml`; bundle expansions are curated from app package source')
const APP_EXAMPLES = [
{
id: 'echo',
rel: 'docs/echo-agent-composition.md',
title: 'Echo Agent App Composition',
label: 'examples/echo-agent',
config: 'examples/echo-agent/cordis.yml',
summary: 'The echo demo swaps in a local mock LLM and teaching echo tool, then loads the stdio app package for the shared spine and terminal front door.',
},
{
id: 'coding',
rel: 'docs/coding-agent-composition.md',
title: 'Coding Agent App Composition',
label: 'examples/coding-agent',
config: 'examples/coding-agent/cordis.yml',
summary: 'The coding REPL demo adds the real DeepSeek adapter, filesystem tools, todo_write, compaction, and both subagent transports on top of the stdio app package.',
},
{
id: 'acp',
rel: 'docs/acp-agent-composition.md',
title: 'ACP Agent App Composition',
label: 'examples/acp-agent',
config: 'examples/acp-agent/cordis.yml',
summary: 'The ACP demo exposes the same agent spine over JSON-RPC stdio, with no stdout logger and no pre-created agent; clients create sessions through the ACP bridge.',
},
]
type AppExample = typeof APP_EXAMPLES[number]
function renderAppExpansion(lines: string[], appNode: string, pluginName: string): void {
const agentCore = nodeId('bundle', 'agent_core')
const jsonl = nodeId('bundle', 'jsonl')
lines.push(` ${appNode} --> ${agentCore}["@deepseek-ai/dsh-agent-core"]`)
lines.push(` ${appNode} --> ${jsonl}["@deepseek-ai/dsh-session-persistence-jsonl"]`)
if (pluginName === '@deepseek-ai/dsh-stdio-agent') {
lines.push(` ${appNode} --> ${nodeId('frontdoor', 'stdio')}["readline UI<br/>console logger<br/>pre-created main agent"]`)
} else if (pluginName === '@deepseek-ai/dsh-acp-agent') {
lines.push(` ${appNode} --> ${nodeId('frontdoor', 'acp')}["@deepseek-ai/dsh-acp<br/>JSON-RPC stdio bridge<br/>sessions created by client"]`)
}
lines.push(
'This graph is for SDK users asking which pieces a runnable agent loads. Leaf configs choose adapters and optional product tools; app packages provide the front door; `dsh-agent-core` bundles the providerless spine.',
` ${agentCore} --> ${nodeId('spine', 'llm')}["ctx.llm"]`,
` ${agentCore} --> ${nodeId('spine', 'sessions')}["ctx.sessions"]`,
` ${agentCore} --> ${nodeId('spine', 'tools')}["ctx.tools + tool-bash"]`,
` ${agentCore} --> ${nodeId('spine', 'loop')}["ctx.agents + ctx.agentLoop"]`,
)
}
function renderAppComposition(example: AppExample): string {
const plugins = parseExampleCordis(example.config)
const lines = generatedHeader(example.title, 'hybrid: the leaf plugin list is parsed from its `cordis.yml`; app package expansion is curated from package source')
lines.push(
example.summary,
'',
'```mermaid',
'flowchart LR',
` cfg["${escLabel(example.label)}<br/>cordis.yml"]`,
)
const bundleTargets: Record<string, string> = {
'@deepseek-ai/dsh-stdio-agent': nodeId('bundle', 'stdio'),
'@deepseek-ai/dsh-acp-agent': nodeId('bundle', 'acp_agent'),
}
for (const example of examples) {
lines.push(` subgraph ${nodeId('example', example.id)}["${escLabel(example.label)}"]`)
lines.push(` ${nodeId('cfg', example.id)}["cordis.yml"]`)
for (const plugin of parseExampleCordis(example.config)) {
const pluginNode = nodeId(`plugin_${example.id}`, plugin.id)
lines.push(` ${pluginNode}["${escLabel(plugin.id)}<br/>${escLabel(plugin.name)}"]`)
lines.push(` ${nodeId('cfg', example.id)} --> ${pluginNode}`)
const bundle = bundleTargets[plugin.name]
if (bundle !== undefined) lines.push(` ${pluginNode} --> ${bundle}`)
for (const plugin of plugins) {
const pluginNode = nodeId(`plugin_${example.id}`, plugin.id)
lines.push(` ${pluginNode}["${escLabel(plugin.id)}<br/>${escLabel(plugin.name)}"]`)
lines.push(` cfg --> ${pluginNode}`)
if (plugin.name === '@deepseek-ai/dsh-stdio-agent' || plugin.name === '@deepseek-ai/dsh-acp-agent') {
renderAppExpansion(lines, pluginNode, plugin.name)
}
lines.push(' end')
}
lines.push(
` ${nodeId('bundle', 'stdio')}["@deepseek-ai/dsh-stdio-agent"] --> ${nodeId('bundle', 'agent_core')}["@deepseek-ai/dsh-agent-core"]`,
` ${nodeId('bundle', 'stdio')} --> ${nodeId('bundle', 'jsonl')}["@deepseek-ai/dsh-session-persistence-jsonl"]`,
` ${nodeId('bundle', 'stdio')} --> ${nodeId('bundle', 'ui_stdio')}["@deepseek-ai/dsh-ui-stdio"]`,
` ${nodeId('bundle', 'acp_agent')}["@deepseek-ai/dsh-acp-agent"] --> ${nodeId('bundle', 'agent_core')}`,
` ${nodeId('bundle', 'acp_agent')} --> ${nodeId('bundle', 'jsonl')}`,
` ${nodeId('bundle', 'acp_agent')} --> ${nodeId('bundle', 'acp')}["@deepseek-ai/dsh-acp"]`,
` ${nodeId('bundle', 'agent_core')} --> ${nodeId('spine', 'llm')}["ctx.llm"]`,
` ${nodeId('bundle', 'agent_core')} --> ${nodeId('spine', 'sessions')}["ctx.sessions"]`,
` ${nodeId('bundle', 'agent_core')} --> ${nodeId('spine', 'tools')}["ctx.tools + tool-bash"]`,
` ${nodeId('bundle', 'agent_core')} --> ${nodeId('spine', 'loop')}["ctx.agents + ctx.agentLoop"]`,
'```',
'',
'| Example | Parsed plugin ids | Config |',
'| --- | --- | --- |',
'| Plugin id | Package / module |',
'| --- | --- |',
...plugins.map(plugin => `| \`${plugin.id}\` | \`${plugin.name}\` |`),
'',
`Source config: ${repoLink(example.config, `\`${example.config}\``, '..')}.`,
'',
)
for (const example of examples) {
const plugins = parseExampleCordis(example.config)
lines.push(`| \`${example.label}\` | ${plugins.map(plugin => `\`${plugin.id}\``).join(', ')} | [\`${example.config}\`](../../${example.config}) |`)
}
lines.push('')
return lines.join('\n')
}
@@ -580,7 +537,7 @@ function renderEventRelations(pkgs: Pkg[]): string {
)
for (const event of [...events].sort((a, b) => a.name.localeCompare(b.name))) {
const relation = relations.get(event.name) ?? { dispatchers: new Map<string, Set<string>>(), listeners: new Set<string>() }
lines.push(`| \`${event.name}\` | \`${event.mode}\` | [\`${event.source}\`](../../${event.source.split(':')[0]}) | ${relationPackages(relation.dispatchers, pkgsByShort)} | ${listenerPackages(relation.listeners, pkgsByShort)} |`)
lines.push(`| \`${event.name}\` | \`${event.mode}\` | ${sourceLink(event.source)} | ${relationPackages(relation.dispatchers, pkgsByShort)} | ${listenerPackages(relation.listeners, pkgsByShort)} |`)
}
const declared = new Set(events.map(event => event.name))
const extra = [...relations.keys()].filter(event => !declared.has(event)).sort()
@@ -596,52 +553,10 @@ function renderEventRelations(pkgs: Pkg[]): string {
return lines.join('\n')
}
async function renderToolAffordance(): Promise<string> {
const catalog = await collectToolCatalog()
const lines = generatedHeader('Tool Affordance Map', 'hybrid: tool names/schemas are boot-harvested from shipped tool plugins; required services and shipped aliases are classified in `scripts/gen-doc-graphs.ts` with a completeness guard')
for (const entry of catalog) {
if (!TOOL_PACKAGE_META[entry.pkg]) {
throw new Error(`gen-doc-graphs: tool package ${entry.pkg} is missing TOOL_PACKAGE_META classification`)
}
}
lines.push(
'This page connects the model-visible tools to the plugin packages and service seams behind them. For exact JSON Schemas, see [tool-catalog/tools.md](../tool-catalog/tools.md).',
'',
'```mermaid',
'flowchart LR',
' model["Model request tools[]"]',
)
const requirementNodes = new Set<string>()
for (const entry of catalog) {
const meta = TOOL_PACKAGE_META[entry.pkg]
if (!meta) continue
const packageNode = nodeId('toolpkg', entry.pkg)
const names = entry.schemas.map(schema => schema.name).join(', ')
lines.push(` ${packageNode}["${escLabel(entry.pkg.replace(SCOPE, ''))}<br/>${escLabel(names)}"]`)
lines.push(` model --> ${packageNode}`)
for (const req of meta.requires) {
const reqNode = nodeId('requires', req)
if (!requirementNodes.has(reqNode)) {
lines.push(` ${reqNode}["${escLabel(req)}"]`)
requirementNodes.add(reqNode)
}
lines.push(` ${packageNode} --> ${reqNode}`)
}
}
lines.push('```', '', '| Tool package | Model-visible names | Requires | Writes / affects | Shipped aliases | Note |', '| --- | --- | --- | --- | --- | --- |')
for (const entry of catalog) {
const meta = TOOL_PACKAGE_META[entry.pkg]
if (!meta) continue
lines.push(`| \`${entry.pkg}\` | ${codeList(entry.schemas.map(schema => schema.name))} | ${codeList(meta.requires)} | ${codeList(meta.writes)} | ${codeList(meta.shippedNames ?? [])} | ${tableCell(meta.note)} |`)
}
lines.push('')
return lines.join('\n')
}
function renderLifecycle(): string {
return [
...generatedHeader('Agent Turn And Step Lifecycle', 'curated Mermaid sequence; exact event signatures live in the generated Cordis catalog'),
'This sequence is the visual companion to [architecture.md](../architecture.md#loop-lifecycle-session--turn--step). It keeps durable replay facts on `session/event` and live control/status on `agent/*`.',
'This sequence is the visual companion to [architecture.md](architecture.md#loop-lifecycle-session--turn--step). It keeps durable replay facts on `session/event` and live control/status on `agent/*`.',
'',
'```mermaid',
'sequenceDiagram',
@@ -656,30 +571,30 @@ function renderLifecycle(): string {
' participant Persistence',
' participant SDK as UI or SDK listener',
' User->>Agent: send(content)',
' Agent-->>SDK: agent/queued',
` Agent-->>SDK: ${mermaidCode('agent/queued')}`,
' Agent->>Driver: queued work wakes driver',
' Driver-->>SDK: agent/status running',
' Driver->>Session: turn/start',
' Driver->>Hooks: agent/prompt-submit waterfall',
` Driver-->>SDK: ${mermaidCode('agent/status')} running`,
` Driver->>Session: ${mermaidCode('turn/start')}`,
` Driver->>Hooks: ${mermaidCode('agent/prompt-submit')} waterfall`,
' Hooks-->>Driver: allow, block, or add context',
' Driver->>Session: user/message or rejected turn/end',
' Driver->>Prompt: system-prompt/assemble waterfall',
' Driver-->>Driver: agent/pre-step serial checkpoint',
' Driver->>Session: step/start',
' Driver->>LLM: agent/request waterfall, then llm/stream waterfall',
` Driver->>Session: ${mermaidCode('user/message')} or rejected ${mermaidCode('turn/end')}`,
` Driver->>Prompt: ${mermaidCode('system-prompt/assemble')} waterfall`,
` Driver-->>Driver: ${mermaidCode('agent/pre-step')} serial checkpoint`,
` Driver->>Session: ${mermaidCode('step/start')}`,
` Driver->>LLM: ${mermaidCode('agent/request')} waterfall, then ${mermaidCode('llm/stream')} waterfall`,
' LLM-->>Driver: StreamChunk*',
' Driver->>Session: assistant/chunk*',
' Session-->>SDK: session/event assistant/chunk*',
' Driver->>Hooks: agent/step-result waterfall',
' Driver->>Session: assistant/message',
' Driver->>Session: tool/call',
` Driver->>Session: ${mermaidCode('assistant/chunk')}*`,
` Session-->>SDK: ${mermaidCode('session/event')} ${mermaidCode('assistant/chunk')}*`,
` Driver->>Hooks: ${mermaidCode('agent/step-result')} waterfall`,
` Driver->>Session: ${mermaidCode('assistant/message')}`,
` Driver->>Session: ${mermaidCode('tool/call')}`,
' Driver->>Tools: execute through pre and post waterfalls',
' Tools-->>Session: tool-owned events when applicable',
' Driver->>Session: tool/result and step/end',
' Driver->>Hooks: agent/turn-continuation waterfall',
' Driver->>Session: turn/end',
' Driver->>Persistence: session/flush parallel checkpoint',
' Driver-->>SDK: agent/status idle',
` Driver->>Session: ${mermaidCode('tool/result')} and ${mermaidCode('step/end')}`,
` Driver->>Hooks: ${mermaidCode('agent/turn-continuation')} waterfall`,
` Driver->>Session: ${mermaidCode('turn/end')}`,
` Driver->>Persistence: ${mermaidCode('session/flush')} parallel checkpoint`,
` Driver-->>SDK: ${mermaidCode('agent/status')} idle`,
'```',
'',
'SDK users that need replayable transcript data should consume `session/event`; `agent/*` is the live coordination surface for queue/status, prompt interception, request shaping, steering, continuation, and errors.',
@@ -695,16 +610,16 @@ function renderToolPipeline(): string {
'```mermaid',
'flowchart TD',
' model["Assistant message contains tool-call block"]',
' toolCall["Session event: tool/call<br/>logged before execution"]',
` toolCall["Session event: ${mermaidCode('tool/call')}<br/>logged before execution"]`,
' presentCall["UI pending card<br/>presentCall(args)"]',
' pre["tools/pre-execute waterfall<br/>hooks, permission, sandbox"]',
` pre["${mermaidCode('tools/pre-execute')} waterfall<br/>hooks, permission, sandbox"]`,
' denied["deny or ask<br/>tool body skipped"]',
' toolBody["Registered tool execute() body"]',
' fsGate["fs/write-intent or fs/edit-intent<br/>tool-fs mutations only"]',
' owned["Tool-owned session events<br/>todo/write, fs/observed, hook/invoked, hook/result"]',
' post["tools/post-execute waterfall<br/>accept, block, replace, add context"]',
` fsGate["${mermaidCode('fs/write-intent')} or ${mermaidCode('fs/edit-intent')}<br/>tool-fs mutations only"]`,
` owned["Tool-owned session events<br/>${mermaidCode('todo/write')}, ${mermaidCode('fs/observed')}, ${mermaidCode('hook/invoked')}, ${mermaidCode('hook/result')}"]`,
` post["${mermaidCode('tools/post-execute')} waterfall<br/>accept, block, replace, add context"]`,
' context["Buffered additionalContext<br/>context/message after all tool results"]',
' toolResult["Session event: tool/result<br/>single model-facing outcome"]',
` toolResult["Session event: ${mermaidCode('tool/result')}<br/>single model-facing outcome"]`,
' presentResult["UI completed card<br/>presentResult(args, result)"]',
' model --> toolCall',
' toolCall --> presentCall',
@@ -726,82 +641,6 @@ function renderToolPipeline(): string {
].join('\n')
}
function renderSessionSurface(): string {
return [
...generatedHeader('Session Surface And Message Projection', 'curated Mermaid dataflow; exact event/type shapes live in core-data-structures'),
'This graph separates the append-only log from the derived message surface the next model request sees.',
'',
'```mermaid',
'flowchart LR',
' append["Session.append(type, data)"]',
' log["Append-only SessionEvent log"]',
' surface["SurfaceManager linked list<br/>surfaceOp + sourceEventSeqs"]',
' derive["deriveMessages()"]',
' model["GenerateOptions.messages"]',
' persist["JSONL / SQLite persistence"]',
' replay["load / replay / fork seed"]',
' append --> log',
' log --> surface',
' surface --> derive --> model',
' log --> persist --> replay --> log',
'```',
'',
'See [core-data-structures/session.md](../core-data-structures/session.md) for the full `SessionEventMap`, surface operations, and turn-enclosure invariant.',
'',
].join('\n')
}
function renderSubagentLineage(): string {
return [
...generatedHeader('Subagent And Session Lineage', 'curated Mermaid flow; provider inventory is visible in the generated capability seam graph'),
'This graph keeps delegation semantics separate from hook observation. A subagent backend creates an ordinary child agent/session through the shared provider registry.',
'',
'```mermaid',
'flowchart TD',
' parent["Parent Agent + Session"]',
' tool["tool-subagent<br/>model-facing name"]',
' registry["ctx.subagents provider registry"]',
' spawn["spawn provider<br/>fresh child session"]',
' fork["fork provider<br/>seeded from completed-turn prefix"]',
' acp["ACP provider<br/>out-of-process child"]',
' child["Child AgentHandle<br/>ordinary Agent lifecycle"]',
' result["SubagentResult returned to tool"]',
' parent --> tool --> registry',
' registry --> spawn --> child',
' registry --> fork --> child',
' registry --> acp --> child',
' child --> result --> parent',
'```',
'',
'The hooks stack adds richer lifecycle observation around child runs; the core ownership rule stays the same: the provider owns the child handle and must dispose it.',
'',
].join('\n')
}
function renderHotReload(): string {
return [
...generatedHeader('Plugin Disposal And Hot Reload Ownership', 'curated Mermaid flow based on Cordis fiber/effect conventions'),
'This graph is a maintainer checklist for plugin authors: registrations are effects, service injection gates activation, and owned handles must be disposed by their owner.',
'',
'```mermaid',
'flowchart TD',
' plugin["ctx.plugin(plugin) creates fiber"]',
' inject["static inject gates activation"]',
' service["ctx.provide / Service constructor"]',
' effects["ctx.effect registrations<br/>events, tools, adapters, timers"]',
' reload["HMR / fiber.dispose()"]',
' disposers["Run disposers in owner fiber"]',
' quiescence["Owned AgentHandle.dispose()<br/>or service teardown awaits quiescence"]',
' plugin --> inject --> service',
' inject --> effects',
' reload --> disposers --> quiescence',
'```',
'',
'Hook bridges and SDK plugins increase the number of long-lived listeners, so this ownership graph should stay small and visible.',
'',
].join('\n')
}
function renderSnapshotReplay(): string {
return [
...generatedHeader('ACP Snapshot Replay', 'curated Mermaid sequence based on the snapshot test harness'),
@@ -818,7 +657,7 @@ function renderSnapshotReplay(): string {
' Recorder->>Fixture: session.jsonl + workspace inputs',
' Fixture->>Workspace: seed files and hook configs',
' Fixture->>Replay: recorded StreamChunk script',
' Replay->>ACP: deterministic llm/stream chunks',
` Replay->>ACP: deterministic ${mermaidCode('llm/stream')} chunks`,
' ACP->>Workspace: bash, fs, and hook side effects',
' ACP->>Golden: normalized sessionUpdate stream',
' Golden-->>ACP: diff must be empty',
@@ -829,72 +668,66 @@ function renderSnapshotReplay(): string {
].join('\n')
}
async function renderDocs(): Promise<GraphDoc[]> {
function renderDocs(): GraphDoc[] {
const pkgs = collectPackages()
const docs: GraphDoc[] = [
{ rel: `${OUT_DIR}/package-topology.md`, content: renderPackageTopology(pkgs) },
{ rel: `${OUT_DIR}/capability-seams.md`, content: renderCapabilitySeams(pkgs) },
{ rel: `${OUT_DIR}/app-composition.md`, content: renderAppComposition() },
{ rel: `${OUT_DIR}/event-producer-consumer.md`, content: renderEventRelations(pkgs) },
{ rel: `${OUT_DIR}/tool-affordance-map.md`, content: await renderToolAffordance() },
{ rel: `${OUT_DIR}/agent-lifecycle.md`, content: renderLifecycle() },
{ rel: `${OUT_DIR}/tool-execution-pipeline.md`, content: renderToolPipeline() },
{ rel: `${OUT_DIR}/session-surface.md`, content: renderSessionSurface() },
{ rel: `${OUT_DIR}/subagent-lineage.md`, content: renderSubagentLineage() },
{ rel: `${OUT_DIR}/hot-reload-disposal.md`, content: renderHotReload() },
{ rel: `${OUT_DIR}/snapshot-replay.md`, content: renderSnapshotReplay() },
{ rel: 'docs/capability-seams.md', content: renderCapabilitySeams(pkgs) },
...APP_EXAMPLES.map(example => ({ rel: example.rel, content: renderAppComposition(example) })),
{ rel: 'docs/event-producer-consumer.md', content: renderEventRelations(pkgs) },
{ rel: 'docs/agent-lifecycle.md', content: renderLifecycle() },
{ rel: 'docs/tool-execution-pipeline.md', content: renderToolPipeline() },
{ rel: 'docs/acp/snapshot-replay.md', content: renderSnapshotReplay() },
]
docs.unshift({ rel: `${OUT_DIR}/README.md`, content: renderIndex(docs) })
docs.unshift({ rel: 'docs/graph-atlas.md', content: renderIndex(docs) })
return docs
}
function renderIndex(docs: GraphDoc[]): string {
const labels: Record<string, string> = {
'package-topology.md': 'package topology by group',
'capability-seams.md': 'capability seams and core services',
'app-composition.md': 'app composition',
'event-producer-consumer.md': 'event producer/consumer matrix',
'tool-affordance-map.md': 'tool affordance map',
'agent-lifecycle.md': 'agent turn and step lifecycle',
'tool-execution-pipeline.md': 'tool execution pipeline',
'session-surface.md': 'session surface and message projection',
'subagent-lineage.md': 'subagent and session lineage',
'hot-reload-disposal.md': 'plugin disposal and hot reload ownership',
'snapshot-replay.md': 'ACP snapshot replay',
'docs/capability-seams.md': 'capability seams and core services',
'docs/echo-agent-composition.md': 'echo-agent app composition',
'docs/coding-agent-composition.md': 'coding-agent app composition',
'docs/acp-agent-composition.md': 'acp-agent app composition',
'docs/event-producer-consumer.md': 'event producer/consumer matrix',
'docs/agent-lifecycle.md': 'agent turn and step lifecycle',
'docs/tool-execution-pipeline.md': 'tool execution pipeline',
'docs/acp/snapshot-replay.md': 'ACP snapshot replay',
}
const modes: Record<string, string> = {
'package-topology.md': 'generated',
'capability-seams.md': 'hybrid generated',
'app-composition.md': 'hybrid generated',
'event-producer-consumer.md': 'hybrid generated',
'tool-affordance-map.md': 'hybrid generated',
'agent-lifecycle.md': 'curated',
'tool-execution-pipeline.md': 'curated',
'session-surface.md': 'curated',
'subagent-lineage.md': 'curated',
'hot-reload-disposal.md': 'curated',
'snapshot-replay.md': 'curated',
'docs/capability-seams.md': 'hybrid generated',
'docs/echo-agent-composition.md': 'hybrid generated',
'docs/coding-agent-composition.md': 'hybrid generated',
'docs/acp-agent-composition.md': 'hybrid generated',
'docs/event-producer-consumer.md': 'hybrid generated',
'docs/agent-lifecycle.md': 'curated',
'docs/tool-execution-pipeline.md': 'curated',
'docs/acp/snapshot-replay.md': 'curated',
}
const rows = [
'| [module dependency graph](module-graph.md) | `generated` |',
'| [tool schema catalog and package map](tool-catalog/tools.md) | `generated` |',
...docs.map((doc) => {
const link = doc.rel.replace(/^docs\//, '')
return `| [${labels[doc.rel] ?? link}](${link}) | \`${modes[doc.rel] ?? 'generated'}\` |`
}),
]
return [
...generatedHeader('Documentation Graph Atlas', 'mixed: each linked page declares generated, hybrid, or curated mode'),
'The graph atlas is the relationship layer above the generated catalogs. Use it to navigate package topology, capability seams, event flow, model-facing tools, and runtime lifecycle paths. Exact signatures and type shapes still live in [cordis-catalog/](../cordis-catalog/events-and-services.md), [tool-catalog/](../tool-catalog/tools.md), and [core-data-structures/](../core-data-structures/core.md).',
...generatedHeader('Documentation Graph Index', 'mixed: each linked page declares generated, hybrid, or curated mode'),
'These diagrams are the relationship layer above the generated catalogs. Use them to navigate package topology, capability seams, event flow, model-facing tools, app composition, and runtime lifecycle paths. Exact signatures and type shapes still live in [cordis-catalog/](cordis-catalog/events-and-services.md), [tool-catalog/](tool-catalog/tools.md), and [core-data-structures/](core-data-structures/core.md).',
'',
'The process decision behind this atlas is recorded in [the documentation graph atlas RFC](../rfc/implemented/process/2026-07-03-documentation-graph-atlas.md).',
'The process decision behind this index is recorded in [the documentation graph RFC](rfc/implemented/process/2026-07-03-documentation-graph-atlas.md).',
'',
'| Graph | Mode |',
'| --- | --- |',
...docs.map((doc) => {
const file = doc.rel.split('/').at(-1) ?? doc.rel
return `| [${labels[file] ?? file}](${file}) | \`${modes[file] ?? 'generated'}\` |`
}),
...rows,
'',
'Regenerate with `pnpm run gen-doc-graphs`; verify freshness with `pnpm run verify-doc-graphs`.',
'',
].join('\n')
}
async function main(): Promise<void> {
const docs = await renderDocs()
function main(): void {
const docs = renderDocs()
if (process.argv.includes('--check')) {
const stale: string[] = []
for (const doc of docs) {
@@ -910,11 +743,13 @@ async function main(): Promise<void> {
process.exit(1)
}
mkdirSync(resolve(root, OUT_DIR), { recursive: true })
for (const doc of docs) writeFileSync(resolve(root, doc.rel), doc.content)
for (const doc of docs) {
mkdirSync(dirname(resolve(root, doc.rel)), { recursive: true })
writeFileSync(resolve(root, doc.rel), doc.content)
}
console.log(`gen-doc-graphs: wrote ${docs.length} graph doc(s).`)
}
if (process.argv[1] && import.meta.filename === resolve(process.argv[1])) {
await main()
main()
}

View File

@@ -6,7 +6,8 @@
* these as `workspace:^` plus test-only extras, which would add noise). This
* script reads every `packages/* /* /package.json`, keeps only the
* `@deepseek-ai/dsh-*` peer edges (dropping the `cordis` peer), and renders a
* GitHub-viewable Mermaid graph plus a dependency table.
* GitHub-viewable Mermaid graph grouped by `packages/<group>/` plus a
* dependency table.
*
* The file is fully generated — never hand-edit it. Output is deterministic
* (packages and edges sorted) so a regenerate-and-diff freshness check is
@@ -17,8 +18,8 @@
* is stale (CI / pre-push gate)
*/
import { dirname, resolve } from 'node:path'
import { globSync, readFileSync, writeFileSync } from 'node:fs'
import { resolve } from 'node:path'
const root = resolve(import.meta.dirname, '..')
const OUT = 'docs/module-graph.md'
@@ -27,10 +28,30 @@ const SCOPE = '@deepseek-ai/dsh-'
interface Pkg {
/** Short name, `@deepseek-ai/dsh-` prefix stripped (e.g. `agent-loop`). */
short: string
/** Package group from `packages/<group>/<pkg>`. */
group: string
/** Repo-relative package directory. */
rel: string
/** Short names of this package's in-repo peer dependencies, sorted. */
deps: string[]
}
const GROUP_ORDER = [
'util',
'llm',
'core',
'bash',
'fs',
'compact',
'subagent',
'web',
'todo',
'hooks',
'session-persistence',
'support',
'ui',
]
/** Read every workspace package and its `@deepseek-ai/dsh-*` peer edges. */
function collect(): Pkg[] {
const pkgs: Pkg[] = []
@@ -44,7 +65,9 @@ function collect(): Pkg[] {
.filter(d => d.startsWith(SCOPE))
.map(d => d.slice(SCOPE.length))
.sort()
pkgs.push({ short: json.name.slice(SCOPE.length), deps })
const [, group, leaf] = rel.split('/')
if (group === undefined || leaf === undefined) throw new Error(`gen-module-graph: unexpected package path ${rel}`)
pkgs.push({ short: json.name.slice(SCOPE.length), group, rel: dirname(rel), deps })
}
return topoSort(pkgs)
}
@@ -63,7 +86,7 @@ function topoSort(pkgs: Pkg[]): Pkg[] {
while (remaining.size > 0) {
const ready = [...remaining.values()]
.filter(p => p.deps.every(d => placed.has(d)))
.sort((a, b) => a.short.localeCompare(b.short))
.sort(comparePackages)
if (ready.length === 0) throw new Error(`gen-module-graph: dependency cycle among ${[...remaining.keys()].join(', ')}`)
for (const p of ready) {
out.push(p)
@@ -74,28 +97,71 @@ function topoSort(pkgs: Pkg[]): Pkg[] {
return out
}
function comparePackages(a: Pkg, b: Pkg): number {
const groupA = GROUP_ORDER.indexOf(a.group)
const groupB = GROUP_ORDER.indexOf(b.group)
const normA = groupA === -1 ? Number.MAX_SAFE_INTEGER : groupA
const normB = groupB === -1 ? Number.MAX_SAFE_INTEGER : groupB
return normA - normB || a.group.localeCompare(b.group) || a.short.localeCompare(b.short)
}
function nodeId(prefix: string, value: string): string {
return `${prefix}_${value.replace(/[^a-zA-Z0-9_]/g, '_')}`
}
function escLabel(value: string): string {
return value.replace(/"/g, '\\"')
}
function packageLink(pkg: Pkg): string {
return `[\`${pkg.short}\`](../${pkg.rel})`
}
/** Render the full docs/module-graph.md content (pure, deterministic). */
function render(pkgs: Pkg[]): string {
const edges: string[] = []
for (const p of pkgs) {
for (const d of p.deps) edges.push(` ${p.short} --> ${d}`)
for (const d of p.deps) edges.push(` ${nodeId('pkg', p.short)} --> ${nodeId('pkg', d)}`)
}
const rows = pkgs.map(p => `| \`${p.short}\` | ${p.deps.length ? p.deps.map(d => `\`${d}\``).join(', ') : '—'} |`)
const byShort = new Map(pkgs.map(pkg => [pkg.short, pkg]))
const groups = [...new Set(pkgs.map(pkg => pkg.group))].sort((a, b) => {
const ia = GROUP_ORDER.indexOf(a)
const ib = GROUP_ORDER.indexOf(b)
const na = ia === -1 ? Number.MAX_SAFE_INTEGER : ia
const nb = ib === -1 ? Number.MAX_SAFE_INTEGER : ib
return na - nb || a.localeCompare(b)
})
const groupBlocks: string[] = []
for (const group of groups) {
groupBlocks.push(` subgraph ${nodeId('group', group)}["packages/${escLabel(group)}"]`)
for (const pkg of pkgs.filter(p => p.group === group).sort((a, b) => a.short.localeCompare(b.short))) {
groupBlocks.push(` ${nodeId('pkg', pkg.short)}["${escLabel(pkg.short)}"]`)
}
groupBlocks.push(' end')
}
const rows = pkgs.map((p) => {
const deps = p.deps.length ? p.deps.map((d) => {
const dep = byShort.get(d)
return dep ? packageLink(dep) : `\`${d}\``
}).join(', ') : '—'
return `| ${packageLink(p)} | \`${p.group}\` | ${deps} |`
})
return [
'<!-- Generated by scripts/gen-module-graph.ts — do not edit by hand.',
' Run `pnpm run gen-module-graph` to regenerate. -->',
'',
'# Module dependency graph',
'',
'Inter-package dependencies among the `@deepseek-ai/dsh-*` harness packages, derived from each package\'s `peerDependencies` (the canonical runtime-dependency signal). An edge `a --> b` means package `a` depends on package `b`. Names have the `@deepseek-ai/dsh-` prefix stripped.',
'Inter-package dependencies among the `@deepseek-ai/dsh-*` harness packages, derived from each package\'s `peerDependencies` (the canonical runtime-dependency signal) and grouped by the `packages/<group>/<pkg>` hierarchy. An edge `a --> b` means package `a` depends on package `b`. Names have the `@deepseek-ai/dsh-` prefix stripped.',
'',
'```mermaid',
'graph TD',
'flowchart TD',
...groupBlocks,
...edges,
'```',
'',
'| Package | Depends on |',
'| --- | --- |',
'| Package | Group | Depends on |',
'| --- | --- | --- |',
...rows,
'',
].join('\n')

View File

@@ -75,6 +75,12 @@ interface ToolPackage {
dir: string
/** Repo-relative source path linked from the catalog entry. */
source: string
/** Services or owning runtime surfaces the package requires at execution time. */
requires: string[]
/** Session events or other visible state the tools write or affect. */
writes: string[]
/** Additional model-visible names shipped by example/app config. */
shippedNames?: string[]
/** Plug the injected seams + the tool plugin onto a context that already
* carries `systemPrompt` + `tools`. */
mount: (ctx: Context) => Promise<void>
@@ -98,15 +104,21 @@ const TOOL_PACKAGES: ToolPackage[] = [
pkg: '@deepseek-ai/dsh-tool-bash',
dir: 'tool-bash',
source: 'packages/bash/tool-bash/src/index.ts',
requires: ['ctx.tools', 'ctx.bash'],
writes: ['tool/call', 'tool/result', 'context/message via agent.inject() for background completion notices'],
async mount(ctx) {
await ctx.plugin(LocalBashExecutor)
await ctx.plugin(ToolBash)
},
note:
'The bash/bash_output/bash_kill tools are model-facing consumers of the bash executor seam.',
},
{
pkg: '@deepseek-ai/dsh-tool-fs',
dir: 'tool-fs',
source: 'packages/fs/tool-fs/src/index.ts',
requires: ['ctx.tools', 'ctx.fs', 'ctx.systemPrompt'],
writes: ['tool/call', 'fs/write-intent or fs/edit-intent for mutations', 'fs/observed after successful file operations', 'tool/result'],
async mount(ctx) {
// The tool injects `fs`; boot the local backend to satisfy it. The schemas
// do not depend on the policy plugin (an event gate that changes behavior,
@@ -121,6 +133,9 @@ const TOOL_PACKAGES: ToolPackage[] = [
pkg: '@deepseek-ai/dsh-tool-subagent',
dir: 'tool-subagent',
source: 'packages/subagent/tool-subagent/src/index.ts',
requires: ['ctx.tools', 'ctx.subagents'],
writes: ['tool/call', 'tool/result', 'child session events through the chosen provider'],
shippedNames: ['subagent', 'subagent_fork'],
async mount(ctx) {
await ctx.plugin(SubagentService)
// Register a scripted provider under the name the tool delegates to.
@@ -134,14 +149,20 @@ const TOOL_PACKAGES: ToolPackage[] = [
pkg: '@deepseek-ai/dsh-tool-todo',
dir: 'tool-todo',
source: 'packages/todo/tool-todo/src/index.ts',
requires: ['ctx.tools', 'owning Agent session'],
writes: ['tool/call', 'todo/write', 'tool/result'],
async mount(ctx) {
await ctx.plugin(ToolTodo)
},
note:
'todo_write is session-owned state; UIs render the latest todo/write event as a checklist or ACP plan.',
},
{
pkg: '@deepseek-ai/dsh-tool-web',
dir: 'tool-web',
source: 'packages/web/tool-web/src/index.ts',
requires: ['ctx.tools', 'ctx.web', 'ctx.systemPrompt'],
writes: ['tool/call', 'tool/result'],
async mount(ctx) {
// The tools inject `web`; boot the seam plus one search and one fetch
// provider so both `web_search` and `web_fetch` register. The schemas do
@@ -152,6 +173,8 @@ const TOOL_PACKAGES: ToolPackage[] = [
await ctx.plugin(WebFetchLocal)
await ctx.plugin(ToolWeb)
},
note:
'web_search and web_fetch keep provider selection behind ctx.web so model-visible schemas stay stable across backend swaps.',
},
]
@@ -159,6 +182,9 @@ const TOOL_PACKAGES: ToolPackage[] = [
interface CatalogPackage {
pkg: string
source: string
requires: string[]
writes: string[]
shippedNames?: string[]
schemas: ToolSchema[]
/** A deployment note (see {@link ToolPackage.note}), rendered after the tools. */
note?: string
@@ -208,7 +234,15 @@ export async function collectToolCatalog(packages: ToolPackage[] = TOOL_PACKAGES
await ctx.plugin(ToolRegistry)
await entry.mount(ctx)
const schemas = ctx.tools.schemas().sort((a, b) => a.name.localeCompare(b.name))
catalog.push({ pkg: entry.pkg, source: entry.source, schemas, ...entry.note !== undefined ? { note: entry.note } : {} })
catalog.push({
pkg: entry.pkg,
source: entry.source,
requires: entry.requires,
writes: entry.writes,
schemas,
...entry.shippedNames !== undefined ? { shippedNames: entry.shippedNames } : {},
...entry.note !== undefined ? { note: entry.note } : {},
})
} finally {
await ctx.fiber.dispose()
}
@@ -226,6 +260,14 @@ function renderTool(schema: ToolSchema, source: string): string[] {
return out
}
function codeList(values: string[] | undefined): string {
return values?.length ? values.map(value => `\`${value}\``).join(', ') : '-'
}
function tableCell(value: string | undefined): string {
return value ? value.replace(/\|/g, '\\|').replace(/\n/g, '<br>') : '-'
}
/** Render the full catalog (pure, deterministic given the manifest-ordered input). */
export function render(catalog: ToolCatalog): string {
const lines: string[] = [
@@ -240,6 +282,14 @@ export function render(catalog: ToolCatalog): string {
'',
'Scope: shipped product tools under `packages/*/tool-*`, each booted with its DEFAULT config. The registered tool NAME can be a load-time config (e.g. `tool-subagent`\'s `toolName`), so a deployment may surface a package under a different or additional name — a per-package note records those shipped aliases where they exist. The `examples/` demo tools (e.g. `echo`) are excluded, matching the cordis catalog\'s packages-only scope.',
'',
'## Tool Package Map',
'',
'This table connects model-visible tool names to the plugin package and service seams behind them. Exact JSON Schemas follow in the package sections below.',
'',
'| Tool package | Model-visible names | Requires | Writes / affects | Shipped aliases | Deployment note |',
'| --- | --- | --- | --- | --- | --- |',
...catalog.map(entry => `| \`${entry.pkg}\` | ${codeList(entry.schemas.map(schema => schema.name))} | ${codeList(entry.requires)} | ${codeList(entry.writes)} | ${codeList(entry.shippedNames)} | ${tableCell(entry.note)} |`),
'',
]
for (const entry of catalog) {
lines.push(`## \`${entry.pkg}\``, '')

View File

@@ -5,9 +5,9 @@
* render.
*
* Scope matches the Markdown link gate so any Mermaid diagram in repo-authored
* docs is checked: README.md, docs/** /*.md, packages/* /*.md,
* packages/* /* /*.md, examples/** /*.md, AGENTS.md, packages/AGENTS.md, and
* .agents/skills/** /*.md.
* docs is checked: README.md, README.zh.md, docs/** /*.md,
* packages/* /*.md, packages/* /* /*.md, examples/** /*.md, AGENTS.md,
* packages/AGENTS.md, and .agents/skills/** /*.md.
*
* Run: `tsx scripts/verify-mermaid.ts`.
*/
@@ -25,6 +25,7 @@ const root = resolve(import.meta.dirname, '..')
const PATTERNS = [
'README.md',
'README.zh.md',
'docs/**/*.md',
'packages/*/*.md',
'packages/*/*/*.md',