mirror of
https://github.com/deepseek-ai/deepseek-harness
synced 2026-08-15 21:04:50 +00:00
Remove the Echo agent
This commit is contained in:
@@ -16,7 +16,6 @@ Each example is now **mostly an invocation of an app package**, splitting the wi
|
||||
- **`@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.
|
||||
- **echo-agent loads `dsh-cli-demo`**, swapping the LLM backend to the local `mock-llm` and adding the local `echo-tool` at the leaf. `mock-llm.ts` and `echo-tool.ts` stay as example-local teaching plugins.
|
||||
- **`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.
|
||||
@@ -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:tui`, `demo:headless`, 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,4 +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 [remove-stdio-agent decision](../simplification/2026-07-20-remove-stdio-agent.md) owns the final TUI/Headless split and removal of the line-oriented app.
|
||||
- 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,7 +2,7 @@
|
||||
|
||||
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 [remove-stdio-agent](../simplification/2026-07-20-remove-stdio-agent.md) decision subsequently removed that surface entirely. The uniform depth-two hierarchy remains the decision owned here.
|
||||
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
|
||||
|
||||
|
||||
@@ -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: c7bd01011121f04683afde1d05b046970e497a71
|
||||
2026-07-17-dedicated-full-screen-tui-front-door.zh.md: 5de443f367725a116e70d368657562b6732904e7
|
||||
2026-07-17-dedicated-full-screen-tui-front-door.md: 8fbc5dddc029190b346075a65c9e7857187f3d2b
|
||||
2026-07-17-dedicated-full-screen-tui-front-door.zh.md: 6ddc3523b7a7173013efe2ef15c5ca0e940929fb
|
||||
|
||||
@@ -6,7 +6,7 @@ English | [中文](2026-07-17-dedicated-full-screen-tui-front-door.zh.md)
|
||||
|
||||
## Problem
|
||||
|
||||
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 [remove-stdio-agent decision](../simplification/2026-07-20-remove-stdio-agent.md) removes that redundant line agent; this Note continues to own the TUI design.
|
||||
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.
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ Status: implemented
|
||||
|
||||
## 问题
|
||||
|
||||
在本入口引入时,面向行的 agent 负责 pipe 与普通终端,但全屏 coding 界面必须负责原始输入、差分绘制、光标状态、浮层和终端恢复。把这两类契约合并到一个 UI 插件中,会迫使面向 stream 的路径依赖仅适用于 TTY 的生命周期。后续的[移除 stdio agent 决策](../simplification/2026-07-20-remove-stdio-agent.md)移除了这一重复的面向行 agent;本 Note 继续负责 TUI 设计。
|
||||
在本入口引入时,面向行的 agent 负责 pipe 与普通终端,但全屏 coding 界面必须负责原始输入、差分绘制、光标状态、浮层和终端恢复。把这两类契约合并到一个 UI 插件中,会迫使面向 stream 的路径依赖仅适用于 TTY 的生命周期。后续的[移除重复 agent 决策](../simplification/2026-07-20-remove-stdio-and-echo-agents.md)移除了这个面向行 agent;本 Note 继续负责 TUI 设计。
|
||||
|
||||
交互通道必须继续作为 Cordis 插件,使用与其他入口相同的 agent(智能体)、会话、工具和用户交互服务。它需要恢复持久历史、跟随压缩替换、显示工具自有的呈现内容,并在启动失败和资源释放时恢复终端。独立聊天应用或第二套 agent 组合会在插件图之外重复实现这些行为。
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -26,14 +26,13 @@ Every graph page declares one maintenance mode:
|
||||
|
||||
### First shipped index
|
||||
|
||||
The index links twelve 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 |
|
||||
| [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 |
|
||||
|
||||
@@ -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 built-mode `examples/echo-agent/tests/echo.e2e.ts` smoke boots `dsh-cli-demo`'s 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.
|
||||
|
||||
|
||||
@@ -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,7 +2,7 @@
|
||||
|
||||
Status: implemented
|
||||
|
||||
The later [remove-stdio-agent decision](2026-07-20-remove-stdio-agent.md) supersedes this package-placement decision and removes the folded package, app, and line-oriented surface entirely.
|
||||
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
|
||||
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
# Agent Note: Remove the line-oriented stdio agent
|
||||
|
||||
Status: implemented
|
||||
|
||||
English | [中文](2026-07-20-remove-stdio-agent.zh.md)
|
||||
|
||||
## Problem
|
||||
|
||||
DeepSeek Harness had two terminal agents after the full-screen TUI shipped. `@deepseek-ai/dsh-tui` owned the interactive coding experience, while `@deepseek-ai/dsh-stdio` retained a line-oriented multi-turn chat protocol for ordinary streams. The latter was no longer a distinct product need: interactive users use the TUI, and scripts need a bounded Headless task with explicit output and exit semantics rather than prompts mixed with model and tool output.
|
||||
|
||||
The redundant surface extended beyond one UI plugin. `@deepseek-ai/dsh-stdio-demo` selected between two terminal modes, `examples/repl-agent` owned a second copy of the coding composition, `demo:repl` exposed it, Loader and built-bin tests drove its prompt protocol, and the SDK generator offered a `stdio` interface that could create new users of the obsolete package. Keeping any of those paths would preserve the line agent indirectly.
|
||||
|
||||
Standard input and output are also used as transport by ACP, the SDK JSON-RPC bridge, subprocesses, and test fixtures. Those byte channels are protocol boundaries, not the line-oriented agent, so removing every generic use of process streams would conflate unrelated designs.
|
||||
|
||||
## Decision
|
||||
|
||||
The line-oriented agent is removed without a compatibility package or mode alias. The `packages/ui/stdio` plugin, `@deepseek-ai/dsh-stdio-demo` package identity, `examples/repl-agent` leaf, `demo:repl` command, prompt/render tests, and supporting manifest, catalog, graph, and documentation entries are deleted.
|
||||
|
||||
The two remaining application roles are explicit:
|
||||
|
||||
- [`@deepseek-ai/dsh-tui-demo`](../../../../packages/examples/tui-demo/README.md) is the only terminal-interactive app. `examples/tui-agent` owns the complete coding composition and its Code Mode overlay directly; it no longer includes or patches another terminal leaf.
|
||||
- [`@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 and generic real-agent e2e suites, while `examples/echo-agent` supplies the keyless mock task and CI smoke.
|
||||
|
||||
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 continue to create or resume one exact session. No old option is accepted because the repository is pre-release and has no compatibility promise.
|
||||
|
||||
ACP and JSON-RPC retain their stdio transports. Child-process `stdio` settings and stream-reading APIs also remain where they describe operating-system I/O rather than the removed agent.
|
||||
|
||||
## Verification
|
||||
|
||||
TUI Loader coverage runs the real app under a pseudo-terminal in both source and built modes. Headless Loader coverage proves the mock tool round trip, multi-turn test drivers exercise a single app-owned agent without a UI protocol, and the CLI built-bin suite pins text, JSON, stream-JSON, persistence, failure, and signal behavior. Generated package/config/module graphs reject stale package references.
|
||||
|
||||
## Alternatives considered
|
||||
|
||||
- **Keep the line agent only for pipes** — rejected because Headless already has a clearer bounded-task contract, format-pure stdout, durable completion, and process exit status.
|
||||
- **Keep the package as a compatibility wrapper over Headless** — rejected because a multi-turn prompt protocol cannot honestly preserve its behavior by delegating to a one-shot CLI, and the pre-release policy favors the correct public surface.
|
||||
- **Let the TUI fall back when streams are not TTYs** — rejected because silent interface changes hide deployment mistakes; the TUI fails loud and callers select Headless explicitly.
|
||||
- **Remove every use of the term or mechanism stdio** — rejected because ACP and JSON-RPC intentionally use standard I/O as a framed transport and do not expose the removed line agent.
|
||||
|
||||
## Consequences
|
||||
|
||||
- Terminal interaction has one owner, one app package, one coding leaf, and one test strategy.
|
||||
- Automation has an explicit task/result contract rather than prompt parsing or EOF-driven conversation control.
|
||||
- Existing line-agent configurations and SDK `--interface=stdio` invocations fail instead of being translated.
|
||||
- The TUI requires a TTY pair; non-interactive environments use Headless, ACP, or JSON-RPC according to their protocol needs.
|
||||
@@ -1,44 +0,0 @@
|
||||
# Agent Note: 移除面向行的 stdio agent
|
||||
|
||||
Status: implemented
|
||||
|
||||
[English](2026-07-20-remove-stdio-agent.md) | 中文
|
||||
|
||||
## 问题
|
||||
|
||||
全屏 TUI 交付后,DeepSeek Harness 同时存在两个终端 agent。`@deepseek-ai/dsh-tui` 负责交互式 coding 体验,而 `@deepseek-ai/dsh-stdio` 仍为普通 stream 保留面向行的多轮聊天协议。后者已不再对应独立的产品需求:交互用户使用 TUI;脚本需要的是具有明确输出和退出语义的有界 Headless 任务,而不是与模型和工具输出混在一起的提示符。
|
||||
|
||||
重复 surface 不只涉及一个 UI 插件。`@deepseek-ai/dsh-stdio-demo` 在两种终端模式间选择,`examples/repl-agent` 维护第二份 coding 组装,`demo:repl` 对外暴露它,Loader 与 built-bin 测试驱动其提示符协议,SDK 生成器还提供可以创建旧包新用户的 `stdio` interface。保留其中任何路径,都会间接保留面向行的 agent。
|
||||
|
||||
ACP、SDK JSON-RPC bridge、子进程和测试 fixture 同样使用标准输入输出作为 transport。这些字节通道是协议边界,并不是面向行的 agent;因此,删除所有通用进程 stream 用法会混淆彼此无关的设计。
|
||||
|
||||
## 决策
|
||||
|
||||
移除面向行的 agent,不提供兼容 package 或 mode alias。删除 `packages/ui/stdio` 插件、`@deepseek-ai/dsh-stdio-demo` package identity、`examples/repl-agent` 叶节点、`demo:repl` 命令、提示符/渲染测试,以及相关 manifest、catalog、graph 和文档条目。
|
||||
|
||||
保留的两个应用角色均改为显式选择:
|
||||
|
||||
- [`@deepseek-ai/dsh-tui-demo`](../../../../packages/examples/tui-demo/README.md) 是唯一的终端交互式 app。`examples/tui-agent` 直接拥有完整 coding 组装及其 Code Mode overlay,不再 include 或 patch 另一个终端叶节点。
|
||||
- [`@deepseek-ai/dsh-cli-demo`](../../../../packages/examples/cli-demo/README.md) 负责非交互式执行。`examples/headless-agent` 拥有真实模型的单次组装和通用真实 agent e2e suite,`examples/echo-agent` 则提供 keyless mock 任务与 CI smoke。
|
||||
|
||||
SDK project model 与 create/config workflow 将 `stdio` run-interface 选项替换为 `tui`;生成的 TUI 工程组合 `@deepseek-ai/dsh-tui`,并继续创建或恢复一个确切 session。仓库处于 pre-release 阶段且没有兼容性承诺,因此不会接受旧选项。
|
||||
|
||||
ACP 和 JSON-RPC 保留各自的 stdio transport。描述操作系统 I/O 而非已移除 agent 的子进程 `stdio` 设置与 stream 读取 API 也继续保留。
|
||||
|
||||
## 验证
|
||||
|
||||
TUI Loader 覆盖在 source 与 built 两种模式下通过伪终端运行真实 app。Headless Loader 覆盖验证 mock 工具往返;多轮测试 driver 在没有 UI 协议的情况下驱动同一个 app-owned agent;CLI built-bin suite 固定 text、JSON、stream-JSON、持久化、失败和 signal 行为。生成的 package/config/module graph 会拒绝陈旧的 package 引用。
|
||||
|
||||
## 曾考虑的替代方案
|
||||
|
||||
- **仅为 pipe 保留面向行的 agent**:不予采纳,因为 Headless 已提供更清晰的有界任务契约、格式纯净的 stdout、持久完成边界和进程退出状态。
|
||||
- **保留 package,并将其作为 Headless 的兼容 wrapper**:不予采纳,因为多轮提示符协议无法通过委托给单次 CLI 来诚实地保持行为,而且 pre-release 策略优先选择正确的公开 surface。
|
||||
- **让 TUI 在 stream 不是 TTY 时回退**:不予采纳,因为静默切换 interface 会掩盖部署错误;TUI 会快速失败,由调用方显式选择 Headless。
|
||||
- **移除 stdio 这个术语或机制的所有用法**:不予采纳,因为 ACP 与 JSON-RPC 有意使用标准 I/O 作为分帧 transport,并不暴露已移除的面向行 agent。
|
||||
|
||||
## 后果
|
||||
|
||||
- 终端交互只有一个 owner、一个 app package、一个 coding 叶节点和一套测试策略。
|
||||
- 自动化使用显式 task/result 契约,不再解析提示符或通过 EOF 控制对话。
|
||||
- 现有面向行的 agent 配置和 SDK `--interface=stdio` 调用会直接失败,不会被转换。
|
||||
- TUI 要求成对的 TTY;非交互环境根据协议需要使用 Headless、ACP 或 JSON-RPC。
|
||||
@@ -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-20-remove-stdio-agent.md: e4dcd371490e0810900134ab9a05f6f894ca06d0
|
||||
2026-07-20-remove-stdio-agent.zh.md: 9f0c2348de27b9c724e6f656881ec316bf48005f
|
||||
2026-07-20-remove-stdio-and-echo-agents.md: 2aba8193710c96d3726b91062bfa43d039b4cabf
|
||||
2026-07-20-remove-stdio-and-echo-agents.zh.md: 2c3916683f4743384a2ce4104319da26145837fe
|
||||
@@ -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.
|
||||
@@ -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,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: 7277e6be6d88a73ea3abbd277e314715c8abc050
|
||||
2026-07-18-tui-terminal-state-snapshots.zh.md: 068a8b8a7f469f40492924bf94757bc0e25c3404
|
||||
2026-07-18-tui-terminal-state-snapshots.md: 8e86588f69fdb9d615232252ecf57309d440f1cd
|
||||
2026-07-18-tui-terminal-state-snapshots.zh.md: b70a46830f44e9da663e30745fcdb7ad281592da
|
||||
|
||||
@@ -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 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 [line-agent removal](../simplification/2026-07-20-remove-stdio-agent.md) owns this consolidation.
|
||||
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
|
||||
|
||||
|
||||
@@ -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` 中拥有独立叶节点,与 Headless 和 ACP 叶节点并列。它直接拥有交互式 coding 后端与工具,并加载 `@deepseek-ai/dsh-tui-demo`;TUI 快照和 PTY 测试也归属这个叶节点。[面向行 agent 的移除决策](../simplification/2026-07-20-remove-stdio-agent.md)负责此次整合。
|
||||
可运行 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)负责此次整合。
|
||||
|
||||
### 已录制会话回放
|
||||
|
||||
|
||||
@@ -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
|
||||
DSH_EXAMPLE_MODE=lib pnpm exec vitest run --config vitest.e2e.config.ts examples/echo-agent/tests/echo.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
|
||||
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.
|
||||
|
||||
@@ -58,7 +58,6 @@ 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 "task" # mock-model headless agent, no key needed
|
||||
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)
|
||||
@@ -85,12 +84,7 @@ pnpm run website:build
|
||||
pnpm run verify-module-graph
|
||||
pnpm run build
|
||||
pnpm run hygiene
|
||||
out=$(pnpm run demo:echo --output-format stream-json -- "echo ci smoke" 2>&1)
|
||||
printf '%s\n' "$out" | grep -q '"type":"tool/call"'
|
||||
printf '%s\n' "$out" | grep -q 'ECHO: CI SMOKE'
|
||||
test -n "$(find .sessions -path '.sessions/cwd-*/main-session-*.jsonl' -type f -print -quit)"
|
||||
rm -rf .sessions
|
||||
DSH_EXAMPLE_MODE=lib pnpm exec vitest run --config vitest.e2e.config.ts examples/echo-agent/tests/echo.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
|
||||
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,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: 4ce9391e9286391a601d59d8401870c9ca8c79f3
|
||||
README.zh.md: c4f996762b36d2ebe00cd256e2f18b0a62550ba8
|
||||
README.md: 32958db0e74bd14d6d41e8d7886b8d3257fe0f59
|
||||
README.zh.md: b28b175a8296347a7bed05b4e53c0d75dc51efed
|
||||
|
||||
10
README.md
10
README.md
@@ -11,11 +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 "task" # keyless mock-model headless agent
|
||||
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/).
|
||||
|
||||
10
README.zh.md
10
README.zh.md
@@ -11,11 +11,11 @@
|
||||
```sh
|
||||
pnpm install
|
||||
pnpm run test # vitest
|
||||
pnpm run demo:echo "task" # keyless mock-model headless agent
|
||||
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/)。
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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,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: 811eeb04d1730a8062454f932477d1e05275f3cf
|
||||
extension-cookbook.zh.md: a1c22aeffcd337401bed9444ebd52ebd5b524595
|
||||
extension-cookbook.md: 32877b6170fd75ec901dda7cc0aec6b5a92e6cc6
|
||||
extension-cookbook.zh.md: d6bb6075b47867dcb5a848c835062ef4d9af5d45
|
||||
|
||||
@@ -87,7 +87,7 @@ export function apply(ctx: Context) {
|
||||
|
||||
## Runnable wirings
|
||||
|
||||
Five runnable leaves load their plugin trees from `cordis.yml`: [`examples/echo-agent`](../../examples/echo-agent) (keyless mock model + echo tool through Headless, `pnpm run demo:echo "task"`), [`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).
|
||||
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
|
||||
|
||||
|
||||
@@ -87,7 +87,7 @@ export function apply(ctx: Context) {
|
||||
|
||||
## 可运行的组装示例
|
||||
|
||||
五个可运行叶子从 `cordis.yml` 加载各自的插件树:[`examples/echo-agent`](../../examples/echo-agent)(通过 Headless 运行的 keyless mock 模型 + echo 工具,`pnpm run demo:echo "task"`)、[`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) 共享主干。
|
||||
四个可运行叶子从 `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) 共享主干。
|
||||
|
||||
## 功能→机制映射
|
||||
|
||||
|
||||
@@ -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: 6517a14f094a5098c26815e04f81e3f8ea1ceff9
|
||||
development.zh.md: bb70970679aaa8ffaed927b746002277cb422f6b
|
||||
development.md: 3327d094a31ad9af62a23c9562cdfa03218961a5
|
||||
development.zh.md: cb1cb86f9f3a34c455844467a20dfdfd08e15098
|
||||
|
||||
@@ -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,10 +102,10 @@ When changing package public behavior, update the relevant README or JSDoc in th
|
||||
|
||||
## Demos
|
||||
|
||||
The Headless 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 "echo hello"
|
||||
pnpm run demo:headless "summarize this workspace"
|
||||
```
|
||||
|
||||
The full-screen interactive coding agent needs `DEEPSEEK_API_KEY` in the environment or repo-root `.env`:
|
||||
|
||||
@@ -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,10 +102,10 @@ pnpm run hygiene # knip, publint, workspace constraints, and NodeNext dec
|
||||
|
||||
## 演示
|
||||
|
||||
Headless echo 演示不需要 API 凭证:
|
||||
单次运行的 Headless coding agent 需要环境变量或仓库根目录 `.env` 中的 `DEEPSEEK_API_KEY`:
|
||||
|
||||
```sh
|
||||
pnpm run demo:echo "echo hello"
|
||||
pnpm run demo:headless "summarize this workspace"
|
||||
```
|
||||
|
||||
全屏交互式 coding agent 需要环境变量或仓库根目录 `.env` 中的 `DEEPSEEK_API_KEY`:
|
||||
|
||||
@@ -12,7 +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` |
|
||||
| [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` |
|
||||
|
||||
@@ -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,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
|
||||
|
||||
@@ -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}!` }]
|
||||
},
|
||||
}))
|
||||
}
|
||||
|
||||
@@ -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,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: 83296e54220c668410fe69d689199171251a7787
|
||||
llm-adapter.zh.md: 89b7185690dbdfe33cbafe7b0ee4c3e83cfe0df8
|
||||
llm-adapter.md: 3e83289b8072ef231f83c0fa3cfe3260547b42fa
|
||||
llm-adapter.zh.md: 92fcf9b22f4bb356ada4c46f9a03ef0cc2d159da
|
||||
|
||||
@@ -145,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
|
||||
|
||||
|
||||
@@ -145,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,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: 0616f163f995b152d7a28841506027558de2c32c
|
||||
config.zh.md: fa91445ae88456a61a4736ce8b71aed482227ce8
|
||||
config.md: 8958729d04224215ca420c3103d253a8a5783405
|
||||
config.zh.md: 530f2b335453d5064acdac28a60d7df51cd915f0
|
||||
|
||||
@@ -8,7 +8,6 @@ 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.
|
||||
- [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.
|
||||
|
||||
@@ -8,7 +8,6 @@ Harness 使用 `cordis.yml` 描述 Agent 加载哪些插件以及每个插件的
|
||||
|
||||
仓库中的示例就是可以运行的配置,也是新项目最可靠的起点:
|
||||
|
||||
- [echo-agent](../../../examples/echo-agent/cordis.yml) 使用本地 mock 模型,不需要 API key。
|
||||
- [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 接入编辑器客户端。
|
||||
|
||||
@@ -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: 62e899adfa33e566083b8224b9bdf77c1038557a
|
||||
quickstart.zh.md: 382c9685ebe0c919c2fd039484898b44e9264aa7
|
||||
quickstart.md: 25ce51ee3d010d2eb800071b9697fc62857dace1
|
||||
quickstart.zh.md: e2e023670a999566e273d8893c42103dc273e7b1
|
||||
|
||||
@@ -8,6 +8,7 @@ This guide gets an agent running in five minutes.
|
||||
|
||||
- [Node.js](https://nodejs.org/) ^22.19 or >= 24
|
||||
- [pnpm](https://pnpm.io/) 11 through Corepack
|
||||
- A [DeepSeek Platform](https://platform.deepseek.com/) API key
|
||||
|
||||
```sh
|
||||
node -v
|
||||
@@ -15,25 +16,32 @@ corepack enable
|
||||
pnpm -v
|
||||
```
|
||||
|
||||
## Step 1: run the keyless Headless demo
|
||||
## Step 1: install and configure the API key
|
||||
|
||||
```sh
|
||||
git clone https://github.com/deepseek-harness/deepseek-harness.git
|
||||
cd deepseek-harness
|
||||
pnpm install
|
||||
pnpm run demo:echo "echo hello world"
|
||||
```
|
||||
|
||||
The local mock model calls the `echo` tool, which returns the text in uppercase, and the final response is printed without opening an interactive UI. Use `--output-format stream-json` when you need the canonical event stream.
|
||||
|
||||
## Step 2: use a real model in the TUI
|
||||
|
||||
Get an API key from [DeepSeek Platform](https://platform.deepseek.com/) and create the gitignored repository-root `.env`:
|
||||
Create the gitignored repository-root `.env`:
|
||||
|
||||
```sh
|
||||
DEEPSEEK_API_KEY=sk-your-key-here
|
||||
```
|
||||
|
||||
## Step 2: run one Headless task
|
||||
|
||||
Run a non-interactive task and print its final answer:
|
||||
|
||||
```sh
|
||||
pnpm run demo:headless "summarize the architecture of this workspace"
|
||||
```
|
||||
|
||||
Headless runs one complete model/tool turn, persists the session, prints the result, and exits. Use `--output-format stream-json` when you need the canonical event stream.
|
||||
|
||||
## Step 3: use the TUI
|
||||
|
||||
Start the interactive coding agent:
|
||||
|
||||
```sh
|
||||
@@ -44,7 +52,7 @@ The full-screen agent can read and write files, run commands, delegate subtasks,
|
||||
|
||||
## What happened
|
||||
|
||||
echo-agent uses the Headless `@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 model and capability plugins appropriate to each surface.
|
||||
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
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
- [Node.js](https://nodejs.org/) ^22.19 或 >= 24
|
||||
- 通过 Corepack 使用 [pnpm](https://pnpm.io/) 11
|
||||
- [DeepSeek Platform](https://platform.deepseek.com/) API key
|
||||
|
||||
```sh
|
||||
node -v
|
||||
@@ -15,25 +16,32 @@ corepack enable
|
||||
pnpm -v
|
||||
```
|
||||
|
||||
## 第一步:运行 keyless Headless 演示
|
||||
## 第一步:安装并配置 API key
|
||||
|
||||
```sh
|
||||
git clone https://github.com/deepseek-harness/deepseek-harness.git
|
||||
cd deepseek-harness
|
||||
pnpm install
|
||||
pnpm run demo:echo "echo hello world"
|
||||
```
|
||||
|
||||
本地 mock 模型会调用 `echo` 工具,由工具返回大写文本,最终回复在不打开交互式 UI 的情况下直接输出。需要规范事件流时可使用 `--output-format stream-json`。
|
||||
|
||||
## 第二步:在 TUI 中使用真实模型
|
||||
|
||||
前往 [DeepSeek Platform](https://platform.deepseek.com/) 获取 API key,并创建已被 Git 忽略的仓库根目录 `.env`:
|
||||
在仓库根目录创建已被 Git 忽略的 `.env`:
|
||||
|
||||
```sh
|
||||
DEEPSEEK_API_KEY=sk-your-key-here
|
||||
```
|
||||
|
||||
## 第二步:运行一个 Headless 任务
|
||||
|
||||
运行一个非交互式任务并打印最终回答:
|
||||
|
||||
```sh
|
||||
pnpm run demo:headless "summarize the architecture of this workspace"
|
||||
```
|
||||
|
||||
Headless 运行一个完整的模型/工具轮次,持久化会话,打印结果后退出。需要规范事件流时可使用 `--output-format stream-json`。
|
||||
|
||||
## 第三步:使用 TUI
|
||||
|
||||
启动交互式 coding agent:
|
||||
|
||||
```sh
|
||||
@@ -44,7 +52,7 @@ pnpm run demo:tui
|
||||
|
||||
## 回头看
|
||||
|
||||
echo-agent 使用 Headless `@deepseek-ai/dsh-cli-demo` app,tui-agent 使用交互式 `@deepseek-ai/dsh-tui-demo` app。二者加载同一个 providerless agent spine,并通过各自的 `cordis.yml` 为对应 surface 选择模型和能力插件。
|
||||
headless-agent 使用 `@deepseek-ai/dsh-cli-demo` app,tui-agent 使用交互式 `@deepseek-ai/dsh-tui-demo` app。二者加载同一个 providerless agent spine,并通过各自的 `cordis.yml` 为对应 surface 选择 DeepSeek 模型和能力插件。
|
||||
|
||||
## 下一步
|
||||
|
||||
|
||||
@@ -11,8 +11,6 @@ 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 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.
|
||||
|
||||
@@ -1,17 +1,6 @@
|
||||
# Examples
|
||||
|
||||
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 or demo-only mocks. 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.
|
||||
|
||||
## echo-agent
|
||||
|
||||
A mock model + echo tool on the headless one-shot app — the all-mock skeleton. It demonstrates:
|
||||
|
||||
- A thin leaf `cordis.yml` loading the `@deepseek-ai/dsh-cli-demo` app
|
||||
- Registering a mock `LlmAdapter` (streaming scripted responses)
|
||||
- Registering a tool via `ctx.tools.register()`
|
||||
- A network-free Headless task with text or DSH-native JSON output
|
||||
|
||||
Run with: `pnpm run demo:echo "echo hello"`. The task prefix `echo ` triggers a tool-call round trip.
|
||||
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
|
||||
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
# echo-agent
|
||||
|
||||
Network-free Headless demo with a scripted mock model and an echo tool.
|
||||
|
||||
## What it shows
|
||||
|
||||
The leaf loads [`@deepseek-ai/dsh-cli-demo`](../../packages/examples/cli-demo), which supplies the shared spine, JSONL persistence, one fresh `main` agent, and the one-shot CLI driver. Two local plugins provide the demo behavior:
|
||||
|
||||
- `mock-llm.ts` registers a scripted `LlmAdapter`; a task beginning with `echo ` requests the tool.
|
||||
- `echo-tool.ts` registers a typed tool that returns the input uppercased.
|
||||
|
||||
| File | Role |
|
||||
|---|---|
|
||||
| `src/mock-llm.ts` | Streaming mock adapter |
|
||||
| `src/echo-tool.ts` | Model-facing echo tool |
|
||||
| `cordis.yml` | Mock plugins, local providers, and one `@deepseek-ai/dsh-cli-demo` entry |
|
||||
|
||||
## Run
|
||||
|
||||
```sh
|
||||
pnpm run demo:echo "echo hello world"
|
||||
pnpm run demo:echo --output-format stream-json -- "echo hello world"
|
||||
```
|
||||
|
||||
The first command prints the final canned response. `stream-json` also exposes the canonical `tool/call` and `tool/result` events. Sessions persist under `.sessions/` relative to the launch directory; remove that generated directory when finished.
|
||||
@@ -1,40 +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 headless one-shot app package.
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
cfg["examples/echo-agent<br/>cordis.yml"]
|
||||
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_cli_agent["cli-agent<br/>@deepseek-ai/dsh-cli-demo"]
|
||||
cfg --> plugin_echo_cli_agent
|
||||
plugin_echo_cli_agent --> bundle_agent_core["@deepseek-ai/dsh-agent-spine-demo"]
|
||||
plugin_echo_cli_agent --> bundle_jsonl["@deepseek-ai/dsh-session-persistence-jsonl"]
|
||||
plugin_echo_cli_agent --> frontdoor_cli["one-shot driver<br/>format-pure stdout<br/>fresh top-level 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 |
|
||||
| --- | --- |
|
||||
| `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` |
|
||||
| `cli-agent` | `@deepseek-ai/dsh-cli-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.
|
||||
@@ -1,26 +0,0 @@
|
||||
# Headless agent with the network-free `mock-echo` adapter and example-local
|
||||
# `echo` tool. No API key is needed because the adapter never touches the network.
|
||||
|
||||
- id: mock-llm
|
||||
name: './src/mock-llm.ts'
|
||||
|
||||
- id: echo-tool
|
||||
name: './src/echo-tool.ts'
|
||||
|
||||
- id: bash
|
||||
name: '@deepseek-ai/dsh-bash-local'
|
||||
|
||||
- id: fs-local
|
||||
name: '@deepseek-ai/dsh-fs-local'
|
||||
config:
|
||||
cwd: !!js process.cwd()
|
||||
|
||||
- id: cli-agent
|
||||
name: '@deepseek-ai/dsh-cli-demo'
|
||||
config:
|
||||
provider: mock
|
||||
model: mock-echo
|
||||
persona: 'You are echo-agent, a demo agent.'
|
||||
persistenceRoot: './.sessions'
|
||||
workspaceContext:
|
||||
maxBytes: 65536
|
||||
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"name": "echo-agent-example",
|
||||
"private": true,
|
||||
"version": "0.0.1",
|
||||
"type": "module",
|
||||
"description": "Runnable headless demo: scripted mock model + echo tool"
|
||||
}
|
||||
@@ -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()}` }]
|
||||
},
|
||||
}))
|
||||
}
|
||||
@@ -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())
|
||||
}
|
||||
@@ -1,37 +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'
|
||||
import type { SessionEvent } from '@deepseek-ai/dsh-session'
|
||||
|
||||
const binScript = fileURLToPath(new URL('../../../packages/examples/cli-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(task: string, outputFormat: 'text' | 'stream-json' = 'text'): Promise<string> {
|
||||
const { stdout } = await runLoaderSmoke({
|
||||
label: 'echo-agent',
|
||||
tempDirPrefix: 'echo-smoke-',
|
||||
binScript,
|
||||
configPath,
|
||||
binArgs: ['--config', configPath, '--output-format', outputFormat, task],
|
||||
tsconfigPath,
|
||||
})
|
||||
return stdout
|
||||
}
|
||||
|
||||
describe('echo-agent keyless smoke (Headless through the real Loader tree)', () => {
|
||||
it('runs the echo tool round-trip and exposes both events in stream-json', async () => {
|
||||
const lines = (await runEcho('echo hello world', 'stream-json'))
|
||||
.trimEnd().split('\n').map(line => JSON.parse(line) as Record<string, unknown>)
|
||||
const events = lines.slice(0, -1).map(line => line['event'] as SessionEvent)
|
||||
expect(events.some(event => event.type === 'tool/call' && event.data.name === 'echo')).toBe(true)
|
||||
expect(JSON.stringify(events.find(event => event.type === 'tool/result'))).toContain('ECHO: HELLO WORLD')
|
||||
expect(lines.at(-1)).toMatchObject({ type: 'result', success: true })
|
||||
}, LOADER_SMOKE_TEST_TIMEOUT_MS)
|
||||
|
||||
it('prints the final canned reply for a direct one-shot task', async () => {
|
||||
const stdout = await runEcho('just chatting')
|
||||
expect(stdout).toContain('You said: "just chatting"')
|
||||
expect(stdout).not.toContain('tool/call')
|
||||
}, LOADER_SMOKE_TEST_TIMEOUT_MS)
|
||||
})
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env node
|
||||
/** Test driver that sends two turns through one headless Loader composition. */
|
||||
/** 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'
|
||||
22
examples/headless-agent/tests/fixtures/time-context-mock-llm.ts
vendored
Normal file
22
examples/headless-agent/tests/fixtures/time-context-mock-llm.ts
vendored
Normal file
@@ -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())
|
||||
}
|
||||
@@ -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'
|
||||
@@ -11,8 +11,8 @@
|
||||
- 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.'
|
||||
persistenceRoot: './.sessions'
|
||||
workspaceContext: false
|
||||
@@ -9,8 +9,9 @@
|
||||
},
|
||||
"examples": {
|
||||
"entry": [
|
||||
"echo-agent/src/*.ts",
|
||||
"headless-agent/tests/fixtures/cli-mock-llm.ts",
|
||||
"headless-agent/tests/fixtures/time-context-driver.ts",
|
||||
"headless-agent/tests/fixtures/time-context-mock-llm.ts",
|
||||
"tui-agent/tests/fixtures/tui-scripted-llm.ts",
|
||||
"*/tests/**/*.e2e.ts",
|
||||
"*/tests/**/*.snapshot.ts"
|
||||
|
||||
@@ -78,7 +78,6 @@
|
||||
"constraints": "tsx scripts/check-workspace-constraints.ts",
|
||||
"doc-sync": "pnpm run doc-typecheck && pnpm run verify-cordis-catalog && pnpm run verify-cordis-api && pnpm run verify-export-jsdoc && pnpm run verify-tool-catalog && pnpm run verify-config-catalog && pnpm run verify-persistence-catalog && pnpm run verify-doc-graphs && pnpm run verify-scoped-events && pnpm run verify-md-wrap && pnpm run verify-md-links && pnpm run verify-doc-refs && pnpm run verify-package-paths && pnpm run verify-package-readme-model-experience && pnpm run verify-mermaid && pnpm run verify-agent-note-classification && pnpm run verify-agent-note-format && pnpm run verify-type-equiv && pnpm run verify-translation-prompt && pnpm run verify-translation-pairing && pnpm run verify-doc-budgets && pnpm run verify-package-readme-limitations && pnpm run docs:check",
|
||||
"hygiene": "pnpm run knip && pnpm run publint && pnpm run constraints && pnpm run verify-cordis-config && pnpm run verify-node-next-types && pnpm run verify-runtime-closure",
|
||||
"demo:echo": "node --expose-internals --import tsx packages/examples/cli-demo/src/bin.ts --config examples/echo-agent/cordis.yml",
|
||||
"demo:headless": "node --expose-internals --import tsx packages/examples/cli-demo/src/bin.ts --config examples/headless-agent/cordis.yml",
|
||||
"demo:tui": "node --expose-internals --import tsx packages/examples/tui-demo/src/bin.ts examples/tui-agent/cordis.yml",
|
||||
"demo:code-mode": "node scripts/demo-code-mode.mjs",
|
||||
|
||||
@@ -8,11 +8,11 @@ import { LOADER_SMOKE_TEST_TIMEOUT_MS, runLoaderSmoke } from '@deepseek-ai/dsh-l
|
||||
// Keep the Loader config under examples so both modes exercise the same deployable
|
||||
// topology: local fixture source plus bare plugins owned by the examples workspace.
|
||||
const driver = fileURLToPath(new URL(
|
||||
'../../../../examples/echo-agent/tests/fixtures/context/time-context/driver.ts',
|
||||
'../../../../examples/headless-agent/tests/fixtures/time-context-driver.ts',
|
||||
import.meta.url,
|
||||
))
|
||||
const configPath = fileURLToPath(new URL(
|
||||
'../../../../examples/echo-agent/tests/fixtures/context/time-context/cordis.yml',
|
||||
'../../../../examples/headless-agent/tests/fixtures/time-context.cordis.yml',
|
||||
import.meta.url,
|
||||
))
|
||||
const repoTsconfig = fileURLToPath(new URL('../../../../tsconfig.json', import.meta.url))
|
||||
|
||||
@@ -435,14 +435,6 @@ function stripYamlScalar(value: string): string {
|
||||
}
|
||||
|
||||
const APP_EXAMPLES = [
|
||||
{
|
||||
id: 'echo',
|
||||
rel: 'examples/echo-agent/composition.md',
|
||||
title: 'Echo Agent App Composition',
|
||||
label: 'examples/echo-agent',
|
||||
config: 'examples/echo-agent/cordis.yml',
|
||||
summary: 'The echo demo swaps in a local mock LLM and teaching echo tool, then loads the headless one-shot app package.',
|
||||
},
|
||||
{
|
||||
id: 'tui',
|
||||
rel: 'examples/tui-agent/composition.md',
|
||||
@@ -1013,7 +1005,6 @@ function renderDocs(): GraphDoc[] {
|
||||
function renderIndex(docs: GraphDoc[]): string {
|
||||
const labels: Record<string, string> = {
|
||||
'docs/capability-seams.md': 'capability seams and core services',
|
||||
'examples/echo-agent/composition.md': 'echo-agent app composition',
|
||||
'examples/headless-agent/composition.md': 'headless-agent app composition',
|
||||
'examples/tui-agent/composition.md': 'tui-agent app composition',
|
||||
'examples/cordis-agent/composition.md': 'cordis-agent app composition',
|
||||
@@ -1025,7 +1016,6 @@ function renderIndex(docs: GraphDoc[]): string {
|
||||
}
|
||||
const modes: Record<string, string> = {
|
||||
'docs/capability-seams.md': 'hybrid generated',
|
||||
'examples/echo-agent/composition.md': 'hybrid generated',
|
||||
'examples/headless-agent/composition.md': 'hybrid generated',
|
||||
'examples/tui-agent/composition.md': 'hybrid generated',
|
||||
'examples/cordis-agent/composition.md': 'hybrid generated',
|
||||
|
||||
@@ -5,9 +5,8 @@
|
||||
* independent commands can overlap and which commands wait for built artifacts.
|
||||
*/
|
||||
import { spawn } from 'node:child_process'
|
||||
import { readdir, rm } from 'node:fs/promises'
|
||||
import { availableParallelism } from 'node:os'
|
||||
import { join, resolve } from 'node:path'
|
||||
import { resolve } from 'node:path'
|
||||
import { performance } from 'node:perf_hooks'
|
||||
|
||||
type Mode =
|
||||
@@ -210,7 +209,6 @@ function ciPrimaryGates(): Gate[] {
|
||||
pnpmScript('duplication', 'duplication'),
|
||||
coverageGate(),
|
||||
snapshotGate(),
|
||||
demoSmokeGate({ needs: ['lint'] }),
|
||||
...docSyncLeafGates(),
|
||||
pnpmScript('module-graph', 'verify-module-graph', { label: 'module graph' }),
|
||||
pnpmScript('knip', 'knip'),
|
||||
@@ -229,18 +227,12 @@ function ciStaticGates(): Gate[] {
|
||||
pnpmScript('runtime-closure', 'verify-runtime-closure', { label: 'runtime closure' }),
|
||||
pnpmScript('constraints', 'constraints'),
|
||||
pnpmScript('cordis-config', 'verify-cordis-config', { label: 'Cordis config' }),
|
||||
...staticDemoSmokeGates(),
|
||||
...docSyncLeafGates(),
|
||||
pnpmScript('module-graph', 'verify-module-graph', { label: 'module graph' }),
|
||||
pnpmScript('knip', 'knip'),
|
||||
]
|
||||
}
|
||||
|
||||
function staticDemoSmokeGates(): Gate[] {
|
||||
// Native Windows session persistence is outside the gates-only support scope.
|
||||
return process.platform === 'win32' ? [] : [demoSmokeGate()]
|
||||
}
|
||||
|
||||
function ciArtifactGates(): Gate[] {
|
||||
return [
|
||||
pnpmScript('build', 'build'),
|
||||
@@ -353,49 +345,13 @@ function docSyncLeafGates(options: {
|
||||
]
|
||||
}
|
||||
|
||||
function demoSmokeGate(options: { needs?: string[] } = {}): Gate {
|
||||
const dependencyOptions = options.needs === undefined ? {} : { needs: options.needs }
|
||||
return {
|
||||
id: 'demo-smoke',
|
||||
label: 'demo smoke',
|
||||
displayCommand: 'pnpm run demo:echo --output-format stream-json -- "echo ci smoke"',
|
||||
...pnpmInvocation(['run', 'demo:echo', '--output-format', 'stream-json', '--', 'echo ci smoke']),
|
||||
...dependencyOptions,
|
||||
verify: async (result) => {
|
||||
const output = result.stdout + result.stderr
|
||||
const sessionsRoot = join(root, '.sessions')
|
||||
try {
|
||||
if (!output.includes('"type":"tool/call"') || !output.includes('"name":"echo"')) {
|
||||
throw new Error('demo smoke did not show the echo tool call.')
|
||||
}
|
||||
if (!output.includes('ECHO: CI SMOKE')) {
|
||||
throw new Error('demo smoke did not show the echo tool result.')
|
||||
}
|
||||
const buckets = await readdir(sessionsRoot, { withFileTypes: true })
|
||||
let found = false
|
||||
for (const bucket of buckets) {
|
||||
if (!bucket.isDirectory() || !bucket.name.startsWith('cwd-')) continue
|
||||
const entries = await readdir(join(sessionsRoot, bucket.name))
|
||||
if (entries.some(entry => /^main-session-.+\.jsonl$/.test(entry))) {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if (!found) throw new Error('demo smoke did not create a main-session JSONL log in a cwd bucket.')
|
||||
} finally {
|
||||
await rm(sessionsRoot, { recursive: true, force: true })
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
function builtBinSmokeGate(): Gate {
|
||||
return pnpmExec('built-bin-smoke', [
|
||||
'vitest',
|
||||
'run',
|
||||
'--config',
|
||||
'vitest.e2e.config.ts',
|
||||
'examples/echo-agent/tests/echo.e2e.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',
|
||||
|
||||
Reference in New Issue
Block a user