From 665c10ff1980482929358199a21fc504108066f7 Mon Sep 17 00:00:00 2001
From: Tianyi Cui <53024+tianyicui@users.noreply.github.com>
Date: Fri, 3 Jul 2026 01:13:52 +0800
Subject: [PATCH 1/5] docs(graphs): add generated documentation atlas
---
README.md | 2 +-
docs/architecture.md | 4 +-
docs/development.md | 4 +-
docs/graphs/README.md | 26 +
docs/graphs/agent-lifecycle.md | 41 +
docs/graphs/app-composition.md | 90 ++
docs/graphs/capability-seams.md | 110 +++
docs/graphs/event-producer-consumer.md | 36 +
docs/graphs/hot-reload-disposal.md | 24 +
docs/graphs/package-topology.md | 189 ++++
docs/graphs/session-surface.md | 25 +
docs/graphs/snapshot-replay.md | 24 +
docs/graphs/subagent-lineage.md | 27 +
docs/graphs/tool-affordance-map.md | 35 +
docs/graphs/tool-execution-pipeline.md | 30 +
docs/rfc/README.md | 1 +
.../2026-07-03-documentation-graph-atlas.md | 66 ++
package.json | 4 +-
scripts/gen-doc-graphs.ts | 851 ++++++++++++++++++
19 files changed, 1584 insertions(+), 5 deletions(-)
create mode 100644 docs/graphs/README.md
create mode 100644 docs/graphs/agent-lifecycle.md
create mode 100644 docs/graphs/app-composition.md
create mode 100644 docs/graphs/capability-seams.md
create mode 100644 docs/graphs/event-producer-consumer.md
create mode 100644 docs/graphs/hot-reload-disposal.md
create mode 100644 docs/graphs/package-topology.md
create mode 100644 docs/graphs/session-surface.md
create mode 100644 docs/graphs/snapshot-replay.md
create mode 100644 docs/graphs/subagent-lineage.md
create mode 100644 docs/graphs/tool-affordance-map.md
create mode 100644 docs/graphs/tool-execution-pipeline.md
create mode 100644 docs/rfc/implemented/process/2026-07-03-documentation-graph-atlas.md
create mode 100644 scripts/gen-doc-graphs.ts
diff --git a/README.md b/README.md
index 1ce5aa8960..2d97f103af 100644
--- a/README.md
+++ b/README.md
@@ -17,6 +17,6 @@ pnpm run demo:echo # runnable echo-agent example (no API key needed)
pnpm run demo:coding # the real DeepSeek coding agent (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) 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 atlas](docs/graphs/README.md) before package work. Local context lives in [packages/](packages/) and [vendor/](vendor/).
For agents, follow [AGENTS.md](AGENTS.md).
diff --git a/docs/architecture.md b/docs/architecture.md
index 5cd56c6e91..903e58909c 100644
--- a/docs/architecture.md
+++ b/docs/architecture.md
@@ -8,9 +8,9 @@ The harness core is deliberately tiny: a handful of abstract services plus one c
Requirement context: [Coding Harness MVP 需求分析][mvp-doc].
-For a catalog of the **data structures** this architecture moves around — the core vocabulary types, their literal shapes, and the seam types grouped by capability — see [core-data-structures/](core-data-structures/core.md). This document covers behavior; that one covers the types.
+For a catalog of the **data structures** this architecture moves around — the core vocabulary types, their literal shapes, and the seam types grouped by capability — see [core-data-structures/](core-data-structures/core.md). For visual relationship maps across packages, seams, events, tools, lifecycle, and replay, see the [documentation graph atlas](graphs/README.md). This document covers behavior; those references cover types and topology.
-**Contents:** [Layering](#layering) · [Service map](#service-map) · [Capability seams](#capability-seams-interface--implementation--consumer) · [The vocabulary (dsh-llm)](#the-vocabulary-dsh-llm) · [Event-sourced sessions](#event-sourced-sessions-dsh-session) · [Prompt assembly](#prompt-assembly-dsh-system-prompt) · [Tool pipeline](#tool-pipeline-dsh-tools) · [Agents and the loop](#agents-dsh-agent-and-the-loop-dsh-agent-loop) ([lifecycle](#loop-lifecycle-session--turn--step), [event taxonomy](#event-taxonomy), [waterfall semantics](#cordis-waterfall-semantics-important)) · [Plugin sanity checklist](#plugin-sanity-checklist) · [Extension cookbook](#extension-cookbook) · [Deferred work](#deferred-work-todo)
+**Contents:** [Layering](#layering) · [Service map](#service-map) · [Capability seams](#capability-seams-interface--implementation--consumer) · [The vocabulary (dsh-llm)](#the-vocabulary-dsh-llm) · [Event-sourced sessions](#event-sourced-sessions-dsh-session) · [Prompt assembly](#prompt-assembly-dsh-system-prompt) · [Tool pipeline](#tool-pipeline-dsh-tools) · [Agents and the loop](#agents-dsh-agent-and-the-loop-dsh-agent-loop) ([lifecycle](#loop-lifecycle-session--turn--step), [event taxonomy](#event-taxonomy), [waterfall semantics](#cordis-waterfall-semantics-important)) · [Graph atlas](graphs/README.md) · [Plugin sanity checklist](#plugin-sanity-checklist) · [Extension cookbook](#extension-cookbook) · [Deferred work](#deferred-work-todo)
[microkernel-doc]: https://trtgsjkv6r.feishu.cn/wiki/VS9Lw1kQki6mDJk2UHocyuphnsc
[mvp-doc]: https://trtgsjkv6r.feishu.cn/wiki/ZwK6wfBE9i91V6kzMGYcgRGanxg
diff --git a/docs/development.md b/docs/development.md
index 431d7b4dac..965f23d90f 100644
--- a/docs/development.md
+++ b/docs/development.md
@@ -96,9 +96,11 @@ 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 verify-md-wrap # fail on hard-wrapped prose paragraphs in docs/README markdown
pnpm run verify-type-equiv # fail if a ```ts type-equiv doc block drifts from its source type
-pnpm run doc-sync # doc-typecheck, cordis-catalog freshness, markdown wrap/link, and type-equiv verification
+pnpm run doc-sync # doc-typecheck, generated doc freshness, markdown wrap/link, and type-equiv verification
pnpm run gen-module-graph # regenerate docs/module-graph.md from package peerDeps
pnpm run verify-module-graph # fail if docs/module-graph.md is stale
pnpm run build # emit lib/types intermediates, then bundle lib/index.* runtime files
diff --git a/docs/graphs/README.md b/docs/graphs/README.md
new file mode 100644
index 0000000000..995481d160
--- /dev/null
+++ b/docs/graphs/README.md
@@ -0,0 +1,26 @@
+
+
+# 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`.
diff --git a/docs/graphs/agent-lifecycle.md b/docs/graphs/agent-lifecycle.md
new file mode 100644
index 0000000000..cc25e25694
--- /dev/null
+++ b/docs/graphs/agent-lifecycle.md
@@ -0,0 +1,41 @@
+
+
+# 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 shows the durable session event path separately from live `agent/*` notifications.
+
+```mermaid
+sequenceDiagram
+ participant User
+ participant Agent
+ participant Loop
+ participant Prompt as ctx.systemPrompt
+ participant LLM as ctx.llm
+ participant Tools as ctx.tools
+ participant Session
+ participant Persistence
+ User->>Agent: send(content)
+ Agent->>Loop: queued work wakes driver
+ Loop->>Session: turn/start + user/message
+ Loop-->>User: agent/turn-start
+ Loop->>Prompt: system-prompt/assemble waterfall
+ Loop-->>Loop: agent/pre-step serial checkpoint
+ Loop->>Session: step/start
+ Loop->>LLM: agent/request waterfall, then llm/stream waterfall
+ LLM-->>Loop: StreamChunk*
+ Loop->>Session: assistant/chunk*
+ Loop-->>User: agent/stream-chunk* (master live mirror)
+ Loop->>Session: assistant/message
+ Loop->>Tools: tools/execute waterfall for each tool-call
+ Tools-->>Session: tool-owned events when applicable
+ Loop->>Session: tool/result
+ Loop-->>Loop: agent/turn-continuation waterfall
+ Loop->>Session: turn/end
+ Loop->>Persistence: session/flush parallel checkpoint
+ Loop-->>User: agent/status idle
+```
+
+Future pressure from the hooks stack: PR #129 removes the live `agent/stream-chunk` mirror and leaves durable `assistant/chunk` on `session/event` as the authoritative token stream. Consumers that need replayable transcript data should already treat `session/event` as the load-bearing path.
diff --git a/docs/graphs/app-composition.md b/docs/graphs/app-composition.md
new file mode 100644
index 0000000000..ce2bab32d8
--- /dev/null
+++ b/docs/graphs/app-composition.md
@@ -0,0 +1,90 @@
+
+
+# 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
@cordisjs/plugin-hmr"]
+ cfg_echo --> plugin_echo_hmr
+ plugin_echo_mock_llm["mock-llm
./src/mock-llm.ts"]
+ cfg_echo --> plugin_echo_mock_llm
+ plugin_echo_echo_tool["echo-tool
./src/echo-tool.ts"]
+ cfg_echo --> plugin_echo_echo_tool
+ plugin_echo_bash["bash
@deepseek-ai/dsh-bash-local"]
+ cfg_echo --> plugin_echo_bash
+ plugin_echo_stdio_agent["stdio-agent
@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
@cordisjs/plugin-hmr"]
+ cfg_coding --> plugin_coding_hmr
+ plugin_coding_llm_deepseek["llm-deepseek
@deepseek-ai/dsh-llm-deepseek"]
+ cfg_coding --> plugin_coding_llm_deepseek
+ plugin_coding_bash["bash
@deepseek-ai/dsh-bash-local"]
+ cfg_coding --> plugin_coding_bash
+ plugin_coding_stdio_agent["stdio-agent
@deepseek-ai/dsh-stdio-agent"]
+ cfg_coding --> plugin_coding_stdio_agent
+ plugin_coding_stdio_agent --> bundle_stdio
+ plugin_coding_compact_basic["compact-basic
@deepseek-ai/dsh-compact-basic"]
+ cfg_coding --> plugin_coding_compact_basic
+ plugin_coding_subagent["subagent
@deepseek-ai/dsh-subagent"]
+ cfg_coding --> plugin_coding_subagent
+ plugin_coding_subagent_spawn["subagent-spawn
@deepseek-ai/dsh-subagent-spawn"]
+ cfg_coding --> plugin_coding_subagent_spawn
+ plugin_coding_subagent_fork["subagent-fork
@deepseek-ai/dsh-subagent-fork"]
+ cfg_coding --> plugin_coding_subagent_fork
+ plugin_coding_tool_subagent["tool-subagent
@deepseek-ai/dsh-tool-subagent"]
+ cfg_coding --> plugin_coding_tool_subagent
+ plugin_coding_tool_subagent_fork["tool-subagent-fork
@deepseek-ai/dsh-tool-subagent"]
+ cfg_coding --> plugin_coding_tool_subagent_fork
+ plugin_coding_tool_todo["tool-todo
@deepseek-ai/dsh-tool-todo"]
+ cfg_coding --> plugin_coding_tool_todo
+ end
+ subgraph example_acp["examples/acp-agent"]
+ cfg_acp["cordis.yml"]
+ plugin_acp_llm_deepseek["llm-deepseek
@deepseek-ai/dsh-llm-deepseek"]
+ cfg_acp --> plugin_acp_llm_deepseek
+ plugin_acp_bash["bash
@deepseek-ai/dsh-bash-local"]
+ cfg_acp --> plugin_acp_bash
+ plugin_acp_acp_agent["acp-agent
@deepseek-ai/dsh-acp-agent"]
+ cfg_acp --> plugin_acp_acp_agent
+ plugin_acp_acp_agent --> bundle_acp_agent
+ plugin_acp_subagent["subagent
@deepseek-ai/dsh-subagent"]
+ cfg_acp --> plugin_acp_subagent
+ plugin_acp_subagent_spawn["subagent-spawn
@deepseek-ai/dsh-subagent-spawn"]
+ cfg_acp --> plugin_acp_subagent_spawn
+ plugin_acp_subagent_fork["subagent-fork
@deepseek-ai/dsh-subagent-fork"]
+ cfg_acp --> plugin_acp_subagent_fork
+ plugin_acp_tool_subagent["tool-subagent
@deepseek-ai/dsh-tool-subagent"]
+ cfg_acp --> plugin_acp_tool_subagent
+ plugin_acp_tool_subagent_fork["tool-subagent-fork
@deepseek-ai/dsh-tool-subagent"]
+ cfg_acp --> plugin_acp_tool_subagent_fork
+ plugin_acp_tool_todo["tool-todo
@deepseek-ai/dsh-tool-todo"]
+ cfg_acp --> plugin_acp_tool_todo
+ 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` | [`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` | [`examples/acp-agent/cordis.yml`](../../examples/acp-agent/cordis.yml) |
diff --git a/docs/graphs/capability-seams.md b/docs/graphs/capability-seams.md
new file mode 100644
index 0000000000..d3b95b55f8
--- /dev/null
+++ b/docs/graphs/capability-seams.md
@@ -0,0 +1,110 @@
+
+
+# Capability Seams And Core Services
+
+Maintenance mode: hybrid: services are discovered from Cordis declarations; interface/implementation/consumer roles are classified in `scripts/gen-doc-graphs.ts` with a completeness guard.
+
+A service can be a core spine service, a swappable capability seam, or a bundle/composition point. The graph shows the package that owns the service declaration, known implementation packages, and packages that consume the service directly.
+
+```mermaid
+flowchart LR
+ pkg_llm["llm"]
+ svc_llm["ctx.llm
LLM adapter registry"]
+ pkg_llm_deepseek["llm-deepseek"]
+ pkg_llm_pi_ai["llm-pi-ai"]
+ pkg_llm_replay["llm-replay"]
+ pkg_agent_loop["agent-loop"]
+ pkg_compact_basic["compact-basic"]
+ pkg_session["session"]
+ svc_sessions["ctx.sessions
In-memory session store"]
+ pkg_agent["agent"]
+ pkg_session_persistence["session-persistence"]
+ pkg_subagent_inprocess["subagent-inprocess"]
+ pkg_invariants["invariants"]
+ svc_sessionPersistence["ctx.sessionPersistence
Durable session persistence seam"]
+ pkg_session_persistence_jsonl["session-persistence-jsonl"]
+ pkg_session_persistence_sqlite["session-persistence-sqlite"]
+ pkg_acp["acp"]
+ pkg_system_prompt["system-prompt"]
+ svc_systemPrompt["ctx.systemPrompt
System prompt assembly registry"]
+ pkg_tools["tools"]
+ svc_tools["ctx.tools
Tool registry and execution waterfall"]
+ pkg_tool_bash["tool-bash"]
+ pkg_tool_subagent["tool-subagent"]
+ pkg_tool_todo["tool-todo"]
+ svc_agents["ctx.agents
Agent registry"]
+ pkg_stdio_agent["stdio-agent"]
+ svc_agentLoop["ctx.agentLoop
Concrete loop driver"]
+ pkg_agent_core["agent-core"]
+ pkg_bash["bash"]
+ svc_bash["ctx.bash
Bash executor seam"]
+ pkg_bash_local["bash-local"]
+ pkg_compact["compact"]
+ svc_compact["ctx.compact
Compaction seam"]
+ pkg_subagent["subagent"]
+ svc_subagents["ctx.subagents
Subagent provider registry"]
+ pkg_subagent_spawn["subagent-spawn"]
+ pkg_subagent_fork["subagent-fork"]
+ pkg_subagent_acp["subagent-acp"]
+ pkg_subagent_mock["subagent-mock"]
+ pkg_agent --> svc_agents
+ pkg_agent_loop --> svc_agentLoop
+ pkg_bash --> svc_bash
+ pkg_bash_local --> svc_bash
+ pkg_compact --> svc_compact
+ pkg_compact_basic --> svc_compact
+ pkg_llm --> svc_llm
+ pkg_llm_deepseek --> svc_llm
+ pkg_llm_pi_ai --> svc_llm
+ pkg_llm_replay --> svc_llm
+ pkg_session --> svc_sessions
+ pkg_session_persistence --> svc_sessionPersistence
+ pkg_session_persistence_jsonl --> svc_sessionPersistence
+ pkg_session_persistence_sqlite --> svc_sessionPersistence
+ pkg_subagent --> svc_subagents
+ pkg_subagent_acp --> svc_subagents
+ pkg_subagent_fork --> svc_subagents
+ pkg_subagent_mock --> svc_subagents
+ pkg_subagent_spawn --> svc_subagents
+ pkg_system_prompt --> svc_systemPrompt
+ pkg_tools --> svc_tools
+ svc_agentLoop --> pkg_agent_core
+ svc_agents --> pkg_acp
+ svc_agents --> pkg_agent_loop
+ svc_agents --> pkg_invariants
+ svc_agents --> pkg_stdio_agent
+ svc_agents --> pkg_subagent_inprocess
+ svc_bash --> pkg_tool_bash
+ svc_compact --> pkg_compact_basic
+ svc_llm --> pkg_agent_loop
+ svc_llm --> pkg_compact_basic
+ svc_sessionPersistence --> pkg_acp
+ svc_sessionPersistence --> pkg_agent_loop
+ svc_sessions --> pkg_agent
+ svc_sessions --> pkg_agent_loop
+ svc_sessions --> pkg_invariants
+ svc_sessions --> pkg_session_persistence
+ svc_sessions --> pkg_subagent_inprocess
+ svc_subagents --> pkg_tool_subagent
+ svc_systemPrompt --> pkg_agent_loop
+ svc_systemPrompt --> pkg_tools
+ svc_tools --> pkg_acp
+ svc_tools --> pkg_agent_loop
+ svc_tools --> pkg_tool_bash
+ svc_tools --> pkg_tool_subagent
+ svc_tools --> pkg_tool_todo
+```
+
+| ctx key | Role | Owner | Implementations | Direct consumers | 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) | 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-subagent`](../../packages/subagent/tool-subagent), [`tool-todo`](../../packages/todo/tool-todo), [`acp`](../../packages/ui/acp) | Registers tool definitions, exposes schemas to the prompt, and routes calls through tools/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) | The model-facing bash tools consume this seam; sandboxed or remote executors can replace bash-local. |
+| `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. |
diff --git a/docs/graphs/event-producer-consumer.md b/docs/graphs/event-producer-consumer.md
new file mode 100644
index 0000000000..dfce3fe04a
--- /dev/null
+++ b/docs/graphs/event-producer-consumer.md
@@ -0,0 +1,36 @@
+
+
+# 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:137`](../../packages/core/agent/src/types.ts) | [`agent`](../../packages/core/agent) (`emit`) | - |
+| `agent/disposed` | `emit` | [`packages/core/agent/src/types.ts:143`](../../packages/core/agent/src/types.ts) | [`agent`](../../packages/core/agent) (`emit`) | - |
+| `agent/error` | `emit` | [`packages/core/agent/src/types.ts:254`](../../packages/core/agent/src/types.ts) | [`agent-loop`](../../packages/core/agent-loop) (`emit`) | - |
+| `agent/pre-step` | `serial` | [`packages/core/agent/src/types.ts:214`](../../packages/core/agent/src/types.ts) | [`agent-loop`](../../packages/core/agent-loop) (`serial`) | [`compact-basic`](../../packages/compact/compact-basic) |
+| `agent/queued` | `emit` | [`packages/core/agent/src/types.ts:156`](../../packages/core/agent/src/types.ts) | [`agent-loop`](../../packages/core/agent-loop) (`emit`) | - |
+| `agent/request` | `waterfall` | [`packages/core/agent/src/types.ts:223`](../../packages/core/agent/src/types.ts) | [`agent-loop`](../../packages/core/agent-loop) (`waterfall`), [`compact-basic`](../../packages/compact/compact-basic) (`waterfall`) | - |
+| `agent/status` | `emit` | [`packages/core/agent/src/types.ts:150`](../../packages/core/agent/src/types.ts) | [`agent-loop`](../../packages/core/agent-loop) (`emit`) | [`acp`](../../packages/ui/acp), [`invariants`](../../packages/support/invariants), [`ui-stdio`](../../packages/support/ui-stdio) |
+| `agent/steering` | `emit` | [`packages/core/agent/src/types.ts:248`](../../packages/core/agent/src/types.ts) | [`agent-loop`](../../packages/core/agent-loop) (`emit`) | - |
+| `agent/step-end` | `emit` | [`packages/core/agent/src/types.ts:180`](../../packages/core/agent/src/types.ts) | [`agent-loop`](../../packages/core/agent-loop) (`emit`) | - |
+| `agent/step-result` | `waterfall` | [`packages/core/agent/src/types.ts:229`](../../packages/core/agent/src/types.ts) | [`agent-loop`](../../packages/core/agent-loop) (`waterfall`) | - |
+| `agent/step-start` | `emit` | [`packages/core/agent/src/types.ts:175`](../../packages/core/agent/src/types.ts) | [`agent-loop`](../../packages/core/agent-loop) (`emit`) | - |
+| `agent/stream-chunk` | `emit` | [`packages/core/agent/src/types.ts:243`](../../packages/core/agent/src/types.ts) | [`agent-loop`](../../packages/core/agent-loop) (`emit`) | [`ui-stdio`](../../packages/support/ui-stdio) |
+| `agent/turn-continuation` | `waterfall` | [`packages/core/agent/src/types.ts:236`](../../packages/core/agent/src/types.ts) | [`agent-loop`](../../packages/core/agent-loop) (`waterfall`) | - |
+| `agent/turn-end` | `emit` | [`packages/core/agent/src/types.ts:169`](../../packages/core/agent/src/types.ts) | [`agent-loop`](../../packages/core/agent-loop) (`emit`) | [`ui-stdio`](../../packages/support/ui-stdio) |
+| `agent/turn-start` | `emit` | [`packages/core/agent/src/types.ts:163`](../../packages/core/agent/src/types.ts) | [`agent-loop`](../../packages/core/agent-loop) (`emit`) | [`ui-stdio`](../../packages/support/ui-stdio) |
+| `llm/stream` | `waterfall` | [`packages/llm/llm/src/index.ts:31`](../../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:34`](../../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:40`](../../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), [`ui-stdio`](../../packages/support/ui-stdio) |
+| `session/flush` | `parallel` | [`packages/core/session/src/index.ts:49`](../../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:65`](../../packages/subagent/subagent/src/index.ts) | [`subagent`](../../packages/subagent/subagent) (`events.dispatch`) | - |
+| `subagent/start` | `emit` | [`packages/subagent/subagent/src/index.ts:59`](../../packages/subagent/subagent/src/index.ts) | [`subagent`](../../packages/subagent/subagent) (`events.dispatch`) | - |
+| `system-prompt/assemble` | `waterfall` | [`packages/core/system-prompt/src/index.ts:24`](../../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:30`](../../packages/core/system-prompt/src/index.ts) | [`system-prompt`](../../packages/core/system-prompt) (`emit`) | - |
+| `tools/change` | `emit` | [`packages/core/tools/src/index.ts:48`](../../packages/core/tools/src/index.ts) | [`tools`](../../packages/core/tools) (`emit`) | - |
+| `tools/execute` | `waterfall` | [`packages/core/tools/src/index.ts:43`](../../packages/core/tools/src/index.ts) | [`tools`](../../packages/core/tools) (`waterfall`) | - |
diff --git a/docs/graphs/hot-reload-disposal.md b/docs/graphs/hot-reload-disposal.md
new file mode 100644
index 0000000000..ca98c25692
--- /dev/null
+++ b/docs/graphs/hot-reload-disposal.md
@@ -0,0 +1,24 @@
+
+
+# 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
events, tools, adapters, timers"]
+ reload["HMR / fiber.dispose()"]
+ disposers["Run disposers in owner fiber"]
+ quiescence["Owned AgentHandle.dispose()
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.
diff --git a/docs/graphs/package-topology.md b/docs/graphs/package-topology.md
new file mode 100644
index 0000000000..68d5a2442c
--- /dev/null
+++ b/docs/graphs/package-topology.md
@@ -0,0 +1,189 @@
+
+
+# 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//` 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_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_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_todo["packages/todo"]
+ pkg_tool_todo["tool-todo"]
+ end
+ subgraph group_support["packages/support"]
+ pkg_invariants["invariants"]
+ pkg_llm_replay["llm-replay"]
+ pkg_subagent_mock["subagent-mock"]
+ pkg_ui_stdio["ui-stdio"]
+ 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_agent --> pkg_brand
+ pkg_agent --> pkg_llm
+ pkg_agent --> pkg_session
+ pkg_compact --> pkg_llm
+ pkg_compact --> 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_ui_stdio --> pkg_agent
+ pkg_ui_stdio --> pkg_llm
+ pkg_ui_stdio --> 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_subagent --> pkg_agent
+ pkg_subagent --> pkg_llm
+ pkg_subagent --> pkg_tools
+ pkg_tool_todo --> pkg_agent
+ pkg_tool_todo --> pkg_session
+ pkg_tool_todo --> 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_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_session
+ pkg_stdio_agent --> pkg_session_persistence_jsonl
+ pkg_stdio_agent --> pkg_ui_stdio
+```
+
+| 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) |
+| [`agent`](../../packages/core/agent) | `core` | [`brand`](../../packages/util/brand), [`llm`](../../packages/llm/llm), [`session`](../../packages/core/session) |
+| [`compact`](../../packages/compact/compact) | `compact` | [`llm`](../../packages/llm/llm), [`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) |
+| [`ui-stdio`](../../packages/support/ui-stdio) | `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) |
+| [`subagent`](../../packages/subagent/subagent) | `subagent` | [`agent`](../../packages/core/agent), [`llm`](../../packages/llm/llm), [`tools`](../../packages/core/tools) |
+| [`tool-todo`](../../packages/todo/tool-todo) | `todo` | [`agent`](../../packages/core/agent), [`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) |
+| [`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), [`session`](../../packages/core/session), [`session-persistence-jsonl`](../../packages/session-persistence/session-persistence-jsonl), [`ui-stdio`](../../packages/support/ui-stdio) |
diff --git a/docs/graphs/session-surface.md b/docs/graphs/session-surface.md
new file mode 100644
index 0000000000..b25f85b2d2
--- /dev/null
+++ b/docs/graphs/session-surface.md
@@ -0,0 +1,25 @@
+
+
+# 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
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.
diff --git a/docs/graphs/snapshot-replay.md b/docs/graphs/snapshot-replay.md
new file mode 100644
index 0000000000..d0ee1c9061
--- /dev/null
+++ b/docs/graphs/snapshot-replay.md
@@ -0,0 +1,24 @@
+
+
+# ACP Snapshot Replay
+
+Maintenance mode: curated Mermaid sequence based on the snapshot test harness.
+
+This graph explains what a snapshot scenario proves: recorded real-model session logs are replayed keylessly, then ACP stdout is normalized and diffed.
+
+```mermaid
+sequenceDiagram
+ participant Recorder as Real API recording
+ participant Fixture as snapshot fixture
+ participant Replay as llm-replay adapter
+ participant ACP as acp-agent subprocess
+ participant Golden as stdout golden
+ Recorder->>Fixture: session.jsonl + workspace inputs
+ Fixture->>Replay: recorded StreamChunk script
+ Replay->>ACP: deterministic llm/stream chunks
+ ACP->>Golden: normalized sessionUpdate stream
+ Golden-->>ACP: diff must be empty
+```
+
+Future pressure from the fs stack: policy rejection scenarios are valuable because they prove both world state and failed tool-card rendering, not just that replay returns text.
diff --git a/docs/graphs/subagent-lineage.md b/docs/graphs/subagent-lineage.md
new file mode 100644
index 0000000000..f687405782
--- /dev/null
+++ b/docs/graphs/subagent-lineage.md
@@ -0,0 +1,27 @@
+
+
+# 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
model-facing name"]
+ registry["ctx.subagents provider registry"]
+ spawn["spawn provider
fresh child session"]
+ fork["fork provider
seeded from completed-turn prefix"]
+ acp["ACP provider
out-of-process child"]
+ child["Child AgentHandle
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.
diff --git a/docs/graphs/tool-affordance-map.md b/docs/graphs/tool-affordance-map.md
new file mode 100644
index 0000000000..995dee7b08
--- /dev/null
+++ b/docs/graphs/tool-affordance-map.md
@@ -0,0 +1,35 @@
+
+
+# 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
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_subagent["tool-subagent
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
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
+```
+
+| 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-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. |
diff --git a/docs/graphs/tool-execution-pipeline.md b/docs/graphs/tool-execution-pipeline.md
new file mode 100644
index 0000000000..7fde39af83
--- /dev/null
+++ b/docs/graphs/tool-execution-pipeline.md
@@ -0,0 +1,30 @@
+
+
+# Tool Execution Pipeline
+
+Maintenance mode: curated Mermaid flow; exact tool schemas and event signatures live in generated catalogs.
+
+This graph shows where policy, hooks, sandboxing, and future filesystem guards fit without changing the loop. The key extension point is the `tools/execute` waterfall.
+
+```mermaid
+flowchart TD
+ model["Assistant message contains tool-call block"]
+ call["Session event: tool/call"]
+ waterfall["ctx.tools.execute()
tools/execute waterfall"]
+ policy["Policy / permission / hooks listener"]
+ body["Registered tool execute() body"]
+ owned["Tool-owned session events
todo/write, future fs policy facts"]
+ result["Session event: tool/result"]
+ ui["UI presentation
presentCall / presentResult"]
+ model --> call --> waterfall
+ waterfall --> policy
+ policy -->|next()| body
+ policy -->|veto / throw| result
+ body --> owned
+ body --> result
+ call --> ui
+ result --> ui
+```
+
+Future pressure from the fs stack: PR #128 snapshots a policy rejection card. The graph keeps the veto path explicit because filesystem read-before-edit checks, permission prompts, and hook bridges all belong on this path.
diff --git a/docs/rfc/README.md b/docs/rfc/README.md
index 981710c4f5..8c5b5ebc9b 100644
--- a/docs/rfc/README.md
+++ b/docs/rfc/README.md
@@ -136,6 +136,7 @@ Do NOT write one for a mechanical or local choice (a variable name, a one-file r
| [Generated cordis events + services catalog](implemented/process/2026-06-20-generated-cordis-catalog.md) | 2026-06-20 |
| [Classify RFCs by kind via path-encoded subdirectories](implemented/process/2026-06-20-rfc-classification.md) | 2026-06-20 |
| [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 |
### Testing
diff --git a/docs/rfc/implemented/process/2026-07-03-documentation-graph-atlas.md b/docs/rfc/implemented/process/2026-07-03-documentation-graph-atlas.md
new file mode 100644
index 0000000000..e707786c14
--- /dev/null
+++ b/docs/rfc/implemented/process/2026-07-03-documentation-graph-atlas.md
@@ -0,0 +1,66 @@
+# RFC: Documentation graph atlas for maintainers and SDK users
+
+Status: implemented (accepted 2026-07-03)
+
+## Context
+
+The repo already had several high-trust documentation surfaces, each on a different axis: [module-graph.md](../../../module-graph.md) is generated from package `peerDependencies`, [cordis-catalog/events-and-services.md](../../../cordis-catalog/events-and-services.md) is generated from Cordis `Events` and `Context` declarations, [tool-catalog/tools.md](../../../tool-catalog/tools.md) is generated by booting shipped tool plugins, and [core-data-structures/](../../../core-data-structures/core.md) uses `ts type-equiv` blocks to keep pasted type definitions synchronized with source.
+
+Those references are accurate, but they are mostly catalogs. A maintainer still has to synthesize the relationships: which packages form a capability seam, which app bundles a concrete spine, which event is durable vs live, where a hook or policy plugin can intercept work, and which model-facing tool depends on which service. An SDK user has the same problem from another angle: "Which package do I install or load for the behavior I want, and which event/service/tool do I extend?"
+
+The pressure is already visible in the open stacks even though this implementation is based on `origin/master`: the hooks stack through PR #129 makes event producer/consumer topology and interception points much more important, while the filesystem stack through PR #128 makes capability seams, policy vetoes, tool presentation, and SDK assembly paths much more important. Graphs based only on today's small bash/todo/subagent surface would become obsolete as soon as those stacks land.
+
+## 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`.
+
+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.
+
+### Maintenance modes
+
+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.
+
+### First shipped atlas
+
+The first atlas ships twelve files: the index plus eleven graph pages.
+
+| 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 |
+
+### Why one generator
+
+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.
+
+### Completeness guards
+
+The hybrid pages must fail loud when their manifests are stale:
+
+- The capability seam graph imports the Cordis service collector and asserts every discovered harness `ctx.` 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.
+
+## Format choices
+
+Use Mermaid for committed diagrams because GitHub renders it in Markdown and it adds no new docs build dependency. Use Markdown tables for dense many-to-many data such as event producer/consumer relationships. Do not adopt PlantUML, hosted diagram services, or generated SVGs until Mermaid becomes the limiting factor.
+
+## Consequences
+
+- Maintainers get visual entry points for topology, seams, event flow, lifecycle, session replay, 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`, so graph drift is 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.
diff --git a/package.json b/package.json
index 7d82af4aad..3a35adf5e8 100644
--- a/package.json
+++ b/package.json
@@ -36,10 +36,12 @@
"verify-cordis-catalog": "tsx scripts/gen-cordis-catalog.ts --check",
"gen-tool-catalog": "tsx scripts/gen-tool-catalog.ts",
"verify-tool-catalog": "tsx scripts/gen-tool-catalog.ts --check",
+ "gen-doc-graphs": "tsx scripts/gen-doc-graphs.ts",
+ "verify-doc-graphs": "tsx scripts/gen-doc-graphs.ts --check",
"gen-module-graph": "tsx scripts/gen-module-graph.ts",
"verify-module-graph": "tsx scripts/gen-module-graph.ts --check",
"constraints": "tsx scripts/check-workspace-constraints.ts",
- "doc-sync": "pnpm run doc-typecheck && pnpm run verify-cordis-catalog && pnpm run verify-tool-catalog && pnpm run verify-md-wrap && pnpm run verify-md-links && pnpm run verify-doc-refs && pnpm run verify-package-paths && pnpm run verify-rfc-classification && pnpm run verify-type-equiv",
+ "doc-sync": "pnpm run doc-typecheck && pnpm run verify-cordis-catalog && pnpm run verify-tool-catalog && pnpm run verify-doc-graphs && pnpm run verify-md-wrap && pnpm run verify-md-links && pnpm run verify-doc-refs && pnpm run verify-package-paths && pnpm run verify-rfc-classification && pnpm run verify-type-equiv",
"hygiene": "pnpm run knip && pnpm run publint && pnpm run constraints && pnpm run verify-node-next-types",
"demo:echo": "node --expose-internals --import tsx packages/ui/stdio-agent/src/bin.ts examples/echo-agent/cordis.yml",
"demo:coding": "node --expose-internals --import tsx packages/ui/stdio-agent/src/bin.ts examples/coding-agent/cordis.yml",
diff --git a/scripts/gen-doc-graphs.ts b/scripts/gen-doc-graphs.ts
new file mode 100644
index 0000000000..38a385fa62
--- /dev/null
+++ b/scripts/gen-doc-graphs.ts
@@ -0,0 +1,851 @@
+/**
+ * Generate (and verify) the documentation graph atlas in docs/graphs/.
+ *
+ * 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?"
+ *
+ * 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,
+ * 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 --check` -> exit 1 if any file is stale
+ */
+
+import { existsSync, globSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs'
+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 {
+ name: string
+ peerDependencies?: Record
+}
+
+interface Pkg {
+ short: string
+ name: string
+ group: string
+ rel: string
+ deps: string[]
+}
+
+interface GraphDoc {
+ rel: string
+ content: string
+}
+
+interface ServiceRole {
+ key: string
+ pkg: string
+ title: string
+ mode: 'core' | 'seam' | 'bundle'
+ implementations?: string[]
+ consumers?: string[]
+ note: string
+}
+
+interface ExamplePlugin {
+ id: string
+ name: string
+}
+
+interface EventRelation {
+ dispatchers: Map>
+ listeners: Set
+}
+
+interface ToolPackageMeta {
+ requires: string[]
+ writes: string[]
+ shippedNames?: string[]
+ note: string
+}
+
+const GROUP_ORDER = ['util', 'llm', 'core', 'bash', 'compact', 'subagent', 'session-persistence', 'todo', 'support', 'ui']
+
+const SERVICE_ROLES: ServiceRole[] = [
+ {
+ key: 'llm',
+ pkg: 'llm',
+ title: 'LLM adapter registry',
+ mode: 'seam',
+ implementations: ['llm-deepseek', 'llm-pi-ai', 'llm-replay'],
+ consumers: ['agent-loop', 'compact-basic'],
+ note: 'Adapters register provider implementations; the loop and compaction call the provider-neutral stream service.',
+ },
+ {
+ key: 'sessions',
+ pkg: 'session',
+ title: 'In-memory session store',
+ mode: 'core',
+ consumers: ['agent-loop', 'agent', 'session-persistence', 'subagent-inprocess', 'invariants'],
+ note: 'Owns append-only Session instances and emits the durable session event feed.',
+ },
+ {
+ key: 'sessionPersistence',
+ pkg: 'session-persistence',
+ title: 'Durable session persistence seam',
+ mode: 'seam',
+ implementations: ['session-persistence-jsonl', 'session-persistence-sqlite'],
+ consumers: ['agent-loop', 'acp'],
+ note: 'Backends persist the same SessionEvent vocabulary; apps choose a backend at composition time.',
+ },
+ {
+ key: 'systemPrompt',
+ pkg: 'system-prompt',
+ title: 'System prompt assembly registry',
+ mode: 'core',
+ consumers: ['agent-loop', 'tools'],
+ note: 'Collects prompt sections and model-facing tool schemas for each step.',
+ },
+ {
+ key: 'tools',
+ pkg: 'tools',
+ title: 'Tool registry and execution waterfall',
+ mode: 'core',
+ consumers: ['agent-loop', 'tool-bash', 'tool-subagent', 'tool-todo', 'acp'],
+ note: 'Registers tool definitions, exposes schemas to the prompt, and routes calls through tools/execute.',
+ },
+ {
+ key: 'agents',
+ pkg: 'agent',
+ title: 'Agent registry',
+ mode: 'core',
+ consumers: ['agent-loop', 'acp', 'subagent-inprocess', 'stdio-agent', 'invariants'],
+ note: 'Owns live Agent handles and the create/resume factory seam.',
+ },
+ {
+ key: 'agentLoop',
+ pkg: 'agent-loop',
+ title: 'Concrete loop driver',
+ mode: 'bundle',
+ consumers: ['agent-core'],
+ note: 'The one concrete loop plugin; extension packages depend on dsh-agent events and services, not on this package.',
+ },
+ {
+ key: 'bash',
+ pkg: 'bash',
+ title: 'Bash executor seam',
+ mode: 'seam',
+ implementations: ['bash-local'],
+ consumers: ['tool-bash'],
+ note: 'The model-facing bash tools consume this seam; sandboxed or remote executors can replace bash-local.',
+ },
+ {
+ key: 'compact',
+ pkg: 'compact',
+ title: 'Compaction seam',
+ mode: 'seam',
+ implementations: ['compact-basic'],
+ consumers: ['compact-basic'],
+ note: 'The basic backend currently consumes the pre-step event directly; a model-facing compact tool remains deferred.',
+ },
+ {
+ key: 'subagents',
+ pkg: 'subagent',
+ title: 'Subagent provider registry',
+ mode: 'seam',
+ implementations: ['subagent-spawn', 'subagent-fork', 'subagent-acp', 'subagent-mock'],
+ consumers: ['tool-subagent'],
+ note: 'Providers implement transports; tool-subagent exposes one configured provider as a model-facing tool name.',
+ },
+]
+
+const TOOL_PACKAGE_META: Record = {
+ '@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-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.',
+ },
+}
+
+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
+ // listeners or strand an already-started child run.
+ { event: 'subagent/start', pkg: 'subagent', method: 'events.dispatch' },
+ { event: 'subagent/end', pkg: 'subagent', method: 'events.dispatch' },
+]
+
+function generatedHeader(title: string, source: string): string[] {
+ return [
+ '',
+ '',
+ `# ${title}`,
+ '',
+ `Maintenance mode: ${source}.`,
+ '',
+ ]
+}
+
+function collectPackages(): Pkg[] {
+ const pkgs: Pkg[] = []
+ for (const rel of globSync('packages/*/*/package.json', { cwd: root }).sort()) {
+ const json = JSON.parse(readFileSync(resolve(root, rel), 'utf8')) as PkgJson
+ if (!json.name.startsWith(SCOPE)) continue
+ const [, group, leaf] = rel.split('/')
+ if (group === undefined || leaf === undefined) throw new Error(`gen-doc-graphs: unexpected package path ${rel}`)
+ const deps = Object.keys(json.peerDependencies ?? {})
+ .filter(dep => dep.startsWith(SCOPE))
+ .map(dep => dep.slice(SCOPE.length))
+ .sort()
+ pkgs.push({
+ short: json.name.slice(SCOPE.length),
+ name: json.name,
+ group,
+ rel: dirname(rel),
+ deps,
+ })
+ }
+ return topoSort(pkgs)
+}
+
+function topoSort(pkgs: Pkg[]): Pkg[] {
+ const remaining = new Map(pkgs.map(p => [p.short, p]))
+ const placed = new Set()
+ const out: Pkg[] = []
+ while (remaining.size > 0) {
+ const ready = [...remaining.values()]
+ .filter(pkg => pkg.deps.every(dep => placed.has(dep)))
+ .sort(comparePackages)
+ if (ready.length === 0) throw new Error(`gen-doc-graphs: dependency cycle among ${[...remaining.keys()].join(', ')}`)
+ for (const pkg of ready) {
+ out.push(pkg)
+ placed.add(pkg.short)
+ remaining.delete(pkg.short)
+ }
+ }
+ 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 pkgLink(pkg: Pkg | undefined, fallback: string): string {
+ return pkg ? `[\`${pkg.short}\`](../../${pkg.rel})` : `\`${fallback}\``
+}
+
+function pkgList(names: string[] | undefined, pkgsByShort: Map): string {
+ if (!names || names.length === 0) return '-'
+ 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, '
')
+}
+
+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//` 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))
+ const missing = [...discovered].filter(key => !classified.has(key)).sort()
+ const stale = [...classified].filter(key => !discovered.has(key)).sort()
+ if (missing.length || stale.length) {
+ throw new Error([
+ missing.length ? `missing service role classification: ${missing.join(', ')}` : '',
+ stale.length ? `stale service role classification: ${stale.join(', ')}` : '',
+ ].filter(Boolean).join('; '))
+ }
+}
+
+function renderCapabilitySeams(pkgs: Pkg[]): string {
+ assertServiceRolesComplete()
+ const pkgsByShort = new Map(pkgs.map(pkg => [pkg.short, pkg]))
+ const nodes = new Map()
+ const edges = new Set()
+ const addNode = (id: string, label: string): void => {
+ if (!nodes.has(id)) nodes.set(id, ` ${id}["${escLabel(label)}"]`)
+ }
+ const addEdge = (from: string, to: string): void => { edges.add(` ${from} --> ${to}`) }
+ const lines = generatedHeader('Capability Seams And Core Services', 'hybrid: services are discovered from Cordis declarations; interface/implementation/consumer roles are classified in `scripts/gen-doc-graphs.ts` with a completeness guard')
+ lines.push(
+ 'A service can be a core spine service, a swappable capability seam, or a bundle/composition point. The graph shows the package that owns the service declaration, known implementation packages, and packages that consume the service directly.',
+ '',
+ '```mermaid',
+ 'flowchart LR',
+ )
+ for (const role of SERVICE_ROLES) {
+ const svc = nodeId('svc', role.key)
+ const owner = nodeId('pkg', role.pkg)
+ addNode(owner, role.pkg)
+ addNode(svc, `ctx.${role.key}
${role.title}`)
+ addEdge(owner, svc)
+ for (const impl of role.implementations ?? []) {
+ addNode(nodeId('pkg', impl), impl)
+ addEdge(nodeId('pkg', impl), svc)
+ }
+ for (const consumer of role.consumers ?? []) {
+ addNode(nodeId('pkg', consumer), consumer)
+ addEdge(svc, nodeId('pkg', consumer))
+ }
+ }
+ lines.push(...nodes.values(), ...[...edges].sort())
+ lines.push('```', '', '| ctx key | Role | Owner | Implementations | Direct consumers | Note |', '| --- | --- | --- | --- | --- | --- |')
+ for (const role of SERVICE_ROLES) {
+ lines.push(`| \`ctx.${role.key}\` | \`${role.mode}\` | ${pkgLink(pkgsByShort.get(role.pkg), role.pkg)} | ${pkgList(role.implementations, pkgsByShort)} | ${pkgList(role.consumers, pkgsByShort)} | ${tableCell(role.note)} |`)
+ }
+ lines.push('')
+ return lines.join('\n')
+}
+
+function parseExampleCordis(rel: string): ExamplePlugin[] {
+ const text = readFileSync(resolve(root, rel), 'utf8')
+ const plugins: ExamplePlugin[] = []
+ let current: { id: string; name?: string } | null = null
+ const flush = (): void => {
+ if (current?.name) plugins.push({ id: current.id, name: current.name })
+ }
+ for (const line of text.split('\n')) {
+ const id = /^-\s+id:\s+(.+?)\s*$/.exec(line)
+ if (id?.[1] !== undefined) {
+ flush()
+ current = { id: stripYamlScalar(id[1]) }
+ continue
+ }
+ const name = /^\s+name:\s+(.+?)\s*$/.exec(line)
+ if (name?.[1] !== undefined && current) current.name = stripYamlScalar(name[1])
+ }
+ flush()
+ return plugins
+}
+
+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')
+ 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.',
+ '',
+ '```mermaid',
+ 'flowchart LR',
+ )
+ const bundleTargets: Record = {
+ '@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)}
${escLabel(plugin.name)}"]`)
+ lines.push(` ${nodeId('cfg', example.id)} --> ${pluginNode}`)
+ const bundle = bundleTargets[plugin.name]
+ if (bundle !== undefined) lines.push(` ${pluginNode} --> ${bundle}`)
+ }
+ 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 |',
+ '| --- | --- | --- |',
+ )
+ 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')
+}
+
+function collectEventRelations(): Map {
+ const out = new Map()
+ const ensure = (event: string): EventRelation => {
+ const existing = out.get(event)
+ if (existing) return existing
+ const next = { dispatchers: new Map>(), listeners: new Set() }
+ out.set(event, next)
+ return next
+ }
+ for (const rel of globSync('packages/*/*/src/**/*.ts', { cwd: root }).sort()) {
+ const [, , leaf] = rel.split('/')
+ if (leaf === undefined) continue
+ const text = readFileSync(resolve(root, rel), 'utf8')
+ const sf = ts.createSourceFile(rel, text, ts.ScriptTarget.Latest, true)
+ const visit = (node: ts.Node): void => {
+ if (ts.isCallExpression(node) && ts.isPropertyAccessExpression(node.expression)) {
+ const method = node.expression.name.text
+ if (!isCordisContextReceiver(node.expression, sf)) {
+ ts.forEachChild(node, visit)
+ return
+ }
+ if (method === 'on') {
+ const event = eventArg(node.arguments, method)
+ if (event) ensure(event).listeners.add(leaf)
+ } else if (method === 'emit' || method === 'parallel' || method === 'serial' || method === 'waterfall') {
+ const event = eventArg(node.arguments, method)
+ if (event) {
+ const relation = ensure(event)
+ const methods = relation.dispatchers.get(leaf) ?? new Set()
+ methods.add(method)
+ relation.dispatchers.set(leaf, methods)
+ }
+ }
+ }
+ ts.forEachChild(node, visit)
+ }
+ visit(sf)
+ }
+ for (const entry of DYNAMIC_EVENT_DISPATCHERS) {
+ const relation = ensure(entry.event)
+ const methods = relation.dispatchers.get(entry.pkg) ?? new Set()
+ methods.add(entry.method)
+ relation.dispatchers.set(entry.pkg, methods)
+ }
+ return out
+}
+
+function isCordisContextReceiver(expr: ts.PropertyAccessExpression, sf: ts.SourceFile): boolean {
+ const target = expr.expression.getText(sf)
+ return target === 'ctx' || target === 'this.ctx'
+}
+
+function eventArg(args: ts.NodeArray, method: string): string | undefined {
+ if (method === 'waterfall') {
+ const arg = args.find(ts.isStringLiteralLike)
+ return arg?.text
+ }
+ const first = args[0]
+ return first && ts.isStringLiteralLike(first) ? first.text : undefined
+}
+
+function relationPackages(map: Map>, pkgsByShort: Map): string {
+ if (map.size === 0) return '-'
+ return [...map.entries()]
+ .sort(([a], [b]) => a.localeCompare(b))
+ .map(([pkg, methods]) => `${pkgLink(pkgsByShort.get(pkg), pkg)} (${[...methods].sort().map(m => `\`${m}\``).join(', ')})`)
+ .join(', ')
+}
+
+function listenerPackages(listeners: Set, pkgsByShort: Map): string {
+ if (listeners.size === 0) return '-'
+ return [...listeners].sort().map(pkg => pkgLink(pkgsByShort.get(pkg), pkg)).join(', ')
+}
+
+function renderEventRelations(pkgs: Pkg[]): string {
+ const events = collectEvents()
+ const relations = collectEventRelations()
+ const pkgsByShort = new Map(pkgs.map(pkg => [pkg.short, pkg]))
+ const lines = generatedHeader('Event Producer And Consumer Matrix', 'hybrid generated: Cordis event declarations and most producer/listener edges are AST-scanned; dynamic dispatch sites are classified in `scripts/gen-doc-graphs.ts`')
+ lines.push(
+ '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 |',
+ '| --- | --- | --- | --- | --- |',
+ )
+ for (const event of [...events].sort((a, b) => a.name.localeCompare(b.name))) {
+ const relation = relations.get(event.name) ?? { dispatchers: new Map>(), listeners: new Set() }
+ lines.push(`| \`${event.name}\` | \`${event.mode}\` | [\`${event.source}\`](../../${event.source.split(':')[0]}) | ${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()
+ if (extra.length > 0) {
+ lines.push('', '## Non-harness or undeclared event strings seen in package source', '', '| Event string | Dispatchers | Listeners |', '| --- | --- | --- |')
+ for (const event of extra) {
+ const relation = relations.get(event)
+ if (!relation) continue
+ lines.push(`| \`${event}\` | ${relationPackages(relation.dispatchers, pkgsByShort)} | ${listenerPackages(relation.listeners, pkgsByShort)} |`)
+ }
+ }
+ lines.push('')
+ return lines.join('\n')
+}
+
+async function renderToolAffordance(): Promise {
+ 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()
+ 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, ''))}
${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 shows the durable session event path separately from live `agent/*` notifications.',
+ '',
+ '```mermaid',
+ 'sequenceDiagram',
+ ' participant User',
+ ' participant Agent',
+ ' participant Loop',
+ ' participant Prompt as ctx.systemPrompt',
+ ' participant LLM as ctx.llm',
+ ' participant Tools as ctx.tools',
+ ' participant Session',
+ ' participant Persistence',
+ ' User->>Agent: send(content)',
+ ' Agent->>Loop: queued work wakes driver',
+ ' Loop->>Session: turn/start + user/message',
+ ' Loop-->>User: agent/turn-start',
+ ' Loop->>Prompt: system-prompt/assemble waterfall',
+ ' Loop-->>Loop: agent/pre-step serial checkpoint',
+ ' Loop->>Session: step/start',
+ ' Loop->>LLM: agent/request waterfall, then llm/stream waterfall',
+ ' LLM-->>Loop: StreamChunk*',
+ ' Loop->>Session: assistant/chunk*',
+ ' Loop-->>User: agent/stream-chunk* (master live mirror)',
+ ' Loop->>Session: assistant/message',
+ ' Loop->>Tools: tools/execute waterfall for each tool-call',
+ ' Tools-->>Session: tool-owned events when applicable',
+ ' Loop->>Session: tool/result',
+ ' Loop-->>Loop: agent/turn-continuation waterfall',
+ ' Loop->>Session: turn/end',
+ ' Loop->>Persistence: session/flush parallel checkpoint',
+ ' Loop-->>User: agent/status idle',
+ '```',
+ '',
+ 'Future pressure from the hooks stack: PR #129 removes the live `agent/stream-chunk` mirror and leaves durable `assistant/chunk` on `session/event` as the authoritative token stream. Consumers that need replayable transcript data should already treat `session/event` as the load-bearing path.',
+ '',
+ ].join('\n')
+}
+
+function renderToolPipeline(): string {
+ return [
+ ...generatedHeader('Tool Execution Pipeline', 'curated Mermaid flow; exact tool schemas and event signatures live in generated catalogs'),
+ 'This graph shows where policy, hooks, sandboxing, and future filesystem guards fit without changing the loop. The key extension point is the `tools/execute` waterfall.',
+ '',
+ '```mermaid',
+ 'flowchart TD',
+ ' model["Assistant message contains tool-call block"]',
+ ' call["Session event: tool/call"]',
+ ' waterfall["ctx.tools.execute()
tools/execute waterfall"]',
+ ' policy["Policy / permission / hooks listener"]',
+ ' body["Registered tool execute() body"]',
+ ' owned["Tool-owned session events
todo/write, future fs policy facts"]',
+ ' result["Session event: tool/result"]',
+ ' ui["UI presentation
presentCall / presentResult"]',
+ ' model --> call --> waterfall',
+ ' waterfall --> policy',
+ ' policy -->|next()| body',
+ ' policy -->|veto / throw| result',
+ ' body --> owned',
+ ' body --> result',
+ ' call --> ui',
+ ' result --> ui',
+ '```',
+ '',
+ 'Future pressure from the fs stack: PR #128 snapshots a policy rejection card. The graph keeps the veto path explicit because filesystem read-before-edit checks, permission prompts, and hook bridges all belong on this path.',
+ '',
+ ].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
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
model-facing name"]',
+ ' registry["ctx.subagents provider registry"]',
+ ' spawn["spawn provider
fresh child session"]',
+ ' fork["fork provider
seeded from completed-turn prefix"]',
+ ' acp["ACP provider
out-of-process child"]',
+ ' child["Child AgentHandle
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
events, tools, adapters, timers"]',
+ ' reload["HMR / fiber.dispose()"]',
+ ' disposers["Run disposers in owner fiber"]',
+ ' quiescence["Owned AgentHandle.dispose()
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'),
+ 'This graph explains what a snapshot scenario proves: recorded real-model session logs are replayed keylessly, then ACP stdout is normalized and diffed.',
+ '',
+ '```mermaid',
+ 'sequenceDiagram',
+ ' participant Recorder as Real API recording',
+ ' participant Fixture as snapshot fixture',
+ ' participant Replay as llm-replay adapter',
+ ' participant ACP as acp-agent subprocess',
+ ' participant Golden as stdout golden',
+ ' Recorder->>Fixture: session.jsonl + workspace inputs',
+ ' Fixture->>Replay: recorded StreamChunk script',
+ ' Replay->>ACP: deterministic llm/stream chunks',
+ ' ACP->>Golden: normalized sessionUpdate stream',
+ ' Golden-->>ACP: diff must be empty',
+ '```',
+ '',
+ 'Future pressure from the fs stack: policy rejection scenarios are valuable because they prove both world state and failed tool-card rendering, not just that replay returns text.',
+ '',
+ ].join('\n')
+}
+
+async function renderDocs(): Promise {
+ 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() },
+ ]
+ docs.unshift({ rel: `${OUT_DIR}/README.md`, content: renderIndex(docs) })
+ return docs
+}
+
+function renderIndex(docs: GraphDoc[]): string {
+ const labels: Record = {
+ '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',
+ }
+ const modes: Record = {
+ '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',
+ }
+ 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).',
+ '',
+ '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 |',
+ '| --- | --- |',
+ ...docs.map((doc) => {
+ const file = doc.rel.split('/').at(-1) ?? doc.rel
+ return `| [${labels[file] ?? file}](${file}) | \`${modes[file] ?? 'generated'}\` |`
+ }),
+ '',
+ 'Regenerate with `pnpm run gen-doc-graphs`; verify freshness with `pnpm run verify-doc-graphs`.',
+ '',
+ ].join('\n')
+}
+
+async function main(): Promise {
+ const docs = await renderDocs()
+ if (process.argv.includes('--check')) {
+ const stale: string[] = []
+ for (const doc of docs) {
+ const abs = resolve(root, doc.rel)
+ const committed = existsSync(abs) ? readFileSync(abs, 'utf8') : null
+ if (committed !== doc.content) stale.push(doc.rel)
+ }
+ if (stale.length === 0) {
+ console.log(`gen-doc-graphs: ${docs.length} graph doc(s) are up to date.`)
+ return
+ }
+ console.error(`gen-doc-graphs: stale graph doc(s): ${stale.join(', ')}. Run \`pnpm run gen-doc-graphs\` and commit the result.`)
+ process.exit(1)
+ }
+
+ mkdirSync(resolve(root, OUT_DIR), { recursive: true })
+ for (const doc of docs) 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()
+}
From 8caf9231967a4761d6c1cd31ee5516f319864969 Mon Sep 17 00:00:00 2001
From: Tianyi Cui <53024+tianyicui@users.noreply.github.com>
Date: Fri, 3 Jul 2026 01:32:01 +0800
Subject: [PATCH 2/5] docs(graphs): verify mermaid syntax
---
.github/workflows/ci.yml | 6 +-
docs/development.md | 1 +
docs/graphs/agent-lifecycle.md | 36 +-
docs/graphs/tool-execution-pipeline.md | 20 +-
.../2026-07-03-documentation-graph-atlas.md | 3 +-
package.json | 6 +-
pnpm-lock.yaml | 1157 ++++++++++++++++-
scripts/gen-doc-graphs.ts | 56 +-
scripts/verify-mermaid.ts | 107 ++
9 files changed, 1328 insertions(+), 64 deletions(-)
create mode 100644 scripts/verify-mermaid.ts
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 9064a38cea..4212717339 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -49,10 +49,10 @@ jobs:
# Doc-sync gates (doc-sync-enforcement RFC). doc-typecheck compiles the
# fenced ts blocks against the root project-reference graph. The cordis
- # catalog freshness check, type-equiv check, and markdown wrap/link checks
- # only read source. Same `doc-sync` script the pre-push hook runs
+ # catalog freshness check, type-equiv check, Mermaid syntax check, and
+ # markdown wrap/link checks only read source. Same `doc-sync` script the pre-push hook runs
# (quality-gates RFC: one source of truth).
- - name: Doc-sync gates (doc code blocks + cordis catalog + type-equiv + markdown wrap/links)
+ - name: Doc-sync gates (doc code blocks + catalogs + mermaid + markdown)
run: pnpm run doc-sync
# Module-graph freshness: regenerate docs/module-graph.md from the
diff --git a/docs/development.md b/docs/development.md
index 965f23d90f..03a2c73c62 100644
--- a/docs/development.md
+++ b/docs/development.md
@@ -99,6 +99,7 @@ pnpm run verify-cordis-catalog # fail if the cordis events/services catalog is
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 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
pnpm run verify-type-equiv # fail if a ```ts type-equiv doc block drifts from its source type
pnpm run doc-sync # doc-typecheck, generated doc freshness, markdown wrap/link, and type-equiv verification
pnpm run gen-module-graph # regenerate docs/module-graph.md from package peerDeps
diff --git a/docs/graphs/agent-lifecycle.md b/docs/graphs/agent-lifecycle.md
index cc25e25694..36c8702a95 100644
--- a/docs/graphs/agent-lifecycle.md
+++ b/docs/graphs/agent-lifecycle.md
@@ -11,31 +11,31 @@ This sequence is the visual companion to [architecture.md](../architecture.md#lo
sequenceDiagram
participant User
participant Agent
- participant Loop
+ participant Driver
participant Prompt as ctx.systemPrompt
participant LLM as ctx.llm
participant Tools as ctx.tools
participant Session
participant Persistence
User->>Agent: send(content)
- Agent->>Loop: queued work wakes driver
- Loop->>Session: turn/start + user/message
- Loop-->>User: agent/turn-start
- Loop->>Prompt: system-prompt/assemble waterfall
- Loop-->>Loop: agent/pre-step serial checkpoint
- Loop->>Session: step/start
- Loop->>LLM: agent/request waterfall, then llm/stream waterfall
- LLM-->>Loop: StreamChunk*
- Loop->>Session: assistant/chunk*
- Loop-->>User: agent/stream-chunk* (master live mirror)
- Loop->>Session: assistant/message
- Loop->>Tools: tools/execute waterfall for each tool-call
+ Agent->>Driver: queued work wakes driver
+ Driver->>Session: turn/start + user/message
+ Driver-->>User: agent/turn-start
+ 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*
+ Driver-->>User: agent/stream-chunk* (master live mirror)
+ Driver->>Session: assistant/message
+ Driver->>Tools: tools/execute waterfall for each tool-call
Tools-->>Session: tool-owned events when applicable
- Loop->>Session: tool/result
- Loop-->>Loop: agent/turn-continuation waterfall
- Loop->>Session: turn/end
- Loop->>Persistence: session/flush parallel checkpoint
- Loop-->>User: agent/status idle
+ Driver->>Session: tool/result
+ Driver-->>Driver: agent/turn-continuation waterfall
+ Driver->>Session: turn/end
+ Driver->>Persistence: session/flush parallel checkpoint
+ Driver-->>User: agent/status idle
```
Future pressure from the hooks stack: PR #129 removes the live `agent/stream-chunk` mirror and leaves durable `assistant/chunk` on `session/event` as the authoritative token stream. Consumers that need replayable transcript data should already treat `session/event` as the load-bearing path.
diff --git a/docs/graphs/tool-execution-pipeline.md b/docs/graphs/tool-execution-pipeline.md
index 7fde39af83..079250ca90 100644
--- a/docs/graphs/tool-execution-pipeline.md
+++ b/docs/graphs/tool-execution-pipeline.md
@@ -10,21 +10,21 @@ This graph shows where policy, hooks, sandboxing, and future filesystem guards f
```mermaid
flowchart TD
model["Assistant message contains tool-call block"]
- call["Session event: tool/call"]
+ toolCall["Session event: tool/call"]
waterfall["ctx.tools.execute()
tools/execute waterfall"]
policy["Policy / permission / hooks listener"]
- body["Registered tool execute() body"]
+ toolBody["Registered tool execute() body"]
owned["Tool-owned session events
todo/write, future fs policy facts"]
- result["Session event: tool/result"]
+ toolResult["Session event: tool/result"]
ui["UI presentation
presentCall / presentResult"]
- model --> call --> waterfall
+ model --> toolCall --> waterfall
waterfall --> policy
- policy -->|next()| body
- policy -->|veto / throw| result
- body --> owned
- body --> result
- call --> ui
- result --> ui
+ policy -->|next| toolBody
+ policy -->|veto / throw| toolResult
+ toolBody --> owned
+ toolBody --> toolResult
+ toolCall --> ui
+ toolResult --> ui
```
Future pressure from the fs stack: PR #128 snapshots a policy rejection card. The graph keeps the veto path explicit because filesystem read-before-edit checks, permission prompts, and hook bridges all belong on this path.
diff --git a/docs/rfc/implemented/process/2026-07-03-documentation-graph-atlas.md b/docs/rfc/implemented/process/2026-07-03-documentation-graph-atlas.md
index e707786c14..97c7b13bf6 100644
--- a/docs/rfc/implemented/process/2026-07-03-documentation-graph-atlas.md
+++ b/docs/rfc/implemented/process/2026-07-03-documentation-graph-atlas.md
@@ -53,6 +53,7 @@ The hybrid pages must fail loud when their manifests are stale:
- The capability seam graph imports the Cordis service collector and asserts every discovered harness `ctx.` 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.
## Format choices
@@ -62,5 +63,5 @@ Use Mermaid for committed diagrams because GitHub renders it in Markdown and it
- Maintainers get visual entry points for topology, seams, event flow, lifecycle, session replay, 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`, so graph drift is caught with the other doc freshness gates.
+- `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.
diff --git a/package.json b/package.json
index 3a35adf5e8..48bd1ae96b 100644
--- a/package.json
+++ b/package.json
@@ -29,6 +29,7 @@
"verify-md-links": "tsx scripts/verify-md-links.ts",
"verify-doc-refs": "tsx scripts/verify-doc-refs.ts",
"verify-package-paths": "tsx scripts/verify-package-paths.ts",
+ "verify-mermaid": "tsx scripts/verify-mermaid.ts",
"verify-rfc-classification": "tsx scripts/verify-rfc-classification.ts",
"verify-type-equiv": "tsx scripts/verify-type-equiv.ts",
"verify-node-next-types": "tsx scripts/verify-node-next-types.ts",
@@ -41,7 +42,7 @@
"gen-module-graph": "tsx scripts/gen-module-graph.ts",
"verify-module-graph": "tsx scripts/gen-module-graph.ts --check",
"constraints": "tsx scripts/check-workspace-constraints.ts",
- "doc-sync": "pnpm run doc-typecheck && pnpm run verify-cordis-catalog && pnpm run verify-tool-catalog && pnpm run verify-doc-graphs && pnpm run verify-md-wrap && pnpm run verify-md-links && pnpm run verify-doc-refs && pnpm run verify-package-paths && pnpm run verify-rfc-classification && pnpm run verify-type-equiv",
+ "doc-sync": "pnpm run doc-typecheck && pnpm run verify-cordis-catalog && pnpm run verify-tool-catalog && pnpm run verify-doc-graphs && pnpm run verify-md-wrap && pnpm run verify-md-links && pnpm run verify-doc-refs && pnpm run verify-package-paths && pnpm run verify-mermaid && pnpm run verify-rfc-classification && pnpm run verify-type-equiv",
"hygiene": "pnpm run knip && pnpm run publint && pnpm run constraints && pnpm run verify-node-next-types",
"demo:echo": "node --expose-internals --import tsx packages/ui/stdio-agent/src/bin.ts examples/echo-agent/cordis.yml",
"demo:coding": "node --expose-internals --import tsx packages/ui/stdio-agent/src/bin.ts examples/coding-agent/cordis.yml",
@@ -51,15 +52,18 @@
"devDependencies": {
"@agentclientprotocol/sdk": "0.25.1",
"@stylistic/eslint-plugin": "^5.10.0",
+ "@types/jsdom": "^28.0.3",
"@types/mdast": "^4.0.4",
"@types/node": "^25.3.5",
"@vitest/coverage-v8": "^4.1.8",
"eslint": "^10.4.1",
"fast-check": "^4.8.0",
+ "jsdom": "29.1.1",
"knip": "^6.16.1",
"lefthook": "^2.1.9",
"mdast-util-from-markdown": "^2.0.3",
"mdast-util-gfm": "^3.1.0",
+ "mermaid": "11.16.0",
"micromark-extension-gfm": "^3.0.0",
"publint": "^0.3.21",
"tsdown": "^0.22.2",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 185ed72b33..66643dcbc3 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -14,6 +14,9 @@ importers:
'@stylistic/eslint-plugin':
specifier: ^5.10.0
version: 5.10.0(eslint@10.5.0(jiti@2.7.0))
+ '@types/jsdom':
+ specifier: ^28.0.3
+ version: 28.0.3
'@types/mdast':
specifier: ^4.0.4
version: 4.0.4
@@ -29,6 +32,9 @@ importers:
fast-check:
specifier: ^4.8.0
version: 4.8.0
+ jsdom:
+ specifier: 29.1.1
+ version: 29.1.1
knip:
specifier: ^6.16.1
version: 6.16.1
@@ -41,6 +47,9 @@ importers:
mdast-util-gfm:
specifier: ^3.1.0
version: 3.1.0
+ mermaid:
+ specifier: 11.16.0
+ version: 11.16.0
micromark-extension-gfm:
specifier: ^3.0.0
version: 3.0.0
@@ -64,7 +73,7 @@ importers:
version: 6.1.1(typescript@6.0.3)(vite@8.0.16(@types/node@25.9.3)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
vitest:
specifier: ^4.1.8
- version: 4.1.8(@types/node@25.9.3)(@vitest/coverage-v8@4.1.8)(vite@8.0.16(@types/node@25.9.3)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
+ version: 4.1.8(@types/node@25.9.3)(@vitest/coverage-v8@4.1.8)(jsdom@29.1.1)(vite@8.0.16(@types/node@25.9.3)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
packages/bash/bash:
devDependencies:
@@ -884,6 +893,9 @@ packages:
peerDependencies:
zod: ^3.25.0 || ^4.0.0
+ '@antfu/install-pkg@1.1.0':
+ resolution: {integrity: sha512-MGQsmw10ZyI+EJo45CdSER4zEb+p31LpDAFp2Z3gkSd1yqVZGi0Ebx++YTEMonJy4oChEMLsxZ64j8FH6sSqtQ==}
+
'@anthropic-ai/sdk@0.91.1':
resolution: {integrity: sha512-LAmu761tSN9r66ixvmciswUj/ZC+1Q4iAfpedTfSVLeswRwnY3n2Nb6Tsk+cLPP28aLOPWeMgIuTuCcMC6W/iw==}
hasBin: true
@@ -893,6 +905,21 @@ packages:
zod:
optional: true
+ '@asamuzakjp/css-color@5.1.11':
+ resolution: {integrity: sha512-KVw6qIiCTUQhByfTd78h2yD1/00waTmm9uy/R7Ck/ctUyAPj+AEDLkQIdJW0T8+qGgj3j5bpNKK7Q3G+LedJWg==}
+ engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0}
+
+ '@asamuzakjp/dom-selector@7.1.1':
+ resolution: {integrity: sha512-67RZDnYRc8H/8MLDgQCDE//zoqVFwajkepHZgmXrbwybzXOEwOWGPYGmALYl9J2DOLfFPPs6kKCqmbzV895hTQ==}
+ engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0}
+
+ '@asamuzakjp/generational-cache@1.0.1':
+ resolution: {integrity: sha512-wajfB8KqzMCN2KGNFdLkReeHncd0AslUSrvHVvvYWuU8ghncRJoA50kT3zP9MVL0+9g4/67H+cdvBskj9THPzg==}
+ engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0}
+
+ '@asamuzakjp/nwsapi@2.3.9':
+ resolution: {integrity: sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q==}
+
'@aws-crypto/crc32@5.2.0':
resolution: {integrity: sha512-nLbCWqQNgUiwwtFsen1AdzAtvuLRsQS8rYgMuxCrdKf9kOssamGLuPwyTY9wyYblNr9+1XM8v6zoDTPPSIeANg==}
engines: {node: '>=16.0.0'}
@@ -1044,6 +1071,16 @@ packages:
resolution: {integrity: sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==}
engines: {node: '>=18'}
+ '@braintree/sanitize-url@7.1.2':
+ resolution: {integrity: sha512-jigsZK+sMF/cuiB7sERuo9V7N9jx+dhmHHnQyDSVdpZwVutaBu7WvNYqMDLSgFgfB30n452TP3vjDAvFC973mA==}
+
+ '@bramus/specificity@2.4.2':
+ resolution: {integrity: sha512-ctxtJ/eA+t+6q2++vj5j7FYX3nRu311q1wfYH3xjlLOsczhlhxAg2FWNUXhpGvAw3BWo1xBcvOV6/YLc2r5FJw==}
+ hasBin: true
+
+ '@chevrotain/types@11.1.2':
+ resolution: {integrity: sha512-U+HFai5+zmJCkK86QsaJtoITlboZHBqrVketcO2ROv865xfCMSFpELQoz1GkX5GzME8pTa+3kbKrZHQtI0gdbw==}
+
'@cordisjs/plugin-include@1.0.4':
resolution: {integrity: sha512-b1Hm1wmue0v7d/jayoXoBjCV2J14XWTL5yyDZEYeL2L9HgcyTq6JbCw99ozSbei98uAbkwq/pBhimsp/HsySeg==}
peerDependencies:
@@ -1060,6 +1097,42 @@ packages:
peerDependencies:
cordis: ^4.0.0-rc.5
+ '@csstools/color-helpers@6.1.0':
+ resolution: {integrity: sha512-064IFJdjTfUqnjpCVpMOdbr8FLQBhinbZj6yRv2An2E41O/pLEXqfFRWqGq/SxlE5PEUYTlvWsG2r8MswAVvkg==}
+ engines: {node: '>=20.19.0'}
+
+ '@csstools/css-calc@3.2.1':
+ resolution: {integrity: sha512-DtdHlgXh5ZkA43cwBcAm+huzgJiwx3ZTWVjBs94kwz2xKqSimDA3lBgCjphYgwgVUMWatSM0pDd8TILB1yrVVg==}
+ engines: {node: '>=20.19.0'}
+ peerDependencies:
+ '@csstools/css-parser-algorithms': ^4.0.0
+ '@csstools/css-tokenizer': ^4.0.0
+
+ '@csstools/css-color-parser@4.1.9':
+ resolution: {integrity: sha512-paQcIaOO53Rk5+YrBaBjm/SgrV4INImjo2BT1DtQRYr+XeTRbeAYlS+jxXp9drqvKmtFnWRJKIalDLhZZDu42A==}
+ engines: {node: '>=20.19.0'}
+ peerDependencies:
+ '@csstools/css-parser-algorithms': ^4.0.0
+ '@csstools/css-tokenizer': ^4.0.0
+
+ '@csstools/css-parser-algorithms@4.0.0':
+ resolution: {integrity: sha512-+B87qS7fIG3L5h3qwJ/IFbjoVoOe/bpOdh9hAjXbvx0o8ImEmUsGXN0inFOnk2ChCFgqkkGFQ+TpM5rbhkKe4w==}
+ engines: {node: '>=20.19.0'}
+ peerDependencies:
+ '@csstools/css-tokenizer': ^4.0.0
+
+ '@csstools/css-syntax-patches-for-csstree@1.1.6':
+ resolution: {integrity: sha512-TcJCWFbXLPpJYq6z7bfOyjWYJDiDg2/I4gyUC9pqPNqHFRIey0EB0q0L5cSnQDfWJg8Jd6VadakxdIez/3zkqQ==}
+ peerDependencies:
+ css-tree: ^3.2.1
+ peerDependenciesMeta:
+ css-tree:
+ optional: true
+
+ '@csstools/css-tokenizer@4.0.0':
+ resolution: {integrity: sha512-QxULHAm7cNu72w97JUNCBFODFaXpbDg+dP8b/oWFAZ2MTRppA3U00Y2L1HqaS4J6yBqxwa/Y3nMBaxVKbB/NsA==}
+ engines: {node: '>=20.19.0'}
+
'@earendil-works/pi-ai@0.79.3':
resolution: {integrity: sha512-lMSput/haP5uZAGbXhS5rAYd3GB7GYdJkoAUxg3VFummBeqGqGqllaTWrbHFN12kVGyVfWHhdySNXkiqVh65Iw==}
engines: {node: '>=22.19.0'}
@@ -1269,6 +1342,15 @@ packages:
resolution: {integrity: sha512-+CNAzxglkrpNf/kKywqQfk74QjtceuOE7Qm+AF8miRvPF/wmmK5+OJOgVh3AVTT3RP2mH3+FOaxlE5v72owk0A==}
engines: {node: ^20.19.0 || ^22.13.0 || >=24}
+ '@exodus/bytes@1.15.1':
+ resolution: {integrity: sha512-S6mL0yNB/Abt9Ei4tq8gDhcczc4S3+vQ4ra7vxnAf+YHC02srtqxKKZghx2Dq6p0e66THKwR6r8N6P95wEty7Q==}
+ engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0}
+ peerDependencies:
+ '@noble/hashes': ^1.8.0 || ^2.0.0
+ peerDependenciesMeta:
+ '@noble/hashes':
+ optional: true
+
'@google/genai@1.52.0':
resolution: {integrity: sha512-gwSvbpiN/17O9TbsqSsE/OzZcpv5Fo4RQjdngGgogtuB9RsyJ8ZHhX5KjHj1bp5N9snN2eK8LDGXSaWW2hof8Q==}
engines: {node: '>=20.0.0'}
@@ -1298,6 +1380,12 @@ packages:
resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==}
engines: {node: '>=18.18'}
+ '@iconify/types@2.0.0':
+ resolution: {integrity: sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==}
+
+ '@iconify/utils@3.1.3':
+ resolution: {integrity: sha512-LPKOXPn/zV+zis1oOfGWogaXVpqUybF3ZS6SCZIsz8vg0ivVp9+fVqyYB7xq0aiST/VhUQYGO1qo6uoYSiEJqw==}
+
'@jridgewell/gen-mapping@0.3.13':
resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==}
@@ -1311,6 +1399,9 @@ packages:
'@jridgewell/trace-mapping@0.3.31':
resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==}
+ '@mermaid-js/parser@1.2.0':
+ resolution: {integrity: sha512-oYPyv8A4As1yH5Bx+04iQEQxXuIQDe0GKCNSRgao6z8AM9jixXIfP0vsppRLvGf+nKIOb9/LdpWA4YuJiVvESA==}
+
'@mistralai/mistralai@2.2.1':
resolution: {integrity: sha512-uKU8CZmL2RzYKmplsU01hii4p3pe4HqJefpWNRWXm1Tcm0Sm4xXfwSLIy4k7ZCPlbETCGcp69E7hZs+WOJ5itQ==}
@@ -1844,6 +1935,99 @@ packages:
'@types/chai@5.2.3':
resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==}
+ '@types/d3-array@3.2.2':
+ resolution: {integrity: sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw==}
+
+ '@types/d3-axis@3.0.6':
+ resolution: {integrity: sha512-pYeijfZuBd87T0hGn0FO1vQ/cgLk6E1ALJjfkC0oJ8cbwkZl3TpgS8bVBLZN+2jjGgg38epgxb2zmoGtSfvgMw==}
+
+ '@types/d3-brush@3.0.6':
+ resolution: {integrity: sha512-nH60IZNNxEcrh6L1ZSMNA28rj27ut/2ZmI3r96Zd+1jrZD++zD3LsMIjWlvg4AYrHn/Pqz4CF3veCxGjtbqt7A==}
+
+ '@types/d3-chord@3.0.6':
+ resolution: {integrity: sha512-LFYWWd8nwfwEmTZG9PfQxd17HbNPksHBiJHaKuY1XeqscXacsS2tyoo6OdRsjf+NQYeB6XrNL3a25E3gH69lcg==}
+
+ '@types/d3-color@3.1.3':
+ resolution: {integrity: sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==}
+
+ '@types/d3-contour@3.0.6':
+ resolution: {integrity: sha512-BjzLgXGnCWjUSYGfH1cpdo41/hgdWETu4YxpezoztawmqsvCeep+8QGfiY6YbDvfgHz/DkjeIkkZVJavB4a3rg==}
+
+ '@types/d3-delaunay@6.0.4':
+ resolution: {integrity: sha512-ZMaSKu4THYCU6sV64Lhg6qjf1orxBthaC161plr5KuPHo3CNm8DTHiLw/5Eq2b6TsNP0W0iJrUOFscY6Q450Hw==}
+
+ '@types/d3-dispatch@3.0.7':
+ resolution: {integrity: sha512-5o9OIAdKkhN1QItV2oqaE5KMIiXAvDWBDPrD85e58Qlz1c1kI/J0NcqbEG88CoTwJrYe7ntUCVfeUl2UJKbWgA==}
+
+ '@types/d3-drag@3.0.7':
+ resolution: {integrity: sha512-HE3jVKlzU9AaMazNufooRJ5ZpWmLIoc90A37WU2JMmeq28w1FQqCZswHZ3xR+SuxYftzHq6WU6KJHvqxKzTxxQ==}
+
+ '@types/d3-dsv@3.0.7':
+ resolution: {integrity: sha512-n6QBF9/+XASqcKK6waudgL0pf/S5XHPPI8APyMLLUHd8NqouBGLsU8MgtO7NINGtPBtk9Kko/W4ea0oAspwh9g==}
+
+ '@types/d3-ease@3.0.2':
+ resolution: {integrity: sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==}
+
+ '@types/d3-fetch@3.0.7':
+ resolution: {integrity: sha512-fTAfNmxSb9SOWNB9IoG5c8Hg6R+AzUHDRlsXsDZsNp6sxAEOP0tkP3gKkNSO/qmHPoBFTxNrjDprVHDQDvo5aA==}
+
+ '@types/d3-force@3.0.10':
+ resolution: {integrity: sha512-ZYeSaCF3p73RdOKcjj+swRlZfnYpK1EbaDiYICEEp5Q6sUiqFaFQ9qgoshp5CzIyyb/yD09kD9o2zEltCexlgw==}
+
+ '@types/d3-format@3.0.4':
+ resolution: {integrity: sha512-fALi2aI6shfg7vM5KiR1wNJnZ7r6UuggVqtDA+xiEdPZQwy/trcQaHnwShLuLdta2rTymCNpxYTiMZX/e09F4g==}
+
+ '@types/d3-geo@3.1.0':
+ resolution: {integrity: sha512-856sckF0oP/diXtS4jNsiQw/UuK5fQG8l/a9VVLeSouf1/PPbBE1i1W852zVwKwYCBkFJJB7nCFTbk6UMEXBOQ==}
+
+ '@types/d3-hierarchy@3.1.7':
+ resolution: {integrity: sha512-tJFtNoYBtRtkNysX1Xq4sxtjK8YgoWUNpIiUee0/jHGRwqvzYxkq0hGVbbOGSz+JgFxxRu4K8nb3YpG3CMARtg==}
+
+ '@types/d3-interpolate@3.0.4':
+ resolution: {integrity: sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==}
+
+ '@types/d3-path@3.1.1':
+ resolution: {integrity: sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==}
+
+ '@types/d3-polygon@3.0.2':
+ resolution: {integrity: sha512-ZuWOtMaHCkN9xoeEMr1ubW2nGWsp4nIql+OPQRstu4ypeZ+zk3YKqQT0CXVe/PYqrKpZAi+J9mTs05TKwjXSRA==}
+
+ '@types/d3-quadtree@3.0.6':
+ resolution: {integrity: sha512-oUzyO1/Zm6rsxKRHA1vH0NEDG58HrT5icx/azi9MF1TWdtttWl0UIUsjEQBBh+SIkrpd21ZjEv7ptxWys1ncsg==}
+
+ '@types/d3-random@3.0.3':
+ resolution: {integrity: sha512-Imagg1vJ3y76Y2ea0871wpabqp613+8/r0mCLEBfdtqC7xMSfj9idOnmBYyMoULfHePJyxMAw3nWhJxzc+LFwQ==}
+
+ '@types/d3-scale-chromatic@3.1.0':
+ resolution: {integrity: sha512-iWMJgwkK7yTRmWqRB5plb1kadXyQ5Sj8V/zYlFGMUBbIPKQScw+Dku9cAAMgJG+z5GYDoMjWGLVOvjghDEFnKQ==}
+
+ '@types/d3-scale@4.0.9':
+ resolution: {integrity: sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==}
+
+ '@types/d3-selection@3.0.11':
+ resolution: {integrity: sha512-bhAXu23DJWsrI45xafYpkQ4NtcKMwWnAC/vKrd2l+nxMFuvOT3XMYTIj2opv8vq8AO5Yh7Qac/nSeP/3zjTK0w==}
+
+ '@types/d3-shape@3.1.8':
+ resolution: {integrity: sha512-lae0iWfcDeR7qt7rA88BNiqdvPS5pFVPpo5OfjElwNaT2yyekbM0C9vK+yqBqEmHr6lDkRnYNoTBYlAgJa7a4w==}
+
+ '@types/d3-time-format@4.0.3':
+ resolution: {integrity: sha512-5xg9rC+wWL8kdDj153qZcsJ0FWiFt0J5RB6LYUNZjwSnesfblqrI/bJ1wBdJ8OQfncgbJG5+2F+qfqnqyzYxyg==}
+
+ '@types/d3-time@3.0.4':
+ resolution: {integrity: sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==}
+
+ '@types/d3-timer@3.0.2':
+ resolution: {integrity: sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==}
+
+ '@types/d3-transition@3.0.9':
+ resolution: {integrity: sha512-uZS5shfxzO3rGlu0cC3bjmMFKsXv+SmZZcgp0KD22ts4uGXp5EVYGzu/0YdwZeKmddhcAccYtREJKkPfXkZuCg==}
+
+ '@types/d3-zoom@3.0.8':
+ resolution: {integrity: sha512-iqMC4/YlFCSlO8+2Ii1GGGliCAY4XdeG748w5vQUbevlbDu0zSjH/+jojorQVBK/se0j6DUFNPBGSqD3YWYnDw==}
+
+ '@types/d3@7.4.3':
+ resolution: {integrity: sha512-lZXZ9ckh5R8uiFVt8ogUNf+pIrK4EsWrx2Np75WvF/eTpJ0FMHNhjXk8CKEx/+gpHbNQyJWehbFaTvqmHWB3ww==}
+
'@types/debug@4.1.13':
resolution: {integrity: sha512-KSVgmQmzMwPlmtljOomayoR89W4FynCAi3E8PPs7vmDVPe84hT+vGPKkJfThkmXs0x0jAaa9U8uW8bbfyS2fWw==}
@@ -1856,6 +2040,12 @@ packages:
'@types/estree@1.0.9':
resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==}
+ '@types/geojson@7946.0.16':
+ resolution: {integrity: sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==}
+
+ '@types/jsdom@28.0.3':
+ resolution: {integrity: sha512-/HQ2uFoetFTXuye8vzIcHw2z6Fwi7Hi/qcgC+RoS9NCyewiqxhVGqlG+ViGB6lkax481R6dmhf1I7lIGlzJStQ==}
+
'@types/jsesc@2.5.1':
resolution: {integrity: sha512-9VN+6yxLOPLOav+7PwjZbxiID2bVaeq0ED4qSQmdQTdjnXJSaCVKTR58t15oqH1H5t8Ng2ZX1SabJVoN9Q34bw==}
@@ -1877,6 +2067,12 @@ packages:
'@types/retry@0.12.0':
resolution: {integrity: sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==}
+ '@types/tough-cookie@4.0.5':
+ resolution: {integrity: sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==}
+
+ '@types/trusted-types@2.0.7':
+ resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==}
+
'@types/unist@3.0.3':
resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==}
@@ -1939,6 +2135,9 @@ packages:
resolution: {integrity: sha512-QVLZu3ZPQEE+HICQyAMZ2yLQhxf0meY/wx6Hx14YcTNj13JB3qHlX3lJ02L3fLGHgERRH71kvYDwiXIguT3AjQ==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ '@upsetjs/venn.js@2.0.0':
+ resolution: {integrity: sha512-WbBhLrooyePuQ1VZxrJjtLvTc4NVfpOyKx0sKqioq9bX1C1m7Jgykkn8gLrtwumBioXIqam8DLxp88Adbue6Hw==}
+
'@vitest/coverage-v8@4.1.8':
resolution: {integrity: sha512-lt3kovsyHwYe00wq4D1ti0Z974fWj4NLp6siqiyEufUpyFwK9Yhi7rBhac9JL5aA0zoMrJqc4vYPZRUnI7l7nw==}
peerDependencies:
@@ -2022,6 +2221,9 @@ packages:
base64-js@1.5.1:
resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==}
+ bidi-js@1.0.3:
+ resolution: {integrity: sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==}
+
bignumber.js@9.3.1:
resolution: {integrity: sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ==}
@@ -2056,6 +2258,14 @@ packages:
resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==}
engines: {node: '>= 14.16.0'}
+ commander@7.2.0:
+ resolution: {integrity: sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==}
+ engines: {node: '>= 10'}
+
+ commander@8.3.0:
+ resolution: {integrity: sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==}
+ engines: {node: '>= 12'}
+
convert-source-map@2.0.0:
resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==}
@@ -2071,6 +2281,12 @@ packages:
'@cordisjs/plugin-loader':
optional: true
+ cose-base@1.0.3:
+ resolution: {integrity: sha512-s9whTXInMSgAp/NVXVNuVxVKzGH2qck3aQlVHxDCdAEPgtMKwc4Wq6/QKhgdEdgbLSi9rBTAcPoRa6JpiG4ksg==}
+
+ cose-base@2.2.0:
+ resolution: {integrity: sha512-AzlgcsCbUMymkADOJtQm3wO9S3ltPfYOFD5033keQn9NJzIbtnZj+UdBJe7DYml/8TdbtHJW3j58SOnKhWY/5g==}
+
cosmokit@1.8.1:
resolution: {integrity: sha512-PDBv4l90xZKrUsZ0vtoycgZpO/j4iFsqJXrAxsyBDsnQRI7ZMJXIjgDJsKNjd5L8jnVnnlrDCdhkFbTncgCVjQ==}
@@ -2078,10 +2294,177 @@ packages:
resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==}
engines: {node: '>= 8'}
+ css-tree@3.2.1:
+ resolution: {integrity: sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA==}
+ engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0}
+
+ cytoscape-cose-bilkent@4.1.0:
+ resolution: {integrity: sha512-wgQlVIUJF13Quxiv5e1gstZ08rnZj2XaLHGoFMYXz7SkNfCDOOteKBE6SYRfA9WxxI/iBc3ajfDoc6hb/MRAHQ==}
+ peerDependencies:
+ cytoscape: ^3.2.0
+
+ cytoscape-fcose@2.2.0:
+ resolution: {integrity: sha512-ki1/VuRIHFCzxWNrsshHYPs6L7TvLu3DL+TyIGEsRcvVERmxokbf5Gdk7mFxZnTdiGtnA4cfSmjZJMviqSuZrQ==}
+ peerDependencies:
+ cytoscape: ^3.2.0
+
+ cytoscape@3.34.0:
+ resolution: {integrity: sha512-62rNSrioXw93uliKFBwjukeQyeWwH2PqDrTac31r2P6464u3AUvTk0xS4LVvT251g7IgkFunrI48ZEZGjywSOg==}
+ engines: {node: '>=0.10'}
+
+ d3-array@2.12.1:
+ resolution: {integrity: sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ==}
+
+ d3-array@3.2.4:
+ resolution: {integrity: sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==}
+ engines: {node: '>=12'}
+
+ d3-axis@3.0.0:
+ resolution: {integrity: sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw==}
+ engines: {node: '>=12'}
+
+ d3-brush@3.0.0:
+ resolution: {integrity: sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ==}
+ engines: {node: '>=12'}
+
+ d3-chord@3.0.1:
+ resolution: {integrity: sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g==}
+ engines: {node: '>=12'}
+
+ d3-color@3.1.0:
+ resolution: {integrity: sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==}
+ engines: {node: '>=12'}
+
+ d3-contour@4.0.2:
+ resolution: {integrity: sha512-4EzFTRIikzs47RGmdxbeUvLWtGedDUNkTcmzoeyg4sP/dvCexO47AaQL7VKy/gul85TOxw+IBgA8US2xwbToNA==}
+ engines: {node: '>=12'}
+
+ d3-delaunay@6.0.4:
+ resolution: {integrity: sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A==}
+ engines: {node: '>=12'}
+
+ d3-dispatch@3.0.1:
+ resolution: {integrity: sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==}
+ engines: {node: '>=12'}
+
+ d3-drag@3.0.0:
+ resolution: {integrity: sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==}
+ engines: {node: '>=12'}
+
+ d3-dsv@3.0.1:
+ resolution: {integrity: sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==}
+ engines: {node: '>=12'}
+ hasBin: true
+
+ d3-ease@3.0.1:
+ resolution: {integrity: sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==}
+ engines: {node: '>=12'}
+
+ d3-fetch@3.0.1:
+ resolution: {integrity: sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw==}
+ engines: {node: '>=12'}
+
+ d3-force@3.0.0:
+ resolution: {integrity: sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==}
+ engines: {node: '>=12'}
+
+ d3-format@3.1.2:
+ resolution: {integrity: sha512-AJDdYOdnyRDV5b6ArilzCPPwc1ejkHcoyFarqlPqT7zRYjhavcT3uSrqcMvsgh2CgoPbK3RCwyHaVyxYcP2Arg==}
+ engines: {node: '>=12'}
+
+ d3-geo@3.1.1:
+ resolution: {integrity: sha512-637ln3gXKXOwhalDzinUgY83KzNWZRKbYubaG+fGVuc/dxO64RRljtCTnf5ecMyE1RIdtqpkVcq0IbtU2S8j2Q==}
+ engines: {node: '>=12'}
+
+ d3-hierarchy@3.1.2:
+ resolution: {integrity: sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA==}
+ engines: {node: '>=12'}
+
+ d3-interpolate@3.0.1:
+ resolution: {integrity: sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==}
+ engines: {node: '>=12'}
+
+ d3-path@1.0.9:
+ resolution: {integrity: sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg==}
+
+ d3-path@3.1.0:
+ resolution: {integrity: sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==}
+ engines: {node: '>=12'}
+
+ d3-polygon@3.0.1:
+ resolution: {integrity: sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg==}
+ engines: {node: '>=12'}
+
+ d3-quadtree@3.0.1:
+ resolution: {integrity: sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==}
+ engines: {node: '>=12'}
+
+ d3-random@3.0.1:
+ resolution: {integrity: sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ==}
+ engines: {node: '>=12'}
+
+ d3-sankey@0.12.3:
+ resolution: {integrity: sha512-nQhsBRmM19Ax5xEIPLMY9ZmJ/cDvd1BG3UVvt5h3WRxKg5zGRbvnteTyWAbzeSvlh3tW7ZEmq4VwR5mB3tutmQ==}
+
+ d3-scale-chromatic@3.1.0:
+ resolution: {integrity: sha512-A3s5PWiZ9YCXFye1o246KoscMWqf8BsD9eRiJ3He7C9OBaxKhAd5TFCdEx/7VbKtxxTsu//1mMJFrEt572cEyQ==}
+ engines: {node: '>=12'}
+
+ d3-scale@4.0.2:
+ resolution: {integrity: sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==}
+ engines: {node: '>=12'}
+
+ d3-selection@3.0.0:
+ resolution: {integrity: sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==}
+ engines: {node: '>=12'}
+
+ d3-shape@1.3.7:
+ resolution: {integrity: sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw==}
+
+ d3-shape@3.2.0:
+ resolution: {integrity: sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==}
+ engines: {node: '>=12'}
+
+ d3-time-format@4.1.0:
+ resolution: {integrity: sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==}
+ engines: {node: '>=12'}
+
+ d3-time@3.1.0:
+ resolution: {integrity: sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==}
+ engines: {node: '>=12'}
+
+ d3-timer@3.0.1:
+ resolution: {integrity: sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==}
+ engines: {node: '>=12'}
+
+ d3-transition@3.0.1:
+ resolution: {integrity: sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==}
+ engines: {node: '>=12'}
+ peerDependencies:
+ d3-selection: 2 - 3
+
+ d3-zoom@3.0.0:
+ resolution: {integrity: sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==}
+ engines: {node: '>=12'}
+
+ d3@7.9.0:
+ resolution: {integrity: sha512-e1U46jVP+w7Iut8Jt8ri1YsPOvFpg46k+K8TpCb0P+zjCkjkPnV7WzfDJzMHy1LnA+wj5pLT1wjO901gLXeEhA==}
+ engines: {node: '>=12'}
+
+ dagre-d3-es@7.0.14:
+ resolution: {integrity: sha512-P4rFMVq9ESWqmOgK+dlXvOtLwYg0i7u0HBGJER0LZDJT2VHIPAMZ/riPxqJceWMStH5+E61QxFra9kIS3AqdMg==}
+
data-uri-to-buffer@4.0.1:
resolution: {integrity: sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==}
engines: {node: '>= 12'}
+ data-urls@7.0.0:
+ resolution: {integrity: sha512-23XHcCF+coGYevirZceTVD7NdJOqVn+49IHyxgszm+JIiHLoB2TkmPtsYkNWT1pvRSGkc35L6NHs0yHkN2SumA==}
+ engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0}
+
+ dayjs@1.11.21:
+ resolution: {integrity: sha512-98IT+HOahAisibz/yjKbzuOBwYcjJ7BCLPzARyHiyEBmRz4fatF+KPJszEHXsGYjUG234aH/cOjW1wwTbKUZlA==}
+
debug@4.4.3:
resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==}
engines: {node: '>=6.0'}
@@ -2091,6 +2474,9 @@ packages:
supports-color:
optional: true
+ decimal.js@10.6.0:
+ resolution: {integrity: sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==}
+
decode-named-character-reference@1.3.0:
resolution: {integrity: sha512-GtpQYB283KrPp6nRw50q3U9/VfOutZOe103qlN7BPP6Ad27xYnOIWv4lPzo8HCAL+mMZofJ9KEy30fq6MfaK6Q==}
@@ -2100,6 +2486,9 @@ packages:
defu@6.1.7:
resolution: {integrity: sha512-7z22QmUWiQ/2d0KkdYmANbRUVABpZ9SNYyH5vx6PZ+nE5bcC0l7uFvEfHlyld/HcGBFTL536ClDt3DEcSlEJAQ==}
+ delaunator@5.1.0:
+ resolution: {integrity: sha512-AGrQ4QSgssa1NGmWmLPqN5NY2KajF5MqxetNEO+o0n3ZwZZeTmt7bBnvzHWrmkZFxGgr4HdyFgelzgi06otLuQ==}
+
dequal@2.0.3:
resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==}
engines: {node: '>=6'}
@@ -2111,6 +2500,9 @@ packages:
devlop@1.1.0:
resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==}
+ dompurify@3.4.11:
+ resolution: {integrity: sha512-zhlUV12GsaRzMsf9q5M254YhA4+VuF0fG+QFqu6aYpoGlKtz+w8//jBcGVYBgQkR5GHjUomejY84AV+/uPbWdw==}
+
dts-resolver@3.0.0:
resolution: {integrity: sha512-1T1f+z+4tl9XD+m+0HBgWoL/nm0bOIffyWaUuUSBlFg/86IWvfx+wjNaO/ybU0AJzG9/Mi5hBUgGV6zCmWEN7Q==}
engines: {node: ^22.18.0 || >=24.0.0}
@@ -2127,9 +2519,16 @@ packages:
resolution: {integrity: sha512-YGRs8knHhKHVShLkFET/rWAU8kmHbOV5LwN938RHI0pljAJ1Gf6SzXsSmRaEzcXTtOOmVqJ5+WtQPL5uigY50Q==}
engines: {node: '>=14'}
+ entities@8.0.0:
+ resolution: {integrity: sha512-zwfzJecQ/Uej6tusMqwAqU/6KL2XaB2VZ2Jg54Je6ahNBGNH6Ek6g3jjNCF0fG9EWQKGZNddNjU5F1ZQn/sBnA==}
+ engines: {node: '>=20.19.0'}
+
es-module-lexer@2.1.0:
resolution: {integrity: sha512-n27zTYMjYu1aj4MjCWzSP7G9r75utsaoc8m61weK+W8JMBGGQybd43GstCXZ3WNmSFtGT9wi59qQTW6mhTR5LQ==}
+ es-toolkit@1.49.0:
+ resolution: {integrity: sha512-G5iZ6Pc/FNRY/soKZHC+TxGDD83rHUDXxzaWhGCX44vAv/tMs56WMusnm/KMNK+luUPsgA9U28cGr4RDlSzL2g==}
+
esbuild@0.28.1:
resolution: {integrity: sha512-HrJrvZv5ayxBzPfwphOoNzkzOIIlifzk0KJrGK2c8R4+LKpMtpYLQeUdjnwjWv/LZlkH2laZk+4w78pi99D4Vw==}
engines: {node: '>=18'}
@@ -2298,6 +2697,9 @@ packages:
resolution: {integrity: sha512-eAmLkjDjAFCVXg7A1unxHsLf961m6y17QFqXqAXGj/gVkKFrEICfStRfwUlGNfeCEjNRa32JEWOUTlYXPyyKvA==}
engines: {node: '>=14'}
+ hachure-fill@0.5.2:
+ resolution: {integrity: sha512-3GKBOn+m2LX9iq+JC1064cSFprJY4jL1jCXTcpnfER5HYE2l/4EfWSGzkPa/ZDBmYI0ZOEj5VHV/eKnPGkHuOg==}
+
has-flag@4.0.0:
resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==}
engines: {node: '>=8'}
@@ -2305,6 +2707,10 @@ packages:
hookable@6.1.1:
resolution: {integrity: sha512-U9LYDy1CwhMCnprUfeAZWZGByVbhd54hwepegYTK7Pi5NvqEj63ifz5z+xukznehT7i6NIZRu89Ay1AZmRsLEQ==}
+ html-encoding-sniffer@6.0.0:
+ resolution: {integrity: sha512-CV9TW3Y3f8/wT0BRFc1/KAVQ3TUHiXmaAb6VW9vtiMFf7SLoMd1PdAc4W3KFOFETBJUb90KatHqlsZMWV+R9Gg==}
+ engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0}
+
html-escaper@2.0.2:
resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==}
@@ -2316,6 +2722,10 @@ packages:
resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==}
engines: {node: '>= 14'}
+ iconv-lite@0.6.3:
+ resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==}
+ engines: {node: '>=0.10.0'}
+
ignore@5.3.2:
resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==}
engines: {node: '>= 4'}
@@ -2324,6 +2734,9 @@ packages:
resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==}
engines: {node: '>= 4'}
+ import-meta-resolve@4.2.0:
+ resolution: {integrity: sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg==}
+
import-without-cache@0.4.0:
resolution: {integrity: sha512-NkJQA7oZ4YHQhd2+H3BoRFKF3d/XNsiKpHZCQEMH9pDX27hQQLsTyOocyRgaIVtf8gHX3Nt3LPkR4e5EdtPAGQ==}
engines: {node: ^22.18.0 || >=24.0.0}
@@ -2332,6 +2745,13 @@ packages:
resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==}
engines: {node: '>=0.8.19'}
+ internmap@1.0.1:
+ resolution: {integrity: sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw==}
+
+ internmap@2.0.3:
+ resolution: {integrity: sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==}
+ engines: {node: '>=12'}
+
is-extglob@2.1.1:
resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==}
engines: {node: '>=0.10.0'}
@@ -2340,6 +2760,9 @@ packages:
resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==}
engines: {node: '>=0.10.0'}
+ is-potential-custom-element-name@1.0.1:
+ resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==}
+
isexe@2.0.0:
resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
@@ -2369,6 +2792,15 @@ packages:
resolution: {integrity: sha512-ePWsvanv0DWuDRsW8dnt+R4jQ31SCRCQ7hhNcPXZPsoBZiemuZNYGf7adZdqX2D86j6rvKp3RpCxVTSb8WQlOw==}
hasBin: true
+ jsdom@29.1.1:
+ resolution: {integrity: sha512-ECi4Fi2f7BdJtUKTflYRTiaMxIB0O6zfR1fX0GXpUrf6flp8QIYn1UT20YQqdSOfk2dfkCwS8LAFoJDEppNK5Q==}
+ engines: {node: ^20.19.0 || ^22.13.0 || >=24.0.0}
+ peerDependencies:
+ canvas: ^3.0.0
+ peerDependenciesMeta:
+ canvas:
+ optional: true
+
jsesc@3.1.0:
resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==}
engines: {node: '>=6'}
@@ -2396,14 +2828,27 @@ packages:
jws@4.0.1:
resolution: {integrity: sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==}
+ katex@0.16.47:
+ resolution: {integrity: sha512-Eeo8Ys1doU1z+x8AZsPpQu+p/QcZBI5PeOo7QGQdy2x2m0MU/hYagBbGOmXwr5KVbEfVuWv9LpnQWeehogurjg==}
+ hasBin: true
+
keyv@4.5.4:
resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==}
+ khroma@2.1.0:
+ resolution: {integrity: sha512-Ls993zuzfayK269Svk9hzpeGUKob/sIgZzyHYdjQoAdQetRKpOLj+k/QQQ/6Qi0Yz65mlROrfd+Ev+1+7dz9Kw==}
+
knip@6.16.1:
resolution: {integrity: sha512-TKMn1rxgH6h9vXR9Y0B+Cq7AdPTr9EI02IwoT65NzqYUkvoDQAaJ/aPybiFpAhZ1px6cNYYwXf86iHkBgzCo9w==}
engines: {node: ^20.19.0 || >=22.12.0}
hasBin: true
+ layout-base@1.0.2:
+ resolution: {integrity: sha512-8h2oVEZNktL4BH2JCOI90iD1yXwL6iNW7KcCKT2QZgQJR2vbqDsldCTPRU9NifTCqHZci57XvQQ15YTu+sTYPg==}
+
+ layout-base@2.0.1:
+ resolution: {integrity: sha512-dp3s92+uNI1hWIpPGH3jK2kxE2lMjdXdr+DH8ynZHpd6PUlH6x6cbuXnoMmiNumznqaNO31xu9e79F0uuZ0JFg==}
+
lefthook-darwin-arm64@2.1.9:
resolution: {integrity: sha512-119HryNcvr4nqn0wUIrNPgpMEPn9yMQzEcW/lezRsnb56PCJriJB92+MCySPVcWDxJnZef7o0T3jdnPNiSH7Qg==}
cpu: [arm64]
@@ -2540,12 +2985,19 @@ packages:
resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==}
engines: {node: '>=10'}
+ lodash-es@4.18.1:
+ resolution: {integrity: sha512-J8xewKD/Gk22OZbhpOVSwcs60zhd95ESDwezOFuA3/099925PdHJ7OFHNTGtajL3AlZkykD32HykiMo+BIBI8A==}
+
long@5.3.2:
resolution: {integrity: sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==}
longest-streak@3.1.0:
resolution: {integrity: sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==}
+ lru-cache@11.5.1:
+ resolution: {integrity: sha512-RPimw/7aMdv2oqRrxKwvZXcPfwBrn/JZ2xYcY9Hus/6LaS3VOAKVWKWgNLCFSiOm1ESXinjsDlidVU7JlnCN2A==}
+ engines: {node: 20 || >=22}
+
magic-string@0.30.21:
resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==}
@@ -2559,6 +3011,11 @@ packages:
markdown-table@3.0.4:
resolution: {integrity: sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==}
+ marked@16.4.2:
+ resolution: {integrity: sha512-TI3V8YYWvkVf3KJe1dRkpnjs68JUPyEa5vjKrp1XEEJUAOaQc+Qj+L1qWbPd0SJuAdQkFU0h73sXXqwDYxsiDA==}
+ engines: {node: '>= 20'}
+ hasBin: true
+
mdast-util-find-and-replace@3.0.2:
resolution: {integrity: sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg==}
@@ -2592,6 +3049,12 @@ packages:
mdast-util-to-string@4.0.0:
resolution: {integrity: sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==}
+ mdn-data@2.27.1:
+ resolution: {integrity: sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==}
+
+ mermaid@11.16.0:
+ resolution: {integrity: sha512-Zvm3kbstgdpvIJPPItlL7fppIZ3kibvc1oZIGxdvk9t6UFz6flv+Jw7FtRGKwfcI8OckmH04LqG6LlS6X4B1pA==}
+
micromark-core-commonmark@2.0.3:
resolution: {integrity: sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==}
@@ -2746,9 +3209,15 @@ packages:
package-manager-detector@1.6.0:
resolution: {integrity: sha512-61A5ThoTiDG/C8s8UMZwSorAGwMJ0ERVGj2OjoW5pAalsNOg15+iQiPzrLJ4jhZ1HJzmC2PIHT2oEiH3R5fzNA==}
+ parse5@8.0.1:
+ resolution: {integrity: sha512-z1e/HMG90obSGeidlli3hj7cbocou0/wa5HacvI3ASx34PecNjNQeaHNo5WIZpWofN9kgkqV1q5YvXe3F0FoPw==}
+
partial-json@0.1.7:
resolution: {integrity: sha512-Njv/59hHaokb/hRUjce3Hdv12wd60MtM9Z5Olmn+nehe0QDAsRtRbJPvJ0Z91TusF0SuZRIvnM+S4l6EIP8leA==}
+ path-data-parser@0.1.0:
+ resolution: {integrity: sha512-NOnmBpt5Y2RWbuv0LMzsayp3lVylAHLPUTut412ZA3l+C4uw4ZVkQbjShYCQ8TCpUMdPapr4YjUqLYD6v68j+w==}
+
path-exists@4.0.0:
resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==}
engines: {node: '>=8'}
@@ -2771,6 +3240,12 @@ packages:
resolution: {integrity: sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==}
engines: {node: '>=12'}
+ points-on-curve@0.2.0:
+ resolution: {integrity: sha512-0mYKnYYe9ZcqMCWhUjItv/oHjvgEsfKvnUTg8sAtnHr3GVy7rGkXCb6d5cSyqrWqL4k81b9CPg3urd+T7aop3A==}
+
+ points-on-path@0.2.1:
+ resolution: {integrity: sha512-25ClnWWuw7JbWZcgqY/gJ4FQWadKxGWk+3kR/7kD0tCaDtPPMj7oHu2ToLaVhfpnHrZzYby2w6tUA0eOIuUg8g==}
+
postcss@8.5.15:
resolution: {integrity: sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==}
engines: {node: ^10 || ^12 || >=14}
@@ -2802,6 +3277,10 @@ packages:
resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==}
engines: {node: '>= 14.18.0'}
+ require-from-string@2.0.2:
+ resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==}
+ engines: {node: '>=0.10.0'}
+
resolve-pkg-maps@1.0.0:
resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==}
@@ -2809,6 +3288,9 @@ packages:
resolution: {integrity: sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==}
engines: {node: '>= 4'}
+ robust-predicates@3.0.3:
+ resolution: {integrity: sha512-NS3levdsRIUOmiJ8FZWCP7LG3QpJyrs/TE0Zpf1yvZu8cAJJ6QMW92H1c7kWpdIHo8RvmLxN/o2JXTKHp74lUA==}
+
rolldown-plugin-dts@0.25.2:
resolution: {integrity: sha512-nMhN/R+vmR8GM45ZW1FWMSjRTSDDn/6w4GTf8RNrEFCBdl8B1kySWrU1ixPtbwzXoRlcO+R/S88VgXuJQwfdDg==}
engines: {node: ^22.18.0 || >=24.0.0}
@@ -2838,6 +3320,12 @@ packages:
engines: {node: ^20.19.0 || >=22.12.0}
hasBin: true
+ roughjs@4.6.6:
+ resolution: {integrity: sha512-ZUz/69+SYpFN/g/lUlo2FXcIjRkSu3nDarreVdGGndHEBJ6cXPdKguS8JGxwj5HA5xIbVKSmLgr5b3AWxtRfvQ==}
+
+ rw@1.3.3:
+ resolution: {integrity: sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==}
+
sade@1.8.1:
resolution: {integrity: sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==}
engines: {node: '>=6'}
@@ -2845,6 +3333,13 @@ packages:
safe-buffer@5.2.1:
resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==}
+ safer-buffer@2.1.2:
+ resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==}
+
+ saxes@6.0.0:
+ resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==}
+ engines: {node: '>=v12.22.7'}
+
schemastery@3.18.0:
resolution: {integrity: sha512-Jw2uxjoyyqc/yeurmChUEc/jbi8GsrdXV/KmqRUDZXJAXAmrJiPsz8vKa17l/VckyzljHZ9oGaul443CQiXxtA==}
@@ -2885,6 +3380,9 @@ packages:
strnum@2.4.0:
resolution: {integrity: sha512-sHrVyWWdq28RbhjuJdZsA1SnGRJV6NiXbk6AXBxDOsgAcA+lmpUZCYjOdLBxkXMwis6RRe7dlZt4VlIWFVzkmg==}
+ stylis@4.4.0:
+ resolution: {integrity: sha512-5Z9ZpRzfuH6l/UAvCPAPUo3665Nk2wLaZU3x+TLHKVzIz33+sbJqbtrYoC3KD4/uVOr2Zp+L0LySezP9OHV9yA==}
+
supports-color@7.2.0:
resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==}
engines: {node: '>=8'}
@@ -2893,6 +3391,9 @@ packages:
resolution: {integrity: sha512-VL+lNrEoIXww1coLPOmiEmK/0sGigko5COxI09KzHc2VJXJsQ37UaQ+8quuxjDeA7+KnLGTWRyOXSLLR2Wb4jw==}
engines: {node: '>=12'}
+ symbol-tree@3.2.4:
+ resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==}
+
tinybench@2.9.0:
resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==}
@@ -2908,6 +3409,21 @@ packages:
resolution: {integrity: sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==}
engines: {node: '>=14.0.0'}
+ tldts-core@7.4.5:
+ resolution: {integrity: sha512-pGrwzZDvPwKe+7NNUqAunb6rqTfynr0VOUhCMdqbu5xlvNiszsAJygRzwvpVycdzejlbpY+SWJOn+s75Og7FEA==}
+
+ tldts@7.4.5:
+ resolution: {integrity: sha512-RfEzKWcq5fHUOFq7J3rl3Oz6ylKGtcHqUznzj4EcXsxLSIjJcvpbXAQtWGeJQ0xKnimR5e0Cn+cn9TssfMzm+g==}
+ hasBin: true
+
+ tough-cookie@6.0.1:
+ resolution: {integrity: sha512-LktZQb3IeoUWB9lqR5EWTHgW/VTITCXg4D21M+lvybRVdylLrRMnqaIONLVb5mav8vM19m44HIcGq4qASeu2Qw==}
+ engines: {node: '>=16'}
+
+ tr46@6.0.0:
+ resolution: {integrity: sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw==}
+ engines: {node: '>=20'}
+
tree-kill@1.2.2:
resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==}
hasBin: true
@@ -2921,6 +3437,10 @@ packages:
peerDependencies:
typescript: '>=4.8.4'
+ ts-dedent@2.3.0:
+ resolution: {integrity: sha512-JfJeIHke7y2egdGGgRAvpCwYFUsHlM2gPcrVOxFkznt/4uzQ7HFmvE63iFHVLBJNDuyDOQgijDK/tXH/f6Msjg==}
+ engines: {node: '>=6.10'}
+
tsconfck@3.1.6:
resolution: {integrity: sha512-ks6Vjr/jEw0P1gmOVwutM3B7fWxoWBL2KRDb1JfqGVawBmO5UsvmWOQFGHBPl5yxYz4eERr19E6L7NMv+Fej4w==}
engines: {node: ^18 || >=20}
@@ -3003,6 +3523,10 @@ packages:
undici-types@7.24.6:
resolution: {integrity: sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==}
+ undici@7.28.0:
+ resolution: {integrity: sha512-cRZYrTDwWznlnRiPjggAGxZXanty6M8RV1ff8Wm4LWXBp7/IG8v5DnOm74DtUBp9OONpK75YlPnIjQqX0dBDtA==}
+ engines: {node: '>=20.18.1'}
+
unist-util-is@6.0.1:
resolution: {integrity: sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==}
@@ -3018,6 +3542,10 @@ packages:
uri-js@4.4.1:
resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==}
+ uuid@14.0.1:
+ resolution: {integrity: sha512-6ZxzVpzDXDa3bJWaHilVayA+BH/1zmxCJoVgvmqJnid/gPoKHxUrS/aC/T6LGQtNHT+XHG9fXPJB4d+IrU30Ew==}
+ hasBin: true
+
vite-tsconfig-paths@6.1.1:
resolution: {integrity: sha512-2cihq7zliibCCZ8P9cKJrQBkfgdvcFkOOc3Y02o3GWUDLgqjWsZudaoiuOwO/gzTzy17cS5F7ZPo4bsnS4DGkg==}
peerDependencies:
@@ -3107,6 +3635,10 @@ packages:
jsdom:
optional: true
+ w3c-xmlserializer@5.0.0:
+ resolution: {integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==}
+ engines: {node: '>=18'}
+
walk-up-path@4.0.0:
resolution: {integrity: sha512-3hu+tD8YzSLGuFYtPRb48vdhKMi0KQV5sn+uWr8+7dMEq/2G/dtLrdDinkLjqq5TIbIBjYJ4Ax/n3YiaW7QM8A==}
engines: {node: 20 || >=22}
@@ -3115,6 +3647,18 @@ packages:
resolution: {integrity: sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==}
engines: {node: '>= 8'}
+ webidl-conversions@8.0.1:
+ resolution: {integrity: sha512-BMhLD/Sw+GbJC21C/UgyaZX41nPt8bUTg+jWyDeg7e7YN4xOM05YPSIXceACnXVtqyEw/LMClUQMtMZ+PGGpqQ==}
+ engines: {node: '>=20'}
+
+ whatwg-mimetype@5.0.0:
+ resolution: {integrity: sha512-sXcNcHOC51uPGF0P/D4NVtrkjSU2fNsm9iog4ZvZJsL3rjoDAzXZhkm2MWt1y+PUdggKAYVoMAIYcs78wJ51Cw==}
+ engines: {node: '>=20'}
+
+ whatwg-url@16.0.1:
+ resolution: {integrity: sha512-1to4zXBxmXHV3IiSSEInrreIlu02vUOvrhxJJH5vcxYTBDAx51cqZiKdyTxlecdKNSjj8EcxGBxNf6Vg+945gw==}
+ engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0}
+
which@2.0.2:
resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==}
engines: {node: '>= 8'}
@@ -3141,10 +3685,17 @@ packages:
utf-8-validate:
optional: true
+ xml-name-validator@5.0.0:
+ resolution: {integrity: sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==}
+ engines: {node: '>=18'}
+
xml-naming@0.1.0:
resolution: {integrity: sha512-k8KO9hrMyNk6tUWqUfkTEZbezRRpONVOzUTnc97VnCvyj6Tf9lyUR9EDAIeiVLv56jsMcoXEwjW8Kv5yPY52lw==}
engines: {node: '>=16.0.0'}
+ xmlchars@2.2.0:
+ resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==}
+
yaml@2.9.0:
resolution: {integrity: sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==}
engines: {node: '>= 14.6'}
@@ -3171,12 +3722,37 @@ snapshots:
dependencies:
zod: 4.4.3
+ '@antfu/install-pkg@1.1.0':
+ dependencies:
+ package-manager-detector: 1.6.0
+ tinyexec: 1.2.4
+
'@anthropic-ai/sdk@0.91.1(zod@4.4.3)':
dependencies:
json-schema-to-ts: 3.1.1
optionalDependencies:
zod: 4.4.3
+ '@asamuzakjp/css-color@5.1.11':
+ dependencies:
+ '@asamuzakjp/generational-cache': 1.0.1
+ '@csstools/css-calc': 3.2.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)
+ '@csstools/css-color-parser': 4.1.9(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)
+ '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0)
+ '@csstools/css-tokenizer': 4.0.0
+
+ '@asamuzakjp/dom-selector@7.1.1':
+ dependencies:
+ '@asamuzakjp/generational-cache': 1.0.1
+ '@asamuzakjp/nwsapi': 2.3.9
+ bidi-js: 1.0.3
+ css-tree: 3.2.1
+ is-potential-custom-element-name: 1.0.1
+
+ '@asamuzakjp/generational-cache@1.0.1': {}
+
+ '@asamuzakjp/nwsapi@2.3.9': {}
+
'@aws-crypto/crc32@5.2.0':
dependencies:
'@aws-crypto/util': 5.2.0
@@ -3445,6 +4021,14 @@ snapshots:
'@bcoe/v8-coverage@1.0.2': {}
+ '@braintree/sanitize-url@7.1.2': {}
+
+ '@bramus/specificity@2.4.2':
+ dependencies:
+ css-tree: 3.2.1
+
+ '@chevrotain/types@11.1.2': {}
+
'@cordisjs/plugin-include@1.0.4(@cordisjs/plugin-loader@1.0.0-rc.4)(cordis@4.0.0-rc.6)':
dependencies:
'@cordisjs/plugin-loader': 1.0.0-rc.4(cordis@4.0.0-rc.6)
@@ -3462,6 +4046,30 @@ snapshots:
cordis: 4.0.0-rc.6(@cordisjs/plugin-include@1.0.4)(@cordisjs/plugin-loader@1.0.0-rc.4)
cosmokit: 1.8.1
+ '@csstools/color-helpers@6.1.0': {}
+
+ '@csstools/css-calc@3.2.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)':
+ dependencies:
+ '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0)
+ '@csstools/css-tokenizer': 4.0.0
+
+ '@csstools/css-color-parser@4.1.9(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)':
+ dependencies:
+ '@csstools/color-helpers': 6.1.0
+ '@csstools/css-calc': 3.2.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)
+ '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0)
+ '@csstools/css-tokenizer': 4.0.0
+
+ '@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0)':
+ dependencies:
+ '@csstools/css-tokenizer': 4.0.0
+
+ '@csstools/css-syntax-patches-for-csstree@1.1.6(css-tree@3.2.1)':
+ optionalDependencies:
+ css-tree: 3.2.1
+
+ '@csstools/css-tokenizer@4.0.0': {}
+
'@earendil-works/pi-ai@0.79.3(ws@8.21.0)(zod@4.4.3)':
dependencies:
'@anthropic-ai/sdk': 0.91.1(zod@4.4.3)
@@ -3622,6 +4230,8 @@ snapshots:
'@eslint/core': 1.2.1
levn: 0.4.1
+ '@exodus/bytes@1.15.1': {}
+
'@google/genai@1.52.0':
dependencies:
google-auth-library: 10.7.0
@@ -3649,6 +4259,14 @@ snapshots:
'@humanwhocodes/retry@0.4.3': {}
+ '@iconify/types@2.0.0': {}
+
+ '@iconify/utils@3.1.3':
+ dependencies:
+ '@antfu/install-pkg': 1.1.0
+ '@iconify/types': 2.0.0
+ import-meta-resolve: 4.2.0
+
'@jridgewell/gen-mapping@0.3.13':
dependencies:
'@jridgewell/sourcemap-codec': 1.5.5
@@ -3663,6 +4281,10 @@ snapshots:
'@jridgewell/resolve-uri': 3.1.2
'@jridgewell/sourcemap-codec': 1.5.5
+ '@mermaid-js/parser@1.2.0':
+ dependencies:
+ '@chevrotain/types': 11.1.2
+
'@mistralai/mistralai@2.2.1':
dependencies:
ws: 8.21.0
@@ -4021,6 +4643,123 @@ snapshots:
'@types/deep-eql': 4.0.2
assertion-error: 2.0.1
+ '@types/d3-array@3.2.2': {}
+
+ '@types/d3-axis@3.0.6':
+ dependencies:
+ '@types/d3-selection': 3.0.11
+
+ '@types/d3-brush@3.0.6':
+ dependencies:
+ '@types/d3-selection': 3.0.11
+
+ '@types/d3-chord@3.0.6': {}
+
+ '@types/d3-color@3.1.3': {}
+
+ '@types/d3-contour@3.0.6':
+ dependencies:
+ '@types/d3-array': 3.2.2
+ '@types/geojson': 7946.0.16
+
+ '@types/d3-delaunay@6.0.4': {}
+
+ '@types/d3-dispatch@3.0.7': {}
+
+ '@types/d3-drag@3.0.7':
+ dependencies:
+ '@types/d3-selection': 3.0.11
+
+ '@types/d3-dsv@3.0.7': {}
+
+ '@types/d3-ease@3.0.2': {}
+
+ '@types/d3-fetch@3.0.7':
+ dependencies:
+ '@types/d3-dsv': 3.0.7
+
+ '@types/d3-force@3.0.10': {}
+
+ '@types/d3-format@3.0.4': {}
+
+ '@types/d3-geo@3.1.0':
+ dependencies:
+ '@types/geojson': 7946.0.16
+
+ '@types/d3-hierarchy@3.1.7': {}
+
+ '@types/d3-interpolate@3.0.4':
+ dependencies:
+ '@types/d3-color': 3.1.3
+
+ '@types/d3-path@3.1.1': {}
+
+ '@types/d3-polygon@3.0.2': {}
+
+ '@types/d3-quadtree@3.0.6': {}
+
+ '@types/d3-random@3.0.3': {}
+
+ '@types/d3-scale-chromatic@3.1.0': {}
+
+ '@types/d3-scale@4.0.9':
+ dependencies:
+ '@types/d3-time': 3.0.4
+
+ '@types/d3-selection@3.0.11': {}
+
+ '@types/d3-shape@3.1.8':
+ dependencies:
+ '@types/d3-path': 3.1.1
+
+ '@types/d3-time-format@4.0.3': {}
+
+ '@types/d3-time@3.0.4': {}
+
+ '@types/d3-timer@3.0.2': {}
+
+ '@types/d3-transition@3.0.9':
+ dependencies:
+ '@types/d3-selection': 3.0.11
+
+ '@types/d3-zoom@3.0.8':
+ dependencies:
+ '@types/d3-interpolate': 3.0.4
+ '@types/d3-selection': 3.0.11
+
+ '@types/d3@7.4.3':
+ dependencies:
+ '@types/d3-array': 3.2.2
+ '@types/d3-axis': 3.0.6
+ '@types/d3-brush': 3.0.6
+ '@types/d3-chord': 3.0.6
+ '@types/d3-color': 3.1.3
+ '@types/d3-contour': 3.0.6
+ '@types/d3-delaunay': 6.0.4
+ '@types/d3-dispatch': 3.0.7
+ '@types/d3-drag': 3.0.7
+ '@types/d3-dsv': 3.0.7
+ '@types/d3-ease': 3.0.2
+ '@types/d3-fetch': 3.0.7
+ '@types/d3-force': 3.0.10
+ '@types/d3-format': 3.0.4
+ '@types/d3-geo': 3.1.0
+ '@types/d3-hierarchy': 3.1.7
+ '@types/d3-interpolate': 3.0.4
+ '@types/d3-path': 3.1.1
+ '@types/d3-polygon': 3.0.2
+ '@types/d3-quadtree': 3.0.6
+ '@types/d3-random': 3.0.3
+ '@types/d3-scale': 4.0.9
+ '@types/d3-scale-chromatic': 3.1.0
+ '@types/d3-selection': 3.0.11
+ '@types/d3-shape': 3.1.8
+ '@types/d3-time': 3.0.4
+ '@types/d3-time-format': 4.0.3
+ '@types/d3-timer': 3.0.2
+ '@types/d3-transition': 3.0.9
+ '@types/d3-zoom': 3.0.8
+
'@types/debug@4.1.13':
dependencies:
'@types/ms': 2.1.0
@@ -4031,6 +4770,15 @@ snapshots:
'@types/estree@1.0.9': {}
+ '@types/geojson@7946.0.16': {}
+
+ '@types/jsdom@28.0.3':
+ dependencies:
+ '@types/node': 25.9.3
+ '@types/tough-cookie': 4.0.5
+ parse5: 8.0.1
+ undici-types: 7.24.6
+
'@types/jsesc@2.5.1': {}
'@types/json-schema@7.0.15': {}
@@ -4049,6 +4797,11 @@ snapshots:
'@types/retry@0.12.0': {}
+ '@types/tough-cookie@4.0.5': {}
+
+ '@types/trusted-types@2.0.7':
+ optional: true
+
'@types/unist@3.0.3': {}
'@typescript-eslint/eslint-plugin@8.61.0(@typescript-eslint/parser@8.61.0(eslint@10.5.0(jiti@2.7.0))(typescript@6.0.3))(eslint@10.5.0(jiti@2.7.0))(typescript@6.0.3)':
@@ -4142,6 +4895,11 @@ snapshots:
'@typescript-eslint/types': 8.61.0
eslint-visitor-keys: 5.0.1
+ '@upsetjs/venn.js@2.0.0':
+ optionalDependencies:
+ d3-selection: 3.0.0
+ d3-transition: 3.0.1(d3-selection@3.0.0)
+
'@vitest/coverage-v8@4.1.8(vitest@4.1.8)':
dependencies:
'@bcoe/v8-coverage': 1.0.2
@@ -4154,7 +4912,7 @@ snapshots:
obug: 2.1.3
std-env: 4.1.0
tinyrainbow: 3.1.0
- vitest: 4.1.8(@types/node@25.9.3)(@vitest/coverage-v8@4.1.8)(vite@8.0.16(@types/node@25.9.3)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
+ vitest: 4.1.8(@types/node@25.9.3)(@vitest/coverage-v8@4.1.8)(jsdom@29.1.1)(vite@8.0.16(@types/node@25.9.3)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
'@vitest/expect@4.1.8':
dependencies:
@@ -4236,6 +4994,10 @@ snapshots:
base64-js@1.5.1: {}
+ bidi-js@1.0.3:
+ dependencies:
+ require-from-string: 2.0.2
+
bignumber.js@9.3.1: {}
birpc@4.0.0: {}
@@ -4260,6 +5022,10 @@ snapshots:
dependencies:
readdirp: 4.1.2
+ commander@7.2.0: {}
+
+ commander@8.3.0: {}
+
convert-source-map@2.0.0: {}
cordis@4.0.0-rc.6(@cordisjs/plugin-include@1.0.4)(@cordisjs/plugin-loader@1.0.0-rc.4):
@@ -4278,6 +5044,14 @@ snapshots:
'@cordisjs/plugin-include': link:vendor/include
'@cordisjs/plugin-loader': link:vendor/loader
+ cose-base@1.0.3:
+ dependencies:
+ layout-base: 1.0.2
+
+ cose-base@2.2.0:
+ dependencies:
+ layout-base: 2.0.1
+
cosmokit@1.8.1: {}
cross-spawn@7.0.6:
@@ -4286,12 +5060,212 @@ snapshots:
shebang-command: 2.0.0
which: 2.0.2
+ css-tree@3.2.1:
+ dependencies:
+ mdn-data: 2.27.1
+ source-map-js: 1.2.1
+
+ cytoscape-cose-bilkent@4.1.0(cytoscape@3.34.0):
+ dependencies:
+ cose-base: 1.0.3
+ cytoscape: 3.34.0
+
+ cytoscape-fcose@2.2.0(cytoscape@3.34.0):
+ dependencies:
+ cose-base: 2.2.0
+ cytoscape: 3.34.0
+
+ cytoscape@3.34.0: {}
+
+ d3-array@2.12.1:
+ dependencies:
+ internmap: 1.0.1
+
+ d3-array@3.2.4:
+ dependencies:
+ internmap: 2.0.3
+
+ d3-axis@3.0.0: {}
+
+ d3-brush@3.0.0:
+ dependencies:
+ d3-dispatch: 3.0.1
+ d3-drag: 3.0.0
+ d3-interpolate: 3.0.1
+ d3-selection: 3.0.0
+ d3-transition: 3.0.1(d3-selection@3.0.0)
+
+ d3-chord@3.0.1:
+ dependencies:
+ d3-path: 3.1.0
+
+ d3-color@3.1.0: {}
+
+ d3-contour@4.0.2:
+ dependencies:
+ d3-array: 3.2.4
+
+ d3-delaunay@6.0.4:
+ dependencies:
+ delaunator: 5.1.0
+
+ d3-dispatch@3.0.1: {}
+
+ d3-drag@3.0.0:
+ dependencies:
+ d3-dispatch: 3.0.1
+ d3-selection: 3.0.0
+
+ d3-dsv@3.0.1:
+ dependencies:
+ commander: 7.2.0
+ iconv-lite: 0.6.3
+ rw: 1.3.3
+
+ d3-ease@3.0.1: {}
+
+ d3-fetch@3.0.1:
+ dependencies:
+ d3-dsv: 3.0.1
+
+ d3-force@3.0.0:
+ dependencies:
+ d3-dispatch: 3.0.1
+ d3-quadtree: 3.0.1
+ d3-timer: 3.0.1
+
+ d3-format@3.1.2: {}
+
+ d3-geo@3.1.1:
+ dependencies:
+ d3-array: 3.2.4
+
+ d3-hierarchy@3.1.2: {}
+
+ d3-interpolate@3.0.1:
+ dependencies:
+ d3-color: 3.1.0
+
+ d3-path@1.0.9: {}
+
+ d3-path@3.1.0: {}
+
+ d3-polygon@3.0.1: {}
+
+ d3-quadtree@3.0.1: {}
+
+ d3-random@3.0.1: {}
+
+ d3-sankey@0.12.3:
+ dependencies:
+ d3-array: 2.12.1
+ d3-shape: 1.3.7
+
+ d3-scale-chromatic@3.1.0:
+ dependencies:
+ d3-color: 3.1.0
+ d3-interpolate: 3.0.1
+
+ d3-scale@4.0.2:
+ dependencies:
+ d3-array: 3.2.4
+ d3-format: 3.1.2
+ d3-interpolate: 3.0.1
+ d3-time: 3.1.0
+ d3-time-format: 4.1.0
+
+ d3-selection@3.0.0: {}
+
+ d3-shape@1.3.7:
+ dependencies:
+ d3-path: 1.0.9
+
+ d3-shape@3.2.0:
+ dependencies:
+ d3-path: 3.1.0
+
+ d3-time-format@4.1.0:
+ dependencies:
+ d3-time: 3.1.0
+
+ d3-time@3.1.0:
+ dependencies:
+ d3-array: 3.2.4
+
+ d3-timer@3.0.1: {}
+
+ d3-transition@3.0.1(d3-selection@3.0.0):
+ dependencies:
+ d3-color: 3.1.0
+ d3-dispatch: 3.0.1
+ d3-ease: 3.0.1
+ d3-interpolate: 3.0.1
+ d3-selection: 3.0.0
+ d3-timer: 3.0.1
+
+ d3-zoom@3.0.0:
+ dependencies:
+ d3-dispatch: 3.0.1
+ d3-drag: 3.0.0
+ d3-interpolate: 3.0.1
+ d3-selection: 3.0.0
+ d3-transition: 3.0.1(d3-selection@3.0.0)
+
+ d3@7.9.0:
+ dependencies:
+ d3-array: 3.2.4
+ d3-axis: 3.0.0
+ d3-brush: 3.0.0
+ d3-chord: 3.0.1
+ d3-color: 3.1.0
+ d3-contour: 4.0.2
+ d3-delaunay: 6.0.4
+ d3-dispatch: 3.0.1
+ d3-drag: 3.0.0
+ d3-dsv: 3.0.1
+ d3-ease: 3.0.1
+ d3-fetch: 3.0.1
+ d3-force: 3.0.0
+ d3-format: 3.1.2
+ d3-geo: 3.1.1
+ d3-hierarchy: 3.1.2
+ d3-interpolate: 3.0.1
+ d3-path: 3.1.0
+ d3-polygon: 3.0.1
+ d3-quadtree: 3.0.1
+ d3-random: 3.0.1
+ d3-scale: 4.0.2
+ d3-scale-chromatic: 3.1.0
+ d3-selection: 3.0.0
+ d3-shape: 3.2.0
+ d3-time: 3.1.0
+ d3-time-format: 4.1.0
+ d3-timer: 3.0.1
+ d3-transition: 3.0.1(d3-selection@3.0.0)
+ d3-zoom: 3.0.0
+
+ dagre-d3-es@7.0.14:
+ dependencies:
+ d3: 7.9.0
+ lodash-es: 4.18.1
+
data-uri-to-buffer@4.0.1: {}
+ data-urls@7.0.0:
+ dependencies:
+ whatwg-mimetype: 5.0.0
+ whatwg-url: 16.0.1
+ transitivePeerDependencies:
+ - '@noble/hashes'
+
+ dayjs@1.11.21: {}
+
debug@4.4.3:
dependencies:
ms: 2.1.3
+ decimal.js@10.6.0: {}
+
decode-named-character-reference@1.3.0:
dependencies:
character-entities: 2.0.2
@@ -4300,6 +5274,10 @@ snapshots:
defu@6.1.7: {}
+ delaunator@5.1.0:
+ dependencies:
+ robust-predicates: 3.0.3
+
dequal@2.0.3: {}
detect-libc@2.1.2: {}
@@ -4308,6 +5286,10 @@ snapshots:
dependencies:
dequal: 2.0.3
+ dompurify@3.4.11:
+ optionalDependencies:
+ '@types/trusted-types': 2.0.7
+
dts-resolver@3.0.0(oxc-resolver@11.20.0):
optionalDependencies:
oxc-resolver: 11.20.0
@@ -4318,8 +5300,12 @@ snapshots:
empathic@2.0.1: {}
+ entities@8.0.0: {}
+
es-module-lexer@2.1.0: {}
+ es-toolkit@1.49.0: {}
+
esbuild@0.28.1:
optionalDependencies:
'@esbuild/aix-ppc64': 0.28.1
@@ -4540,10 +5526,18 @@ snapshots:
google-logging-utils@1.1.3: {}
+ hachure-fill@0.5.2: {}
+
has-flag@4.0.0: {}
hookable@6.1.1: {}
+ html-encoding-sniffer@6.0.0:
+ dependencies:
+ '@exodus/bytes': 1.15.1
+ transitivePeerDependencies:
+ - '@noble/hashes'
+
html-escaper@2.0.2: {}
http-proxy-agent@7.0.2:
@@ -4560,20 +5554,32 @@ snapshots:
transitivePeerDependencies:
- supports-color
+ iconv-lite@0.6.3:
+ dependencies:
+ safer-buffer: 2.1.2
+
ignore@5.3.2: {}
ignore@7.0.5: {}
+ import-meta-resolve@4.2.0: {}
+
import-without-cache@0.4.0: {}
imurmurhash@0.1.4: {}
+ internmap@1.0.1: {}
+
+ internmap@2.0.3: {}
+
is-extglob@2.1.1: {}
is-glob@4.0.3:
dependencies:
is-extglob: 2.1.1
+ is-potential-custom-element-name@1.0.1: {}
+
isexe@2.0.0: {}
istanbul-lib-coverage@3.2.2: {}
@@ -4599,6 +5605,32 @@ snapshots:
dependencies:
argparse: 2.0.1
+ jsdom@29.1.1:
+ dependencies:
+ '@asamuzakjp/css-color': 5.1.11
+ '@asamuzakjp/dom-selector': 7.1.1
+ '@bramus/specificity': 2.4.2
+ '@csstools/css-syntax-patches-for-csstree': 1.1.6(css-tree@3.2.1)
+ '@exodus/bytes': 1.15.1
+ css-tree: 3.2.1
+ data-urls: 7.0.0
+ decimal.js: 10.6.0
+ html-encoding-sniffer: 6.0.0
+ is-potential-custom-element-name: 1.0.1
+ lru-cache: 11.5.1
+ parse5: 8.0.1
+ saxes: 6.0.0
+ symbol-tree: 3.2.4
+ tough-cookie: 6.0.1
+ undici: 7.28.0
+ w3c-xmlserializer: 5.0.0
+ webidl-conversions: 8.0.1
+ whatwg-mimetype: 5.0.0
+ whatwg-url: 16.0.1
+ xml-name-validator: 5.0.0
+ transitivePeerDependencies:
+ - '@noble/hashes'
+
jsesc@3.1.0: {}
json-bigint@1.0.0:
@@ -4627,10 +5659,16 @@ snapshots:
jwa: 2.0.1
safe-buffer: 5.2.1
+ katex@0.16.47:
+ dependencies:
+ commander: 8.3.0
+
keyv@4.5.4:
dependencies:
json-buffer: 3.0.1
+ khroma@2.1.0: {}
+
knip@6.16.1:
dependencies:
fdir: 6.5.0(picomatch@4.0.4)
@@ -4647,6 +5685,10 @@ snapshots:
yaml: 2.9.0
zod: 4.4.3
+ layout-base@1.0.2: {}
+
+ layout-base@2.0.1: {}
+
lefthook-darwin-arm64@2.1.9:
optional: true
@@ -4748,10 +5790,14 @@ snapshots:
dependencies:
p-locate: 5.0.0
+ lodash-es@4.18.1: {}
+
long@5.3.2: {}
longest-streak@3.1.0: {}
+ lru-cache@11.5.1: {}
+
magic-string@0.30.21:
dependencies:
'@jridgewell/sourcemap-codec': 1.5.5
@@ -4768,6 +5814,8 @@ snapshots:
markdown-table@3.0.4: {}
+ marked@16.4.2: {}
+
mdast-util-find-and-replace@3.0.2:
dependencies:
'@types/mdast': 4.0.4
@@ -4870,6 +5918,32 @@ snapshots:
dependencies:
'@types/mdast': 4.0.4
+ mdn-data@2.27.1: {}
+
+ mermaid@11.16.0:
+ dependencies:
+ '@braintree/sanitize-url': 7.1.2
+ '@iconify/utils': 3.1.3
+ '@mermaid-js/parser': 1.2.0
+ '@types/d3': 7.4.3
+ '@upsetjs/venn.js': 2.0.0
+ cytoscape: 3.34.0
+ cytoscape-cose-bilkent: 4.1.0(cytoscape@3.34.0)
+ cytoscape-fcose: 2.2.0(cytoscape@3.34.0)
+ d3: 7.9.0
+ d3-sankey: 0.12.3
+ dagre-d3-es: 7.0.14
+ dayjs: 1.11.21
+ dompurify: 3.4.11
+ es-toolkit: 1.49.0
+ katex: 0.16.47
+ khroma: 2.1.0
+ marked: 16.4.2
+ roughjs: 4.6.6
+ stylis: 4.4.0
+ ts-dedent: 2.3.0
+ uuid: 14.0.1
+
micromark-core-commonmark@2.0.3:
dependencies:
decode-named-character-reference: 1.3.0
@@ -5159,8 +6233,14 @@ snapshots:
package-manager-detector@1.6.0: {}
+ parse5@8.0.1:
+ dependencies:
+ entities: 8.0.0
+
partial-json@0.1.7: {}
+ path-data-parser@0.1.0: {}
+
path-exists@4.0.0: {}
path-expression-matcher@1.5.0: {}
@@ -5173,6 +6253,13 @@ snapshots:
picomatch@4.0.4: {}
+ points-on-curve@0.2.0: {}
+
+ points-on-path@0.2.1:
+ dependencies:
+ path-data-parser: 0.1.0
+ points-on-curve: 0.2.0
+
postcss@8.5.15:
dependencies:
nanoid: 3.3.12
@@ -5210,10 +6297,14 @@ snapshots:
readdirp@4.1.2: {}
+ require-from-string@2.0.2: {}
+
resolve-pkg-maps@1.0.0: {}
retry@0.13.1: {}
+ robust-predicates@3.0.3: {}
+
rolldown-plugin-dts@0.25.2(oxc-resolver@11.20.0)(rolldown@1.1.1)(typescript@6.0.3):
dependencies:
'@babel/generator': 8.0.0-rc.6
@@ -5272,12 +6363,27 @@ snapshots:
'@rolldown/binding-win32-arm64-msvc': 1.1.1
'@rolldown/binding-win32-x64-msvc': 1.1.1
+ roughjs@4.6.6:
+ dependencies:
+ hachure-fill: 0.5.2
+ path-data-parser: 0.1.0
+ points-on-curve: 0.2.0
+ points-on-path: 0.2.1
+
+ rw@1.3.3: {}
+
sade@1.8.1:
dependencies:
mri: 1.2.0
safe-buffer@5.2.1: {}
+ safer-buffer@2.1.2: {}
+
+ saxes@6.0.0:
+ dependencies:
+ xmlchars: 2.2.0
+
schemastery@3.18.0:
dependencies:
'@standard-schema/spec': 1.1.0
@@ -5307,12 +6413,16 @@ snapshots:
dependencies:
anynum: 1.0.0
+ stylis@4.4.0: {}
+
supports-color@7.2.0:
dependencies:
has-flag: 4.0.0
supports-color@9.4.0: {}
+ symbol-tree@3.2.4: {}
+
tinybench@2.9.0: {}
tinyexec@1.2.4: {}
@@ -5324,6 +6434,20 @@ snapshots:
tinyrainbow@3.1.0: {}
+ tldts-core@7.4.5: {}
+
+ tldts@7.4.5:
+ dependencies:
+ tldts-core: 7.4.5
+
+ tough-cookie@6.0.1:
+ dependencies:
+ tldts: 7.4.5
+
+ tr46@6.0.0:
+ dependencies:
+ punycode: 2.3.1
+
tree-kill@1.2.2: {}
ts-algebra@2.0.0: {}
@@ -5332,6 +6456,8 @@ snapshots:
dependencies:
typescript: 6.0.3
+ ts-dedent@2.3.0: {}
+
tsconfck@3.1.6(typescript@6.0.3):
optionalDependencies:
typescript: 6.0.3
@@ -5399,6 +6525,8 @@ snapshots:
undici-types@7.24.6: {}
+ undici@7.28.0: {}
+
unist-util-is@6.0.1:
dependencies:
'@types/unist': 3.0.3
@@ -5422,6 +6550,8 @@ snapshots:
dependencies:
punycode: 2.3.1
+ uuid@14.0.1: {}
+
vite-tsconfig-paths@6.1.1(typescript@6.0.3)(vite@8.0.16(@types/node@25.9.3)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)):
dependencies:
debug: 4.4.3
@@ -5447,7 +6577,7 @@ snapshots:
tsx: 4.22.4
yaml: 2.9.0
- vitest@4.1.8(@types/node@25.9.3)(@vitest/coverage-v8@4.1.8)(vite@8.0.16(@types/node@25.9.3)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)):
+ vitest@4.1.8(@types/node@25.9.3)(@vitest/coverage-v8@4.1.8)(jsdom@29.1.1)(vite@8.0.16(@types/node@25.9.3)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)):
dependencies:
'@vitest/expect': 4.1.8
'@vitest/mocker': 4.1.8(vite@8.0.16(@types/node@25.9.3)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))
@@ -5472,13 +6602,30 @@ snapshots:
optionalDependencies:
'@types/node': 25.9.3
'@vitest/coverage-v8': 4.1.8(vitest@4.1.8)
+ jsdom: 29.1.1
transitivePeerDependencies:
- msw
+ w3c-xmlserializer@5.0.0:
+ dependencies:
+ xml-name-validator: 5.0.0
+
walk-up-path@4.0.0: {}
web-streams-polyfill@3.3.3: {}
+ webidl-conversions@8.0.1: {}
+
+ whatwg-mimetype@5.0.0: {}
+
+ whatwg-url@16.0.1:
+ dependencies:
+ '@exodus/bytes': 1.15.1
+ tr46: 6.0.0
+ webidl-conversions: 8.0.1
+ transitivePeerDependencies:
+ - '@noble/hashes'
+
which@2.0.2:
dependencies:
isexe: 2.0.0
@@ -5492,8 +6639,12 @@ snapshots:
ws@8.21.0: {}
+ xml-name-validator@5.0.0: {}
+
xml-naming@0.1.0: {}
+ xmlchars@2.2.0: {}
+
yaml@2.9.0: {}
yocto-queue@0.1.0: {}
diff --git a/scripts/gen-doc-graphs.ts b/scripts/gen-doc-graphs.ts
index 38a385fa62..cac4c0b2f9 100644
--- a/scripts/gen-doc-graphs.ts
+++ b/scripts/gen-doc-graphs.ts
@@ -598,31 +598,31 @@ function renderLifecycle(): string {
'sequenceDiagram',
' participant User',
' participant Agent',
- ' participant Loop',
+ ' participant Driver',
' participant Prompt as ctx.systemPrompt',
' participant LLM as ctx.llm',
' participant Tools as ctx.tools',
' participant Session',
' participant Persistence',
' User->>Agent: send(content)',
- ' Agent->>Loop: queued work wakes driver',
- ' Loop->>Session: turn/start + user/message',
- ' Loop-->>User: agent/turn-start',
- ' Loop->>Prompt: system-prompt/assemble waterfall',
- ' Loop-->>Loop: agent/pre-step serial checkpoint',
- ' Loop->>Session: step/start',
- ' Loop->>LLM: agent/request waterfall, then llm/stream waterfall',
- ' LLM-->>Loop: StreamChunk*',
- ' Loop->>Session: assistant/chunk*',
- ' Loop-->>User: agent/stream-chunk* (master live mirror)',
- ' Loop->>Session: assistant/message',
- ' Loop->>Tools: tools/execute waterfall for each tool-call',
+ ' Agent->>Driver: queued work wakes driver',
+ ' Driver->>Session: turn/start + user/message',
+ ' Driver-->>User: agent/turn-start',
+ ' 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*',
+ ' Driver-->>User: agent/stream-chunk* (master live mirror)',
+ ' Driver->>Session: assistant/message',
+ ' Driver->>Tools: tools/execute waterfall for each tool-call',
' Tools-->>Session: tool-owned events when applicable',
- ' Loop->>Session: tool/result',
- ' Loop-->>Loop: agent/turn-continuation waterfall',
- ' Loop->>Session: turn/end',
- ' Loop->>Persistence: session/flush parallel checkpoint',
- ' Loop-->>User: agent/status idle',
+ ' Driver->>Session: tool/result',
+ ' Driver-->>Driver: agent/turn-continuation waterfall',
+ ' Driver->>Session: turn/end',
+ ' Driver->>Persistence: session/flush parallel checkpoint',
+ ' Driver-->>User: agent/status idle',
'```',
'',
'Future pressure from the hooks stack: PR #129 removes the live `agent/stream-chunk` mirror and leaves durable `assistant/chunk` on `session/event` as the authoritative token stream. Consumers that need replayable transcript data should already treat `session/event` as the load-bearing path.',
@@ -638,21 +638,21 @@ function renderToolPipeline(): string {
'```mermaid',
'flowchart TD',
' model["Assistant message contains tool-call block"]',
- ' call["Session event: tool/call"]',
+ ' toolCall["Session event: tool/call"]',
' waterfall["ctx.tools.execute()
tools/execute waterfall"]',
' policy["Policy / permission / hooks listener"]',
- ' body["Registered tool execute() body"]',
+ ' toolBody["Registered tool execute() body"]',
' owned["Tool-owned session events
todo/write, future fs policy facts"]',
- ' result["Session event: tool/result"]',
+ ' toolResult["Session event: tool/result"]',
' ui["UI presentation
presentCall / presentResult"]',
- ' model --> call --> waterfall',
+ ' model --> toolCall --> waterfall',
' waterfall --> policy',
- ' policy -->|next()| body',
- ' policy -->|veto / throw| result',
- ' body --> owned',
- ' body --> result',
- ' call --> ui',
- ' result --> ui',
+ ' policy -->|next| toolBody',
+ ' policy -->|veto / throw| toolResult',
+ ' toolBody --> owned',
+ ' toolBody --> toolResult',
+ ' toolCall --> ui',
+ ' toolResult --> ui',
'```',
'',
'Future pressure from the fs stack: PR #128 snapshots a policy rejection card. The graph keeps the veto path explicit because filesystem read-before-edit checks, permission prompts, and hook bridges all belong on this path.',
diff --git a/scripts/verify-mermaid.ts b/scripts/verify-mermaid.ts
new file mode 100644
index 0000000000..ee27bbeee3
--- /dev/null
+++ b/scripts/verify-mermaid.ts
@@ -0,0 +1,107 @@
+/**
+ * Doc-sync gate: verify every fenced ```mermaid block parses with Mermaid's
+ * own parser. Markdown link/type/code gates can say a diagram block exists and
+ * is linked, but only Mermaid can catch syntax errors that GitHub would fail to
+ * 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.
+ *
+ * Run: `tsx scripts/verify-mermaid.ts`.
+ */
+
+import { readFileSync, realpathSync } from 'node:fs'
+import { resolve } from 'node:path'
+import { glob } from 'node:fs/promises'
+import { fromMarkdown } from 'mdast-util-from-markdown'
+import { gfmFromMarkdown } from 'mdast-util-gfm'
+import { gfm } from 'micromark-extension-gfm'
+import { JSDOM } from 'jsdom'
+import type { Nodes } from 'mdast'
+
+const root = resolve(import.meta.dirname, '..')
+
+const PATTERNS = [
+ 'README.md',
+ 'docs/**/*.md',
+ 'packages/*/*.md',
+ 'packages/*/*/*.md',
+ 'examples/**/*.md',
+ 'AGENTS.md',
+ 'packages/AGENTS.md',
+ '.agents/skills/**/*.md',
+]
+
+interface Block {
+ file: string
+ line: number
+ source: string
+}
+
+interface Violation {
+ file: string
+ line: number
+ message: string
+}
+
+function extractMermaidBlocks(file: string): Block[] {
+ const source = readFileSync(resolve(root, file), 'utf8')
+ const tree = fromMarkdown(source, { extensions: [gfm()], mdastExtensions: [gfmFromMarkdown()] })
+ const out: Block[] = []
+ const visit = (node: Nodes): void => {
+ if (node.type === 'code' && node.lang === 'mermaid') {
+ out.push({ file, line: node.position?.start.line ?? 0, source: node.value })
+ }
+ if ('children' in node) {
+ for (const child of node.children) visit(child)
+ }
+ }
+ visit(tree)
+ return out
+}
+
+function formatError(error: unknown): string {
+ if (error instanceof Error) return error.message.replace(/\s+/g, ' ').trim()
+ return String(error).replace(/\s+/g, ' ').trim()
+}
+
+const blocks: Block[] = []
+const seen = new Set()
+let checkedFiles = 0
+for (const pattern of PATTERNS) {
+ for await (const match of glob(pattern, { cwd: root })) {
+ const real = realpathSync(resolve(root, match))
+ if (seen.has(real)) continue
+ seen.add(real)
+ checkedFiles++
+ blocks.push(...extractMermaidBlocks(match))
+ }
+}
+
+const violations: Violation[] = []
+const { window } = new JSDOM('')
+Object.defineProperty(globalThis, 'window', { value: window })
+Object.defineProperty(globalThis, 'document', { value: window.document })
+Object.defineProperty(globalThis, 'navigator', { value: window.navigator })
+const mermaid = (await import('mermaid')).default
+mermaid.initialize({ startOnLoad: false })
+for (const block of blocks) {
+ try {
+ await mermaid.parse(block.source, { suppressErrors: false })
+ } catch (error: unknown) {
+ violations.push({ file: block.file, line: block.line, message: formatError(error) })
+ }
+}
+
+if (violations.length === 0) {
+ console.log(`verify-mermaid: ${blocks.length} mermaid block(s) parsed across ${checkedFiles} file(s).`)
+ process.exit(0)
+}
+
+console.error('verify-mermaid: Mermaid syntax errors found:')
+for (const violation of violations) {
+ console.error(` ${violation.file}:${violation.line} ${violation.message}`)
+}
+process.exit(1)
From fc6b7ccf1e9842616ddf7429615c960e685cd2aa Mon Sep 17 00:00:00 2001
From: Tianyi Cui <53024+tianyicui@users.noreply.github.com>
Date: Sat, 4 Jul 2026 22:43:14 +0800
Subject: [PATCH 3/5] docs: refresh graph atlas after master merge
---
docs/graphs/event-producer-consumer.md | 8 ++++----
docs/graphs/package-topology.md | 9 ++-------
2 files changed, 6 insertions(+), 11 deletions(-)
diff --git a/docs/graphs/event-producer-consumer.md b/docs/graphs/event-producer-consumer.md
index a46e9bf0dc..885086534b 100644
--- a/docs/graphs/event-producer-consumer.md
+++ b/docs/graphs/event-producer-consumer.md
@@ -9,15 +9,15 @@ This matrix shows which packages dispatch each harness-owned event and which pac
| 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`) | [`ui-stdio`](../../packages/support/ui-stdio) |
-| `agent/disposed` | `emit` | [`packages/core/agent/src/types.ts:241`](../../packages/core/agent/src/types.ts) | [`agent`](../../packages/core/agent) (`emit`) | [`ui-stdio`](../../packages/support/ui-stdio) |
+| `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), [`ui-stdio`](../../packages/support/ui-stdio) |
+| `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) |
@@ -26,7 +26,7 @@ This matrix shows which packages dispatch each harness-owned event and which pac
| `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), [`ui-stdio`](../../packages/support/ui-stdio) |
+| `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) |
diff --git a/docs/graphs/package-topology.md b/docs/graphs/package-topology.md
index b22cd74f1b..04fadd1438 100644
--- a/docs/graphs/package-topology.md
+++ b/docs/graphs/package-topology.md
@@ -73,7 +73,6 @@ flowchart TD
pkg_invariants["invariants"]
pkg_llm_replay["llm-replay"]
pkg_subagent_mock["subagent-mock"]
- pkg_ui_stdio["ui-stdio"]
end
subgraph group_ui["packages/ui"]
pkg_acp["acp"]
@@ -121,9 +120,6 @@ flowchart TD
pkg_invariants --> pkg_agent
pkg_invariants --> pkg_llm
pkg_invariants --> pkg_session
- pkg_ui_stdio --> pkg_agent
- pkg_ui_stdio --> pkg_llm
- pkg_ui_stdio --> pkg_session
pkg_agent_loop --> pkg_agent
pkg_agent_loop --> pkg_llm
pkg_agent_loop --> pkg_session
@@ -198,9 +194,9 @@ flowchart TD
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
- pkg_stdio_agent --> pkg_ui_stdio
```
| Package | Group | Depends on |
@@ -231,7 +227,6 @@ flowchart TD
| [`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) |
-| [`ui-stdio`](../../packages/support/ui-stdio) | `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) |
@@ -249,4 +244,4 @@ flowchart TD
| [`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), [`session`](../../packages/core/session), [`session-persistence-jsonl`](../../packages/session-persistence/session-persistence-jsonl), [`ui-stdio`](../../packages/support/ui-stdio) |
+| [`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) |
From 1316022cc8eeebd348e0960f0e486fa89a44d17c Mon Sep 17 00:00:00 2001
From: Tianyi Cui <53024+tianyicui@users.noreply.github.com>
Date: Sun, 5 Jul 2026 01:25:58 +0800
Subject: [PATCH 4/5] docs: revise graph docs from review
---
README.i18n.yaml | 4 +-
README.md | 2 +-
README.zh.md | 2 +-
docs/acp-agent-composition.md | 67 +++
docs/{graphs => acp}/snapshot-replay.md | 2 +-
docs/agent-lifecycle.md | 49 ++
docs/architecture.md | 2 +-
docs/{graphs => }/capability-seams.md | 24 +-
docs/coding-agent-composition.md | 67 +++
docs/development.i18n.yaml | 4 +-
docs/development.md | 4 +-
docs/development.zh.md | 4 +-
docs/echo-agent-composition.md | 40 ++
docs/event-producer-consumer.md | 38 ++
docs/graph-atlas.md | 25 +
docs/graphs/README.md | 26 -
docs/graphs/agent-lifecycle.md | 49 --
docs/graphs/app-composition.md | 106 ----
docs/graphs/event-producer-consumer.md | 38 --
docs/graphs/hot-reload-disposal.md | 24 -
docs/graphs/package-topology.md | 247 ---------
docs/graphs/session-surface.md | 25 -
docs/graphs/subagent-lineage.md | 27 -
docs/graphs/tool-affordance-map.md | 50 --
docs/module-graph.md | 402 +++++++++------
docs/rfc/README.md | 2 +-
.../2026-07-03-documentation-graph-atlas.md | 44 +-
docs/tool-catalog/tools.md | 18 +
docs/{graphs => }/tool-execution-pipeline.md | 12 +-
.../core/tools/tests/gen-tool-catalog.spec.ts | 5 +
scripts/gen-doc-graphs.ts | 471 ++++++------------
scripts/gen-module-graph.ts | 86 +++-
scripts/gen-tool-catalog.ts | 52 +-
scripts/verify-mermaid.ts | 7 +-
34 files changed, 882 insertions(+), 1143 deletions(-)
create mode 100644 docs/acp-agent-composition.md
rename docs/{graphs => acp}/snapshot-replay.md (95%)
create mode 100644 docs/agent-lifecycle.md
rename docs/{graphs => }/capability-seams.md (51%)
create mode 100644 docs/coding-agent-composition.md
create mode 100644 docs/echo-agent-composition.md
create mode 100644 docs/event-producer-consumer.md
create mode 100644 docs/graph-atlas.md
delete mode 100644 docs/graphs/README.md
delete mode 100644 docs/graphs/agent-lifecycle.md
delete mode 100644 docs/graphs/app-composition.md
delete mode 100644 docs/graphs/event-producer-consumer.md
delete mode 100644 docs/graphs/hot-reload-disposal.md
delete mode 100644 docs/graphs/package-topology.md
delete mode 100644 docs/graphs/session-surface.md
delete mode 100644 docs/graphs/subagent-lineage.md
delete mode 100644 docs/graphs/tool-affordance-map.md
rename docs/{graphs => }/tool-execution-pipeline.md (71%)
diff --git a/README.i18n.yaml b/README.i18n.yaml
index c042e0a535..db27519f15 100644
--- a/README.i18n.yaml
+++ b/README.i18n.yaml
@@ -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
diff --git a/README.md b/README.md
index 11db682a99..53dd3896eb 100644
--- a/README.md
+++ b/README.md
@@ -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).
diff --git a/README.zh.md b/README.zh.md
index f2cd9da367..5de4c5b680 100644
--- a/README.zh.md
+++ b/README.zh.md
@@ -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)。
diff --git a/docs/acp-agent-composition.md b/docs/acp-agent-composition.md
new file mode 100644
index 0000000000..db3a59eaed
--- /dev/null
+++ b/docs/acp-agent-composition.md
@@ -0,0 +1,67 @@
+
+
+# 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
cordis.yml"]
+ plugin_acp_llm_deepseek["llm-deepseek
@deepseek-ai/dsh-llm-deepseek"]
+ cfg --> plugin_acp_llm_deepseek
+ plugin_acp_bash["bash
@deepseek-ai/dsh-bash-local"]
+ cfg --> plugin_acp_bash
+ plugin_acp_acp_agent["acp-agent
@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
JSON-RPC stdio bridge
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
@deepseek-ai/dsh-subagent"]
+ cfg --> plugin_acp_subagent
+ plugin_acp_subagent_spawn["subagent-spawn
@deepseek-ai/dsh-subagent-spawn"]
+ cfg --> plugin_acp_subagent_spawn
+ plugin_acp_subagent_fork["subagent-fork
@deepseek-ai/dsh-subagent-fork"]
+ cfg --> plugin_acp_subagent_fork
+ plugin_acp_tool_subagent["tool-subagent
@deepseek-ai/dsh-tool-subagent"]
+ cfg --> plugin_acp_tool_subagent
+ plugin_acp_tool_subagent_fork["tool-subagent-fork
@deepseek-ai/dsh-tool-subagent"]
+ cfg --> plugin_acp_tool_subagent_fork
+ plugin_acp_tool_todo["tool-todo
@deepseek-ai/dsh-tool-todo"]
+ cfg --> plugin_acp_tool_todo
+ plugin_acp_fs_local["fs-local
@deepseek-ai/dsh-fs-local"]
+ cfg --> plugin_acp_fs_local
+ plugin_acp_fs_policy["fs-policy
@deepseek-ai/dsh-fs-policy"]
+ cfg --> plugin_acp_fs_policy
+ plugin_acp_tool_fs["tool-fs
@deepseek-ai/dsh-tool-fs"]
+ cfg --> plugin_acp_tool_fs
+ plugin_acp_hooks_claude["hooks-claude
@deepseek-ai/dsh-hooks-claude"]
+ cfg --> plugin_acp_hooks_claude
+ plugin_acp_hooks_codex["hooks-codex
@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).
diff --git a/docs/graphs/snapshot-replay.md b/docs/acp/snapshot-replay.md
similarity index 95%
rename from docs/graphs/snapshot-replay.md
rename to docs/acp/snapshot-replay.md
index 1c5e20520a..0acf3c0a96 100644
--- a/docs/graphs/snapshot-replay.md
+++ b/docs/acp/snapshot-replay.md
@@ -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 llm/stream chunks
ACP->>Workspace: bash, fs, and hook side effects
ACP->>Golden: normalized sessionUpdate stream
Golden-->>ACP: diff must be empty
diff --git a/docs/agent-lifecycle.md b/docs/agent-lifecycle.md
new file mode 100644
index 0000000000..eb2744f18a
--- /dev/null
+++ b/docs/agent-lifecycle.md
@@ -0,0 +1,49 @@
+
+
+# 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.
diff --git a/docs/architecture.md b/docs/architecture.md
index e4b0bdb5f1..800a1a2099 100644
--- a/docs/architecture.md
+++ b/docs/architecture.md
@@ -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
diff --git a/docs/graphs/capability-seams.md b/docs/capability-seams.md
similarity index 51%
rename from docs/graphs/capability-seams.md
rename to docs/capability-seams.md
index 60bbd10656..7af76d043b 100644
--- a/docs/graphs/capability-seams.md
+++ b/docs/capability-seams.md
@@ -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. |
diff --git a/docs/coding-agent-composition.md b/docs/coding-agent-composition.md
new file mode 100644
index 0000000000..9a339499f8
--- /dev/null
+++ b/docs/coding-agent-composition.md
@@ -0,0 +1,67 @@
+
+
+# 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
cordis.yml"]
+ plugin_coding_hmr["hmr
@cordisjs/plugin-hmr"]
+ cfg --> plugin_coding_hmr
+ plugin_coding_llm_deepseek["llm-deepseek
@deepseek-ai/dsh-llm-deepseek"]
+ cfg --> plugin_coding_llm_deepseek
+ plugin_coding_bash["bash
@deepseek-ai/dsh-bash-local"]
+ cfg --> plugin_coding_bash
+ plugin_coding_stdio_agent["stdio-agent
@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
console logger
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
@deepseek-ai/dsh-compact-basic"]
+ cfg --> plugin_coding_compact_basic
+ plugin_coding_subagent["subagent
@deepseek-ai/dsh-subagent"]
+ cfg --> plugin_coding_subagent
+ plugin_coding_subagent_spawn["subagent-spawn
@deepseek-ai/dsh-subagent-spawn"]
+ cfg --> plugin_coding_subagent_spawn
+ plugin_coding_subagent_fork["subagent-fork
@deepseek-ai/dsh-subagent-fork"]
+ cfg --> plugin_coding_subagent_fork
+ plugin_coding_tool_subagent["tool-subagent
@deepseek-ai/dsh-tool-subagent"]
+ cfg --> plugin_coding_tool_subagent
+ plugin_coding_tool_subagent_fork["tool-subagent-fork
@deepseek-ai/dsh-tool-subagent"]
+ cfg --> plugin_coding_tool_subagent_fork
+ plugin_coding_tool_todo["tool-todo
@deepseek-ai/dsh-tool-todo"]
+ cfg --> plugin_coding_tool_todo
+ plugin_coding_fs_local["fs-local
@deepseek-ai/dsh-fs-local"]
+ cfg --> plugin_coding_fs_local
+ plugin_coding_fs_policy["fs-policy
@deepseek-ai/dsh-fs-policy"]
+ cfg --> plugin_coding_fs_policy
+ plugin_coding_tool_fs["tool-fs
@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).
diff --git a/docs/development.i18n.yaml b/docs/development.i18n.yaml
index 64b874cf8f..7d0922febd 100644
--- a/docs/development.i18n.yaml
+++ b/docs/development.i18n.yaml
@@ -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
diff --git a/docs/development.md b/docs/development.md
index 18b3791701..af8d13e08b 100644
--- a/docs/development.md
+++ b/docs/development.md
@@ -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
diff --git a/docs/development.zh.md b/docs/development.zh.md
index 7005ad24be..3163c7a21e 100644
--- a/docs/development.zh.md
+++ b/docs/development.zh.md
@@ -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
diff --git a/docs/echo-agent-composition.md b/docs/echo-agent-composition.md
new file mode 100644
index 0000000000..1e1e2719cf
--- /dev/null
+++ b/docs/echo-agent-composition.md
@@ -0,0 +1,40 @@
+
+
+# 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
cordis.yml"]
+ plugin_echo_hmr["hmr
@cordisjs/plugin-hmr"]
+ cfg --> plugin_echo_hmr
+ plugin_echo_mock_llm["mock-llm
./src/mock-llm.ts"]
+ cfg --> plugin_echo_mock_llm
+ plugin_echo_echo_tool["echo-tool
./src/echo-tool.ts"]
+ cfg --> plugin_echo_echo_tool
+ plugin_echo_bash["bash
@deepseek-ai/dsh-bash-local"]
+ cfg --> plugin_echo_bash
+ plugin_echo_stdio_agent["stdio-agent
@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
console logger
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).
diff --git a/docs/event-producer-consumer.md b/docs/event-producer-consumer.md
new file mode 100644
index 0000000000..63cedd7510
--- /dev/null
+++ b/docs/event-producer-consumer.md
@@ -0,0 +1,38 @@
+
+
+# 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`) | - |
diff --git a/docs/graph-atlas.md b/docs/graph-atlas.md
new file mode 100644
index 0000000000..09875015f8
--- /dev/null
+++ b/docs/graph-atlas.md
@@ -0,0 +1,25 @@
+
+
+# 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`.
diff --git a/docs/graphs/README.md b/docs/graphs/README.md
deleted file mode 100644
index 995481d160..0000000000
--- a/docs/graphs/README.md
+++ /dev/null
@@ -1,26 +0,0 @@
-
-
-# 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`.
diff --git a/docs/graphs/agent-lifecycle.md b/docs/graphs/agent-lifecycle.md
deleted file mode 100644
index aca82a80a0..0000000000
--- a/docs/graphs/agent-lifecycle.md
+++ /dev/null
@@ -1,49 +0,0 @@
-
-
-# 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.
diff --git a/docs/graphs/app-composition.md b/docs/graphs/app-composition.md
deleted file mode 100644
index ba1ff05a2a..0000000000
--- a/docs/graphs/app-composition.md
+++ /dev/null
@@ -1,106 +0,0 @@
-
-
-# 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
@cordisjs/plugin-hmr"]
- cfg_echo --> plugin_echo_hmr
- plugin_echo_mock_llm["mock-llm
./src/mock-llm.ts"]
- cfg_echo --> plugin_echo_mock_llm
- plugin_echo_echo_tool["echo-tool
./src/echo-tool.ts"]
- cfg_echo --> plugin_echo_echo_tool
- plugin_echo_bash["bash
@deepseek-ai/dsh-bash-local"]
- cfg_echo --> plugin_echo_bash
- plugin_echo_stdio_agent["stdio-agent
@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
@cordisjs/plugin-hmr"]
- cfg_coding --> plugin_coding_hmr
- plugin_coding_llm_deepseek["llm-deepseek
@deepseek-ai/dsh-llm-deepseek"]
- cfg_coding --> plugin_coding_llm_deepseek
- plugin_coding_bash["bash
@deepseek-ai/dsh-bash-local"]
- cfg_coding --> plugin_coding_bash
- plugin_coding_stdio_agent["stdio-agent
@deepseek-ai/dsh-stdio-agent"]
- cfg_coding --> plugin_coding_stdio_agent
- plugin_coding_stdio_agent --> bundle_stdio
- plugin_coding_compact_basic["compact-basic
@deepseek-ai/dsh-compact-basic"]
- cfg_coding --> plugin_coding_compact_basic
- plugin_coding_subagent["subagent
@deepseek-ai/dsh-subagent"]
- cfg_coding --> plugin_coding_subagent
- plugin_coding_subagent_spawn["subagent-spawn
@deepseek-ai/dsh-subagent-spawn"]
- cfg_coding --> plugin_coding_subagent_spawn
- plugin_coding_subagent_fork["subagent-fork
@deepseek-ai/dsh-subagent-fork"]
- cfg_coding --> plugin_coding_subagent_fork
- plugin_coding_tool_subagent["tool-subagent
@deepseek-ai/dsh-tool-subagent"]
- cfg_coding --> plugin_coding_tool_subagent
- plugin_coding_tool_subagent_fork["tool-subagent-fork
@deepseek-ai/dsh-tool-subagent"]
- cfg_coding --> plugin_coding_tool_subagent_fork
- plugin_coding_tool_todo["tool-todo
@deepseek-ai/dsh-tool-todo"]
- cfg_coding --> plugin_coding_tool_todo
- plugin_coding_fs_local["fs-local
@deepseek-ai/dsh-fs-local"]
- cfg_coding --> plugin_coding_fs_local
- plugin_coding_fs_policy["fs-policy
@deepseek-ai/dsh-fs-policy"]
- cfg_coding --> plugin_coding_fs_policy
- plugin_coding_tool_fs["tool-fs
@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
@deepseek-ai/dsh-llm-deepseek"]
- cfg_acp --> plugin_acp_llm_deepseek
- plugin_acp_bash["bash
@deepseek-ai/dsh-bash-local"]
- cfg_acp --> plugin_acp_bash
- plugin_acp_acp_agent["acp-agent
@deepseek-ai/dsh-acp-agent"]
- cfg_acp --> plugin_acp_acp_agent
- plugin_acp_acp_agent --> bundle_acp_agent
- plugin_acp_subagent["subagent
@deepseek-ai/dsh-subagent"]
- cfg_acp --> plugin_acp_subagent
- plugin_acp_subagent_spawn["subagent-spawn
@deepseek-ai/dsh-subagent-spawn"]
- cfg_acp --> plugin_acp_subagent_spawn
- plugin_acp_subagent_fork["subagent-fork
@deepseek-ai/dsh-subagent-fork"]
- cfg_acp --> plugin_acp_subagent_fork
- plugin_acp_tool_subagent["tool-subagent
@deepseek-ai/dsh-tool-subagent"]
- cfg_acp --> plugin_acp_tool_subagent
- plugin_acp_tool_subagent_fork["tool-subagent-fork
@deepseek-ai/dsh-tool-subagent"]
- cfg_acp --> plugin_acp_tool_subagent_fork
- plugin_acp_tool_todo["tool-todo
@deepseek-ai/dsh-tool-todo"]
- cfg_acp --> plugin_acp_tool_todo
- plugin_acp_fs_local["fs-local
@deepseek-ai/dsh-fs-local"]
- cfg_acp --> plugin_acp_fs_local
- plugin_acp_fs_policy["fs-policy
@deepseek-ai/dsh-fs-policy"]
- cfg_acp --> plugin_acp_fs_policy
- plugin_acp_tool_fs["tool-fs
@deepseek-ai/dsh-tool-fs"]
- cfg_acp --> plugin_acp_tool_fs
- plugin_acp_hooks_claude["hooks-claude
@deepseek-ai/dsh-hooks-claude"]
- cfg_acp --> plugin_acp_hooks_claude
- plugin_acp_hooks_codex["hooks-codex
@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) |
diff --git a/docs/graphs/event-producer-consumer.md b/docs/graphs/event-producer-consumer.md
deleted file mode 100644
index 885086534b..0000000000
--- a/docs/graphs/event-producer-consumer.md
+++ /dev/null
@@ -1,38 +0,0 @@
-
-
-# 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`) | - |
diff --git a/docs/graphs/hot-reload-disposal.md b/docs/graphs/hot-reload-disposal.md
deleted file mode 100644
index ca98c25692..0000000000
--- a/docs/graphs/hot-reload-disposal.md
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
-# 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
events, tools, adapters, timers"]
- reload["HMR / fiber.dispose()"]
- disposers["Run disposers in owner fiber"]
- quiescence["Owned AgentHandle.dispose()
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.
diff --git a/docs/graphs/package-topology.md b/docs/graphs/package-topology.md
deleted file mode 100644
index 04fadd1438..0000000000
--- a/docs/graphs/package-topology.md
+++ /dev/null
@@ -1,247 +0,0 @@
-
-
-# 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//` 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) |
diff --git a/docs/graphs/session-surface.md b/docs/graphs/session-surface.md
deleted file mode 100644
index b25f85b2d2..0000000000
--- a/docs/graphs/session-surface.md
+++ /dev/null
@@ -1,25 +0,0 @@
-
-
-# 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
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.
diff --git a/docs/graphs/subagent-lineage.md b/docs/graphs/subagent-lineage.md
deleted file mode 100644
index f687405782..0000000000
--- a/docs/graphs/subagent-lineage.md
+++ /dev/null
@@ -1,27 +0,0 @@
-
-
-# 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
model-facing name"]
- registry["ctx.subagents provider registry"]
- spawn["spawn provider
fresh child session"]
- fork["fork provider
seeded from completed-turn prefix"]
- acp["ACP provider
out-of-process child"]
- child["Child AgentHandle
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.
diff --git a/docs/graphs/tool-affordance-map.md b/docs/graphs/tool-affordance-map.md
deleted file mode 100644
index 58632da390..0000000000
--- a/docs/graphs/tool-affordance-map.md
+++ /dev/null
@@ -1,50 +0,0 @@
-
-
-# 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
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
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
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
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
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. |
diff --git a/docs/module-graph.md b/docs/module-graph.md
index b2f86332aa..c38baf0c1a 100644
--- a/docs/module-graph.md
+++ b/docs/module-graph.md
@@ -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//` 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) |
diff --git a/docs/rfc/README.md b/docs/rfc/README.md
index d04d6a37cb..1b5a3d350e 100644
--- a/docs/rfc/README.md
+++ b/docs/rfc/README.md
@@ -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 |
diff --git a/docs/rfc/implemented/process/2026-07-03-documentation-graph-atlas.md b/docs/rfc/implemented/process/2026-07-03-documentation-graph-atlas.md
index 97c7b13bf6..d10aa6de61 100644
--- a/docs/rfc/implemented/process/2026-07-03-documentation-graph-atlas.md
+++ b/docs/rfc/implemented/process/2026-07-03-documentation-graph-atlas.md
@@ -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//` 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.` 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.
diff --git a/docs/tool-catalog/tools.md b/docs/tool-catalog/tools.md
index 5b81b9d9d7..b6eb25c447 100644
--- a/docs/tool-catalog/tools.md
+++ b/docs/tool-catalog/tools.md
@@ -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.
diff --git a/docs/graphs/tool-execution-pipeline.md b/docs/tool-execution-pipeline.md
similarity index 71%
rename from docs/graphs/tool-execution-pipeline.md
rename to docs/tool-execution-pipeline.md
index a5f0aeb36f..81bac5eaaf 100644
--- a/docs/graphs/tool-execution-pipeline.md
+++ b/docs/tool-execution-pipeline.md
@@ -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
logged before execution"]
+ toolCall["Session event: tool/call
logged before execution"]
presentCall["UI pending card
presentCall(args)"]
- pre["tools/pre-execute waterfall
hooks, permission, sandbox"]
+ pre["tools/pre-execute waterfall
hooks, permission, sandbox"]
denied["deny or ask
tool body skipped"]
toolBody["Registered tool execute() body"]
- fsGate["fs/write-intent or fs/edit-intent
tool-fs mutations only"]
- owned["Tool-owned session events
todo/write, fs/observed, hook/invoked, hook/result"]
- post["tools/post-execute waterfall
accept, block, replace, add context"]
+ fsGate["fs/write-intent or fs/edit-intent
tool-fs mutations only"]
+ owned["Tool-owned session events
todo/write, fs/observed, hook/invoked, hook/result"]
+ post["tools/post-execute waterfall
accept, block, replace, add context"]
context["Buffered additionalContext
context/message after all tool results"]
- toolResult["Session event: tool/result
single model-facing outcome"]
+ toolResult["Session event: tool/result
single model-facing outcome"]
presentResult["UI completed card
presentResult(args, result)"]
model --> toolCall
toolCall --> presentCall
diff --git a/packages/core/tools/tests/gen-tool-catalog.spec.ts b/packages/core/tools/tests/gen-tool-catalog.spec.ts
index f034c0529f..efc9ae44e5 100644
--- a/packages/core/tools/tests/gen-tool-catalog.spec.ts
+++ b/packages/core/tools/tests/gen-tool-catalog.spec.ts
@@ -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 }],
},
]
diff --git a/scripts/gen-doc-graphs.ts b/scripts/gen-doc-graphs.ts
index 07c09427de..92784fd416 100644
--- a/scripts/gen-doc-graphs.ts
+++ b/scripts/gen-doc-graphs.ts
@@ -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
}
-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 = {
- '@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 `${value.replace(/&/g, '&').replace(//g, '>')}`
+}
+
+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 {
@@ -311,52 +285,10 @@ function pkgList(names: string[] | undefined, pkgsByShort: Map): 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, '
')
}
-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//` 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
console logger
pre-created main agent"]`)
+ } else if (pluginName === '@deepseek-ai/dsh-acp-agent') {
+ lines.push(` ${appNode} --> ${nodeId('frontdoor', 'acp')}["@deepseek-ai/dsh-acp
JSON-RPC stdio bridge
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)}
cordis.yml"]`,
)
- const bundleTargets: Record = {
- '@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)}
${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)}
${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>(), listeners: new Set() }
- 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 {
- 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()
- 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, ''))}
${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
logged before execution"]',
+ ` toolCall["Session event: ${mermaidCode('tool/call')}
logged before execution"]`,
' presentCall["UI pending card
presentCall(args)"]',
- ' pre["tools/pre-execute waterfall
hooks, permission, sandbox"]',
+ ` pre["${mermaidCode('tools/pre-execute')} waterfall
hooks, permission, sandbox"]`,
' denied["deny or ask
tool body skipped"]',
' toolBody["Registered tool execute() body"]',
- ' fsGate["fs/write-intent or fs/edit-intent
tool-fs mutations only"]',
- ' owned["Tool-owned session events
todo/write, fs/observed, hook/invoked, hook/result"]',
- ' post["tools/post-execute waterfall
accept, block, replace, add context"]',
+ ` fsGate["${mermaidCode('fs/write-intent')} or ${mermaidCode('fs/edit-intent')}
tool-fs mutations only"]`,
+ ` owned["Tool-owned session events
${mermaidCode('todo/write')}, ${mermaidCode('fs/observed')}, ${mermaidCode('hook/invoked')}, ${mermaidCode('hook/result')}"]`,
+ ` post["${mermaidCode('tools/post-execute')} waterfall
accept, block, replace, add context"]`,
' context["Buffered additionalContext
context/message after all tool results"]',
- ' toolResult["Session event: tool/result
single model-facing outcome"]',
+ ` toolResult["Session event: ${mermaidCode('tool/result')}
single model-facing outcome"]`,
' presentResult["UI completed card
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
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
model-facing name"]',
- ' registry["ctx.subagents provider registry"]',
- ' spawn["spawn provider
fresh child session"]',
- ' fork["fork provider
seeded from completed-turn prefix"]',
- ' acp["ACP provider
out-of-process child"]',
- ' child["Child AgentHandle
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
events, tools, adapters, timers"]',
- ' reload["HMR / fiber.dispose()"]',
- ' disposers["Run disposers in owner fiber"]',
- ' quiescence["Owned AgentHandle.dispose()
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 {
+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 = {
- '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 = {
- '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 {
- 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 {
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()
}
diff --git a/scripts/gen-module-graph.ts b/scripts/gen-module-graph.ts
index ebaf7d5db8..b84701c819 100644
--- a/scripts/gen-module-graph.ts
+++ b/scripts/gen-module-graph.ts
@@ -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//` 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: 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 [
'',
'',
'# 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//` 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')
diff --git a/scripts/gen-tool-catalog.ts b/scripts/gen-tool-catalog.ts
index 3298c507f8..e42754bf53 100644
--- a/scripts/gen-tool-catalog.ts
+++ b/scripts/gen-tool-catalog.ts
@@ -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
@@ -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, '
') : '-'
+}
+
/** 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}\``, '')
diff --git a/scripts/verify-mermaid.ts b/scripts/verify-mermaid.ts
index ee27bbeee3..954c246640 100644
--- a/scripts/verify-mermaid.ts
+++ b/scripts/verify-mermaid.ts
@@ -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',
From 54a3c8b2a426a0e875382f70b4e0d7b6df30bf6b Mon Sep 17 00:00:00 2001
From: Tianyi Cui <53024+tianyicui@users.noreply.github.com>
Date: Sun, 5 Jul 2026 02:54:01 +0800
Subject: [PATCH 5/5] docs: address graph review placement
---
docs/acp/snapshot-replay.md | 4 +-
docs/agent-lifecycle.md | 4 +-
docs/capability-seams.md | 4 +-
docs/event-producer-consumer.md | 4 +-
docs/graph-atlas.md | 10 +--
.../2026-07-03-documentation-graph-atlas.md | 6 +-
docs/tool-execution-pipeline.md | 4 +-
.../acp-agent/composition.md | 6 +-
.../coding-agent/composition.md | 6 +-
.../echo-agent/composition.md | 6 +-
scripts/gen-doc-graphs.ts | 73 ++++++++++++-------
11 files changed, 74 insertions(+), 53 deletions(-)
rename docs/acp-agent-composition.md => examples/acp-agent/composition.md (97%)
rename docs/coding-agent-composition.md => examples/coding-agent/composition.md (97%)
rename docs/echo-agent-composition.md => examples/echo-agent/composition.md (95%)
diff --git a/docs/acp/snapshot-replay.md b/docs/acp/snapshot-replay.md
index 0acf3c0a96..4242f2406d 100644
--- a/docs/acp/snapshot-replay.md
+++ b/docs/acp/snapshot-replay.md
@@ -3,8 +3,6 @@
# ACP Snapshot Replay
-Maintenance mode: curated Mermaid sequence based on the snapshot test harness.
-
This graph explains what a snapshot scenario proves: recorded real-model session logs are replayed keylessly, ACP stdout is normalized and diffed, and scenario workspaces preserve tool side effects that the UI stream alone cannot prove.
```mermaid
@@ -25,3 +23,5 @@ sequenceDiagram
```
The fs and hook snapshot matrix is valuable because it proves world state, hook decisions, and failed tool-card rendering, not just that replay returns text.
+
+Maintenance mode: curated Mermaid sequence based on the snapshot test harness.
diff --git a/docs/agent-lifecycle.md b/docs/agent-lifecycle.md
index eb2744f18a..d6d9ab1ba7 100644
--- a/docs/agent-lifecycle.md
+++ b/docs/agent-lifecycle.md
@@ -3,8 +3,6 @@
# 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
@@ -47,3 +45,5 @@ sequenceDiagram
```
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.
+
+Maintenance mode: curated Mermaid sequence; exact event signatures live in the generated Cordis catalog.
diff --git a/docs/capability-seams.md b/docs/capability-seams.md
index 7af76d043b..414ee898d1 100644
--- a/docs/capability-seams.md
+++ b/docs/capability-seams.md
@@ -3,8 +3,6 @@
# Capability Seams And Core Services
-Maintenance mode: hybrid: services are discovered from Cordis declarations; interface/implementation/consumer roles are classified in `scripts/gen-doc-graphs.ts` with a completeness guard.
-
A service can be a core spine service, a swappable capability seam, or a bundle/composition point. The graph shows the package that owns the service declaration, known implementation packages, and packages that consume the service directly.
```mermaid
@@ -140,3 +138,5 @@ flowchart LR
| `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. |
+
+Maintenance mode: hybrid: services are discovered from Cordis declarations; interface/implementation/consumer roles are classified in `scripts/gen-doc-graphs.ts` with a completeness guard.
diff --git a/docs/event-producer-consumer.md b/docs/event-producer-consumer.md
index 1ac69ffd90..5460f662d4 100644
--- a/docs/event-producer-consumer.md
+++ b/docs/event-producer-consumer.md
@@ -3,8 +3,6 @@
# 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 |
@@ -34,3 +32,5 @@ This matrix shows which packages dispatch each harness-owned event and which pac
| `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) |
+
+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`.
diff --git a/docs/graph-atlas.md b/docs/graph-atlas.md
index c99b54dce6..9fb678d3d7 100644
--- a/docs/graph-atlas.md
+++ b/docs/graph-atlas.md
@@ -3,8 +3,6 @@
# 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 the generated [events](cordis-catalog/events.md) / [services](cordis-catalog/services.md) catalogs, [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).
@@ -14,12 +12,14 @@ The process decision behind this index is recorded in [the documentation graph R
| [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` |
+| [echo-agent app composition](../examples/echo-agent/composition.md) | `hybrid generated` |
+| [coding-agent app composition](../examples/coding-agent/composition.md) | `hybrid generated` |
+| [acp-agent app composition](../examples/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`.
+
+Maintenance mode: mixed: each linked page declares generated, hybrid, or curated mode.
diff --git a/docs/rfc/implemented/process/2026-07-03-documentation-graph-atlas.md b/docs/rfc/implemented/process/2026-07-03-documentation-graph-atlas.md
index ad3e90a16f..c8d46c6fd2 100644
--- a/docs/rfc/implemented/process/2026-07-03-documentation-graph-atlas.md
+++ b/docs/rfc/implemented/process/2026-07-03-documentation-graph-atlas.md
@@ -33,9 +33,9 @@ The first index links ten relationship surfaces. Package topology and tool-packa
| [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 |
+| [echo-agent app composition](../../../../examples/echo-agent/composition.md) | hybrid generated | `examples/echo-agent/cordis.yml` plugin list plus curated app/bundle expansion |
+| [coding-agent app composition](../../../../examples/coding-agent/composition.md) | hybrid generated | `examples/coding-agent/cordis.yml` plugin list plus curated app/bundle expansion |
+| [acp-agent app composition](../../../../examples/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 |
diff --git a/docs/tool-execution-pipeline.md b/docs/tool-execution-pipeline.md
index 81bac5eaaf..db50a3beec 100644
--- a/docs/tool-execution-pipeline.md
+++ b/docs/tool-execution-pipeline.md
@@ -3,8 +3,6 @@
# Tool Execution Pipeline
-Maintenance mode: curated Mermaid flow; exact tool schemas and event signatures live in generated catalogs.
-
This graph shows where policy, hooks, sandboxing, filesystem guards, result rewriting, and UI rendering fit without changing the loop. The key extension points are the `tools/pre-execute` and `tools/post-execute` waterfalls.
```mermaid
@@ -37,3 +35,5 @@ flowchart TD
```
Filesystem read-before-edit checks live below `tool-fs` on the `fs/*` event gate, while hook bridges and future permission prompts live on the generic tool waterfalls. That split lets the same hooks observe bash, fs, web, todo, and subagent calls without coupling those tools to one policy service.
+
+Maintenance mode: curated Mermaid flow; exact tool schemas and event signatures live in generated catalogs.
diff --git a/docs/acp-agent-composition.md b/examples/acp-agent/composition.md
similarity index 97%
rename from docs/acp-agent-composition.md
rename to examples/acp-agent/composition.md
index db3a59eaed..2644a2b112 100644
--- a/docs/acp-agent-composition.md
+++ b/examples/acp-agent/composition.md
@@ -3,8 +3,6 @@
# 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
@@ -64,4 +62,6 @@ flowchart LR
| `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).
+Source config: [`examples/acp-agent/cordis.yml`](cordis.yml).
+
+Maintenance mode: hybrid: the leaf plugin list is parsed from its `cordis.yml`; app package expansion is curated from package source.
diff --git a/docs/coding-agent-composition.md b/examples/coding-agent/composition.md
similarity index 97%
rename from docs/coding-agent-composition.md
rename to examples/coding-agent/composition.md
index 9a339499f8..926cb56109 100644
--- a/docs/coding-agent-composition.md
+++ b/examples/coding-agent/composition.md
@@ -3,8 +3,6 @@
# 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
@@ -64,4 +62,6 @@ flowchart LR
| `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).
+Source config: [`examples/coding-agent/cordis.yml`](cordis.yml).
+
+Maintenance mode: hybrid: the leaf plugin list is parsed from its `cordis.yml`; app package expansion is curated from package source.
diff --git a/docs/echo-agent-composition.md b/examples/echo-agent/composition.md
similarity index 95%
rename from docs/echo-agent-composition.md
rename to examples/echo-agent/composition.md
index 1e1e2719cf..1491c56955 100644
--- a/docs/echo-agent-composition.md
+++ b/examples/echo-agent/composition.md
@@ -3,8 +3,6 @@
# 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
@@ -37,4 +35,6 @@ flowchart LR
| `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).
+Source config: [`examples/echo-agent/cordis.yml`](cordis.yml).
+
+Maintenance mode: hybrid: the leaf plugin list is parsed from its `cordis.yml`; app package expansion is curated from package source.
diff --git a/scripts/gen-doc-graphs.ts b/scripts/gen-doc-graphs.ts
index 931e01f041..183f8ca62a 100644
--- a/scripts/gen-doc-graphs.ts
+++ b/scripts/gen-doc-graphs.ts
@@ -5,7 +5,7 @@
* - 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/*.md relationship diagrams answer "how do those pieces fit together?"
+ * - generated 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
@@ -19,7 +19,7 @@
*/
import { existsSync, globSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs'
-import { dirname, resolve } from 'node:path'
+import { dirname, relative, resolve } from 'node:path'
import ts from 'typescript'
import { collectEvents, collectServices } from './gen-cordis-catalog.ts'
@@ -196,18 +196,28 @@ const DYNAMIC_EVENT_DISPATCHERS: Array<{ event: string; pkg: string; method: str
{ event: 'subagent/end', pkg: 'subagent', method: 'events.dispatch' },
]
-function generatedHeader(title: string, source: string): string[] {
+function generatedHeader(title: string): string[] {
return [
'',
'',
`# ${title}`,
'',
- `Maintenance mode: ${source}.`,
- '',
]
}
+function maintenanceFooter(source: string): string[] {
+ return [`Maintenance mode: ${source}.`, '']
+}
+
+function graphIndexLink(rel: string): string {
+ return relative('docs', rel).replaceAll('\\', '/')
+}
+
+function linkFromDoc(docRel: string, targetRel: string): string {
+ return relative(dirname(docRel), targetRel).replaceAll('\\', '/')
+}
+
function collectPackages(): Pkg[] {
const pkgs: Pkg[] = []
for (const rel of globSync('packages/*/*/package.json', { cwd: root }).sort()) {
@@ -305,6 +315,7 @@ function assertServiceRolesComplete(): void {
function renderCapabilitySeams(pkgs: Pkg[]): string {
assertServiceRolesComplete()
const pkgsByShort = new Map(pkgs.map(pkg => [pkg.short, pkg]))
+ const maintenance = 'hybrid: services are discovered from Cordis declarations; interface/implementation/consumer roles are classified in `scripts/gen-doc-graphs.ts` with a completeness guard'
const nodes = new Map()
const edges = new Set()
const companionEdges = new Set()
@@ -312,7 +323,7 @@ function renderCapabilitySeams(pkgs: Pkg[]): string {
if (!nodes.has(id)) nodes.set(id, ` ${id}["${escLabel(label)}"]`)
}
const addEdge = (from: string, to: string): void => { edges.add(` ${from} --> ${to}`) }
- const lines = generatedHeader('Capability Seams And Core Services', 'hybrid: services are discovered from Cordis declarations; interface/implementation/consumer roles are classified in `scripts/gen-doc-graphs.ts` with a completeness guard')
+ const lines = generatedHeader('Capability Seams And Core Services')
lines.push(
'A service can be a core spine service, a swappable capability seam, or a bundle/composition point. The graph shows the package that owns the service declaration, known implementation packages, and packages that consume the service directly.',
'',
@@ -343,7 +354,7 @@ function renderCapabilitySeams(pkgs: Pkg[]): string {
for (const role of SERVICE_ROLES) {
lines.push(`| \`ctx.${role.key}\` | \`${role.mode}\` | ${pkgLink(pkgsByShort.get(role.pkg), role.pkg)} | ${pkgList(role.implementations, pkgsByShort)} | ${pkgList(role.consumers, pkgsByShort)} | ${pkgList(role.companions, pkgsByShort)} | ${tableCell(role.note)} |`)
}
- lines.push('')
+ lines.push('', ...maintenanceFooter(maintenance))
return lines.join('\n')
}
@@ -375,7 +386,7 @@ function stripYamlScalar(value: string): string {
const APP_EXAMPLES = [
{
id: 'echo',
- rel: 'docs/echo-agent-composition.md',
+ rel: 'examples/echo-agent/composition.md',
title: 'Echo Agent App Composition',
label: 'examples/echo-agent',
config: 'examples/echo-agent/cordis.yml',
@@ -383,7 +394,7 @@ const APP_EXAMPLES = [
},
{
id: 'coding',
- rel: 'docs/coding-agent-composition.md',
+ rel: 'examples/coding-agent/composition.md',
title: 'Coding Agent App Composition',
label: 'examples/coding-agent',
config: 'examples/coding-agent/cordis.yml',
@@ -391,7 +402,7 @@ const APP_EXAMPLES = [
},
{
id: 'acp',
- rel: 'docs/acp-agent-composition.md',
+ rel: 'examples/acp-agent/composition.md',
title: 'ACP Agent App Composition',
label: 'examples/acp-agent',
config: 'examples/acp-agent/cordis.yml',
@@ -421,7 +432,8 @@ function renderAppExpansion(lines: string[], appNode: string, pluginName: string
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')
+ const maintenance = 'hybrid: the leaf plugin list is parsed from its `cordis.yml`; app package expansion is curated from package source'
+ const lines = generatedHeader(example.title)
lines.push(
example.summary,
'',
@@ -444,9 +456,9 @@ function renderAppComposition(example: AppExample): string {
'| --- | --- |',
...plugins.map(plugin => `| \`${plugin.id}\` | \`${plugin.name}\` |`),
'',
- `Source config: ${repoLink(example.config, `\`${example.config}\``, '..')}.`,
- '',
+ `Source config: [\`${example.config}\`](${linkFromDoc(example.rel, example.config)}).`,
)
+ lines.push('', ...maintenanceFooter(maintenance))
return lines.join('\n')
}
@@ -528,7 +540,8 @@ function renderEventRelations(pkgs: Pkg[]): string {
const events = collectEvents()
const relations = collectEventRelations()
const pkgsByShort = new Map(pkgs.map(pkg => [pkg.short, pkg]))
- const lines = generatedHeader('Event Producer And Consumer Matrix', 'hybrid generated: Cordis event declarations and most producer/listener edges are AST-scanned; dynamic dispatch sites are classified in `scripts/gen-doc-graphs.ts`')
+ const maintenance = 'hybrid generated: Cordis event declarations and most producer/listener edges are AST-scanned; dynamic dispatch sites are classified in `scripts/gen-doc-graphs.ts`'
+ const lines = generatedHeader('Event Producer And Consumer Matrix')
lines.push(
'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.',
'',
@@ -549,13 +562,14 @@ function renderEventRelations(pkgs: Pkg[]): string {
lines.push(`| \`${event}\` | ${relationPackages(relation.dispatchers, pkgsByShort)} | ${listenerPackages(relation.listeners, pkgsByShort)} |`)
}
}
- lines.push('')
+ lines.push('', ...maintenanceFooter(maintenance))
return lines.join('\n')
}
function renderLifecycle(): string {
+ const maintenance = 'curated Mermaid sequence; exact event signatures live in the generated Cordis catalog'
return [
- ...generatedHeader('Agent Turn And Step Lifecycle', 'curated Mermaid sequence; exact event signatures live in the generated Cordis catalog'),
+ ...generatedHeader('Agent Turn And Step Lifecycle'),
'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',
@@ -599,12 +613,14 @@ function renderLifecycle(): string {
'',
'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.',
'',
+ ...maintenanceFooter(maintenance),
].join('\n')
}
function renderToolPipeline(): string {
+ const maintenance = 'curated Mermaid flow; exact tool schemas and event signatures live in generated catalogs'
return [
- ...generatedHeader('Tool Execution Pipeline', 'curated Mermaid flow; exact tool schemas and event signatures live in generated catalogs'),
+ ...generatedHeader('Tool Execution Pipeline'),
'This graph shows where policy, hooks, sandboxing, filesystem guards, result rewriting, and UI rendering fit without changing the loop. The key extension points are the `tools/pre-execute` and `tools/post-execute` waterfalls.',
'',
'```mermaid',
@@ -638,12 +654,14 @@ function renderToolPipeline(): string {
'',
'Filesystem read-before-edit checks live below `tool-fs` on the `fs/*` event gate, while hook bridges and future permission prompts live on the generic tool waterfalls. That split lets the same hooks observe bash, fs, web, todo, and subagent calls without coupling those tools to one policy service.',
'',
+ ...maintenanceFooter(maintenance),
].join('\n')
}
function renderSnapshotReplay(): string {
+ const maintenance = 'curated Mermaid sequence based on the snapshot test harness'
return [
- ...generatedHeader('ACP Snapshot Replay', 'curated Mermaid sequence based on the snapshot test harness'),
+ ...generatedHeader('ACP Snapshot Replay'),
'This graph explains what a snapshot scenario proves: recorded real-model session logs are replayed keylessly, ACP stdout is normalized and diffed, and scenario workspaces preserve tool side effects that the UI stream alone cannot prove.',
'',
'```mermaid',
@@ -665,6 +683,7 @@ function renderSnapshotReplay(): string {
'',
'The fs and hook snapshot matrix is valuable because it proves world state, hook decisions, and failed tool-card rendering, not just that replay returns text.',
'',
+ ...maintenanceFooter(maintenance),
].join('\n')
}
@@ -685,9 +704,9 @@ function renderDocs(): GraphDoc[] {
function renderIndex(docs: GraphDoc[]): string {
const labels: Record = {
'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',
+ 'examples/echo-agent/composition.md': 'echo-agent app composition',
+ 'examples/coding-agent/composition.md': 'coding-agent app composition',
+ 'examples/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',
@@ -695,9 +714,9 @@ function renderIndex(docs: GraphDoc[]): string {
}
const modes: Record = {
'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',
+ 'examples/echo-agent/composition.md': 'hybrid generated',
+ 'examples/coding-agent/composition.md': 'hybrid generated',
+ 'examples/acp-agent/composition.md': 'hybrid generated',
'docs/event-producer-consumer.md': 'hybrid generated',
'docs/agent-lifecycle.md': 'curated',
'docs/tool-execution-pipeline.md': 'curated',
@@ -707,12 +726,13 @@ function renderIndex(docs: GraphDoc[]): string {
'| [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\//, '')
+ const link = graphIndexLink(doc.rel)
return `| [${labels[doc.rel] ?? link}](${link}) | \`${modes[doc.rel] ?? 'generated'}\` |`
}),
]
+ const maintenance = 'mixed: each linked page declares generated, hybrid, or curated mode'
return [
- ...generatedHeader('Documentation Graph Index', 'mixed: each linked page declares generated, hybrid, or curated mode'),
+ ...generatedHeader('Documentation Graph Index'),
'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 the generated [events](cordis-catalog/events.md) / [services](cordis-catalog/services.md) catalogs, [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).',
@@ -723,6 +743,7 @@ function renderIndex(docs: GraphDoc[]): string {
'',
'Regenerate with `pnpm run gen-doc-graphs`; verify freshness with `pnpm run verify-doc-graphs`.',
'',
+ ...maintenanceFooter(maintenance),
].join('\n')
}