mirror of
https://github.com/deepseek-ai/deepseek-harness
synced 2026-08-15 21:04:50 +00:00
Merge branch 'master' into codex/fs-directory-listing
This commit is contained in:
@@ -25,6 +25,7 @@ These define the conventions and gates this repo is checked against, and they ar
|
||||
- **AGENTS.md § Defensive patterns (hard-won)** — each bullet is a bug class that bit us. Reviewing anything touching process lifecycle, async/await, disposal, or adapter error paths? Re-read this first — then look for the *adjacent* mistake it doesn't name.
|
||||
- **AGENTS.md § Type Safety and Documentation** — the doc-sync rule (code change ⇒ update README + JSDoc in the SAME commit) and the no-hard-wrap markdown convention.
|
||||
- **[packages/AGENTS.md](../../../packages/AGENTS.md)** — per-package conventions (file layout, the HMR-safety test requirement).
|
||||
- **[docs/i18n/translation-rules.md](../../../docs/i18n/translation-rules.md) and [docs/i18n/terminology.md](../../../docs/i18n/terminology.md)** — the authoritative standard for bilingual-doc review: faithfulness, structure, typography, and the binding terminology table. For PRs touching translated docs or pending terms, read these before judging the translation; [dsh-translate-docs](../dsh-translate-docs/SKILL.md) is the translator workflow.
|
||||
- **[RFC index](../../../docs/rfc/README.md)** — the *why* behind the architecture. Especially [quality gates](../../../docs/rfc/implemented/process/2026-06-11-quality-gates.md) (what a PR must pass) and [capability seams](../../../docs/rfc/implemented/architecture/2026-06-13-capability-seams.md) (the three-package split). If a change seems to fight an RFC, that's a discussion, not a silent override — and not an automatic veto either: an RFC can be wrong for this case, so reason about it.
|
||||
|
||||
## Hard blockers (documented requirements — missing one blocks merge)
|
||||
@@ -45,6 +46,7 @@ Where your independent reasoning earns its keep. Start here, then keep going acr
|
||||
- **Seam discipline.** New swappable capability? Check it's split per the capability-seams RFC (interface / impl / consumer), and that the consumer injects the interface key, never an implementation type.
|
||||
- **Test quality — sufficiency, not just coverage.** 100% per-file coverage and a green suite are necessary, not sufficient: they prove the lines *ran*, not that the feature *works the way it ships*. Judge whether the tests are sufficient on two axes. (1) **Would they fail if the behavior regressed?** A test that passes but asserts the wrong thing — or restates the implementation instead of the contract (events fired, disposal reached, the world changed) — is worse than none. (2) **Do they exercise the REAL thing, the way it's actually used?** Prefer the genuine collaborator over a fake, drive the change through its real entry path (the cordis Loader, the ACP bridge, a booted subprocess — not a hand-built `ctx.plugin({...})` that bypasses `unwrapExports`), and verify the WORLD (re-read the file/log/registry externally), not the agent's self-report. A test that fakes the inputs just enough to cover every line will agree with whatever the author assumed; the real thing won't. When a test sets up a *clean/happy* path to reach a line, ask whether the line's PURPOSE is exercised — e.g. a durability/teardown path "tested" by a fully-completed turn never proves the mid-flight teardown it exists for; a torn-tail recovery branch covered by a well-formed log never proves recovery. Flag tests that hit the line but not the scenario. See AGENTS.md § Defensive patterns "Line coverage is not behavior coverage" and "Prefer the REAL implementation over a mock/stand-in in tests".
|
||||
- **Snapshot coverage for transcript/UX changes.** If the PR changes the editor-facing transcript or end-to-end agent UX — the ACP bridge's event→update translation, the agent loop's observable output, tool presentation, or anything an editor renders — it must add or update a snapshot scenario (`examples/*/tests/**/*.snapshot.ts`, goldens under `examples/acp-agent/tests/snapshots/`) or note explicitly why none applies (AGENTS.md § Conventions). Review the golden diff itself: a changed `stdout.golden.txt` / `session.golden.txt` is a behavior change in disguise — confirm it's intended, not an accidental regression someone re-recorded away. A pure internal refactor with no observable-output change is exempt, but the PR should say so. See [docs/rfc/implemented/testing/2026-06-19-acp-snapshot-tests.md](../../../docs/rfc/implemented/testing/2026-06-19-acp-snapshot-tests.md).
|
||||
- **Bilingual docs: review translation quality, not just pairing.** If the PR adds or edits a doc pair, read the changed English and Chinese sides and compare the meaning, not only the mechanical diff. Verify terms against [terminology.md](../../../docs/i18n/terminology.md), including first-occurrence annotations and "do not translate as" prohibitions; if a new term has no established precedent, the PR should keep it in English, list it under `待定术语`, and update the terminology table once the rendering is decided. A green `verify-translation-pairing` only proves hashes, switchers, and structure were recorded — it does not prove the translation is faithful, natural, or correctly termed. Treat [translation-rules.md](../../../docs/i18n/translation-rules.md) MUST/MUST NOT violations as blocking.
|
||||
- **Intent and contracts.** Does the change do what the PR says, and honor the documented contract on *both* sides of every seam it touches (see AGENTS.md "Honor cross-seam contracts on BOTH sides")?
|
||||
|
||||
## How to respond
|
||||
|
||||
16
AGENTS.md
16
AGENTS.md
@@ -94,7 +94,7 @@ packages/ Harness packages, grouped by role at packages/<group>/<pkg>/.
|
||||
acp/ Agent Client Protocol bridge: drive the agent from an ACP
|
||||
editor (Zed) over JSON-RPC stdio
|
||||
stdio-agent/ stdio chat APP: agent-core spine + console logger + readline
|
||||
UI + a pre-created main agent + a bin (the demo:echo/coding
|
||||
UI + a pre-created main agent + a bin (the demo:echo/repl
|
||||
front door)
|
||||
acp-agent/ ACP server APP: agent-core spine + JSONL persistence + the
|
||||
acp bridge, NO stdout logger + a bin (the demo:acp front door)
|
||||
@@ -115,10 +115,10 @@ examples/ Runnable demos (not workspaces; see examples/AGENTS.md). Each is a
|
||||
teaching plugins. The app package bundles the agent-core spine +
|
||||
front-door cluster + boot glue (a bin). No start.ts. echo-agent =
|
||||
mock model + echo tool on dsh-stdio-agent (pnpm run demo:echo, no
|
||||
key). coding-agent = the real thing: DeepSeek V4 + fs tools
|
||||
key). coding-agent = the REPL agent demo: DeepSeek V4 + fs tools
|
||||
(read/write/edit) + bash tools + subagent + todo_write on the same
|
||||
app (pnpm run demo:coding, needs DEEPSEEK_API_KEY). acp-agent = the
|
||||
coding agent as an ACP server on dsh-acp-agent (pnpm run demo:acp,
|
||||
app (pnpm run demo:repl, needs DEEPSEEK_API_KEY). acp-agent = the
|
||||
ACP server agent demo on dsh-acp-agent (pnpm run demo:acp,
|
||||
needs DEEPSEEK_API_KEY).
|
||||
cordis.snapshot.yml = the acp leaf with llm-replay for keyless
|
||||
snapshot replay.
|
||||
@@ -188,10 +188,10 @@ pnpm run verify-node-next-types # assert built declarations typecheck for a
|
||||
pnpm run doc-sync # doc-typecheck + verify-cordis-catalog + verify-tool-catalog + verify-md-wrap + verify-md-links + verify-doc-refs + verify-package-paths + verify-rfc-classification + verify-type-equiv + verify-translation-pairing (CI runs this)
|
||||
pnpm run demo:echo # run examples/echo-agent (no API key; type "echo hi" to
|
||||
# see a tool call) — the mock skeleton
|
||||
pnpm run demo:coding # run examples/coding-agent — the real agent (needs
|
||||
# DEEPSEEK_API_KEY; give it a coding task)
|
||||
pnpm run demo:acp # run examples/acp-agent — the coding agent as an ACP
|
||||
# server over JSON-RPC stdio (needs DEEPSEEK_API_KEY;
|
||||
pnpm run demo:repl # run examples/coding-agent — the REPL agent demo
|
||||
# (needs DEEPSEEK_API_KEY; give it a coding task)
|
||||
pnpm run demo:acp # run examples/acp-agent — the ACP server agent demo
|
||||
# over JSON-RPC stdio (needs DEEPSEEK_API_KEY;
|
||||
# drive it from Zed or another ACP client)
|
||||
```
|
||||
|
||||
|
||||
@@ -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: 33c03fad1450d91ba1adef3d89ce44d0ff73c25b
|
||||
README.zh.md: 9d520023c528810ce75ee80efec2f2f087fb7b0e
|
||||
README.md: 7ddf68bab06ecf891856e6d1393ccdefd9eeba38
|
||||
README.zh.md: 59a0419164f2dfee6f66903cc93d7b35da1d9063
|
||||
|
||||
10
README.md
10
README.md
@@ -2,11 +2,7 @@
|
||||
|
||||
English | [中文](README.zh.md)
|
||||
|
||||
Monorepo for the DeepSeek Harness group.
|
||||
|
||||
## Projects
|
||||
|
||||
- **DeepSeek Code** — DeepSeek's coding agent product.
|
||||
The **DeepSeek Harness SDK** is a plugin-based SDK for building agent harnesses.
|
||||
|
||||
## Development
|
||||
|
||||
@@ -15,8 +11,8 @@ This monorepo is built on the [Cordis](https://github.com/cordiverse/cordis) fra
|
||||
```sh
|
||||
pnpm install
|
||||
pnpm run test # vitest
|
||||
pnpm run demo:echo # runnable echo-agent example (no API key needed)
|
||||
pnpm run demo:coding # the real DeepSeek coding agent (needs DEEPSEEK_API_KEY)
|
||||
pnpm run demo:repl # REPL agent demo (needs DEEPSEEK_API_KEY)
|
||||
pnpm run demo:acp # ACP server agent demo (needs DEEPSEEK_API_KEY)
|
||||
```
|
||||
|
||||
For humans, start with the [development guide](docs/development.md) for local setup, hooks, environment variables, and quality gates, then read the [architecture design](docs/architecture.md) before package work. Local context lives in [packages/](packages/) and [vendor/](vendor/).
|
||||
|
||||
10
README.zh.md
10
README.zh.md
@@ -2,11 +2,7 @@
|
||||
|
||||
[English](README.md) | 中文
|
||||
|
||||
DeepSeek Harness 小组的 monorepo。
|
||||
|
||||
## 项目
|
||||
|
||||
- **DeepSeek Code** — DeepSeek 的编码 agent(智能体)产品。
|
||||
**DeepSeek Harness SDK** 是用于构建 agent harness 的 SDK,采取基于插件的设计。
|
||||
|
||||
## 开发
|
||||
|
||||
@@ -15,8 +11,8 @@ DeepSeek Harness 小组的 monorepo。
|
||||
```sh
|
||||
pnpm install
|
||||
pnpm run test # vitest
|
||||
pnpm run demo:echo # runnable echo-agent example (no API key needed)
|
||||
pnpm run demo:coding # the real DeepSeek coding agent (needs DEEPSEEK_API_KEY)
|
||||
pnpm run demo:repl # REPL agent demo (needs DEEPSEEK_API_KEY)
|
||||
pnpm run demo:acp # ACP server agent demo (needs DEEPSEEK_API_KEY)
|
||||
```
|
||||
|
||||
面向人类读者:先读[开发指南](docs/development.md)了解本地环境搭建、钩子、环境变量与质量门禁,动手改 package 之前再读[架构设计](docs/architecture.md)。局部上下文见 [packages/](packages/) 与 [vendor/](vendor/)。
|
||||
|
||||
@@ -83,4 +83,4 @@ export function apply(ctx: Context) {
|
||||
|
||||
## Runnable wirings
|
||||
|
||||
Three complete examples load their plugin trees from `cordis.yml`: [`examples/echo-agent`](../../examples/echo-agent) (mock model + echo tool — the all-mock skeleton check, `pnpm run demo:echo`), [`examples/coding-agent`](../../examples/coding-agent) (DeepSeek V4 + the bash tool suite — the real thing, `pnpm run demo:coding`), and [`examples/acp-agent`](../../examples/acp-agent) (the same coding agent exposed as an ACP server over JSON-RPC stdio — the client-driver shape, `pnpm run demo:acp`). Each leaf is now just its swappable backends plus an app-package entry: the stdio demos load [`@deepseek-ai/dsh-stdio-agent`](../../packages/ui/stdio-agent), the ACP demo loads [`@deepseek-ai/dsh-acp-agent`](../../packages/ui/acp-agent), and both app packages share the spine via the [`@deepseek-ai/dsh-agent-core`](../../packages/core/agent-core) bundle.
|
||||
Three complete examples load their plugin trees from `cordis.yml`: [`examples/echo-agent`](../../examples/echo-agent) (mock model + echo tool — the all-mock skeleton check, `pnpm run demo:echo`), [`examples/coding-agent`](../../examples/coding-agent) (DeepSeek V4 + the bash tool suite behind a terminal REPL UI, `pnpm run demo:repl`), and [`examples/acp-agent`](../../examples/acp-agent) (an agent exposed as an ACP server over JSON-RPC stdio — the client-driver shape, `pnpm run demo:acp`). Each leaf is now just its swappable backends plus an app-package entry: the stdio demos load [`@deepseek-ai/dsh-stdio-agent`](../../packages/ui/stdio-agent), the ACP demo loads [`@deepseek-ai/dsh-acp-agent`](../../packages/ui/acp-agent), and both app packages share the spine via the [`@deepseek-ai/dsh-agent-core`](../../packages/core/agent-core) bundle.
|
||||
|
||||
@@ -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: ce431d95c5dbb976d0c3ffa827af46ba608b400e
|
||||
development.zh.md: 36155ee2b93f2bc309ca1341cbed82c37e2759c9
|
||||
development.md: 3e11ae594759e6251e46f3bf9e0b021d9e1555c5
|
||||
development.zh.md: e8cea20a713767411304c2a3c97099004b9392c3
|
||||
|
||||
@@ -9,7 +9,7 @@ This guide covers the local setup needed to work on DeepSeek Harness and underst
|
||||
- Node.js 24 or newer. The repo declares `node >=24`; CI runs the matrix on Node 24 and 26.
|
||||
- Corepack-enabled pnpm. The repo pins `pnpm@11.7.0` in `package.json`; run `corepack enable` if `pnpm --version` does not resolve through Corepack.
|
||||
- Git.
|
||||
- Optional: a DeepSeek API key for the coding-agent demo and real-API e2e tests.
|
||||
- Optional: a DeepSeek API key for the REPL/ACP agent demos and real-API e2e tests.
|
||||
|
||||
## First-time setup
|
||||
|
||||
@@ -45,7 +45,7 @@ pnpm run build
|
||||
|
||||
## Environment variables
|
||||
|
||||
The real DeepSeek adapter and coding-agent demo read credentials from the environment or from a gitignored `.env` at the repo root:
|
||||
The real DeepSeek adapter and key-backed agent demos read credentials from the environment or from a gitignored `.env` at the repo root:
|
||||
|
||||
```sh
|
||||
DEEPSEEK_API_KEY=sk-...
|
||||
@@ -118,13 +118,13 @@ The echo demo does not need API credentials:
|
||||
pnpm run demo:echo
|
||||
```
|
||||
|
||||
The coding-agent demo uses the real DeepSeek adapter and needs `DEEPSEEK_API_KEY` in the environment or repo-root `.env`:
|
||||
The REPL agent demo uses the real DeepSeek adapter and needs `DEEPSEEK_API_KEY` in the environment or repo-root `.env`:
|
||||
|
||||
```sh
|
||||
pnpm run demo:coding
|
||||
pnpm run demo:repl
|
||||
```
|
||||
|
||||
The ACP server demo exposes the same coding agent over JSON-RPC stdio and also needs `DEEPSEEK_API_KEY`:
|
||||
The ACP server agent demo exposes the agent over JSON-RPC stdio and also needs `DEEPSEEK_API_KEY`:
|
||||
|
||||
```sh
|
||||
pnpm run demo:acp
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
- Node.js 24 或更新版本。仓库声明 `node >=24`;CI 在 Node 24 和 26 上跑矩阵。
|
||||
- 启用了 Corepack 的 pnpm。仓库在 `package.json` 中钉住 `pnpm@11.7.0`;如果 `pnpm --version` 无法通过 Corepack 解析,先运行 `corepack enable`。
|
||||
- Git。
|
||||
- 可选:一个 DeepSeek API key,用于 coding-agent 演示和真实 API 的 e2e 测试。
|
||||
- 可选:一个 DeepSeek API key,用于 REPL/ACP agent(智能体)演示和真实 API 的 e2e 测试。
|
||||
|
||||
## 首次搭建
|
||||
|
||||
@@ -45,7 +45,7 @@ pnpm run build
|
||||
|
||||
## 环境变量
|
||||
|
||||
真实的 DeepSeek 适配器和 coding-agent 演示从环境变量或仓库根目录一个被 gitignore 的 `.env` 读取凭证:
|
||||
真实的 DeepSeek 适配器和需要密钥的 agent 演示从环境变量或仓库根目录一个被 gitignore 的 `.env` 读取凭证:
|
||||
|
||||
```sh
|
||||
DEEPSEEK_API_KEY=sk-...
|
||||
@@ -118,13 +118,13 @@ echo 演示不需要 API 凭证:
|
||||
pnpm run demo:echo
|
||||
```
|
||||
|
||||
coding-agent 演示使用真实的 DeepSeek 适配器,需要环境变量或仓库根目录 `.env` 中的 `DEEPSEEK_API_KEY`:
|
||||
REPL agent 演示使用真实的 DeepSeek 适配器,需要环境变量或仓库根目录 `.env` 中的 `DEEPSEEK_API_KEY`:
|
||||
|
||||
```sh
|
||||
pnpm run demo:coding
|
||||
pnpm run demo:repl
|
||||
```
|
||||
|
||||
ACP 服务器演示把同一个编码 agent(智能体)通过 JSON-RPC stdio 暴露出来,同样需要 `DEEPSEEK_API_KEY`:
|
||||
ACP 服务器 agent 演示通过 JSON-RPC stdio 暴露 agent,同样需要 `DEEPSEEK_API_KEY`:
|
||||
|
||||
```sh
|
||||
pnpm run demo:acp
|
||||
|
||||
@@ -37,7 +37,7 @@ The old `base*.yml`/`acp-tail.yml` includes already deduped the *config*, but a
|
||||
## Verification
|
||||
|
||||
- Each example directory is `cordis.yml` (+ the acp `cordis.snapshot.yml`) + `README.md` + tests only — no `start.ts`, no infra preamble; `base.yml`/`base-core.yml`/`acp-tail.yml` are gone.
|
||||
- `demo:echo` / `demo:coding` / `demo:acp` run via the app-package `bin`s.
|
||||
- `demo:echo` / `demo:repl` / `demo:acp` run via the app-package `bin`s.
|
||||
- The new packages carry the per-file 100% coverage gate and a README like every `@deepseek-ai/dsh-*`. Each app package has a keyless **real-load-path** smoke that boots it through its `bin` + the cordis Loader (not a hand-built `ctx.plugin({...})` mount), guarding the `unwrapExports` export-shape bug class ([postmortem 0001](../../../postmortem/0001-acp-default-export-drops-inject.md)).
|
||||
- The ACP snapshot **replay** transcript is unchanged: the boot restructuring preserved the plugin set + load order, so `pnpm run test:snapshot` stays green against the committed goldens with no re-record.
|
||||
|
||||
|
||||
@@ -15,12 +15,12 @@ Run with: `pnpm run demo:echo`. When prompted, type "echo <something>" to trigge
|
||||
|
||||
## coding-agent
|
||||
|
||||
The real thing: DeepSeek V4 + the `read`/`write`/`edit` filesystem tools + the bash tool suite, `subagent` delegation, and the `todo_write` task tracker on the same `@deepseek-ai/dsh-stdio-agent` app. Where echo-agent proves the skeleton with mocks, this is a usable coding assistant.
|
||||
A REPL agent demo: DeepSeek V4 + the `read`/`write`/`edit` filesystem tools + the bash tool suite, `subagent` delegation, and the `todo_write` task tracker on the same `@deepseek-ai/dsh-stdio-agent` app. The UI is a terminal readline REPL.
|
||||
|
||||
Run with: `pnpm run demo:coding` (needs `DEEPSEEK_API_KEY` in the environment or a gitignored repo-root `.env`). See [coding-agent/README.md](coding-agent/README.md) for details.
|
||||
Run with: `pnpm run demo:repl` (needs `DEEPSEEK_API_KEY` in the environment or a gitignored repo-root `.env`). See [coding-agent/README.md](coding-agent/README.md) for details.
|
||||
|
||||
## acp-agent
|
||||
|
||||
The same coding agent exposed as an **Agent Client Protocol (ACP)** server over JSON-RPC stdio, via the [`@deepseek-ai/dsh-acp-agent`](../packages/ui/acp-agent) app — drive it from Zed or any other ACP client. Also the home of the keyless snapshot tests.
|
||||
An agent demo exposed as an **Agent Client Protocol (ACP)** server over JSON-RPC stdio, via the [`@deepseek-ai/dsh-acp-agent`](../packages/ui/acp-agent) app — drive it from Zed or any other ACP client. Also the home of the keyless snapshot tests.
|
||||
|
||||
Run with: `pnpm run demo:acp` (needs `DEEPSEEK_API_KEY`). See [acp-agent/README.md](acp-agent/README.md) for the Zed setup and the snapshot-test design.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# acp-agent example
|
||||
|
||||
The DeepSeek Harness coding agent exposed as an **Agent Client Protocol (ACP)** server over JSON-RPC stdio — drive it from Zed or any other ACP client.
|
||||
The DeepSeek Harness agent demo exposed as an **Agent Client Protocol (ACP)** server over JSON-RPC stdio — drive it from Zed or any other ACP client.
|
||||
|
||||
```sh
|
||||
pnpm run demo:acp # needs DEEPSEEK_API_KEY (repo-root .env or env)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "acp-agent-example",
|
||||
"description": "Runnable demo: the coding agent as an ACP server over JSON-RPC stdio (Zed & other ACP editors)",
|
||||
"description": "Runnable demo: an agent as an ACP server over JSON-RPC stdio (Zed & other ACP editors)",
|
||||
"private": true,
|
||||
"version": "0.0.1",
|
||||
"type": "module"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# coding-agent
|
||||
|
||||
The real stdio coding-agent wiring: DeepSeek V4 + the `read`/`write`/`edit` filesystem tools + the bash tool suite + subagent delegation + `todo_write` + stdio chat + JSONL persistence, loaded from `cordis.yml`. Where echo-agent proves the skeleton with mocks, this example is a usable coding assistant.
|
||||
The REPL agent demo wiring: DeepSeek V4 + the `read`/`write`/`edit` filesystem tools + the bash tool suite + subagent delegation + `todo_write` + stdio chat + JSONL persistence, loaded from `cordis.yml`. The UI is a terminal readline REPL.
|
||||
|
||||
## Run it
|
||||
|
||||
@@ -8,7 +8,7 @@ The real stdio coding-agent wiring: DeepSeek V4 + the `read`/`write`/`edit` file
|
||||
# repo root .env (gitignored) or exported env:
|
||||
# DEEPSEEK_API_KEY=sk-…
|
||||
# DEEPSEEK_BASE_URL=https://… # optional; defaults to the public API
|
||||
pnpm run demo:coding
|
||||
pnpm run demo:repl
|
||||
```
|
||||
|
||||
Type a coding task. The agent works through the `read`/`write`/`edit` filesystem tools for ordinary file operations and `bash` (+ `bash_output` / `bash_kill` for background tasks) for shell commands, searches, and test runs, each in a fresh `bash -c` (the system prompt tells the model to pass `workdir` instead of `cd`). Both the fs tools and bash resolve relative paths against the session workspace. It can also delegate with `subagent`/`subagent_fork` and track multi-step work with `todo_write` (a whole-list task tracker rendered as a checklist). Reasoning streams dimmed; tool calls/results render inline.
|
||||
@@ -26,7 +26,7 @@ Type a coding task. The agent works through the `read`/`write`/`edit` filesystem
|
||||
Each run starts a fresh session by default (its event log lands under `./.sessions/`). To **continue** a previous conversation, set `RESUME_SESSION_ID` to that session's id — the `main` agent then rehydrates the persisted log instead of starting fresh, so the model sees the earlier turns as history:
|
||||
|
||||
```sh
|
||||
RESUME_SESSION_ID=<prior-session-id> pnpm run demo:coding
|
||||
RESUME_SESSION_ID=<prior-session-id> pnpm run demo:repl
|
||||
```
|
||||
|
||||
The id is wired through `cordis.yml` (`resumeSessionId: !!js process.env.RESUME_SESSION_ID`); unset, the agent starts a new session. A missing/unreadable id is non-fatal — it logs a warning and starts no `main` agent.
|
||||
@@ -37,7 +37,7 @@ This example is a thin leaf `cordis.yml`: it picks the swappable backends, loads
|
||||
|
||||
| Entry | Demonstrates |
|
||||
|---|---|
|
||||
| `hmr` (`@cordisjs/plugin-hmr`) | the dev/demo edit-reload loop — a **leaf** entry (not baked into the app) because it is Loader-only and needs `node --expose-internals`, which `demo:coding` passes |
|
||||
| `hmr` (`@cordisjs/plugin-hmr`) | the dev/demo edit-reload loop — a **leaf** entry (not baked into the app) because it is Loader-only and needs `node --expose-internals`, which `demo:repl` passes |
|
||||
| `llm-deepseek` | real `LlmAdapter` via config (`!!js process.env.…` secrets); swap one line to `@deepseek-ai/dsh-llm-pi-ai` for the library-backed twin |
|
||||
| `bash` (`dsh-bash-local`) | the executor implementation — the swappable half of the bash seam. The model-facing `bash`/`bash_output`/`bash_kill` tool schemas (`tool-bash`) come from `agent-core`, so only the executor is a leaf choice |
|
||||
| `stdio-agent` (`@deepseek-ai/dsh-stdio-agent`) | the app bundle: the agent-core spine + console logger + JSONL persistence + readline UI + a pre-created `main` agent. Its config carries the model, system prompt, `persistenceRoot` (`./.sessions`), and `resumeSessionId` — so persistence and the agent are configured here, not wired as separate leaf plugins |
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# The coding-agent plugin tree: the real coding agent. The two swappable
|
||||
# The coding-agent plugin tree: the REPL agent demo. The two swappable
|
||||
# backends — the DeepSeek adapter and the local bash executor — plus `hmr` for
|
||||
# the dev/demo reload loop, then the stdio chat app (@deepseek-ai/dsh-stdio-
|
||||
# agent), which bundles the whole agent-core spine (timer, llm, sessions,
|
||||
@@ -6,7 +6,7 @@
|
||||
# logger, JSONL persistence, the readline UI, and a pre-created `main` agent.
|
||||
#
|
||||
# `hmr` is a leaf entry (not baked into dsh-stdio-agent): it is a Loader-only
|
||||
# dev plugin that needs `--expose-internals` — the `demo:coding` script passes
|
||||
# dev plugin that needs `--expose-internals` — the `demo:repl` script passes
|
||||
# it. Requires DEEPSEEK_API_KEY (and optionally DEEPSEEK_BASE_URL) in the
|
||||
# environment — the dsh-stdio-agent bin loads the gitignored repo-root .env
|
||||
# first. cordis.yml reads them via the `!!js` tag.
|
||||
@@ -37,7 +37,7 @@
|
||||
timeoutMs: 60000
|
||||
|
||||
# The stdio chat app: the whole spine + front-door cluster, configured for a
|
||||
# real coding agent driving a pre-created `main` agent.
|
||||
# REPL agent demo driving a pre-created `main` agent.
|
||||
- id: stdio-agent
|
||||
name: '@deepseek-ai/dsh-stdio-agent'
|
||||
config:
|
||||
@@ -46,7 +46,7 @@
|
||||
# under ./.sessions); unset starts a fresh session each run.
|
||||
resumeSessionId: !!js process.env.RESUME_SESSION_ID
|
||||
persistenceRoot: './.sessions'
|
||||
welcome: 'coding-agent ready. Give it a coding task (its tools are read, write, edit, bash, subagent, and todo_write).'
|
||||
welcome: 'agent REPL ready. Give it a coding task (its tools are read, write, edit, bash, subagent, and todo_write).'
|
||||
systemPrompt: |
|
||||
You are coding-agent, a CLI coding assistant.
|
||||
|
||||
|
||||
@@ -3,5 +3,5 @@
|
||||
"private": true,
|
||||
"version": "0.0.1",
|
||||
"type": "module",
|
||||
"description": "Runnable demo: a real coding agent — DeepSeek V4 + the bash tool suite"
|
||||
"description": "Runnable demo: an agent REPL UI with DeepSeek V4 and coding tools"
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ import { afterEach, describe, expect, it } from 'vitest'
|
||||
* product.
|
||||
*/
|
||||
|
||||
// The dsh-stdio-agent bin (the demo:coding entry) and this example's cordis.yml.
|
||||
// The dsh-stdio-agent bin (the demo:repl entry) and this example's cordis.yml.
|
||||
// The bin resolves its config-path arg from CWD; the test spawns from a temp
|
||||
// cwd, so we pass the example config's ABSOLUTE path.
|
||||
const binScript = fileURLToPath(new URL('../../../packages/ui/stdio-agent/src/bin.ts', import.meta.url))
|
||||
@@ -51,7 +51,7 @@ async function bootAndEof(): Promise<{ stdout: string; code: number }> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const proc = spawn(
|
||||
process.execPath,
|
||||
// --expose-internals: cordis.yml loads the HMR plugin (mirrors demo:coding).
|
||||
// --expose-internals: cordis.yml loads the HMR plugin (mirrors demo:repl).
|
||||
['--expose-internals', '--import', tsxLoader, binScript, configPath],
|
||||
{
|
||||
cwd,
|
||||
@@ -94,6 +94,6 @@ describe('coding-agent keyless smoke (real cordis.yml via the Loader)', () => {
|
||||
it('boots the full plugin tree, prints its banner, and exits cleanly on EOF', async () => {
|
||||
const { stdout, code } = await bootAndEof()
|
||||
expect(code).toBe(0)
|
||||
expect(stdout).toContain('coding-agent ready.')
|
||||
expect(stdout).toContain('agent REPL ready.')
|
||||
}, 15_000)
|
||||
})
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
"doc-sync": "pnpm run doc-typecheck && pnpm run verify-cordis-catalog && pnpm run verify-tool-catalog && pnpm run verify-md-wrap && pnpm run verify-md-links && pnpm run verify-doc-refs && pnpm run verify-package-paths && pnpm run verify-rfc-classification && pnpm run verify-type-equiv && pnpm run verify-translation-pairing",
|
||||
"hygiene": "pnpm run knip && pnpm run publint && pnpm run constraints && pnpm run verify-node-next-types",
|
||||
"demo:echo": "node --expose-internals --import tsx packages/ui/stdio-agent/src/bin.ts examples/echo-agent/cordis.yml",
|
||||
"demo:coding": "node --expose-internals --import tsx packages/ui/stdio-agent/src/bin.ts examples/coding-agent/cordis.yml",
|
||||
"demo:repl": "node --expose-internals --import tsx packages/ui/stdio-agent/src/bin.ts examples/coding-agent/cordis.yml",
|
||||
"demo:acp": "node --import tsx packages/ui/acp-agent/src/bin.ts examples/acp-agent/cordis.yml",
|
||||
"postinstall": "node scripts/install-lefthook.mjs"
|
||||
},
|
||||
|
||||
@@ -15,7 +15,7 @@ This package consolidates what were two near-identical copies under `examples/ec
|
||||
- id: ui-stdio
|
||||
name: '@deepseek-ai/dsh-ui-stdio'
|
||||
config:
|
||||
welcome: 'coding-agent ready. Give it a coding task.'
|
||||
welcome: 'agent REPL ready. Give it a coding task.'
|
||||
```
|
||||
|
||||
## Rendering
|
||||
|
||||
@@ -15,7 +15,7 @@ A terminal chat always wants the same cluster, so the package owns it rather tha
|
||||
| `@deepseek-ai/dsh-session-persistence-jsonl` | durable JSONL session log under `persistenceRoot` |
|
||||
| `@deepseek-ai/dsh-ui-stdio` | the readline UI, bound to the `main` agent |
|
||||
|
||||
`@cordisjs/plugin-hmr` (the dev/demo edit-reload loop) is deliberately a **leaf** entry, NOT baked in here: it is a Loader-only, subprocess-only dev plugin — its constructor throws without `node --expose-internals` + a live `loader`, and the in-process test tier cannot even import it (so a package whose `apply` statically pulled it in could never carry the per-file coverage gate). Unlike the console logger, a stray `hmr` is not a stdout-purity footgun, so leaving it at the leaf costs no safety. The `demo:echo` / `demo:coding` leaves load it and pass `--expose-internals`.
|
||||
`@cordisjs/plugin-hmr` (the dev/demo edit-reload loop) is deliberately a **leaf** entry, NOT baked in here: it is a Loader-only, subprocess-only dev plugin — its constructor throws without `node --expose-internals` + a live `loader`, and the in-process test tier cannot even import it (so a package whose `apply` statically pulled it in could never carry the per-file coverage gate). Unlike the console logger, a stray `hmr` is not a stdout-purity footgun, so leaving it at the leaf costs no safety. The `demo:echo` / `demo:repl` leaves load it and pass `--expose-internals`.
|
||||
|
||||
The leaf `cordis.yml` supplies only the **swappable backends** — an LLM adapter (`llm-deepseek` for the real model, or the mock `mock-llm` for a demo) and a bash executor (`bash-local`) — `hmr`, plus this app's [`Config`](#config). The whole plugin tree a run loads is therefore: this app's cluster, the spine inside `agent-core`, `hmr`, and the two leaf backends.
|
||||
|
||||
@@ -31,12 +31,12 @@ The leaf `cordis.yml` supplies only the **swappable backends** — an LLM adapte
|
||||
|
||||
## The bin
|
||||
|
||||
`dsh-stdio-agent [path-to-cordis.yml]` (default `./cordis.yml`) loads a gitignored `.env` from the cwd (`DEEPSEEK_API_KEY` / `DEEPSEEK_BASE_URL`), then drives the cordis Loader against the config and awaits the whole plugin tree before returning. Run it under `node --expose-internals`: the cordis Loader resolves the config's bare plugin specifiers (`@deepseek-ai/dsh-*`, npm packages) through its internal module loader, which is only active under that flag. The `demo:echo` / `demo:coding` scripts invoke it that way.
|
||||
`dsh-stdio-agent [path-to-cordis.yml]` (default `./cordis.yml`) loads a gitignored `.env` from the cwd (`DEEPSEEK_API_KEY` / `DEEPSEEK_BASE_URL`), then drives the cordis Loader against the config and awaits the whole plugin tree before returning. Run it under `node --expose-internals`: the cordis Loader resolves the config's bare plugin specifiers (`@deepseek-ai/dsh-*`, npm packages) through its internal module loader, which is only active under that flag. The `demo:echo` / `demo:repl` scripts invoke it that way.
|
||||
|
||||
## Example leaf `cordis.yml`
|
||||
|
||||
```yaml
|
||||
# A real coding agent: hmr + the DeepSeek adapter + local bash, then this app.
|
||||
# A REPL agent demo: hmr + the DeepSeek adapter + local bash, then this app.
|
||||
- id: hmr
|
||||
name: '@cordisjs/plugin-hmr'
|
||||
config:
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
* duplicated in their `start.ts`: load the gitignored repo-root `.env`, then
|
||||
* drive the cordis Loader against the config path (default `./cordis.yml`).
|
||||
*
|
||||
* Usage: `dsh-stdio-agent [path-to-cordis.yml]`. The `demo:echo` / `demo:coding`
|
||||
* Usage: `dsh-stdio-agent [path-to-cordis.yml]`. The `demo:echo` / `demo:repl`
|
||||
* scripts invoke it with the example's config.
|
||||
*
|
||||
* @module @deepseek-ai/dsh-stdio-agent/bin
|
||||
@@ -105,7 +105,7 @@ function assertEntriesLoaded(ctx: Context): void {
|
||||
*
|
||||
* Bare plugin specifiers in the config (`@deepseek-ai/dsh-*`, npm packages) are
|
||||
* resolved by the cordis Loader's internal module loader, which is only active
|
||||
* under `node --expose-internals` (the flag the `demo:echo`/`demo:coding` scripts
|
||||
* under `node --expose-internals` (the flag the `demo:echo`/`demo:repl` scripts
|
||||
* pass). Without it the Loader falls back to resolving relative to its own module
|
||||
* and cannot find the config's plugins, so a consumer running the built bin must
|
||||
* pass `--expose-internals` (or install the plugins where node hoists them).
|
||||
|
||||
@@ -27,6 +27,8 @@ const vendoredPackages = new Set([
|
||||
'@cordisjs/plugin-logger-console',
|
||||
])
|
||||
|
||||
const localArtifactDirs = new Set(['node_modules'])
|
||||
|
||||
/** The subset of package.json fields this constraint check cares about. */
|
||||
interface PackageManifest {
|
||||
name?: string
|
||||
@@ -64,10 +66,13 @@ function packageDirs(base: string, depth: number): string[] {
|
||||
if (depth === 1) {
|
||||
return readdirSync(join(root, base), { withFileTypes: true })
|
||||
.filter(entry => entry.isDirectory())
|
||||
.filter(entry => !localArtifactDirs.has(entry.name))
|
||||
.filter(entry => existsSync(join(root, base, entry.name, 'package.json')))
|
||||
.map(entry => join(base, entry.name))
|
||||
}
|
||||
return readdirSync(join(root, base), { withFileTypes: true })
|
||||
.filter(entry => entry.isDirectory())
|
||||
.filter(entry => !localArtifactDirs.has(entry.name))
|
||||
.flatMap(group => packageDirs(join(base, group.name), depth - 1))
|
||||
}
|
||||
|
||||
@@ -177,6 +182,7 @@ function checkHierarchyShape(): string[] {
|
||||
}
|
||||
for (const pkg of readdirSync(join(packagesRoot, group.name), { withFileTypes: true })) {
|
||||
if (!pkg.isDirectory()) continue
|
||||
if (localArtifactDirs.has(pkg.name)) continue
|
||||
const pkgRel = join(groupRel, pkg.name)
|
||||
if (!existsSync(join(packagesRoot, group.name, pkg.name, 'package.json'))) {
|
||||
errors.push(`${pkgRel}: expected a package here (no package.json found) — the hierarchy is exactly packages/<group>/<pkg>, no deeper nesting`)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { execFileSync } from 'node:child_process'
|
||||
import { readdirSync } from 'node:fs'
|
||||
import { existsSync, readdirSync } from 'node:fs'
|
||||
import { resolve } from 'node:path'
|
||||
|
||||
// publint every harness package. Packages live at packages/<group>/<pkg>
|
||||
@@ -14,6 +14,7 @@ const packages = readdirSync(packagesRoot, { withFileTypes: true })
|
||||
.flatMap(group =>
|
||||
readdirSync(resolve(packagesRoot, group.name), { withFileTypes: true })
|
||||
.filter(pkg => pkg.isDirectory())
|
||||
.filter(pkg => existsSync(resolve(packagesRoot, group.name, pkg.name, 'package.json')))
|
||||
.map(pkg => `packages/${group.name}/${pkg.name}`),
|
||||
)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user