mirror of
https://github.com/deepseek-ai/deepseek-harness
synced 2026-08-15 21:04:50 +00:00
docs: fix NIH-audit review findings (Codex round 1)
- Drop the AGENTS.md budget bump: trim filler words in the layout map and command comments so the new convention line fits the existing 1680 ceiling (1679/1680; master was 1680/1680) - timers/promises note: 'Replace both' -> all three sites, and add pty-local to the acceptance criteria (EN+ZH) - execa note: 17 value-taking options plus boolean flags, not 18 (EN+ZH) - rejected roll-up: lsp-local src is ~1,800 lines, not 2,112 (EN+ZH) - re-record the three touched i18n pairs
This commit is contained in:
@@ -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-26-builtin-timer-promises-for-hand-rolled-sleeps.md: 036e2f2906ca99aaab30a2164649f9c750b4ad21
|
||||
2026-07-26-builtin-timer-promises-for-hand-rolled-sleeps.zh.md: 15a6f0dd412d142647df2722335a454a028cb798
|
||||
2026-07-26-builtin-timer-promises-for-hand-rolled-sleeps.md: 1a012aeabc7f9445127d6b8edcbe2f72e62f0eba
|
||||
2026-07-26-builtin-timer-promises-for-hand-rolled-sleeps.zh.md: 742d2c5ee8573c9b2bdf555c938c83dd6ea9f999
|
||||
|
||||
@@ -14,7 +14,7 @@ Three packages hand-roll promise-wrapped timers that the `node:timers/promises`
|
||||
|
||||
## Proposal
|
||||
|
||||
Replace both with `import { setTimeout } from 'node:timers/promises'`:
|
||||
Replace all three with `import { setTimeout } from 'node:timers/promises'`:
|
||||
|
||||
- llm-retry: `try { await setTimeout(delayMs, undefined, { signal }); /* retry */ } catch { /* abort → fail */ }` — with a signal, the promise rejects only with the abort error, and a pre-aborted signal rejects immediately; behavior is identical, including timer clearing on abort. The empty `catch` names the abort rejection per the repo's empty-catch rule.
|
||||
- workflow-workerthread: `setTimeout(ms, undefined, { ref: false })` — exact semantics including not holding the event loop open.
|
||||
@@ -29,8 +29,8 @@ No dedicated tests pin the helpers themselves; the packages' behavior suites kee
|
||||
|
||||
## Acceptance criteria
|
||||
|
||||
- Neither package defines a promise-wrapped `setTimeout` helper; both import from `node:timers/promises`.
|
||||
- `llm-retry` and `workflow-workerthread` test suites pass unchanged (behavioral parity).
|
||||
- None of the three packages defines a promise-wrapped `setTimeout` helper; all import from `node:timers/promises`.
|
||||
- The `llm-retry`, `workflow-workerthread`, and `pty-local` test suites pass unchanged (behavioral parity).
|
||||
|
||||
## Risks
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ Status: proposed
|
||||
|
||||
## 提案
|
||||
|
||||
用 `import { setTimeout } from 'node:timers/promises'` 替换上述实现:
|
||||
用 `import { setTimeout } from 'node:timers/promises'` 替换这三处实现:
|
||||
|
||||
- llm-retry:`try { await setTimeout(delayMs, undefined, { signal }); /* retry */ } catch { /* abort → fail */ }`。传入 signal 后,该 promise 只会以 abort 错误拒绝,已提前中止的 signal 则立即拒绝;行为完全一致,包括中止时清除定时器。按仓库的空 catch 规则,这个空 `catch` 注明其吞下的是 abort 拒绝。
|
||||
- workflow-workerthread:`setTimeout(ms, undefined, { ref: false })`,语义完全等价,包括不会让事件循环保持存活。
|
||||
@@ -29,8 +29,8 @@ Status: proposed
|
||||
|
||||
## 验收标准
|
||||
|
||||
- 上述包不再各自定义 promise 包装的 `setTimeout` 辅助函数,而是都从 `node:timers/promises` 导入。
|
||||
- `llm-retry` 与 `workflow-workerthread` 的测试套件原样通过(行为等价)。
|
||||
- 这三个包都不再各自定义 promise 包装的 `setTimeout` 辅助函数,而是都从 `node:timers/promises` 导入。
|
||||
- `llm-retry`、`workflow-workerthread` 与 `pty-local` 的测试套件原样通过(行为等价)。
|
||||
|
||||
## 风险
|
||||
|
||||
|
||||
@@ -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-26-execa-for-test-subprocess-plumbing.md: 3b2ba9062a72dfe03c9e9a84fa13fe23da39302a
|
||||
2026-07-26-execa-for-test-subprocess-plumbing.zh.md: 61e12233fc49ca7788882ca409d6f67f030d2475
|
||||
2026-07-26-execa-for-test-subprocess-plumbing.md: 99a86258fe4d59db6a0e144dbcee94c095f70f8f
|
||||
2026-07-26-execa-for-test-subprocess-plumbing.zh.md: 525e09f07ce3e5dc61f1cadab5c11ea0790cccee
|
||||
|
||||
@@ -10,7 +10,7 @@ Roughly ten e2e/smoke files re-derive the same spawn-collect-timeout choreograph
|
||||
|
||||
Two related test-infra hand-rolls compound the case:
|
||||
|
||||
- `packages/support/llm-mock-server/src/cli.ts` hand-tokenizes 18 `--flag value` options (~45–60 lines of loop and value-extraction helpers) where the `node:util` `parseArgs` builtin is already the repo idiom (`cli-demo`, `acp-demo`, `verify-runtime-closure.ts`, `packages/sdk/scripts`).
|
||||
- `packages/support/llm-mock-server/src/cli.ts` hand-tokenizes 17 value-taking `--flag value` options plus boolean flags (~45–60 lines of loop and value-extraction helpers) where the `node:util` `parseArgs` builtin is already the repo idiom (`cli-demo`, `acp-demo`, `verify-runtime-closure.ts`, `packages/sdk/scripts`).
|
||||
- `apps/web/tests/smoke-real.e2e.ts` and `apps/web/tests/scaffold.ts` carry two verbatim copies of a regex `.env` parser (~20 lines) where the `process.loadEnvFile` builtin has exactly the required no-override semantics — and the vitest e2e/snapshot/web configs already load root `.env` with it before these files run, making the copies arguably dead.
|
||||
- The snapshot harness hand-rolls three poll-until-deadline loops (`waitForPersistedTurnStart`/`waitForPersistedTurnEnd`/`waitForWorkspaceFile` in `packages/support/acp-snapshot/src/harness.ts`, ~55 lines) plus `waitForFile` in `crash-recovery.e2e.ts`, where `vi.waitFor`/`expect.poll` cover the shape — vitest is already a runtime dependency of `dsh-acp-snapshot`, so this adds nothing.
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ Status: proposed
|
||||
|
||||
另有两处相关的测试基础设施手写代码进一步强化了替换的理由:
|
||||
|
||||
- `packages/support/llm-mock-server/src/cli.ts` 手工逐个切分 18 个 `--flag value` 选项(约 45–60 行的循环与取值辅助函数),而 `node:util` 内置的 `parseArgs` 早已是本仓库的惯用写法(`cli-demo`、`acp-demo`、`verify-runtime-closure.ts`、`packages/sdk/scripts`)。
|
||||
- `packages/support/llm-mock-server/src/cli.ts` 手工逐个切分 17 个带值的 `--flag value` 选项外加若干布尔标志(约 45–60 行的循环与取值辅助函数),而 `node:util` 内置的 `parseArgs` 早已是本仓库的惯用写法(`cli-demo`、`acp-demo`、`verify-runtime-closure.ts`、`packages/sdk/scripts`)。
|
||||
- `apps/web/tests/smoke-real.e2e.ts` 与 `apps/web/tests/scaffold.ts` 携带两份逐字相同的正则 `.env` 解析器拷贝(约 20 行),而内置的 `process.loadEnvFile` 恰好具备所需的「不覆盖已有值」语义;并且 vitest 的 e2e/snapshot/web 配置在这些文件运行之前就已用它加载了根 `.env`,这两份拷贝几乎可以视为死代码。
|
||||
- 快照 harness 手写了三个「轮询直到截止时间」的循环(`packages/support/acp-snapshot/src/harness.ts` 中的 `waitForPersistedTurnStart`/`waitForPersistedTurnEnd`/`waitForWorkspaceFile`,约 55 行),外加 `crash-recovery.e2e.ts` 中的 `waitForFile`,而 `vi.waitFor`/`expect.poll` 正好覆盖这种形态;vitest 本来就是 `dsh-acp-snapshot` 的运行时依赖,因此这不新增任何东西。
|
||||
|
||||
|
||||
@@ -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-26-dependency-swaps-rejected-by-nih-audit.md: 6ee4ce36bcc25b206eebedd18270021e4937761f
|
||||
2026-07-26-dependency-swaps-rejected-by-nih-audit.zh.md: b983dfcfa12171bfe1ae9bc79936d3a5876e5e68
|
||||
2026-07-26-dependency-swaps-rejected-by-nih-audit.md: c988ca0c75e9c50686551f3be1971d736b971e2a
|
||||
2026-07-26-dependency-swaps-rejected-by-nih-audit.zh.md: e85161cb2ee616d388aa2a9dd065c315c60cd44a
|
||||
|
||||
@@ -14,7 +14,7 @@ Adopt the following dependency swaps. Rejected — per-item evidence below; a fu
|
||||
|
||||
**Protocol and parsing:**
|
||||
|
||||
- **`vscode-jsonrpc` for LSP base-protocol framing/correlation** (`lsp-local`): the swappable core is ~255 of 2,112 src lines; the package cannot express the configured `maxMessageBytes` incoming-size bound (restoring it means rebuilding the deleted framing), inverts the cancel-grace teardown semantics (`raceAbort` rejects immediately then tears down; vscode-jsonrpc keeps the promise pending), errors on pre-header stdout banners real servers emit, and is CJS in an ESM-everywhere repo. The [LSP seam note](../../implemented/architecture/2026-07-15-lsp-capability-seam.md) assigns JSON-RPC ownership to `dsh-lsp-local`; this audit is the explicit on-record weighing of the dependency it lacked.
|
||||
- **`vscode-jsonrpc` for LSP base-protocol framing/correlation** (`lsp-local`): the swappable core is ~255 of ~1,800 src lines; the package cannot express the configured `maxMessageBytes` incoming-size bound (restoring it means rebuilding the deleted framing), inverts the cancel-grace teardown semantics (`raceAbort` rejects immediately then tears down; vscode-jsonrpc keeps the promise pending), errors on pre-header stdout banners real servers emit, and is CJS in an ESM-everywhere repo. The [LSP seam note](../../implemented/architecture/2026-07-15-lsp-capability-seam.md) assigns JSON-RPC ownership to `dsh-lsp-local`; this audit is the explicit on-record weighing of the dependency it lacked.
|
||||
- **`vscode-languageserver-types` for lsp-local's wire-type subset**: ~80 type lines and ~45 guard lines, but upstream guards differ in both directions (accept `uri: undefined` the repo must reject; require `targetRange` the repo tolerates absent), and the initialize-result shapes live in `vscode-languageserver-protocol`, dragging `vscode-jsonrpc` in as a runtime dep — ~1 MB for 80 spec-exact lines.
|
||||
- **`json-rpc-2.0` for `dsh-jsonrpc`**: deletable correlation/dispatch is real (~100–130 lines) but the NDJSON wire must stay bit-identical for the hand-rolled Python SDK client, the package is single-maintainer, and the [GUI RPC note](../../implemented/architecture/2026-07-19-gui-layering-and-rpc-protocol.md) already treats this package as a frozen narrow surface. `vscode-jsonrpc` is a worse fit still (Content-Length framing, cancellation vocabulary the protocol lacks).
|
||||
- **`jsonrpcclient` for the Python SDK client**: v4 builds/parses messages only — ~20 lines — while the 500 lines that matter (subprocess lifecycle, threaded reader, id correlation, bidirectional server-role responses) stay; the library is in low-maintenance mode.
|
||||
|
||||
@@ -14,7 +14,7 @@ Status: rejected — 下列每一项替换在证据上都未达到净简化门
|
||||
|
||||
**协议与解析:**
|
||||
|
||||
- **以 `vscode-jsonrpc` 承担 LSP 基础协议的分帧/关联**(`lsp-local`):可替换的核心只占 src 全部 2,112 行中的约 255 行;该包无法表达可配置的 `maxMessageBytes` 入站大小上限(要恢复它就得重建被删掉的分帧代码),反转了取消宽限期的拆除语义(`raceAbort` 立即 reject 再拆除;vscode-jsonrpc 让 promise 保持挂起),会在真实服务器输出的 header 前 stdout 横幅上报错,而且在这个 ESM 通行的仓库里它是 CJS。[LSP seam 决策](../../implemented/architecture/2026-07-15-lsp-capability-seam.md)把 JSON-RPC 的所有权划给 `dsh-lsp-local`;本次审计正是对该决策当时缺失的这项依赖权衡的明文记录。
|
||||
- **以 `vscode-jsonrpc` 承担 LSP 基础协议的分帧/关联**(`lsp-local`):可替换的核心只占 src 约 1,800 行中的约 255 行;该包无法表达可配置的 `maxMessageBytes` 入站大小上限(要恢复它就得重建被删掉的分帧代码),反转了取消宽限期的拆除语义(`raceAbort` 立即 reject 再拆除;vscode-jsonrpc 让 promise 保持挂起),会在真实服务器输出的 header 前 stdout 横幅上报错,而且在这个 ESM 通行的仓库里它是 CJS。[LSP seam 决策](../../implemented/architecture/2026-07-15-lsp-capability-seam.md)把 JSON-RPC 的所有权划给 `dsh-lsp-local`;本次审计正是对该决策当时缺失的这项依赖权衡的明文记录。
|
||||
- **以 `vscode-languageserver-types` 承担 lsp-local 的协议类型子集**:约 80 行类型加约 45 行守卫,但上游守卫在两个方向上都与本仓库不一致(接受本仓库必须拒绝的 `uri: undefined`;强制要求本仓库容忍缺失的 `targetRange`),而且 initialize 结果的形状住在 `vscode-languageserver-protocol` 里,会把 `vscode-jsonrpc` 拖成运行时依赖——为 80 行严格贴合规范的代码付出约 1 MB。
|
||||
- **以 `json-rpc-2.0` 替换 `dsh-jsonrpc`**:可删除的关联/分发代码确实存在(约 100–130 行),但 NDJSON 协议格式(wire format)必须与手写的 Python SDK 客户端逐位一致,该包只有单一维护者,且 [GUI RPC 决策](../../implemented/architecture/2026-07-19-gui-layering-and-rpc-protocol.md)已把这个包当作冻结的窄接口面对待。`vscode-jsonrpc` 更不合适(Content-Length 分帧、该协议并不具备的取消词汇)。
|
||||
- **以 `jsonrpcclient` 承担 Python SDK 客户端**:v4 只做消息的构造/解析——约 20 行——而真正要紧的 500 行(子进程生命周期、线程化读取器、id 关联、双向的服务端角色应答)全都保留;该库处于低维护模式。
|
||||
|
||||
18
AGENTS.md
18
AGENTS.md
@@ -12,7 +12,7 @@ DeepSeek Harness SDK is a plugin-based agent harness on vendored Cordis: **every
|
||||
vendor/ Vendored Cordis source — manifest + sync procedure in vendor/README.md
|
||||
packages/ @deepseek-ai/dsh-<pkg> workspaces at packages/<group>/<pkg>/
|
||||
core/ product API spine: session, system-prompt, tools, agent, agent-loop
|
||||
llm/ LLM seam + the DeepSeek adapters (hand-rolled + pi-ai design twin)
|
||||
llm/ LLM seam + DeepSeek adapters (hand-rolled + pi-ai design twin)
|
||||
bash/ bash executor seam + local impl + model-facing bash tools
|
||||
pty/ persistent PTY seam/backend/tools
|
||||
fs/ filesystem seam + local impl + policy gate + read/write/edit tools
|
||||
@@ -22,17 +22,17 @@ packages/ @deepseek-ai/dsh-<pkg> workspaces at packages/<group>/<pkg>/
|
||||
compact/ compaction seam + basic backend
|
||||
context/ request-context plugins
|
||||
subagent/ subagent seam + spawn/fork/ACP backends + delegation tool
|
||||
workflow/ workflow seam + worker-thread engine + the workflow tool
|
||||
todo/ the todo_write tool
|
||||
workflow/ workflow seam + worker-thread engine + workflow tool
|
||||
todo/ todo_write tool
|
||||
plan/ plan mode as logged per-agent collaboration state
|
||||
guard/ loop-hygiene plugins
|
||||
cordis/ self-referential toolset: the agent inspects/mounts plugins in its own runtime
|
||||
hooks/ Claude Code / Codex hook bridges + shared wire-protocol library
|
||||
hooks/ Claude Code/Codex hook bridges + shared wire-protocol library
|
||||
session-persistence/ persistence seam + JSONL/SQLite backends
|
||||
acp/ automation-only Agent Client Protocol server
|
||||
ui/ TUI/JSON-RPC bridges; boot, approval, interaction plugins
|
||||
examples/ demo bundles (agent-spine + TUI/CLI/ACP/JSON-RPC bins) leaves load
|
||||
support/ dev/test infrastructure packages
|
||||
support/ dev/test infrastructure
|
||||
util/ zero-dependency utilities
|
||||
python/ Python SDK and bundled runtime (see python/README.md)
|
||||
native/ node-addon-landlock-run source of record (see native/README.md)
|
||||
@@ -60,11 +60,11 @@ pnpm run lint
|
||||
pnpm run duplication # cross-file TypeScript clone detection
|
||||
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 leaf list in scripts/run-gates.ts
|
||||
pnpm run website:build # VitePress build (doubles as the site's dead-link check)
|
||||
pnpm run doc-sync # all documentation gates; leaf list in scripts/run-gates.ts
|
||||
pnpm run website:build # VitePress build (doubles as dead-link check)
|
||||
pnpm run demo:headless "task" # one-shot agent (needs DEEPSEEK_API_KEY)
|
||||
pnpm run demo:tui # full-screen TUI coding agent (needs DEEPSEEK_API_KEY)
|
||||
pnpm run demo:cordis # self-referential demo: the agent modifies its own runtime (needs key)
|
||||
pnpm run demo:cordis # the agent modifies its own runtime (needs key)
|
||||
pnpm run demo:acp # ACP automation server (needs DEEPSEEK_API_KEY)
|
||||
```
|
||||
|
||||
@@ -96,7 +96,7 @@ Real-API tests and demos read `DEEPSEEK_API_KEY`, optional `DEEPSEEK_BASE_URL`,
|
||||
- **Model-visible ⟺ logged**: anything that reaches a model request must be reconstructable from the session log; a new model-visible input requires a session event.
|
||||
- **Plugins, not loop changes**: new behavior goes on the documented extension seams; changing `agent-loop` requires updating docs/architecture.md.
|
||||
- **Capability seams are three packages** — interface / implementation / consumer; don't split preemptively.
|
||||
- **Prefer maintained dependencies over hand-rolling** when the swap genuinely deletes owned code and tests ([policy](.agents/notes/implemented/process/2026-07-26-dependencies-over-hand-rolling.md)).
|
||||
- **Prefer maintained dependencies over hand-rolling** when they genuinely delete owned code and tests ([policy](.agents/notes/implemented/process/2026-07-26-dependencies-over-hand-rolling.md)).
|
||||
- **Explicit > implicit at package seams**: defaulting is an explicit `resolve(request): Spec` step in the owning implementation, never a hidden `?? default` inside `run()` (the `dsh-bash` request/spec split is the template).
|
||||
- **No hardcoded tunables in plugins**: deployment-varying choices are validated `Config` fields changeable from cordis.yml; a `DEFAULT_*` constant or test seam is not configurability. Protocol constants, external specs, and security invariants stay fixed.
|
||||
- **Misconfiguration fails loud** at load when self-contained, otherwise at the earliest resolvable point; never silently skip a missing referent.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"AGENTS.md": 1700,
|
||||
"AGENTS.md": 1680,
|
||||
"docs/AGENTS.md": 1150,
|
||||
"docs/architecture.md": 1800,
|
||||
"docs/cordis-primer.md": 600,
|
||||
|
||||
Reference in New Issue
Block a user