Merge latest invariant service seam

# Conflicts:
#	docs/module-graph.md
#	package.json
#	packages/examples/tui-demo/package.json
#	packages/examples/tui-demo/src/invariant.ts
#	packages/ui/stdio/package.json
#	packages/ui/stdio/tests/stdio.spec.ts
#	packages/ui/stdio/tsconfig.json
#	pnpm-lock.yaml
This commit is contained in:
Tianyi Cui
2026-07-20 21:02:52 +08:00
176 changed files with 1352 additions and 4308 deletions

View File

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

View File

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

View File

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

View File

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

View File

@@ -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. Its invariant companion 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

View File

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

View File

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

View File

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

View File

@@ -2,5 +2,5 @@
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write
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

View File

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

View File

@@ -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 中增加工具专用分支。

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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` 调用会直接失败,不会被转换。

View File

@@ -2,5 +2,5 @@
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write
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

View File

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

View File

@@ -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)负责此次整合
### 已录制会话回放

View File

@@ -2,5 +2,5 @@
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write
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

View File

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

View File

@@ -44,7 +44,7 @@ create 还提供一次 `none / plugin / tool` 选择。`plugin` 固定生成 `pl
| 功能 | create 状态 | 功能选项 | 限制与关系 |
|---|---|---|---|
| `provider` | required | `deepseek`(默认)/ `custom` | DeepSeek 收集 API keycustom 另收集 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运行接口为 stdiobash 为 local持久化为 JSONLhmr、fs、todo 与 skill 处于选中状态。初始目录树为:
使用默认答案创建 npm 工程时provider 为 DeepSeek运行接口为 TUIbash 为 local持久化为 JSONLhmr、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。