Procházet zdrojové kódy

Merge pull request #441 from deepseek-harness/codex/remove-stdio-agent

Remove the stdio and Echo agents
Tianyi Cui před 1 měsícem
rodič
revize
7b7d6f38e8
100 změnil soubory, kde provedl 456 přidání a 1120 odebrání
  1. 9 9
      .agents/notes/implemented/architecture/2026-06-20-extract-example-app-packages.md
  2. 2 0
      .agents/notes/implemented/architecture/2026-06-20-package-hierarchy.md
  3. 2 2
      .agents/notes/implemented/architecture/2026-07-05-prompt-variables-and-tool-guidance-ownership.md
  4. 1 1
      .agents/notes/implemented/architecture/2026-07-08-tool-output-spill-files.md
  5. 1 1
      .agents/notes/implemented/feature/2026-06-18-compaction-capability-seam.md
  6. 3 3
      .agents/notes/implemented/feature/2026-06-25-ask-user-question.md
  7. 1 1
      .agents/notes/implemented/feature/2026-07-06-explicit-tool-order.md
  8. 1 1
      .agents/notes/implemented/feature/2026-07-09-bash-backed-grep-glob-discovery.md
  9. 2 2
      .agents/notes/implemented/feature/2026-07-17-dedicated-full-screen-tui-front-door.i18n.yaml
  10. 4 4
      .agents/notes/implemented/feature/2026-07-17-dedicated-full-screen-tui-front-door.md
  11. 4 4
      .agents/notes/implemented/feature/2026-07-17-dedicated-full-screen-tui-front-door.zh.md
  12. 1 1
      .agents/notes/implemented/process/2026-06-11-quality-gates.md
  13. 1 1
      .agents/notes/implemented/process/2026-06-16-pnpm-over-yarn.md
  14. 4 3
      .agents/notes/implemented/process/2026-07-03-documentation-graph-atlas.md
  15. 1 1
      .agents/notes/implemented/process/2026-07-06-node-engine-floor.md
  16. 1 1
      .agents/notes/implemented/simplification/2026-06-20-drop-unconsumed-llm-adapter-change-event.md
  17. 2 0
      .agents/notes/implemented/simplification/2026-07-04-fold-stdio-ui-helper.md
  18. 6 0
      .agents/notes/implemented/simplification/2026-07-20-remove-stdio-and-echo-agents.i18n.yaml
  19. 45 0
      .agents/notes/implemented/simplification/2026-07-20-remove-stdio-and-echo-agents.md
  20. 45 0
      .agents/notes/implemented/simplification/2026-07-20-remove-stdio-and-echo-agents.zh.md
  21. 2 2
      .agents/notes/implemented/testing/2026-07-18-tui-terminal-state-snapshots.i18n.yaml
  22. 1 1
      .agents/notes/implemented/testing/2026-07-18-tui-terminal-state-snapshots.md
  23. 1 1
      .agents/notes/implemented/testing/2026-07-18-tui-terminal-state-snapshots.zh.md
  24. 2 2
      .agents/notes/proposed/feature/2026-07-14-sdk-developer-projects.i18n.yaml
  25. 6 6
      .agents/notes/proposed/feature/2026-07-14-sdk-developer-projects.md
  26. 6 6
      .agents/notes/proposed/feature/2026-07-14-sdk-developer-projects.zh.md
  27. 2 2
      .agents/skills/dsh-pre-push-checks/SKILL.md
  28. 4 11
      AGENTS.md
  29. 2 2
      README.i18n.yaml
  30. 5 6
      README.md
  31. 5 6
      README.zh.md
  32. 1 1
      docs/architecture.md
  33. 7 6
      docs/capability-seams.md
  34. 44 84
      docs/config-catalog.md
  35. 2 2
      docs/cookbook/adding-a-tool.i18n.yaml
  36. 1 1
      docs/cookbook/adding-a-tool.md
  37. 1 1
      docs/cookbook/adding-a-tool.zh.md
  38. 2 2
      docs/cookbook/extension-cookbook.i18n.yaml
  39. 1 1
      docs/cookbook/extension-cookbook.md
  40. 1 1
      docs/cookbook/extension-cookbook.zh.md
  41. 1 1
      docs/core-data-structures/user-interaction.md
  42. 2 2
      docs/development.i18n.yaml
  43. 5 11
      docs/development.md
  44. 5 11
      docs/development.zh.md
  45. 6 6
      docs/event-producer-consumer.md
  46. 0 2
      docs/graph-atlas.md
  47. 3 3
      docs/i18n/translation-prompt.md
  48. 14 22
      docs/module-graph.md
  49. 1 1
      docs/postmortem/0001-acp-default-export-drops-inject.md
  50. 1 1
      docs/testing.md
  51. 2 2
      docs/tool-catalog.md
  52. 2 2
      docs/user/develop/basic/index.i18n.yaml
  53. 6 6
      docs/user/develop/basic/index.md
  54. 6 6
      docs/user/develop/basic/index.zh.md
  55. 2 2
      docs/user/develop/practice/llm-adapter.i18n.yaml
  56. 5 4
      docs/user/develop/practice/llm-adapter.md
  57. 5 4
      docs/user/develop/practice/llm-adapter.zh.md
  58. 2 2
      docs/user/guide/config.i18n.yaml
  59. 9 4
      docs/user/guide/config.md
  60. 9 4
      docs/user/guide/config.zh.md
  61. 2 2
      docs/user/guide/index.i18n.yaml
  62. 4 2
      docs/user/guide/index.md
  63. 4 2
      docs/user/guide/index.zh.md
  64. 2 2
      docs/user/guide/quickstart.i18n.yaml
  65. 18 57
      docs/user/guide/quickstart.md
  66. 20 59
      docs/user/guide/quickstart.zh.md
  67. 1 3
      examples/AGENTS.md
  68. 4 23
      examples/README.md
  69. 1 1
      examples/cordis-agent/README.md
  70. 6 6
      examples/cordis-agent/composition.md
  71. 3 3
      examples/cordis-agent/cordis.yml
  72. 9 13
      examples/cordis-agent/tests/keyless-smoke.e2e.ts
  73. 0 34
      examples/echo-agent/README.md
  74. 0 43
      examples/echo-agent/composition.md
  75. 0 43
      examples/echo-agent/cordis.yml
  76. 0 7
      examples/echo-agent/package.json
  77. 0 19
      examples/echo-agent/src/echo-tool.ts
  78. 0 59
      examples/echo-agent/src/mock-llm.ts
  79. 0 43
      examples/echo-agent/tests/echo.e2e.ts
  80. 1 1
      examples/headless-agent/README.md
  81. 0 0
      examples/headless-agent/tests/code-mode.e2e.ts
  82. 0 0
      examples/headless-agent/tests/coding-task.e2e.ts
  83. 0 0
      examples/headless-agent/tests/compaction.e2e.ts
  84. 16 0
      examples/headless-agent/tests/fixtures/time-context-driver.ts
  85. 22 0
      examples/headless-agent/tests/fixtures/time-context-mock-llm.ts
  86. 6 7
      examples/headless-agent/tests/fixtures/time-context.cordis.yml
  87. 0 0
      examples/headless-agent/tests/full-loop.e2e.ts
  88. 1 1
      examples/headless-agent/tests/harness.ts
  89. 0 0
      examples/headless-agent/tests/resume.e2e.ts
  90. 0 0
      examples/headless-agent/tests/todo-write.e2e.ts
  91. 2 1
      examples/package.json
  92. 0 68
      examples/repl-agent/README.md
  93. 0 33
      examples/repl-agent/code-mode.cordis.yml
  94. 0 91
      examples/repl-agent/composition.md
  95. 0 145
      examples/repl-agent/cordis.yml
  96. 0 7
      examples/repl-agent/package.json
  97. 0 27
      examples/repl-agent/tests/code-mode-keyless-smoke.e2e.ts
  98. 0 28
      examples/repl-agent/tests/keyless-smoke.e2e.ts
  99. 6 6
      examples/tui-agent/README.md
  100. 10 9
      examples/tui-agent/code-mode.cordis.yml

+ 9 - 9
.agents/notes/implemented/architecture/2026-06-20-extract-example-app-packages.md

@@ -6,29 +6,28 @@ Status: implemented
 
 An example folder is supposed to be *thin* — the variable wiring of a demo, not the demo's machinery. Before this change it was thick. Each example carried a hand-rolled `start.ts` boot bootstrap, an infra preamble (`timer`, and — for the stdio demos — `logger` + `hmr`), nested includes of three shared YAML fragments (`base.yml` / `base-core.yml` / `acp-agent/acp-tail.yml`), and per-example `agent-loop`/persistence/system-prompt config. The actual app — the spine of services every agent needs — was spread across the leaf and those includes.
 
-The leaf configs also owned a coupled front door. ACP requires stdout purity and creates agents through `session/new`; stdio requires a console logger and a pre-created `main`. Prose warnings were the only guard against combining these incorrectly, while three `start.ts` files duplicated the Loader bootstrap and lifecycle code.
+The leaf configs also owned coupled front doors. ACP requires stdout purity and creates agents through `session/new`; terminal and Headless apps pre-create `main` but have different process I/O contracts. Prose warnings were the only guard against combining these incorrectly, while three `start.ts` files duplicated the Loader bootstrap and lifecycle code.
 
 ## Decision
 
 Each example is now **mostly an invocation of an app package**, splitting the wiring along the existing [interface / implementation / consumer seam](2026-06-13-capability-seams.md): the **app package owns the composition**, the leaf `cordis.yml` owns only the **swappable choices** (which LLM adapter, which bash executor, model, prompt, persistence root).
 
 - **`@deepseek-ai/dsh-agent-spine-demo`** ([packages/examples/agent-spine-demo](../../../../packages/examples/agent-spine-demo)) composes the providerless, executor-less, UI-less spine and forwards the loop's agent-list config. Its dependency on the concrete loop is intentional because this package composes the spine rather than extending it; swapping the loop means supplying another bundle.
