Merge commit 'refs/codex/unblock/master-current' into HEAD

# Conflicts:
#	docs/config-catalog.md
#	packages/session-persistence/session-persistence-jsonl/README.md
#	packages/session-persistence/session-persistence-jsonl/src/index.ts
#	packages/session-persistence/session-persistence-jsonl/tests/jsonl.spec.ts
This commit is contained in:
Tianyi Cui
2026-07-21 20:28:30 +08:00
1144 changed files with 42128 additions and 8470 deletions

View File

@@ -1,12 +1,12 @@
# DeepSeek Harness Architecture
The **DeepSeek Harness SDK** builds agent harnesses on Cordis. The principle is simple: **everything is a plugin**. The shipped loop is one plugin, not a privileged kernel.
The **DeepSeek Harness SDK** builds on Cordis: **everything is a plugin**, including the shipped loop.
## Overview
A harness is one [Cordis](cordis-primer.md) context. Packages add services (`ctx.llm`, `ctx.tools`, `ctx.sessions`), typed events (`agent/request`, `tools/pre-execute`, `session/event`), and disposable prompt, tool, provider, adapter, and listener registrations.
Harnesses are [Cordis](cordis-primer.md) contexts whose packages contribute disposable services, events, and registrations.
`packages/core/` groups the default agent flow; surrounding capabilities are equally first-class Cordis plugins.
`packages/core/` groups the default agent flow.
### Default Services
@@ -30,14 +30,17 @@ A harness is one [Cordis](cordis-primer.md) context. Packages add services (`ctx
| `ctx.sandboxPolicy` | [`sandbox/`](../packages/sandbox/README.md) | shared sandbox policy home |
| `ctx.codeRuntime` | [`code-runtime/`](../packages/code-runtime/README.md) | model-written program execution |
| `ctx.fs` | [`fs/`](../packages/fs/README.md) | filesystem provider primitives and policy events |
| `ctx.lsp` | [`lsp/`](../packages/lsp/README.md) | semantic navigation registry |
| `ctx.skills` | [`skill/`](../packages/skill/README.md) | skill provider registry and progressive disclosure |
| `ctx.web` | [`web/`](../packages/web/README.md) | search/fetch provider registries |
| `ctx.compact`, `ctx.toolResultPrune` | [`compact/`](../packages/compact/README.md)/[`compact-tool-result-prune`](../packages/compact/compact-tool-result-prune/README.md) | summary compaction; optional model-free result pruning |
| `ctx.subagents` | [`subagent/`](../packages/subagent/README.md) | named delegation providers |
| `ctx.tasks` | [`tasks/`](../packages/tasks/README.md) | background task registry + generic `task_*` control tools |
| `ctx.workflows` | [`workflow/`](../packages/workflow/README.md) | script-driven multi-agent orchestration |
| `ctx.goals` | [`goal/`](../packages/goal/README.md) | persisted same-session goals |
| `ctx.sessionPersistence` | [`session-persistence/`](../packages/session-persistence/README.md) | durable storage for session logs |
| `ctx.sessionQuery` | [`session-query/`](../packages/session-query/README.md) | live-preferred logical-corpus exact reads and relationship traces |
| `ctx.invariants` | [`support/invariants`](../packages/support/invariants/README.md) | registry and package-name selection for package-owned runtime checks |
## Event
@@ -55,11 +58,11 @@ Waterfall events behave like around-middleware: a listener delegates by calling
## Default Loop Lifecycle
The shipped loop drains work from prompt through checkpoint. Every pause is a service call or event available to plugins.
The shipped loop drains prompt-to-checkpoint work through plugin-visible services and events.
A **session** is an append-only event log. A **turn** drains queued input until the model stops asking for tools and no plugin requests continuation. A **step** is one model request plus the tool executions caused by that response. In the flow below ([sequence companion](agent-lifecycle.md)), quoted names are durable session events and event names are extension points.
A **session** is an append-only log. Each ordinary **turn** claims one queued `send()` item; injection claims none. A claimed `send()` successor awaits the preceding claimed ordinary turn's checkpoint but may share its `running` interval ([decision](../.agents/notes/implemented/simplification/2026-07-17-one-send-one-turn.md)). A turn ends when model and plugins stop it. A **step** is one model request plus tools. Below ([sequence companion](agent-lifecycle.md)), quotes mark durable events; other names are extension points.
Startup resolves identity. No id mints `<config-id>-session-<uuid>`; `sessionId` resumes or creates; `resumeSessionId` requires history. Active failures emit `agent-loop/config-start-failed(sessionId, error)`, so front doors reject work; teardown stays silent.
No id mints `<config-id>-session-<uuid>`; `sessionId` resumes/creates; `resumeSessionId` needs history. Resume restores lineage, seeds, and delegation depth pre-publication. Failures emit `agent-loop/config-start-failed(sessionId, error)`; front doors reject; teardown stays silent.
### Turn Flow
@@ -69,13 +72,13 @@ choose declarative identity and fresh/resume path
-> enter session + agent -> session/created -> agent/created
-> enable driving -> agent/session-start(source) -> start driver
forever:
wait for queued messages
wait for a queued message
emit agent/status(running)
TURN:
'turn/start'
each queued message -> agent/prompt-submit
claimed message -> agent/prompt-submit
allowed prompt -> 'user/message' plus injected context
every prompt blocked -> 'turn/end'(rejected)
blocked prompt -> 'prompt/blocked' -> 'turn/end'(rejected)
STEP loop:
drain steering
assemble system prompt and tool schemas
@@ -86,7 +89,7 @@ forever:
agent/request (config only) -> log request/header -> llm/stream (frozen)
on final adapter-path or terminal in-band failure:
'step/end'
agent/request-error(original error, consecutive retry attempt, signal)
agent/request-error(original error, failure facts, immutable prior failures, signal)
retry in the next numbered step or preserve the original error
otherwise:
'assistant/chunk'
@@ -109,15 +112,15 @@ forever:
Each step assembles ordered prompt sections, tool schemas, and `{{name}}` variables; unknown or valueless references fail the turn. `dsh-system-prompt` owns the harness identity and default persona, which an agent scope may shadow. The loop supplies `model` and `cwd` ([prompt ownership](../.agents/notes/implemented/architecture/2026-07-05-prompt-variables-and-tool-guidance-ownership.md)).
Tool-time context—including async `agent.inject()` notices and post-tool `additionalContexts`—settles, then follows recorded results. Steering drains before `agent/post-step`, which observes durable output, results, context, and steering before signal closure. Leftovers become queued input. Terminal `agent/turn-stop` runs after continuation and steering folding, stays authoritative through turn close and flush, and discards later steering but preserves queued prompts.
Tool-time context—including async `agent.inject()` notices and post-tool `additionalContexts`—settles after results. Steering drains; before signal closure, `agent/post-step` observes durable output, results, context, and steering. Leftovers queue. Terminal `agent/turn-stop` runs after continuation and steering folding, remains authoritative through close/flush, and discards later steering while preserving queued prompts.
Optional pruning precedes summaries; retry requires durable surface progress; cancellation wins ([decision](../.agents/notes/implemented/architecture/2026-07-10-after-call-compaction-pressure-and-overflow-recovery.md)).
Pruning precedes summaries; overflow retries require durable progress. Bounded transient retries compose on `agent/request-error`; cancellation wins ([compaction](../.agents/notes/implemented/architecture/2026-07-10-after-call-compaction-pressure-and-overflow-recovery.md), [retry](../.agents/notes/implemented/architecture/2026-06-21-bounded-llm-request-recovery.md)).
### Failure Boundaries
The turn is the containment boundary. Final adapter-path and terminal in-band failures close the step before `agent/request-error`; retry opens a numbered step; otherwise, the provider error survives. Attempts reset on success.
The turn is the containment boundary. Adapter failures close the step, entering `agent/request-error` with the exact `Error`, `LlmFailure`, and retry history. Retry opens a numbered step; success clears history; exhaustion stores the failure on `turn/end`. Failed chunks commit no message or tool.
Other failures use `agent/error`. Cancellation and disposal beat recovery; undispatched model tool calls receive synthetic `tool/call` and `ABORTED` result pairs before `turn/end`. `cancel()` clears queues and aborts active work; disposal awaits quiescence before unregistering.
Other failures use `agent/error`. Cancellation beats recovery; undispatched calls get synthetic `ABORTED` results. Effective `cancel()` emits `agent/cancel-requested` before queue clearing or abort; observers cannot veto it, and idle calls emit nothing. Disposal awaits quiescence.
Every session event is turn-enclosed. Reloading preserves an interrupted tail and closes it with a synthetic `interrupted` turn end. Failures after durable turn close report only through `agent/error` because no safe in-turn position remains. Each turn has one `TurnEndReason`; [TurnEndReasonMap](core-data-structures/session.md#why-a-turn-ended-turnendreasonmap) owns the variants.
@@ -135,48 +138,50 @@ Every live agent owns a scoped `agent.ctx`. Its registrations shadow globals, re
The session log is the source of truth. `deriveMessages()` projects session events into the `Message[]` sent to the model; raw `assistant/chunk` events stay in the log for replay and UI fidelity. Replay, fork, resume, transcript rendering, telemetry, and persistence all derive from the same event stream.
**Model-visible ⟺ logged**: the log reconstructs every request — messages at `step/start` fronted by the header's session prefix, headers by folding `request/header` — and dev invariants assert this ([reconstructability](../.agents/notes/implemented/architecture/2026-07-05-reconstructable-requests.md)).
**Model-visible ⟺ logged**: the log reconstructs every request — messages at `step/start` fronted by the header's session prefix, and headers by folding `request/header` — and the package-owned `dsh-agent-loop/invariant` can assert it through `ctx.invariants` ([reconstructability](../.agents/notes/implemented/architecture/2026-07-05-reconstructable-requests.md)).
Durability is a plugin concern. Persistence backends buffer synchronous `session/event` notifications and the loop awaits a turn-end checkpoint before moving on. The `SessionPersistence` seam stores `SessionEvent` directly, with metadata in `SessionHeader`; JSONL and SQLite share one contract suite.
Durability is a plugin concern. Backends buffer synchronous `session/event` notifications; the loop awaits a turn-end checkpoint. `SessionPersistence` stores `SessionEvent` directly and metadata in `SessionHeader`; JSONL defaults to checksummed Zstandard, with SQLite under one contract.
### Model Content
Messages contain typed blocks (`text`, `reasoning`, `tool-call`, `tool-result`) derived from merge-extensible `ContentBlockMap`; the same pattern types `MessageSource`, `FinishReason`, `TurnTrigger`, and `TurnEndReason`. New block types coordinate adapters, UI bridges, compaction pricing, token metering, and persistence as one repo-wide contract; replay measurement types live in [token-meter.md](core-data-structures/token-meter.md).
Streaming uses raw chunks (`block-start` through `finish`) and `BlockAssembler`. The loop logs and assembles chunks, storing provider/model provenance plus replay state. An `LlmAdapter` implements `stream()`, registers provider routes, and may expose selector metadata; it resolves and validates model ids. Replay state reaches targets only when both routes map to one adapter instance, which owns validation and conversion. The contract lives in [llm-streaming.md](core-data-structures/llm-streaming.md).
Streaming uses raw chunks and `BlockAssembler`. One `LlmAdapter.stream()` is one provider attempt; adapters report facts, while recovery policy lives on `agent/request-error`. The loop logs chunks and successful provenance/replay state. Remote adapters stop stalled transport with per-read idle watchdogs. Replay state reaches targets only when routes share an adapter instance ([contract](core-data-structures/llm-streaming.md)).
## Extension And Composition
### Capability Pattern
A swappable capability usually splits into **interface / implementation / consumer**: the interface owns its `ctx` key and events, an implementation registers a backend, and a consumer exposes model behavior through tools or prompts. Bash is the reference; the [capability graph](capability-seams.md) shows every family.
A swappable capability usually splits into **interface / implementation / consumer**: service/events, a backend, and model-facing tools/prompts. Bash is the reference; the [capability graph](capability-seams.md) maps each family.
Some seams bend the template deliberately: LLM combines interface and consumer because adapters implement it; filesystem wraps provider primitives with policy; web keeps search/fetch provider registries behind one service; skills and subagents use named providers. Subagents spawn fresh, fork a completed-turn prefix, or use ACP children ([subagent.md](core-data-structures/subagent.md)).
Exceptions combine layers: LLM interface/consumer; filesystem policy; web registries; named skill/subagent providers. Subagents spawn fresh, fork a completed-turn prefix, or use ACP children ([subagent.md](core-data-structures/subagent.md)).
`dsh-workspace-context` composes baselines on `agent/session-prefix` and appends `ctx.fs`-discovered nested changes on `tools/post-execute`; its [decision](../.agents/notes/implemented/feature/2026-06-24-workspace-context.md) records isolation. `dsh-paths` owns shared paths.
### Bundles And Apps
`dsh-agent-spine-demo` bundles the default spine ([README](../packages/examples/agent-spine-demo/README.md)). `dsh-stdio-demo` selects `dsh-tui` for interactive terminals and line-oriented `dsh-stdio` for pipes; `dsh-cli-demo` runs one persisted headless turn with format-pure stdout; `dsh-acp-demo` adds stdout-pure ACP over JSON-RPC ([ui/](../packages/ui/README.md)). `dsh-jsonrpc-agent` boots external `cordis.yml`; the Python SDK supplies its default only without an explicit config channel and drives `dsh-jsonrpc` over line-delimited JSON-RPC ([Python SDK](../python/README.md)). Deployments remain thin leaves with swappable backends and optional product tools ([examples/](../examples/AGENTS.md), [runnable wirings](cookbook/extension-cookbook.md#runnable-wirings), [graph atlas](graph-atlas.md)).
`dsh-agent-spine-demo` bundles the default spine and an opt-in persisted-goal stack ([README](../packages/examples/agent-spine-demo/README.md)). `dsh-tui-demo` owns the interactive full-screen terminal and enables goals plus `/goal` by default; `dsh-cli-demo` runs one persisted headless turn with format-pure stdout; `dsh-acp-demo` adds stdout-pure ACP over JSON-RPC and enables the same goal and command stack ([ui/](../packages/ui/README.md)). `dsh-jsonrpc-agent` boots external `cordis.yml`; the Python SDK supplies its default only without an explicit config channel and drives `dsh-jsonrpc` over line-delimited JSON-RPC ([Python SDK](../python/README.md)). Deployments remain thin leaves with swappable backends and optional product tools ([examples/](../examples/AGENTS.md), [runnable wirings](cookbook/extension-cookbook.md#runnable-wirings), [graph atlas](graph-atlas.md)).
### Where New Behavior Goes
New behavior should attach to a documented extension point; changing the shipped loop requires updating this map.
New behavior attaches to a documented extension point; a loop change updates this map.
| Goal | Mechanism |
|---|---|
| Add a model provider | register an adapter on `ctx.llm` |
| Add a model-facing capability | register a tool on `ctx.tools`; schemas flow into prompt assembly |
| Add command execution | implement and register a `ctx.bash` backend |
| Add a long-running/background capability | register the work on `ctx.tasks`; the generic `task_*` tools collect/stop it |
| Add a model-facing capability | register on `ctx.tools`; schemas enter prompt assembly |
| Add shell execution | implement and register a `ctx.bash` backend |
| Add a human command | register on `ctx.commands`; adapters discover and dispatch it without a model turn |
| Add background work | register on `ctx.tasks`; generic `task_*` tools collect or stop it |
| Add filesystem access or policy | implement a `ctx.fs` provider or listen on `fs/*` policy events |
| Confine spawned processes | a `ctx.sandbox` backend; consumers wrap their argv before spawning |
| Intercept prompts, requests, model completion/failure, tool use, or continuation | listen on the relevant `agent/*` or `tools/*` event; use serial `agent/turn-stop` for a monotonic terminal stop |
| Add a session-stable request prefix outside history | compose it on `agent/session-prefix`, once per loop instance; logged on the request header |
| Intercept a request, tool, or turn | use its `agent/*` or `tools/*` event; `agent/turn-stop` is the serial terminal stop |
| Add a session-stable prefix outside history | compose `agent/session-prefix`; the request header logs it |
| Add UI or editor integration | drive `ctx.agents` and render from `session/event` |
| Add durable session state | add a `SessionEventMap` member and render/replay from the log |
| Manage a same-session objective | use `ctx.goals`; continue through `Agent` and `agent/*` |
| Fork a live session | use `ctx.sessions.fork(source, boundary?, childSessionId?)` |
| Scope a tool, prompt section, or listener to ONE agent | register it through that agent's `agent.ctx` (see Agent Scope) |
| Scope a registration to one agent | use that agent's `agent.ctx` (see Agent Scope) |
The [extension cookbook](cookbook/extension-cookbook.md) carries plugin skeletons and the feature-to-seam map; step-by-step guides cover [packages](cookbook/adding-a-package.md), [tools](cookbook/adding-a-tool.md), [LLM adapters](cookbook/adding-an-llm-adapter.md), and [vendored packages](cookbook/adding-a-vendored-package.md).

View File

@@ -26,6 +26,8 @@ flowchart LR
pkg_session_query["session-query"]
pkg_subagent_inprocess["subagent-inprocess"]
pkg_invariants["invariants"]
svc_invariants["ctx.invariants<br/>Package-owned invariant registry"]
pkg_scope["scope"]
svc_sessionPersistence["ctx.sessionPersistence<br/>Durable session persistence seam"]
pkg_session_persistence_jsonl["session-persistence-jsonl"]
pkg_session_persistence_sqlite["session-persistence-sqlite"]
@@ -47,13 +49,18 @@ flowchart LR
pkg_tool_todo["tool-todo"]
pkg_user_interaction["user-interaction"]
svc_userInteraction["ctx.userInteraction<br/>Human question/answer seam"]
pkg_stdio_demo["stdio-demo"]
pkg_tui["tui"]
pkg_commands["commands"]
svc_commands["ctx.commands<br/>Human command registry"]
pkg_skill["skill"]
svc_skills["ctx.skills<br/>Skill provider registry"]
pkg_skill_local["skill-local"]
svc_agents["ctx.agents<br/>Agent service"]
pkg_tui_demo["tui-demo"]
svc_agentLoop["ctx.agentLoop<br/>Concrete loop driver"]
pkg_agent_spine_demo["agent-spine-demo"]
pkg_goal["goal"]
svc_goals["ctx.goals<br/>Same-session goal domain"]
pkg_bash["bash"]
svc_bash["ctx.bash<br/>Bash executor seam"]
pkg_bash_local["bash-local"]
@@ -83,6 +90,7 @@ flowchart LR
pkg_subagent_spawn["subagent-spawn"]
pkg_subagent_fork["subagent-fork"]
pkg_subagent_acp["subagent-acp"]
pkg_tool_ralph["tool-ralph"]
pkg_tasks["tasks"]
svc_tasks["ctx.tasks<br/>Background task registry"]
pkg_tool_tasks["tool-tasks"]
@@ -110,12 +118,15 @@ flowchart LR
pkg_bash_sandbox --> svc_bash
pkg_code_runtime --> svc_codeRuntime
pkg_code_runtime_worker --> svc_codeRuntime
pkg_commands --> svc_commands
pkg_compact --> svc_compact
pkg_compact_basic --> svc_compact
pkg_compact_tool_result_prune --> svc_toolResultPrune
pkg_fs --> svc_fs
pkg_fs_local --> svc_fs
pkg_fs_sandbox --> svc_fs
pkg_goal --> svc_goals
pkg_invariants --> svc_invariants
pkg_llm --> svc_llm
pkg_llm_deepseek --> svc_llm
pkg_llm_pi_ai --> svc_llm
@@ -133,7 +144,6 @@ flowchart LR
pkg_skill_local --> svc_skills
pkg_spill --> svc_spillStore
pkg_spill_local --> svc_spillStore
pkg_stdio_demo --> svc_userInteraction
pkg_subagent --> svc_subagents
pkg_subagent_acp --> svc_subagents
pkg_subagent_fork --> svc_subagents
@@ -143,6 +153,7 @@ flowchart LR
pkg_token_meter --> svc_tokenMeter
pkg_tool_bash --> svc_bashEnv
pkg_tools --> svc_tools
pkg_tui --> svc_userInteraction
pkg_user_interaction --> svc_userInteraction
pkg_web --> svc_web
pkg_web_fetch_local --> svc_web
@@ -155,17 +166,22 @@ flowchart LR
svc_agents --> pkg_acp
svc_agents --> pkg_agent_loop
svc_agents --> pkg_cli_demo
svc_agents --> pkg_invariants
svc_agents --> pkg_stdio_demo
svc_agents --> pkg_subagent_inprocess
svc_agents --> pkg_tui_demo
svc_approval --> pkg_tool_bash
svc_approval --> pkg_tools
svc_bash --> pkg_hooks_claude
svc_bash --> pkg_hooks_codex
svc_bash --> pkg_tool_bash
svc_codeRuntime --> pkg_tools
svc_commands --> pkg_acp
svc_commands --> pkg_tui
svc_compact --> pkg_compact_basic
svc_fs --> pkg_tool_fs
svc_invariants --> pkg_agent
svc_invariants --> pkg_agent_loop
svc_invariants --> pkg_scope
svc_invariants --> pkg_session
svc_llm --> pkg_agent_loop
svc_llm --> pkg_compact_basic
svc_permission --> pkg_acp
@@ -181,12 +197,12 @@ flowchart LR
svc_sessions --> pkg_agent
svc_sessions --> pkg_agent_loop
svc_sessions --> pkg_cli_demo
svc_sessions --> pkg_invariants
svc_sessions --> pkg_session_persistence
svc_sessions --> pkg_session_query
svc_sessions --> pkg_subagent_inprocess
svc_skills --> pkg_tool_skill
svc_spillStore --> pkg_spill_policy
svc_subagents --> pkg_tool_ralph
svc_subagents --> pkg_tool_subagent
svc_systemPrompt --> pkg_agent_loop
svc_systemPrompt --> pkg_tool_fs
@@ -208,9 +224,10 @@ flowchart LR
svc_tools --> pkg_tool_todo
svc_tools --> pkg_tool_web
svc_userInteraction --> pkg_acp
svc_userInteraction --> pkg_stdio_demo
svc_userInteraction --> pkg_tool_ask_user
svc_userInteraction --> pkg_tui
svc_web --> pkg_tool_web
svc_workflows --> pkg_tool_ralph
svc_workflows --> pkg_tool_workflow
svc_fs -. event gate .-> pkg_fs_policy
```
@@ -220,15 +237,18 @@ flowchart LR
| `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.tokenMeter` | `core` | [`token-meter`](../packages/llm/token-meter) | - | [`compact-basic`](../packages/compact/compact-basic) | - | Owns isolated per-session replay folds; pressure consumers share immutable revisioned measurements. |
| `ctx.toolResultPrune` | `core` | [`compact-tool-result-prune`](../packages/compact/compact-tool-result-prune) | - | [`compact-basic`](../packages/compact/compact-basic) | - | Rewrites oversized current tool results through replayable single-node surface replacements before summary compaction. |
| `ctx.sessions` | `core` | [`session`](../packages/core/session) | - | [`agent-loop`](../packages/core/agent-loop), [`agent`](../packages/core/agent), [`cli-demo`](../packages/examples/cli-demo), [`session-persistence`](../packages/session-persistence/session-persistence), [`session-query`](../packages/session-query/session-query), [`subagent-inprocess`](../packages/subagent/subagent-inprocess), [`invariants`](../packages/support/invariants) | - | Owns append-only Session instances and emits the durable session event feed. |
| `ctx.sessions` | `core` | [`session`](../packages/core/session) | - | [`agent-loop`](../packages/core/agent-loop), [`agent`](../packages/core/agent), [`cli-demo`](../packages/examples/cli-demo), [`session-persistence`](../packages/session-persistence/session-persistence), [`session-query`](../packages/session-query/session-query), [`subagent-inprocess`](../packages/subagent/subagent-inprocess) | - | Owns append-only Session instances and emits the durable session event feed. |
| `ctx.invariants` | `core` | [`invariants`](../packages/support/invariants) | - | [`session`](../packages/core/session), [`agent`](../packages/core/agent), [`scope`](../packages/core/scope), [`agent-loop`](../packages/core/agent-loop) | - | Companion subpaths register owner-local checks; the service owns selection, uniqueness, child fibers, and package-attributed failures. |
| `ctx.sessionPersistence` | `seam` | [`session-persistence`](../packages/session-persistence/session-persistence) | [`session-persistence-jsonl`](../packages/session-persistence/session-persistence-jsonl), [`session-persistence-sqlite`](../packages/session-persistence/session-persistence-sqlite) | [`agent-loop`](../packages/core/agent-loop), [`tool-bash`](../packages/bash/tool-bash), [`hooks-claude`](../packages/hooks/hooks-claude), [`hooks-codex`](../packages/hooks/hooks-codex), [`acp`](../packages/ui/acp), [`session-query`](../packages/session-query/session-query) | - | Backends persist the same SessionEvent vocabulary; apps choose a backend at composition time. |
| `ctx.sessionQuery` | `seam` | [`session-query`](../packages/session-query/session-query) | - | - | - | Resolves live and optional persisted logs into one logical corpus for exact reads and relationship traces. |
| `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-ask-user`](../packages/ui/tool-ask-user), [`tool-bash`](../packages/bash/tool-bash), [`tool-cordis`](../packages/cordis/tool-cordis), [`tool-fs`](../packages/fs/tool-fs), [`tool-skill`](../packages/skill/tool-skill), [`tool-subagent`](../packages/subagent/tool-subagent), [`tool-todo`](../packages/todo/tool-todo), [`tool-web`](../packages/web/tool-web), [`acp`](../packages/ui/acp) | - | Registers capabilities, owns Code Mode transport, and routes calls through pre-policy, monotonic guards, around dispatch, post-policy, and final-result observation. |
| `ctx.userInteraction` | `seam` | [`user-interaction`](../packages/ui/user-interaction) | [`stdio-demo`](../packages/examples/stdio-demo), [`acp`](../packages/ui/acp) | [`tool-ask-user`](../packages/ui/tool-ask-user), [`stdio-demo`](../packages/examples/stdio-demo), [`acp`](../packages/ui/acp) | - | UI front doors provide the active human-answer provider; tool-ask-user pauses a tool call on the provider-neutral ask() promise. |
| `ctx.userInteraction` | `seam` | [`user-interaction`](../packages/ui/user-interaction) | [`tui`](../packages/ui/tui), [`acp`](../packages/ui/acp) | [`tool-ask-user`](../packages/ui/tool-ask-user), [`tui`](../packages/ui/tui), [`acp`](../packages/ui/acp) | - | UI front doors provide the active human-answer provider; tool-ask-user pauses a tool call on the provider-neutral ask() promise. |
| `ctx.commands` | `core` | [`commands`](../packages/ui/commands) | - | [`tui`](../packages/ui/tui), [`acp`](../packages/ui/acp) | - | Plugins register direct human commands; TUI and ACP consume the same effective per-agent catalog without sending invocations to the model. |
| `ctx.skills` | `seam` | [`skill`](../packages/skill/skill) | [`skill-local`](../packages/skill/skill-local) | [`tool-skill`](../packages/skill/tool-skill) | - | Merges provider skill catalogs; tool-skill renders the session-prefix catalog and loads complete skill bodies. |
| `ctx.agents` | `core` | [`agent`](../packages/core/agent) | - | [`agent-loop`](../packages/core/agent-loop), [`acp`](../packages/ui/acp), [`cli-demo`](../packages/examples/cli-demo), [`subagent-inprocess`](../packages/subagent/subagent-inprocess), [`stdio-demo`](../packages/examples/stdio-demo), [`invariants`](../packages/support/invariants) | - | Owns live Agent handles, the create/resume factory seam, and process-local initiator propagation. |
| `ctx.agents` | `core` | [`agent`](../packages/core/agent) | - | [`agent-loop`](../packages/core/agent-loop), [`acp`](../packages/ui/acp), [`cli-demo`](../packages/examples/cli-demo), [`subagent-inprocess`](../packages/subagent/subagent-inprocess), [`tui-demo`](../packages/examples/tui-demo) | - | Owns live Agent handles, the create/resume factory seam, and process-local initiator propagation. |
| `ctx.agentLoop` | `bundle` | [`agent-loop`](../packages/core/agent-loop) | - | [`agent-spine-demo`](../packages/examples/agent-spine-demo) | - | The one concrete loop plugin; extension packages depend on dsh-agent events and services, not on this package. |
| `ctx.goals` | `core` | [`goal`](../packages/goal/goal) | - | - | - | Folds revisioned objective state from the session log and keeps live continuation activation process-local. |
| `ctx.bash` | `seam` | [`bash`](../packages/bash/bash) | [`bash-local`](../packages/bash/bash-local), [`bash-sandbox`](../packages/bash/bash-sandbox) | [`tool-bash`](../packages/bash/tool-bash), [`hooks-claude`](../packages/hooks/hooks-claude), [`hooks-codex`](../packages/hooks/hooks-codex) | - | The model-facing bash tools and hook bridges consume this seam; sandboxed or remote executors replace bash-local without touching them. |
| `ctx.bashEnv` | `core` | [`tool-bash`](../packages/bash/tool-bash) | - | - | - | Plugins declare effect-scoped DSH_* facts; tool-bash collects one trusted snapshot per execution and the executor rebuilds the namespace. |
| `ctx.sandbox` | `seam` | [`sandbox`](../packages/sandbox/sandbox) | [`sandbox-local`](../packages/sandbox/sandbox-local) | [`bash-sandbox`](../packages/bash/bash-sandbox) | - | Consumers hand over the exact argv they are about to spawn; same-world backends wrap it under a per-call policy and report enforcement. |
@@ -238,10 +258,10 @@ flowchart LR
| `ctx.codeRuntime` | `seam` | [`code-runtime`](../packages/code-runtime/code-runtime) | [`code-runtime-worker`](../packages/code-runtime/code-runtime-worker) | [`tools`](../packages/core/tools) | - | Runs one model-written program against host-provided async bindings; backends differ by substrate and language (the tool registry consumes it for Code Mode). |
| `ctx.fs` | `seam` | [`fs`](../packages/fs/fs) | [`fs-local`](../packages/fs/fs-local), [`fs-sandbox`](../packages/fs/fs-sandbox) | [`tool-fs`](../packages/fs/tool-fs) | [`fs-policy`](../packages/fs/fs-policy) | tool-fs executes read/write/edit through ctx.fs; fs-sandbox fences mutations by the shared sandbox mode; fs-policy contributes observed-state checks through the fs/* event gate. |
| `ctx.compact` | `seam` | [`compact`](../packages/compact/compact) | [`compact-basic`](../packages/compact/compact-basic) | [`compact-basic`](../packages/compact/compact-basic) | - | The basic backend consumes post-step pressure and request-error recovery events; a model-facing compact tool remains deferred. |
| `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) | [`tool-subagent`](../packages/subagent/tool-subagent) | - | Providers implement transports; tool-subagent exposes one configured provider as a model-facing tool name. |
| `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) | [`tool-subagent`](../packages/subagent/tool-subagent), [`tool-ralph`](../packages/workflow/tool-ralph) | - | Providers implement transports; tool-subagent exposes configured delegation while tool-ralph requires one fresh structured-output route. |
| `ctx.tasks` | `core` | [`tasks`](../packages/tasks/tasks) | - | [`tool-bash`](../packages/bash/tool-bash), [`tool-subagent`](../packages/subagent/tool-subagent), [`tool-tasks`](../packages/tasks/tool-tasks) | - | Producers (tool-bash background commands, tool-subagent background delegations) register running work; tool-tasks is the model-facing control surface that reads, lists, and kills it. |
| `ctx.web` | `seam` | [`web`](../packages/web/web) | [`web-search-exa`](../packages/web/web-search-exa), [`web-search-perplexity`](../packages/web/web-search-perplexity), [`web-search-deepseek`](../packages/web/web-search-deepseek), [`web-fetch-local`](../packages/web/web-fetch-local) | [`tool-web`](../packages/web/tool-web) | - | Search and fetch providers register into one ctx.web seam; tool-web owns the stable model-facing names. |
| `ctx.spillStore` | `seam` | [`spill`](../packages/spill/spill) | [`spill-local`](../packages/spill/spill-local) | [`spill-policy`](../packages/spill/spill-policy) | - | The backend saves oversized tool text and returns a model-facing locator plus retrieval hint; spill-policy is the tools/post-execute consumer that decides when to spill. |
| `ctx.workflows` | `seam` | [`workflow`](../packages/workflow/workflow) | [`workflow-workerthread`](../packages/workflow/workflow-workerthread) | [`tool-workflow`](../packages/workflow/tool-workflow) | - | One engine per context (bash shape, no named-provider registry); the worker-thread engine fans agent() calls out through ctx.subagents. |
| `ctx.workflows` | `seam` | [`workflow`](../packages/workflow/workflow) | [`workflow-workerthread`](../packages/workflow/workflow-workerthread) | [`tool-workflow`](../packages/workflow/tool-workflow), [`tool-ralph`](../packages/workflow/tool-ralph) | - | One engine per context (bash shape, no named-provider registry); the general workflow and fixed Ralph consumers start runs whose agent() calls fan out through ctx.subagents. |
Maintenance mode: hybrid: services are discovered from Cordis declarations; interface/implementation/consumer roles are classified in `scripts/gen-doc-graphs.ts` with a completeness guard.

View File

@@ -11,7 +11,7 @@ A `Requires:` line lists the service keys the plugin `inject`s: its `cordis.yml`
## `@deepseek-ai/dsh-acp`
Requires: `agents` · `sessionPersistence` · `tools` · `userInteraction` · `llm` · `systemPrompt`
Requires: `agents` · `commands` · `sessionPersistence` · `tools` · `userInteraction` · `llm` · `systemPrompt`
```ts config-catalog
/** Plugin config: the agent template ACP sessions are created from. */
@@ -27,7 +27,7 @@ export interface AcpConfig {
Depends on: `Stream` (`@agentclientprotocol/sdk`)
Source: [`packages/ui/acp/src/index.ts:206`](../packages/ui/acp/src/index.ts)
Source: [`packages/ui/acp/src/index.ts:252`](../packages/ui/acp/src/index.ts)
## `@deepseek-ai/dsh-acp-demo`
@@ -58,6 +58,8 @@ export interface Config {
dshHome?: string
/** Directory the JSONL session backend writes under. Defaults to `./.sessions`. */
persistenceRoot?: string
/** JSONL artifact encoding; defaults to checksummed Zstandard frames. */
persistenceCompression?: JsonlCompression
/** Controls automatic AGENTS.md/CLAUDE.md loading; configure a byte budget or set `false`. */
workspaceContext: agentCore.Config['workspaceContext']
/** Skill registry, local-provider, and model-facing consumer config forwarded to agent-spine-demo. */
@@ -66,12 +68,16 @@ export interface Config {
toolBash?: NonNullable<agentCore.Config['toolBash']>
/** Generic background-task controls forwarded through agent-core; set false to omit their tool surface. */
toolTasks?: NonNullable<agentCore.Config['toolTasks']>
/** Persisted same-session goals; owner defaults enable them, or false disables the stack and command. */
goals?: agentCore.GoalConfig | false
/** Bounded transient model-request retry policy forwarded through agent-core. */
llmRetry?: NonNullable<agentCore.Config['llmRetry']>
}
```
Depends on: [`agentCore`](../packages/examples/agent-spine-demo/src/index.ts) · [`ToolsConfig`](#deepseek-aidsh-tools)
Depends on: [`agentCore`](../packages/examples/agent-spine-demo/src/index.ts) · [`JsonlCompression`](../packages/session-persistence/session-persistence-jsonl/src/index.ts) · [`ToolsConfig`](#deepseek-aidsh-tools)
Source: [`packages/examples/acp-demo/src/index.ts:33`](../packages/examples/acp-demo/src/index.ts)
Source: [`packages/examples/acp-demo/src/index.ts:38`](../packages/examples/acp-demo/src/index.ts)
## `@deepseek-ai/dsh-agent-loop`
@@ -101,7 +107,7 @@ export interface Config {
Depends on: [`AgentOptions`](core-data-structures/core.md) · [`SessionId`](core-data-structures/core.md)
Source: [`packages/core/agent-loop/src/index.ts:369`](../packages/core/agent-loop/src/index.ts)
Source: [`packages/core/agent-loop/src/index.ts:360`](../packages/core/agent-loop/src/index.ts)
## `@deepseek-ai/dsh-agent-spine-demo`
@@ -114,8 +120,11 @@ Source: [`packages/core/agent-loop/src/index.ts:369`](../packages/core/agent-loo
* order), the `tools` object to the tool registry (its presentation `mode`),
* `dshHome` to bash environment and local skill discovery, `skills` to the
* skill registry/local provider/tool consumer, `workspaceContext` to the
* workspace-context loader, and `toolBash`/`toolTasks` to the model-facing tool
* plugins this bundle owns. Owner schemas supply defaults for optional input;
* workspace-context loader, `llmRetry` to the bounded request-recovery policy,
* and `toolBash`/`toolTasks` to the model-facing tool plugins this bundle owns.
* `goals` opts into and configures the persisted goal domain plus its model tool
* and same-session driver; `invariants` configures global and package-filtered
* relational checks. Owner schemas supply defaults for optional input;
* workspace context instead requires an explicit byte budget or `false` because
* it changes model-visible input. Producer opt-in stays producer-local:
* `toolBash` configures bash only; independently composed producers keep their
@@ -142,6 +151,12 @@ export interface Config {
toolBash?: toolBash.Config
/** Generic background-task controls; set false to keep the task service without model-facing task tools. */
toolTasks?: toolTasks.Config | false
/** Global enablement and package-name filters for invariant companions. */
invariants?: InvariantConfig
/** Opt-in persisted same-session goal stack; set false or omit to leave it unmounted. */
goals?: GoalConfig | false
/** Bounded transient model-request retry policy. */
llmRetry?: llmRetry.Config
}
/** Skill bundle config forwarded to the registry, local provider, and model-facing consumer. */
@@ -155,11 +170,19 @@ export interface SkillConfig {
/** Model-facing skill catalog and tool settings. */
tool?: toolSkill.Config
}
/** Persisted goal domain, model-tool policy, and same-session driver config. */
export interface GoalConfig {
/** Goal-domain creation defaults. */
domain?: GoalDomainConfig
/** Model-facing goal-tool authority policy. */
tool?: toolGoal.Config
}
```
Depends on: [`AgentLoopConfig`](#deepseek-aidsh-agent-loop) · [`SkillLocal`](../packages/skill/skill-local/src/index.ts) · [`SkillRegistryConfig`](#deepseek-aidsh-skill) · [`SystemPromptConfig`](#deepseek-aidsh-system-prompt) · [`toolBash`](../packages/bash/tool-bash/src/index.ts) · [`ToolsConfig`](#deepseek-aidsh-tools) · [`toolSkill`](../packages/skill/tool-skill/src/index.ts) · [`toolTasks`](../packages/tasks/tool-tasks/src/index.ts) · [`workspaceContext`](../packages/context/workspace-context/src/index.ts)
Depends on: [`AgentLoopConfig`](#deepseek-aidsh-agent-loop) · [`GoalDomainConfig`](#deepseek-aidsh-goal) · [`InvariantConfig`](#deepseek-aidsh-invariants) · [`llmRetry`](../packages/llm/llm-retry/src/index.ts) · [`SkillLocal`](../packages/skill/skill-local/src/index.ts) · [`SkillRegistryConfig`](#deepseek-aidsh-skill) · [`SystemPromptConfig`](#deepseek-aidsh-system-prompt) · [`toolBash`](../packages/bash/tool-bash/src/index.ts) · [`toolGoal`](../packages/goal/tool-goal/src/index.ts) · [`ToolsConfig`](#deepseek-aidsh-tools) · [`toolSkill`](../packages/skill/tool-skill/src/index.ts) · [`toolTasks`](../packages/tasks/tool-tasks/src/index.ts) · [`workspaceContext`](../packages/context/workspace-context/src/index.ts)
Source: [`packages/examples/agent-spine-demo/src/index.ts:59`](../packages/examples/agent-spine-demo/src/index.ts)
Source: [`packages/examples/agent-spine-demo/src/index.ts:78`](../packages/examples/agent-spine-demo/src/index.ts)
## `@deepseek-ai/dsh-bash-local`
@@ -224,20 +247,24 @@ export interface Config {
dshHome?: string
/** Directory the JSONL session backend writes under. Defaults to `./.sessions`. */
persistenceRoot?: string
/** JSONL artifact encoding; defaults to checksummed Zstandard frames. */
persistenceCompression?: JsonlCompression
/** Skill registry, local-provider, and model-facing consumer config. */
skills?: agentCore.SkillConfig
/** Model-facing bash tool config forwarded through agent-spine-demo. */
toolBash?: NonNullable<agentCore.Config['toolBash']>
/** Generic background-task control-tool config forwarded through agent-spine-demo. */
toolTasks?: NonNullable<agentCore.Config['toolTasks']>
/** Bounded transient model-request retry policy forwarded through agent-spine-demo. */
llmRetry?: NonNullable<agentCore.Config['llmRetry']>
/** Controls automatic AGENTS.md/CLAUDE.md loading; configure a byte budget or set `false`. */
workspaceContext: agentCore.Config['workspaceContext']
}
```
Depends on: [`agentCore`](../packages/examples/agent-spine-demo/src/index.ts) · [`ToolsConfig`](#deepseek-aidsh-tools)
Depends on: [`agentCore`](../packages/examples/agent-spine-demo/src/index.ts) · [`JsonlCompression`](../packages/session-persistence/session-persistence-jsonl/src/index.ts) · [`ToolsConfig`](#deepseek-aidsh-tools)
Source: [`packages/examples/cli-demo/src/index.ts:22`](../packages/examples/cli-demo/src/index.ts)
Source: [`packages/examples/cli-demo/src/index.ts:25`](../packages/examples/cli-demo/src/index.ts)
## `@deepseek-ai/dsh-code-runtime-worker`
@@ -280,15 +307,25 @@ Source: [`packages/code-runtime/code-runtime-worker/src/index.ts:21`](../package
Requires: `llm` · `tokenMeter`
```ts config-catalog
/** Basic compaction configuration; every common field has a deployment default. */
export interface BasicCompactConfig {
/** Compact at this fraction of the token meter's context window. Defaults to `0.8`. */
/** Basic compaction configuration with an optional exact-target policy table. */
export interface BasicCompactConfig extends CompactPolicyConfig {
/** Exact provider/model overrides; duplicate targets fail plugin load. */
modelPolicies?: ModelCompactPolicyConfig[]
/** Enable automatic post-step pressure and overflow-recovery listeners. Defaults to `true`. */
auto?: boolean
}
/** Policy fields shared by the default policy and exact model overrides. */
export interface CompactPolicyConfig {
/** Compact at this fraction of the model's context window. Defaults to `0.8`. */
thresholdRatio?: number
/** Recent surface tokens retained verbatim. Defaults to `floor(contextWindow * 0.16)`. */
/** Recent context retained as a fraction of the model's window. Defaults to `0.16`. */
retainRatio?: number
/** Absolute recent-context budget; mutually exclusive with `retainRatio`. */
retainTokens?: number
/** Summary provider; `''` resolves the latest routed pair, then the agent pair. Defaults to `''`. */
/** Summary provider; set together with `summarizationModel`, or inherit the conversation target. */
summarizationProvider?: string
/** Summary model; `''` resolves the latest routed pair, then the agent pair. Defaults to `''`. */
/** Summary model; set together with `summarizationProvider`, or inherit the conversation target. */
summarizationModel?: string
/** Provider generation cap for summarization. Defaults to `8192`. */
maxTokens?: number
@@ -296,12 +333,18 @@ export interface BasicCompactConfig {
compactionRetries?: number
/** Maximum retries after canonical context overflow; `0` disables recovery. Defaults to `1`. */
maxOverflowRetries?: number
/** Enable automatic post-step pressure and overflow-recovery listeners. Defaults to `true`. */
auto?: boolean
}
/** Exact provider/model override merged over the default compaction policy. */
export interface ModelCompactPolicyConfig extends CompactPolicyConfig {
/** Registered provider route to match. */
provider: string
/** Exact routed model id to match within `provider`. */
model: string
}
```
Source: [`packages/compact/compact-basic/src/types.ts:8`](../packages/compact/compact-basic/src/types.ts)
Source: [`packages/compact/compact-basic/src/types.ts:38`](../packages/compact/compact-basic/src/types.ts)
## `@deepseek-ai/dsh-compact-tool-result-prune`
@@ -349,6 +392,20 @@ Depends on: [`LocalConfig`](#deepseek-aidsh-fs-local)
Source: [`packages/fs/fs-sandbox/src/index.ts:49`](../packages/fs/fs-sandbox/src/index.ts)
## `@deepseek-ai/dsh-goal`
Requires: `agents`
```ts config-catalog
/** Deployment defaults for goal creation. */
export interface Config {
/** Total rounds used when a create request omits its own cap. */
defaultMaxGoalRounds?: number
}
```
Source: [`packages/goal/goal/src/index.ts:56`](../packages/goal/goal/src/index.ts)
## `@deepseek-ai/dsh-hooks-claude`
Requires: `bash`
@@ -410,6 +467,22 @@ export interface Config {
Source: [`packages/hooks/hooks-codex/src/index.ts:42`](../packages/hooks/hooks-codex/src/index.ts)
## `@deepseek-ai/dsh-invariants`
```ts config-catalog
/** Runtime invariant selection configured on the service plugin. */
export interface Config {
/** Global switch; defaults to `true`. */
readonly enabled?: boolean
/** Case-sensitive JavaScript regex sources that admit package names; empty admits all. */
readonly package_allowlist?: string[]
/** Case-sensitive JavaScript regex sources that exclude package names after allowlist matching. */
readonly package_blocklist?: string[]
}
```
Source: [`packages/support/invariants/src/index.ts:15`](../packages/support/invariants/src/index.ts)
## `@deepseek-ai/dsh-jsonrpc`
Requires: `agents`
@@ -454,6 +527,8 @@ export interface Config {
reasoningEffort?: 'high' | 'max'
/** Advisory models shown by discovery consumers; defaults to V4 Flash and V4 Pro. */
models?: DeepSeekCatalogModel[]
/** Maximum provider idle time while one stream read is outstanding (default five minutes). */
streamIdleTimeoutMs?: number
}
/** One optional model entry advertised by the hand-written adapter. */
@@ -464,10 +539,12 @@ export interface DeepSeekCatalogModel {
name?: string
/** Optional selector detail for deployments with similar model variants. */
description?: string
/** Known combined request/response context capacity; omitted when deployment metadata is unavailable. */
contextWindow?: number
}
```
Source: [`packages/llm/llm-deepseek/src/index.ts:33`](../packages/llm/llm-deepseek/src/index.ts)
Source: [`packages/llm/llm-deepseek/src/index.ts:34`](../packages/llm/llm-deepseek/src/index.ts)
## `@deepseek-ai/dsh-llm-pi-ai`
@@ -502,16 +579,14 @@ export interface PiAiProviderProfile {
timeoutMs?: number
/** WebSocket connection timeout in milliseconds. */
websocketConnectTimeoutMs?: number
/** Provider SDK retry count. */
maxRetries?: number
/** Maximum provider-requested retry delay in milliseconds. */
maxRetryDelayMs?: number
/** Maximum provider idle time while one stream read is outstanding. */
streamIdleTimeoutMs?: number
}
```
Depends on: `CacheRetention` (`@earendil-works/pi-ai`) · `ThinkingBudgets` (`@earendil-works/pi-ai`) · `ThinkingLevel` (`@earendil-works/pi-ai`) · `Transport` (`@earendil-works/pi-ai`)
Source: [`packages/llm/llm-pi-ai/src/config.ts:40`](../packages/llm/llm-pi-ai/src/config.ts)
Source: [`packages/llm/llm-pi-ai/src/config.ts:48`](../packages/llm/llm-pi-ai/src/config.ts)
## `@deepseek-ai/dsh-llm-replay`
@@ -552,10 +627,74 @@ export interface ReplayModelConfig {
name?: string
/** Optional selector description. */
description?: string
/** Optional positive integer context capacity published by the replay adapter. */
contextWindow?: number
}
```
Source: [`packages/support/llm-replay/src/index.ts:375`](../packages/support/llm-replay/src/index.ts)
Source: [`packages/support/llm-replay/src/index.ts:385`](../packages/support/llm-replay/src/index.ts)
## `@deepseek-ai/dsh-llm-retry`
Requires: `agents`
```ts config-catalog
/** Deployment-owned limits and classification for transient request recovery. */
export interface Config {
/** Maximum transient retries after the first request (default 2). */
maxTransientRetries?: number
/** Initial local exponential-backoff delay in milliseconds (default 500). */
initialDelayMs?: number
/** Maximum accepted or locally scheduled delay in milliseconds (default 10000). */
maxDelayMs?: number
/** Symmetric random multiplier range around one (default 0.1). */
jitterRatio?: number
/** Stable failure codes eligible for this policy. */
retryableCodes?: string[]
}
```
Source: [`packages/llm/llm-retry/src/index.ts:39`](../packages/llm/llm-retry/src/index.ts)
## `@deepseek-ai/dsh-lsp-local`
Requires: `lsp`
```ts config-catalog
/** Plugin configuration: provider id → local language-server configuration. */
export interface Config {
/** Non-empty table of stable provider ids to independent local server configurations. */
servers: Record<string, LspLocalServerConfig>
}
/** One configured local language server and its host bounds. */
export interface LspLocalServerConfig {
/** Executable to spawn (absolute, or resolved on PATH at load). */
command: string
/** Lowercase leading-dot extension → LSP language id (e.g. `{ '.ts': 'typescript' }`). */
extensionToLanguage: Record<string, string>
/** Arguments passed to the executable (no shell). Default `[]`. */
args?: string[]
/** Extra env vars merged on top of the scrubbed ambient env. Default `{}`. */
env?: Record<string, string>
/** Static `initialize` options forwarded to the server. Default `null`. */
initializationOptions?: unknown
/** Static answer to every `workspace/configuration` item. Default `null`. */
configuration?: unknown
/** Largest single framed message accepted from the server (bytes). Default 16000000. */
maxMessageBytes?: number
/** Largest stderr tail retained for diagnostics (bytes). Default 1000000. */
maxStderrBytes?: number
/** Largest source file this host will open (bytes). Default 4000000. */
maxDocumentBytes?: number
/** Graceful `shutdown`/`exit` budget before escalation (ms). Default 5000. */
shutdownTimeoutMs?: number
/** Request-cancel and SIGTERM→SIGKILL grace (ms). Default 2000. */
killGraceMs?: number
}
```
Source: [`packages/lsp/lsp-local/src/index.ts:85`](../packages/lsp/lsp-local/src/index.ts)
## `@deepseek-ai/dsh-mcp-client`
@@ -740,10 +879,15 @@ export interface Config {
* first materialization.
*/
root: string
/** Physical encoding; defaults to checksummed Zstandard frames. */
compression?: JsonlCompression
}
/** Physical encoding selected for JSONL session artifacts. */
export type JsonlCompression = 'zstd' | 'none'
```
Source: [`packages/session-persistence/session-persistence-jsonl/src/index.ts:25`](../packages/session-persistence/session-persistence-jsonl/src/index.ts)
Source: [`packages/session-persistence/session-persistence-jsonl/src/index.ts:37`](../packages/session-persistence/session-persistence-jsonl/src/index.ts)
## `@deepseek-ai/dsh-session-persistence-sqlite`
@@ -862,88 +1006,6 @@ export interface Config {
Source: [`packages/spill/spill-policy/src/index.ts:45`](../packages/spill/spill-policy/src/index.ts)
## `@deepseek-ai/dsh-stdio`
Requires: `agents` · `userInteraction`
```ts config-catalog
/** Serializable plugin configuration (cordis-native, schemastery). */
export interface Config {
/** Banner printed once on start, before the first `> ` prompt. */
welcome?: string
/** Exact shared agent/session identity stdin drives. Defaults to `'main'`. */
sessionId?: string
}
```
Source: [`packages/ui/stdio/src/index.ts:33`](../packages/ui/stdio/src/index.ts)
## `@deepseek-ai/dsh-stdio-demo`
```ts config-catalog
/**
* App config: the swappable per-demo values, each routed to where the app wires
* it. `provider`/`model`/`resumeSessionId` configure the pre-created `main` agent (through
* {@link @deepseek-ai/dsh-agent-spine-demo}'s forwarded `agents` list); `persona` is
* the deployment persona (forwarded to the system-prompt plugin); `toolOrder`
* is the explicit model-facing tool order (forwarded to the system-prompt plugin);
* fresh sessions use `process.cwd()` as their workspace cwd; resumed sessions
* keep their persisted cwd. `persistenceRoot` is the JSONL backend's directory;
* `welcome` is the UI banner and `ui` configures terminal mode/presentation.
*/
export interface Config {
/** Provider route for the `main` agent. */
provider: string
/** Model name for the `main` agent (must have a registered adapter). */
model: string
/** Bundled agent-loop concurrency cap; `1` is serial and omission uses its default. */
maxParallelToolCalls?: number
/** Deployment persona (the system-prompt plugin's `persona` config). */
persona?: string
/** Explicit model-facing tool order (the system-prompt plugin's `toolOrder` config; see dsh-system-prompt). */
toolOrder?: string[]
/** Tool-registry config — its presentation `mode` (forwarded through agent-spine-demo; see dsh-tools). */
tools?: ToolsConfig
/** DeepSeek Harness home directory exposed to bash and used for local skill discovery. */
dshHome?: string
/** Directory the JSONL session backend writes under. Defaults to `./.sessions`. */
persistenceRoot?: string
/** stdin-chat banner printed once on start. Defaults to `'ready.'`. */
welcome?: string
/** Terminal front-door selection and pi-tui presentation settings. */
ui?: UiConfig
/** Skill registry, local-provider, and model-facing consumer config forwarded to agent-spine-demo. */
skills?: agentCore.SkillConfig
/** Model-facing bash tool config forwarded through agent-core. */
toolBash?: NonNullable<agentCore.Config['toolBash']>
/** Generic background-task controls forwarded through agent-core; set false to omit their tool surface. */
toolTasks?: NonNullable<agentCore.Config['toolTasks']>
/**
* If set, the pre-created agent RESUMES this persisted session id instead of
* starting fresh. Sourced from an env var in the leaf `cordis.yml`
* (`resumeSessionId: !!js process.env.RESUME_SESSION_ID`).
*/
resumeSessionId?: string
/** Controls automatic AGENTS.md/CLAUDE.md loading; configure a byte budget or set `false`. */
workspaceContext: agentCore.Config['workspaceContext']
}
/** App-level terminal selection with nested TUI presentation settings. */
export interface UiConfig {
/** Select a concrete front door or infer it from the process streams. */
mode?: TerminalMode
/** Settings forwarded only when the pi-tui front door is selected. */
tui?: uiTui.TuiConfig
}
/** Terminal front door selected by the app bundle. */
export type TerminalMode = 'auto' | 'readline' | 'tui'
```
Depends on: [`agentCore`](../packages/examples/agent-spine-demo/src/index.ts) · [`ToolsConfig`](#deepseek-aidsh-tools) · [`uiTui`](../packages/ui/tui/src/index.ts)
Source: [`packages/examples/stdio-demo/src/index.ts:75`](../packages/examples/stdio-demo/src/index.ts)
## `@deepseek-ai/dsh-subagent-acp`
Requires: `subagents`
@@ -958,8 +1020,11 @@ export interface Config {
/** Arguments passed to {@link command}. */
args: string[]
/**
* Working directory for the child process and its ACP session. Defaults to
* the parent process's cwd when omitted.
* Working directory override for the child process and its ACP session.
* Must be non-empty; a relative path resolves against the harness launch
* directory at load, and the result must be an existing directory. When
* omitted, each child inherits its delegating parent session's cwd — and
* starting one from a parent session that has no cwd fails.
*/
cwd?: string
/**
@@ -989,7 +1054,7 @@ export interface Config {
export type PermissionPolicy = 'allow' | 'reject'
```
Source: [`packages/subagent/subagent-acp/src/index.ts:18`](../packages/subagent/subagent-acp/src/index.ts)
Source: [`packages/subagent/subagent-acp/src/index.ts:21`](../packages/subagent/subagent-acp/src/index.ts)
## `@deepseek-ai/dsh-subagent-fork`
@@ -1059,11 +1124,8 @@ Source: [`packages/context/time-context/src/index.ts:19`](../packages/context/ti
## `@deepseek-ai/dsh-token-meter`
```ts config-catalog
/** Token-meter plugin configuration. */
export interface TokenMeterConfig {
/** Service-wide context-window capacity in tokens. Defaults to `128000`. */
contextWindow?: number
}
/** Token-meter plugin configuration; the fixed estimator has no settings. */
export type TokenMeterConfig = Record<string, never>
```
Source: [`packages/llm/token-meter/src/types.ts:10`](../packages/llm/token-meter/src/types.ts)
@@ -1144,6 +1206,58 @@ export interface Config {
Source: [`packages/fs/tool-fs-search/src/index.ts:62`](../packages/fs/tool-fs-search/src/index.ts)
## `@deepseek-ai/dsh-tool-goal`
Requires: `agents` · `goals` · `tools` · `systemPrompt`
```ts config-catalog
/** Model policy and hard lower bounds for goal-state updates. */
export interface Config {
/** Minimum admitted goal rounds before the model may self-report `blocked`. */
blockedAfterConsecutiveRounds?: number
}
```
Source: [`packages/goal/tool-goal/src/index.ts:27`](../packages/goal/tool-goal/src/index.ts)
## `@deepseek-ai/dsh-tool-lsp`
Requires: `tools` · `lsp` · `systemPrompt`
```ts config-catalog
/** Plugin configuration: result caps and the timeout budget. */
export interface Config {
/** Largest number of rendered locations before an omission marker (default 100). */
maxLocations?: number
/** Largest complete rendered result in characters, including truncation metadata (default 16000). */
maxResultChars?: number
/** Tool-call timeout budget in ms (default 60000). */
timeoutMs?: number
}
```
Source: [`packages/lsp/tool-lsp/src/index.ts:58`](../packages/lsp/tool-lsp/src/index.ts)
## `@deepseek-ai/dsh-tool-ralph`
Requires: `tools` · `workflows` · `subagents` · `systemPrompt`
```ts config-catalog
/** Deployment policy for the fixed Ralph workflow. */
export interface Config {
/** Fresh structured-output provider used for every round (default `spawn`). */
subagentProvider?: string
/** Default and deployment ceiling for one call's round count (default 256). */
maxRounds?: number
/** Maximum serialized characters in one structured handoff (default 16384). */
maxHandoffChars?: number
/** Maximum characters in a successful parent-facing terminal text (default 16384). */
maxResultChars?: number
}
```
Source: [`packages/workflow/tool-ralph/src/index.ts:22`](../packages/workflow/tool-ralph/src/index.ts)
## `@deepseek-ai/dsh-tool-skill`
Requires: `tools` · `skills`
@@ -1189,8 +1303,7 @@ export interface Config {
/**
* Tool filter applied to every child. Filtered tools disappear from its
* prompt and reject execution. Requires the provider's `toolFilter`
* capability; unknown names fail startup. Children otherwise see this tool,
* so deny it or set `maxDepth` to bound recursion.
* capability; unknown names fail startup.
*/
toolFilter?: {
/** Global tool names the child keeps; everything else is removed. */
@@ -1199,10 +1312,15 @@ export interface Config {
deny?: string[]
}
/**
* Maximum child depth. Requires the provider's `depthLimit` capability and a
* non-negative safe integer. Omission is unbounded.
* Maximum child depth: a non-negative safe integer (default `3`; `0` forbids
* delegation entirely), or `'provider-managed'` to send no cap. A numeric cap
* requires the provider's `depthLimit` capability (mount fails loud
* otherwise). The provider checks the calling agent's current depth at every
* start; the tool remains model-visible so runtime policy owns rejection.
* `'provider-managed'` is for an out-of-process provider (ACP) whose
* recursion budget belongs to the child harness's own deployment.
*/
maxDepth?: number
maxDepth?: number | 'provider-managed'
}
```
@@ -1288,7 +1406,7 @@ Source: [`packages/core/tools/src/index.ts:382`](../packages/core/tools/src/inde
## `@deepseek-ai/dsh-tui`
Requires: `agents` · `userInteraction` · `tools`
Requires: `agents` · `commands` · `userInteraction` · `tools` · `llm` · `systemPrompt` · `tokenMeter`
```ts config-catalog
/** Serializable plugin configuration. */
@@ -1303,14 +1421,20 @@ export interface Config extends TuiConfig {
export interface TuiConfig {
/** Render model reasoning blocks. */
showReasoning?: boolean
/** Maximum tool-output lines shown before the card is collapsed. */
/** Maximum tool-card body lines retained in its collapsed head/tail preview. */
maxToolOutputLines?: number
/** Maximum options visible at once in a user-question dialog. */
/** Maximum options visible at once in a user-question panel. */
maxQuestionOptions?: number
/** User-question dialog width in terminal columns. */
/** Maximum models visible at once in the model selector. */
maxModelOptions?: number
/** User-question panel width in terminal columns, clamped to the terminal. */
questionDialogWidth?: number
/** User-question dialog maximum height in terminal rows. */
/** User-question panel maximum height in terminal rows. */
questionDialogMaxHeight?: number
/** Model-selector width in terminal columns. */
modelDialogWidth?: number
/** Model-selector maximum height in terminal rows. */
modelDialogMaxHeight?: number
/** Show the terminal's hardware cursor at the pi editor's IME marker. */
showHardwareCursor?: boolean
/** Apply the built-in ANSI color palette. */
@@ -1320,7 +1444,53 @@ export interface TuiConfig {
}
```
Source: [`packages/ui/tui/src/index.ts:100`](../packages/ui/tui/src/index.ts)
Source: [`packages/ui/tui/src/index.ts:127`](../packages/ui/tui/src/index.ts)
## `@deepseek-ai/dsh-tui-demo`
```ts config-catalog
/** App config routed to the spine, TUI, configured agent, and JSONL backend. */
export interface Config {
/** Provider route for the `main` agent. */
provider: string
/** Model name for the `main` agent; a matching adapter must be registered. */
model: string
/** Bundled agent-loop concurrency cap; `1` is serial and omission uses its default. */
maxParallelToolCalls?: number
/** Deployment persona forwarded to the system-prompt plugin. */
persona?: string
/** Explicit model-facing tool order forwarded to the system-prompt plugin. */
toolOrder?: string[]
/** Tool-registry presentation config forwarded through agent-spine-demo. */
tools?: ToolsConfig
/** DeepSeek Harness home directory exposed to bash and used for local skill discovery. */
dshHome?: string
/** Directory the JSONL session backend writes under. Defaults to `./.sessions`. */
persistenceRoot?: string
/** JSONL artifact encoding; defaults to checksummed Zstandard frames. */
persistenceCompression?: JsonlCompression
/** TUI subtitle rendered on start. Defaults to `ready.`. */
welcome?: string
/** Full-screen TUI presentation settings. */
ui?: uiTui.TuiConfig
/** Skill registry, local-provider, and model-facing consumer config. */
skills?: agentCore.SkillConfig
/** Model-facing bash tool config forwarded through agent-spine-demo. */
toolBash?: NonNullable<agentCore.Config['toolBash']>
/** Generic background-task controls forwarded through agent-spine-demo; set false to omit them. */
toolTasks?: NonNullable<agentCore.Config['toolTasks']>
/** Persisted same-session goals; owner defaults enable them, or false disables the stack and command. */
goals?: agentCore.GoalConfig | false
/** Persisted session id to resume instead of creating a fresh session. */
resumeSessionId?: string
/** Controls automatic AGENTS.md/CLAUDE.md loading; configure a byte budget or set `false`. */
workspaceContext: agentCore.Config['workspaceContext']
}
```
Depends on: [`agentCore`](../packages/examples/agent-spine-demo/src/index.ts) · [`JsonlCompression`](../packages/session-persistence/session-persistence-jsonl/src/index.ts) · [`ToolsConfig`](#deepseek-aidsh-tools) · [`uiTui`](../packages/ui/tui/src/index.ts)
Source: [`packages/examples/tui-demo/src/index.ts:33`](../packages/examples/tui-demo/src/index.ts)
## `@deepseek-ai/dsh-user-approval`
@@ -1517,9 +1687,12 @@ Source: [`packages/context/workspace-context/src/config.ts:16`](../packages/cont
These load from a `cordis.yml` entry with no `config:` block; they declare no config surface.
- `@deepseek-ai/dsh-agent` ([`packages/core/agent/src/index.ts`](../packages/core/agent/src/index.ts))
- `@deepseek-ai/dsh-command-goal` — requires `commands` · `goals` ([`packages/goal/command-goal/src/index.ts`](../packages/goal/command-goal/src/index.ts))
- `@deepseek-ai/dsh-commands` ([`packages/ui/commands/src/index.ts`](../packages/ui/commands/src/index.ts))
- `@deepseek-ai/dsh-fs-policy` ([`packages/fs/fs-policy/src/index.ts`](../packages/fs/fs-policy/src/index.ts))
- `@deepseek-ai/dsh-invariants` — requires `sessions` ([`packages/support/invariants/src/index.ts`](../packages/support/invariants/src/index.ts))
- `@deepseek-ai/dsh-goal-session` — requires `agents` · `goals` · `sessions` ([`packages/goal/goal-session/src/index.ts`](../packages/goal/goal-session/src/index.ts))
- `@deepseek-ai/dsh-llm` ([`packages/llm/llm/src/index.ts`](../packages/llm/llm/src/index.ts))
- `@deepseek-ai/dsh-lsp` ([`packages/lsp/lsp/src/index.ts`](../packages/lsp/lsp/src/index.ts))
- `@deepseek-ai/dsh-session` ([`packages/core/session/src/index.ts`](../packages/core/session/src/index.ts))
- `@deepseek-ai/dsh-subagent` ([`packages/subagent/subagent/src/index.ts`](../packages/subagent/subagent/src/index.ts))
- `@deepseek-ai/dsh-tasks` ([`packages/tasks/tasks/src/index.ts`](../packages/tasks/tasks/src/index.ts))

View File

@@ -2,5 +2,5 @@
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write
adding-a-tool.md: 68a8449bc189497b917efe678837d757f85aaf75
adding-a-tool.zh.md: 003534e04550bfbee6740aa3b6bee02ac2cdc237
adding-a-tool.md: a45315dc0ec92ab28963c2aca32dffcf5f778dcd
adding-a-tool.zh.md: f574957ddd0e42cedc93ddc0f3270110a8f110c5

View File

@@ -2,7 +2,7 @@
English | [中文](adding-a-tool.zh.md)
How to give the model a new capability. Reference implementations: `examples/echo-agent/src/echo-tool.ts` (minimal) and `packages/bash/tool-bash` (production-grade, three-package seam).
How to give the model a new capability. The minimal shape below shows the contract; `packages/bash/tool-bash` is the production-grade three-package seam.
## The minimal shape

View File

@@ -2,7 +2,7 @@
[English](adding-a-tool.md) | 中文
如何为模型赋予一项新能力。参考实现:`examples/echo-agent/src/echo-tool.ts`(最小化)和 `packages/bash/tool-bash`生产级由三个包package构成的 seam
如何为模型赋予一项新能力。下文的最小形态展示这项契约;`packages/bash/tool-bash`生产级由三个包package构成的 seam。
## 最小形态

View File

@@ -2,5 +2,5 @@
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write
extension-cookbook.md: 37793e4e76bf5171c759ca78be473912101bd9f4
extension-cookbook.zh.md: 8f170f225b55721c78ef27c0e87e481b5cb00f64
extension-cookbook.md: a1f6d2f0d27b2258ae06236721bbd80cbd3af80e
extension-cookbook.zh.md: f7729492b68bfef50d5e289581028d0c0c4164cf

View File

@@ -87,7 +87,7 @@ export function apply(ctx: Context) {
## Runnable wirings
Six runnable leaves load their plugin trees from `cordis.yml`: [`examples/echo-agent`](../../examples/echo-agent) (mock model + echo tool, `pnpm run demo:echo`), [`examples/repl-agent`](../../examples/repl-agent) (DeepSeek V4 + coding tools through a line-oriented readline REPL, `pnpm run demo:repl`), [`examples/tui-agent`](../../examples/tui-agent) (the same coding composition through full-screen pi-tui, `pnpm run demo:tui`), [`examples/headless-agent`](../../examples/headless-agent) (the same capability class behind a one-shot task and DSH-native output, `pnpm run demo:headless -- "task"`), [`examples/cordis-agent`](../../examples/cordis-agent) (self-inspection and dynamic plugin mounting, `pnpm run demo:cordis`), and [`examples/acp-agent`](../../examples/acp-agent) (an ACP server over JSON-RPC stdio, `pnpm run demo:acp`). The terminal leaves load [`@deepseek-ai/dsh-stdio-demo`](../../packages/examples/stdio-demo), the headless leaf loads [`@deepseek-ai/dsh-cli-demo`](../../packages/examples/cli-demo), the ACP leaf loads [`@deepseek-ai/dsh-acp-demo`](../../packages/examples/acp-demo), and all three app packages share [`@deepseek-ai/dsh-agent-spine-demo`](../../packages/examples/agent-spine-demo).
Four runnable leaves load their plugin trees from `cordis.yml`: [`examples/tui-agent`](../../examples/tui-agent) (DeepSeek coding tools through the full-screen TUI, `pnpm run demo:tui`), [`examples/headless-agent`](../../examples/headless-agent) (the coding capabilities behind a one-shot task and DSH-native output, `pnpm run demo:headless "task"`), [`examples/cordis-agent`](../../examples/cordis-agent) (self-inspection and dynamic plugin mounting through the TUI, `pnpm run demo:cordis`), and [`examples/acp-agent`](../../examples/acp-agent) (an ACP server over JSON-RPC stdio, `pnpm run demo:acp`). Interactive leaves load [`@deepseek-ai/dsh-tui-demo`](../../packages/examples/tui-demo), non-interactive leaves load [`@deepseek-ai/dsh-cli-demo`](../../packages/examples/cli-demo), the ACP leaf loads [`@deepseek-ai/dsh-acp-demo`](../../packages/examples/acp-demo), and all three app packages share [`@deepseek-ai/dsh-agent-spine-demo`](../../packages/examples/agent-spine-demo).
## The feature → mechanism map
@@ -98,7 +98,7 @@ Every product feature maps to a listener on a documented extension seam — the
| Product feature | Plugin mechanism |
|---|---|
| Hook system (user + project level) | listeners on `agent/session-start`, `agent/prompt-submit`, `agent/request`, `agent/step-result`, `tools/pre-execute`, `tools/post-execute`, `agent/turn-continuation` — each interception waterfall returns a typed Decision; the `dsh-hooks-claude` / `dsh-hooks-codex` bridges map hook config files onto these seams |
| `/goal` | force-continue via `agent/turn-continuation` + `steer()` reminders |
| `/goal` | `ctx.goals` owns durable state, `dsh-goal-session` schedules same-session rounds through the public `Agent`, and separate command/tool producers expose human/model control |
| `/loop` | on the `turn/end` session event, `send()` the next iteration; or force-continue |
| Dynamic workflow | `ctx.workflows` + the worker-thread engine + the `workflow` tool; structured in-process children enforce output with scoped prompt/tool registrations, a monotonic tool guard, final `tools/result` commit (including enclosing `run_code`), and terminal `agent/turn-stop` |
| Queued + steering messages | core `Agent.send()` / `Agent.steer()` |

View File

@@ -87,7 +87,7 @@ export function apply(ctx: Context) {
## 可运行的组装示例
个可运行叶子从 `cordis.yml` 加载各自的插件树:[`examples/echo-agent`](../../examples/echo-agent)mock 模型 + echo 工具,`pnpm run demo:echo`)、[`examples/repl-agent`](../../examples/repl-agent)DeepSeek V4 + coding 工具,通过面向行的 readline REPL 交互,`pnpm run demo:repl`)、[`examples/tui-agent`](../../examples/tui-agent)(通过全屏 pi-tui 复用相同的 coding 组装,`pnpm run demo:tui`)、[`examples/headless-agent`](../../examples/headless-agent)同类能力通过单次任务和 DSH 原生输出运行,`pnpm run demo:headless -- "task"`)、[`examples/cordis-agent`](../../examples/cordis-agent)(自我检查和动态插件挂载,`pnpm run demo:cordis`)与 [`examples/acp-agent`](../../examples/acp-agent)(通过 JSON-RPC stdio 暴露的 ACP 服务器,`pnpm run demo:acp`)。终端叶子加载 [`@deepseek-ai/dsh-stdio-demo`](../../packages/examples/stdio-demo)headless 叶子加载 [`@deepseek-ai/dsh-cli-demo`](../../packages/examples/cli-demo)ACP 叶子加载 [`@deepseek-ai/dsh-acp-demo`](../../packages/examples/acp-demo),三个 app 包都通过 [`@deepseek-ai/dsh-agent-spine-demo`](../../packages/examples/agent-spine-demo) 共享主干。
个可运行叶子从 `cordis.yml` 加载各自的插件树:[`examples/tui-agent`](../../examples/tui-agent)通过全屏 TUI 运行的 DeepSeek coding 工具,`pnpm run demo:tui`)、[`examples/headless-agent`](../../examples/headless-agent)(通过单次任务和 DSH 原生输出运行的 coding 能力`pnpm run demo:headless "task"`)、[`examples/cordis-agent`](../../examples/cordis-agent)通过 TUI 进行自我检查和动态插件挂载,`pnpm run demo:cordis`)与 [`examples/acp-agent`](../../examples/acp-agent)(通过 JSON-RPC stdio 暴露的 ACP 服务器,`pnpm run demo:acp`)。交互式叶子加载 [`@deepseek-ai/dsh-tui-demo`](../../packages/examples/tui-demo)非交互式叶子加载 [`@deepseek-ai/dsh-cli-demo`](../../packages/examples/cli-demo)ACP 叶子加载 [`@deepseek-ai/dsh-acp-demo`](../../packages/examples/acp-demo),三个 app 包都通过 [`@deepseek-ai/dsh-agent-spine-demo`](../../packages/examples/agent-spine-demo) 共享主干。
## 功能→机制映射
@@ -98,7 +98,7 @@ export function apply(ctx: Context) {
| 产品功能 | 插件机制 |
|---|---|
| 钩子系统(用户级 + 项目级) | `agent/session-start``agent/prompt-submit``agent/request``agent/step-result``tools/pre-execute``tools/post-execute``agent/turn-continuation` 上的监听器——每个拦截 waterfall 返回一个类型化 Decision`dsh-hooks-claude` / `dsh-hooks-codex` 桥接器将钩子配置文件映射到这些 seam 上 |
| `/goal` | 通过 `agent/turn-continuation` 强制继续 + `steer()` 提醒 |
| `/goal` | `ctx.goals` 管理持久状态,`dsh-goal-session` 通过公共 `Agent` 调度同会话回合,独立的命令/工具生产方分别提供人类/模型控制 |
| `/loop` | 在 `turn/end` 会话事件上 `send()` 下一次迭代;或强制继续 |
| 动态工作流 | `ctx.workflows` + worker-thread 引擎 + `workflow` 工具;结构化的进程内子任务通过作用域化的 prompt/工具注册、单调工具守卫、最终 `tools/result` 提交(包括外层 `run_code`)和终端 `agent/turn-stop` 来强制输出 |
| 排队消息 + steering中途引导 | 核心 `Agent.send()` / `Agent.steer()` |

View File

@@ -13,6 +13,27 @@ Dispatch modes: **emit** (fire-and-forget), **waterfall** (each listener gets `n
## `agent/*`
### `agent/cancel-requested` — emit
Effective broad cancellation was requested, before queued/steering work is cleared or the active step is aborted. This observe-only notification cannot veto cancellation; listener failures are contained.
```ts cordis-catalog
/**
* Effective broad cancellation was requested, before queued/steering work
* is cleared or the active step is aborted. This observe-only notification
* cannot veto cancellation; listener failures are contained.
* @param agent - the agent whose current work is being cancelled.
* @param reason - resolved cancellation reason, including the default.
* Scope-filtered dispatch (`@deepseek-ai/dsh-scope`): agent-scoped listeners receive only that agent.
* @mode emit
*/
'agent/cancel-requested'(this: Scoped<Agent>, agent: Agent, reason: string): void
```
Types: [Agent](../core-data-structures/core.md) · [Scoped](../core-data-structures/scope.md)
Source: [`packages/core/agent/src/types.ts:191`](../../packages/core/agent/src/types.ts)
### `agent/created` — emit
A fully configured agent and live session were published. Setup is composition-only; `agent/session-start` is the first startup-driving seam. Synchronous listener failure vetoes publication, while returned-promise rejection is reported. Detach requested during dispatch waits until every creation listener has observed the stable entry.
@@ -33,7 +54,7 @@ A fully configured agent and live session were published. Setup is composition-o
Types: [Agent](../core-data-structures/core.md) · [Scoped](../core-data-structures/scope.md)
Source: [`packages/core/agent/src/types.ts:143`](../../packages/core/agent/src/types.ts)
Source: [`packages/core/agent/src/types.ts:153`](../../packages/core/agent/src/types.ts)
### `agent/disposed` — emit
@@ -53,7 +74,7 @@ An agent left the registry; AgentLoop emits this after driver quiescence but bef
Types: [Agent](../core-data-structures/core.md) · [Scoped](../core-data-structures/scope.md)
Source: [`packages/core/agent/src/types.ts:152`](../../packages/core/agent/src/types.ts)
Source: [`packages/core/agent/src/types.ts:162`](../../packages/core/agent/src/types.ts)
### `agent/error` — emit
@@ -75,7 +96,7 @@ A step or turn errored. The loop reports a failure here (plus the logger) even w
Types: [Agent](../core-data-structures/core.md) · [Scoped](../core-data-structures/scope.md)
Source: [`packages/core/agent/src/types.ts:307`](../../packages/core/agent/src/types.ts)
Source: [`packages/core/agent/src/types.ts:328`](../../packages/core/agent/src/types.ts)
### `agent/post-step` — serial
@@ -98,7 +119,7 @@ Awaited serial checkpoint after the response, real or synthetic tool results, in
Types: [Agent](../core-data-structures/core.md) · [Scoped](../core-data-structures/scope.md)
Source: [`packages/core/agent/src/types.ts:260`](../../packages/core/agent/src/types.ts)
Source: [`packages/core/agent/src/types.ts:280`](../../packages/core/agent/src/types.ts)
### `agent/pre-step` — serial
@@ -121,18 +142,18 @@ Awaited serial checkpoint before `step/start`; appends land outside the pending
Types: [Agent](../core-data-structures/core.md) · [Scoped](../core-data-structures/scope.md)
Source: [`packages/core/agent/src/types.ts:200`](../../packages/core/agent/src/types.ts)
Source: [`packages/core/agent/src/types.ts:220`](../../packages/core/agent/src/types.ts)
### `agent/prompt-submit` — waterfall
Allow, rewrite, or block one drained prompt before it becomes a user message. Call `next()` for the unchanged default.
Allow, rewrite, or block one claimed prompt before it becomes a user message. Call `next()` for the unchanged default.
```ts cordis-catalog
/**
* Allow, rewrite, or block one drained prompt before it becomes a user
* Allow, rewrite, or block one claimed prompt before it becomes a user
* message. Call `next()` for the unchanged default.
* @param agent - the agent draining its inbox.
* @param content - the drained message's blocks, as queued.
* @param agent - the agent whose turn claimed the message.
* @param content - the claimed message's blocks, as queued.
* @param source - the message's resolved source.
* Scope-filtered dispatch (`@deepseek-ai/dsh-scope`): agent-scoped listeners receive only that agent.
* @mode waterfall
@@ -142,7 +163,7 @@ Allow, rewrite, or block one drained prompt before it becomes a user message. Ca
Types: [Agent](../core-data-structures/core.md) · [ContentBlock](../core-data-structures/core.md) · [MessageSource](../core-data-structures/core.md) · [PromptDecision](../core-data-structures/core.md) · [Scoped](../core-data-structures/scope.md)
Source: [`packages/core/agent/src/types.ts:210`](../../packages/core/agent/src/types.ts)
Source: [`packages/core/agent/src/types.ts:230`](../../packages/core/agent/src/types.ts)
### `agent/queued` — emit
@@ -163,7 +184,7 @@ Detached, frozen content entered the agent's inbox. Source defaults have already
Types: [Agent](../core-data-structures/core.md) · [ContentBlock](../core-data-structures/core.md) · [MessageSource](../core-data-structures/core.md) · [Scoped](../core-data-structures/scope.md)
Source: [`packages/core/agent/src/types.ts:171`](../../packages/core/agent/src/types.ts)
Source: [`packages/core/agent/src/types.ts:181`](../../packages/core/agent/src/types.ts)
### `agent/request` — waterfall
@@ -186,7 +207,7 @@ Replace the frozen call configuration. Model-visible content must use logged cha
Types: [Agent](../core-data-structures/core.md) · [LlmCallConfig](../core-data-structures/core.md) · [Scoped](../core-data-structures/scope.md)
Source: [`packages/core/agent/src/types.ts:222`](../../packages/core/agent/src/types.ts)
Source: [`packages/core/agent/src/types.ts:242`](../../packages/core/agent/src/types.ts)
### `agent/request-error` — waterfall
@@ -201,17 +222,18 @@ Recover a model-request failure after its failed step has closed. `retry` opens
* @param turn - the open turn number.
* @param step - the failed step number.
* @param error - the original model-request failure.
* @param retryAttempt - zero-based number of prior recovery retries.
* @param failure - serializable facts normalized at the final adapter boundary.
* @param priorFailures - immutable failures that already authorized another request in this consecutive sequence.
* @param signal - the turn abort signal.
* Scope-filtered dispatch (`@deepseek-ai/dsh-scope`): agent-scoped listeners receive only that agent.
* @mode waterfall
*/
'agent/request-error'(this: Scoped<Agent>, agent: Agent, turn: number, step: number, error: RequestError, retryAttempt: number, signal: AbortSignal, next: () => Promise<RequestErrorDecision>): Promise<RequestErrorDecision>
'agent/request-error'(this: Scoped<Agent>, agent: Agent, turn: number, step: number, error: RequestError, failure: LlmFailure, priorFailures: readonly LlmFailure[], signal: AbortSignal, next: () => Promise<RequestErrorDecision>): Promise<RequestErrorDecision>
```
Types: [Agent](../core-data-structures/core.md) · [RequestError](../core-data-structures/core.md) · [RequestErrorDecision](../core-data-structures/core.md) · [Scoped](../core-data-structures/scope.md)
Types: [Agent](../core-data-structures/core.md) · [LlmFailure](../core-data-structures/llm-streaming.md) · [RequestError](../core-data-structures/core.md) · [RequestErrorDecision](../core-data-structures/core.md) · [Scoped](../core-data-structures/scope.md)
Source: [`packages/core/agent/src/types.ts:274`](../../packages/core/agent/src/types.ts)
Source: [`packages/core/agent/src/types.ts:295`](../../packages/core/agent/src/types.ts)
### `agent/session-prefix` — waterfall
@@ -237,7 +259,7 @@ Compose request-only messages placed before derived history. The frozen result i
Types: [Agent](../core-data-structures/core.md) · [Message](../core-data-structures/core.md) · [Scoped](../core-data-structures/scope.md)
Source: [`packages/core/agent/src/types.ts:237`](../../packages/core/agent/src/types.ts)
Source: [`packages/core/agent/src/types.ts:257`](../../packages/core/agent/src/types.ts)
### `agent/session-start` — emit
@@ -259,7 +281,7 @@ The session lifecycle began, once before the first turn. Use `agent.inject()` to
Types: [Agent](../core-data-structures/core.md) · [Scoped](../core-data-structures/scope.md) · [SessionStartSource](../core-data-structures/core.md)
Source: [`packages/core/agent/src/types.ts:184`](../../packages/core/agent/src/types.ts)
Source: [`packages/core/agent/src/types.ts:204`](../../packages/core/agent/src/types.ts)
### `agent/status` — emit
@@ -279,7 +301,7 @@ Agent status changed (`idle` ⇄ `running`, or → `disposed`). `send()` does no
Types: [Agent](../core-data-structures/core.md) · [AgentStatus](../core-data-structures/core.md) · [Scoped](../core-data-structures/scope.md)
Source: [`packages/core/agent/src/types.ts:161`](../../packages/core/agent/src/types.ts)
Source: [`packages/core/agent/src/types.ts:171`](../../packages/core/agent/src/types.ts)
### `agent/step-result` — waterfall
@@ -301,7 +323,7 @@ Waterfall: post-process the assembled assistant Message before tool dispatch (va
Types: [Agent](../core-data-structures/core.md) · [Message](../core-data-structures/core.md) · [Scoped](../core-data-structures/scope.md)
Source: [`packages/core/agent/src/types.ts:248`](../../packages/core/agent/src/types.ts)
Source: [`packages/core/agent/src/types.ts:268`](../../packages/core/agent/src/types.ts)
### `agent/turn-continuation` — waterfall
@@ -322,7 +344,7 @@ Override whether the turn continues. The default continues after tool calls or s
Types: [Agent](../core-data-structures/core.md) · [ContinuationDecision](../core-data-structures/core.md) · [Scoped](../core-data-structures/scope.md)
Source: [`packages/core/agent/src/types.ts:284`](../../packages/core/agent/src/types.ts)
Source: [`packages/core/agent/src/types.ts:305`](../../packages/core/agent/src/types.ts)
### `agent/turn-stop` — serial
@@ -343,7 +365,7 @@ Monotonic terminal-stop checkpoint after continuation and steering are folded; a
Types: [Agent](../core-data-structures/core.md) · [ContinuationStop](../core-data-structures/core.md) · [Scoped](../core-data-structures/scope.md)
Source: [`packages/core/agent/src/types.ts:294`](../../packages/core/agent/src/types.ts)
Source: [`packages/core/agent/src/types.ts:315`](../../packages/core/agent/src/types.ts)
## `agent-loop/*`
@@ -366,7 +388,7 @@ A declarative agent entry failed before it could publish a live agent. Consumers
Types: [SessionId](../core-data-structures/core.md)
Source: [`packages/core/agent-loop/src/index.ts:362`](../../packages/core/agent-loop/src/index.ts)
Source: [`packages/core/agent-loop/src/index.ts:353`](../../packages/core/agent-loop/src/index.ts)
## `approval/*`
@@ -389,6 +411,24 @@ Types: [ApprovalOutcome](../core-data-structures/approval.md) · [ApprovalReques
Source: [`packages/ui/user-approval/src/index.ts:31`](../../packages/ui/user-approval/src/index.ts)
## `commands/*`
### `commands/change` — emit
A command was registered or unregistered. This is an unfiltered registry notification because a global or scoped change may affect any UI view. Observer failures are contained and cannot veto the registry mutation.
```ts cordis-catalog
/**
* A command was registered or unregistered. This is an unfiltered registry
* notification because a global or scoped change may affect any UI view.
* Observer failures are contained and cannot veto the registry mutation.
* @mode emit
*/
'commands/change'(): void
```
Source: [`packages/ui/commands/src/index.ts:83`](../../packages/ui/commands/src/index.ts)
## `fs/*`
### `fs/edit-intent` — waterfall
@@ -450,6 +490,29 @@ Types: [FsTarget](../core-data-structures/filesystem.md) · [FsWriteIntent](../c
Source: [`packages/fs/fs/src/index.ts:54`](../../packages/fs/fs/src/index.ts)
## `goal/*`
### `goal/changed` — emit
Goal mutation accepted by one live agent. The matching context event is already appended or queued in that agent's active tool-batch FIFO. Listener failures are contained. Scope-filtered dispatch (`@deepseek-ai/dsh-scope`): agent-scoped listeners receive only that agent.
```ts cordis-catalog
/**
* Goal mutation accepted by one live agent. The matching context event is
* already appended or queued in that agent's active tool-batch FIFO.
* Listener failures are contained.
* Scope-filtered dispatch (`@deepseek-ai/dsh-scope`): agent-scoped listeners receive only that agent.
* @param agent - agent whose session owns the goal.
* @param change - fresh current projection or clear tombstone.
* @mode emit
*/
'goal/changed'(this: import('@deepseek-ai/dsh-scope').Scoped<Agent>, agent: Agent, change: GoalChanged): void
```
Types: [Agent](../core-data-structures/core.md) · [GoalChanged](../core-data-structures/goal.md) · [Scoped](../core-data-structures/scope.md)
Source: [`packages/goal/goal/src/types.ts:167`](../../packages/goal/goal/src/types.ts)
## `llm/*`
### `llm/stream` — waterfall
@@ -473,7 +536,7 @@ Waterfall around every streaming model call (retry, replay, routing). Bound to t
Types: [GenerateOptions](../core-data-structures/core.md) · [LlmService](../core-data-structures/llm-streaming.md) · [StreamChunk](../core-data-structures/llm-streaming.md)
Source: [`packages/llm/llm/src/index.ts:43`](../../packages/llm/llm/src/index.ts)
Source: [`packages/llm/llm/src/index.ts:52`](../../packages/llm/llm/src/index.ts)
## `session/*`
@@ -585,7 +648,7 @@ A ready child settled. Scope-filtered dispatch uses the same delegating parent c
Types: [Scoped](../core-data-structures/scope.md) · [SubagentService](../core-data-structures/subagent.md)
Source: [`packages/subagent/subagent/src/index.ts:112`](../../packages/subagent/subagent/src/index.ts)
Source: [`packages/subagent/subagent/src/index.ts:139`](../../packages/subagent/subagent/src/index.ts)
### `subagent/provider-added` — emit
@@ -602,7 +665,7 @@ A provider became resolvable in the registry.
Types: [SubagentProvider](../core-data-structures/subagent.md)
Source: [`packages/subagent/subagent/src/index.ts:86`](../../packages/subagent/subagent/src/index.ts)
Source: [`packages/subagent/subagent/src/index.ts:113`](../../packages/subagent/subagent/src/index.ts)
### `subagent/provider-removed` — emit
@@ -617,7 +680,7 @@ A provider left the registry. Accepted runs remain holder-owned.
'subagent/provider-removed'(name: string): void
```
Source: [`packages/subagent/subagent/src/index.ts:92`](../../packages/subagent/subagent/src/index.ts)
Source: [`packages/subagent/subagent/src/index.ts:119`](../../packages/subagent/subagent/src/index.ts)
### `subagent/start` — emit
@@ -639,7 +702,7 @@ A provider established a ready child. For in-process providers, `ctx.agents.get(
Types: [Scoped](../core-data-structures/scope.md) · [SubagentService](../core-data-structures/subagent.md)
Source: [`packages/subagent/subagent/src/index.ts:103`](../../packages/subagent/subagent/src/index.ts)
Source: [`packages/subagent/subagent/src/index.ts:130`](../../packages/subagent/subagent/src/index.ts)
## `system-prompt/*`

View File

@@ -44,7 +44,7 @@ async resume(ownerCtx: Context, options: ResumeAgentOptions): Promise<AgentHandl
Types: [Agent](../core-data-structures/core.md) · [AgentOptions](../core-data-structures/core.md) · [SessionHeader](../core-data-structures/persistence.md) · [SessionId](../core-data-structures/core.md)
Source: [`packages/core/agent-loop/src/index.ts:407`](../../packages/core/agent-loop/src/index.ts)
Source: [`packages/core/agent-loop/src/index.ts:398`](../../packages/core/agent-loop/src/index.ts)
## `ctx.agents` — `AgentRegistry`
@@ -216,7 +216,7 @@ roots(): Agent[]
Types: [Agent](../core-data-structures/core.md) · [SessionId](../core-data-structures/core.md)
Source: [`packages/core/agent/src/index.ts:217`](../../packages/core/agent/src/index.ts)
Source: [`packages/core/agent/src/index.ts:224`](../../packages/core/agent/src/index.ts)
## `ctx.approval` — `ApprovalService`
@@ -340,6 +340,47 @@ Types: [CodeRunRequest](../core-data-structures/code-runtime.md) · [CodeRunResu
Source: [`packages/code-runtime/code-runtime/src/index.ts:30`](../../packages/code-runtime/code-runtime/src/index.ts)
## `ctx.commands` — `CommandService`
Human-command registry. Plain-context definitions are global; definitions registered through a command-injected child of an agent context shadow globals for that agent.
```ts cordis-catalog
/**
* Register a global or calling-agent-scoped command.
* @param definition - discovery metadata and direct UI handler.
* @returns the exact effect disposer that unregisters this definition.
*/
register(definition: CommandDefinition): () => void
/**
* List the effective immutable command descriptors for one agent.
* @param agent - exact receiving agent and scoped-layer key.
* @returns name-sorted descriptors after scoped shadowing.
*/
list(agent: Agent): readonly CommandDescriptor[]
/**
* Resolve one effective command definition.
* @param agent - exact receiving agent and scoped-layer key.
* @param name - command name without a slash.
* @returns the scoped shadow or global definition.
*/
find(agent: Agent, name: string): CommandDefinition | undefined
/**
* Parse and execute a known command without sending it to the model.
* @param agent - exact receiving agent.
* @param line - complete slash-command line.
* @param signal - cancellation signal owned by the UI request.
* @returns a detached result, or `undefined` when syntax or name does not resolve.
*/
async execute( agent: Agent, line: string, signal: AbortSignal, ): Promise<CommandResult | undefined>
```
Types: [Agent](../core-data-structures/core.md) · [CommandDefinition](../core-data-structures/commands.md) · [CommandDescriptor](../core-data-structures/commands.md) · [CommandResult](../core-data-structures/commands.md)
Source: [`packages/ui/commands/src/index.ts:207`](../../packages/ui/commands/src/index.ts)
## `ctx.compact` — `CompactService` (abstract seam)
Abstract compaction service. Implementations own trigger policy, retention, and summarization, and may consume a separate measurement service. A successful run replaces the selected surface span with one summary node and prevents concurrent compaction of the same session. Load one implementation per context as `ctx.compact`.
@@ -381,7 +422,7 @@ abstract compactRegion( start: number, end: number, agent: CompactAgentContext,
Types: [CompactionResult](../core-data-structures/compaction.md) · [CompactionTrigger](../core-data-structures/compaction.md)
Source: [`packages/compact/compact/src/index.ts:40`](../../packages/compact/compact/src/index.ts)
Source: [`packages/compact/compact/src/index.ts:39`](../../packages/compact/compact/src/index.ts)
## `ctx.fs` — `FileSystem` (abstract seam)
@@ -485,6 +526,111 @@ Types: [FsDirEntry](../core-data-structures/filesystem.md) · [FsEditOutcome](..
Source: [`packages/fs/fs/src/index.ts:81`](../../packages/fs/fs/src/index.ts)
## `ctx.goals` — `GoalService`
Goal service (`ctx.goals`) backed exclusively by the owning session log.
```ts cordis-catalog
/**
* Read the current goal for one exact live agent.
* @param agent - owning live agent.
* @returns a fresh view or `undefined` when no goal is current.
* @throws {@link GoalError} when the agent is not the registry's live instance.
*/
get(agent: Agent): GoalView | undefined
/**
* Remove process-local continuation authority without changing durable goal
* phase or revision. Lifecycle owners use this before unloading a driver;
* a later human-authorized {@link resume} records the new activation edge.
* @param agent - owning live agent.
* @returns a fresh disarmed view, or `undefined` when no goal is current.
*/
disarm(agent: Agent): GoalView | undefined
/**
* Create and arm a goal. A completed goal may be replaced; every other
* current phase must be cleared or resumed instead.
* @param agent - owning live agent.
* @param request - objective and optional round cap.
* @returns the created live view.
*/
create(agent: Agent, request: CreateGoalRequest): GoalView
/**
* Edit objective and/or round cap without changing phase.
* @param agent - owning live agent.
* @param ref - expected current revision.
* @param request - at least one replacement field.
* @returns the edited view.
*/
edit(agent: Agent, ref: GoalRef, request: EditGoalRequest): GoalView
/**
* Pause an active goal and disarm automatic continuation.
* @param agent - owning live agent.
* @param ref - expected current revision.
* @returns the paused view.
*/
pause(agent: Agent, ref: GoalRef): GoalView
/**
* Resume and arm a stopped goal, or rearm an active goal after a
* session-start edge, while its round budget still has capacity.
* @param agent - owning live agent.
* @param ref - expected current revision.
* @returns the active view.
*/
resume(agent: Agent, ref: GoalRef): GoalView
/**
* Mark a current non-complete goal complete and disarm it.
* @param agent - owning live agent.
* @param ref - expected current revision.
* @returns the completed view.
*/
complete(agent: Agent, ref: GoalRef): GoalView
/**
* Mark an active goal blocked and disarm it.
* @param agent - owning live agent.
* @param ref - expected current revision.
* @param reason - policy-owned stable code and human-readable explanation.
* @returns the blocked view with its durable reason.
*/
block(agent: Agent, ref: GoalRef, reason: GoalBlockReason): GoalView
/**
* Clear the current goal while retaining a durable tombstone and history.
* @param agent - owning live agent.
* @param ref - expected current revision.
* @returns the tombstone ref whose revision is one past the cleared snapshot.
*/
clear(agent: Agent, ref: GoalRef): GoalRef
```
Types: [Agent](../core-data-structures/core.md) · [CreateGoalRequest](../core-data-structures/goal.md) · [EditGoalRequest](../core-data-structures/goal.md) · [GoalBlockReason](../core-data-structures/goal.md) · [GoalRef](../core-data-structures/goal.md) · [GoalView](../core-data-structures/goal.md)
Source: [`packages/goal/goal/src/index.ts:135`](../../packages/goal/goal/src/index.ts)
## `ctx.invariants` — `InvariantService`
Package-owned invariant registry with global and regex-based selection.
```ts cordis-catalog
/**
* Register one package's invariant installer. The package name is reserved
* even when filtering disables its checks. Enabled installers run in a child
* fiber; failure disposes that fiber and releases the reservation.
* @param packageName - full npm package name that owns the contribution.
* @param installer - listener or startup-check installer for the child context.
* @returns an effect-scoped disposer for the registration.
*/
register(packageName: string, installer: InvariantInstaller): () => void
```
Source: [`packages/support/invariants/src/index.ts:94`](../../packages/support/invariants/src/index.ts)
## `ctx.llm` — `LlmService`
The abstract `llm` service: an adapter registry plus a streaming model-call surface, interceptable via the `llm/stream` waterfall.
@@ -514,6 +660,16 @@ listProviders(): LlmProviderInfo[]
*/
async listModels(provider: string): Promise<LlmModelInfo[]>
/**
* Resolve context capacity from the adapter that owns one exact route.
* This query is independent of the advisory model catalog: an unlisted model
* may return metadata, while `undefined` never rejects later routing.
* @param provider - registered provider route to inspect.
* @param model - exact model id passed to the adapter.
* @returns detached context metadata, or `undefined` when the adapter has none.
*/
async resolveModelContext( provider: string, model: string, ): Promise<LlmModelContext | undefined>
/**
* Stream one model call as raw chunks (token-level deltas). Throws
* `LlmError` with code `NO_ADAPTER` if no adapter is registered for
@@ -529,9 +685,9 @@ async listModels(provider: string): Promise<LlmModelInfo[]>
stream(options: GenerateOptions): AsyncIterable<StreamChunk>
```
Types: [GenerateOptions](../core-data-structures/core.md) · [LlmAdapter](../core-data-structures/llm-streaming.md) · [LlmModelInfo](../core-data-structures/core.md) · [LlmProviderInfo](../core-data-structures/core.md) · [StreamChunk](../core-data-structures/llm-streaming.md)
Types: [GenerateOptions](../core-data-structures/core.md) · [LlmAdapter](../core-data-structures/llm-streaming.md) · [LlmModelContext](../core-data-structures/core.md) · [LlmModelInfo](../core-data-structures/core.md) · [LlmProviderInfo](../core-data-structures/core.md) · [StreamChunk](../core-data-structures/llm-streaming.md)
Source: [`packages/llm/llm/src/index.ts:97`](../../packages/llm/llm/src/index.ts)
Source: [`packages/llm/llm/src/index.ts:159`](../../packages/llm/llm/src/index.ts)
## `ctx.permission` — `PermissionService`
@@ -718,9 +874,9 @@ Persistence is intentionally not implemented here — persistence plugins subscr
* Create a session owned by the calling fiber: disposing that fiber stops
* event notification and removes the session from the store. `options.seed`
* populates the session with a copy of those events (replay/fork);
* `options.meta` attaches creation metadata (validated absolute `cwd`,
* `parentSession` lineage) as the immutable {@link SessionHeader} (the store
* fills `version`/`id`/`createdAt`).
* `options.meta` attaches creation metadata (validated absolute `cwd`, seed
* and parent lineage, and delegation depth) as the immutable
* {@link SessionHeader} (the store fills `version`/`id`/`createdAt`).
*
* For an agent whose session must be torn down IN ORDER with its loop (so the
* loop's final flush is captured before the store attachment ends), do NOT use this
@@ -832,7 +988,7 @@ fork(source: SessionForkSource, boundary?: number, childSessionId?: SessionId):
Types: [CreateSessionOptions](../core-data-structures/persistence.md) · [Session](../core-data-structures/session.md) · [SessionId](../core-data-structures/core.md)
Source: [`packages/core/session/src/index.ts:549`](../../packages/core/session/src/index.ts)
Source: [`packages/core/session/src/index.ts:553`](../../packages/core/session/src/index.ts)
## `ctx.skills` — `SkillService`
@@ -946,7 +1102,7 @@ async start(name: string, request: SubagentStartRequest): Promise<SubagentRun>
Types: [SubagentProvider](../core-data-structures/subagent.md) · [SubagentRun](../core-data-structures/subagent.md) · [SubagentStartRequest](../core-data-structures/subagent.md)
Source: [`packages/subagent/subagent/src/index.ts:153`](../../packages/subagent/subagent/src/index.ts)
Source: [`packages/subagent/subagent/src/index.ts:180`](../../packages/subagent/subagent/src/index.ts)
## `ctx.systemPrompt` — `SystemPrompt`
@@ -1118,7 +1274,7 @@ estimateMessage(message: Message): number
Types: [EpochHeader](../core-data-structures/session.md) · [Message](../core-data-structures/core.md) · [Session](../core-data-structures/session.md) · [TokenMeasurement](../core-data-structures/token-meter.md)
Source: [`packages/llm/token-meter/src/index.ts:106`](../../packages/llm/token-meter/src/index.ts)
Source: [`packages/llm/token-meter/src/index.ts:82`](../../packages/llm/token-meter/src/index.ts)
## `ctx.toolResultPrune` — `ToolResultPruneService`

View File

@@ -0,0 +1,84 @@
# Human Commands
The human-command seam of [`dsh-commands`](../../packages/ui/commands). TUI and ACP adapters use it to discover and directly execute plugin-owned commands for an exact agent without creating a model message. The [command Agent Note](../../.agents/notes/implemented/feature/2026-07-19-plugin-command-registration.md) owns dispatch and lifecycle rationale; the [package README](../../packages/ui/commands/README.md) owns composition and limitations.
Source: [`packages/ui/commands/src/index.ts`](../../packages/ui/commands/src/index.ts)
## Input metadata
ACP currently exposes one unstructured-input hint. Command availability follows plugin composition: every adapter consuming the registry sees every effective definition.
```ts type-equiv
/** Immutable command input metadata compatible with ACP unstructured input. */
interface CommandInputDescriptor {
/** Placeholder shown before the user supplies free-form input. */
readonly hint: string
}
```
## Definition
`CommandDefinition` is the plugin-authored registration. The registry validates and freezes a detached effective definition.
```ts type-equiv
/** Plugin-owned command registration. */
interface CommandDefinition {
/** Lowercase command name without the leading slash. */
readonly name: string
/** Human-readable summary used in discovery UI. */
readonly description: string
/** Optional free-form input hint advertised to capable clients. */
readonly input?: CommandInputDescriptor
/** Execute against the receiving agent without sending the command to the model. */
readonly handler: (invocation: CommandInvocation) => CommandResult | Promise<CommandResult>
}
```
## Invocation and result
The adapter owns cancellation and passes the exact target agent. `rawInput` begins immediately after the parsed name and retains the adapter-delivered separator and suffix. Results are direct UI outcomes, not tool results or session events.
```ts type-equiv
/** Invocation passed to one registered command handler. */
interface CommandInvocation {
/** Exact agent whose human-facing surface received the command. */
readonly agent: Agent
/** Exact text following the registered command name, including separator whitespace. */
readonly rawInput: string
/** Cancellation signal owned by the dispatching UI request. */
readonly signal: AbortSignal
}
```
```ts type-equiv
/** Expected command outcome rendered directly by the dispatching UI. */
type CommandResult =
| { readonly kind: 'success'; readonly text?: string }
| { readonly kind: 'error'; readonly text: string }
```
## Discovery and parsing views
Adapters receive handler-free immutable descriptors after scope resolution. `parseCommand()` returns `ParsedCommand` before registry resolution; syntax-valid input can still name an unavailable command.
```ts type-equiv
/** Handler-free immutable command view returned to UI adapters. */
interface CommandDescriptor {
/** Lowercase command name without the leading slash. */
readonly name: string
/** Human-readable summary used in discovery UI. */
readonly description: string
/** Optional free-form input hint advertised to capable clients. */
readonly input?: CommandInputDescriptor
}
```
```ts type-equiv
/** Syntactically valid slash command before registry resolution. */
interface ParsedCommand {
/** Lowercase command name without the leading slash. */
readonly name: string
/** Exact text following the command name. */
readonly rawInput: string
}
```

View File

@@ -18,6 +18,8 @@ Everything else is documented on a **sub-page**, not here. The rule that draws t
| [llm-streaming.md](llm-streaming.md) | the `StreamChunk` wire protocol + adapter contract, `BlockAssembler`, the `LlmAdapter` seam |
| [token-meter.md](token-meter.md) | immutable scalar and positional replay measurements with consumed-log revisions |
| [scope.md](scope.md) | scoped registration identity, dispatch carriers, and the owned `Scope` context |
| [goal.md](goal.md) | persisted goal identity, lifecycle snapshots, activation, change records, and round attribution |
| [commands.md](commands.md) | the human-command seam: definitions, adapter discovery, direct invocation, results, and parsing views |
| [session.md](session.md) | the full `SessionEventMap` variant catalog, `TurnTrigger`/`TurnEndReason`, `deriveMessages()`, the turn-enclosure invariant |
| [persistence.md](persistence.md) | the durability seam: `SessionPersistence`, JSONL + SQLite backends, `session/flush`, crash recovery, `SessionHeader` |
| [session-query.md](session-query.md) | logical records, bounded exact-event reads, and relationship traces |
@@ -29,6 +31,7 @@ Everything else is documented on a **sub-page**, not here. The rule that draws t
| [sandbox.md](sandbox.md) | the process-confinement seam: file-effect modes, `SandboxPolicy`, `ConfinedArgv`, enforcement and fail-closed errors |
| [code-runtime.md](code-runtime.md) | the code-execution seam: `CodeRunRequest`/`Result`, binding namespaces, captured logs, the `CodeRunFailure` taxonomy |
| [filesystem.md](filesystem.md) | the filesystem seam: `FsTarget`, read/write/edit outcomes, observed-file state, `FsErrorCode` |
| [lsp.md](lsp.md) | the LSP navigation seam: `LspQueryRequest`/`Result`, `LspProvider`/`Service`, four operations, `LspError` |
| [skills.md](skills.md) | the skill service: discovery priority, `SkillSummary`/`SkillDefinition`, session-prefix catalog, model-facing `skill` loading |
| [compaction.md](compaction.md) | the compaction seam: the `compact/*` session events, `CompactionResult`, the `CompactService` interface |
| [subagent.md](subagent.md) | the subagent seam: the named-provider registry, `SubagentStartRequest`/`Result`/`Run`, the start-time-vs-runtime capability split |
@@ -190,6 +193,16 @@ interface LlmModelInfo {
}
```
Correctness-sensitive model capacity is queried separately from the advisory catalog and is owned by the adapter serving the exact route.
```ts type-equiv
/** Provider-owned context capacity for one exact provider/model route. */
interface LlmModelContext {
/** Maximum combined request and response context in tokens. */
contextWindow: number
}
```
```ts type-equiv
/** A single model request, fully assembled. */
interface GenerateOptions {
@@ -224,7 +237,7 @@ interface GenerateOptions {
}
```
Why a model response stopped is a merge-extensible reason:
Why a model response stopped is a merge-extensible reason. Terminal provider failures carry the streaming contract's [`LlmFailure`](llm-streaming.md#llmfailure):
```ts type-equiv
/**
@@ -235,8 +248,8 @@ interface FinishReasonMap {
'stop': { kind: 'stop' }
'tool-calls': { kind: 'tool-calls' }
'max-tokens': { kind: 'max-tokens' }
'aborted': { kind: 'aborted' }
'error': { kind: 'error'; message: string; code?: string }
'aborted': { kind: 'aborted'; failure: LlmFailure }
'error': { kind: 'error'; failure: LlmFailure }
}
```
@@ -360,15 +373,20 @@ interface Agent {
readonly ctx: Context
/**
* Queue detached, frozen lossless-JSON input; starts a turn when idle.
* Queue one detached, frozen lossless-JSON item. If claimed, it is the sole
* ordinary message in its FIFO-ordered turn; the next claimed item waits for
* that turn's checkpoint.
* Invalid input throws synchronously before notification or enqueue.
*/
send(content: ContentBlock[], options?: SendOptions): void
/**
* Steer a running turn: content is injected between steps of the current
* turn. Uses the same owned-value and synchronous-validation boundary as
* {@link send}; when idle, behaves exactly like that method.
* Submit steering while the agent is `running`. An open turn records it at
* the next steering checkpoint before a request or continuation decision;
* policy may stop before another step. After turn close and its checkpoint,
* any remainder is queued for a later turn; terminal `agent/turn-stop`,
* cancellation, or disposal may discard it. Uses the same synchronous
* snapshot-and-validation boundary as {@link send}; when idle, delegates to it.
*/
steer(content: ContentBlock[], options?: SendOptions): void
@@ -382,8 +400,9 @@ interface Agent {
inject(content: ContentBlock[], options?: InjectOptions): void
/**
* Clear queued and steering work, including work waiting to start, and abort
* the active step. The supplied reason is preserved across pre-step and active
* Clear all queued and steering work, including items waiting to start, and
* abort the active step. An effective call first emits `agent/cancel-requested`
* with the resolved reason. That reason is preserved across pre-step and active
* cancellation windows, and `whenIdle()` resolves after cancellation reaches
* quiescence. Idle cancellation is a no-op and does not arm a later cancel.
*/
@@ -395,7 +414,7 @@ interface Agent {
}
```
`AgentStatus` is `'idle' | 'running' | 'disposed'`, and `SessionId` is branded. `AgentOptions` is merge-extensible and currently includes `provider?` and `model?`; dispatch requires both after `agent/request`. Persona belongs to `dsh-system-prompt`: an agent-scoped `deployment:persona` may shadow the global default.
`AgentStatus` is `'idle' | 'running' | 'disposed'`, and `SessionId` is branded. `running` describes the driver-wide drain interval, which can span turn close, its durability checkpoint, and consecutive queued turns; it does not prove a turn is still open. `AgentOptions` is merge-extensible and currently includes `provider?` and `model?`; dispatch requires both after `agent/request`. Persona belongs to `dsh-system-prompt`: an agent-scoped `deployment:persona` may shadow the global default.
The [event taxonomy](../architecture.md#event) owns the `agent/*` lifecycle, checkpoint, and waterfall contracts. Turn and step boundaries are durable session events rather than agent emits.
@@ -419,13 +438,14 @@ interface HookContext {
}
```
`agent/prompt-submit` returns a `PromptDecision` (allow a drained queued message — optionally rewriting its `content` or attaching `additionalContexts` — or block it; a batch whose every prompt is blocked opens a zero-step turn that ends `rejected`):
`agent/prompt-submit` returns a `PromptDecision` (allow the turn's claimed queued message — optionally rewriting its `content` or attaching `additionalContexts` — or record `prompt/blocked` and end that zero-step turn as `rejected`):
```ts type-equiv
/**
* Prompt interception result. `allow.content` replaces the prompt and each
* `additionalContexts` entry becomes a separate context message. `block` records a
* durable `prompt/blocked`; an all-blocked batch ends a zero-step rejected turn.
* `additionalContexts` entry becomes a separate context message. `block`
* records a durable `prompt/blocked` and ends the claimed prompt's zero-step
* turn as rejected.
*/
type PromptDecision =
| { kind: 'allow'; content?: ContentBlock[]; additionalContexts?: HookContext[] }
@@ -441,14 +461,14 @@ type ContinuationDecision =
| { action: 'continue'; reason?: { content: ContentBlock[]; source: MessageSource } }
```
`agent/request-error` receives the original `RequestError`, whose optional provider-neutral `code` supports stable routing without message parsing:
`agent/request-error` receives the exact original `RequestError` beside its immutable `LlmFailure`, an immutable list of failures that already authorized another request in the consecutive sequence, the turn signal, and `next()`. Recovery plugins route on `failure.code`, not the live error's message; each policy counts only its own codes, and a successful request clears the history:
```ts type-equiv
/** Model-request failure with an optional machine-routable provider code. */
type RequestError = Error & { code?: string }
```
It returns a `RequestErrorDecision`; `retry` opens a new numbered step after the recovery listener's durable mutation, while `fail` preserves that error:
It returns a `RequestErrorDecision`; `retry` opens a new numbered step after the recovery listener's durable mutation, while `fail` retains the structured failure on `turn/end`:
```ts type-equiv
/** Failed-request recovery decision; `retry` opens another numbered step while listeners delegate by calling `next()`. */

View File

@@ -0,0 +1,143 @@
# Same-session goals
Types shared by the event-sourced goal domain and its policy consumers. The [goal-domain Agent Note](../../.agents/notes/implemented/feature/2026-07-19-persisted-same-session-goal-domain.md) owns the persistence and activation decisions; this page records the literal shapes from [`packages/goal/goal/src/types.ts`](../../packages/goal/goal/src/types.ts).
## Identity and lifecycle
`GoalId` is a [branded id](core.md#branded-ids). A caller mutates one exact revision through `GoalRef`; every accepted durable mutation increments the revision.
```ts type-equiv
/** Compare-and-set identity for one exact goal revision. */
interface GoalRef {
/** Stable goal identity. */
readonly id: GoalId
/** Positive revision; every durable mutation increments it. */
readonly revision: number
}
```
The durable phase answers what happened to the objective. Process-local activation separately answers whether a continuation consumer may start another round.
```ts type-equiv
/** Durable continuation phase. Activation is process-local and separate. */
type GoalPhase =
| 'active'
| 'paused'
| 'blocked'
| 'complete'
```
Blocking is the single durable stopped-by-a-problem state. Its policy-owned reason carries a stable lower-kebab-case code for routing and a free-form explanation for humans and models.
```ts type-equiv
/** Machine-routable and human-readable explanation for a blocked goal. */
interface GoalBlockReason {
/** Stable lower-kebab-case classification chosen by the blocking policy. */
readonly code: string
/** Non-empty explanation shown to humans and models. */
readonly message: string
}
```
```ts type-equiv
/** Full durable state written by every non-clear goal mutation. */
interface GoalSnapshot extends GoalRef {
/** Human-requested completion objective. */
readonly objective: string
/** Durable lifecycle phase. */
readonly phase: GoalPhase
/** Present exactly while `phase` is `blocked`. */
readonly blockedReason?: GoalBlockReason
/** Total admitted goal-round cap. */
readonly maxGoalRounds: number
}
```
```ts type-equiv
/** Current goal projection, including values derived from the session log. */
interface GoalView extends GoalSnapshot {
/** Highest admitted round number for this goal. */
readonly roundsStarted: number
/** Epoch milliseconds of the create mutation. */
readonly createdAt: number
/** Epoch milliseconds of the latest mutation. */
readonly updatedAt: number
/** Process-local continuation eligibility; never persisted. */
readonly activation: GoalActivation
}
```
## Durable changes
Every mutation is a `context/message` whose metadata is either a complete snapshot or a clear tombstone. The version, metadata, goal source, and verbatim rendered content form one replay invariant.
```ts type-equiv
/** Full-snapshot goal mutation retained in a model-visible context event. */
interface GoalSnapshotChangeMeta {
readonly kind: 'goal/change'
readonly version: 1
readonly operation: Exclude<GoalOperation, 'clear'>
readonly goal: GoalSnapshot
readonly roundsStarted: number
readonly createdAt: number
readonly updatedAt: number
}
```
```ts type-equiv
/** Tombstone retained when the current goal is cleared. */
interface GoalClearChangeMeta {
readonly kind: 'goal/change'
readonly version: 1
readonly operation: 'clear'
readonly cleared: GoalRef
readonly clearedAt: number
}
```
Goal state changes use round `0`. A continuation consumer attributes each admitted user-message turn with a positive, sequential round number and the current revision; replay rejects gaps, stale revisions, stopped phases, and cap overflow.
```ts type-equiv
/** Message attribution for durable goal state and continuation rounds. */
interface GoalMessageSource {
readonly kind: 'goal'
readonly goalId: GoalId
readonly revision: number
/** Zero for state changes; positive for admitted continuation rounds. */
readonly round: number
}
```
## Requests and notifications
Creation separates caller omission from the deployment choice, which `create()` resolves internally. An edit is a partial replacement whose runtime validator requires at least one field. Every mutation notification carries the accepted operation and exact revision; clear omits `goal`.
```ts type-equiv
/** Input whose omitted round cap is resolved by the service configuration. */
interface CreateGoalRequest {
readonly objective: string
readonly maxGoalRounds?: number
}
```
```ts type-equiv
/** Fields changed by an edit; at least one must be present. */
interface EditGoalRequest {
readonly objective?: string
readonly maxGoalRounds?: number
}
```
```ts type-equiv
/** Live notification after one goal mutation has been accepted for logging. */
interface GoalChanged {
readonly operation: GoalOperation
readonly ref: GoalRef
/** Absent for a clear tombstone. */
readonly goal?: GoalView
}
```
## Service behavior
[`GoalService`](../../packages/goal/goal/src/index.ts) resolves creation defaults, folds strict replay, enforces exact-live-agent identity and compare-and-set mutations, overlays deferred injections, and emits contained `goal/changed` notifications. The package [README](../../packages/goal/goal/README.md) owns the callable and model-visible contract.

View File

@@ -31,18 +31,40 @@ type StreamChunk =
}
```
## `LlmFailure`
Every thrown or in-band final-adapter failure normalizes to one serializable provider-neutral payload. `providerRetryAfterMs` is a validated positive delay requested by the provider, not a retry decision; `ProviderRequestId` is an opaque branded string for diagnostics.
```ts type-equiv
/** Serializable provider-boundary facts; policy decides whether they are retryable. */
interface LlmFailure {
/** Human-readable provider or transport failure. */
readonly message: string
/** Stable provider-neutral machine-routing code. */
readonly code: string
/** HTTP status observed at the provider boundary, when available. */
readonly status?: number
/** Provider-requested delay in milliseconds, when valid and available. */
readonly providerRetryAfterMs?: number
/** Opaque provider-issued request identifier for diagnostics. */
readonly requestId?: ProviderRequestId
}
```
## The adapter contract
Every adapter MUST obey these, and every consumer may rely on them:
- **`usage` before `finish`, nothing after `finish`.** Defer both to the provider's end-of-stream marker so a trailing usage-only chunk can't violate the ordering.
- **Tool-call `arguments` stay raw JSON strings end-to-end.** Partial fragments stream via `argumentsDelta`; a provider that hands back parsed objects re-stringifies at `block-end`.
- **Two sanctioned error paths.** A failure may either THROW from `stream()` (transport/protocol errors) **or** end the stream with `finish {kind:'error'|'aborted'}` (provider in-band errors, for adapters that can't throw mid-stream). Consumers must handle *both*. The agent loop closes the failed step and offers either form to `agent/request-error`; absent recovery it becomes a turn error, and no normal completed assistant message is logged for that request.
- **Two sanctioned error paths, one fact shape.** A failure may either THROW from `stream()` (transport/protocol errors) **or** end the stream with `finish {kind:'error'|'aborted', failure}` (provider in-band errors, for adapters that can't throw mid-stream). `LlmError.failure` carries the same `LlmFailure`. The final adapter boundary preserves the exact thrown `Error` object and associates immutable facts with that call; the agent loop closes the failed step and offers the error, facts, and immutable prior-retried facts to `agent/request-error`. Absent recovery the structured failure becomes the turn error, and no normal assistant message or tool side effect is committed for that attempt.
- **One adapter call is one provider attempt.** Adapters disable library retries. Agent-level recovery opens another durable numbered step; direct `ctx.llm.stream()` callers remain single-attempt.
- **Provider stalls are bounded at the transport.** Both shipping remote adapters expose positive finite `streamIdleTimeoutMs` with a five-minute default. The watchdog arms only while iterator `next()` is outstanding, uses one stable signal for the whole request, maps its own expiry to `TIMEOUT`, and keeps an earlier caller abort as `ABORTED`.
- **Context overflow has one canonical code.** Both DeepSeek adapters classify explicit provider detail through `isContextWindowExceededError()` and surface `CONTEXT_WINDOW_EXCEEDED`, whether the failure arrives as a thrown HTTP `LlmError` or an in-band finish error. Consumers route on the code, never provider text.
- **Every provider HTTP request carries the app-attribution header.** Adapters send `attributionHeaders()` (below) - the `User-Agent` baseline - and prove it with a wire-level test (mock server asserting the received header, or the library's header hook for a library-backed adapter).
- **Replay state is adapter-owned.** A successful `finish` may carry lossless-JSON state needed to reconstruct a native provider response. The loop stores it with the assembled assistant message unless an `agent/step-result` listener rewrote the content. On a later request, `LlmService` passes the state only when the historical provider and target provider are currently registered to the exact same adapter instance. That adapter validates the state and owns any cross-model or cross-provider conversion; other adapters receive the provider-neutral content and provenance without the private state.
This contract was pinned down by two deliberately independent implementations: `dsh-llm-deepseek` (hand-rolled fetch/SSE) and `dsh-llm-pi-ai` (a generic multi-provider adapter through `@earendil-works/pi-ai`). The library-backed adapter cannot throw mid-stream, so it exercises the finish-chunk error path the hand-rolled one might not.
This contract is pinned down by two deliberately independent implementations: `dsh-llm-deepseek` (hand-rolled fetch/SSE) and `dsh-llm-pi-ai` (a generic multi-provider adapter through `@earendil-works/pi-ai`). The library-backed adapter exercises the finish-chunk error path, while transport-boundary tests prove each idle watchdog stops its actual request.
## `AppIdentity` — app attribution
@@ -132,7 +154,7 @@ declare class BlockAssembler {
## The seam
`LlmAdapter` is the provider seam: subclass, implement `stream()`, and register one adapter instance with `ctx.llm.registerAdapter(providers, adapter)`. `GenerateOptions.provider` selects the registered adapter; `GenerateOptions.model` is passed to that adapter and need not be registered at lifecycle start. Duplicate provider routes fail atomically. Optional `providerInfo()` and asynchronous `listModels()` methods feed `LlmService.listProviders()` / `listModels()` with detached selector metadata. That catalog is advisory rather than a request whitelist: the adapter remains authoritative and may accept unlisted model ids. Adapter lookup happens at the terminal continuation of the `llm/stream` waterfall, so a listener may short-circuit the call or route a mutable one-shot request before lookup. The `block-start` / `block-end` `index` correlation and the assembler together mean an adapter only has to emit well-formed chunks — block reassembly is not each adapter's problem. The consumer surface (`ctx.llm.stream()`) and the `llm/stream` waterfall are described in [architecture.md § Content blocks and streaming](../architecture.md#content-blocks-and-streaming-dsh-llm).
`LlmAdapter` is the provider seam: subclass, implement `stream()`, and register one adapter instance with `ctx.llm.registerAdapter(providers, adapter)`. `GenerateOptions.provider` selects the registered adapter; `GenerateOptions.model` is passed to that adapter and need not be registered at lifecycle start. Duplicate provider routes fail atomically. Optional `providerInfo()` and asynchronous `listModels()` methods feed `LlmService.listProviders()` / `listModels()` with detached selector metadata. That catalog is advisory rather than a request whitelist: the adapter remains authoritative and may accept unlisted model ids. The separate `resolveModelContext()` query exposes correctness-sensitive capacity for an exact route without making catalog membership authoritative; absence means unknown metadata, not invalid routing. Adapter lookup happens at the terminal continuation of the `llm/stream` waterfall, so a listener may short-circuit the call or route a mutable one-shot request before lookup. The `block-start` / `block-end` `index` correlation and the assembler together mean an adapter only has to emit well-formed chunks — block reassembly is not each adapter's problem. The consumer surface (`ctx.llm.stream()`) and the `llm/stream` waterfall are described in [architecture.md § Content blocks and streaming](../architecture.md#content-blocks-and-streaming-dsh-llm).
```ts public-api
/**
@@ -156,6 +178,17 @@ declare abstract class LlmAdapter {
* @returns discoverable models in adapter-preferred order.
*/
listModels(_provider: string): Promise<readonly LlmModelInfo[]>;
/**
* Resolve context capacity for one model accepted by this adapter. Absence
* means the adapter does not know the capacity, not that routing is invalid.
* @param _provider - one provider route owned by this adapter.
* @param _model - exact model id passed to {@link GenerateOptions.model}.
* @returns provider-owned context metadata, or `undefined` when unavailable.
*/
resolveModelContext(
_provider: string,
_model: string,
): Promise<LlmModelContext | undefined>;
/**
* Stream one model call as raw chunks. The only required method.
* @param options - the fully-assembled request; implementations must honor `options.signal`.

View File

@@ -0,0 +1,163 @@
# LSP navigation
The LSP seam — a [capability seam](../../.agents/notes/implemented/architecture/2026-07-15-lsp-capability-seam.md) exposing semantic code navigation on one `ctx.lsp` service, split across packages: interface ([dsh-lsp](../../packages/lsp/lsp), `ctx.lsp` + the provider registry), a generic implementation ([dsh-lsp-local](../../packages/lsp/lsp-local), a configured stdio language-server host), and consumer ([dsh-tool-lsp](../../packages/lsp/tool-lsp), the `lsp` tool schema). LSP is **one optional capability**, not part of the agent-loop spine — so its vocabulary lives here, not in [core.md](core.md). A provider swap does not change how the model asks for navigation.
Source: [`packages/lsp/lsp/src/types.ts`](../../packages/lsp/lsp/src/types.ts)
## Operations and coordinates
The seam and model expose exactly four semantic queries; the union is closed, so adding one is a compile-enforced change across the seam, providers, and the tool. Positions and ranges are zero-based UTF-16, matching the protocol; the model-facing tool owns the one-based cursor convention and converts on the way in and out.
```ts type-equiv
/**
* The four semantic queries the seam and model expose. A closed union: adding an operation is a
* compile-enforced change across the seam, providers, and the tool. Symbols and call hierarchy are
* deliberately deferred (they need different schemas).
*/
type LspOperation = 'goToDefinition' | 'findReferences' | 'goToImplementation' | 'hover'
```
```ts type-equiv
/** A zero-based UTF-16 cursor coordinate, matching the LSP wire convention. */
interface LspPosition {
/** Zero-based line. */
readonly line: number
/** Zero-based UTF-16 code-unit offset within the line. */
readonly character: number
}
```
```ts type-equiv
/** A zero-based UTF-16 half-open range `[start, end)`. */
interface LspRange {
readonly start: LspPosition
readonly end: LspPosition
}
```
## Request
Every field is required: `workspaceRoot` is caller-supplied, `languageId` comes from the provider's registration (not the request), and consumers own timeouts and result limits — so no field needs implementation defaulting and there is no `resolve()` step. The provider receives the caller's request plus the derived `languageId`, which only synchronizes the transient document and never participates in selection.
```ts type-equiv
/**
* A caller's normalized query. Every field is required: `workspaceRoot` is caller-supplied,
* `languageId` comes from the provider registration (not here), and consumers own timeouts and
* result limits — so no field needs implementation defaulting and there is no `resolve()` step.
*/
interface LspQueryRequest {
/** Which semantic query to run. */
readonly operation: LspOperation
/** The source file to query (relative to `workspaceRoot` or absolute; the provider canonicalizes). */
readonly filePath: string
/** The zero-based UTF-16 cursor position to query at. */
readonly position: LspPosition
/** The workspace root the provider resolves against and indexes; required, never defaulted. */
readonly workspaceRoot: string
}
```
```ts type-equiv
/**
* A request as a provider receives it: the caller's {@link LspQueryRequest} plus the `languageId`
* the seam derived from the provider's extension mapping. The language id only synchronizes the
* transient document; it does not participate in selection.
*/
interface LspProviderQuery extends LspQueryRequest {
/** The LSP language id for `filePath`, from this provider's extension mapping. */
readonly languageId: string
}
```
## Result
A CLOSED discriminated union: navigation operations normalize to `locations`, `hover` to content or `null`. Consumers `switch` on `kind` to exhaustiveness so a new arm breaks compilation until handled. `findReferences` always includes declarations — the provider enforces this internally, so callers get no flag. The `locations` variant carries `resolvedWorkspaceRoot`: the provider's canonical form of the request's `workspaceRoot` and the root its `file:` URIs are relative to, so a caller relativizing display paths uses it rather than the possibly-symlinked request root.
```ts type-equiv
/** One resolved location: a document URI and the range within it. */
interface LspLocation {
/** The target document URI (`file:` or otherwise), verbatim from the server. */
readonly uri: string
/** The range within the target document. */
readonly range: LspRange
}
```
```ts type-equiv
/** Normalized hover content, or `null` for no hover at the position. */
interface LspHover {
/** The normalized hover text (markdown or plaintext, provider-joined). */
readonly contents: string
/** The range the hover applies to, when the server supplied one. */
readonly range?: LspRange
}
```
```ts type-equiv
/**
* The closed result union. Navigation operations (`goToDefinition`, `findReferences`,
* `goToImplementation`) normalize to `locations`; `hover` normalizes to content or `null`.
* Consumers `switch` on `kind` to exhaustiveness so a new arm breaks compilation until handled.
*
* The `locations` variant carries `resolvedWorkspaceRoot`: the provider's canonical form of the
* request's `workspaceRoot`, and the root its `file:` location URIs are relative to. A caller that
* relativizes display paths MUST use this, not the request's (possibly symlinked) `workspaceRoot`;
* otherwise a symlinked workspace misclassifies in-workspace results as external.
*/
type LspQueryResult =
| { readonly kind: 'locations'; readonly locations: readonly LspLocation[]; readonly resolvedWorkspaceRoot: string }
| { readonly kind: 'hover'; readonly hover: LspHover | null }
```
## Provider and service
A provider owns a stable branded `id` and an exclusive lowercase leading-dot extension map. `registerProvider` reserves the id and every extension atomically — an invalid or conflicting registration publishes nothing — and its disposer releases all reservations. Selection is per query and order-independent; no match throws `LspError` `LSP_UNAVAILABLE`. The seam exposes no protocol types, process/document controls, or generic JSON-RPC escape hatch.
```ts type-equiv
/**
* A language-server backend registered on `ctx.lsp`. Each provider owns a stable {@link
* LspProviderId} and an extension-to-language-id map (lowercase, leading-dot keys).
* `findReferences` always includes declarations — the provider enforces this internally; callers
* get no flag.
*/
interface LspProvider {
/** Stable provider identity, reserved atomically with the extension mappings. */
readonly id: LspProviderId
/** Lowercase leading-dot extension → LSP language id (e.g. `{ '.ts': 'typescript' }`). */
readonly extensionToLanguage: Readonly<Record<string, string>>
/**
* Run one query. The seam has already selected this provider and derived `languageId`.
* @param request - the resolved provider query (caller request + derived language id).
* @param signal - optional cancellation; the provider stops its own work when it aborts.
* @returns the normalized, closed-union result.
*/
query(request: LspProviderQuery, signal?: AbortSignal): Promise<LspQueryResult>
}
```
```ts type-equiv
/**
* The LSP capability seam (`ctx.lsp`). Owns provider registration/selection and normalized query
* execution; exposes exactly the four operations and no protocol escape hatch.
*/
interface LspService {
/**
* Register a provider, atomically reserving its id and every normalized extension. Any conflict
* or invalid input publishes nothing and throws `LspError`; the returned disposer releases all
* reservations. Disposed with the calling fiber.
* @param provider - the backend to register.
* @returns a synchronous disposer releasing the id and all extension reservations.
*/
registerProvider(provider: LspProvider): () => void
/**
* Select a provider by the file's extension and run one query. Selection is per-query and
* order-independent; no match throws `LspError` `LSP_UNAVAILABLE`.
* @param request - the normalized query.
* @param signal - optional cancellation forwarded to the selected provider.
* @returns the normalized, closed-union result.
*/
query(request: LspQueryRequest, signal?: AbortSignal): Promise<LspQueryResult>
}
```
`LspProviderId` is the seam's branded id (`Branded<'LspProviderId'>` from [dsh-brand](../../packages/util/brand)); `LspError` extends `HarnessError` with stable codes such as `LSP_INVALID_PROVIDER`, `LSP_CONFLICT`, `LSP_UNAVAILABLE`, `LSP_DISPOSED`, `LSP_UNSUPPORTED_OPERATION`, and `LSP_MALFORMED_RESPONSE`, which callers route on instead of parsing `message`.

View File

@@ -6,7 +6,7 @@ The seam is a textbook [capability seam](../../.agents/notes/implemented/archite
## The flush checkpoint
`session/event` is a *synchronous* notification; persistence plugins buffer it (write-behind) and drain at the awaited `session/flush` checkpoint the loop fires at every turn end. Flush is `ctx.parallel` (awaited): a turn's events are durably committed before the next turn starts, and the turn boundary is the commit boundary. A rejecting flush is reported via `agent/error` and the logger — never as a session event (it would land past the commit boundary), so the backend keeps its buffered events for the next flush.
`session/event` is a *synchronous* notification; persistence plugins buffer it (write-behind) until `session/flush`. The loop awaits an ordinary turn's checkpoint before claiming the next queue item; synchronous idle `inject()` schedules its checkpoint without blocking `send()`, and disposal still drains it. A successful flush durably commits the closed turn as one unit; a rejecting flush is reported through `agent/error` and the logger — never as a session event past the closed turn — while the backend keeps its buffered events for the next flush.
## Crash recovery preserves an interrupted turn
@@ -60,12 +60,18 @@ interface SessionHeader {
* boundary lets resume and replay distinguish parent history from child work.
*/
readonly seedLength?: number
/**
* Delegation depth: absent (zero) for a top-level session, parent depth + 1
* for a subagent child. Persisted so a recursion budget survives restart and
* resume — a runtime-only depth would reset a resumed child to top-level.
*/
readonly delegationDepth?: number
}
```
## `CreateSessionOptions` — seeding and metadata
Creating a `Session` through the store takes a `seed` (replay/fork an existing event log) and `meta` (the storage-level fields the store folds into a `SessionHeader`). The store fills in `version`/`id` and defaults `createdAt`; the caller supplies the validated absolute `cwd`, the `parentSession` lineage, the `seedLength` seed boundary, and — only when reconstructing a persisted session — the original `createdAt` to preserve it.
Creating a `Session` through the store takes a `seed` (replay/fork an existing event log) and `meta` (the storage-level fields the store folds into a `SessionHeader`). The store fills in `version`/`id` and defaults `createdAt`; the caller supplies the validated absolute `cwd`, the `parentSession` lineage, the `seedLength` seed boundary, the `delegationDepth`, and — only when reconstructing a persisted session — the original `createdAt` to preserve it.
```ts type-equiv
/**
@@ -85,6 +91,7 @@ interface CreateSessionOptions {
readonly parentSession?: SessionId
readonly createdAt?: number
readonly seedLength?: number
readonly delegationDepth?: number
}
}
```
@@ -95,5 +102,5 @@ Replay/fork is therefore `ctx.sessions.create(id, { seed: seedEvents })`; resumi
Both implement the same abstract `SessionPersistence` (locate/create/append/load/list over `SessionEvent`) and pass `runPersistenceContract`, proving the seam is genuinely backend-agnostic:
- **[dsh-session-persistence-jsonl](../../packages/session-persistence/session-persistence-jsonl)** — an append-only JSONL log per session with crash-safe atomic writes, the interrupted-turn crash recovery above, and a read/replay path.
- **[dsh-session-persistence-jsonl](../../packages/session-persistence/session-persistence-jsonl)** — an append-only logical JSONL log per session, stored as checksummed concatenated Zstandard frames by default or raw lines by configuration, with crash-safe atomic writes, interrupted-turn recovery, and a read/replay path.
- **[dsh-session-persistence-sqlite](../../packages/session-persistence/session-persistence-sqlite)** — `node:sqlite`, one row per `SessionEvent`. The row shape `(session_id, seq, type, time, data, source_event_seqs, surface_op)` maps 1:1 onto the event, including optional surface metadata, so there is no parallel persisted schema to keep in sync.

View File

@@ -17,27 +17,28 @@ The append-only event types. Merge-extensible: a plugin declares extra event typ
*/
interface SessionEventMap {
/**
* Opens turn `turn`. `trigger` records what started it — a drained message
* batch or an idle-time injection. The turn is the durability/replay
* Opens turn `turn`. `trigger` records what started it — one claimed queued
* message or an idle-time injection. The turn is the durability/replay
* boundary: every event sits between a `turn/start` and its matching
* `turn/end` (the turn-enclosure invariant).
*/
'turn/start': { turn: number; trigger: TurnTrigger }
/**
* Closes turn `turn` with the {@link TurnEndReason} that ended it. The loop
* fires the awaited `session/flush` checkpoint at every turn end, so the turn
* boundary is also the durable-commit boundary.
* awaits `session/flush` after an ordinary turn ends before claiming the next
* queued item. Success commits the turn; rejection is reported live and does
* not prevent later work.
*/
'turn/end': { turn: number; reason: TurnEndReason }
/** Opens step `step` of turn `turn` — one model call plus the tool executions it requested. */
'step/start': { turn: number; step: number }
/** Closes step `step` of turn `turn`. */
'step/end': { turn: number; step: number }
/** A user-visible prompt (queued message drained at turn start). */
/** A user-visible prompt (the queued message claimed for this turn). */
'user/message': { content: ContentBlock[]; source: MessageSource }
/**
* Durable record of a prompt veto and its reason. It is log-only: the blocked
* prompt never enters the model-visible surface, including in a mixed batch.
* prompt never enters the model-visible surface, and its turn runs zero steps.
*/
'prompt/blocked': { content: ContentBlock[]; source: MessageSource; reason: string }
/**
@@ -429,7 +430,7 @@ declare class Session {
- `context/message` → a user-role message carrying its `content` verbatim at its chronological position. Optional JSON `meta` remains in the event log and is never rendered.
- `steering/message` → a user-role message carrying its content verbatim at its chronological position.
Everything else (`turn/*`, `step/*`) is structural and does not project into a message. Token usage is observed on `assistant/message.usage` (the step that produced it); an operational error's step number is on `turn/end.reason` for `kind: 'error'`. Because this unreleased format intentionally has no compatibility promise, seed/load validation rejects request headers without provider+model and assistant messages without provider/model provenance instead of guessing a route for historical data.
Everything else (`turn/*`, `step/*`, plugin-owned `llm/retry`) is structural and does not project into a message. Token accounting reads per-step `assistant/chunk { type: 'usage' }` records and treats `assistant/message.usage` as the committed-step fallback when no usage chunk exists; failed model-request attempts have no assistant message, so their usage chunk is the durable accounting record. An operational error's step number is on `turn/end.reason` for `kind: 'error'`, with normalized `LlmFailure` facts for a final model-request failure and message/code for other live errors. Because this unreleased format intentionally has no compatibility promise, seed/load validation rejects request headers without provider+model and assistant messages without provider/model provenance instead of guessing a route for historical data.
## Live-session fork API
@@ -473,15 +474,19 @@ interface TurnEndReasonMap {
* The turn failed: a step threw or the model reported a failure. `step` is the
* step number the failure occurred on (the operational error's location — the
* single durable record of an in-turn failure; live diagnostics also fire via
* `agent/error`). `code` is the error's code when one was attached.
* `agent/error`). Final model-request failures retain their normalized facts
* as one `failure`; other turn failures retain their live Error message/code.
*/
error: { kind: 'error'; step: number; message: string; code?: string }
error: { kind: 'error'; step: number } & (
| { failure: LlmFailure; message?: never; code?: never }
| { message: string; code?: string; failure?: never }
)
disposed: { kind: 'disposed' }
/** At least one step reached its output-token ceiling, even if a plugin continued the turn. */
'max-tokens': { kind: 'max-tokens' }
/**
* Policy blocked every prompt before the first step. The zero-step turn still
* records a balanced durable boundary and the veto reason.
* Policy blocked the turn's claimed prompt before the first step. The
* zero-step turn still records a balanced durable boundary and veto reason.
*/
rejected: { kind: 'rejected'; reason: string }
/**
@@ -492,11 +497,11 @@ interface TurnEndReasonMap {
}
```
`max-tokens` mirrors the model-call `FinishReason` of the same name: any `max-tokens` step in a turn makes the whole turn end `max-tokens` rather than `completed` (the cut-short fact wins over a later continuation), so a consumer can tell a clean stop from a truncated one — but only over `completed`: the `disposed`/`aborted`/`error` outcomes take precedence. `rejected` is a zero-step turn whose whole prompt batch an `agent/prompt-submit` hook blocked (the ACP bridge maps it to `cancelled`). `interrupted` is the one reason no loop emits — it is synthesized by crash recovery (see [persistence.md](persistence.md)). Both maps are merge-extensible.
`max-tokens` mirrors the model-call `FinishReason` of the same name: any `max-tokens` step in a turn makes the whole turn end `max-tokens` rather than `completed` (the cut-short fact wins over a later continuation), so a consumer can tell a clean stop from a truncated one — but only over `completed`: the `disposed`/`aborted`/`error` outcomes take precedence. `rejected` is a zero-step turn whose claimed prompt an `agent/prompt-submit` hook blocked (the ACP bridge maps it to `cancelled`). `interrupted` is the one reason no loop emits — it is synthesized by crash recovery (see [persistence.md](persistence.md)). Both maps are merge-extensible.
## The turn-enclosure invariant
Every session event lives **inside** a turn (between a `turn/start` and its `turn/end`). The loop appends queued `user/message` events *after* `turn/start`, and an idle `agent.inject()` wraps its `context/message` in a one-shot `injection` turn. This makes the turn the single durability/replay boundary: a backend can treat anything after the last `turn/end` as an interrupted-crash tail without risking the loss of legitimately-recorded between-turn context. The `dsh-invariants` plugin enforces it in dev (a message event outside an open turn throws). See [the turn-enclosure invariant Agent Note](../../.agents/notes/implemented/architecture/2026-06-15-turn-enclosure-invariant.md).
Every session event lives **inside** a turn (between a `turn/start` and its `turn/end`). The loop appends queued `user/message` events *after* `turn/start`, and an idle `agent.inject()` wraps its `context/message` in a one-shot `injection` turn. This makes the turn the single durability/replay boundary: a backend can treat anything after the last `turn/end` as an interrupted-crash tail without risking the loss of legitimately-recorded between-turn context. The optional `dsh-session/invariant` companion enforces it in dev through `ctx.invariants` (a message event outside an open turn throws). See [the turn-enclosure invariant Agent Note](../../.agents/notes/implemented/architecture/2026-06-15-turn-enclosure-invariant.md).
## Plugin-contributed log-only events
@@ -506,6 +511,6 @@ The hook bridges' `hook/invoked` / `hook/result` provenance pairs (from `@deepse
## Durability contract
What a persistence backend relies on: the durable log persists every event verbatim, **including** `assistant/chunk` — `seq` must stay contiguous, so chunks cannot be filtered out of the canonical log. All `event.data` must be JSON-serializable; `Session.append` enforces this at the source (throwing on non-serializable data), so a bad event never enters the log and `session.events` always equals what a backend can persist. Adding an event type that carries non-serializable data, or that breaks the turn/step nesting the invariants plugin checks, is a breaking change to the on-disk format.
What a persistence backend relies on: the durable log persists every event verbatim, **including** `assistant/chunk` — `seq` must stay contiguous, so chunks cannot be filtered out of the canonical log. All `event.data` must be JSON-serializable; `Session.append` enforces this at the source (throwing on non-serializable data), so a bad event never enters the log and `session.events` always equals what a backend can persist. Adding an event type that carries non-serializable data, or that breaks the turn/step nesting checked by the session invariant companion, is a breaking change to the on-disk format.
The backends that consume this contract are on [persistence.md](persistence.md).

View File

@@ -49,7 +49,10 @@ interface SubagentStartRequest {
* The spawning ("parent") agent — the one whose tool call started this
* subagent. REQUIRED: in-process backends read `parent.session.header` for
* the working directory, the `parentSession` lineage to stamp on the child,
* and the parent's delegation depth. Out-of-process backends (ACP) ignore it.
* and the parent's delegation depth. The out-of-process backend (ACP) reads
* exactly one field — the session header's cwd, the child's workspace when
* no deployment `cwd` override is configured; nothing else crosses the
* process boundary.
*/
readonly parent: Agent
/**
@@ -237,5 +240,5 @@ interface SubagentProvider {
The spawn and fork backends create an ordinary agent through `parent.ctx`, pass cancellation into core creation, and dispose through `AgentHandle`. Provider removal blocks new starts without revoking accepted runs. Each child gets a new flat scope rather than inheriting parent registrations. Depth and fork seeding reuse existing agent and session vocabulary:
- **Delegation depth** is a merge-extensible `AgentOptions.subagentDepth` field (`0` for a top-level agent, parent + 1 for a child). Only `undefined` means top level; every stored present value must be a non-negative safe integer. The seam owns it — the loop neither sets nor reads it — so a nested spawn validates its parent's stored depth, rejects a derived child depth outside the safe-integer domain, and applies a defined absolute `request.maxDepth` cap to that child.
- **Delegation depth** is durable `SessionHeader.delegationDepth` plus the merge-extensible runtime field `AgentOptions.subagentDepth`; absence means top-level depth zero, and the greater present value is authoritative. The seam owns both fields — the loop neither sets nor reads them — so an in-process child persists parent depth + 1, resume cannot lower it, and every start rejects a derived depth outside the safe-integer domain or above a defined absolute `request.maxDepth` cap.
- **Fork seeding** uses `CreateAgentOptions.seed` (a `SessionEvent[]` prefix threaded through `AgentLoop.createAgent` → `ctx.sessions.prepare({ seed })`, the same primitive `resume` uses). The fork backend passes a *balanced completed-turn prefix* of the parent's log — the parent's events up to and including its last `turn/end` — so the seed is contiguous-from-0 and the [invariants](../../packages/support/invariants) replay accepts it (the in-flight, unbalanced turn is excluded).

View File

@@ -1,6 +1,6 @@
# User Interaction
The user-interaction seam of [dsh-user-interaction](../../packages/ui/user-interaction). It is the provider-neutral vocabulary a tool or permission plugin uses when it needs the human to answer before the agent can continue. UI surfaces provide the active `UserInteractionProvider`: `dsh-stdio-demo` selects keyboard-driven `dsh-tui` overlays or `dsh-stdio` readline prompts, and `dsh-acp` maps questions to ACP form elicitations.
The user-interaction seam of [dsh-user-interaction](../../packages/ui/user-interaction). It is the provider-neutral vocabulary a tool or permission plugin uses when it needs the human to answer before the agent can continue. UI surfaces provide the active `UserInteractionProvider`: `dsh-tui` uses keyboard-driven overlays, and `dsh-acp` maps questions to ACP form elicitations.
Source: [`packages/ui/user-interaction/src/index.ts`](../../packages/ui/user-interaction/src/index.ts)

View File

@@ -8,7 +8,7 @@ Source: [`packages/workflow/workflow/src/types.ts`](../../packages/workflow/work
## The start request
What a caller asks for when starting a run. The tool layer builds this from the model's `{ script, meta, args }` call plus the calling agent; `meta` and `args` are plain JSON DATA (the engine shape-validates `meta` and rejects loud BEFORE anything runs — no script text is ever evaluated to obtain it). `parent` is REQUIRED — every child the script spawns is attributed to it (cwd, lineage, and depth flow through the [subagent seam](subagent.md)).
What a caller asks for when starting a run. The ordinary workflow tool builds this from the model's `{ script, meta, args }` call plus the calling agent; specialized consumers may also select one engine-wide `subagentProvider` and lower `maxTotalAgents` for the run, but the script cannot observe or replace either policy. `meta` and `args` are plain JSON DATA (the engine shape-validates `meta` and rejects loud BEFORE anything runs — no script text is ever evaluated to obtain it). `parent` is REQUIRED — every child the script spawns is attributed to it (cwd, lineage, and depth flow through the [subagent seam](subagent.md)).
```ts type-equiv
/**
@@ -26,6 +26,17 @@ interface WorkflowStartRequest {
meta: WorkflowMeta
/** Optional input exposed verbatim to the script as the `args` global. */
args?: unknown
/**
* Optional engine-wide child-provider override for this run. The workflow
* script cannot observe or replace it; omission uses the engine's configured
* provider.
*/
subagentProvider?: string
/**
* Optional per-run total-child ceiling. Implementations reject values above
* their deployment ceiling before publishing the run.
*/
maxTotalAgents?: number
/** The agent on whose behalf the run executes (parent of every child). */
parent: Agent
/** Cancels the run when aborted (the tool's `exec.signal`). */

View File

@@ -12,7 +12,7 @@ When an interface documents two valid ways to signal something — an adapter ma
## Async state is not synchronous state
`agent.send()` does not flip status before returning; a background task's completion races turn boundaries; `reader.close()` fires for both EOF and disposal. Never gate control flow on a status you only just requested — drive lifecycle off the events/promises that actually fire (`agent/status`, `task.done`), and observe the transition (saw `running` THEN `idle`) rather than counting actions you assume map 1:1 to turns (the loop batches queued messages). The guard cuts both ways: if the awaited transition can never occur (EOF with no work submitted → never `running`), the wait hangs — handle the "nothing to wait for" branch explicitly.
`agent.send()` does not flip status before returning; a background task's completion races turn boundaries; `reader.close()` fires for both EOF and disposal. Never gate control flow on a status you only just requested — drive lifecycle off the events/promises that actually fire (`agent/status`, `task.done`), and observe the transition (saw `running` THEN `idle`) instead of treating status as a per-send result: several queued sends run as consecutive turns under one `running` interval, while cancellation or disposal can discard unstarted items. The guard cuts both ways: if the awaited transition can never occur (EOF with no work submitted → never `running`), the wait hangs — handle the "nothing to wait for" branch explicitly.
## Dispose must reach quiescence, not just request it

View File

@@ -2,5 +2,5 @@
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write
development.md: 94eb4f03329b574862a1ac1de2f8c1d4db4f4a0a
development.zh.md: b533aff43a66ff7cfc5dc61e5b9b224a01c51f12
development.md: 3327d094a31ad9af62a23c9562cdfa03218961a5
development.zh.md: cb1cb86f9f3a34c455844467a20dfdfd08e15098

View File

@@ -9,7 +9,7 @@ This onboarding guide helps project contributors get started with the local envi
- Node.js supports 22.19+ and 24+. CI covers 22.19, 24, and 26; see the [Node engine floor Agent Note](../.agents/notes/implemented/process/2026-07-06-node-engine-floor.md).
- Corepack-enabled pnpm. The repo pins `pnpm@11.7.0` in `package.json`; run `corepack enable` if `pnpm --version` does not resolve through Corepack.
- Git.
- Optional: a DeepSeek API key for the REPL/ACP agent demos and real-API e2e tests.
- Optional: a DeepSeek API key for the TUI/Headless/ACP agent demos and real-API e2e tests.
## First-time setup
@@ -63,7 +63,7 @@ lefthook is configured in `lefthook.yml` as an early local checkpoint before rev
The vendor manifest guard checks that changes under `vendor/*/src` are staged with the matching `vendor/README.md` manifest update. See `vendor/README.md` before editing vendored code.
These hooks do not exactly mirror CI. Notably, `pre-push` runs unit tests without coverage, while CI runs `pnpm run test:coverage`; CI also runs echo-agent and built-bin smoke tests and exercises the compatibility matrix on Node 22.19, 24, and 26.
These hooks do not exactly mirror CI. Notably, `pre-push` runs unit tests without coverage, while CI runs `pnpm run test:coverage`; CI also runs built-bin smoke tests and exercises the compatibility matrix on Node 22.19, 24, and 26.
## CI gates
@@ -102,19 +102,13 @@ When changing package public behavior, update the relevant README or JSDoc in th
## Demos
The echo demo does not need API credentials:
The one-shot Headless coding agent needs `DEEPSEEK_API_KEY` in the environment or repo-root `.env`:
```sh
pnpm run demo:echo
pnpm run demo:headless "summarize this workspace"
```
The repl-agent demo uses the line-oriented readline front door and needs `DEEPSEEK_API_KEY` in the environment or repo-root `.env`:
```sh
pnpm run demo:repl
```
The full-screen TUI reuses the repl-agent composition through the pi-tui front door and needs the same credentials:
The full-screen interactive coding agent needs `DEEPSEEK_API_KEY` in the environment or repo-root `.env`:
```sh
pnpm run demo:tui

View File

@@ -9,7 +9,7 @@
- Node.js 支持 22.19+ 与 24+。CI 覆盖 22.19、24 和 26见 [Node 引擎下限 Agent Note](../.agents/notes/implemented/process/2026-07-06-node-engine-floor.md)。
- 启用了 Corepack 的 pnpm。仓库在 `package.json` 中固定使用 `pnpm@11.7.0`;如果 `pnpm --version` 无法通过 Corepack 解析,请先运行 `corepack enable`
- Git。
- 可选:一个 DeepSeek API key用于 REPL/ACPAgent Client Protocol agent智能体演示和真实 API 的 e2e 测试。
- 可选:一个 DeepSeek API key用于 TUI/Headless/ACPAgent Client Protocol agent智能体演示和真实 API 的 e2e 测试。
## 首次搭建
@@ -63,7 +63,7 @@ lefthook 在 `lefthook.yml` 中配置,作为评审前的本地早期检查点
vendor manifest 守卫检查 `vendor/*/src` 下的改动是否连同对应的 `vendor/README.md` manifest 更新一起暂存。请在编辑 vendor 代码前先阅读 `vendor/README.md`
这些钩子并不与 CI 完全一致。特别是:`pre-push` 运行不带覆盖率的单元测试,而 CI 运行 `pnpm run test:coverage`CI 还会运行 echo-agent 和 built-bin 冒烟测试,并在 Node 22.19、24 和 26 上执行兼容性矩阵。
这些钩子并不与 CI 完全一致。特别是:`pre-push` 运行不带覆盖率的单元测试,而 CI 运行 `pnpm run test:coverage`CI 还会运行 built-bin 冒烟测试,并在 Node 22.19、24 和 26 上执行兼容性矩阵。
## CI 门禁
@@ -102,19 +102,13 @@ pnpm run hygiene # knip, publint, workspace constraints, and NodeNext dec
## 演示
echo 演示不需要 API 凭证
单次运行的 Headless coding agent 需要环境变量或仓库根目录 `.env` 中的 `DEEPSEEK_API_KEY`
```sh
pnpm run demo:echo
pnpm run demo:headless "summarize this workspace"
```
repl-agent 示例使用面向行的 readline 前端,并需要环境变量或仓库根目录 `.env` 中的 `DEEPSEEK_API_KEY`
```sh
pnpm run demo:repl
```
全屏 TUI 通过 pi-tui 前端复用 repl-agent 组装,并需要相同的凭证:
全屏交互式 coding agent 需要环境变量或仓库根目录 `.env` 中的 `DEEPSEEK_API_KEY`
```sh
pnpm run demo:tui

View File

@@ -7,54 +7,57 @@ This matrix shows which packages dispatch each harness-owned event and which pac
| Event | Mode | Declared in | Dispatchers | Listeners |
| --- | --- | --- | --- | --- |
| `agent-loop/config-start-failed` | `emit` | [`packages/core/agent-loop/src/index.ts:362`](../packages/core/agent-loop/src/index.ts) | [`agent-loop`](../packages/core/agent-loop) (`events.dispatch`) | [`stdio`](../packages/ui/stdio), [`tui`](../packages/ui/tui) |
| `agent/created` | `emit` | [`packages/core/agent/src/types.ts:143`](../packages/core/agent/src/types.ts) | [`agent`](../packages/core/agent) (`events.dispatch`) | [`stdio`](../packages/ui/stdio), [`tui`](../packages/ui/tui) |
| `agent/disposed` | `emit` | [`packages/core/agent/src/types.ts:152`](../packages/core/agent/src/types.ts) | [`agent`](../packages/core/agent) (`events.dispatch`) | [`agent-loop`](../packages/core/agent-loop), [`stdio`](../packages/ui/stdio), [`tui`](../packages/ui/tui) |
| `agent/error` | `emit` | [`packages/core/agent/src/types.ts:307`](../packages/core/agent/src/types.ts) | [`agent-loop`](../packages/core/agent-loop) (`emit`) | [`tui`](../packages/ui/tui) |
| `agent/post-step` | `serial` | [`packages/core/agent/src/types.ts:260`](../packages/core/agent/src/types.ts) | [`agent-loop`](../packages/core/agent-loop) (`serial`) | [`compact-basic`](../packages/compact/compact-basic) |
| `agent/pre-step` | `serial` | [`packages/core/agent/src/types.ts:200`](../packages/core/agent/src/types.ts) | [`agent-loop`](../packages/core/agent-loop) (`serial`) | [`time-context`](../packages/context/time-context), [`user-approval`](../packages/ui/user-approval) |
| `agent/prompt-submit` | `waterfall` | [`packages/core/agent/src/types.ts:210`](../packages/core/agent/src/types.ts) | [`agent-loop`](../packages/core/agent-loop) (`waterfall`) | [`acp`](../packages/ui/acp), [`hooks-claude`](../packages/hooks/hooks-claude), [`hooks-codex`](../packages/hooks/hooks-codex), [`repeat-tool-guard`](../packages/guard/repeat-tool-guard) |
| `agent/queued` | `emit` | [`packages/core/agent/src/types.ts:171`](../packages/core/agent/src/types.ts) | [`agent-loop`](../packages/core/agent-loop) (`emit`) | - |
| `agent/request` | `waterfall` | [`packages/core/agent/src/types.ts:222`](../packages/core/agent/src/types.ts) | [`agent-loop`](../packages/core/agent-loop) (`waterfall`) | [`acp`](../packages/ui/acp) |
| `agent/request-error` | `waterfall` | [`packages/core/agent/src/types.ts:274`](../packages/core/agent/src/types.ts) | [`agent-loop`](../packages/core/agent-loop) (`waterfall`) | [`compact-basic`](../packages/compact/compact-basic) |
| `agent/session-prefix` | `waterfall` | [`packages/core/agent/src/types.ts:237`](../packages/core/agent/src/types.ts) | [`agent-loop`](../packages/core/agent-loop) (`waterfall`) | [`tool-skill`](../packages/skill/tool-skill), [`workspace-context`](../packages/context/workspace-context) |
| `agent/session-start` | `emit` | [`packages/core/agent/src/types.ts:184`](../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), [`stdio`](../packages/ui/stdio) |
| `agent/status` | `emit` | [`packages/core/agent/src/types.ts:161`](../packages/core/agent/src/types.ts) | [`agent-loop`](../packages/core/agent-loop) (`emit`) | [`invariants`](../packages/support/invariants), [`stdio`](../packages/ui/stdio), [`tui`](../packages/ui/tui) |
| `agent/step-result` | `waterfall` | [`packages/core/agent/src/types.ts:248`](../packages/core/agent/src/types.ts) | [`agent-loop`](../packages/core/agent-loop) (`waterfall`) | - |
| `agent/turn-continuation` | `waterfall` | [`packages/core/agent/src/types.ts:284`](../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/turn-stop` | `serial` | [`packages/core/agent/src/types.ts:294`](../packages/core/agent/src/types.ts) | [`agent-loop`](../packages/core/agent-loop) (`serial`) | [`subagent-inprocess`](../packages/subagent/subagent-inprocess) |
| `agent-loop/config-start-failed` | `emit` | [`packages/core/agent-loop/src/index.ts:353`](../packages/core/agent-loop/src/index.ts) | [`agent-loop`](../packages/core/agent-loop) (`events.dispatch`) | [`tui`](../packages/ui/tui) |
| `agent/cancel-requested` | `emit` | [`packages/core/agent/src/types.ts:191`](../packages/core/agent/src/types.ts) | [`agent-loop`](../packages/core/agent-loop) (`emit`) | [`goal-session`](../packages/goal/goal-session) |
| `agent/created` | `emit` | [`packages/core/agent/src/types.ts:153`](../packages/core/agent/src/types.ts) | [`agent`](../packages/core/agent) (`events.dispatch`) | [`goal-session`](../packages/goal/goal-session), [`tui`](../packages/ui/tui) |
| `agent/disposed` | `emit` | [`packages/core/agent/src/types.ts:162`](../packages/core/agent/src/types.ts) | [`agent`](../packages/core/agent) (`events.dispatch`) | [`agent-loop`](../packages/core/agent-loop), [`goal-session`](../packages/goal/goal-session), [`tui`](../packages/ui/tui) |
| `agent/error` | `emit` | [`packages/core/agent/src/types.ts:328`](../packages/core/agent/src/types.ts) | [`agent-loop`](../packages/core/agent-loop) (`emit`) | [`goal-session`](../packages/goal/goal-session), [`tui`](../packages/ui/tui) |
| `agent/post-step` | `serial` | [`packages/core/agent/src/types.ts:280`](../packages/core/agent/src/types.ts) | [`agent-loop`](../packages/core/agent-loop) (`serial`) | [`compact-basic`](../packages/compact/compact-basic) |
| `agent/pre-step` | `serial` | [`packages/core/agent/src/types.ts:220`](../packages/core/agent/src/types.ts) | [`agent-loop`](../packages/core/agent-loop) (`serial`) | [`time-context`](../packages/context/time-context), [`user-approval`](../packages/ui/user-approval) |
| `agent/prompt-submit` | `waterfall` | [`packages/core/agent/src/types.ts:230`](../packages/core/agent/src/types.ts) | [`agent-loop`](../packages/core/agent-loop) (`waterfall`) | [`acp`](../packages/ui/acp), [`goal-session`](../packages/goal/goal-session), [`hooks-claude`](../packages/hooks/hooks-claude), [`hooks-codex`](../packages/hooks/hooks-codex), [`repeat-tool-guard`](../packages/guard/repeat-tool-guard) |
| `agent/queued` | `emit` | [`packages/core/agent/src/types.ts:181`](../packages/core/agent/src/types.ts) | [`agent-loop`](../packages/core/agent-loop) (`emit`) | [`goal-session`](../packages/goal/goal-session) |
| `agent/request` | `waterfall` | [`packages/core/agent/src/types.ts:242`](../packages/core/agent/src/types.ts) | [`agent-loop`](../packages/core/agent-loop) (`waterfall`) | [`agent`](../packages/core/agent) |
| `agent/request-error` | `waterfall` | [`packages/core/agent/src/types.ts:295`](../packages/core/agent/src/types.ts) | [`agent-loop`](../packages/core/agent-loop) (`waterfall`) | [`compact-basic`](../packages/compact/compact-basic), [`llm-retry`](../packages/llm/llm-retry) |
| `agent/session-prefix` | `waterfall` | [`packages/core/agent/src/types.ts:257`](../packages/core/agent/src/types.ts) | [`agent-loop`](../packages/core/agent-loop) (`waterfall`) | [`tool-skill`](../packages/skill/tool-skill), [`workspace-context`](../packages/context/workspace-context) |
| `agent/session-start` | `emit` | [`packages/core/agent/src/types.ts:204`](../packages/core/agent/src/types.ts) | [`agent-loop`](../packages/core/agent-loop) (`emit`) | [`goal`](../packages/goal/goal), [`goal-session`](../packages/goal/goal-session), [`hooks-claude`](../packages/hooks/hooks-claude), [`hooks-codex`](../packages/hooks/hooks-codex) |
| `agent/status` | `emit` | [`packages/core/agent/src/types.ts:171`](../packages/core/agent/src/types.ts) | [`agent-loop`](../packages/core/agent-loop) (`emit`) | [`agent`](../packages/core/agent), [`goal-session`](../packages/goal/goal-session), [`tui`](../packages/ui/tui) |
| `agent/step-result` | `waterfall` | [`packages/core/agent/src/types.ts:268`](../packages/core/agent/src/types.ts) | [`agent-loop`](../packages/core/agent-loop) (`waterfall`) | - |
| `agent/turn-continuation` | `waterfall` | [`packages/core/agent/src/types.ts:305`](../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/turn-stop` | `serial` | [`packages/core/agent/src/types.ts:315`](../packages/core/agent/src/types.ts) | [`agent-loop`](../packages/core/agent-loop) (`serial`) | [`subagent-inprocess`](../packages/subagent/subagent-inprocess), [`tool-goal`](../packages/goal/tool-goal) |
| `approval/request` | `waterfall` | [`packages/ui/user-approval/src/index.ts:31`](../packages/ui/user-approval/src/index.ts) | [`user-approval`](../packages/ui/user-approval) (`waterfall`) | [`acp`](../packages/ui/acp) |
| `commands/change` | `emit` | [`packages/ui/commands/src/index.ts:83`](../packages/ui/commands/src/index.ts) | [`commands`](../packages/ui/commands) (`events.dispatch`) | [`acp`](../packages/ui/acp), [`tui`](../packages/ui/tui) |
| `fs/edit-intent` | `waterfall` | [`packages/fs/fs/src/index.ts:62`](../packages/fs/fs/src/index.ts) | [`tool-fs`](../packages/fs/tool-fs) (`waterfall`) | [`fs-policy`](../packages/fs/fs-policy) |
| `fs/observed` | `emit` | [`packages/fs/fs/src/index.ts:71`](../packages/fs/fs/src/index.ts) | [`tool-fs`](../packages/fs/tool-fs) (`emit`) | [`fs-policy`](../packages/fs/fs-policy) |
| `fs/write-intent` | `waterfall` | [`packages/fs/fs/src/index.ts:54`](../packages/fs/fs/src/index.ts) | [`tool-fs`](../packages/fs/tool-fs) (`waterfall`) | [`fs-policy`](../packages/fs/fs-policy) |
| `llm/stream` | `waterfall` | [`packages/llm/llm/src/index.ts:43`](../packages/llm/llm/src/index.ts) | [`llm`](../packages/llm/llm) (`waterfall`) | [`invariants`](../packages/support/invariants), [`llm-replay`](../packages/support/llm-replay) |
| `session/created` | `emit` | [`packages/core/session/src/index.ts:47`](../packages/core/session/src/index.ts) | [`session`](../packages/core/session) (`events.dispatch`) | [`invariants`](../packages/support/invariants), [`jsonrpc`](../packages/ui/jsonrpc), [`session-persistence`](../packages/session-persistence/session-persistence) |
| `goal/changed` | `emit` | [`packages/goal/goal/src/types.ts:167`](../packages/goal/goal/src/types.ts) | [`goal`](../packages/goal/goal) (`emit`) | [`goal-session`](../packages/goal/goal-session) |
| `llm/stream` | `waterfall` | [`packages/llm/llm/src/index.ts:52`](../packages/llm/llm/src/index.ts) | [`llm`](../packages/llm/llm) (`waterfall`) | [`agent-loop`](../packages/core/agent-loop), [`llm`](../packages/llm/llm), [`llm-replay`](../packages/support/llm-replay) |
| `session/created` | `emit` | [`packages/core/session/src/index.ts:47`](../packages/core/session/src/index.ts) | [`session`](../packages/core/session) (`events.dispatch`) | [`compact`](../packages/compact/compact), [`goal`](../packages/goal/goal), [`hook-protocol`](../packages/hooks/hook-protocol), [`jsonrpc`](../packages/ui/jsonrpc), [`llm-retry`](../packages/llm/llm-retry), [`session`](../packages/core/session), [`session-persistence`](../packages/session-persistence/session-persistence), [`user-approval`](../packages/ui/user-approval) |
| `session/disposed` | `emit` | [`packages/core/session/src/index.ts:57`](../packages/core/session/src/index.ts) | [`session`](../packages/core/session) (`events.dispatch`) | [`agent-loop`](../packages/core/agent-loop), [`session-persistence`](../packages/session-persistence/session-persistence) |
| `session/event` | `emit` | [`packages/core/session/src/index.ts:69`](../packages/core/session/src/index.ts) | [`session`](../packages/core/session) (`events.dispatch`) | [`acp`](../packages/ui/acp), [`cli-demo`](../packages/examples/cli-demo), [`invariants`](../packages/support/invariants), [`jsonrpc`](../packages/ui/jsonrpc), [`session-persistence`](../packages/session-persistence/session-persistence), [`stdio`](../packages/ui/stdio), [`token-meter`](../packages/llm/token-meter), [`tui`](../packages/ui/tui), [`workspace-context`](../packages/context/workspace-context) |
| `session/event` | `emit` | [`packages/core/session/src/index.ts:69`](../packages/core/session/src/index.ts) | [`session`](../packages/core/session) (`events.dispatch`) | [`acp`](../packages/ui/acp), [`cli-demo`](../packages/examples/cli-demo), [`compact`](../packages/compact/compact), [`goal`](../packages/goal/goal), [`goal-session`](../packages/goal/goal-session), [`hook-protocol`](../packages/hooks/hook-protocol), [`jsonrpc`](../packages/ui/jsonrpc), [`session`](../packages/core/session), [`session-persistence`](../packages/session-persistence/session-persistence), [`token-meter`](../packages/llm/token-meter), [`tui`](../packages/ui/tui), [`user-approval`](../packages/ui/user-approval), [`workspace-context`](../packages/context/workspace-context) |
| `session/flush` | `parallel` | [`packages/core/session/src/index.ts:79`](../packages/core/session/src/index.ts) | [`session`](../packages/core/session) (`events.dispatch`) | [`session-persistence`](../packages/session-persistence/session-persistence) |
| `subagent/end` | `emit` | [`packages/subagent/subagent/src/index.ts:112`](../packages/subagent/subagent/src/index.ts) | [`subagent`](../packages/subagent/subagent) (`events.dispatch`) | [`hooks-claude`](../packages/hooks/hooks-claude), [`jsonrpc`](../packages/ui/jsonrpc) |
| `subagent/provider-added` | `emit` | [`packages/subagent/subagent/src/index.ts:86`](../packages/subagent/subagent/src/index.ts) | [`subagent`](../packages/subagent/subagent) (`emit`) | [`tool-subagent`](../packages/subagent/tool-subagent) |
| `subagent/provider-removed` | `emit` | [`packages/subagent/subagent/src/index.ts:92`](../packages/subagent/subagent/src/index.ts) | [`subagent`](../packages/subagent/subagent) (`events.dispatch`) | [`tool-subagent`](../packages/subagent/tool-subagent) |
| `subagent/start` | `emit` | [`packages/subagent/subagent/src/index.ts:103`](../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:27`](../packages/core/system-prompt/src/index.ts) | [`system-prompt`](../packages/core/system-prompt) (`waterfall`) | [`acp`](../packages/ui/acp) |
| `subagent/end` | `emit` | [`packages/subagent/subagent/src/index.ts:139`](../packages/subagent/subagent/src/index.ts) | [`subagent`](../packages/subagent/subagent) (`events.dispatch`) | [`hooks-claude`](../packages/hooks/hooks-claude), [`jsonrpc`](../packages/ui/jsonrpc), [`subagent`](../packages/subagent/subagent) |
| `subagent/provider-added` | `emit` | [`packages/subagent/subagent/src/index.ts:113`](../packages/subagent/subagent/src/index.ts) | [`subagent`](../packages/subagent/subagent) (`emit`) | [`subagent`](../packages/subagent/subagent), [`tool-subagent`](../packages/subagent/tool-subagent) |
| `subagent/provider-removed` | `emit` | [`packages/subagent/subagent/src/index.ts:119`](../packages/subagent/subagent/src/index.ts) | [`subagent`](../packages/subagent/subagent) (`events.dispatch`) | [`subagent`](../packages/subagent/subagent), [`tool-subagent`](../packages/subagent/tool-subagent) |
| `subagent/start` | `emit` | [`packages/subagent/subagent/src/index.ts:130`](../packages/subagent/subagent/src/index.ts) | [`subagent`](../packages/subagent/subagent) (`events.dispatch`) | [`hooks-claude`](../packages/hooks/hooks-claude), [`subagent`](../packages/subagent/subagent) |
| `system-prompt/assemble` | `waterfall` | [`packages/core/system-prompt/src/index.ts:27`](../packages/core/system-prompt/src/index.ts) | [`system-prompt`](../packages/core/system-prompt) (`waterfall`) | [`agent`](../packages/core/agent), [`system-prompt`](../packages/core/system-prompt) |
| `system-prompt/change` | `emit` | [`packages/core/system-prompt/src/index.ts:33`](../packages/core/system-prompt/src/index.ts) | [`system-prompt`](../packages/core/system-prompt) (`emit`) | - |
| `tools/change` | `emit` | [`packages/core/tools/src/index.ts:116`](../packages/core/tools/src/index.ts) | [`tools`](../packages/core/tools) (`emit`) | - |
| `tools/execute` | `waterfall` | [`packages/core/tools/src/index.ts:89`](../packages/core/tools/src/index.ts) | [`tools`](../packages/core/tools) (`waterfall`) | [`timeout-policy`](../packages/timeout/timeout-policy) |
| `tools/post-execute` | `waterfall` | [`packages/core/tools/src/index.ts:98`](../packages/core/tools/src/index.ts) | [`tools`](../packages/core/tools) (`waterfall`) | [`hooks-claude`](../packages/hooks/hooks-claude), [`hooks-codex`](../packages/hooks/hooks-codex), [`repeat-tool-guard`](../packages/guard/repeat-tool-guard), [`spill-policy`](../packages/spill/spill-policy), [`workspace-context`](../packages/context/workspace-context) |
| `tools/pre-execute` | `waterfall` | [`packages/core/tools/src/index.ts:80`](../packages/core/tools/src/index.ts) | [`tools`](../packages/core/tools) (`waterfall`) | [`hooks-claude`](../packages/hooks/hooks-claude), [`hooks-codex`](../packages/hooks/hooks-codex) |
| `tools/result` | `emit` | [`packages/core/tools/src/index.ts:106`](../packages/core/tools/src/index.ts) | [`tools`](../packages/core/tools) (`events.dispatch`) | [`subagent-inprocess`](../packages/subagent/subagent-inprocess), [`workspace-context`](../packages/context/workspace-context) |
| `workflow/agent-end` | `emit` | [`packages/workflow/workflow/src/index.ts:81`](../packages/workflow/workflow/src/index.ts) | [`workflow`](../packages/workflow/workflow) (`events.dispatch`) | - |
| `workflow/agent-start` | `emit` | [`packages/workflow/workflow/src/index.ts:70`](../packages/workflow/workflow/src/index.ts) | [`workflow`](../packages/workflow/workflow) (`events.dispatch`) | - |
| `workflow/end` | `emit` | [`packages/workflow/workflow/src/index.ts:91`](../packages/workflow/workflow/src/index.ts) | [`workflow`](../packages/workflow/workflow) (`events.dispatch`) | - |
| `workflow/agent-end` | `emit` | [`packages/workflow/workflow/src/index.ts:81`](../packages/workflow/workflow/src/index.ts) | [`workflow`](../packages/workflow/workflow) (`events.dispatch`) | [`workflow`](../packages/workflow/workflow) |
| `workflow/agent-start` | `emit` | [`packages/workflow/workflow/src/index.ts:70`](../packages/workflow/workflow/src/index.ts) | [`workflow`](../packages/workflow/workflow) (`events.dispatch`) | [`workflow`](../packages/workflow/workflow) |
| `workflow/end` | `emit` | [`packages/workflow/workflow/src/index.ts:91`](../packages/workflow/workflow/src/index.ts) | [`workflow`](../packages/workflow/workflow) (`events.dispatch`) | [`workflow`](../packages/workflow/workflow) |
| `workflow/log` | `emit` | [`packages/workflow/workflow/src/index.ts:60`](../packages/workflow/workflow/src/index.ts) | [`workflow`](../packages/workflow/workflow) (`events.dispatch`) | - |
| `workflow/phase` | `emit` | [`packages/workflow/workflow/src/index.ts:53`](../packages/workflow/workflow/src/index.ts) | [`workflow`](../packages/workflow/workflow) (`events.dispatch`) | - |
| `workflow/start` | `emit` | [`packages/workflow/workflow/src/index.ts:45`](../packages/workflow/workflow/src/index.ts) | [`workflow`](../packages/workflow/workflow) (`events.dispatch`) | - |
| `workflow/start` | `emit` | [`packages/workflow/workflow/src/index.ts:45`](../packages/workflow/workflow/src/index.ts) | [`workflow`](../packages/workflow/workflow) (`events.dispatch`) | [`workflow`](../packages/workflow/workflow) |
## Non-harness or undeclared event strings seen in package source
| Event string | Dispatchers | Listeners |
| --- | --- | --- |
| `internal/dispatch` | - | [`invariants`](../packages/support/invariants) |
| `internal/dispatch` | - | [`compact`](../packages/compact/compact), [`fs`](../packages/fs/fs), [`goal`](../packages/goal/goal), [`goal-session`](../packages/goal/goal-session), [`hook-protocol`](../packages/hooks/hook-protocol), [`llm-retry`](../packages/llm/llm-retry), [`permission`](../packages/ui/permission), [`sandbox-policy`](../packages/sandbox/sandbox-policy), [`scope`](../packages/core/scope), [`session`](../packages/core/session), [`subagent`](../packages/subagent/subagent), [`time-context`](../packages/context/time-context), [`tool-todo`](../packages/todo/tool-todo), [`tools`](../packages/core/tools), [`user-approval`](../packages/ui/user-approval), [`workflow`](../packages/workflow/workflow) |
| `internal/status` | - | [`agent`](../packages/core/agent) |
Maintenance mode: generated: Cordis event declarations and producer/listener edges are resolved from the repository TypeScript Program.

View File

@@ -14,4 +14,28 @@ FIXME(glossary-completeness): Expand this glossary before the first release so i
- **shadowing** — most-specific-wins name resolution: a scoped tool/section/variable replaces its same-named global twin for that scope alone. The per-agent persona and per-agent tool-variant mechanism.
- **restriction / scope-local registration** — a restriction (`tools.restrict`) filters the GLOBAL tool surface for one scope (compose by intersection); scope-local registrations are merged after that filter. A filtered-away global tool is absent from the prompt AND refuses execution, indistinguishably from a nonexistent one.
- **setup window** — the creation slot where a creator composes an agent's scoped world (`CreateAgentOptions.setup`): after the scope and agent object exist but before the agent or session is published, `agent/session-start` fires, or the first prompt is assembled. Setup registers; it never drives the agent.
- **lineage** — parent/child facts carried as data (`parentSession`, `subagentDepth`); never affects visibility. <a id="lineage"></a>
- **lineage** — parent/child facts carried as data (`parentSession`, durable `delegationDepth`, runtime `subagentDepth`); never affects visibility. <a id="lineage"></a>
## goal
- **goal** — one durable completion objective attached to an existing session, with a revisioned `active` / `paused` / `blocked` / `complete` phase and a goal-round cap; `blocked` retains a policy code and explanation. A goal is state, not a scheduler or a separate conversation; the session log remains its source of truth.
- **goal round** — one continuation cycle admitted for the current goal. The same-session driver materializes a goal round as one goal-sourced [turn](#turn), which can contain multiple steps; unrelated human turns in the same session do not consume the goal-round cap. <a id="goal-round"></a>
- **goal activation** — process-local permission for a continuation consumer to admit another goal round. Activation is either `armed` or `disarmed`; it is deliberately absent from durable replay, so resume and fork require a later human-authorized resume mutation through `/goal` or the model tool before automatic work.
## human command
- **human command** — a slash-prefixed instruction interpreted and executed by a human-facing adapter through `ctx.commands`, without becoming a model message. It is distinct from a model-facing tool and from shell command execution through `ctx.bash`.
- **command plane** — discovery, parsing, dispatch, cancellation, and result rendering owned by UI adapters and command plugins. Command output is UI state unless the handler separately mutates a durable domain.
- **goal command** — the `/goal` human command contributed by `dsh-command-goal`; it observes or mutates the current goal directly while the goal domain owns every durable, model-visible record.
## loop hierarchy
- **turn** — one drain of admitted input in a session, ending after the model and its tools stop or a terminal policy intervenes. <a id="turn"></a>
- **step** — one model request plus the tool executions caused by its response; a turn contains one or more steps. <a id="step"></a>
- **round** — an outer policy iteration containing a turn, such as a [goal round](#goal-round) or one fresh-agent Ralph attempt. Round counters belong to that policy and do not count every turn in a session. <a id="round"></a>
## Ralph
- **Ralph loop** — one foreground fresh-agent workflow run toward an immutable objective. It is a model-facing tool policy composed from workflow and subagent primitives, not a same-session goal, agent-loop mode, scheduler, or generic workflow-script feature. <a id="ralph-loop"></a>
- **Ralph round** — one fresh child session in a [Ralph loop](#ralph-loop). The child receives no parent or prior-child conversation seed; the shared workspace and one bounded [Ralph handoff](#ralph-handoff) carry cross-round state. <a id="ralph-round"></a>
- **Ralph handoff** — the normalized bounded structured report passed from one continuing Ralph round to the next, containing status, summary, evidence, next steps, and blocker text. It supplements the shared workspace rather than replacing it as authority. <a id="ralph-handoff"></a>

View File

@@ -12,8 +12,6 @@ The process decision behind this index is recorded in [the documentation graph A
| [module dependency graph](module-graph.md) | `generated` |
| [tool schema catalog and package map](tool-catalog.md) | `generated` |
| [capability seams and core services](capability-seams.md) | `hybrid generated` |
| [echo-agent app composition](../examples/echo-agent/composition.md) | `hybrid generated` |
| [repl-agent app composition](../examples/repl-agent/composition.md) | `hybrid generated` |
| [tui-agent app composition](../examples/tui-agent/composition.md) | `hybrid generated` |
| [headless-agent app composition](../examples/headless-agent/composition.md) | `hybrid generated` |
| [cordis-agent app composition](../examples/cordis-agent/composition.md) | `hybrid generated` |

View File

@@ -28,9 +28,9 @@
**dispose资源释放必须等待所有任务完全停稳不能仅下发终止指令就返回**:如果清理过程只发出终止或中断信号,却不等任务停止就返回,就会留下孤儿进程。清理应采用异步方式,等待所有子任务彻底退出(先发出终止信号,再等待退出);发出信号前应先关闭监听器与通知注册表,使延迟到达的完成事件不再触发通知。测试要证明 dispose 的确等到清理完成:执行完 `await fiber.dispose()` 后进程 PID 立即消失,不能只检查进程最终会自行消亡。
> **Async state is not synchronous state** — `agent.send()` does not flip status before returning; a background task's completion races turn boundaries; `reader.close()` fires for both EOF and disposal. Never gate control flow on a status you only just requested — drive lifecycle off the events/promises that actually fire (`agent/status`, `task.done`), and observe the transition (saw `running` THEN `idle`) rather than counting actions you assume map 1:1 to turns.
> **Async state is not synchronous state** — `agent.send()` does not flip status before returning; a background task's completion races turn boundaries; `reader.close()` fires for both EOF and disposal. Never gate control flow on a status you only just requested — drive lifecycle off the events/promises that actually fire (`agent/status`, `task.done`), and observe the transition (saw `running` THEN `idle`) instead of treating status as a per-send result: several queued sends run as consecutive turns under one `running` interval, while cancellation or disposal can discard unstarted items.
**异步状态不等同于同步瞬时状态**:调用 `agent.send()` 不会在返回前同步更新状态;后台任务的完成时间与轮次边界存在竞态;`reader.close()` 既会在读到文件末尾时触发,也会在资源释放时触发。切勿把刚刚发起的状态变更当成已经生效,据此控制流程;生命周期逻辑应以实际触发的事件和已完成的 promise`agent/status``task.done`)为准,并观察完整的状态变化(先 `running`,再 `idle`),不要根据操作次数推断操作与轮次一一对应
**异步状态不等同于同步瞬时状态**:调用 `agent.send()` 不会在返回前同步更新状态;后台任务的完成时间与轮次边界存在竞态;`reader.close()` 既会在读到文件末尾时触发,也会在资源释放时触发。切勿把刚刚发起的状态变更当成已经生效,据此控制流程;生命周期逻辑应以实际触发的事件和已完成的 promise`agent/status``task.done`)为准,并观察完整的状态变化(先 `running`,再 `idle`),不要把状态当作逐次 `send()` 的结果:多次排队的 `send()` 会作为连续轮次运行,但可能共用一个 `running` 区间;取消或资源释放还可能丢弃尚未启动的队列项
## ③ 测试政策清单

View File

@@ -65,6 +65,7 @@
| waterfall | waterfall | waterfall瀑布式事件 | | |
| wheel | wheel 包 | | | Python 打包格式 |
| worktree | worktree | | | git 工作区概念 |
| Zstandard | Zstandard | | | RFC 8878 compression format; `zstd` remains a code value. |
## 双语类(中英文文本各自使用中英文)

View File

@@ -123,9 +123,9 @@ Follow the Good versions; these sentence-level examples illustrate error categor
- Good: `A green gate does not mean the translation is correct.`
### Code block comments — never translate
- Source code block contains: `# readline coding agent (needs DEEPSEEK_API_KEY)`
- Bad: `# readline 编码 agent需要 DEEPSEEK_API_KEY`
- Good: `# readline coding agent (needs DEEPSEEK_API_KEY)` (byte-identical)
- Source code block contains: `# full-screen TUI coding agent (needs DEEPSEEK_API_KEY)`
- Bad: `# 全屏 TUI coding agent需要 DEEPSEEK_API_KEY`
- Good: `# full-screen TUI coding agent (needs DEEPSEEK_API_KEY)` (byte-identical)
### Language switcher — English to Chinese
- Source: `English | [中文](README.zh.md)`

View File

@@ -18,6 +18,7 @@ flowchart TD
pkg_llm["llm"]
pkg_llm_deepseek["llm-deepseek"]
pkg_llm_pi_ai["llm-pi-ai"]
pkg_llm_retry["llm-retry"]
pkg_token_meter["token-meter"]
end
subgraph group_core["packages/core"]
@@ -28,6 +29,12 @@ flowchart TD
pkg_system_prompt["system-prompt"]
pkg_tools["tools"]
end
subgraph group_goal["packages/goal"]
pkg_command_goal["command-goal"]
pkg_goal["goal"]
pkg_goal_session["goal-session"]
pkg_tool_goal["tool-goal"]
end
subgraph group_bash["packages/bash"]
pkg_bash["bash"]
pkg_bash_local["bash-local"]
@@ -106,9 +113,9 @@ flowchart TD
subgraph group_ui["packages/ui"]
pkg_acp["acp"]
pkg_app_boot["app-boot"]
pkg_commands["commands"]
pkg_jsonrpc["jsonrpc"]
pkg_permission["permission"]
pkg_stdio["stdio"]
pkg_tool_ask_user["tool-ask-user"]
pkg_tui["tui"]
pkg_user_approval["user-approval"]
@@ -127,11 +134,16 @@ flowchart TD
pkg_agent_spine_demo["agent-spine-demo"]
pkg_cli_demo["cli-demo"]
pkg_jsonrpc_demo["jsonrpc-demo"]
pkg_stdio_demo["stdio-demo"]
pkg_tui_demo["tui-demo"]
end
subgraph group_guard["packages/guard"]
pkg_repeat_tool_guard["repeat-tool-guard"]
end
subgraph group_lsp["packages/lsp"]
pkg_lsp["lsp"]
pkg_lsp_local["lsp-local"]
pkg_tool_lsp["tool-lsp"]
end
subgraph group_mcp["packages/mcp"]
pkg_mcp_client["mcp-client"]
end
@@ -150,128 +162,225 @@ flowchart TD
pkg_tool_tasks["tool-tasks"]
end
subgraph group_workflow["packages/workflow"]
pkg_tool_ralph["tool-ralph"]
pkg_tool_workflow["tool-workflow"]
pkg_workflow["workflow"]
pkg_workflow_workerthread["workflow-workerthread"]
end
pkg_brand --> pkg_invariants
pkg_home --> pkg_invariants
pkg_paths --> pkg_invariants
pkg_retention --> pkg_invariants
pkg_timeout --> pkg_invariants
pkg_scope --> pkg_invariants
pkg_skill --> pkg_invariants
pkg_subagent_subprocess --> pkg_invariants
pkg_acp_snapshot --> pkg_invariants
pkg_loader_smoke --> pkg_invariants
pkg_app_boot --> pkg_invariants
pkg_code_runtime --> pkg_invariants
pkg_jsonrpc_demo --> pkg_invariants
pkg_llm --> pkg_brand
pkg_llm --> pkg_invariants
pkg_code_runtime_worker --> pkg_code_runtime
pkg_code_runtime_worker --> pkg_invariants
pkg_helper --> pkg_brand
pkg_helper --> pkg_invariants
pkg_scripts --> pkg_app_boot
pkg_scripts --> pkg_invariants
pkg_telemetry --> pkg_brand
pkg_telemetry --> pkg_invariants
pkg_llm_deepseek --> pkg_invariants
pkg_llm_deepseek --> pkg_llm
pkg_llm_deepseek --> pkg_timeout
pkg_llm_pi_ai --> pkg_invariants
pkg_llm_pi_ai --> pkg_llm
pkg_llm_pi_ai --> pkg_timeout
pkg_session --> pkg_brand
pkg_session --> pkg_invariants
pkg_session --> pkg_llm
pkg_session --> pkg_scope
pkg_system_prompt --> pkg_invariants
pkg_system_prompt --> pkg_llm
pkg_system_prompt --> pkg_scope
pkg_web --> pkg_invariants
pkg_web --> pkg_llm
pkg_lsp --> pkg_brand
pkg_lsp --> pkg_invariants
pkg_lsp --> pkg_llm
pkg_sandbox --> pkg_invariants
pkg_sandbox --> pkg_llm
pkg_token_meter --> pkg_invariants
pkg_token_meter --> pkg_llm
pkg_token_meter --> pkg_session
pkg_agent --> pkg_brand
pkg_agent --> pkg_invariants
pkg_agent --> pkg_llm
pkg_agent --> pkg_scope
pkg_agent --> pkg_session
pkg_agent --> pkg_system_prompt
pkg_bash --> pkg_invariants
pkg_bash --> pkg_sandbox
pkg_fs --> pkg_brand
pkg_fs --> pkg_invariants
pkg_fs --> pkg_llm
pkg_fs --> pkg_sandbox
pkg_compact --> pkg_invariants
pkg_compact --> pkg_llm
pkg_compact --> pkg_session
pkg_compact_tool_result_prune --> pkg_invariants
pkg_compact_tool_result_prune --> pkg_llm
pkg_compact_tool_result_prune --> pkg_session
pkg_web_fetch_local --> pkg_invariants
pkg_web_fetch_local --> pkg_timeout
pkg_web_fetch_local --> pkg_web
pkg_web_search_deepseek --> pkg_invariants
pkg_web_search_deepseek --> pkg_web
pkg_web_search_exa --> pkg_invariants
pkg_web_search_exa --> pkg_web
pkg_web_search_perplexity --> pkg_invariants
pkg_web_search_perplexity --> pkg_web
pkg_spill --> pkg_brand
pkg_spill --> pkg_invariants
pkg_spill --> pkg_llm
pkg_spill --> pkg_session
pkg_session_persistence --> pkg_invariants
pkg_session_persistence --> pkg_session
pkg_llm_replay --> pkg_invariants
pkg_llm_replay --> pkg_llm
pkg_llm_replay --> pkg_session
pkg_lsp_local --> pkg_brand
pkg_lsp_local --> pkg_invariants
pkg_lsp_local --> pkg_llm
pkg_lsp_local --> pkg_lsp
pkg_lsp_local --> pkg_timeout
pkg_sandbox_local --> pkg_invariants
pkg_sandbox_local --> pkg_llm
pkg_sandbox_local --> pkg_sandbox
pkg_sandbox_policy --> pkg_invariants
pkg_sandbox_policy --> pkg_sandbox
pkg_sandbox_policy --> pkg_session
pkg_llm_retry --> pkg_agent
pkg_llm_retry --> pkg_invariants
pkg_llm_retry --> pkg_llm
pkg_llm_retry --> pkg_session
pkg_llm_retry --> pkg_timeout
pkg_goal --> pkg_agent
pkg_goal --> pkg_brand
pkg_goal --> pkg_invariants
pkg_goal --> pkg_llm
pkg_goal --> pkg_scope
pkg_goal --> pkg_session
pkg_bash_local --> pkg_bash
pkg_bash_local --> pkg_invariants
pkg_bash_local --> pkg_timeout
pkg_fs_local --> pkg_fs
pkg_fs_local --> pkg_invariants
pkg_fs_policy --> pkg_fs
pkg_fs_policy --> pkg_invariants
pkg_skill_local --> pkg_fs
pkg_skill_local --> pkg_home
pkg_skill_local --> pkg_invariants
pkg_skill_local --> pkg_skill
pkg_compact_basic --> pkg_agent
pkg_compact_basic --> pkg_compact
pkg_compact_basic --> pkg_compact_tool_result_prune
pkg_compact_basic --> pkg_invariants
pkg_compact_basic --> pkg_llm
pkg_compact_basic --> pkg_session
pkg_compact_basic --> pkg_token_meter
pkg_spill_local --> pkg_invariants
pkg_spill_local --> pkg_spill
pkg_hook_protocol --> pkg_bash
pkg_hook_protocol --> pkg_invariants
pkg_hook_protocol --> pkg_session
pkg_session_persistence_jsonl --> pkg_invariants
pkg_session_persistence_jsonl --> pkg_session
pkg_session_persistence_jsonl --> pkg_session_persistence
pkg_session_persistence_sqlite --> pkg_invariants
pkg_session_persistence_sqlite --> pkg_session
pkg_session_persistence_sqlite --> pkg_session_persistence
pkg_session_query --> pkg_invariants
pkg_session_query --> pkg_llm
pkg_session_query --> pkg_session
pkg_session_query --> pkg_session_persistence
pkg_invariants --> pkg_agent
pkg_invariants --> pkg_llm
pkg_invariants --> pkg_scope
pkg_invariants --> pkg_session
pkg_commands --> pkg_agent
pkg_commands --> pkg_invariants
pkg_commands --> pkg_scope
pkg_user_approval --> pkg_agent
pkg_user_approval --> pkg_brand
pkg_user_approval --> pkg_invariants
pkg_user_approval --> pkg_llm
pkg_user_approval --> pkg_scope
pkg_user_approval --> pkg_session
pkg_user_approval --> pkg_system_prompt
pkg_user_interaction --> pkg_agent
pkg_user_interaction --> pkg_invariants
pkg_user_interaction --> pkg_llm
pkg_time_context --> pkg_agent
pkg_time_context --> pkg_invariants
pkg_time_context --> pkg_session
pkg_tasks --> pkg_agent
pkg_tasks --> pkg_brand
pkg_tasks --> pkg_invariants
pkg_tasks --> pkg_session
pkg_tasks --> pkg_timeout
pkg_workflow --> pkg_agent
pkg_workflow --> pkg_brand
pkg_workflow --> pkg_invariants
pkg_workflow --> pkg_llm
pkg_workflow --> pkg_session
pkg_tools --> pkg_agent
pkg_tools --> pkg_code_runtime
pkg_tools --> pkg_invariants
pkg_tools --> pkg_llm
pkg_tools --> pkg_scope
pkg_tools --> pkg_session
pkg_tools --> pkg_system_prompt
pkg_tools --> pkg_user_approval
pkg_command_goal --> pkg_commands
pkg_command_goal --> pkg_goal
pkg_command_goal --> pkg_invariants
pkg_goal_session --> pkg_agent
pkg_goal_session --> pkg_goal
pkg_goal_session --> pkg_invariants
pkg_goal_session --> pkg_llm
pkg_goal_session --> pkg_session
pkg_bash_sandbox --> pkg_bash
pkg_bash_sandbox --> pkg_bash_local
pkg_bash_sandbox --> pkg_invariants
pkg_bash_sandbox --> pkg_sandbox
pkg_bash_sandbox --> pkg_sandbox_policy
pkg_fs_sandbox --> pkg_fs
pkg_fs_sandbox --> pkg_fs_local
pkg_fs_sandbox --> pkg_invariants
pkg_fs_sandbox --> pkg_sandbox
pkg_fs_sandbox --> pkg_sandbox_policy
pkg_permission --> pkg_bash
pkg_permission --> pkg_invariants
pkg_permission --> pkg_sandbox
pkg_permission --> pkg_sandbox_policy
pkg_permission --> pkg_session
pkg_permission --> pkg_user_approval
pkg_agent_loop --> pkg_agent
pkg_agent_loop --> pkg_invariants
pkg_agent_loop --> pkg_llm
pkg_agent_loop --> pkg_scope
pkg_agent_loop --> pkg_session
pkg_agent_loop --> pkg_session_persistence
pkg_agent_loop --> pkg_system_prompt
pkg_agent_loop --> pkg_tools
pkg_tool_goal --> pkg_agent
pkg_tool_goal --> pkg_goal
pkg_tool_goal --> pkg_invariants
pkg_tool_goal --> pkg_llm
pkg_tool_goal --> pkg_session
pkg_tool_goal --> pkg_system_prompt
pkg_tool_goal --> pkg_tools
pkg_tool_bash --> pkg_agent
pkg_tool_bash --> pkg_bash
pkg_tool_bash --> pkg_home
pkg_tool_bash --> pkg_invariants
pkg_tool_bash --> pkg_llm
pkg_tool_bash --> pkg_sandbox
pkg_tool_bash --> pkg_sandbox_policy
@@ -281,6 +390,7 @@ flowchart TD
pkg_tool_bash --> pkg_tools
pkg_tool_bash --> pkg_user_approval
pkg_tool_fs --> pkg_fs
pkg_tool_fs --> pkg_invariants
pkg_tool_fs --> pkg_llm
pkg_tool_fs --> pkg_sandbox
pkg_tool_fs --> pkg_sandbox_policy
@@ -289,6 +399,7 @@ flowchart TD
pkg_tool_fs --> pkg_tools
pkg_tool_fs --> pkg_user_approval
pkg_tool_fs_search --> pkg_bash
pkg_tool_fs_search --> pkg_invariants
pkg_tool_fs_search --> pkg_llm
pkg_tool_fs_search --> pkg_retention
pkg_tool_fs_search --> pkg_session
@@ -296,46 +407,58 @@ flowchart TD
pkg_tool_fs_search --> pkg_system_prompt
pkg_tool_fs_search --> pkg_tools
pkg_tool_skill --> pkg_agent
pkg_tool_skill --> pkg_invariants
pkg_tool_skill --> pkg_llm
pkg_tool_skill --> pkg_skill
pkg_tool_skill --> pkg_tools
pkg_subagent --> pkg_agent
pkg_subagent --> pkg_brand
pkg_subagent --> pkg_invariants
pkg_subagent --> pkg_llm
pkg_subagent --> pkg_scope
pkg_subagent --> pkg_session
pkg_subagent --> pkg_tools
pkg_tool_web --> pkg_invariants
pkg_tool_web --> pkg_llm
pkg_tool_web --> pkg_system_prompt
pkg_tool_web --> pkg_tools
pkg_tool_web --> pkg_web
pkg_spill_policy --> pkg_invariants
pkg_spill_policy --> pkg_llm
pkg_spill_policy --> pkg_retention
pkg_spill_policy --> pkg_session
pkg_spill_policy --> pkg_spill
pkg_spill_policy --> pkg_tools
pkg_timeout_policy --> pkg_invariants
pkg_timeout_policy --> pkg_llm
pkg_timeout_policy --> pkg_timeout
pkg_timeout_policy --> pkg_tools
pkg_tool_todo --> pkg_agent
pkg_tool_todo --> pkg_invariants
pkg_tool_todo --> pkg_session
pkg_tool_todo --> pkg_tools
pkg_tool_cordis --> pkg_invariants
pkg_tool_cordis --> pkg_scope
pkg_tool_cordis --> pkg_tools
pkg_hooks_codex --> pkg_agent
pkg_hooks_codex --> pkg_hook_protocol
pkg_hooks_codex --> pkg_invariants
pkg_hooks_codex --> pkg_llm
pkg_hooks_codex --> pkg_session
pkg_hooks_codex --> pkg_session_persistence
pkg_hooks_codex --> pkg_tools
pkg_agent_loop_testkit --> pkg_agent
pkg_agent_loop_testkit --> pkg_invariants
pkg_agent_loop_testkit --> pkg_llm
pkg_agent_loop_testkit --> pkg_session
pkg_agent_loop_testkit --> pkg_system_prompt
pkg_agent_loop_testkit --> pkg_tools
pkg_acp --> pkg_agent
pkg_acp --> pkg_bash
pkg_acp --> pkg_commands
pkg_acp --> pkg_invariants
pkg_acp --> pkg_llm
pkg_acp --> pkg_llm_retry
pkg_acp --> pkg_permission
pkg_acp --> pkg_sandbox
pkg_acp --> pkg_session
@@ -345,98 +468,133 @@ flowchart TD
pkg_acp --> pkg_user_approval
pkg_acp --> pkg_user_interaction
pkg_tool_ask_user --> pkg_agent
pkg_tool_ask_user --> pkg_invariants
pkg_tool_ask_user --> pkg_tools
pkg_tool_ask_user --> pkg_user_interaction
pkg_workspace_context --> pkg_agent
pkg_workspace_context --> pkg_fs
pkg_workspace_context --> pkg_invariants
pkg_workspace_context --> pkg_llm
pkg_workspace_context --> pkg_paths
pkg_workspace_context --> pkg_session
pkg_workspace_context --> pkg_tools
pkg_repeat_tool_guard --> pkg_agent
pkg_repeat_tool_guard --> pkg_invariants
pkg_repeat_tool_guard --> pkg_tools
pkg_tool_lsp --> pkg_invariants
pkg_tool_lsp --> pkg_llm
pkg_tool_lsp --> pkg_lsp
pkg_tool_lsp --> pkg_system_prompt
pkg_tool_lsp --> pkg_timeout
pkg_tool_lsp --> pkg_tools
pkg_mcp_client --> pkg_invariants
pkg_mcp_client --> pkg_llm
pkg_mcp_client --> pkg_tools
pkg_tool_tasks --> pkg_agent
pkg_tool_tasks --> pkg_invariants
pkg_tool_tasks --> pkg_system_prompt
pkg_tool_tasks --> pkg_tasks
pkg_tool_tasks --> pkg_tools
pkg_tool_workflow --> pkg_agent
pkg_tool_workflow --> pkg_invariants
pkg_tool_workflow --> pkg_llm
pkg_tool_workflow --> pkg_system_prompt
pkg_tool_workflow --> pkg_tools
pkg_tool_workflow --> pkg_workflow
pkg_subagent_acp --> pkg_agent
pkg_subagent_acp --> pkg_invariants
pkg_subagent_acp --> pkg_llm
pkg_subagent_acp --> pkg_session
pkg_subagent_acp --> pkg_subagent
pkg_subagent_acp --> pkg_subagent_subprocess
pkg_subagent_inprocess --> pkg_agent
pkg_subagent_inprocess --> pkg_invariants
pkg_subagent_inprocess --> pkg_llm
pkg_subagent_inprocess --> pkg_session
pkg_subagent_inprocess --> pkg_subagent
pkg_subagent_inprocess --> pkg_system_prompt
pkg_subagent_inprocess --> pkg_tools
pkg_tool_subagent --> pkg_agent
pkg_tool_subagent --> pkg_invariants
pkg_tool_subagent --> pkg_llm
pkg_tool_subagent --> pkg_subagent
pkg_tool_subagent --> pkg_tasks
pkg_tool_subagent --> pkg_tools
pkg_hooks_claude --> pkg_agent
pkg_hooks_claude --> pkg_hook_protocol
pkg_hooks_claude --> pkg_invariants
pkg_hooks_claude --> pkg_llm
pkg_hooks_claude --> pkg_session
pkg_hooks_claude --> pkg_session_persistence
pkg_hooks_claude --> pkg_subagent
pkg_hooks_claude --> pkg_tools
pkg_jsonrpc --> pkg_agent
pkg_jsonrpc --> pkg_invariants
pkg_jsonrpc --> pkg_llm
pkg_jsonrpc --> pkg_llm_deepseek
pkg_jsonrpc --> pkg_scope
pkg_jsonrpc --> pkg_session
pkg_jsonrpc --> pkg_subagent
pkg_stdio --> pkg_agent
pkg_stdio --> pkg_agent_loop
pkg_stdio --> pkg_llm
pkg_stdio --> pkg_session
pkg_stdio --> pkg_user_interaction
pkg_tui --> pkg_agent
pkg_tui --> pkg_agent_loop
pkg_tui --> pkg_commands
pkg_tui --> pkg_invariants
pkg_tui --> pkg_llm
pkg_tui --> pkg_llm_retry
pkg_tui --> pkg_session
pkg_tui --> pkg_system_prompt
pkg_tui --> pkg_token_meter
pkg_tui --> pkg_tools
pkg_tui --> pkg_user_interaction
pkg_agent_spine_demo --> pkg_agent
pkg_agent_spine_demo --> pkg_agent_loop
pkg_agent_spine_demo --> pkg_goal
pkg_agent_spine_demo --> pkg_goal_session
pkg_agent_spine_demo --> pkg_home
pkg_agent_spine_demo --> pkg_invariants
pkg_agent_spine_demo --> pkg_llm
pkg_agent_spine_demo --> pkg_llm_retry
pkg_agent_spine_demo --> pkg_scope
pkg_agent_spine_demo --> pkg_session
pkg_agent_spine_demo --> pkg_skill
pkg_agent_spine_demo --> pkg_skill_local
pkg_agent_spine_demo --> pkg_system_prompt
pkg_agent_spine_demo --> pkg_tasks
pkg_agent_spine_demo --> pkg_tool_bash
pkg_agent_spine_demo --> pkg_tool_goal
pkg_agent_spine_demo --> pkg_tool_skill
pkg_agent_spine_demo --> pkg_tool_tasks
pkg_agent_spine_demo --> pkg_tools
pkg_agent_spine_demo --> pkg_workspace_context
pkg_tool_ralph --> pkg_agent
pkg_tool_ralph --> pkg_invariants
pkg_tool_ralph --> pkg_llm
pkg_tool_ralph --> pkg_subagent
pkg_tool_ralph --> pkg_system_prompt
pkg_tool_ralph --> pkg_tools
pkg_tool_ralph --> pkg_workflow
pkg_workflow_workerthread --> pkg_agent
pkg_workflow_workerthread --> pkg_brand
pkg_workflow_workerthread --> pkg_invariants
pkg_workflow_workerthread --> pkg_llm
pkg_workflow_workerthread --> pkg_session
pkg_workflow_workerthread --> pkg_subagent
pkg_workflow_workerthread --> pkg_tools
pkg_workflow_workerthread --> pkg_workflow
pkg_subagent_fork --> pkg_agent
pkg_subagent_fork --> pkg_invariants
pkg_subagent_fork --> pkg_session
pkg_subagent_fork --> pkg_subagent
pkg_subagent_fork --> pkg_subagent_inprocess
pkg_subagent_spawn --> pkg_invariants
pkg_subagent_spawn --> pkg_subagent
pkg_subagent_spawn --> pkg_subagent_inprocess
pkg_acp_demo --> pkg_acp
pkg_acp_demo --> pkg_agent_spine_demo
pkg_acp_demo --> pkg_app_boot
pkg_acp_demo --> pkg_command_goal
pkg_acp_demo --> pkg_commands
pkg_acp_demo --> pkg_invariants
pkg_acp_demo --> pkg_session_persistence_jsonl
pkg_acp_demo --> pkg_tools
pkg_acp_demo --> pkg_user_interaction
@@ -444,118 +602,130 @@ flowchart TD
pkg_cli_demo --> pkg_agent
pkg_cli_demo --> pkg_agent_spine_demo
pkg_cli_demo --> pkg_app_boot
pkg_cli_demo --> pkg_invariants
pkg_cli_demo --> pkg_llm
pkg_cli_demo --> pkg_session
pkg_cli_demo --> pkg_session_persistence_jsonl
pkg_cli_demo --> pkg_tools
pkg_cli_demo --> pkg_workspace_context
pkg_stdio_demo --> pkg_agent
pkg_stdio_demo --> pkg_agent_loop
pkg_stdio_demo --> pkg_agent_spine_demo
pkg_stdio_demo --> pkg_app_boot
pkg_stdio_demo --> pkg_llm
pkg_stdio_demo --> pkg_session
pkg_stdio_demo --> pkg_session_persistence_jsonl
pkg_stdio_demo --> pkg_stdio
pkg_stdio_demo --> pkg_tool_ask_user
pkg_stdio_demo --> pkg_tools
pkg_stdio_demo --> pkg_tui
pkg_stdio_demo --> pkg_user_interaction
pkg_stdio_demo --> pkg_workspace_context
pkg_tui_demo --> pkg_agent
pkg_tui_demo --> pkg_agent_loop
pkg_tui_demo --> pkg_agent_spine_demo
pkg_tui_demo --> pkg_app_boot
pkg_tui_demo --> pkg_command_goal
pkg_tui_demo --> pkg_commands
pkg_tui_demo --> pkg_invariants
pkg_tui_demo --> pkg_llm
pkg_tui_demo --> pkg_session
pkg_tui_demo --> pkg_session_persistence_jsonl
pkg_tui_demo --> pkg_tool_ask_user
pkg_tui_demo --> pkg_tools
pkg_tui_demo --> pkg_tui
pkg_tui_demo --> pkg_user_interaction
pkg_tui_demo --> pkg_workspace_context
```
| Package | Group | Depends on |
| --- | --- | --- |
| [`brand`](../packages/util/brand) | `util` | — |
| [`home`](../packages/util/home) | `util` | |
| [`paths`](../packages/util/paths) | `util` | |
| [`retention`](../packages/util/retention) | `util` | |
| [`timeout`](../packages/util/timeout) | `util` | |
| [`scope`](../packages/core/scope) | `core` | |
| [`skill`](../packages/skill/skill) | `skill` | — |
| [`subagent-subprocess`](../packages/subagent/subagent-subprocess) | `subagent` | — |
| [`acp-snapshot`](../packages/support/acp-snapshot) | `support` | — |
| [`loader-smoke`](../packages/support/loader-smoke) | `support` | |
| [`app-boot`](../packages/ui/app-boot) | `ui` | — |
| [`code-runtime`](../packages/code-runtime/code-runtime) | `code-runtime` | — |
| [`jsonrpc-demo`](../packages/examples/jsonrpc-demo) | `examples` | — |
| [`llm`](../packages/llm/llm) | `llm` | [`brand`](../packages/util/brand) |
| [`code-runtime-worker`](../packages/code-runtime/code-runtime-worker) | `code-runtime` | [`code-runtime`](../packages/code-runtime/code-runtime) |
| [`helper`](../packages/sdk/helper) | `sdk` | [`brand`](../packages/util/brand) |
| [`scripts`](../packages/sdk/scripts) | `sdk` | [`app-boot`](../packages/ui/app-boot) |
| [`telemetry`](../packages/sdk/telemetry) | `sdk` | [`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), [`scope`](../packages/core/scope) |
| [`system-prompt`](../packages/core/system-prompt) | `core` | [`llm`](../packages/llm/llm), [`scope`](../packages/core/scope) |
| [`web`](../packages/web/web) | `web` | [`llm`](../packages/llm/llm) |
| [`sandbox`](../packages/sandbox/sandbox) | `sandbox` | [`llm`](../packages/llm/llm) |
| [`token-meter`](../packages/llm/token-meter) | `llm` | [`llm`](../packages/llm/llm), [`session`](../packages/core/session) |
| [`agent`](../packages/core/agent) | `core` | [`brand`](../packages/util/brand), [`llm`](../packages/llm/llm), [`scope`](../packages/core/scope), [`session`](../packages/core/session), [`system-prompt`](../packages/core/system-prompt) |
| [`bash`](../packages/bash/bash) | `bash` | [`sandbox`](../packages/sandbox/sandbox) |
| [`fs`](../packages/fs/fs) | `fs` | [`brand`](../packages/util/brand), [`llm`](../packages/llm/llm), [`sandbox`](../packages/sandbox/sandbox) |
| [`compact`](../packages/compact/compact) | `compact` | [`llm`](../packages/llm/llm), [`session`](../packages/core/session) |
| [`compact-tool-result-prune`](../packages/compact/compact-tool-result-prune) | `compact` | [`llm`](../packages/llm/llm), [`session`](../packages/core/session) |
| [`web-fetch-local`](../packages/web/web-fetch-local) | `web` | [`timeout`](../packages/util/timeout), [`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) |
| [`spill`](../packages/spill/spill) | `spill` | [`brand`](../packages/util/brand), [`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) |
| [`sandbox-local`](../packages/sandbox/sandbox-local) | `sandbox` | [`llm`](../packages/llm/llm), [`sandbox`](../packages/sandbox/sandbox) |
| [`sandbox-policy`](../packages/sandbox/sandbox-policy) | `sandbox` | [`sandbox`](../packages/sandbox/sandbox), [`session`](../packages/core/session) |
| [`bash-local`](../packages/bash/bash-local) | `bash` | [`bash`](../packages/bash/bash), [`timeout`](../packages/util/timeout) |
| [`fs-local`](../packages/fs/fs-local) | `fs` | [`fs`](../packages/fs/fs) |
| [`fs-policy`](../packages/fs/fs-policy) | `fs` | [`fs`](../packages/fs/fs) |
| [`skill-local`](../packages/skill/skill-local) | `skill` | [`fs`](../packages/fs/fs), [`home`](../packages/util/home), [`skill`](../packages/skill/skill) |
| [`compact-basic`](../packages/compact/compact-basic) | `compact` | [`agent`](../packages/core/agent), [`compact`](../packages/compact/compact), [`compact-tool-result-prune`](../packages/compact/compact-tool-result-prune), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`token-meter`](../packages/llm/token-meter) |
| [`spill-local`](../packages/spill/spill-local) | `spill` | [`spill`](../packages/spill/spill) |
| [`hook-protocol`](../packages/hooks/hook-protocol) | `hooks` | [`bash`](../packages/bash/bash), [`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) |
| [`session-query`](../packages/session-query/session-query) | `session-query` | [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`session-persistence`](../packages/session-persistence/session-persistence) |
| [`invariants`](../packages/support/invariants) | `support` | [`agent`](../packages/core/agent), [`llm`](../packages/llm/llm), [`scope`](../packages/core/scope), [`session`](../packages/core/session) |
| [`user-approval`](../packages/ui/user-approval) | `ui` | [`agent`](../packages/core/agent), [`brand`](../packages/util/brand), [`llm`](../packages/llm/llm), [`scope`](../packages/core/scope), [`session`](../packages/core/session), [`system-prompt`](../packages/core/system-prompt) |
| [`user-interaction`](../packages/ui/user-interaction) | `ui` | [`agent`](../packages/core/agent), [`llm`](../packages/llm/llm) |
| [`time-context`](../packages/context/time-context) | `context` | [`agent`](../packages/core/agent) |
| [`tasks`](../packages/tasks/tasks) | `tasks` | [`agent`](../packages/core/agent), [`brand`](../packages/util/brand), [`session`](../packages/core/session), [`timeout`](../packages/util/timeout) |
| [`workflow`](../packages/workflow/workflow) | `workflow` | [`agent`](../packages/core/agent), [`brand`](../packages/util/brand), [`llm`](../packages/llm/llm), [`session`](../packages/core/session) |
| [`tools`](../packages/core/tools) | `core` | [`agent`](../packages/core/agent), [`code-runtime`](../packages/code-runtime/code-runtime), [`llm`](../packages/llm/llm), [`scope`](../packages/core/scope), [`session`](../packages/core/session), [`system-prompt`](../packages/core/system-prompt), [`user-approval`](../packages/ui/user-approval) |
| [`bash-sandbox`](../packages/bash/bash-sandbox) | `bash` | [`bash`](../packages/bash/bash), [`bash-local`](../packages/bash/bash-local), [`sandbox`](../packages/sandbox/sandbox), [`sandbox-policy`](../packages/sandbox/sandbox-policy) |
| [`fs-sandbox`](../packages/fs/fs-sandbox) | `fs` | [`fs`](../packages/fs/fs), [`fs-local`](../packages/fs/fs-local), [`sandbox`](../packages/sandbox/sandbox), [`sandbox-policy`](../packages/sandbox/sandbox-policy) |
| [`permission`](../packages/ui/permission) | `ui` | [`bash`](../packages/bash/bash), [`sandbox`](../packages/sandbox/sandbox), [`sandbox-policy`](../packages/sandbox/sandbox-policy), [`session`](../packages/core/session), [`user-approval`](../packages/ui/user-approval) |
| [`agent-loop`](../packages/core/agent-loop) | `core` | [`agent`](../packages/core/agent), [`llm`](../packages/llm/llm), [`scope`](../packages/core/scope), [`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), [`home`](../packages/util/home), [`llm`](../packages/llm/llm), [`sandbox`](../packages/sandbox/sandbox), [`sandbox-policy`](../packages/sandbox/sandbox-policy), [`session-persistence`](../packages/session-persistence/session-persistence), [`system-prompt`](../packages/core/system-prompt), [`tasks`](../packages/tasks/tasks), [`tools`](../packages/core/tools), [`user-approval`](../packages/ui/user-approval) |
| [`tool-fs`](../packages/fs/tool-fs) | `fs` | [`fs`](../packages/fs/fs), [`llm`](../packages/llm/llm), [`sandbox`](../packages/sandbox/sandbox), [`sandbox-policy`](../packages/sandbox/sandbox-policy), [`session`](../packages/core/session), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools), [`user-approval`](../packages/ui/user-approval) |
| [`tool-fs-search`](../packages/fs/tool-fs-search) | `fs` | [`bash`](../packages/bash/bash), [`llm`](../packages/llm/llm), [`retention`](../packages/util/retention), [`session`](../packages/core/session), [`spill`](../packages/spill/spill), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools) |
| [`tool-skill`](../packages/skill/tool-skill) | `skill` | [`agent`](../packages/core/agent), [`llm`](../packages/llm/llm), [`skill`](../packages/skill/skill), [`tools`](../packages/core/tools) |
| [`subagent`](../packages/subagent/subagent) | `subagent` | [`agent`](../packages/core/agent), [`brand`](../packages/util/brand), [`llm`](../packages/llm/llm), [`scope`](../packages/core/scope), [`session`](../packages/core/session), [`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) |
| [`spill-policy`](../packages/spill/spill-policy) | `spill` | [`llm`](../packages/llm/llm), [`retention`](../packages/util/retention), [`session`](../packages/core/session), [`spill`](../packages/spill/spill), [`tools`](../packages/core/tools) |
| [`timeout-policy`](../packages/timeout/timeout-policy) | `timeout` | [`llm`](../packages/llm/llm), [`timeout`](../packages/util/timeout), [`tools`](../packages/core/tools) |
| [`tool-todo`](../packages/todo/tool-todo) | `todo` | [`agent`](../packages/core/agent), [`session`](../packages/core/session), [`tools`](../packages/core/tools) |
| [`tool-cordis`](../packages/cordis/tool-cordis) | `cordis` | [`scope`](../packages/core/scope), [`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), [`session-persistence`](../packages/session-persistence/session-persistence), [`tools`](../packages/core/tools) |
| [`agent-loop-testkit`](../packages/support/agent-loop-testkit) | `support` | [`agent`](../packages/core/agent), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools) |
| [`acp`](../packages/ui/acp) | `ui` | [`agent`](../packages/core/agent), [`bash`](../packages/bash/bash), [`llm`](../packages/llm/llm), [`permission`](../packages/ui/permission), [`sandbox`](../packages/sandbox/sandbox), [`session`](../packages/core/session), [`session-persistence`](../packages/session-persistence/session-persistence), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools), [`user-approval`](../packages/ui/user-approval), [`user-interaction`](../packages/ui/user-interaction) |
| [`tool-ask-user`](../packages/ui/tool-ask-user) | `ui` | [`agent`](../packages/core/agent), [`tools`](../packages/core/tools), [`user-interaction`](../packages/ui/user-interaction) |
| [`workspace-context`](../packages/context/workspace-context) | `context` | [`agent`](../packages/core/agent), [`fs`](../packages/fs/fs), [`llm`](../packages/llm/llm), [`paths`](../packages/util/paths), [`session`](../packages/core/session), [`tools`](../packages/core/tools) |
| [`repeat-tool-guard`](../packages/guard/repeat-tool-guard) | `guard` | [`agent`](../packages/core/agent), [`tools`](../packages/core/tools) |
| [`mcp-client`](../packages/mcp/mcp-client) | `mcp` | [`llm`](../packages/llm/llm), [`tools`](../packages/core/tools) |
| [`tool-tasks`](../packages/tasks/tool-tasks) | `tasks` | [`agent`](../packages/core/agent), [`system-prompt`](../packages/core/system-prompt), [`tasks`](../packages/tasks/tasks), [`tools`](../packages/core/tools) |
| [`tool-workflow`](../packages/workflow/tool-workflow) | `workflow` | [`agent`](../packages/core/agent), [`llm`](../packages/llm/llm), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools), [`workflow`](../packages/workflow/workflow) |
| [`subagent-acp`](../packages/subagent/subagent-acp) | `subagent` | [`agent`](../packages/core/agent), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`subagent`](../packages/subagent/subagent), [`subagent-subprocess`](../packages/subagent/subagent-subprocess) |
| [`subagent-inprocess`](../packages/subagent/subagent-inprocess) | `subagent` | [`agent`](../packages/core/agent), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`subagent`](../packages/subagent/subagent), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools) |
| [`tool-subagent`](../packages/subagent/tool-subagent) | `subagent` | [`agent`](../packages/core/agent), [`llm`](../packages/llm/llm), [`subagent`](../packages/subagent/subagent), [`tasks`](../packages/tasks/tasks), [`tools`](../packages/core/tools) |
| [`hooks-claude`](../packages/hooks/hooks-claude) | `hooks` | [`agent`](../packages/core/agent), [`hook-protocol`](../packages/hooks/hook-protocol), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`session-persistence`](../packages/session-persistence/session-persistence), [`subagent`](../packages/subagent/subagent), [`tools`](../packages/core/tools) |
| [`jsonrpc`](../packages/ui/jsonrpc) | `ui` | [`agent`](../packages/core/agent), [`llm`](../packages/llm/llm), [`llm-deepseek`](../packages/llm/llm-deepseek), [`scope`](../packages/core/scope), [`session`](../packages/core/session), [`subagent`](../packages/subagent/subagent) |
| [`stdio`](../packages/ui/stdio) | `ui` | [`agent`](../packages/core/agent), [`agent-loop`](../packages/core/agent-loop), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`user-interaction`](../packages/ui/user-interaction) |
| [`tui`](../packages/ui/tui) | `ui` | [`agent`](../packages/core/agent), [`agent-loop`](../packages/core/agent-loop), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`tools`](../packages/core/tools), [`user-interaction`](../packages/ui/user-interaction) |
| [`agent-spine-demo`](../packages/examples/agent-spine-demo) | `examples` | [`agent`](../packages/core/agent), [`agent-loop`](../packages/core/agent-loop), [`home`](../packages/util/home), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`skill`](../packages/skill/skill), [`skill-local`](../packages/skill/skill-local), [`system-prompt`](../packages/core/system-prompt), [`tasks`](../packages/tasks/tasks), [`tool-bash`](../packages/bash/tool-bash), [`tool-skill`](../packages/skill/tool-skill), [`tool-tasks`](../packages/tasks/tool-tasks), [`tools`](../packages/core/tools), [`workspace-context`](../packages/context/workspace-context) |
| [`workflow-workerthread`](../packages/workflow/workflow-workerthread) | `workflow` | [`agent`](../packages/core/agent), [`brand`](../packages/util/brand), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`subagent`](../packages/subagent/subagent), [`tools`](../packages/core/tools), [`workflow`](../packages/workflow/workflow) |
| [`subagent-fork`](../packages/subagent/subagent-fork) | `subagent` | [`agent`](../packages/core/agent), [`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-demo`](../packages/examples/acp-demo) | `examples` | [`acp`](../packages/ui/acp), [`agent-spine-demo`](../packages/examples/agent-spine-demo), [`app-boot`](../packages/ui/app-boot), [`session-persistence-jsonl`](../packages/session-persistence/session-persistence-jsonl), [`tools`](../packages/core/tools), [`user-interaction`](../packages/ui/user-interaction), [`workspace-context`](../packages/context/workspace-context) |
| [`cli-demo`](../packages/examples/cli-demo) | `examples` | [`agent`](../packages/core/agent), [`agent-spine-demo`](../packages/examples/agent-spine-demo), [`app-boot`](../packages/ui/app-boot), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`session-persistence-jsonl`](../packages/session-persistence/session-persistence-jsonl), [`tools`](../packages/core/tools), [`workspace-context`](../packages/context/workspace-context) |
| [`stdio-demo`](../packages/examples/stdio-demo) | `examples` | [`agent`](../packages/core/agent), [`agent-loop`](../packages/core/agent-loop), [`agent-spine-demo`](../packages/examples/agent-spine-demo), [`app-boot`](../packages/ui/app-boot), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`session-persistence-jsonl`](../packages/session-persistence/session-persistence-jsonl), [`stdio`](../packages/ui/stdio), [`tool-ask-user`](../packages/ui/tool-ask-user), [`tools`](../packages/core/tools), [`tui`](../packages/ui/tui), [`user-interaction`](../packages/ui/user-interaction), [`workspace-context`](../packages/context/workspace-context) |
| [`invariants`](../packages/support/invariants) | `support` | — |
| [`brand`](../packages/util/brand) | `util` | [`invariants`](../packages/support/invariants) |
| [`home`](../packages/util/home) | `util` | [`invariants`](../packages/support/invariants) |
| [`paths`](../packages/util/paths) | `util` | [`invariants`](../packages/support/invariants) |
| [`retention`](../packages/util/retention) | `util` | [`invariants`](../packages/support/invariants) |
| [`timeout`](../packages/util/timeout) | `util` | [`invariants`](../packages/support/invariants) |
| [`scope`](../packages/core/scope) | `core` | [`invariants`](../packages/support/invariants) |
| [`skill`](../packages/skill/skill) | `skill` | [`invariants`](../packages/support/invariants) |
| [`subagent-subprocess`](../packages/subagent/subagent-subprocess) | `subagent` | [`invariants`](../packages/support/invariants) |
| [`acp-snapshot`](../packages/support/acp-snapshot) | `support` | [`invariants`](../packages/support/invariants) |
| [`loader-smoke`](../packages/support/loader-smoke) | `support` | [`invariants`](../packages/support/invariants) |
| [`app-boot`](../packages/ui/app-boot) | `ui` | [`invariants`](../packages/support/invariants) |
| [`code-runtime`](../packages/code-runtime/code-runtime) | `code-runtime` | [`invariants`](../packages/support/invariants) |
| [`jsonrpc-demo`](../packages/examples/jsonrpc-demo) | `examples` | [`invariants`](../packages/support/invariants) |
| [`llm`](../packages/llm/llm) | `llm` | [`brand`](../packages/util/brand), [`invariants`](../packages/support/invariants) |
| [`code-runtime-worker`](../packages/code-runtime/code-runtime-worker) | `code-runtime` | [`code-runtime`](../packages/code-runtime/code-runtime), [`invariants`](../packages/support/invariants) |
| [`helper`](../packages/sdk/helper) | `sdk` | [`brand`](../packages/util/brand), [`invariants`](../packages/support/invariants) |
| [`scripts`](../packages/sdk/scripts) | `sdk` | [`app-boot`](../packages/ui/app-boot), [`invariants`](../packages/support/invariants) |
| [`telemetry`](../packages/sdk/telemetry) | `sdk` | [`brand`](../packages/util/brand), [`invariants`](../packages/support/invariants) |
| [`llm-deepseek`](../packages/llm/llm-deepseek) | `llm` | [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`timeout`](../packages/util/timeout) |
| [`llm-pi-ai`](../packages/llm/llm-pi-ai) | `llm` | [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`timeout`](../packages/util/timeout) |
| [`session`](../packages/core/session) | `core` | [`brand`](../packages/util/brand), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`scope`](../packages/core/scope) |
| [`system-prompt`](../packages/core/system-prompt) | `core` | [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`scope`](../packages/core/scope) |
| [`web`](../packages/web/web) | `web` | [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm) |
| [`lsp`](../packages/lsp/lsp) | `lsp` | [`brand`](../packages/util/brand), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm) |
| [`sandbox`](../packages/sandbox/sandbox) | `sandbox` | [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm) |
| [`token-meter`](../packages/llm/token-meter) | `llm` | [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session) |
| [`agent`](../packages/core/agent) | `core` | [`brand`](../packages/util/brand), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`scope`](../packages/core/scope), [`session`](../packages/core/session), [`system-prompt`](../packages/core/system-prompt) |
| [`bash`](../packages/bash/bash) | `bash` | [`invariants`](../packages/support/invariants), [`sandbox`](../packages/sandbox/sandbox) |
| [`fs`](../packages/fs/fs) | `fs` | [`brand`](../packages/util/brand), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`sandbox`](../packages/sandbox/sandbox) |
| [`compact`](../packages/compact/compact) | `compact` | [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session) |
| [`compact-tool-result-prune`](../packages/compact/compact-tool-result-prune) | `compact` | [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session) |
| [`web-fetch-local`](../packages/web/web-fetch-local) | `web` | [`invariants`](../packages/support/invariants), [`timeout`](../packages/util/timeout), [`web`](../packages/web/web) |
| [`web-search-deepseek`](../packages/web/web-search-deepseek) | `web` | [`invariants`](../packages/support/invariants), [`web`](../packages/web/web) |
| [`web-search-exa`](../packages/web/web-search-exa) | `web` | [`invariants`](../packages/support/invariants), [`web`](../packages/web/web) |
| [`web-search-perplexity`](../packages/web/web-search-perplexity) | `web` | [`invariants`](../packages/support/invariants), [`web`](../packages/web/web) |
| [`spill`](../packages/spill/spill) | `spill` | [`brand`](../packages/util/brand), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session) |
| [`session-persistence`](../packages/session-persistence/session-persistence) | `session-persistence` | [`invariants`](../packages/support/invariants), [`session`](../packages/core/session) |
| [`llm-replay`](../packages/support/llm-replay) | `support` | [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session) |
| [`lsp-local`](../packages/lsp/lsp-local) | `lsp` | [`brand`](../packages/util/brand), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`lsp`](../packages/lsp/lsp), [`timeout`](../packages/util/timeout) |
| [`sandbox-local`](../packages/sandbox/sandbox-local) | `sandbox` | [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`sandbox`](../packages/sandbox/sandbox) |
| [`sandbox-policy`](../packages/sandbox/sandbox-policy) | `sandbox` | [`invariants`](../packages/support/invariants), [`sandbox`](../packages/sandbox/sandbox), [`session`](../packages/core/session) |
| [`llm-retry`](../packages/llm/llm-retry) | `llm` | [`agent`](../packages/core/agent), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`timeout`](../packages/util/timeout) |
| [`goal`](../packages/goal/goal) | `goal` | [`agent`](../packages/core/agent), [`brand`](../packages/util/brand), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`scope`](../packages/core/scope), [`session`](../packages/core/session) |
| [`bash-local`](../packages/bash/bash-local) | `bash` | [`bash`](../packages/bash/bash), [`invariants`](../packages/support/invariants), [`timeout`](../packages/util/timeout) |
| [`fs-local`](../packages/fs/fs-local) | `fs` | [`fs`](../packages/fs/fs), [`invariants`](../packages/support/invariants) |
| [`fs-policy`](../packages/fs/fs-policy) | `fs` | [`fs`](../packages/fs/fs), [`invariants`](../packages/support/invariants) |
| [`skill-local`](../packages/skill/skill-local) | `skill` | [`fs`](../packages/fs/fs), [`home`](../packages/util/home), [`invariants`](../packages/support/invariants), [`skill`](../packages/skill/skill) |
| [`compact-basic`](../packages/compact/compact-basic) | `compact` | [`agent`](../packages/core/agent), [`compact`](../packages/compact/compact), [`compact-tool-result-prune`](../packages/compact/compact-tool-result-prune), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`token-meter`](../packages/llm/token-meter) |
| [`spill-local`](../packages/spill/spill-local) | `spill` | [`invariants`](../packages/support/invariants), [`spill`](../packages/spill/spill) |
| [`hook-protocol`](../packages/hooks/hook-protocol) | `hooks` | [`bash`](../packages/bash/bash), [`invariants`](../packages/support/invariants), [`session`](../packages/core/session) |
| [`session-persistence-jsonl`](../packages/session-persistence/session-persistence-jsonl) | `session-persistence` | [`invariants`](../packages/support/invariants), [`session`](../packages/core/session), [`session-persistence`](../packages/session-persistence/session-persistence) |
| [`session-persistence-sqlite`](../packages/session-persistence/session-persistence-sqlite) | `session-persistence` | [`invariants`](../packages/support/invariants), [`session`](../packages/core/session), [`session-persistence`](../packages/session-persistence/session-persistence) |
| [`session-query`](../packages/session-query/session-query) | `session-query` | [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`session-persistence`](../packages/session-persistence/session-persistence) |
| [`commands`](../packages/ui/commands) | `ui` | [`agent`](../packages/core/agent), [`invariants`](../packages/support/invariants), [`scope`](../packages/core/scope) |
| [`user-approval`](../packages/ui/user-approval) | `ui` | [`agent`](../packages/core/agent), [`brand`](../packages/util/brand), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`scope`](../packages/core/scope), [`session`](../packages/core/session), [`system-prompt`](../packages/core/system-prompt) |
| [`user-interaction`](../packages/ui/user-interaction) | `ui` | [`agent`](../packages/core/agent), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm) |
| [`time-context`](../packages/context/time-context) | `context` | [`agent`](../packages/core/agent), [`invariants`](../packages/support/invariants), [`session`](../packages/core/session) |
| [`tasks`](../packages/tasks/tasks) | `tasks` | [`agent`](../packages/core/agent), [`brand`](../packages/util/brand), [`invariants`](../packages/support/invariants), [`session`](../packages/core/session), [`timeout`](../packages/util/timeout) |
| [`workflow`](../packages/workflow/workflow) | `workflow` | [`agent`](../packages/core/agent), [`brand`](../packages/util/brand), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session) |
| [`tools`](../packages/core/tools) | `core` | [`agent`](../packages/core/agent), [`code-runtime`](../packages/code-runtime/code-runtime), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`scope`](../packages/core/scope), [`session`](../packages/core/session), [`system-prompt`](../packages/core/system-prompt), [`user-approval`](../packages/ui/user-approval) |
| [`command-goal`](../packages/goal/command-goal) | `goal` | [`commands`](../packages/ui/commands), [`goal`](../packages/goal/goal), [`invariants`](../packages/support/invariants) |
| [`goal-session`](../packages/goal/goal-session) | `goal` | [`agent`](../packages/core/agent), [`goal`](../packages/goal/goal), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session) |
| [`bash-sandbox`](../packages/bash/bash-sandbox) | `bash` | [`bash`](../packages/bash/bash), [`bash-local`](../packages/bash/bash-local), [`invariants`](../packages/support/invariants), [`sandbox`](../packages/sandbox/sandbox), [`sandbox-policy`](../packages/sandbox/sandbox-policy) |
| [`fs-sandbox`](../packages/fs/fs-sandbox) | `fs` | [`fs`](../packages/fs/fs), [`fs-local`](../packages/fs/fs-local), [`invariants`](../packages/support/invariants), [`sandbox`](../packages/sandbox/sandbox), [`sandbox-policy`](../packages/sandbox/sandbox-policy) |
| [`permission`](../packages/ui/permission) | `ui` | [`bash`](../packages/bash/bash), [`invariants`](../packages/support/invariants), [`sandbox`](../packages/sandbox/sandbox), [`sandbox-policy`](../packages/sandbox/sandbox-policy), [`session`](../packages/core/session), [`user-approval`](../packages/ui/user-approval) |
| [`agent-loop`](../packages/core/agent-loop) | `core` | [`agent`](../packages/core/agent), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`scope`](../packages/core/scope), [`session`](../packages/core/session), [`session-persistence`](../packages/session-persistence/session-persistence), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools) |
| [`tool-goal`](../packages/goal/tool-goal) | `goal` | [`agent`](../packages/core/agent), [`goal`](../packages/goal/goal), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`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), [`home`](../packages/util/home), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`sandbox`](../packages/sandbox/sandbox), [`sandbox-policy`](../packages/sandbox/sandbox-policy), [`session-persistence`](../packages/session-persistence/session-persistence), [`system-prompt`](../packages/core/system-prompt), [`tasks`](../packages/tasks/tasks), [`tools`](../packages/core/tools), [`user-approval`](../packages/ui/user-approval) |
| [`tool-fs`](../packages/fs/tool-fs) | `fs` | [`fs`](../packages/fs/fs), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`sandbox`](../packages/sandbox/sandbox), [`sandbox-policy`](../packages/sandbox/sandbox-policy), [`session`](../packages/core/session), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools), [`user-approval`](../packages/ui/user-approval) |
| [`tool-fs-search`](../packages/fs/tool-fs-search) | `fs` | [`bash`](../packages/bash/bash), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`retention`](../packages/util/retention), [`session`](../packages/core/session), [`spill`](../packages/spill/spill), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools) |
| [`tool-skill`](../packages/skill/tool-skill) | `skill` | [`agent`](../packages/core/agent), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`skill`](../packages/skill/skill), [`tools`](../packages/core/tools) |
| [`subagent`](../packages/subagent/subagent) | `subagent` | [`agent`](../packages/core/agent), [`brand`](../packages/util/brand), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`scope`](../packages/core/scope), [`session`](../packages/core/session), [`tools`](../packages/core/tools) |
| [`tool-web`](../packages/web/tool-web) | `web` | [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools), [`web`](../packages/web/web) |
| [`spill-policy`](../packages/spill/spill-policy) | `spill` | [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`retention`](../packages/util/retention), [`session`](../packages/core/session), [`spill`](../packages/spill/spill), [`tools`](../packages/core/tools) |
| [`timeout-policy`](../packages/timeout/timeout-policy) | `timeout` | [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`timeout`](../packages/util/timeout), [`tools`](../packages/core/tools) |
| [`tool-todo`](../packages/todo/tool-todo) | `todo` | [`agent`](../packages/core/agent), [`invariants`](../packages/support/invariants), [`session`](../packages/core/session), [`tools`](../packages/core/tools) |
| [`tool-cordis`](../packages/cordis/tool-cordis) | `cordis` | [`invariants`](../packages/support/invariants), [`scope`](../packages/core/scope), [`tools`](../packages/core/tools) |
| [`hooks-codex`](../packages/hooks/hooks-codex) | `hooks` | [`agent`](../packages/core/agent), [`hook-protocol`](../packages/hooks/hook-protocol), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`session-persistence`](../packages/session-persistence/session-persistence), [`tools`](../packages/core/tools) |
| [`agent-loop-testkit`](../packages/support/agent-loop-testkit) | `support` | [`agent`](../packages/core/agent), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools) |
| [`acp`](../packages/ui/acp) | `ui` | [`agent`](../packages/core/agent), [`bash`](../packages/bash/bash), [`commands`](../packages/ui/commands), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`llm-retry`](../packages/llm/llm-retry), [`permission`](../packages/ui/permission), [`sandbox`](../packages/sandbox/sandbox), [`session`](../packages/core/session), [`session-persistence`](../packages/session-persistence/session-persistence), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools), [`user-approval`](../packages/ui/user-approval), [`user-interaction`](../packages/ui/user-interaction) |
| [`tool-ask-user`](../packages/ui/tool-ask-user) | `ui` | [`agent`](../packages/core/agent), [`invariants`](../packages/support/invariants), [`tools`](../packages/core/tools), [`user-interaction`](../packages/ui/user-interaction) |
| [`workspace-context`](../packages/context/workspace-context) | `context` | [`agent`](../packages/core/agent), [`fs`](../packages/fs/fs), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`paths`](../packages/util/paths), [`session`](../packages/core/session), [`tools`](../packages/core/tools) |
| [`repeat-tool-guard`](../packages/guard/repeat-tool-guard) | `guard` | [`agent`](../packages/core/agent), [`invariants`](../packages/support/invariants), [`tools`](../packages/core/tools) |
| [`tool-lsp`](../packages/lsp/tool-lsp) | `lsp` | [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`lsp`](../packages/lsp/lsp), [`system-prompt`](../packages/core/system-prompt), [`timeout`](../packages/util/timeout), [`tools`](../packages/core/tools) |
| [`mcp-client`](../packages/mcp/mcp-client) | `mcp` | [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`tools`](../packages/core/tools) |
| [`tool-tasks`](../packages/tasks/tool-tasks) | `tasks` | [`agent`](../packages/core/agent), [`invariants`](../packages/support/invariants), [`system-prompt`](../packages/core/system-prompt), [`tasks`](../packages/tasks/tasks), [`tools`](../packages/core/tools) |
| [`tool-workflow`](../packages/workflow/tool-workflow) | `workflow` | [`agent`](../packages/core/agent), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools), [`workflow`](../packages/workflow/workflow) |
| [`subagent-acp`](../packages/subagent/subagent-acp) | `subagent` | [`agent`](../packages/core/agent), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`subagent`](../packages/subagent/subagent), [`subagent-subprocess`](../packages/subagent/subagent-subprocess) |
| [`subagent-inprocess`](../packages/subagent/subagent-inprocess) | `subagent` | [`agent`](../packages/core/agent), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`subagent`](../packages/subagent/subagent), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools) |
| [`tool-subagent`](../packages/subagent/tool-subagent) | `subagent` | [`agent`](../packages/core/agent), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`subagent`](../packages/subagent/subagent), [`tasks`](../packages/tasks/tasks), [`tools`](../packages/core/tools) |
| [`hooks-claude`](../packages/hooks/hooks-claude) | `hooks` | [`agent`](../packages/core/agent), [`hook-protocol`](../packages/hooks/hook-protocol), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`session-persistence`](../packages/session-persistence/session-persistence), [`subagent`](../packages/subagent/subagent), [`tools`](../packages/core/tools) |
| [`jsonrpc`](../packages/ui/jsonrpc) | `ui` | [`agent`](../packages/core/agent), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`llm-deepseek`](../packages/llm/llm-deepseek), [`scope`](../packages/core/scope), [`session`](../packages/core/session), [`subagent`](../packages/subagent/subagent) |
| [`tui`](../packages/ui/tui) | `ui` | [`agent`](../packages/core/agent), [`agent-loop`](../packages/core/agent-loop), [`commands`](../packages/ui/commands), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`llm-retry`](../packages/llm/llm-retry), [`session`](../packages/core/session), [`system-prompt`](../packages/core/system-prompt), [`token-meter`](../packages/llm/token-meter), [`tools`](../packages/core/tools), [`user-interaction`](../packages/ui/user-interaction) |
| [`agent-spine-demo`](../packages/examples/agent-spine-demo) | `examples` | [`agent`](../packages/core/agent), [`agent-loop`](../packages/core/agent-loop), [`goal`](../packages/goal/goal), [`goal-session`](../packages/goal/goal-session), [`home`](../packages/util/home), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`llm-retry`](../packages/llm/llm-retry), [`scope`](../packages/core/scope), [`session`](../packages/core/session), [`skill`](../packages/skill/skill), [`skill-local`](../packages/skill/skill-local), [`system-prompt`](../packages/core/system-prompt), [`tasks`](../packages/tasks/tasks), [`tool-bash`](../packages/bash/tool-bash), [`tool-goal`](../packages/goal/tool-goal), [`tool-skill`](../packages/skill/tool-skill), [`tool-tasks`](../packages/tasks/tool-tasks), [`tools`](../packages/core/tools), [`workspace-context`](../packages/context/workspace-context) |
| [`tool-ralph`](../packages/workflow/tool-ralph) | `workflow` | [`agent`](../packages/core/agent), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`subagent`](../packages/subagent/subagent), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools), [`workflow`](../packages/workflow/workflow) |
| [`workflow-workerthread`](../packages/workflow/workflow-workerthread) | `workflow` | [`agent`](../packages/core/agent), [`brand`](../packages/util/brand), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`subagent`](../packages/subagent/subagent), [`tools`](../packages/core/tools), [`workflow`](../packages/workflow/workflow) |
| [`subagent-fork`](../packages/subagent/subagent-fork) | `subagent` | [`agent`](../packages/core/agent), [`invariants`](../packages/support/invariants), [`session`](../packages/core/session), [`subagent`](../packages/subagent/subagent), [`subagent-inprocess`](../packages/subagent/subagent-inprocess) |
| [`subagent-spawn`](../packages/subagent/subagent-spawn) | `subagent` | [`invariants`](../packages/support/invariants), [`subagent`](../packages/subagent/subagent), [`subagent-inprocess`](../packages/subagent/subagent-inprocess) |
| [`acp-demo`](../packages/examples/acp-demo) | `examples` | [`acp`](../packages/ui/acp), [`agent-spine-demo`](../packages/examples/agent-spine-demo), [`app-boot`](../packages/ui/app-boot), [`command-goal`](../packages/goal/command-goal), [`commands`](../packages/ui/commands), [`invariants`](../packages/support/invariants), [`session-persistence-jsonl`](../packages/session-persistence/session-persistence-jsonl), [`tools`](../packages/core/tools), [`user-interaction`](../packages/ui/user-interaction), [`workspace-context`](../packages/context/workspace-context) |
| [`cli-demo`](../packages/examples/cli-demo) | `examples` | [`agent`](../packages/core/agent), [`agent-spine-demo`](../packages/examples/agent-spine-demo), [`app-boot`](../packages/ui/app-boot), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`session-persistence-jsonl`](../packages/session-persistence/session-persistence-jsonl), [`tools`](../packages/core/tools), [`workspace-context`](../packages/context/workspace-context) |
| [`tui-demo`](../packages/examples/tui-demo) | `examples` | [`agent`](../packages/core/agent), [`agent-loop`](../packages/core/agent-loop), [`agent-spine-demo`](../packages/examples/agent-spine-demo), [`app-boot`](../packages/ui/app-boot), [`command-goal`](../packages/goal/command-goal), [`commands`](../packages/ui/commands), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`session-persistence-jsonl`](../packages/session-persistence/session-persistence-jsonl), [`tool-ask-user`](../packages/ui/tool-ask-user), [`tools`](../packages/core/tools), [`tui`](../packages/ui/tui), [`user-interaction`](../packages/ui/user-interaction), [`workspace-context`](../packages/context/workspace-context) |

View File

@@ -79,7 +79,7 @@ export type SessionEvent<T extends SessionEventType = SessionEventType> = {
}[T]
```
Sources: [`packages/core/session/src/types.ts:255`](../packages/core/session/src/types.ts) · [`packages/core/session/src/types.ts:262`](../packages/core/session/src/types.ts) · [`packages/core/session/src/types.ts:292`](../packages/core/session/src/types.ts) · [`packages/core/session/src/types.ts:324`](../packages/core/session/src/types.ts)
Sources: [`packages/core/session/src/types.ts:267`](../packages/core/session/src/types.ts) · [`packages/core/session/src/types.ts:274`](../packages/core/session/src/types.ts) · [`packages/core/session/src/types.ts:304`](../packages/core/session/src/types.ts) · [`packages/core/session/src/types.ts:336`](../packages/core/session/src/types.ts)
## Events
@@ -151,7 +151,7 @@ Source: [`packages/ui/user-approval/src/index.ts:68`](../packages/ui/user-approv
Types: [StreamChunk](core-data-structures/llm-streaming.md)
Source: [`packages/core/session/src/types.ts:219`](../packages/core/session/src/types.ts)
Source: [`packages/core/session/src/types.ts:231`](../packages/core/session/src/types.ts)
#### `assistant/message` — surface
@@ -167,7 +167,7 @@ Source: [`packages/core/session/src/types.ts:219`](../packages/core/session/src/
Types: [ContentBlock](core-data-structures/core.md) · [TokenUsage](core-data-structures/llm-streaming.md)
Source: [`packages/core/session/src/types.ts:226`](../packages/core/session/src/types.ts)
Source: [`packages/core/session/src/types.ts:238`](../packages/core/session/src/types.ts)
### `compact/*`
@@ -246,7 +246,7 @@ Source: [`packages/compact/compact/src/types.ts:22`](../packages/compact/compact
Types: [ContentBlock](core-data-structures/core.md) · [MessageSource](core-data-structures/core.md)
Source: [`packages/core/session/src/types.ts:213`](../packages/core/session/src/types.ts)
Source: [`packages/core/session/src/types.ts:225`](../packages/core/session/src/types.ts)
### `hook/*`
@@ -294,6 +294,24 @@ Source: [`packages/hooks/hook-protocol/src/types.ts:19`](../packages/hooks/hook-
Source: [`packages/hooks/hook-protocol/src/types.ts:31`](../packages/hooks/hook-protocol/src/types.ts)
### `llm/*`
#### `llm/retry` — log-only
```ts persistence-catalog
/** Durable, non-surface record of one transient retry scheduled after a closed failed step. */
'llm/retry': {
turn: number
step: number
retry: number
maxRetries: number
delayMs: number
failure: LlmFailure
}
```
Source: [`packages/llm/llm-retry/src/index.ts:18`](../packages/llm/llm-retry/src/index.ts)
### `permission/*`
#### `permission/preset` — log-only
@@ -317,14 +335,14 @@ Source: [`packages/ui/permission/src/index.ts:36`](../packages/ui/permission/src
```ts persistence-catalog
/**
* Durable record of a prompt veto and its reason. It is log-only: the blocked
* prompt never enters the model-visible surface, including in a mixed batch.
* prompt never enters the model-visible surface, and its turn runs zero steps.
*/
'prompt/blocked': { content: ContentBlock[]; source: MessageSource; reason: string }
```
Types: [ContentBlock](core-data-structures/core.md) · [MessageSource](core-data-structures/core.md)
Source: [`packages/core/session/src/types.ts:201`](../packages/core/session/src/types.ts)
Source: [`packages/core/session/src/types.ts:213`](../packages/core/session/src/types.ts)
### `request/*`
@@ -338,7 +356,7 @@ Source: [`packages/core/session/src/types.ts:201`](../packages/core/session/src/
'request/header': { header: EpochHeader; reason: RequestHeaderReason }
```
Source: [`packages/core/session/src/types.ts:251`](../packages/core/session/src/types.ts)
Source: [`packages/core/session/src/types.ts:263`](../packages/core/session/src/types.ts)
### `sandbox/*`
@@ -369,7 +387,7 @@ Source: [`packages/sandbox/sandbox-policy/src/session-mode.ts:34`](../packages/s
Types: [ContentBlock](core-data-structures/core.md) · [MessageSource](core-data-structures/core.md)
Source: [`packages/core/session/src/types.ts:244`](../packages/core/session/src/types.ts)
Source: [`packages/core/session/src/types.ts:256`](../packages/core/session/src/types.ts)
### `step/*`
@@ -380,7 +398,7 @@ Source: [`packages/core/session/src/types.ts:244`](../packages/core/session/src/
'step/end': { turn: number; step: number }
```
Source: [`packages/core/session/src/types.ts:194`](../packages/core/session/src/types.ts)
Source: [`packages/core/session/src/types.ts:206`](../packages/core/session/src/types.ts)
#### `step/start` — log-only
@@ -389,7 +407,7 @@ Source: [`packages/core/session/src/types.ts:194`](../packages/core/session/src/
'step/start': { turn: number; step: number }
```
Source: [`packages/core/session/src/types.ts:192`](../packages/core/session/src/types.ts)
Source: [`packages/core/session/src/types.ts:204`](../packages/core/session/src/types.ts)
### `todo/*`
@@ -402,7 +420,7 @@ Source: [`packages/core/session/src/types.ts:192`](../packages/core/session/src/
Types: [TodoItem](core-data-structures/session.md)
Source: [`packages/core/session/src/types.ts:246`](../packages/core/session/src/types.ts)
Source: [`packages/core/session/src/types.ts:258`](../packages/core/session/src/types.ts)
### `tool/*`
@@ -419,7 +437,7 @@ Source: [`packages/core/session/src/types.ts:246`](../packages/core/session/src/
Types: [CallId](core-data-structures/core.md)
Source: [`packages/core/session/src/types.ts:232`](../packages/core/session/src/types.ts)
Source: [`packages/core/session/src/types.ts:244`](../packages/core/session/src/types.ts)
#### `tool/code-dispatch` — log-only
@@ -463,7 +481,7 @@ Source: [`packages/core/tools/src/code-mode.ts:34`](../packages/core/tools/src/c
Types: [CallId](core-data-structures/core.md) · [ContentBlock](core-data-structures/core.md)
Source: [`packages/core/session/src/types.ts:242`](../packages/core/session/src/types.ts)
Source: [`packages/core/session/src/types.ts:254`](../packages/core/session/src/types.ts)
### `turn/*`
@@ -472,22 +490,23 @@ Source: [`packages/core/session/src/types.ts:242`](../packages/core/session/src/
```ts persistence-catalog
/**
* Closes turn `turn` with the {@link TurnEndReason} that ended it. The loop
* fires the awaited `session/flush` checkpoint at every turn end, so the turn
* boundary is also the durable-commit boundary.
* awaits `session/flush` after an ordinary turn ends before claiming the next
* queued item. Success commits the turn; rejection is reported live and does
* not prevent later work.
*/
'turn/end': { turn: number; reason: TurnEndReason }
```
Types: [TurnEndReason](core-data-structures/session.md)
Source: [`packages/core/session/src/types.ts:190`](../packages/core/session/src/types.ts)
Source: [`packages/core/session/src/types.ts:202`](../packages/core/session/src/types.ts)
#### `turn/start` — log-only
```ts persistence-catalog
/**
* Opens turn `turn`. `trigger` records what started it — a drained message
* batch or an idle-time injection. The turn is the durability/replay
* Opens turn `turn`. `trigger` records what started it — one claimed queued
* message or an idle-time injection. The turn is the durability/replay
* boundary: every event sits between a `turn/start` and its matching
* `turn/end` (the turn-enclosure invariant).
*/
@@ -496,17 +515,17 @@ Source: [`packages/core/session/src/types.ts:190`](../packages/core/session/src/
Types: [TurnTrigger](core-data-structures/session.md)
Source: [`packages/core/session/src/types.ts:184`](../packages/core/session/src/types.ts)
Source: [`packages/core/session/src/types.ts:195`](../packages/core/session/src/types.ts)
### `user/*`
#### `user/message` — surface
```ts persistence-catalog
/** A user-visible prompt (queued message drained at turn start). */
/** A user-visible prompt (the queued message claimed for this turn). */
'user/message': { content: ContentBlock[]; source: MessageSource }
```
Types: [ContentBlock](core-data-structures/core.md) · [MessageSource](core-data-structures/core.md)
Source: [`packages/core/session/src/types.ts:196`](../packages/core/session/src/types.ts)
Source: [`packages/core/session/src/types.ts:208`](../packages/core/session/src/types.ts)

View File

@@ -24,7 +24,7 @@ The ACP server could not create or load a single session — the two RPCs an edi
## Root cause #1 — `export default apply` drops the plugin's `inject` (broke `session/new`)
`packages/ui/acp/src/index.ts` is a *namespace plugin*: it exports `name`, `inject`, `Config`, and `apply` as separate named exports — the same shape as every other plugin in the repo (`invariants`, `llm-deepseek`, `tool-bash`, `stdio-chat`, …). But it *also* ended with one extra line no other plugin had:
`packages/ui/acp/src/index.ts` is a *namespace plugin*: it exports `name`, `inject`, `Config`, and `apply` as separate named exports — the same shape as every other plugin in the repo (`invariants`, `llm-deepseek`, `tool-bash`, `tui`, …). But it *also* ended with one extra line no other plugin had:
```ts ignore-check
export const name = 'acp'

View File

@@ -11,12 +11,14 @@ How this repo tests, tier by tier, and the rules that keep a green suite meaning
## The with-key policy: inference is cheap here
We are DeepSeek — do not ration real-API tests. A no-key test proves plumbing; only a with-key run proves the agent works against a real model. Write many: file-writing prompts, multi-turn conversations, tool use, cancellation mid-stream. Highest-value are **smoke tests** that boot the real example, send one real prompt, and check the world — they catch the "green unit tests, broken product" class that mocks structurally cannot ([postmortem 0001](postmortem/0001-acp-default-export-drops-inject.md)). The self-skip exists only so secretless CI and keyless contributors aren't blocked; it is not a cost signal. Every example ships a keyless smoke and — unless keyless-by-nature — a with-key smoke ([examples/AGENTS.md](../examples/AGENTS.md)).
We are DeepSeek — do not ration real-API tests. A no-key test proves plumbing; only a with-key run proves the agent works against a real model. Write many: file-writing prompts, multi-turn conversations, tool use, cancellation mid-stream. Highest-value are **smoke tests** that boot the real example, send one real prompt, and check the world — they catch the "green unit tests, broken product" class that mocks structurally cannot ([postmortem 0001](postmortem/0001-acp-default-export-drops-inject.md)). The self-skip exists only so secretless CI and keyless contributors aren't blocked; it is not a cost signal. Every example ships both a keyless smoke and a with-key smoke ([examples/AGENTS.md](../examples/AGENTS.md)).
## Prefer the real implementation over a mock
Mock only the genuinely expensive or non-deterministic boundary (the LLM adapter, the network, the clock); keep everything downstream real. A hand-rolled stand-in proves the bridge moves bytes, not that the shipping tool behaves as asserted — the two drift while the test stays green. Example: bridge tool-call tests run the scripted mock MODEL but the real tool + real executor (`makeBridgeHarness({ withBash: true })` plugs `dsh-bash-local` + `dsh-tool-bash` and runs an actual `echo`).
Recovery tests separate pre/post-chunk failures by step and prove failed chunks derive no message or tool side effect. Cover exhaustion, cancellation, policy composition, persistence, status, wire counts, transport-closing idle timeouts, and shipping Loader composition.
## Verify the world, not the self-report
An e2e assertion re-runs the command or re-reads the file externally; a keyword probe on the agent's own output lets a cheating agent pass. Assert untouched files are byte-identical. e2e tests own their resources: create the harness in the test, dispose in `afterEach` (even on failure/retry/timeout); shared fixtures live in a plain `tests/harness.ts`, never another `*.e2e.ts` (importing a spec re-registers its `describe` and duplicates real API calls).
@@ -35,4 +37,4 @@ An e2e assertion re-runs the command or re-reads the file externally; a keyword
## When a snapshot test is required
Any change affecting an editor-facing transcript, headless event stream, or end-to-end agent UX adds or updates a scenario in the owning snapshot suite, or states in the PR why none applies. ACP surfaces use `examples/<name>/tests/snapshots/`, a scenario table over the [`dsh-acp-snapshot`](../packages/support/acp-snapshot/README.md) suite factory (`examples/acp-agent` is primary); `examples/headless-agent` owns the `stream-json` snapshot and replay fixtures. Completed interactive-terminal journeys use JSONL-driven scenarios under `examples/tui-agent/tests/snapshots/`; transient presentation uses the package-local semantic matrix, with a PTY case when input, Loader selection, or terminal teardown changes. New capability seams, lifecycle shapes, or transcript surfaces name their coverage at every tier at plan time and verify the harness can express it — a harness gap is scheduled work, not a mid-build surprise.
Every non-trivial model- or human-visible change adds or updates a keyless scenario in the same PR through a runnable example's owning snapshot suite. Package tests, e2e assertions, mock/test-only compositions, and PR rationale do not replace the assembled transcript; extend the harness when needed. ACP surfaces use `examples/<name>/tests/snapshots/`, a scenario table over the [`dsh-acp-snapshot`](../packages/support/acp-snapshot/README.md) suite factory (`examples/acp-agent` is primary); `examples/headless-agent` owns the `stream-json` snapshot and replay fixtures. Completed interactive-terminal journeys use JSONL-driven scenarios under `examples/tui-agent/tests/snapshots/`; transient presentation uses the package-local semantic matrix, with a PTY case when input, Loader selection, or terminal teardown changes. New capability seams, lifecycle shapes, or transcript surfaces name every coverage tier at plan time and verify the harness can express it before implementation.

View File

@@ -21,8 +21,11 @@ This table connects model-visible tool names to the plugin package and service s
| `@deepseek-ai/dsh-tool-cordis` | `cordis_inspect`, `cordis_mount`, `cordis_unmount` | `ctx.tools` | `tool/call`, `tool/result`, `live plugin-tree mutations (mount/unmount)` | - | Ships in examples/cordis-agent only (a deliberate opt-in — mounted code gets the real ctx, see .agents/notes/implemented/feature/2026-07-08-self-referential-cordis-toolset.md). Plugins the model mounts may register ADDITIONAL model-visible tools at runtime; a full changed request header logs those tool-set changes. |
| `@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-fs-search` | `glob`, `grep` | `ctx.tools`, `ctx.bash`, `ctx.systemPrompt` | `tool/call`, `tool/result` | - | glob and grep are conditional bash-backed discovery tools: they register only when ctx.bash can find `rg`, then run fixed ripgrep commands through ctx.bash as ordinary foreground calls (never background tasks). Capped results save the complete formatted list through the optional ctx.spillStore backend; returned locators are follow-up-readable/searchable when the backend exposes local paths in co-located deployments. |
| `@deepseek-ai/dsh-tool-goal` | `create_goal`, `get_goal`, `update_goal` | `ctx.tools`, `ctx.agents`, `ctx.goals`, `ctx.systemPrompt`, `a calling Agent in an authorized open turn` | `tool/call`, `context/message goal snapshot for mutations`, `tool/result` | - | create, edit, pause, and resume require direct-human root authority; complete and blocked also accept the exact current goal round. The default blocked lower bound is three admitted rounds. |
| `@deepseek-ai/dsh-tool-lsp` | `lsp` | `ctx.tools`, `ctx.lsp`, `ctx.systemPrompt` | `tool/call`, `tool/result` | - | The lsp tool keeps provider selection and language-server subprocesses behind ctx.lsp, so its model-visible schema stays stable across providers. Requires a registered provider (e.g. `@deepseek-ai/dsh-lsp-local`) at runtime; without one, a query returns the structured `LSP_UNAVAILABLE` error rather than changing the schema. |
| `@deepseek-ai/dsh-tool-ralph` | `ralph` | `ctx.tools`, `ctx.workflows`, `ctx.subagents`, `ctx.systemPrompt`, `a calling Agent (exec.agent parents every fresh round)` | `tool/call`, `tool/result`, `workflow and child session events during execution` | - | A fixed foreground workflow starts one fresh structured child per round; the model selects only the immutable objective and an optional round cap. |
| `@deepseek-ai/dsh-tool-skill` | `skill` | `ctx.tools`, `ctx.skills` | `tool/call`, `tool/result` | - | - |
| `@deepseek-ai/dsh-tool-subagent` | `subagent` | `ctx.tools`, `ctx.subagents` | `tool/call`, `tool/result`, `child session events through the chosen provider` | `subagent`, `subagent_fork` | The registered tool name is the load-time `toolName` config (default `subagent`); the schema above is that default. The shipped example agents load this package once per subagent backend, so the model additionally sees `subagent_fork` (bound to the fork backend) with an identical schema — see `examples/repl-agent/cordis.yml` and `examples/acp-agent/cordis.yml`. |
| `@deepseek-ai/dsh-tool-subagent` | `subagent` | `ctx.tools`, `ctx.subagents` | `tool/call`, `tool/result`, `child session events through the chosen provider` | `subagent`, `subagent_fork` | The registered tool name is the load-time `toolName` config (default `subagent`); the schema above is that default. The shipped example agents load this package once per subagent backend, so the model additionally sees `subagent_fork` (bound to the fork backend) with an identical schema — see `examples/tui-agent/cordis.yml` and `examples/acp-agent/cordis.yml`. |
| `@deepseek-ai/dsh-tool-tasks` | `task_kill`, `task_list`, `task_output` | `ctx.tools`, `ctx.tasks`, `ctx.systemPrompt` | `tool/call`, `tool/result`, `context/message via agent.inject() for background completion notices` | - | The kind-agnostic background-task control surface: a background bash command and a background subagent are read, listed, and killed through the same three tools. Loading the plugin attaches the control surface that arms producers' `ctx.tasks.start()`. |
| `@deepseek-ai/dsh-tool-todo` | `todo_write` | `ctx.tools`, `owning Agent session` | `tool/call`, `todo/write`, `tool/result` | - | todo_write is session-owned state; UIs render the latest todo/write event as a checklist or ACP plan. |
| `@deepseek-ai/dsh-tool-workflow` | `workflow` | `ctx.tools`, `ctx.workflows`, `ctx.systemPrompt`, `a calling Agent (exec.agent parents the script children)` | `tool/call`, `tool/result` | - | - |
@@ -393,6 +396,173 @@ Source: [`packages/fs/tool-fs-search/src/index.ts`](../packages/fs/tool-fs-searc
glob and grep are conditional bash-backed discovery tools: they register only when ctx.bash can find `rg`, then run fixed ripgrep commands through ctx.bash as ordinary foreground calls (never background tasks). Capped results save the complete formatted list through the optional ctx.spillStore backend; returned locators are follow-up-readable/searchable when the backend exposes local paths in co-located deployments.
## `@deepseek-ai/dsh-tool-goal`
### `create_goal`
Create one persisted same-session completion goal when the current direct human request is a long-running objective that should continue across autonomous goal rounds. You may infer that intent without requiring the user to say "create a goal". Do not use this for trivial single-turn work. Execution rejects non-human and subagent authority.
```json
{
"type": "object",
"properties": {
"objective": {
"type": "string",
"description": "The concrete completion objective inferred from the direct human request."
},
"max_goal_rounds": {
"type": "number",
"description": "Optional positive safe-integer limit on automatic continuation rounds."
}
},
"required": [
"objective"
]
}
```
Source: [`packages/goal/tool-goal/src/index.ts`](../packages/goal/tool-goal/src/index.ts)
### `get_goal`
Read the current same-session goal, including its exact id/revision, objective, phase, completed continuation rounds, round limit, blocker reason when present, and whether another continuation is armed. Call this before updating a goal.
```json
{
"type": "object",
"properties": {}
}
```
Source: [`packages/goal/tool-goal/src/index.ts`](../packages/goal/tool-goal/src/index.ts)
### `update_goal`
Update the exact current goal revision. edit, pause, and resume require a direct top-level human request. During an automatic continuation of the current goal, complete and blocked are also allowed. blocked is rejected before the configured minimum round count; the model remains responsible for judging that the same condition persisted across those rounds and must explain it in blocked_reason.
```json
{
"type": "object",
"properties": {
"goal_id": {
"type": "string",
"description": "Exact id returned by get_goal."
},
"revision": {
"type": "number",
"description": "Exact positive revision returned by get_goal."
},
"action": {
"type": "string",
"description": "edit | pause | resume | complete | blocked",
"enum": [
"edit",
"pause",
"resume",
"complete",
"blocked"
]
},
"objective": {
"type": "string",
"description": "Replacement objective; valid only with action edit."
},
"max_goal_rounds": {
"type": "number",
"description": "Replacement cap; valid only with action edit."
},
"blocked_reason": {
"type": "string",
"description": "Concrete blocking condition; required only with action blocked."
}
},
"required": [
"goal_id",
"revision",
"action"
]
}
```
Source: [`packages/goal/tool-goal/src/index.ts`](../packages/goal/tool-goal/src/index.ts)
create, edit, pause, and resume require direct-human root authority; complete and blocked also accept the exact current goal round. The default blocked lower bound is three admitted rounds.
## `@deepseek-ai/dsh-tool-lsp`
### `lsp`
Query a language server for precise code navigation. operation is one of goToDefinition, findReferences, goToImplementation, hover. line and character are one-based UTF-16 cursor coordinates. findReferences includes the declaration.
```json
{
"type": "object",
"properties": {
"operation": {
"type": "string",
"description": "goToDefinition, findReferences, goToImplementation, or hover.",
"enum": [
"goToDefinition",
"findReferences",
"goToImplementation",
"hover"
]
},
"file_path": {
"type": "string",
"description": "The source file to query, relative to the workspace or absolute."
},
"line": {
"type": "number",
"description": "One-based line of the cursor."
},
"character": {
"type": "number",
"description": "One-based UTF-16 column of the cursor."
}
},
"required": [
"operation",
"file_path",
"line",
"character"
]
}
```
Source: [`packages/lsp/tool-lsp/src/index.ts`](../packages/lsp/tool-lsp/src/index.ts)
The lsp tool keeps provider selection and language-server subprocesses behind ctx.lsp, so its model-visible schema stays stable across providers. Requires a registered provider (e.g. `@deepseek-ai/dsh-lsp-local`) at runtime; without one, a query returns the structured `LSP_UNAVAILABLE` error rather than changing the schema.
## `@deepseek-ai/dsh-tool-ralph`
### `ralph`
Run a foreground fresh-agent Ralph loop toward one immutable objective. Use only when the direct human explicitly asks for Ralph or fresh-agent iteration. Each round opens a new child with no parent conversation or prior child session; the shared workspace is long-term memory, and only a bounded structured report crosses rounds. The call returns when a worker reports completion or a concrete blocker, or at the round limit. Ordinary long-running same-session work belongs to goal tools.
```json
{
"type": "object",
"properties": {
"objective": {
"type": "string",
"description": "The immutable completion objective for every fresh Ralph round."
},
"maxRounds": {
"type": "number",
"description": "Optional positive safe-integer round cap, bounded by the deployment ceiling."
}
},
"required": [
"objective"
]
}
```
Source: [`packages/workflow/tool-ralph/src/index.ts`](../packages/workflow/tool-ralph/src/index.ts)
A fixed foreground workflow starts one fresh structured child per round; the model selects only the immutable objective and an optional round cap.
## `@deepseek-ai/dsh-tool-skill`
### `skill`
@@ -448,7 +618,7 @@ Delegate a self-contained task to a subagent (a separate agent that works in its
Source: [`packages/subagent/tool-subagent/src/index.ts`](../packages/subagent/tool-subagent/src/index.ts)
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/repl-agent/cordis.yml` and `examples/acp-agent/cordis.yml`.
The registered tool name is the load-time `toolName` config (default `subagent`); the schema above is that default. The shipped example agents load this package once per subagent backend, so the model additionally sees `subagent_fork` (bound to the fork backend) with an identical schema — see `examples/tui-agent/cordis.yml` and `examples/acp-agent/cordis.yml`.
## `@deepseek-ai/dsh-tool-tasks`

View File

@@ -2,5 +2,5 @@
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write
index.md: 5fa46806bc195ad2566fc0a29b45eb1dd7a68179
index.zh.md: a6d238c12841c8c25b00376ee032e5db50fc6b4e
index.md: d7d657ff7b8cb9001dd5e9c3af658a7a3c45b5b7
index.zh.md: 7a134f7aaed470b87ee8ca8978dd39593de2651b

View File

@@ -122,24 +122,24 @@ Function form is sufficient in most cases. Use class form when the plugin provid
## Complete example
`examples/echo-agent/src/echo-tool.ts` is a plugin that registers a tool:
A minimal tool plugin registers its definition on `ctx.tools`:
```ts
import type { Context } from 'cordis'
import { defineTool } from '@deepseek-ai/dsh-tools'
export const name = 'echo-tool'
export const name = 'greet-tool'
export const inject = ['tools']
export function apply(ctx: Context) {
ctx.tools.register(defineTool({
name: 'echo',
description: 'Echo the given text back, uppercased.',
name: 'greet',
description: 'Greet the named person.',
parameters: {
text: { type: 'string', required: true },
name: { type: 'string', required: true },
},
async execute(args) {
return [{ type: 'text', text: `ECHO: ${args.text.toUpperCase()}` }]
return [{ type: 'text', text: `Hello, ${args.name}!` }]
},
}))
}

View File

@@ -122,24 +122,24 @@ export default class MyService extends Service {
## 完整示例
参考仓库中的 `examples/echo-agent/src/echo-tool.ts`,这是一个注册 tool 的插件
最小化的工具插件会在 `ctx.tools` 上注册其定义
```ts
import type { Context } from 'cordis'
import { defineTool } from '@deepseek-ai/dsh-tools'
export const name = 'echo-tool'
export const name = 'greet-tool'
export const inject = ['tools']
export function apply(ctx: Context) {
ctx.tools.register(defineTool({
name: 'echo',
description: 'Echo the given text back, uppercased.',
name: 'greet',
description: 'Greet the named person.',
parameters: {
text: { type: 'string', required: true },
name: { type: 'string', required: true },
},
async execute(args) {
return [{ type: 'text', text: `ECHO: ${args.text.toUpperCase()}` }]
return [{ type: 'text', text: `Hello, ${args.name}!` }]
},
}))
}

View File

@@ -2,5 +2,5 @@
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write
llm-adapter.md: f34fc9e1d5b59a323bb562764821ef910025880e
llm-adapter.zh.md: 3c781ae8a1a011e2f73d5f6de43f6f75e1fb549f
llm-adapter.md: 3e83289b8072ef231f83c0fa3cfe3260547b42fa
llm-adapter.zh.md: 92fcf9b22f4bb356ada4c46f9a03ef0cc2d159da

View File

@@ -131,10 +131,12 @@ The first argument lists the model names handled by the adapter. If `cordis.yml`
- my-model-v1
- my-model-v2
- id: stdio-agent
name: '@deepseek-ai/dsh-stdio-demo'
- id: tui-agent
name: '@deepseek-ai/dsh-tui-demo'
config:
provider: my-llm
model: my-model-v1 # References the model registered above.
workspaceContext: false
```
## Reference implementations
@@ -143,9 +145,8 @@ The repository contains complete implementations:
- `packages/llm/llm-deepseek/` — DeepSeek API adapter using the OpenAI-compatible format
- `packages/llm/llm-pi-ai/` — Pi AI adapter using a different API format
- `examples/echo-agent/src/mock-llm.ts` — minimal local teaching adapter
Start with the mock adapter to study a complete chunk sequence without network behavior.
Compare the two shipped adapters to see the same harness contract implemented over different provider SDKs.
## Error handling

View File

@@ -131,10 +131,12 @@ ctx.llm.registerAdapter(['model-name-1', 'model-name-2'], adapter)
- my-model-v1
- my-model-v2
- id: stdio-agent
name: '@deepseek-ai/dsh-stdio-demo'
- id: tui-agent
name: '@deepseek-ai/dsh-tui-demo'
config:
provider: my-llm
model: my-model-v1 # References the model registered above.
workspaceContext: false
```
## 实战参考
@@ -143,9 +145,8 @@ ctx.llm.registerAdapter(['model-name-1', 'model-name-2'], adapter)
- `packages/llm/llm-deepseek/` — DeepSeek API 适配器OpenAI 兼容格式)
- `packages/llm/llm-pi-ai/` — Pi AI 适配器(不同的 API 格式)
- `examples/echo-agent/src/mock-llm.ts` — 最简 mock 适配器(教学用)
mock 适配器是学习 StreamChunk 协议的最佳起点——它用纯本地逻辑演示了完整的 chunk 序列
对比这两个已交付的适配器,可以看到同一套 harness 契约如何在不同提供方 SDK 之上实现
## 错误处理

View File

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

View File

@@ -8,8 +8,8 @@ Harness uses `cordis.yml` to describe which plugins an agent loads and the confi
The repository examples are runnable configurations and the most reliable starting points for a new project:
- [echo-agent](../../../examples/echo-agent/cordis.yml) uses a local mock model and needs no API key.
- [repl-agent](../../../examples/repl-agent/cordis.yml) combines the DeepSeek model, Bash, filesystem, compaction, subagents, and workflows.
- [tui-agent](../../../examples/tui-agent/cordis.yml) combines the DeepSeek model, Bash, filesystem, compaction, subagents, workflows, and the interactive TUI.
- [headless-agent](../../../examples/headless-agent/cordis.yml) exposes the coding composition as a one-shot task.
- [acp-agent](../../../examples/acp-agent/cordis.yml) connects to editor clients over ACP.
A minimal configuration is a list of plugin entries:
@@ -22,10 +22,15 @@ A minimal configuration is a list of plugin entries:
models:
- deepseek-v4-flash
- id: stdio-agent
name: '@deepseek-ai/dsh-stdio-demo'
- id: bash
name: '@deepseek-ai/dsh-bash-local'
- id: tui-agent
name: '@deepseek-ai/dsh-tui-demo'
config:
provider: deepseek
model: deepseek-v4-flash
workspaceContext: false
```
## Plugin entries

View File

@@ -8,8 +8,8 @@ Harness 使用 `cordis.yml` 描述 Agent 加载哪些插件以及每个插件的
仓库中的示例就是可以运行的配置,也是新项目最可靠的起点:
- [echo-agent](../../../examples/echo-agent/cordis.yml) 使用本地 mock 模型,不需要 API key
- [repl-agent](../../../examples/repl-agent/cordis.yml) 组合 DeepSeek 模型、Bash、文件系统、压缩、子代理和工作流
- [tui-agent](../../../examples/tui-agent/cordis.yml) 组合 DeepSeek 模型、Bash、文件系统、压缩、子代理、工作流和交互式 TUI
- [headless-agent](../../../examples/headless-agent/cordis.yml) 以单次任务形式暴露 coding 组装
- [acp-agent](../../../examples/acp-agent/cordis.yml) 通过 ACP 接入编辑器客户端。
最小配置由一组插件条目组成:
@@ -22,10 +22,15 @@ Harness 使用 `cordis.yml` 描述 Agent 加载哪些插件以及每个插件的
models:
- deepseek-v4-flash
- id: stdio-agent
name: '@deepseek-ai/dsh-stdio-demo'
- id: bash
name: '@deepseek-ai/dsh-bash-local'
- id: tui-agent
name: '@deepseek-ai/dsh-tui-demo'
config:
provider: deepseek
model: deepseek-v4-flash
workspaceContext: false
```
## 插件条目

View File

@@ -2,5 +2,5 @@
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write
index.md: a20b1041e13b01b6b1d01a5baa8975d3e68c6aa0
index.zh.md: 56ec50352218e2e28ad2dd7a6ef387376de75606
index.md: b698b8aeee6cebff374e20ca0f76ddc9e75213c0
index.zh.md: 337d246baa12ccf6d7a9656d1ea3b06002554c13

View File

@@ -14,10 +14,12 @@ Harness implements every capability an AI agent needs—including LLM calls, too
config:
apiKey: !!js process.env.DEEPSEEK_API_KEY
# Select the application template
- name: '@deepseek-ai/dsh-stdio-demo'
# Select the interactive application
- name: '@deepseek-ai/dsh-tui-demo'
config:
provider: deepseek
model: deepseek-v4-flash
workspaceContext: false
```
## Who it is for

View File

@@ -14,10 +14,12 @@ Harness 将一个 AI Agent智能体 所需要的所有能力——LLM 调
config:
apiKey: !!js process.env.DEEPSEEK_API_KEY
# Select the application template
- name: '@deepseek-ai/dsh-stdio-demo'
# Select the interactive application
- name: '@deepseek-ai/dsh-tui-demo'
config:
provider: deepseek
model: deepseek-v4-flash
workspaceContext: false
```
## 适合谁

View File

@@ -2,5 +2,5 @@
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write
quickstart.md: acae2ac095e057971043c2bcece7a52d3ebc1c2c
quickstart.zh.md: 54643fe54e62dbbd3696362cb43ff8569577c53b
quickstart.md: 25ce51ee3d010d2eb800071b9697fc62857dace1
quickstart.zh.md: e2e023670a999566e273d8893c42103dc273e7b1

View File

@@ -7,91 +7,52 @@ This guide gets an agent running in five minutes.
## Prerequisites
- [Node.js](https://nodejs.org/) ^22.19 or >= 24
- [pnpm](https://pnpm.io/) 11 (use Corepack to select the repository-pinned version)
- [pnpm](https://pnpm.io/) 11 through Corepack
- A [DeepSeek Platform](https://platform.deepseek.com/) API key
```sh
# Check versions
node -v # v22.19.x, or v24.x and newer
node -v
corepack enable
pnpm -v # 11.x
pnpm -v
```
## Step 1: run echo-agent
echo-agent needs no API key and runs after dependencies are installed.
## Step 1: install and configure the API key
```sh
# Clone the repository
git clone https://github.com/deepseek-harness/deepseek-harness.git
cd deepseek-harness
# Install dependencies
pnpm install
# Start echo-agent
pnpm run demo:echo
```
The process prints:
```
echo-agent ready. Type a message ("echo <text>" triggers the tool).
>
```
Enter:
```
> echo hello world
```
The model issues a tool call, and the echo tool returns the text in uppercase:
```
[tool call] echo({"text":"hello world"})
[tool result] ECHO: HELLO WORLD
```
Your local environment is ready.
## Step 2: use a real model
Next, connect a real DeepSeek model and run the complete command-line agent.
### Get an API key
Get an API key from [DeepSeek Platform](https://platform.deepseek.com/).
### Configure the environment
Create a gitignored `.env` file in the repository root:
Create the gitignored repository-root `.env`:
```sh
DEEPSEEK_API_KEY=sk-your-key-here
```
### Start repl-agent
## Step 2: run one Headless task
Run a non-interactive task and print its final answer:
```sh
pnpm run demo:repl
pnpm run demo:headless "summarize the architecture of this workspace"
```
```
agent REPL ready. Give it a coding task.
>
Headless runs one complete model/tool turn, persists the session, prints the result, and exits. Use `--output-format stream-json` when you need the canonical event stream.
## Step 3: use the TUI
Start the interactive coding agent:
```sh
pnpm run demo:tui
```
This is a complete coding assistant that can read and write files, run commands, and delegate subtasks.
Try a task:
```
> Create hello.js in the current directory, print "Hello from Harness!", and run it
```
The full-screen agent can read and write files, run commands, delegate subtasks, and track a plan. Try: `Create hello.js in the current directory, print "Hello from Harness!", and run it`.
## What happened
echo-agent and repl-agent use the same application framework (`@deepseek-ai/dsh-stdio-demo`). Their `cordis.yml` files select different plugins and configuration. Custom agents use the same composition model.
headless-agent uses the `@deepseek-ai/dsh-cli-demo` app; tui-agent uses the interactive `@deepseek-ai/dsh-tui-demo` app. Both load the same providerless agent spine, while their `cordis.yml` files select the DeepSeek model and capability plugins appropriate to each surface.
## Next steps

View File

@@ -7,93 +7,54 @@
## 环境准备
- [Node.js](https://nodejs.org/) ^22.19 或 >= 24
- [pnpm](https://pnpm.io/) 11(建议通过 Corepack 使用仓库固定的版本)
- 通过 Corepack 使用 [pnpm](https://pnpm.io/) 11
- [DeepSeek Platform](https://platform.deepseek.com/) API key
```sh
# Check versions
node -v # v22.19.x, or v24.x and newer
node -v
corepack enable
pnpm -v # 11.x
pnpm -v
```
## 第一步:运行 echo-agent
echo-agent 不需要 API key装好依赖就能跑。
## 第一步:安装并配置 API key
```sh
# Clone the repository
git clone https://github.com/deepseek-harness/deepseek-harness.git
cd deepseek-harness
# Install dependencies
pnpm install
# Start echo-agent
pnpm run demo:echo
```
启动后你会看到
```
echo-agent ready. Type a message ("echo <text>" triggers the tool).
>
```
试着输入:
```
> echo hello world
```
你会看到模型发起了一次 tool call工具调用echo 工具将文本转为大写并返回:
```
[tool call] echo({"text":"hello world"})
[tool result] ECHO: HELLO WORLD
```
恭喜!环境没问题。
## 第二步:使用真实模型调用
接下来接入真实的 DeepSeek 模型,跑一个完整的命令行 Agent。
### 获取 API Key
前往 [DeepSeek Platform](https://platform.deepseek.com/) 获取你的 API key。
### 配置环境变量
在仓库根目录创建 `.env` 文件(已被 gitignore
在仓库根目录创建已被 Git 忽略的 `.env`
```sh
DEEPSEEK_API_KEY=sk-your-key-here
```
### 启动 repl-agent
## 第二步:运行一个 Headless 任务
运行一个非交互式任务并打印最终回答:
```sh
pnpm run demo:repl
pnpm run demo:headless "summarize the architecture of this workspace"
```
```
agent REPL ready. Give it a coding task.
>
Headless 运行一个完整的模型/工具轮次,持久化会话,打印结果后退出。需要规范事件流时可使用 `--output-format stream-json`
## 第三步:使用 TUI
启动交互式 coding agent
```sh
pnpm run demo:tui
```
就是一个完整的编程助手,它能读写文件、命令、分子任务。
试着给它一个任务:
```
> Create hello.js in the current directory, print "Hello from Harness!", and run it
```
个全屏 Agent 可以读写文件、运行命令、分子任务和跟踪计划。可以尝试:`Create hello.js in the current directory, print "Hello from Harness!", and run it`
## 回头看
echo-agent 和 repl-agent 用的是同一个应用框架(`@deepseek-ai/dsh-stdio-demo`),区别只在 `cordis.yml`——换了哪些插件、填了什么配置。你以后定制自己的 Agent 也是同样的方式
headless-agent 使用 `@deepseek-ai/dsh-cli-demo` apptui-agent 使用交互式 `@deepseek-ai/dsh-tui-demo` app。二者加载同一个 providerless agent spine并通过各自的 `cordis.yml` 为对应 surface 选择 DeepSeek 模型和能力插件
## 下一步
- [配置文件](./config.md) — 了解 `cordis.yml`完整语法
- [开发插件](../develop/basic/) — 编写自己的 tool 或后端
- [配置文件](./config.md) — 了解 `cordis.yml`格式
- [开发插件](../develop/basic/) — 编写自己的 tool 或后端