-- **`@deepseek-ai/dsh-stdio-demo`** ([packages/examples/stdio-demo](../../../../packages/examples/stdio-demo)) and **`@deepseek-ai/dsh-acp-demo`** ([packages/examples/acp-demo](../../../../packages/examples/acp-demo)) bake in their front doors. Stdio includes `ui-stdio`, a console logger, and `main`; ACP includes the bridge and JSONL persistence but no stdout logger or pre-created agent. Leaves may add plugins, but the safe composition is now the default artifact.
-- **`start.ts` is gone.** Each app package exposes a `bin` (`dsh-stdio-demo` / `dsh-acp-demo`); the `demo:*` scripts invoke it (e.g. `dsh-stdio-demo ./cordis.yml`). The Loader-boot tail, `.env` loading, and fail-loud guards live in the shared [`@deepseek-ai/dsh-app-boot`](../../../../packages/ui/app-boot) package (unit-tested under the per-file coverage gate — see [share the app bins' boot glue](../simplification/2026-07-04-share-app-bin-boot-glue.md)); each bin is a thin self-executing composition over those helpers plus its app-specific lifecycle (the ACP bin: snapshot-mode selection and stdin-dispose). The `bin.ts` files themselves stay coverage-excluded (self-executing CLI entries, like the old `start.ts`) and are driven by the keyless Loader-path tests.
-- **Each leaf `cordis.yml` collapses** to backends + config: the LLM adapter (`llm-deepseek` with apiKey/models, or `llm-replay`), the bash executor (`bash-local`), `hmr` for the stdio demos (see the amendment below), and one app entry carrying the app's config (model, system prompt, persistence root — surfaced as the app package's own `Config`, which routes each value to wherever the app wires it: stdio onto its pre-created agent, acp onto the bridge plugin).
-- **echo-agent folds onto `dsh-stdio-demo`**, swapping the LLM backend to the local `mock-llm` and adding the local `echo-tool` (plus `bash-local`, which the spine's `tool-bash` injects) at the leaf — the clean demonstration of "swap the backend, keep the app". `mock-llm.ts` / `echo-tool.ts` stay as example-local teaching plugins.
+- **`@deepseek-ai/dsh-tui-demo`**, **`@deepseek-ai/dsh-cli-demo`**, and **`@deepseek-ai/dsh-acp-demo`** bake in their process roles. TUI includes the full-screen UI and a pre-created `main`; Headless includes the one-shot driver and a pre-created `main`; ACP includes the bridge and no pre-created agent. All three include JSONL persistence and omit stdout loggers.
+- **`start.ts` is gone.** Each app package exposes a bin; the `demo:*` scripts invoke it. Loader boot, `.env` loading, and fail-loud guards live in the shared [`@deepseek-ai/dsh-app-boot`](../../../../packages/ui/app-boot) package (unit-tested under the per-file coverage gate — see [share the app bins' boot glue](../simplification/2026-07-04-share-app-bin-boot-glue.md)); the thin self-executing entries are driven by keyless Loader-path tests.
+- **Each leaf `cordis.yml` collapses** to backends, optional product tools, and one app entry carrying the app config. TUI and Headless route model/session choices onto a pre-created agent; ACP routes the initial provider/model onto its bridge.
 - **`base.yml`, `base-core.yml`, and `acp-agent/acp-tail.yml` are retired** — the spine they shared now lives in `dsh-agent-spine-demo`.
 
 `bash-local` and the LLM adapter stay **leaf choices**: the bundle ships `tool-bash` (the consumer schema), the leaf picks the executor implementation, so a sandboxed executor or replay adapter swaps in without touching the app.
 
 ### Amendment on implementation: `hmr` stays a leaf entry
 
-The proposal listed `hmr` among the stdio app's baked-in front-door cluster. Validating against the code, baking `hmr` into the `dsh-stdio-demo` package fights cordis in two ways, so it ships as a **leaf `cordis.yml` entry** instead:
+The proposal listed `hmr` among the interactive app's baked-in front-door cluster. Validating against the code, baking `hmr` into the app package fights Cordis in two ways, so it ships as a **leaf `cordis.yml` entry** instead:
 
 1. `@cordisjs/plugin-hmr` is a Loader-only, subprocess-only dev plugin — its constructor throws without `node --expose-internals` + a live `loader` service, so it can only run in the real `demo:*`/bin subprocess, never in the in-process unit/coverage tier.
 2. The in-process test tier (vitest) cannot even *import* the vendored `hmr` module (its class-decorator `@Inject` form fails under Vite's transform), so a package whose `apply` statically imported it could never satisfy the per-file 100% coverage gate on its headline function.
 
-Crucially, `hmr` is **not** a stdout-purity footgun the way the console logger is — a stray `hmr` in the ACP config would not corrupt the JSON-RPC frames — so leaving it at the leaf costs none of the safety the coupling argument is about. The **logger** (the real coupling) stays baked in: the stdio app includes it, the ACP app omits it.
+Crucially, `hmr` is not a stdout-purity footgun: a stray entry in the ACP config does not corrupt JSON-RPC frames. Every shipped app omits a stdout console logger; the app or protocol driver alone owns stdout.
 
 ## Alternatives considered
 
@@ -39,13 +38,13 @@ The old `base*.yml`/`acp-tail.yml` includes already deduped the *config*, but a
 ## Verification
 
 - Example directories contain only their config, README, and tests: `start.ts`, the infrastructure preamble, and the shared YAML includes are gone.
-- `demo:echo`, `demo:repl`, and `demo:acp` invoke the app-package bins.
+- `demo:tui`, `demo:headless`, and `demo:acp` invoke the app-package bins.
 - Each new package has a README and per-file 100% coverage; each app package also has a keyless real-Loader-path bin smoke that catches export-shape failures described in [postmortem 0001](../../../../docs/postmortem/0001-acp-default-export-drops-inject.md).
 - The ACP replay transcript remains unchanged because the plugin set and load order did not change.
 
 ## Consequences
 
-- **The bare-plugin-tree pedagogy.** echo-agent's inlined `cordis.yml` showed every plugin at once; the spine now lives behind a bundle, so seeing the whole tree means opening `dsh-agent-spine-demo`. The app package's README carries that teaching weight.
+- **The bare-plugin-tree pedagogy.** The spine lives behind a bundle, so seeing the whole tree means opening `dsh-agent-spine-demo`. The app package's README carries that teaching weight.
 - **A layer of indirection.** "What does this demo load?" becomes a package read, not a single YAML scan.
 
 ## Related
@@ -53,3 +52,4 @@ The old `base*.yml`/`acp-tail.yml` includes already deduped the *config*, but a
 - Supersedes [Make the shared example base providerless](../../rejected/architecture/2026-06-20-providerless-example-base.md): renaming `base.yml` to the providerless core is moot once the spine moves into `dsh-agent-spine-demo` and the `base*.yml` files are deleted.
 - Builds on the [capability-seams](2026-06-13-capability-seams.md) interface/implementation/consumer split — backends and presentation stay leaf choices; the spine is the shared bundle.
 - Complements [Reorganize packages into a modular hierarchy](2026-06-20-package-hierarchy.md): the new app/core packages slot into existing groups under that hierarchy (`core` for the reusable spine bundle, `ui` for the app-specific front doors).
+- The later [redundant-agent removal](../simplification/2026-07-20-remove-stdio-and-echo-agents.md) owns the final TUI/Headless split and removes the line-oriented and mock-only leaves.

+ 2 - 0
.agents/notes/implemented/architecture/2026-06-20-package-hierarchy.md

@@ -2,6 +2,8 @@
 
 Status: implemented
 
+The later [fold-stdio-helper](../simplification/2026-07-04-fold-stdio-ui-helper.md) decision superseded the original `support/ui-stdio` placement, and the [redundant-agent removal](../simplification/2026-07-20-remove-stdio-and-echo-agents.md) subsequently removed that surface entirely. The uniform depth-two hierarchy remains the decision owned here.
+
 ## Problem
 
 `packages/` was flat: 18 packages all sat at `packages/<name>/`, so a package's location said nothing about whether it was core product API, a swappable capability seam, a provider adapter, a product integration, or example/test support. The package README carried a `FIXME(package-hierarchy)` and `scripts/publint-all.ts` a `TODO(package-inventory)` flagging exactly this. Core packages, provider integrations, capability seams, example UI support, and snapshot-only replay support all looked equally foundational.

+ 2 - 2
.agents/notes/implemented/architecture/2026-07-05-prompt-variables-and-tool-guidance-ownership.md

@@ -8,7 +8,7 @@ The assembled system prompt had four defects, all of one family: facts the harne
 
 **The model could not know its own name.** `AgentOptions.model` drives every request, but no prompt text carried it — and nothing COULD carry it: sections in `dsh-system-prompt` were context-global while the model name is per-agent, and `assemble()` took no per-agent input at all.
 
-**Tool guidance was hand-written prose in leaf YAML.** The bash/subagent/todo_write usage guidance lived in the `systemPrompt` strings of `examples/repl-agent/cordis.yml` and `examples/acp-agent/cordis.yml` — two drifting copies (the ACP one was already abridged) — while `dsh-tool-fs` and `dsh-tool-web` owned their guidance as `ctx.systemPrompt.section()` contributions. Loading or dropping a tool plugin meant editing every deployment's persona by hand; both YAMLs carried a `FIXME(config-comments)` apologizing for a symptom of the split, and the stdio welcome banner hand-enumerated the tool set too.
+**Tool guidance was hand-written prose in leaf YAML.** The bash/subagent/todo_write usage guidance lived in the coding-agent and ACP persona strings — two drifting copies (the ACP one was already abridged) — while `dsh-tool-fs` and `dsh-tool-web` owned their guidance as `ctx.systemPrompt.section()` contributions. Loading or dropping a tool plugin meant editing every deployment's persona by hand; both YAMLs carried a `FIXME(config-comments)` apologizing for a symptom of the split, and the old terminal welcome banner hand-enumerated the tool set too.
 
 **The persona rendered after tool guidance.** The loop string-joined `agent.options.systemPrompt` AFTER the assembled sections, so the model read "Use the read tool…" before "You are a coding agent" — backwards relative to the identity-first convention (Claude Code, Codex) and a second composition path besides the section pipeline.
 
@@ -56,7 +56,7 @@ Per-tool semantics and selection guidance live in tool descriptions. Prompt sect
 
 ## Shipped invariants
 
-- The repl-agent prompt renders identity, persona with the interpolated model, then fs/bash/web guidance through one assembly path.
+- The tui-agent prompt renders identity, persona with the interpolated model, then fs/bash/web guidance through one assembly path.
 - Fork and fresh subagent descriptions reflect whether the provider inherits completed conversation turns; the tool appears, disappears, and is reworded with provider lifecycle changes.
 - Unknown, valueless, malformed, or unbalanced variable references name the section and throw; duplicate section, variable, and tool registrations also throw.
 - Snapshot replay is prompt-independent: it keys recorded chunk streams by turn and step without comparing the outgoing request.

+ 1 - 1
.agents/notes/implemented/architecture/2026-07-08-tool-output-spill-files.md

@@ -162,7 +162,7 @@ Those cases can consume `ctx.spillStore` directly in later work. They are not pa
 - `dsh-spill-local` unit tests cover `saveText`, `encodeSegment` sanitization (separators/tilde/whole-segment dots/empty), the session-hash directory, owner-only permissions, distinct paths per save, the configured/private root, and a storage-failure rejection.
 - `dsh-spill-policy` unit tests drive real tools through `ctx.tools.execute`: disabled-mode no-op, oversized-text replacement, small/non-text passthrough, `read` skip, best-effort fallback (save failure / no backend / no owner), and downstream-composition (bounding a replaced result, preserving `additionalContexts`).
 - `dsh-tool-web` integration drives `web_fetch` through `ctx.tools.execute` with the real `spill-local` backend + policy, proving the model-facing text changes only by the deliberate spill notice while the spill file holds the full formatted result.
-- The `repl-agent` example loads `spill-local` + `spill-policy`, so its keyless Loader smoke exercises the real load path (the namespace-plugin export shape + `inject`).
+- The `tui-agent` example loads `spill-local` + `spill-policy`, so its keyless Loader/PTY smoke exercises the real load path (the namespace-plugin export shape + `inject`).
 
 ## Consequences
 

+ 1 - 1
.agents/notes/implemented/feature/2026-06-18-compaction-capability-seam.md

@@ -116,7 +116,7 @@ Two failure paths, both documented:
 - **`SessionEventMap`** gains `compact/start` / `compact/summary` / `compact/end` by declaration merging (merge-extensible); `SurfaceEventType` is **not** touched. These are session events, not cordis `Events`, so the event-taxonomy gate needs no entry.
 - **`dsh-compact`** owns `toolPairingBalancedBefore(session, seq)` and `toolPairingBalancedAfter(session, seq)`, the cached surface-edge checks that `compactRegion` and `compactIfNeeded` use to avoid splitting a tool-call/result pair. The cache validates current membership by seq and answers both edges from one per-cut balance sequence; stale or missing seqs and orphan results reject.
 - **`dsh-session`** validates positional replacement, complete provenance, and content-only single-node `tool/result` rewrites through its one surface manager. `dsh-invariants` treats fresh appended tool results as executions that require an open step and pending call; validated replacements remain turn-enclosed rewrites.
-- **Wiring**: `examples/repl-agent/cordis.yml` loads zero-config `dsh-token-meter`, `dsh-compact-tool-result-prune`, then `dsh-compact-basic`; service-wide defaults make the composition usable without repeated numeric policy.
+- **Wiring**: `examples/tui-agent/cordis.yml` loads zero-config `dsh-token-meter`, `dsh-compact-tool-result-prune`, then `dsh-compact-basic`; service-wide defaults make the composition usable without repeated numeric policy.
 
 ## Testing
 

+ 3 - 3
.agents/notes/implemented/feature/2026-06-25-ask-user-question.md

@@ -20,7 +20,7 @@ Providers return `{ answers: [{ id, selected, custom? }] }`. `selected` is alway
 
 ## UI mappings
 
-`dsh-stdio-demo`'s in-package readline module renders each question, shows each option's `description` on the next line, supports comma/space-separated numeric choices for `multi_select`, accepts free-form custom answers, and rejects pending questions on abort, provider disposal, or stdin EOF. A batched request is asked in order and resolved as one answer object. The stdio provider serializes simultaneous requests with an internal queue so only one prompt owns stdin at a time.
+`dsh-tui` renders each question as a keyboard overlay, shows option descriptions, supports single- and multi-select choices plus free-form custom answers, and rejects pending questions on abort, provider disposal, or terminal shutdown. Batched and simultaneous requests are queued so one overlay owns keyboard focus at a time.
 
 `dsh-acp` provides the same seam for ACP sessions. It resolves the calling `Agent` through `ownedRecord`, requiring the forward session-map record at `agent.session.id` to own that exact agent object, and calls ACP `unstable_createElicitation` with a session-scoped form for each question. Single-select options become a `choice` string enum; `multi_select` options become a `choice` array enum; optionless questions use a required `custom` text field. If the client returns both `choice` and non-empty `custom`, the custom answer wins. ACP `decline`/`cancel`, a missing answer, a missing session, and a client without elicitation support all become structured `UserInteractionError`s.
 
@@ -42,8 +42,8 @@ ACP elicitation is currently marked unstable in the SDK. The fallback is still s
 
 The feature gives the model a powerful pause primitive, so prompt guidance matters. The tool description tells the model to ask concise questions and use options when possible. Product policy can later wrap `tools/execute` to restrict when the tool is allowed, but the loop should not special-case it.
 
-`dsh-user-interaction` and `dsh-tool-ask-user` both live in `packages/ui` because they form one product-facing human-interaction capability. `agent-core` does not load either the tool or a provider. `stdio-agent` opts into the seam, its readline provider, and the model-facing tool. `acp-agent` keeps only the `userInteraction` seam/provider by default: ACP elicitation support is still client-dependent, so an ACP leaf must opt into the model-facing tool deliberately once its client can complete elicitation requests.
+`dsh-user-interaction` and `dsh-tool-ask-user` both live in `packages/ui` because they form one product-facing human-interaction capability. `agent-core` does not load either the tool or a provider. `dsh-tui-demo` opts into the seam, TUI provider, and model-facing tool. `acp-agent` keeps only the `userInteraction` seam/provider by default: ACP elicitation support is still client-dependent, so an ACP leaf must opt into the model-facing tool deliberately once its client can complete elicitation requests.
 
 ## Testing
 
-Unit coverage pins provider registration/disposal, duplicate-provider rejection, abort-before-provider, empty-question rejection, structured tool errors through `ctx.tools.execute()`, batched answers, multi-select answers, custom answers, and the model schema including the removal of `value`, `recommended`, `allow_custom`, and `desc`. `dsh-stdio-demo` tests cover option descriptions, queued requests, EOF/abort cleanup, optionless free-form input, invalid option reprompts, duplicate multi-select numbers, and batched question flows. ACP bridge tests drive a real in-memory ACP connection with the real `ask_user_question` tool and verify selected-option, custom-overrides-choice, multi-select, and optionless free-form elicitation paths continue the agent loop.
+Unit coverage pins provider registration/disposal, duplicate-provider rejection, abort-before-provider, empty-question rejection, structured tool errors through `ctx.tools.execute()`, batched answers, multi-select answers, custom answers, and the model schema including the removal of `value`, `recommended`, `allow_custom`, and `desc`. TUI tests cover option descriptions, queued requests, shutdown/abort cleanup, optionless free-form input, invalid choices, duplicate multi-select selections, and batched question flows. ACP bridge tests drive a real in-memory ACP connection with the real `ask_user_question` tool and verify selected-option, custom-overrides-choice, multi-select, and optionless free-form elicitation paths continue the agent loop.

+ 1 - 1
.agents/notes/implemented/feature/2026-07-06-explicit-tool-order.md

@@ -21,7 +21,7 @@ The system-prompt assembly owns the canonical model-facing tool order, exactly w
 
 Scope is deliberately narrow: this fixes the REGISTRATION-ORDER race, not plugin behavior. A `system-prompt/assemble` listener may still add, remove, or rearrange tools — same as it may edit sections after their sort — and owns the determinism of what it emits; the waterfall contract already demands deterministic listeners (the reconstructability invariant would catch a listener that diverges between build and replay).
 
-Config plumbing follows the `persona` precedent, and `toolOrder` sits beside it: the app configs (`dsh-stdio-demo`, `dsh-acp-demo`) accept the key and forward it through `dsh-agent-spine-demo` (whose schema is the intersection of the owners' schemas) to the `SystemPrompt` child. One schemastery footnote is load-bearing: a schemastery array defaults to `[]`, but an omitted `toolOrder` must stay ABSENT (= lexicographic) rather than become an explicitly-configured empty list (invalid — it lacks the rest entry), so every schema on the chain forces the default to `undefined`.
+Config plumbing follows the `persona` precedent, and `toolOrder` sits beside it: the TUI, Headless, and ACP app configs accept the key and forward it through `dsh-agent-spine-demo` (whose schema is the intersection of the owners' schemas) to the `SystemPrompt` child. One schemastery footnote is load-bearing: a schemastery array defaults to `[]`, but an omitted `toolOrder` must stay ABSENT (= lexicographic) rather than become an explicitly-configured empty list (invalid — it lacks the rest entry), so every schema on the chain forces the default to `undefined`.
 
 ## Alternatives considered
 

+ 1 - 1
.agents/notes/implemented/feature/2026-07-09-bash-backed-grep-glob-discovery.md

@@ -155,7 +155,7 @@ If the complete logical result fits under the inline cap, no formatted spill art
 - The tools execute through `ctx.bash.resolve(request)` → `ctx.bash.run(spec)`, forward `exec.signal`, never call `ctx.bash.start()`, and never expose a bash task id. The bash request workdir comes from `exec.agent?.session.header.cwd` when available; the resolved `spec.workdir` drives execution and relative-path display.
 - The tools request `stdoutMaxBytes: rawOutputMaxBytes` from the bash seam, parse only untruncated stdout within that cap, and treat over-cap or still-truncated raw output as a clear search failure; raw `rg` output is never exposed to the model.
 - Oversized complete formatted results are saved through `ctx.spillStore.saveText()` when available while inline results stay bounded; spill failure, a missing backend, or a missing owner preserves the inline result and reports the unsaved remainder — never an `isError`.
-- The package README, the generated config catalog, and exported JSDoc document the Config fields and `SEARCH_*` codes; the repl-agent example ships the conditional tool plugin (the acp-agent tree waits on the snapshot re-record above); the fs group README records the `rg` availability and co-located bash/filesystem deployment requirements.
+- The package README, the generated config catalog, and exported JSDoc document the Config fields and `SEARCH_*` codes; the tui-agent example ships the conditional tool plugin (the acp-agent tree waits on the snapshot re-record above); the fs group README records the `rg` availability and co-located bash/filesystem deployment requirements.
 
 ## Risks
 

+ 2 - 2
.agents/notes/implemented/feature/2026-07-17-dedicated-full-screen-tui-front-door.i18n.yaml

@@ -2,5 +2,5 @@
 # side as of the last confirmed-consistent state. Both languages carry equal authority;
 # after editing either side, bring the other along and re-record with:
 #   pnpm run verify-translation-pairing --write
-2026-07-17-dedicated-full-screen-tui-front-door.md: 178b5ea44be67f820a8ea7fed8acb987dffb3f80
-2026-07-17-dedicated-full-screen-tui-front-door.zh.md: ac055bad1b7a692c7a980430fdbd1e34737a9994
+2026-07-17-dedicated-full-screen-tui-front-door.md: 8fbc5dddc029190b346075a65c9e7857187f3d2b
+2026-07-17-dedicated-full-screen-tui-front-door.zh.md: 6ddc3523b7a7173013efe2ef15c5ca0e940929fb

+ 4 - 4
.agents/notes/implemented/feature/2026-07-17-dedicated-full-screen-tui-front-door.md

@@ -6,7 +6,7 @@ English | [中文](2026-07-17-dedicated-full-screen-tui-front-door.zh.md)
 
 ## Problem
 
-The line-oriented `@deepseek-ai/dsh-stdio` front door works in pipes and ordinary terminals, but a full-screen coding interface must own raw input, differential screen drawing, cursor state, overlays, and terminal restoration. Combining those contracts in one UI plugin couples the pipe-safe path to a TTY-only lifecycle and makes it unclear which terminal behavior a composition selects.
+At the time this front door was introduced, the line-oriented agent handled pipes and ordinary terminals, but a full-screen coding interface had to own raw input, differential screen drawing, cursor state, overlays, and terminal restoration. Combining those contracts in one UI plugin would have coupled a stream-oriented path to a TTY-only lifecycle. The later [redundant-agent removal](../simplification/2026-07-20-remove-stdio-and-echo-agents.md) removes that line agent; this Note continues to own the TUI design.
 
 The interactive channel must remain a Cordis plugin over the same agent, session, tool, and user-interaction services as every other front door. It needs to resume durable history, follow compaction replacements, display tool-owned presentation, and restore the terminal on startup failure and disposal. A standalone chat application or a second agent composition would duplicate behavior outside the plugin graph.
 
@@ -14,7 +14,7 @@ The interactive channel must remain a Cordis plugin over the same agent, session
 
 DeepSeek Harness ships [`@deepseek-ai/dsh-tui`](../../../../packages/ui/tui/README.md) as a dedicated Cordis plugin. It owns terminal input and presentation only; agent lifecycle, session persistence, tool execution, and the model-facing question tool remain separate composition entries. The plugin requires both stdin and stdout to be TTYs and fails instead of silently changing to line-oriented behavior.
 
-The app layer selects a concrete terminal front door before mounting it. `@deepseek-ai/dsh-stdio-demo` can resolve `auto` from the two process streams, while the `repl-agent` and `tui-agent` leaves explicitly select readline and TUI respectively. The TUI leaf reuses the repl-agent backend and tool composition through an asserted include patch, so the three runnable agent leaves remain symmetric without duplicating deployment choices.
+The app layer has one terminal front door. `@deepseek-ai/dsh-tui-demo` mounts the TUI before the configured agent, and `examples/tui-agent` owns the interactive coding composition and Code Mode overlay directly. Non-interactive tasks use `@deepseek-ai/dsh-cli-demo`; ACP remains a separate editor protocol.
 
 The selected front door receives the exact generated or resumed `SessionId` used by the pre-created agent. It mounts before the agent composition, waits for the matching root agent, and enters full-screen mode only after that agent exists. A matching `agent-loop/config-start-failed` event is therefore reported before screen takeover and exits with status 1.
 
@@ -42,7 +42,7 @@ The implemented [TUI terminal-state snapshot Agent Note](../testing/2026-07-18-t
 
 ## Consequences
 
-- Interactive terminal work gains a stateful Markdown, card, plan, and question interface without changing the line-oriented protocol used by pipes and automation.
-- The TUI carries a pi-tui dependency and a strict TTY requirement; non-TTY deployments select `@deepseek-ai/dsh-stdio` at composition time.
+- Interactive terminal work has a stateful Markdown, card, plan, and question interface with no second terminal protocol to keep aligned.
+- The TUI carries a pi-tui dependency and a strict TTY requirement; non-TTY deployments use the Headless app or a structured protocol.
 - Session projection makes resume and compaction consistent with the durable conversation, but one configured session owns the transcript and editor.
 - Tool packages extend terminal cards through their existing presentation methods without adding tool-specific branches to the TUI.

+ 4 - 4
.agents/notes/implemented/feature/2026-07-17-dedicated-full-screen-tui-front-door.zh.md

@@ -6,7 +6,7 @@ Status: implemented
 
 ## 问题
 
-逐行输出的 `@deepseek-ai/dsh-stdio` 入口适用于管道和普通终端,但全屏编码界面必须负责原始输入、差分绘制、光标状态、浮层和终端恢复。把这两类契约合并到一个 UI 插件中,会迫使管道安全路径依赖仅适用于 TTY 的生命周期,也使组合无法明确表达所选终端行为
+在本入口引入时,面向行的 agent 负责 pipe 与普通终端,但全屏 coding 界面必须负责原始输入、差分绘制、光标状态、浮层和终端恢复。把这两类契约合并到一个 UI 插件中,会迫使面向 stream 的路径依赖仅适用于 TTY 的生命周期。后续的[移除重复 agent 决策](../simplification/2026-07-20-remove-stdio-and-echo-agents.md)移除了这个面向行 agent;本 Note 继续负责 TUI 设计
 
 交互通道必须继续作为 Cordis 插件,使用与其他入口相同的 agent(智能体)、会话、工具和用户交互服务。它需要恢复持久历史、跟随压缩替换、显示工具自有的呈现内容,并在启动失败和资源释放时恢复终端。独立聊天应用或第二套 agent 组合会在插件图之外重复实现这些行为。
 
@@ -14,7 +14,7 @@ Status: implemented
 
 DeepSeek Harness 将 [`@deepseek-ai/dsh-tui`](../../../../packages/ui/tui/README.md) 作为独立的 Cordis 插件交付。该插件只负责终端输入与呈现;agent 生命周期、会话持久化、工具执行以及模型可见的提问工具仍由不同组合项负责。插件要求 stdin 和 stdout 均为 TTY;条件不满足时会失败,不会静默切换为逐行输出。
 
-应用组合层在挂载前选择具体的终端入口。`@deepseek-ai/dsh-stdio-demo` 可以根据两个进程流通过 `auto` 作出选择,`repl-agent` 和 `tui-agent` 叶节点则分别明确选择 readline 与 TUI。TUI 叶节点通过带断言的 include patch 复用 repl-agent 的后端和工具组合,使三个可运行的 agent 叶节点保持对称,同时避免重复部署选项
+应用组合层只有一个终端入口。`@deepseek-ai/dsh-tui-demo` 在已配置 agent 之前挂载 TUI,`examples/tui-agent` 直接拥有交互式 coding 组装及其 Code Mode overlay。非交互任务使用 `@deepseek-ai/dsh-cli-demo`;ACP 仍是独立的编辑器协议
 
 所选入口接收预创建 agent 使用的同一个新建或恢复 `SessionId`。入口先于 agent 组合挂载,等待相符的根 agent 出现,然后才进入全屏模式。因此,相符的 `agent-loop/config-start-failed` 事件会在接管屏幕前报告,并以状态码 1 退出。
 
@@ -42,7 +42,7 @@ agent 空闲时,编辑器输入调用 `agent.send()`;轮次运行中则调
 
 ## 后果
 
-- 交互式终端获得带状态的 Markdown、卡片、计划和提问界面,同时不会改变管道与自动化使用的逐行协议。
-- TUI 会引入 pi-tui 依赖并严格要求 TTY;非 TTY 部署在组合时选择 `@deepseek-ai/dsh-stdio`
+- 交互式终端拥有带状态的 Markdown、卡片、计划和提问界面,无需再对齐第二套终端协议。
+- TUI 会引入 pi-tui 依赖并严格要求 TTY;非 TTY 部署使用 Headless app 或结构化协议
 - 会话投影使恢复和压缩与持久会话保持一致,但只有一个已配置会话拥有 transcript 和编辑器。
 - 工具包通过既有呈现方法扩展终端卡片,无需在 TUI 中增加工具专用分支。

+ 1 - 1
.agents/notes/implemented/process/2026-06-11-quality-gates.md

@@ -15,7 +15,7 @@ Every AGENTS.md promise gets a command that exits non-zero, wired into git hooks
 - jscpd detects cross-file clones in package production TypeScript and repository scripts; narrow source-range exceptions document deliberately parallel implementations.
 - Per-file 100% coverage on `packages/*/*/src` (v8); unreachable defensive guards carry `/* v8 ignore */ ` with stated reasons instead of deletion.
 - knip (dead code/deps), publint (package correctness), workspace constraints (workspace rules: private, cordis peer+dev, uniform version, ESM), and a NodeNext consumer typecheck for built package declarations.
-- lefthook pre-commit (lint staged, typecheck, vendor-manifest guard) and pre-push (tests, hygiene); CI runs the full matrix on node 22.19/24/26 plus a demo smoke test driving the echo-agent end to end.
+- lefthook pre-commit (lint staged, typecheck, vendor-manifest guard) and pre-push (tests, hygiene); CI runs the full matrix on node 22.19/24/26 plus built application smokes for the Headless, TUI, ACP, JSON-RPC, workflow, and code-runtime entry paths.
 
 ## Consequences
 

+ 1 - 1
.agents/notes/implemented/process/2026-06-16-pnpm-over-yarn.md

@@ -38,4 +38,4 @@ Performance (measured at migration time on the dev NFS filesystem; single-digit-
 
 On a fast local disk pnpm's content-addressed store typically wins on cold/warm installs and, especially, on **disk footprint** across multiple checkouts (one global store hardlinked into every `node_modules` vs Yarn copying ~279 MB per worktree — some devs regularly keep ~10 or more worktrees for this repo). That dedup advantage did **not** show in the migration-time numbers above because the test store and `node_modules` sat on different filesystems, defeating hardlinks; on a single-filesystem dev box or CI cache it applies. The honest summary: install speed on our NFS dev filesystem is a wash within noise; the move is justified by ecosystem alignment, phantom-dependency safety, and cross-checkout disk dedup — not by a raw install-time win.
 
-All quality gates (constraints, typecheck, lint, doc-sync, test:coverage at 100%, build, knip, publint, echo-agent demo smoke) pass unchanged on pnpm, which is the correctness proof that the linker swap introduced no phantom-dependency breakage.
+All quality gates (constraints, typecheck, lint, doc-sync, test:coverage at 100%, build, knip, publint, and built application smokes) pass on pnpm, which is the correctness proof that the linker swap introduces no phantom-dependency breakage.

+ 4 - 3
.agents/notes/implemented/process/2026-07-03-documentation-graph-atlas.md

@@ -26,15 +26,16 @@ Every graph page declares one maintenance mode:
 
 ### First shipped index
 
-The first index links ten relationship surfaces. Package topology and tool-package affordances live in the existing generated catalogs that already own those facts; the remaining focused diagrams are generated by `scripts/gen-doc-graphs.ts`.
+The index links eleven relationship surfaces. Package topology and tool-package affordances live in the existing generated catalogs that already own those facts; the remaining focused diagrams are generated by `scripts/gen-doc-graphs.ts`.
 
 | Graph | Maintenance mode | Source of truth |
 |---|---|---|
 | [module dependency graph](../../../../docs/module-graph.md) | generated | `packages/*/*/package.json` peer dependencies plus package group paths |
 | [tool schema catalog and package map](../../../../docs/tool-catalog.md) | generated | boot-harvested tool schemas plus tool-package service/effect metadata |
 | [capability seams and core services](../../../../docs/capability-seams.md) | hybrid generated | Cordis service declarations plus a role manifest in `gen-doc-graphs.ts` |
-| [echo-agent app composition](../../../../examples/echo-agent/composition.md) | hybrid generated | `examples/echo-agent/cordis.yml` plugin list plus curated app/bundle expansion |
-| [repl-agent app composition](../../../../examples/repl-agent/composition.md) | hybrid generated | `examples/repl-agent/cordis.yml` plugin list plus curated app/bundle expansion |
+| [tui-agent app composition](../../../../examples/tui-agent/composition.md) | hybrid generated | `examples/tui-agent/cordis.yml` plugin list plus curated app/bundle expansion |
+| [headless-agent app composition](../../../../examples/headless-agent/composition.md) | hybrid generated | `examples/headless-agent/cordis.yml` plugin list plus curated app/bundle expansion |
+| [cordis-agent app composition](../../../../examples/cordis-agent/composition.md) | hybrid generated | `examples/cordis-agent/cordis.yml` plugin list plus curated app/bundle expansion |
 | [acp-agent app composition](../../../../examples/acp-agent/composition.md) | hybrid generated | `examples/acp-agent/cordis.yml` plugin list plus curated app/bundle expansion |
 | [event producer/consumer matrix](../../../../docs/event-producer-consumer.md) | hybrid generated | Cordis event declarations, AST-scanned `ctx.on/emit/parallel/serial/waterfall` sites, and explicit dynamic dispatch overrides |
 | [agent turn and step lifecycle](../../../../docs/agent-lifecycle.md) | curated | architecture.md loop lifecycle, Cordis catalog links, and session event semantics |

+ 1 - 1
.agents/notes/implemented/process/2026-07-06-node-engine-floor.md

@@ -13,7 +13,7 @@ Set `engines.node` to `^22.19.0 || >=24.0.0` and test the keyless CI compatibili
 Two Node features gate the source runtime:
 
 - **`node:sqlite`** — `packages/session-persistence/session-persistence-sqlite` does a top-level `import { DatabaseSync } from 'node:sqlite'`. The module dropped its `--experimental-sqlite` flag requirement at **22.13** (LTS) and **23.4** (Current); before those, importing it throws at load.
-- **Native TypeScript type-stripping** — the `packages/examples/stdio-demo/tests/built-bin.e2e.ts` smoke boots the published `lib/bin.js` under plain `node` (no tsx) and loads the example's `.ts` plugins (`mock-llm.ts`, `echo-tool.ts`). Type-stripping is the default from **22.18** (LTS) and **23.6** (Current); before those it needs `--experimental-strip-types`.
+- **Native TypeScript type-stripping** — the built-mode `examples/headless-agent/tests/keyless-smoke.e2e.ts` smoke boots `dsh-cli-demo`'s published `lib/bin.js` under plain `node` (no tsx) and loads the example's `.ts` test adapter (`cli-mock-llm.ts`). Type-stripping is the default from **22.18** (LTS) and **23.6** (Current); before those it needs `--experimental-strip-types`.
 
 Those source features clear on the 22.x line at **22.18**, but the installed Pi adapter dependency raises the advertised LTS floor. `@deepseek-ai/dsh-llm-pi-ai` depends on `@earendil-works/pi-ai@0.79.3`, whose package declares `engines.node >=22.19.0`, so the LTS floor is **22.19**. The 24.x branch remains `>=24.0.0`. The disjoint range excludes Node 23 entirely: Node 23.0–23.5 still has at least one flagged source feature, and the 23 line is non-LTS/EOL, so advertising `>=23.6` would add a dead release line and a CI leg no deployment should use.
 

+ 1 - 1
.agents/notes/implemented/simplification/2026-06-20-drop-unconsumed-llm-adapter-change-event.md

@@ -24,7 +24,7 @@ If an LLM adapter browser or dynamic model-picker needs this signal later, reint
 
 ## Verification
 
-`llm/adapter-change` and its emits are gone and the regenerated cordis catalog is fresh; HMR-safety holds (disposing a contributing fiber removes the adapter); `tools/change` and `system-prompt/change` remain documented and tested; and no production path changed observable behavior — the ACP snapshot expected outputs and the echo-agent smoke are byte-unchanged.
+`llm/adapter-change` and its emits are gone and the regenerated cordis catalog is fresh; HMR-safety holds (disposing a contributing fiber removes the adapter); `tools/change` and `system-prompt/change` remain documented and tested; and the ACP snapshots plus the keyless Headless Loader smoke pin the unchanged production paths.
 
 ## Consequences
 

+ 2 - 0
.agents/notes/implemented/simplification/2026-07-04-fold-stdio-ui-helper.md

@@ -2,6 +2,8 @@
 
 Status: implemented
 
+The later [redundant-agent removal](2026-07-20-remove-stdio-and-echo-agents.md) supersedes this package-placement decision and removes the folded package, app, and line-oriented surface entirely.
+
 ## Problem
 
 The readline UI was a whole package (`@deepseek-ai/dsh-ui-stdio` under `packages/support/`) whose only runtime importer was the app package `@deepseek-ai/dsh-stdio-demo`. The examples reach the readline UI by loading the app, never by composing the helper themselves; every other repo reference was mechanical or descriptive surface that existed BECAUSE the package boundary existed — manifest and tsconfig entries, generated module-graph rows, dependency-graph and README rows, and doc comments naming the package. The ui group README recorded the support placement rationale ("exists chiefly for the examples and the coverage gate — `ui/` is reserved for surfaces shipped as product"), which left a standing tension: a shipped product app depending on a support package documented as NOT product surface.

+ 6 - 0
.agents/notes/implemented/simplification/2026-07-20-remove-stdio-and-echo-agents.i18n.yaml

@@ -0,0 +1,6 @@
+# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
+# 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
+2026-07-20-remove-stdio-and-echo-agents.md: 2aba8193710c96d3726b91062bfa43d039b4cabf
+2026-07-20-remove-stdio-and-echo-agents.zh.md: 2c3916683f4743384a2ce4104319da26145837fe

+ 45 - 0
.agents/notes/implemented/simplification/2026-07-20-remove-stdio-and-echo-agents.md

@@ -0,0 +1,45 @@
+# Agent Note: Remove the stdio and Echo agents
+
+Status: implemented
+
+English | [中文](2026-07-20-remove-stdio-and-echo-agents.zh.md)
+
+## Problem
+
+DeepSeek Harness exposed two redundant product agents beside the TUI and Headless coding agents. The line-oriented stdio agent duplicated terminal interaction and non-interactive execution with a mixed prompt/output protocol. Echo duplicated Headless as a network-free mock model plus one teaching tool, making a test fixture into a user-facing agent and the default quick-start path.
+
+Both agents carried support surfaces beyond their leaf configurations. Stdio owned a UI plugin, app package, SDK interface, REPL leaf, prompt protocol, and Loader tests. Echo owned a runnable command, mock adapter, tool, CI demo gate, graph entry, teaching references, and a shared test fixture. Keeping any of those product paths would preserve the redundant agent indirectly.
+
+Standard input and output remain protocol boundaries for ACP, JSON-RPC, MCP, and child processes. Deterministic model adapters also remain valid inside tests. Those mechanisms do not justify a line-oriented or mock-only product agent.
+
+## Decision
+
+The stdio and Echo agents are removed without compatibility packages, modes, commands, or aliases. The stdio UI and app packages, `examples/repl-agent`, `examples/echo-agent`, `demo:repl`, `demo:echo`, their dedicated tests, and supporting manifests, gates, graphs, and documentation entries are deleted.
+
+The remaining application roles are explicit:
+
+- [`@deepseek-ai/dsh-tui-demo`](../../../../packages/examples/tui-demo/README.md) owns terminal-interactive execution. `examples/tui-agent` owns the complete coding composition, Code Mode overlay, PTY coverage, and terminal snapshots.
+- [`@deepseek-ai/dsh-cli-demo`](../../../../packages/examples/cli-demo/README.md) owns non-interactive execution. `examples/headless-agent` owns the real-model one-shot composition, replay snapshots, generic real-agent suites, and test-only keyless Loader fixtures.
+- [`@deepseek-ai/dsh-acp-demo`](../../../../packages/examples/acp-demo/README.md) and `@deepseek-ai/dsh-jsonrpc` own their framed protocol integrations.
+
+The SDK project model and create/config workflows replace the `stdio` run-interface option with `tui`; generated TUI projects compose `@deepseek-ai/dsh-tui` and create or resume one exact session. Repository-facing demo documentation requires a DeepSeek API key and leads with the real Headless or TUI agents.
+
+Keyless validation is test-owned. The Headless Loader smoke uses a fixture adapter to exercise a real tool round trip, the CLI built-bin suite pins output, persistence, failure, and signal semantics, and package-specific Loader tests keep deterministic adapters beside their scenarios. None is exposed as a runnable mock agent.
+
+## Verification
+
+TUI and Headless Loader coverage run the real app packages in source and built modes. TUI uses a pseudo-terminal; Headless proves its task/result and tool-call contracts. Generated graphs and repository searches reject stale package, command, leaf, and SDK-interface references.
+
+## Alternatives considered
+
+- **Keep the line agent only for pipes** — rejected because Headless has a bounded task contract, format-pure stdout, durable completion, and process exit status.
+- **Keep Echo as the keyless quick start** — rejected because the first product experience should exercise the real model and supported coding agent, not a scripted adapter with a bespoke tool.
+- **Keep Echo only as a CI demo command** — rejected because test-owned Headless fixtures cover the same Loader and built-artifact boundaries without preserving a mock product leaf.
+- **Remove every stdio or mock mechanism** — rejected because framed protocols, process I/O, and deterministic test adapters are independent infrastructure, not the removed agents.
+
+## Consequences
+
+- Interactive and non-interactive product execution each have one owner and one runnable coding leaf.
+- The repository has no keyless user-facing agent demo; local agent demos require `DEEPSEEK_API_KEY`.
+- CI retains keyless real-entry coverage through test fixtures rather than a product command.
+- Existing stdio-agent configurations, Echo commands, and SDK `--interface=stdio` invocations fail instead of being translated.

+ 45 - 0
.agents/notes/implemented/simplification/2026-07-20-remove-stdio-and-echo-agents.zh.md

@@ -0,0 +1,45 @@
+# Agent Note: 移除 stdio 和 Echo agent
+
+Status: implemented
+
+[English](2026-07-20-remove-stdio-and-echo-agents.md) | 中文
+
+## 问题
+
+DeepSeek Harness 在 TUI 和 Headless coding agent 之外,还提供了两个重复的产品 agent(智能体)。面向行的 stdio agent 使用混合的提示符/输出协议,同时重复实现终端交互与非交互执行。Echo 则以无需联网的 mock 模型加一个教学工具重复实现 Headless,把测试 fixture(测试前置数据)变成面向用户的 agent 和默认快速上手路径。
+
+两个 agent 的配套实现都不止叶节点配置。stdio 拥有 UI 插件、app 包(package)、SDK 接口、REPL 叶节点、提示符协议和 Loader 测试。Echo 拥有可运行命令、mock 适配器、工具、CI 演示门禁、图谱条目、教学引用和共享测试 fixture。保留其中任何产品路径,都会间接保留这个重复的 agent。
+
+标准输入输出仍是 ACP、JSON-RPC、MCP 和子进程的协议边界。确定性模型适配器也仍可用于测试。这些机制不足以成为保留面向行或仅使用 mock 的产品 agent 的理由。
+
+## 决策
+
+彻底移除 stdio 和 Echo agent,不提供兼容包、模式、命令或别名。删除 stdio UI 包与 app 包、`examples/repl-agent`、`examples/echo-agent`、`demo:repl`、`demo:echo`、各自的专属测试,以及相关的 manifest(元数据清单)、门禁、图谱和文档条目。
+
+保留的应用角色均有明确归属:
+
+- [`@deepseek-ai/dsh-tui-demo`](../../../../packages/examples/tui-demo/README.md) 负责终端交互式执行。`examples/tui-agent` 拥有完整 coding 组装、Code Mode 覆盖层、PTY 覆盖和终端快照。
+- [`@deepseek-ai/dsh-cli-demo`](../../../../packages/examples/cli-demo/README.md) 负责非交互式执行。`examples/headless-agent` 拥有真实模型的单次任务组装、回放快照、通用真实 agent 测试套件,以及仅供测试使用的无密钥 Loader fixture。
+- [`@deepseek-ai/dsh-acp-demo`](../../../../packages/examples/acp-demo/README.md) 和 `@deepseek-ai/dsh-jsonrpc` 负责各自的分帧协议集成。
+
+SDK 工程模型与 create/config 工作流将 `stdio` 运行接口选项替换为 `tui`;生成的 TUI 工程组合 `@deepseek-ai/dsh-tui`,并创建或恢复一个确切会话。仓库中的演示文档要求 DeepSeek API key,并优先引导到真实的 Headless 或 TUI agent。
+
+无密钥验证由测试负责。Headless Loader 冒烟测试使用 fixture 适配器验证真实工具往返;CLI built-bin 测试套件固定输出、持久化、失败和信号语义;各包专属的 Loader 测试则将确定性适配器放在对应场景旁。其中任何一项都不会作为可运行的 mock agent 对外暴露。
+
+## 验证
+
+TUI 与 Headless 的 Loader 覆盖以源码和构建产物两种模式运行真实 app 包。TUI 使用伪终端;Headless 验证任务/结果契约和工具调用契约。生成图谱与仓库搜索会拒绝陈旧的包、命令、叶节点和 SDK 接口引用。
+
+## 曾考虑的替代方案
+
+- **仅为 pipe 保留面向行 agent**:不予采纳,因为 Headless 已提供有界任务契约、格式纯净的 stdout、持久完成边界和进程退出状态。
+- **保留 Echo 作为无密钥快速上手路径**:不予采纳,因为首次产品体验应使用真实模型和受支持的 coding agent,而不是带专用工具的脚本化适配器。
+- **只为 CI 演示命令保留 Echo**:不予采纳,因为由测试持有的 Headless fixture 可以覆盖相同的 Loader 和构建产物边界,无需保留 mock 产品叶节点。
+- **移除所有 stdio 或 mock 机制**:不予采纳,因为分帧协议、进程 I/O 和确定性测试适配器是独立基础设施,并不是被移除的 agent。
+
+## 后果
+
+- 交互式与非交互式产品执行分别只有一个归属方和一个可运行的 coding 叶节点。
+- 仓库没有面向用户的无密钥 agent 演示;本地 agent 演示需要 `DEEPSEEK_API_KEY`。
+- CI 通过测试 fixture 保留针对真实入口的无密钥覆盖,而不是依赖产品命令。
+- 既有 stdio agent 配置、Echo 命令和 SDK `--interface=stdio` 调用会直接失败,不会被转换。

+ 2 - 2
.agents/notes/implemented/testing/2026-07-18-tui-terminal-state-snapshots.i18n.yaml

@@ -2,5 +2,5 @@
 # side as of the last confirmed-consistent state. Both languages carry equal authority;
 # after editing either side, bring the other along and re-record with:
 #   pnpm run verify-translation-pairing --write
-2026-07-18-tui-terminal-state-snapshots.md: 192e872ab63cf4ff8a121ea0a2ee9345379cfa26
-2026-07-18-tui-terminal-state-snapshots.zh.md: 9766a8087632daa1be0dcfb191696dbad354ff68
+2026-07-18-tui-terminal-state-snapshots.md: 8e86588f69fdb9d615232252ecf57309d440f1cd
+2026-07-18-tui-terminal-state-snapshots.zh.md: b70a46830f44e9da663e30745fcdb7ad281592da

+ 1 - 1
.agents/notes/implemented/testing/2026-07-18-tui-terminal-state-snapshots.md

@@ -21,7 +21,7 @@ TUI coverage has four complementary layers:
 3. `examples/tui-agent/tests/tui.snapshot.ts` replays committed JSONL session logs through the production agent loop and real tools, then compares the resulting semantic terminal state.
 4. `examples/tui-agent/tests/tui-keyless-smoke.e2e.ts` boots the real Loader composition in a PTY, drives a scripted conversation through streaming and `ask_user_question`, and verifies startup, input, exit, failure reporting, and terminal restoration.
 
-The runnable TUI has its own `examples/tui-agent` leaf beside the readline `repl-agent` and `acp-agent` leaves. It reuses the repl-agent backend and tool composition through an asserted include patch while fixing the shared terminal app to `ui.mode: tui`; TUI snapshots and PTY tests live with that leaf.
+The runnable TUI has its own `examples/tui-agent` leaf beside the Headless and ACP leaves. It owns the interactive coding backends and tools directly and loads `@deepseek-ai/dsh-tui-demo`; TUI snapshots and PTY tests live with that leaf. The [redundant-agent removal](../simplification/2026-07-20-remove-stdio-and-echo-agents.md) owns this consolidation.
 
 ### Recorded-session replay
 

+ 1 - 1
.agents/notes/implemented/testing/2026-07-18-tui-terminal-state-snapshots.zh.md

@@ -21,7 +21,7 @@ TUI 覆盖分为四个互补层次:
 3. `examples/tui-agent/tests/tui.snapshot.ts` 通过生产 agent loop 和真实工具回放已提交的 JSONL 会话日志,再比较生成的语义终端状态。
 4. `examples/tui-agent/tests/tui-keyless-smoke.e2e.ts` 在 PTY 中启动真实 Loader 组合,驱动一段经过流式输出和 `ask_user_question` 的脚本化会话,并验证启动、输入、退出、失败报告和终端恢复。
 
-可运行 TUI 在 `examples/tui-agent` 中拥有独立叶节点,与 readline `repl-agent` 和 `acp-agent` 叶节点并列。它通过带断言的 include patch 复用 repl-agent 的后端与工具组合,只把共享终端应用固定为 `ui.mode: tui`;TUI 快照和 PTY 测试也归属这个叶节点。
+可运行 TUI 在 `examples/tui-agent` 中拥有独立叶节点,与 Headless 和 ACP 叶节点并列。它直接拥有交互式 coding 后端与工具,并加载 `@deepseek-ai/dsh-tui-demo`;TUI 快照和 PTY 测试也归属这个叶节点。[移除重复 agent 的决策](../simplification/2026-07-20-remove-stdio-and-echo-agents.md)负责此次整合。
 
 ### 已录制会话回放
 

+ 2 - 2
.agents/notes/proposed/feature/2026-07-14-sdk-developer-projects.i18n.yaml

@@ -2,5 +2,5 @@
 # side as of the last confirmed-consistent state. Both languages carry equal authority;
 # after editing either side, bring the other along and re-record with:
 #   pnpm run verify-translation-pairing --write
-2026-07-14-sdk-developer-projects.md: 1be9abcad1e51a1b9a1406f21ce60073427576e0
-2026-07-14-sdk-developer-projects.zh.md: a8ba1d658f78484a46a7148a4e2ff1b073a3e9f2
+2026-07-14-sdk-developer-projects.md: aa5cf64d7dd33dea229d74c2ae45a9244ee70e3c
+2026-07-14-sdk-developer-projects.zh.md: 8f7d1de5b16f38019c802f07eda701cee72deb4f

+ 6 - 6
.agents/notes/proposed/feature/2026-07-14-sdk-developer-projects.md

@@ -44,7 +44,7 @@ The table is the developer-visible support set for this phase. A `required` feat
 | Feature | Create state | Feature options | Constraints and relationships |
 |---|---|---|---|
 | `provider` | required | `deepseek` (default) / `custom` | DeepSeek collects an API key; custom also collects a base URL, and a CLI option may override the model name |
-| `app` | required | `stdio` (default) / `acp` / `embed` | Selects the run interface |
+| `app` | required | `tui` (default) / `acp` / `embed` | Selects the run interface |
 | `spine` | required | `default` | Timer, the LLM seam, session storage, system prompt, the tool registry, the agent registry, and the agent loop |
 | `bash` | required | `local` (default) / `sandbox` | The two feature options are exclusive and independent of the run interface, and both install the model-facing bash tool; sandbox installs the local sandbox provider and sandboxed bash backend |
 | `persistence` | required | `jsonl` (default) / `sqlite` | Every project selects exactly one persistence backend |
@@ -59,9 +59,9 @@ The table is the developer-visible support set for this phase. A `required` feat
 | `hooks` | optional | `claude` (default) / `codex`, multiple | Each feature option creates a separate editable configuration file |
 | `guard` | optional | `repeat-tool` | Provides repeated-tool-call reminders |
 | `timeout-policy` | optional | `default` | Applies a uniform policy to tools that declare timeout budgets |
-| `ask-user` | optional | `default` | Provides the `ask_user_question` tool; only `acp` and `stdio` can select it because those two feature options provide the injected user-interaction service |
+| `ask-user` | optional | `default` | Provides the `ask_user_question` tool; only `acp` and `tui` can select it because those two feature options provide the injected user-interaction service |
 
-Both `bash` feature options apply to ACP, stdio, and embed and are not selected by the run interface. The sandbox feature option writes no active config key and therefore keeps `dsh-bash-sandbox`'s `read-only` default. Generated `cordis.yml` includes a commented example that developers can change explicitly to `workspace-write`:
+Both `bash` feature options apply to ACP, TUI, and embed and are not selected by the run interface. The sandbox feature option writes no active config key and therefore keeps `dsh-bash-sandbox`'s `read-only` default. Generated `cordis.yml` includes a commented example that developers can change explicitly to `workspace-write`:
 
 ```yaml
 - id: bash
@@ -72,11 +72,11 @@ Both `bash` feature options apply to ACP, stdio, and embed and are not selected
   #   workspaceRoot: !!js process.cwd()
 ```
 
-Feature contributions reference only single-plugin npm packages and never bundle packages such as `agent-spine-demo`, `stdio-demo`, or `acp-demo`. Plugins outside the table are not managed by create in this phase; advanced developers may still compose them by editing the ordinary project files directly.
+Feature contributions reference only single-plugin npm packages and never bundle packages such as `agent-spine-demo`, `tui-demo`, or `acp-demo`. Plugins outside the table are not managed by create in this phase; advanced developers may still compose them by editing the ordinary project files directly.
 
 ## Generated project
 
-With default answers, an npm project uses the DeepSeek provider, the stdio interface, local bash, JSONL persistence, and the preselected hmr, fs, todo, and skill features. Its initial tree is:
+With default answers, an npm project uses the DeepSeek provider, the TUI interface, local bash, JSONL persistence, and the preselected hmr, fs, todo, and skill features. Its initial tree is:
 
 ```text
 my-agent/
@@ -106,7 +106,7 @@ Generated `package.json` provides the following scripts. `dev`, `build`, `start`
 
 `dsh-sdk start` and `dsh-sdk dev` accept a module target and forward arguments after `--` unchanged to the project entrypoint. Generic argument parsing uses Node `parseArgs()` with zero schema: valued flags use `--key=value`, bare flags become `true`, and `--no-*` becomes `false`.
 
-- Stdio projects pass the selected model through `--model=<name>` and create or resume an agent according to optional `--resume=<session-id>`;
+- TUI projects pass the selected model through `--model=<name>` and create or resume an agent according to optional `--resume=<session-id>`;
 - ACP uses protocol `session/load`
 - Embed uses the model written into the generated code.
 

+ 6 - 6
.agents/notes/proposed/feature/2026-07-14-sdk-developer-projects.zh.md

@@ -44,7 +44,7 @@ create 还提供一次 `none / plugin / tool` 选择。`plugin` 固定生成 `pl
 | 功能 | create 状态 | 功能选项 | 限制与关系 |
 |---|---|---|---|
 | `provider` | required | `deepseek`(默认)/ `custom` | DeepSeek 收集 API key;custom 另收集 base URL,模型名可由 CLI 参数覆盖 |
-| `app` | required | `stdio`(默认)/ `acp` / `embed` | 选择运行接口 |
+| `app` | required | `tui`(默认)/ `acp` / `embed` | 选择运行接口 |
 | `spine` | required | `default` | timer、LLM seam、会话存储、系统提示词、工具注册表、agent 注册表,以及 agent loop |
 | `bash` | required | `local`(默认)/ `sandbox` | 两个功能选项互斥、与运行接口正交,且都安装面向模型的 bash 工具;sandbox 安装本地沙箱提供方和沙箱 bash 后端 |
 | `persistence` | required | `jsonl`(默认)/ `sqlite` | 每个工程恰好选择一个持久化后端 |
@@ -59,9 +59,9 @@ create 还提供一次 `none / plugin / tool` 选择。`plugin` 固定生成 `pl
 | `hooks` | optional | `claude`(默认)/ `codex`,可多选 | 各功能选项生成独立的可编辑配置文件 |
 | `guard` | optional | `repeat-tool` | 提供重复工具调用提醒 |
 | `timeout-policy` | optional | `default` | 对声明超时预算的工具执行统一策略 |
-| `ask-user` | optional | `default` | 提供 `ask_user_question` 工具;注入的 user-interaction 服务由 acp/stdio 两个功能选项提供,因此仅这两个接口可选 |
+| `ask-user` | optional | `default` | 提供 `ask_user_question` 工具;注入的 user-interaction 服务由 acp/tui 两个功能选项提供,因此仅这两个接口可选 |
 
-`bash` 的两个功能选项都适用于 ACP、stdio 和 embed,不由运行接口决定。sandbox 功能选项不写任何生效的配置键,因而沿用 `dsh-bash-sandbox` 的 `read-only` 默认值;生成的 `cordis.yml` 保留注释示例,开发者可以显式改为 `workspace-write`:
+`bash` 的两个功能选项都适用于 ACP、TUI 和 embed,不由运行接口决定。sandbox 功能选项不写任何生效的配置键,因而沿用 `dsh-bash-sandbox` 的 `read-only` 默认值;生成的 `cordis.yml` 保留注释示例,开发者可以显式改为 `workspace-write`:
 
 ```yaml
 - id: bash
@@ -72,11 +72,11 @@ create 还提供一次 `none / plugin / tool` 选择。`plugin` 固定生成 `pl
   #   workspaceRoot: !!js process.cwd()
 ```
 
-功能贡献只引用单插件 NPM 包,绝不引用 `agent-spine-demo`、`stdio-demo`、`acp-demo` 这类组合 NPM 包。表格之外的插件不由本期 create 管理;开发者仍可直接编辑普通工程文件进行高级组合。
+功能贡献只引用单插件 NPM 包,绝不引用 `agent-spine-demo`、`tui-demo`、`acp-demo` 这类组合 NPM 包。表格之外的插件不由本期 create 管理;开发者仍可直接编辑普通工程文件进行高级组合。
 
 ## 生成工程
 
-使用默认答案创建 npm 工程时,provider 为 DeepSeek,运行接口为 stdio,bash 为 local,持久化为 JSONL,hmr、fs、todo 与 skill 处于选中状态。初始目录树为:
+使用默认答案创建 npm 工程时,provider 为 DeepSeek,运行接口为 TUI,bash 为 local,持久化为 JSONL,hmr、fs、todo 与 skill 处于选中状态。初始目录树为:
 
 ```text
 my-agent/
@@ -106,7 +106,7 @@ my-agent/
 
 `dsh-sdk start` 与 `dsh-sdk dev` 可以接收模块 target,并把 `--` 后的参数原样转发给工程入口。通用参数解析使用 Node `parseArgs()` 的零 schema 模式:带值 flag 采用 `--key=value`,bare flag 转换为 `true`,`--no-*` 转换为 `false`。
 
-- stdio 工程通过 `--model=<name>` 传入所选 model,并根据可选的 `--resume=<session-id>` 创建或恢复 agent;
+- TUI 工程通过 `--model=<name>` 传入所选 model,并根据可选的 `--resume=<session-id>` 创建或恢复 agent;
 - acp 使用协议 `session/load`
 - embed 使用生成代码中的 model。
 

+ 2 - 2
.agents/skills/dsh-pre-push-checks/SKILL.md

@@ -5,7 +5,7 @@ description: Use before pushing, force-pushing, marking ready for review, claimi
 
 # DSH Pre-Push Checks
 
-Use this skill to choose and run the smallest sufficient verification set before a `deepseek-harness` push. Do not treat the local pre-push hook as the full CI contract: CI also runs coverage, build, demo smoke, and built-bin smoke.
+Use this skill to choose and run the smallest sufficient verification set before a `deepseek-harness` push. Do not treat the local pre-push hook as the full CI contract: CI also runs coverage, build, and built-bin smoke.
 
 ## First Steps
 
@@ -54,7 +54,7 @@ pnpm run test:snapshot
 Run built-bin smoke tests after `pnpm run build` when app packages, app boot, package runtime imports, bin entries, loader behavior, or published artifact paths change.
 
 ```sh
-pnpm exec vitest run --config vitest.e2e.config.ts packages/examples/stdio-demo/tests/built-bin.e2e.ts packages/examples/cli-demo/tests/built-bin.e2e.ts packages/examples/acp-demo/tests/built-bin.e2e.ts
+DSH_EXAMPLE_MODE=lib pnpm exec vitest run --config vitest.e2e.config.ts examples/headless-agent/tests/keyless-smoke.e2e.ts examples/tui-agent/tests/tui-keyless-smoke.e2e.ts packages/examples/cli-demo/tests/built-bin.e2e.ts packages/examples/acp-demo/tests/built-bin.e2e.ts
 ```
 
 Run real e2e when behavior depends on a real model/API, tool-use loop, ACP integration, prompt injection, or end-to-end agent UX. If `.env` is available, use it; do not print secrets.

+ 4 - 11
AGENTS.md

@@ -27,8 +27,8 @@ packages/    @deepseek-ai/dsh-<pkg> workspaces at packages/<group>/<pkg>/
   cordis/      self-referential toolset: the agent inspects/mounts plugins in its own runtime
   hooks/       Claude Code / Codex hook bridges + shared wire-protocol library
   session-persistence/  persistence seam + JSONL/SQLite backends
-  ui/          ACP/stdio/TUI/JSON-RPC bridges; boot, approval, interaction plugins
-  examples/    demo bundles (agent-spine + stdio/CLI/ACP/JSON-RPC bins) leaves load
+  ui/          ACP/TUI/JSON-RPC bridges; boot, approval, interaction plugins
+  examples/    demo bundles (agent-spine + TUI/CLI/ACP/JSON-RPC bins) leaves load
   support/     dev/test infrastructure packages
   util/        zero-dependency utilities
 python/      Python SDK and bundled runtime (see python/README.md)
@@ -58,9 +58,7 @@ pnpm run build          # tsc emits lib/types, tsdown bundles runtime
 pnpm run hygiene        # knip + publint + workspace constraints + NodeNext consumer check
 pnpm run doc-sync       # all documentation gates; see the doc-sync script in package.json
 pnpm run website:build  # VitePress build (doubles as the site's dead-link check)
-pnpm run demo:echo      # mock-model REPL, no key needed
-pnpm run demo:repl      # real REPL coding agent (needs DEEPSEEK_API_KEY)
-pnpm run demo:headless -- "task" # one-shot agent (needs DEEPSEEK_API_KEY)
+pnpm run demo:headless "task" # one-shot agent (needs DEEPSEEK_API_KEY)
 pnpm run demo:tui       # full-screen TUI coding agent (needs DEEPSEEK_API_KEY)
 pnpm run demo:cordis    # self-referential demo: the agent modifies its own runtime (needs key)
 pnpm run demo:acp       # ACP server agent (needs DEEPSEEK_API_KEY)
@@ -86,12 +84,7 @@ pnpm run website:build
 pnpm run verify-module-graph
 pnpm run build
 pnpm run hygiene
-out=$(printf 'echo ci smoke\n' | pnpm run demo:echo 2>&1)
-printf '%s\n' "$out" | grep -q '\[tool call\] echo({"text":"ci smoke"})'
-printf '%s\n' "$out" | grep -q '\[tool result\] ECHO: CI SMOKE'
-test -n "$(find .sessions -path '.sessions/cwd-*/main-session-*.jsonl.zstd' -type f -print -quit)"
-rm -rf .sessions
-pnpm exec vitest run --config vitest.e2e.config.ts packages/examples/stdio-demo/tests/built-bin.e2e.ts packages/examples/cli-demo/tests/built-bin.e2e.ts packages/examples/acp-demo/tests/built-bin.e2e.ts packages/ui/jsonrpc/tests/built-scope-carrier.e2e.ts packages/workflow/workflow-workerthread/tests/built-worker.e2e.ts packages/code-runtime/code-runtime-worker/tests/built-lib.e2e.ts
+DSH_EXAMPLE_MODE=lib pnpm exec vitest run --config vitest.e2e.config.ts examples/headless-agent/tests/keyless-smoke.e2e.ts examples/tui-agent/tests/tui-keyless-smoke.e2e.ts packages/examples/cli-demo/tests/built-bin.e2e.ts packages/examples/acp-demo/tests/built-bin.e2e.ts packages/ui/jsonrpc/tests/built-scope-carrier.e2e.ts packages/workflow/workflow-workerthread/tests/built-worker.e2e.ts packages/code-runtime/code-runtime-worker/tests/built-lib.e2e.ts
 ```
 
 `test:coverage`, not `test`, is the gate ([why](docs/testing.md)); report only commands actually run.

+ 2 - 2
README.i18n.yaml

@@ -2,5 +2,5 @@
 # side as of the last confirmed-consistent state. Both languages carry equal authority;
 # after editing either side, bring the other along and re-record with:
 #   pnpm run verify-translation-pairing --write
-README.md: ef9a3a8832d1eaa35ec5f0fed1780ab27e8ff37c
-README.zh.md: a30d6db4b04f23559c36a7aba80b4feb2962a1c6
+README.md: 32958db0e74bd14d6d41e8d7886b8d3257fe0f59
+README.zh.md: b28b175a8296347a7bed05b4e53c0d75dc51efed

+ 5 - 6
README.md

@@ -11,12 +11,11 @@ This monorepo is built on the [Cordis](https://github.com/cordiverse/cordis) fra
 ```sh
 pnpm install
 pnpm run test          # vitest
-pnpm run demo:echo     # keyless mock-model REPL
-pnpm run demo:repl     # readline coding agent (needs DEEPSEEK_API_KEY)
-pnpm run demo:tui      # full-screen TUI coding agent (needs DEEPSEEK_API_KEY)
-pnpm run demo:headless -- "task" # one-shot coding agent (needs DEEPSEEK_API_KEY)
-pnpm run demo:cordis   # self-referential agent demo (needs DEEPSEEK_API_KEY)
-pnpm run demo:acp      # ACP server agent demo (needs DEEPSEEK_API_KEY)
+# Agent demos require DEEPSEEK_API_KEY.
+pnpm run demo:tui      # full-screen TUI coding agent
+pnpm run demo:headless "task" # one-shot coding agent
+pnpm run demo:cordis   # self-referential agent demo
+pnpm run demo:acp      # ACP server agent demo
 ```
 
 For humans, start with the [development guide](docs/development.md) for local setup, hooks, environment variables, and quality gates, then read the [architecture design](docs/architecture.md) and [documentation graph index](docs/graph-atlas.md) before package work. Local context lives in [packages/](packages/) and [vendor/](vendor/).

+ 5 - 6
README.zh.md

@@ -11,12 +11,11 @@
 ```sh
 pnpm install
 pnpm run test          # vitest
-pnpm run demo:echo     # keyless mock-model REPL
-pnpm run demo:repl     # readline coding agent (needs DEEPSEEK_API_KEY)
-pnpm run demo:tui      # full-screen TUI coding agent (needs DEEPSEEK_API_KEY)
-pnpm run demo:headless -- "task" # one-shot coding agent (needs DEEPSEEK_API_KEY)
-pnpm run demo:cordis   # self-referential agent demo (needs DEEPSEEK_API_KEY)
-pnpm run demo:acp      # ACP server agent demo (needs DEEPSEEK_API_KEY)
+# Agent demos require DEEPSEEK_API_KEY.
+pnpm run demo:tui      # full-screen TUI coding agent
+pnpm run demo:headless "task" # one-shot coding agent
+pnpm run demo:cordis   # self-referential agent demo
+pnpm run demo:acp      # ACP server agent demo
 ```
 
 面向开发者:先读[开发指南](docs/development.md),了解本地环境搭建、钩子、环境变量与质量门禁,动手改 package 之前再读[架构设计](docs/architecture.md)和[文档关系图索引](docs/graph-atlas.md)。局部上下文见 [packages/](packages/) 与 [vendor/](vendor/)。

+ 1 - 1
docs/architecture.md

@@ -157,7 +157,7 @@ Some seams bend the template deliberately: LLM combines interface and consumer b
 
 ### 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 ([README](../packages/examples/agent-spine-demo/README.md)). `dsh-tui-demo` owns the interactive full-screen terminal; `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)).
 
 ### Where New Behavior Goes
 

+ 7 - 6
docs/capability-seams.md

@@ -47,11 +47,12 @@ 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_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_bash["bash"]
@@ -133,7 +134,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 +143,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
@@ -156,8 +157,8 @@ flowchart LR
   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
@@ -208,8 +209,8 @@ 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_workflow
   svc_fs -. event gate .-> pkg_fs_policy
@@ -225,9 +226,9 @@ flowchart LR
 | `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.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), [`invariants`](../packages/support/invariants) | - | 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.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. |

+ 44 - 84
docs/config-catalog.md

@@ -869,90 +869,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:34`](../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
-  /** JSONL artifact encoding; defaults to checksummed Zstandard frames. */
-  persistenceCompression?: JsonlCompression
-  /** 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) · [`JsonlCompression`](../packages/session-persistence/session-persistence-jsonl/src/index.ts) · [`ToolsConfig`](#deepseek-aidsh-tools) · [`uiTui`](../packages/ui/tui/src/index.ts)
-
-Source: [`packages/examples/stdio-demo/src/index.ts:78`](../packages/examples/stdio-demo/src/index.ts)
-
 ## `@deepseek-ai/dsh-subagent-acp`
 
 Requires: `subagents`
@@ -1335,6 +1251,50 @@ export interface TuiConfig {
 
 Source: [`packages/ui/tui/src/index.ts:101`](../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 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:31`](../packages/examples/tui-demo/src/index.ts)
+
 ## `@deepseek-ai/dsh-user-approval`
 
 ```ts config-catalog

+ 2 - 2
docs/cookbook/adding-a-tool.i18n.yaml

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

+ 1 - 1
docs/cookbook/adding-a-tool.md

@@ -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
 

+ 1 - 1
docs/cookbook/adding-a-tool.zh.md

@@ -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
 
 ## 最小形态
 

+ 2 - 2
docs/cookbook/extension-cookbook.i18n.yaml

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

+ 1 - 1
docs/cookbook/extension-cookbook.md

@@ -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
 

+ 1 - 1
docs/cookbook/extension-cookbook.zh.md

@@ -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) 共享主干。
 
 ## 功能→机制映射
 

+ 1 - 1
docs/core-data-structures/user-interaction.md

@@ -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)
 

+ 2 - 2
docs/development.i18n.yaml

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

+ 5 - 11
docs/development.md

@@ -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

+ 5 - 11
docs/development.zh.md

@@ -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/ACP(Agent Client Protocol) agent(智能体)演示和真实 API 的 e2e 测试。
+- 可选:一个 DeepSeek API key,用于 TUI/Headless/ACP(Agent 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

+ 6 - 6
docs/event-producer-consumer.md

@@ -7,9 +7,9 @@ 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:353`](../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:150`](../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:159`](../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-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/created` | `emit` | [`packages/core/agent/src/types.ts:150`](../packages/core/agent/src/types.ts) | [`agent`](../packages/core/agent) (`events.dispatch`) | [`tui`](../packages/ui/tui) |
+| `agent/disposed` | `emit` | [`packages/core/agent/src/types.ts:159`](../packages/core/agent/src/types.ts) | [`agent`](../packages/core/agent) (`events.dispatch`) | [`agent-loop`](../packages/core/agent-loop), [`tui`](../packages/ui/tui) |
 | `agent/error` | `emit` | [`packages/core/agent/src/types.ts:314`](../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:267`](../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:207`](../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) |
@@ -18,8 +18,8 @@ This matrix shows which packages dispatch each harness-owned event and which pac
 | `agent/request` | `waterfall` | [`packages/core/agent/src/types.ts:229`](../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:281`](../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:244`](../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:191`](../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:168`](../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/session-start` | `emit` | [`packages/core/agent/src/types.ts:191`](../packages/core/agent/src/types.ts) | [`agent-loop`](../packages/core/agent-loop) (`emit`) | [`hooks-claude`](../packages/hooks/hooks-claude), [`hooks-codex`](../packages/hooks/hooks-codex) |
+| `agent/status` | `emit` | [`packages/core/agent/src/types.ts:168`](../packages/core/agent/src/types.ts) | [`agent-loop`](../packages/core/agent-loop) (`emit`) | [`invariants`](../packages/support/invariants), [`tui`](../packages/ui/tui) |
 | `agent/step-result` | `waterfall` | [`packages/core/agent/src/types.ts:255`](../packages/core/agent/src/types.ts) | [`agent-loop`](../packages/core/agent-loop) (`waterfall`) | - |
 | `agent/turn-continuation` | `waterfall` | [`packages/core/agent/src/types.ts:291`](../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:301`](../packages/core/agent/src/types.ts) | [`agent-loop`](../packages/core/agent-loop) (`serial`) | [`subagent-inprocess`](../packages/subagent/subagent-inprocess) |
@@ -30,7 +30,7 @@ This matrix shows which packages dispatch each harness-owned event and which pac
 | `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) |
 | `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), [`invariants`](../packages/support/invariants), [`jsonrpc`](../packages/ui/jsonrpc), [`session-persistence`](../packages/session-persistence/session-persistence), [`token-meter`](../packages/llm/token-meter), [`tui`](../packages/ui/tui), [`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:139`](../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:113`](../packages/subagent/subagent/src/index.ts) | [`subagent`](../packages/subagent/subagent) (`emit`) | [`tool-subagent`](../packages/subagent/tool-subagent) |

+ 0 - 2
docs/graph-atlas.md

@@ -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` |

+ 3 - 3
docs/i18n/translation-prompt.md

@@ -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)`

+ 14 - 22
docs/module-graph.md

@@ -108,7 +108,6 @@ flowchart TD
     pkg_app_boot["app-boot"]
     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,7 +126,7 @@ 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"]
@@ -395,11 +394,6 @@ flowchart TD
   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_llm
@@ -449,19 +443,18 @@ flowchart TD
   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_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 |
@@ -550,7 +543,6 @@ flowchart TD
 | [`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) |
@@ -558,4 +550,4 @@ flowchart TD
 | [`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) |
+| [`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), [`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) |

+ 1 - 1
docs/postmortem/0001-acp-default-export-drops-inject.md

@@ -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'

+ 1 - 1
docs/testing.md

@@ -11,7 +11,7 @@ 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
 

+ 2 - 2
docs/tool-catalog.md

@@ -22,7 +22,7 @@ This table connects model-visible tool names to the plugin package and service s
 | `@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-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` | - | - |
@@ -448,7 +448,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`
 

+ 2 - 2
docs/user/develop/basic/index.i18n.yaml

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

+ 6 - 6
docs/user/develop/basic/index.md

@@ -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}!` }]
     },
   }))
 }

+ 6 - 6
docs/user/develop/basic/index.zh.md

@@ -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}!` }]
     },
   }))
 }

+ 2 - 2
docs/user/develop/practice/llm-adapter.i18n.yaml

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

+ 5 - 4
docs/user/develop/practice/llm-adapter.md

@@ -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
 

+ 5 - 4
docs/user/develop/practice/llm-adapter.zh.md

@@ -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 之上实现
 
 ## 错误处理
 

+ 2 - 2
docs/user/guide/config.i18n.yaml

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

+ 9 - 4
docs/user/guide/config.md

@@ -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

+ 9 - 4
docs/user/guide/config.zh.md

@@ -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
 ```
 
 ## 插件条目

+ 2 - 2
docs/user/guide/index.i18n.yaml

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

+ 4 - 2
docs/user/guide/index.md

@@ -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

+ 4 - 2
docs/user/guide/index.zh.md

@@ -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
 ```
 
 ## 适合谁

+ 2 - 2
docs/user/guide/quickstart.i18n.yaml

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

+ 18 - 57
docs/user/guide/quickstart.md

@@ -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.
 
-This is a complete coding assistant that can read and write files, run commands, and delegate subtasks.
+## Step 3: use the TUI
 
-Try a task:
+Start the interactive coding agent:
 
+```sh
+pnpm run demo:tui
 ```
-> 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
 

+ 20 - 59
docs/user/guide/quickstart.zh.md

@@ -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` app,tui-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 或后端

+ 1 - 3
examples/AGENTS.md

@@ -11,9 +11,7 @@ Each example has both:
 - **Keyless:** boot the real `cordis.yml` through the Loader, drive it, and assert output and clean exit. Catches Loader/export-shape failures hand-mounted tests miss ([postmortem](../docs/postmortem/0001-acp-default-export-drops-inject.md)).
 - **With-key:** send a live-model prompt and verify external state, not the model's claim. Self-skip without `DEEPSEEK_API_KEY`; see [testing.md](../docs/testing.md).
 
-Mock-only examples require only the keyless tier; state that exception in the test.
-
-Keyless stdio smokes use `@deepseek-ai/dsh-loader-smoke`; tests supply paths, environment, input, and assertions. Every checked-in test Cordis config lives under its corresponding `examples/<agent>/` leaf. Map a package-owned config to `examples/<agent>/tests/fixtures/<group>/<package>/cordis.yml`, keep its driver and assertions package-local, and declare every package it names in both root `tsconfig.json` references and `examples/package.json`.
+Keyless process smokes use `@deepseek-ai/dsh-loader-smoke` for Loader launch resolution; terminal tests wrap that launch in a pseudo-terminal. Tests supply paths, environment, input, and assertions. Every checked-in test Cordis config lives under its corresponding `examples/<agent>/` leaf. Map a package-owned config to `examples/<agent>/tests/fixtures/<group>/<package>/cordis.yml`, keep its driver and assertions package-local, and declare every package it names in both root `tsconfig.json` references and `examples/package.json`.
 
 Do not inventory example tests here; the `tests/` trees and root scripts are authoritative.
 

+ 4 - 23
examples/README.md

@@ -1,37 +1,18 @@
 # Examples
 
-Runnable demos (not workspaces) that showcase how the harness is wired. Each example is a **thin leaf**: a `cordis.yml` that picks the swappable backends (an LLM adapter, a bash executor), loads one app package, and may add optional product tools or demo-only mocks. The composition — the spine, the front-door cluster, and the boot glue — lives in the app packages ([`@deepseek-ai/dsh-stdio-demo`](../packages/examples/stdio-demo), [`@deepseek-ai/dsh-cli-demo`](../packages/examples/cli-demo), [`@deepseek-ai/dsh-acp-demo`](../packages/examples/acp-demo)) and the [`@deepseek-ai/dsh-agent-spine-demo`](../packages/examples/agent-spine-demo) bundle they share. There is no `start.ts`; the `demo:*` scripts invoke each app package's `bin`.
-
-## echo-agent
-
-A mock model + echo tool on the stdio chat app — the all-mock skeleton. The leaf swaps `dsh-stdio-demo`'s LLM backend to a local `mock-echo` adapter and adds a local `echo` tool. Demonstrates:
-
-- A thin leaf `cordis.yml` loading the `@deepseek-ai/dsh-stdio-demo` app
-- Registering a mock `LlmAdapter` (streaming scripted responses)
-- Registering a tool via `ctx.tools.register()`
-- "Swap the backend, keep the app" — the only difference from `repl-agent` is the adapter
-
-Run with: `pnpm run demo:echo`. When prompted, type "echo <something>" to trigger a tool call round-trip.
-
-## repl-agent
-
-A coding agent with DeepSeek V4, the `read`/`write`/`edit` filesystem tools, the bash tool suite, `subagent` delegation, and the `todo_write` task tracker on the `@deepseek-ai/dsh-stdio-demo` app's readline front door.
-
-Run with: `pnpm run demo:repl` (needs `DEEPSEEK_API_KEY` in the environment or a gitignored repo-root `.env`). See [repl-agent/README.md](repl-agent/README.md) for details.
-
-Run the Code Mode overlay with `pnpm run demo:code-mode`, or pass `acp` for the ACP example. See the [Code Mode example](repl-agent/README.md#code-mode) for its composition and a sample task.
+Runnable demos (not workspaces) that showcase how the harness is wired. Each example is a **thin leaf**: a `cordis.yml` that picks swappable backends, loads one app package, and may add optional product tools. The composition and boot glue live in [`@deepseek-ai/dsh-tui-demo`](../packages/examples/tui-demo), [`@deepseek-ai/dsh-cli-demo`](../packages/examples/cli-demo), [`@deepseek-ai/dsh-acp-demo`](../packages/examples/acp-demo), and their shared [`@deepseek-ai/dsh-agent-spine-demo`](../packages/examples/agent-spine-demo) bundle. There is no `start.ts`; the `demo:*` scripts invoke each app package's bin.
 
 ## headless-agent
 
 A non-interactive agent demo that accepts one positional task, runs one complete model/tool turn on the `@deepseek-ai/dsh-cli-demo` app, persists a fresh session, prints `text`, `json`, or `stream-json`, and exits.
 
-Run with: `pnpm run demo:headless -- "task"` (needs `DEEPSEEK_API_KEY`). See [headless-agent/README.md](headless-agent/README.md) for the output contract, safety boundaries, and snapshot suite.
+Run with: `pnpm run demo:headless "task"` (needs `DEEPSEEK_API_KEY`). See [headless-agent/README.md](headless-agent/README.md) for the output contract, safety boundaries, and snapshot suite.
 
 ## tui-agent
 
-The full-screen terminal sibling of `repl-agent`: it reuses the same coding backends and tools while forcing the shared terminal app to `dsh-tui`. It is the home of TUI PTY and snapshot scenarios.
+The interactive coding agent: DeepSeek V4, filesystem and bash tools, subagents, workflows, `todo_write`, compaction, and the full-screen TUI. It is also the home of TUI PTY and snapshot scenarios.
 
-Run with: `pnpm run demo:tui` (needs `DEEPSEEK_API_KEY`). See [tui-agent/README.md](tui-agent/README.md) for controls and composition.
+Run with: `pnpm run demo:tui` (needs `DEEPSEEK_API_KEY`). Run its Code Mode overlay with `pnpm run demo:code-mode`. See [tui-agent/README.md](tui-agent/README.md) for controls and composition.
 
 ## jsonrpc-agent
 

+ 1 - 1
examples/cordis-agent/README.md

@@ -1,6 +1,6 @@
 # cordis-agent
 
-The self-referential harness demo: the coding spine (DeepSeek V4 + local bash on the stdio chat app) plus [`@deepseek-ai/dsh-tool-cordis`](../../packages/cordis/tool-cordis/README.md), which hands the model three tools over the **live cordis runtime it is running inside** — inspect it, mount new plugins into it, and dispose them again. The `ctx.fs` and `ctx.web` services are mounted (provider-only, no model-facing file/web tools) so the plugins the agent writes have real capabilities to build on; Node built-ins are trapped in the sandbox and redirect to those services. The design (sandbox semantics, mount lifecycle, cross-mount composition, caveats) lives in [the toolset Agent Note](../../.agents/notes/implemented/feature/2026-07-08-self-referential-cordis-toolset.md).
+The self-referential harness demo: the DeepSeek V4 coding spine on the full-screen TUI plus [`@deepseek-ai/dsh-tool-cordis`](../../packages/cordis/tool-cordis/README.md), which hands the model three tools over the **live cordis runtime it is running inside** — inspect it, mount new plugins into it, and dispose them again. The `ctx.fs` and `ctx.web` services are mounted (provider-only, no model-facing file/web tools) so the plugins the agent writes have real capabilities to build on; Node built-ins are trapped in the sandbox and redirect to those services. The design (sandbox semantics, mount lifecycle, cross-mount composition, caveats) lives in [the toolset Agent Note](../../.agents/notes/implemented/feature/2026-07-08-self-referential-cordis-toolset.md).
 
 ## Run it
 

+ 6 - 6
examples/cordis-agent/composition.md

@@ -20,11 +20,11 @@ flowchart LR
   cfg --> plugin_cordis_web
   plugin_cordis_web_fetch_local["web-fetch-local<br/>@deepseek-ai/dsh-web-fetch-local"]
   cfg --> plugin_cordis_web_fetch_local
-  plugin_cordis_stdio_agent["stdio-agent<br/>@deepseek-ai/dsh-stdio-demo"]
-  cfg --> plugin_cordis_stdio_agent
-  plugin_cordis_stdio_agent --> bundle_agent_core["@deepseek-ai/dsh-agent-spine-demo"]
-  plugin_cordis_stdio_agent --> bundle_jsonl["@deepseek-ai/dsh-session-persistence-jsonl"]
-  plugin_cordis_stdio_agent --> frontdoor_stdio["dsh-tui (TTY) / dsh-stdio (pipes)<br/>pre-created main agent"]
+  plugin_cordis_tui_agent["tui-agent<br/>@deepseek-ai/dsh-tui-demo"]
+  cfg --> plugin_cordis_tui_agent
+  plugin_cordis_tui_agent --> bundle_agent_core["@deepseek-ai/dsh-agent-spine-demo"]
+  plugin_cordis_tui_agent --> bundle_jsonl["@deepseek-ai/dsh-session-persistence-jsonl"]
+  plugin_cordis_tui_agent --> frontdoor_tui["@deepseek-ai/dsh-tui<br/>pre-created main agent"]
   bundle_agent_core --> spine_llm["ctx.llm"]
   bundle_agent_core --> spine_sessions["ctx.sessions"]
   bundle_agent_core --> spine_tools["ctx.tools + tool-bash"]
@@ -41,7 +41,7 @@ flowchart LR
 | `fs-local` | `@deepseek-ai/dsh-fs-local` |
 | `web` | `@deepseek-ai/dsh-web` |
 | `web-fetch-local` | `@deepseek-ai/dsh-web-fetch-local` |
-| `stdio-agent` | `@deepseek-ai/dsh-stdio-demo` |
+| `tui-agent` | `@deepseek-ai/dsh-tui-demo` |
 | `tool-cordis` | `@deepseek-ai/dsh-tool-cordis` |
 
 Source config: [`examples/cordis-agent/cordis.yml`](cordis.yml).

+ 3 - 3
examples/cordis-agent/cordis.yml

@@ -1,4 +1,4 @@
-# Self-referential stdio demo: the coding spine plus tools to inspect the live
+# Self-referential TUI demo: the coding spine plus tools to inspect the live
 # service/plugin/tool/mount/API/event state, mount a model-written plugin under
 # `cordis-dynamic`, and quiescently unmount it. The app bin loads the gitignored
 # root `.env` before reading the required DeepSeek key and optional base URL.
@@ -45,8 +45,8 @@
   name: '@deepseek-ai/dsh-web-fetch-local'
 
 # The app bundle pre-creates the self-referential demo's `main` agent.
-- id: stdio-agent
-  name: '@deepseek-ai/dsh-stdio-demo'
+- id: tui-agent
+  name: '@deepseek-ai/dsh-tui-demo'
   config:
     provider: deepseek
     model: deepseek-v4-flash

+ 9 - 13
examples/cordis-agent/tests/keyless-smoke.e2e.ts

@@ -1,27 +1,23 @@
 import { fileURLToPath } from 'node:url'
 import { describe, expect, it } from 'vitest'
-import { LOADER_SMOKE_TEST_TIMEOUT_MS, runLoaderSmoke } from '@deepseek-ai/dsh-loader-smoke'
+import { LOADER_SMOKE_TEST_TIMEOUT_MS } from '@deepseek-ai/dsh-loader-smoke'
+import { runTuiPtySmoke } from '../../tui-agent/tests/pty-harness.ts'
 
-/**
- * Keyless Loader-path smoke for examples/cordis-agent: boot the real tree,
- * including tool-cordis resolved by package name, then close stdin without a
- * prompt and assert the banner. The dummy key never reaches a model call.
- */
-
-const binScript = fileURLToPath(new URL('../../../packages/examples/stdio-demo/src/bin.ts', import.meta.url))
+const binScript = fileURLToPath(new URL('../../../packages/examples/tui-demo/src/bin.ts', import.meta.url))
 const configPath = fileURLToPath(new URL('../cordis.yml', import.meta.url))
 const tsconfigPath = fileURLToPath(new URL('../../../tsconfig.json', import.meta.url))
 
-describe('cordis-agent keyless smoke (real cordis.yml via the Loader)', () => {
-  it('boots the full plugin tree incl. tool-cordis, prints its banner, and exits cleanly on EOF', async () => {
-    const { stdout } = await runLoaderSmoke({
+describe('cordis-agent keyless smoke (real Loader tree in a PTY)', () => {
+  it('boots the full tool-cordis tree and exits cleanly through the TUI', async () => {
+    const output = await runTuiPtySmoke({
       label: 'cordis-agent',
-      tempDirPrefix: 'cordis-smoke-',
+      tempDirPrefix: 'cordis-agent-smoke-',
       binScript,
       configPath,
       tsconfigPath,
       env: { DEEPSEEK_API_KEY: 'keyless-smoke-no-call' },
+      actions: [{ waitFor: 'cordis-agent ready.', send: '/exit\r' }],
     })
-    expect(stdout).toContain('cordis-agent ready.')
+    expect(output).toContain('cordis-agent ready.')
   }, LOADER_SMOKE_TEST_TIMEOUT_MS)
 })

+ 0 - 34
examples/echo-agent/README.md

@@ -1,34 +0,0 @@
-# echo-agent
-
-Runnable demo: stdin chat with a scripted mock model and an echo tool. The all-mock skeleton — "swap the backend, keep the app".
-
-## What it shows
-
-This example is just a leaf `cordis.yml`: it loads the [`@deepseek-ai/dsh-stdio-demo`](../../packages/examples/stdio-demo) app (which bundles the whole [`@deepseek-ai/dsh-agent-spine-demo`](../../packages/examples/agent-spine-demo) spine, JSONL persistence, the TTY-selected `dsh-tui`/`dsh-stdio` front doors, and a pre-created `main` agent), and swaps in two example-local backends plus `hmr`:
-
-- `mock-llm.ts` — a mock `LlmAdapter` that streams scripted responses and calls the `echo` tool when the user types "echo <something>". Registered with `ctx.llm.registerAdapter(['mock-echo'], …)`.
-- `echo-tool.ts` — a tool registered via `ctx.tools.register(defineTool(…))` with typed `execute` args; echoes text back uppercased.
-
-Swapping `mock-llm` for the real `llm-deepseek` adapter is all that separates this from `repl-agent` — the same app, a different backend.
-
-## Plugin files
-
-| File | Role | Key patterns demonstrated |
-|---|---|---|
-| `src/mock-llm.ts` | `LlmAdapter` registration | `ctx.llm.registerAdapter(['mock-echo'], …)`, streaming chunks with the proper `block-start`/`block-end` protocol |
-| `src/echo-tool.ts` | Tool registration | `ctx.tools.register(defineTool(…))` with typed `execute` args, returning `ContentBlock[]` |
-| `cordis.yml` | Leaf wiring | the two backends + `hmr` + one `@deepseek-ai/dsh-stdio-demo` entry carrying the app config |
-
-The spine, UI, persistence, and boot glue all live in `@deepseek-ai/dsh-stdio-demo` and the bundle it loads — this folder holds only the demo-specific mocks and the leaf wiring.
-
-## Run
-
-```sh
-pnpm run demo:echo
-# or:
-node --expose-internals --import tsx packages/examples/stdio-demo/src/bin.ts examples/echo-agent/cordis.yml
-```
-
-Type a message and press Enter. "echo <text>" triggers a tool call round-trip (the mock model requests the `echo` tool, which echoes the text uppercased, and the next model step acknowledges it).
-
-The session is persisted under `.sessions/` relative to the directory you launch the demo from. `pnpm run demo:echo` runs from the repo root, so the logs land in `<repo-root>/.sessions/cwd-<hash>/` (one `.jsonl.zstd` log per session). Clean up with: `rm -rf .sessions`

+ 0 - 43
examples/echo-agent/composition.md

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

+ 0 - 43
examples/echo-agent/cordis.yml

@@ -1,43 +0,0 @@
-# Stdio agent with the network-free `mock-echo` adapter and example-local `echo`
-# tool. The app bundle supplies the spine; this leaf selects backends, HMR, and app config.
-# No API key: the `mock-echo` adapter never touches the network.
-
-# Hot-module reload for the dev/demo loop (a leaf entry, not baked into
-# dsh-stdio-demo — it needs `node --expose-internals`, which `demo:echo` passes).
-- id: hmr
-  name: '@cordisjs/plugin-hmr'
-  config:
-    root: ['.']
-
-# Example-local model and tool plugins resolve relative to this file.
-- id: mock-llm
-  name: './src/mock-llm.ts'
-
-- id: echo-tool
-  name: './src/echo-tool.ts'
-
-# Local bash executor: agent-spine-demo ships the `tool-bash` consumer schema, so the
-# leaf provides the executor it runs on (the echo demo doesn't drive bash, but
-# the tool is part of the shared spine).
-- id: bash
-  name: '@deepseek-ai/dsh-bash-local'
-
-# Local filesystem provider for agent-spine-demo's workspace-context loader. This
-# does not expose model-facing read/write/edit tools in the echo demo.
-- id: fs-local
-  name: '@deepseek-ai/dsh-fs-local'
-  config:
-    cwd: !!js process.cwd()
-
-# The app pre-creates `main` on the mock model and supplies persistence plus
-# TTY-selected `dsh-tui`/`dsh-stdio` front doors; readline mode also owns logging.
-- id: stdio-agent
-  name: '@deepseek-ai/dsh-stdio-demo'
-  config:
-    provider: mock
-    model: mock-echo
-    persona: 'You are echo-agent, a demo agent.'
-    welcome: 'echo-agent ready. Type a message ("echo <text>" triggers the tool).'
-    persistenceRoot: './.sessions'
-    workspaceContext:
-      maxBytes: 65536

+ 0 - 7
examples/echo-agent/package.json

@@ -1,7 +0,0 @@
-{
-  "name": "echo-agent-example",
-  "private": true,
-  "version": "0.0.1",
-  "type": "module",
-  "description": "Runnable demo: stdin chat with a scripted mock model + echo tool"
-}

+ 0 - 19
examples/echo-agent/src/echo-tool.ts

@@ -1,19 +0,0 @@
-import type { Context } from 'cordis'
-import { defineTool } from '@deepseek-ai/dsh-tools'
-
-export const name = 'echo-tool'
-export const inject = ['tools']
-
-export function apply(ctx: Context) {
-  ctx.tools.register(defineTool({
-    name: 'echo',
-    description: 'Echo the given text back, uppercased.',
-    parameters: {
-      text: { type: 'string', required: true },
-    },
-    async execute(args) {
-      // args is typed: { text: string }
-      return [{ type: 'text', text: `ECHO: ${args.text.toUpperCase()}` }]
-    },
-  }))
-}

+ 0 - 59
examples/echo-agent/src/mock-llm.ts

@@ -1,59 +0,0 @@
-import type { Context } from 'cordis'
-import type { GenerateOptions, StreamChunk } from '@deepseek-ai/dsh-llm'
-import { CallId, LlmAdapter } from '@deepseek-ai/dsh-llm'
-
-/**
- * Demo adapter for the `mock-echo` model.
- *
- * Behavior: if the last user text starts with "echo ", it calls the `echo`
- * tool with the rest of the line (exercising the tool round-trip), otherwise
- * it streams a canned reply quoting the input.
- */
-class MockEchoAdapter extends LlmAdapter {
-  async * stream(options: GenerateOptions): AsyncIterable<StreamChunk> {
-    const lastUserText = [...options.messages].reverse()
-      .filter(message => message.role === 'user')
-      .flatMap(message => message.content)
-      .filter(block => block.type === 'text')
-      .map(block => block.text)
-      .find(text => !text.startsWith('<')) ?? ''
-
-    const hasToolResult = options.messages.at(-1)?.content.some(block => block.type === 'tool-result')
-
-    if (lastUserText.startsWith('echo ') && !hasToolResult) {
-      const payload = lastUserText.slice(5)
-      const args = JSON.stringify({ text: payload })
-      yield { type: 'block-start', index: 0, blockType: 'text' }
-      for (const char of 'Let me echo that for you.') {
-        yield { type: 'text-delta', index: 0, text: char }
-        await new Promise(resolve => setTimeout(resolve, 2))
-      }
-      yield { type: 'block-end', index: 0, block: { type: 'text', text: 'Let me echo that for you.' } }
-      yield { type: 'block-start', index: 1, blockType: 'tool-call' }
-      yield { type: 'tool-call-delta', index: 1, id: CallId('call-echo'), name: 'echo', argumentsDelta: args }
-      yield { type: 'block-end', index: 1, block: { type: 'tool-call', id: CallId('call-echo'), name: 'echo', arguments: args } }
-      yield { type: 'usage', usage: { inputTokens: 20, outputTokens: 10 } }
-      yield { type: 'finish', reason: { kind: 'tool-calls' } }
-      return
-    }
-
-    const reply = hasToolResult
-      ? 'The echo tool has spoken.'
-      : `You said: "${lastUserText}". Try "echo <something>" to see a tool call.`
-    yield { type: 'block-start', index: 0, blockType: 'text' }
-    for (const char of reply) {
-      yield { type: 'text-delta', index: 0, text: char }
-      await new Promise(resolve => setTimeout(resolve, 2))
-    }
-    yield { type: 'block-end', index: 0, block: { type: 'text', text: reply } }
-    yield { type: 'usage', usage: { inputTokens: 20, outputTokens: reply.length } }
-    yield { type: 'finish', reason: { kind: 'stop' } }
-  }
-}
-
-export const name = 'mock-llm'
-export const inject = ['llm']
-
-export function apply(ctx: Context) {
-  ctx.llm.registerAdapter(['mock'], new MockEchoAdapter())
-}

+ 0 - 43
examples/echo-agent/tests/echo.e2e.ts

@@ -1,43 +0,0 @@
-import { fileURLToPath } from 'node:url'
-import { describe, expect, it } from 'vitest'
-import { LOADER_SMOKE_TEST_TIMEOUT_MS, runLoaderSmoke } from '@deepseek-ai/dsh-loader-smoke'
-
-/**
- * Keyless-by-nature Loader-path coverage for examples/echo-agent. The real
- * tree uses its deterministic mock model, so this suite is both the boot smoke
- * and the complete behavior proof for the example.
- */
-
-const binScript = fileURLToPath(new URL('../../../packages/examples/stdio-demo/src/bin.ts', import.meta.url))
-const configPath = fileURLToPath(new URL('../cordis.yml', import.meta.url))
-const tsconfigPath = fileURLToPath(new URL('../../../tsconfig.json', import.meta.url))
-
-async function runEcho(stdinLines: readonly string[]): Promise<string> {
-  const { stdout } = await runLoaderSmoke({
-    label: 'echo-agent',
-    tempDirPrefix: 'echo-smoke-',
-    binScript,
-    configPath,
-    tsconfigPath,
-    stdinLines,
-  })
-  return stdout
-}
-
-describe('echo-agent keyless smoke (real cordis.yml via the Loader)', () => {
-  it('boots, prints its welcome banner, and exits cleanly on stdin EOF', async () => {
-    expect(await runEcho([])).toContain('echo-agent ready.')
-  }, LOADER_SMOKE_TEST_TIMEOUT_MS)
-
-  it('runs the echo tool round-trip for an "echo …" line', async () => {
-    const stdout = await runEcho(['echo hello world'])
-    expect(stdout).toContain('[tool call] echo')
-    expect(stdout).toContain('[tool result] ECHO: HELLO WORLD')
-  }, LOADER_SMOKE_TEST_TIMEOUT_MS)
-
-  it('streams a direct canned reply for a non-echo line', async () => {
-    const stdout = await runEcho(['just chatting'])
-    expect(stdout).toContain('just chatting')
-    expect(stdout).not.toContain('[tool call]')
-  }, LOADER_SMOKE_TEST_TIMEOUT_MS)
-})

+ 1 - 1
examples/headless-agent/README.md

@@ -8,7 +8,7 @@ Headless one-shot agent wiring: DeepSeek V4 + local bash and filesystem tools +
 # repo root .env (gitignored) or exported env:
 #   DEEPSEEK_API_KEY=sk-…
 #   DEEPSEEK_BASE_URL=https://…   # optional; defaults to the public API
-pnpm run demo:headless -- "fix the failing test in this workspace"
+pnpm run demo:headless "fix the failing test in this workspace"
 pnpm run demo:headless --output-format json -- "summarize the implementation"
 pnpm run demo:headless --output-format stream-json -- "run the focused tests"
 ```

+ 0 - 0
examples/repl-agent/tests/code-mode.e2e.ts → examples/headless-agent/tests/code-mode.e2e.ts


+ 0 - 0
examples/repl-agent/tests/coding-task.e2e.ts → examples/headless-agent/tests/coding-task.e2e.ts


+ 0 - 0
examples/repl-agent/tests/compaction.e2e.ts → examples/headless-agent/tests/compaction.e2e.ts


+ 16 - 0
examples/headless-agent/tests/fixtures/time-context-driver.ts

@@ -0,0 +1,16 @@
+#!/usr/bin/env node
+/** Test driver that sends two turns through one Headless Loader composition. */
+
+import { boot, resolveConfigPath } from '@deepseek-ai/dsh-app-boot'
+import { runOneShot } from '@deepseek-ai/dsh-cli-demo/src/cli.ts'
+
+const configPath = process.argv[2]
+if (configPath === undefined) throw new Error('time-context driver requires a config path')
+
+const ctx = await boot('time-context-e2e', resolveConfigPath(configPath, undefined))
+try {
+  await runOneShot(ctx, { task: 'first' })
+  await runOneShot(ctx, { task: 'second' })
+} finally {
+  await ctx.fiber.dispose()
+}

+ 22 - 0
examples/headless-agent/tests/fixtures/time-context-mock-llm.ts

@@ -0,0 +1,22 @@
+import type { Context } from 'cordis'
+import { LlmAdapter, type StreamChunk } from '@deepseek-ai/dsh-llm'
+
+/** Deterministic one-step adapter for the time-context Loader fixture. */
+class TimeContextMockAdapter extends LlmAdapter {
+  async * stream(): AsyncIterable<StreamChunk> {
+    const text = 'time context sampled'
+    yield { type: 'block-start', index: 0, blockType: 'text' }
+    yield { type: 'text-delta', index: 0, text }
+    yield { type: 'block-end', index: 0, block: { type: 'text', text } }
+    yield { type: 'usage', usage: { inputTokens: 1, outputTokens: 1 } }
+    yield { type: 'finish', reason: { kind: 'stop' } }
+  }
+}
+
+export const name = 'time-context-mock-llm'
+export const inject = ['llm']
+
+/** Register the test-only `time-context-mock` adapter. */
+export function apply(ctx: Context): void {
+  ctx.llm.registerAdapter(['time-context-mock'], new TimeContextMockAdapter())
+}

+ 6 - 7
examples/echo-agent/tests/fixtures/context/time-context/cordis.yml → examples/headless-agent/tests/fixtures/time-context.cordis.yml

@@ -1,6 +1,6 @@
 # Test-only composition: keep time-context opt-in while exercising its real Loader/app path.
-- id: mock-llm
-  name: '../../../../src/mock-llm.ts'
+- id: time-context-mock-llm
+  name: './time-context-mock-llm.ts'
 
 - id: bash
   name: '@deepseek-ai/dsh-bash-local'
@@ -8,13 +8,12 @@
 - id: time-context
   name: '@deepseek-ai/dsh-time-context'
 
-- id: stdio-agent
-  name: '@deepseek-ai/dsh-stdio-demo'
+- id: cli-agent
+  name: '@deepseek-ai/dsh-cli-demo'
   config:
-    provider: mock
-    model: mock-echo
+    provider: time-context-mock
+    model: time-context-mock
     persona: 'Test the time-context plugin.'
-    welcome: 'time-context e2e ready.'
     persistenceRoot: './.sessions'
     persistenceCompression: 'none'
     workspaceContext: false

+ 0 - 0
examples/repl-agent/tests/full-loop.e2e.ts → examples/headless-agent/tests/full-loop.e2e.ts


+ 1 - 1
examples/repl-agent/tests/harness.ts → examples/headless-agent/tests/harness.ts

@@ -15,7 +15,7 @@ import { BasicCompactService } from '@deepseek-ai/dsh-compact-basic'
 import type { BasicCompactConfig } from '@deepseek-ai/dsh-compact-basic'
 
 /**
- * Shared harness for the repl-agent e2e suites: the full plugin stack
+ * Shared harness for the headless-agent e2e suites: the full plugin stack
  * with the real DeepSeek adapter and the real bash + todo_write tools. Lives
  * outside the *.e2e.ts pattern so importing it never re-registers another
  * file's tests.

+ 0 - 0
examples/repl-agent/tests/resume.e2e.ts → examples/headless-agent/tests/resume.e2e.ts


+ 0 - 0
examples/repl-agent/tests/todo-write.e2e.ts → examples/headless-agent/tests/todo-write.e2e.ts


+ 2 - 1
examples/package.json

@@ -9,6 +9,7 @@
     "@cordisjs/plugin-include": "workspace:*",
     "@deepseek-ai/dsh-acp-demo": "workspace:*",
     "@deepseek-ai/dsh-agent-spine-demo": "workspace:*",
+    "@deepseek-ai/dsh-app-boot": "workspace:*",
     "@deepseek-ai/dsh-bash-local": "workspace:*",
     "@deepseek-ai/dsh-bash-sandbox": "workspace:*",
     "@deepseek-ai/dsh-cli-demo": "workspace:*",
@@ -31,7 +32,7 @@
     "@deepseek-ai/dsh-session-persistence-jsonl": "workspace:*",
     "@deepseek-ai/dsh-spill-local": "workspace:*",
     "@deepseek-ai/dsh-spill-policy": "workspace:*",
-    "@deepseek-ai/dsh-stdio-demo": "workspace:*",
+    "@deepseek-ai/dsh-tui-demo": "workspace:*",
     "@deepseek-ai/dsh-subagent": "workspace:*",
     "@deepseek-ai/dsh-subagent-fork": "workspace:*",
     "@deepseek-ai/dsh-subagent-spawn": "workspace:*",

+ 0 - 68
examples/repl-agent/README.md

@@ -1,68 +0,0 @@
-# repl-agent
-
-The repl-agent wiring: DeepSeek V4 + the `read`/`write`/`edit` filesystem tools + the bash tool suite + subagent delegation + workflows + `todo_write` + readline chat + JSONL persistence, loaded from `cordis.yml`. The sibling [`tui-agent`](../tui-agent/README.md) fixes the same agent composition to the full-screen terminal front door.
-
-## Run it
-
-```sh
-# repo root .env (gitignored) or exported env:
-#   DEEPSEEK_API_KEY=sk-…
-#   DEEPSEEK_BASE_URL=https://…   # optional; defaults to the public API
-pnpm run demo:repl
-```
-
-Type a coding task. The agent works through the `read`/`write`/`edit` filesystem tools for ordinary file operations and `bash` (+ the generic `task_output` / `task_list` / `task_kill` for background tasks) for shell commands, searches, and test runs, each in a fresh `bash -c` (the system prompt tells the model to pass `workdir` instead of `cd`). Both the fs tools and bash resolve relative paths against the session workspace. It can also delegate with `subagent`/`subagent_fork` and track multi-step work with `todo_write`.
-
-The REPL renders reasoning, tool calls/results, and the latest todo list as line-oriented output suitable for terminals and pipes. Use `pnpm run demo:tui` for the interactive Markdown/card interface.
-
-### Resuming a prior session
-
-Each run starts a fresh session by default (its event log lands under `./.sessions/`). To **continue** a previous conversation, set `RESUME_SESSION_ID` to that session's id — the `main` agent then rehydrates the persisted log instead of starting fresh, so the model sees the earlier turns as history:
-
-```sh
-RESUME_SESSION_ID=<prior-session-id> pnpm run demo:repl
-```
-
-The id is wired through `cordis.yml` (`resumeSessionId: !!js process.env.RESUME_SESSION_ID`); unset, the agent starts a new session. A missing or unreadable id starts no agent and emits `agent-loop/config-start-failed`: the TUI prints the failure and exits nonzero, while readline reports any dropped queued input and allows piped EOF to finish. Unset it or choose an existing session id.
-
-## Code Mode
-
-[`code-mode.cordis.yml`](code-mode.cordis.yml) overlays the same tree with the worker-thread runtime and `tools: { mode: code }`. The model receives one `run_code` transport plus a generated TypeScript SDK for the visible tools; only program output returns to model context. Use `mode: both` to expose native calls alongside `run_code`. See the [Code Mode Agent Note](../../.agents/notes/implemented/feature/2026-06-15-code-mode.md) for the execution contract.
-
-```sh
-pnpm run demo:code-mode        # this overlay under the REPL (default UI)
-pnpm run demo:code-mode acp    # the acp-agent example's same-shaped overlay
-```
-
-Try a task that spans several tool calls, e.g.:
-
-> Count the lines of every `*.md` file under docs/ and write the three largest to summary.txt.
-
-and watch the transcript: one `run_code` call, a program looping over tools, and a result the model curated instead of five round-trips of raw tool output.
-
-## What each leaf entry demonstrates
-
-This example is a thin leaf `cordis.yml`: it picks the swappable backends, loads one app package, and adds product tools that are intentionally outside the shared spine. The spine (sessions, system-prompt, tools, agents, invariants, `agent-loop`) and the front-door cluster (JSONL persistence, the selected terminal channel, the pre-created `main` agent) live inside the [`@deepseek-ai/dsh-stdio-demo`](../../packages/examples/stdio-demo) app and the [`@deepseek-ai/dsh-agent-spine-demo`](../../packages/examples/agent-spine-demo) bundle it loads; the leaf wires the backends and model-facing optional tools:
-
-| Entry | Demonstrates |
-|---|---|
-| `hmr` (`@cordisjs/plugin-hmr`) | the dev/demo edit-reload loop — a **leaf** entry (not baked into the app) because it is Loader-only and needs `node --expose-internals`, which `demo:repl` passes |
-| `llm-deepseek` | real `LlmAdapter` via config (`!!js process.env.…` secrets); swap one line to `@deepseek-ai/dsh-llm-pi-ai` for the library-backed twin |
-| `bash` (`dsh-bash-local`) | the executor implementation — the swappable half of the bash seam. The model-facing `bash` schema (`tool-bash`) and generic `task_*` controls (`tool-tasks`) come from `dsh-agent-spine-demo`, so only the executor is a leaf choice |
-| `stdio-agent` (`@deepseek-ai/dsh-stdio-demo`) | the app bundle: the agent-spine demo + JSONL persistence + the configured terminal channel + a pre-created `main` agent. This leaf fixes `ui.mode` to `readline`; `tui-agent` owns the corresponding TUI leaf |
-| `token-meter`, `tool-result-prune`, `compact-basic` | replay-aware pressure, model-free oversized tool-result pruning, and LLM summary compaction. Pruning runs only after a compaction trigger qualifies and can avoid the summarization call |
-| `subagent`, `subagent-spawn`, `subagent-fork` | the subagent provider registry plus the two in-process backends: a fresh child and a child seeded with the parent's completed-turn prefix |
-| `tool-subagent`, `tool-subagent-fork` | two model-facing `dsh-tool-subagent` loads, each bound to a different provider and exposed under a distinct tool name (`subagent`, `subagent_fork`) |
-| `workflow-workerthread`, `tool-workflow` | the worker-thread workflow engine and its model-facing `workflow` tool, with child calls routed through the spawn backend |
-| `tool-todo` | the model-facing `todo_write` tool; writes the whole task list to the session log and renders as a persistent TUI plan or readline checklist |
-| `fs-local`, `fs-policy`, `tool-fs` | the filesystem stack: the local `ctx.fs` provider, the read-before-write/edit policy gate (on the `fs/*` event gate), and the model-facing `read`/`write`/`edit` tools. Relative paths resolve against the session workspace |
-
-## End-to-end tests (`pnpm run test:e2e`, key-gated)
-
-- `tests/full-loop.e2e.ts` — the canary: real model runs `echo e2e-ok` through the real bash tool; asserts `tool/call`/`tool/result` session events and the final answer.
-- `tests/coding-task.e2e.ts` — the swebench-style smoke: a temp dir holds `add.js` (with `a - b` where `a + b` belongs) and a failing `add.test.js`; the agent must fix the bug and verify. The test re-runs `node add.test.js` ITSELF and inspects the files — agent claims are not trusted.
-- `tests/resume.e2e.ts` — durable continuity across processes: run 1 tells the real model a secret code and persists the turn to a temp JSONL root, then the whole context is disposed; run 2 is a fresh context over the same root that RESUMES the session id and asks the model to recall the code. The recall can only come from the rehydrated log.
-- `tests/compaction.e2e.ts` — the compaction smoke: a real multi-step bash task runs with a deliberately tiny context window so automatic pruning or summary compaction fires mid-session. It verifies the world: a replayable surface replacement lands, summary brackets are complete when summarization is needed, the surface shrinks, and the agent still produces a correct final answer.
-- `tests/todo-write.e2e.ts` — a real model drives the real `todo_write` tool and the test verifies the resulting `todo/write` session event.
-
-These self-skip without `DEEPSEEK_API_KEY`. `tests/code-mode.e2e.ts` is the with-key Code Mode proof — a real model, a two-tool task, asserting the wire tool list was exactly `[run_code]`, the `tool/code-dispatch` events landed under the parent call, and the curated answer came back. The keyless Loader smokes run in the default e2e gate: `tests/keyless-smoke.e2e.ts` and `tests/code-mode-keyless-smoke.e2e.ts`.

+ 0 - 33
examples/repl-agent/code-mode.cordis.yml

@@ -1,33 +0,0 @@
-# Code Mode adds `ctx.codeRuntime` and changes the registry to one wire tool,
-# `run_code`, plus a generated SDK for bash/read/write/edit/subagent/todo_write.
-# `demo:code-mode` selects this overlay; the ACP example has the same UI-specific
-# shape. A config patch replaces the whole app config, so unchanged base fields
-# are restated; only `tools`, `welcome`, and the persona's second paragraph differ.
-- id: base
-  name: '@cordisjs/plugin-include'
-  config:
-    path: ./cordis.yml
-    patches:
-      - id: stdio-agent
-        name: '@deepseek-ai/dsh-stdio-demo'
-        config:
-          provider: deepseek
-          model: deepseek-v4-flash
-          resumeSessionId: !!js process.env.RESUME_SESSION_ID
-          persistenceRoot: './.sessions'
-          workspaceContext:
-            maxBytes: 65536
-          tools:
-            mode: code
-          welcome: 'code-mode agent ready. Give it a multi-tool task.'
-          ui:
-            mode: readline
-          persona: |
-            You are a coding agent powered by the {{model}} model.
-
-            You work by writing TypeScript programs for run_code: batch related
-            tool work into one program, loop and branch where it helps, and print
-            or return ONLY the findings that matter.
-      - insert:
-          - id: code-runtime
-            name: '@deepseek-ai/dsh-code-runtime-worker'

+ 0 - 91
examples/repl-agent/composition.md

@@ -1,91 +0,0 @@
-<!-- Generated by scripts/gen-doc-graphs.ts - do not edit by hand.
-     Run `pnpm run gen-doc-graphs` to regenerate. -->
-
-# REPL Agent App Composition
-
-The REPL agent demo adds the real DeepSeek adapter, filesystem tools, todo_write, tool-result pruning, compaction, and both subagent transports on top of the stdio app package.
-
-```mermaid
-flowchart LR
-  cfg["examples/repl-agent<br/>cordis.yml"]
-  plugin_repl_hmr["hmr<br/>@cordisjs/plugin-hmr"]
-  cfg --> plugin_repl_hmr
-  plugin_repl_llm_deepseek["llm-deepseek<br/>@deepseek-ai/dsh-llm-deepseek"]
-  cfg --> plugin_repl_llm_deepseek
-  plugin_repl_bash["bash<br/>@deepseek-ai/dsh-bash-local"]
-  cfg --> plugin_repl_bash
-  plugin_repl_stdio_agent["stdio-agent<br/>@deepseek-ai/dsh-stdio-demo"]
-  cfg --> plugin_repl_stdio_agent
-  plugin_repl_stdio_agent --> bundle_agent_core["@deepseek-ai/dsh-agent-spine-demo"]
-  plugin_repl_stdio_agent --> bundle_jsonl["@deepseek-ai/dsh-session-persistence-jsonl"]
-  plugin_repl_stdio_agent --> frontdoor_stdio["@deepseek-ai/dsh-stdio<br/>pre-created main agent"]
-  bundle_agent_core --> spine_llm["ctx.llm"]
-  bundle_agent_core --> spine_sessions["ctx.sessions"]
-  bundle_agent_core --> spine_tools["ctx.tools + tool-bash"]
-  bundle_agent_core --> spine_loop["ctx.agents + ctx.agentLoop"]
-  plugin_repl_token_meter["token-meter<br/>@deepseek-ai/dsh-token-meter"]
-  cfg --> plugin_repl_token_meter
-  plugin_repl_tool_result_prune["tool-result-prune<br/>@deepseek-ai/dsh-compact-tool-result-prune"]
-  cfg --> plugin_repl_tool_result_prune
-  plugin_repl_compact_basic["compact-basic<br/>@deepseek-ai/dsh-compact-basic"]
-  cfg --> plugin_repl_compact_basic
-  plugin_repl_subagent["subagent<br/>@deepseek-ai/dsh-subagent"]
-  cfg --> plugin_repl_subagent
-  plugin_repl_subagent_spawn["subagent-spawn<br/>@deepseek-ai/dsh-subagent-spawn"]
-  cfg --> plugin_repl_subagent_spawn
-  plugin_repl_subagent_fork["subagent-fork<br/>@deepseek-ai/dsh-subagent-fork"]
-  cfg --> plugin_repl_subagent_fork
-  plugin_repl_tool_subagent["tool-subagent<br/>@deepseek-ai/dsh-tool-subagent"]
-  cfg --> plugin_repl_tool_subagent
-  plugin_repl_tool_subagent_fork["tool-subagent-fork<br/>@deepseek-ai/dsh-tool-subagent"]
-  cfg --> plugin_repl_tool_subagent_fork
-  plugin_repl_workflow_workerthread["workflow-workerthread<br/>@deepseek-ai/dsh-workflow-workerthread"]
-  cfg --> plugin_repl_workflow_workerthread
-  plugin_repl_tool_workflow["tool-workflow<br/>@deepseek-ai/dsh-tool-workflow"]
-  cfg --> plugin_repl_tool_workflow
-  plugin_repl_tool_todo["tool-todo<br/>@deepseek-ai/dsh-tool-todo"]
-  cfg --> plugin_repl_tool_todo
-  plugin_repl_fs_local["fs-local<br/>@deepseek-ai/dsh-fs-local"]
-  cfg --> plugin_repl_fs_local
-  plugin_repl_fs_policy["fs-policy<br/>@deepseek-ai/dsh-fs-policy"]
-  cfg --> plugin_repl_fs_policy
-  plugin_repl_tool_fs["tool-fs<br/>@deepseek-ai/dsh-tool-fs"]
-  cfg --> plugin_repl_tool_fs
-  plugin_repl_tool_fs_search["tool-fs-search<br/>@deepseek-ai/dsh-tool-fs-search"]
-  cfg --> plugin_repl_tool_fs_search
-  plugin_repl_timeout_policy["timeout-policy<br/>@deepseek-ai/dsh-timeout-policy"]
-  cfg --> plugin_repl_timeout_policy
-  plugin_repl_spill_local["spill-local<br/>@deepseek-ai/dsh-spill-local"]
-  cfg --> plugin_repl_spill_local
-  plugin_repl_spill_policy["spill-policy<br/>@deepseek-ai/dsh-spill-policy"]
-  cfg --> plugin_repl_spill_policy
-```
-
-| Plugin id | Package / module |
-| --- | --- |
-| `hmr` | `@cordisjs/plugin-hmr` |
-| `llm-deepseek` | `@deepseek-ai/dsh-llm-deepseek` |
-| `bash` | `@deepseek-ai/dsh-bash-local` |
-| `stdio-agent` | `@deepseek-ai/dsh-stdio-demo` |
-| `token-meter` | `@deepseek-ai/dsh-token-meter` |
-| `tool-result-prune` | `@deepseek-ai/dsh-compact-tool-result-prune` |
-| `compact-basic` | `@deepseek-ai/dsh-compact-basic` |
-| `subagent` | `@deepseek-ai/dsh-subagent` |
-| `subagent-spawn` | `@deepseek-ai/dsh-subagent-spawn` |
-| `subagent-fork` | `@deepseek-ai/dsh-subagent-fork` |
-| `tool-subagent` | `@deepseek-ai/dsh-tool-subagent` |
-| `tool-subagent-fork` | `@deepseek-ai/dsh-tool-subagent` |
-| `workflow-workerthread` | `@deepseek-ai/dsh-workflow-workerthread` |
-| `tool-workflow` | `@deepseek-ai/dsh-tool-workflow` |
-| `tool-todo` | `@deepseek-ai/dsh-tool-todo` |
-| `fs-local` | `@deepseek-ai/dsh-fs-local` |
-| `fs-policy` | `@deepseek-ai/dsh-fs-policy` |
-| `tool-fs` | `@deepseek-ai/dsh-tool-fs` |
-| `tool-fs-search` | `@deepseek-ai/dsh-tool-fs-search` |
-| `timeout-policy` | `@deepseek-ai/dsh-timeout-policy` |
-| `spill-local` | `@deepseek-ai/dsh-spill-local` |
-| `spill-policy` | `@deepseek-ai/dsh-spill-policy` |
-
-Source config: [`examples/repl-agent/cordis.yml`](cordis.yml).
-
-Maintenance mode: hybrid: the leaf plugin list is parsed from its `cordis.yml`; app package expansion is curated from package source.

+ 0 - 145
examples/repl-agent/cordis.yml

@@ -1,145 +0,0 @@
-# Readline coding REPL with swappable DeepSeek and local-bash backends.
-# `dsh-stdio-demo` supplies the agent spine, workspace instructions, generic
-# task controls, JSONL persistence, the line-oriented front door, and `main`.
-# HMR remains a leaf because it requires Loader internals; `demo:repl` passes
-# `--expose-internals`. The app bin loads the gitignored root `.env`; this file
-# reads `DEEPSEEK_API_KEY` and optional `DEEPSEEK_BASE_URL` through `!!js`.
-
-# Hot-module reload for the dev/demo loop (needs `node --expose-internals`).
-- id: hmr
-  name: '@cordisjs/plugin-hmr'
-  config:
-    root: ['.']
-
-# The native DeepSeek adapter.
-- id: llm-deepseek
-  name: '@deepseek-ai/dsh-llm-deepseek'
-  config:
-    apiKey: !!js process.env.DEEPSEEK_API_KEY
-    baseURL: !!js process.env.DEEPSEEK_BASE_URL
-
-# Local executor for the app bundle's bash tool.
-- id: bash
-  name: '@deepseek-ai/dsh-bash-local'
-  config:
-    timeoutMs: 60000
-
-# The app bundle pre-creates the REPL's `main` agent.
-- id: stdio-agent
-  name: '@deepseek-ai/dsh-stdio-demo'
-  config:
-    provider: deepseek
-    model: deepseek-v4-flash
-    # Set RESUME_SESSION_ID to continue a prior persisted session (the ids live
-    # under ./.sessions); unset starts a fresh session each run.
-    resumeSessionId: !!js process.env.RESUME_SESSION_ID
-    persistenceRoot: './.sessions'
-    workspaceContext:
-      maxBytes: 65536
-    welcome: 'agent REPL ready. Give it a coding task.'
-    ui:
-      mode: readline
-    # Keep the persona to identity and behavior; tool plugins own tool guidance.
-    # The loop resolves {{model}} from this agent's configuration.
-    persona: |
-      You are a coding agent powered by the {{model}} model.
-
-      Verify your work by running the code or tests. Keep answers brief and
-      factual.
-
-# Replay-aware request pressure with one service-wide context window.
-- id: token-meter
-  name: '@deepseek-ai/dsh-token-meter'
-
-# Prune oversized tool output without a model call before summary compaction.
-- id: tool-result-prune
-  name: '@deepseek-ai/dsh-compact-tool-result-prune'
-
-# Summarize an older range after measured pressure or a canonical provider overflow.
-# Service-wide policy provides pressure, retention, and one overflow-retry default.
-- id: compact-basic
-  name: '@deepseek-ai/dsh-compact-basic'
-
-# Expose fresh-child `spawn` and completed-prefix `fork` through independent
-# in-process backends. Each tool instance needs a distinct `toolName`; the registry
-# rejects duplicates. These leaves follow the app because it provides `ctx.agents` and `ctx.tools`.
-- id: subagent
-  name: '@deepseek-ai/dsh-subagent'
-
-- id: subagent-spawn
-  name: '@deepseek-ai/dsh-subagent-spawn'
-  config:
-    providerName: spawn
-
-- id: subagent-fork
-  name: '@deepseek-ai/dsh-subagent-fork'
-  config:
-    providerName: fork
-
-- id: tool-subagent
-  name: '@deepseek-ai/dsh-tool-subagent'
-  config:
-    provider: spawn
-    toolName: subagent
-    maxDepth: 1
-
-- id: tool-subagent-fork
-  name: '@deepseek-ai/dsh-tool-subagent'
-  config:
-    provider: fork
-    toolName: subagent_fork
-    maxDepth: 1
-
-
-# The worker-thread workflow engine fans a model-written JavaScript script's
-# `agent()` calls out through the spawn backend; the adjacent tool exposes it to the model.
-- id: workflow-workerthread
-  name: '@deepseek-ai/dsh-workflow-workerthread'
-  config:
-    provider: spawn
-
-- id: tool-workflow
-  name: '@deepseek-ai/dsh-tool-workflow'
-# `todo_write` replaces the logged whole list and renders as a stdio checklist or ACP plan.
-- id: tool-todo
-  name: '@deepseek-ai/dsh-tool-todo'
-
-# Policy loads before the model-facing filesystem tools so writes and edits require
-# an observed file. This single-session app resolves relative paths from the process cwd.
-- id: fs-local
-  name: '@deepseek-ai/dsh-fs-local'
-  config:
-    cwd: !!js process.cwd()
-
-- id: fs-policy
-  name: '@deepseek-ai/dsh-fs-policy'
-
-- id: tool-fs
-  name: '@deepseek-ai/dsh-tool-fs'
-
-# Bash-backed discovery tools (glob/grep): if the local bash executor above
-# can find rg, register fixed ripgrep commands — not ctx.fs. Capped results
-# save the complete formatted list through the spill backend below
-# (ctx.spillStore, optional).
-- id: tool-fs-search
-  name: '@deepseek-ai/dsh-tool-fs-search'
-
-# The tool-call timeout enforcer: arms each declared ToolDefinition.timeoutMs
-# (the search tools above declare 30s) as a deadline on exec.signal. Without
-# it a declared budget is advisory and only the bash executor's own timeout
-# backstop applies.
-- id: timeout-policy
-  name: '@deepseek-ai/dsh-timeout-policy'
-
-# Tool-output spill stack: a local backend that saves oversized tool text under
-# a private session-scoped dir, and the tools/post-execute policy that replaces
-# an over-budget plain-text result with a preview + the spill locator/retrieval
-# hint. A leaf pair after the app (needs ctx.tools). The policy is a no-op until
-# a tool returns more than maxInlineBytes of plain text.
-- id: spill-local
-  name: '@deepseek-ai/dsh-spill-local'
-
-- id: spill-policy
-  name: '@deepseek-ai/dsh-spill-policy'
-  config:
-    maxInlineBytes: 50000

+ 0 - 7
examples/repl-agent/package.json

@@ -1,7 +0,0 @@
-{
-  "name": "repl-agent-example",
-  "private": true,
-  "version": "0.0.1",
-  "type": "module",
-  "description": "Runnable demo: an agent REPL UI with DeepSeek V4 and coding tools"
-}

+ 0 - 27
examples/repl-agent/tests/code-mode-keyless-smoke.e2e.ts

@@ -1,27 +0,0 @@
-import { fileURLToPath } from 'node:url'
-import { describe, expect, it } from 'vitest'
-import { LOADER_SMOKE_TEST_TIMEOUT_MS, runLoaderSmoke } from '@deepseek-ai/dsh-loader-smoke'
-
-/**
- * Keyless Loader-path smoke for the Code Mode overlay: boot the real include
- * tree through stdio-agent and `code-mode.cordis.yml`, then close stdin without
- * a prompt and assert the banner. No model or `run_code` turn runs.
- */
-
-const binScript = fileURLToPath(new URL('../../../packages/examples/stdio-demo/src/bin.ts', import.meta.url))
-const configPath = fileURLToPath(new URL('../code-mode.cordis.yml', import.meta.url))
-const tsconfigPath = fileURLToPath(new URL('../../../tsconfig.json', import.meta.url))
-
-describe('code-mode overlay keyless smoke (real code-mode.cordis.yml via the Loader)', () => {
-  it('boots the Code Mode plugin tree, prints its banner, and exits cleanly on EOF', async () => {
-    const { stdout } = await runLoaderSmoke({
-      label: 'code-mode overlay',
-      tempDirPrefix: 'code-mode-smoke-',
-      binScript,
-      configPath,
-      tsconfigPath,
-      env: { DEEPSEEK_API_KEY: 'keyless-smoke-no-call' },
-    })
-    expect(stdout).toContain('code-mode agent ready.')
-  }, LOADER_SMOKE_TEST_TIMEOUT_MS)
-})

+ 0 - 28
examples/repl-agent/tests/keyless-smoke.e2e.ts

@@ -1,28 +0,0 @@
-import { fileURLToPath } from 'node:url'
-import { describe, expect, it } from 'vitest'
-import { LOADER_SMOKE_TEST_TIMEOUT_MS, runLoaderSmoke } from '@deepseek-ai/dsh-loader-smoke'
-
-/**
- * Keyless Loader-path smoke for examples/repl-agent: boot the real example
- * through the stdio-agent bin and its `cordis.yml`, then close stdin without a
- * prompt and assert the banner. The dummy key satisfies adapter construction;
- * immediate EOF guarantees there is no model call.
- */
-
-const binScript = fileURLToPath(new URL('../../../packages/examples/stdio-demo/src/bin.ts', import.meta.url))
-const configPath = fileURLToPath(new URL('../cordis.yml', import.meta.url))
-const tsconfigPath = fileURLToPath(new URL('../../../tsconfig.json', import.meta.url))
-
-describe('repl-agent keyless smoke (real cordis.yml via the Loader)', () => {
-  it('boots the full plugin tree, prints its banner, and exits cleanly on EOF', async () => {
-    const { stdout } = await runLoaderSmoke({
-      label: 'repl-agent',
-      tempDirPrefix: 'repl-smoke-',
-      binScript,
-      configPath,
-      tsconfigPath,
-      env: { DEEPSEEK_API_KEY: 'keyless-smoke-no-call' },
-    })
-    expect(stdout).toContain('agent REPL ready.')
-  }, LOADER_SMOKE_TEST_TIMEOUT_MS)
-})

+ 6 - 6
examples/tui-agent/README.md

@@ -1,6 +1,6 @@
 # tui-agent
 
-The full-screen terminal counterpart to the [`repl-agent`](../repl-agent/README.md) readline REPL and [`acp-agent`](../acp-agent/README.md) server. It reuses the coding agent's backends and tool composition, then fixes the shared terminal app to the `dsh-tui` front door.
+The full-screen interactive coding agent: DeepSeek V4, local bash and filesystem tools, compaction, subagents, workflows, `todo_write`, timeout/spill policy, and [`@deepseek-ai/dsh-tui-demo`](../../packages/examples/tui-demo).
 
 ## Run it
 
@@ -8,16 +8,16 @@ The full-screen terminal counterpart to the [`repl-agent`](../repl-agent/README.
 pnpm run demo:tui
 ```
 
-The command needs `DEEPSEEK_API_KEY` in the environment or the gitignored repository-root `.env`. Set `RESUME_SESSION_ID` to reopen a persisted conversation under `./.sessions`.
+The command needs `DEEPSEEK_API_KEY` in the environment or gitignored repository-root `.env`. Set `RESUME_SESSION_ID` to reopen a persisted conversation under `./.sessions`.
 
-The TUI renders Markdown history, reasoning, tool-owned terminal/diff/generic cards, token totals, and the latest todo list. Enter submits or steers while the agent is running; Ctrl+O expands cards, Ctrl+R toggles reasoning, Escape cancels, and `/help` lists commands. `ask_user_question` opens a keyboard-driven overlay.
+The TUI renders Markdown history, reasoning, tool-owned terminal/diff/generic cards, token totals, and the latest todo list. Enter submits or steers while the agent runs; Ctrl+O expands cards, Ctrl+R toggles reasoning, Escape cancels, and `/help` lists commands. `ask_user_question` opens a keyboard-driven overlay.
 
-Run `pnpm run demo:code-mode tui` for the sibling Code Mode overlay.
+Run `pnpm run demo:code-mode tui` for the Code Mode overlay.
 
 ## Composition
 
-[`cordis.yml`](cordis.yml) includes the readline repl-agent leaf so the LLM, bash, filesystem, compaction, subagent, workflow, todo, timeout, and spill choices have one owner. Its asserted patch replaces only the terminal app config and forces `ui.mode: tui`; [`code-mode.cordis.yml`](code-mode.cordis.yml) applies the same front-door patch to the repl-agent Code Mode overlay.
+[`cordis.yml`](cordis.yml) owns the interactive coding composition directly. [`code-mode.cordis.yml`](code-mode.cordis.yml) includes that leaf and replaces the tool presentation mode while adding the code runtime. Non-interactive automation uses the sibling [headless-agent](../headless-agent/README.md) composition.
 
 ## Snapshot tests
 
-`tests/snapshots/<scenario>/session.jsonl` supplies recorded user prompts and model chunks; sibling child logs drive subagents and workflows. The keyless suite executes those scripts through the real loop and tool implementations, then compares readable expected terminal cell/style output. Use `pnpm run test:snapshot:refresh` for presentation-only changes and `pnpm run test:snapshot:record` with a DeepSeek key when a recorded model journey changes. The implemented [TUI snapshot Agent Note](../../.agents/notes/implemented/testing/2026-07-18-tui-terminal-state-snapshots.md) owns the scenario matrix and the split between recorded journeys, transient package snapshots, and PTY coverage.
+`tests/snapshots/<scenario>/session.jsonl` supplies recorded user prompts and model chunks; sibling child logs drive subagents and workflows. The keyless suite executes those scripts through the real loop and tools, then compares readable terminal cell/style output. Use `pnpm run test:snapshot:refresh` for presentation-only changes and `pnpm run test:snapshot:record` with a DeepSeek key when a recorded model journey changes. The implemented [TUI snapshot Agent Note](../../.agents/notes/implemented/testing/2026-07-18-tui-terminal-state-snapshots.md) owns the scenario matrix.

+ 10 - 9
examples/tui-agent/code-mode.cordis.yml

@@ -1,12 +1,12 @@
-# Code Mode keeps the TUI front door while reusing the repl-agent overlay's
-# worker runtime and one-tool registry composition.
+# Code Mode keeps the TUI composition while adding the worker runtime and
+# reducing the model-facing registry to the `run_code` transport.
 - id: base
   name: '@cordisjs/plugin-include'
   config:
-    path: ../repl-agent/code-mode.cordis.yml
+    path: ./cordis.yml
     patches:
-      - id: stdio-agent
-        name: '@deepseek-ai/dsh-stdio-demo'
+      - id: tui-agent
+        name: '@deepseek-ai/dsh-tui-demo'
         config:
           provider: deepseek
           model: deepseek-v4-flash
@@ -18,13 +18,14 @@
             mode: code
           welcome: 'TUI Code Mode ready. Give it a multi-tool task.'
           ui:
-            mode: tui
-            tui:
-              showReasoning: true
-              maxToolOutputLines: 12
+            showReasoning: true
+            maxToolOutputLines: 12
           persona: |
             You are a coding agent powered by the {{model}} model.
 
             You work by writing TypeScript programs for run_code: batch related
             tool work into one program, loop and branch where it helps, and print
             or return ONLY the findings that matter.
+      - insert:
+          - id: code-runtime
+            name: '@deepseek-ai/dsh-code-runtime-worker'

Některé soubory nejsou zobrazeny, neboť je v těchto rozdílových datech změněno mnoho souborů