refactor: apply repository naming contract
Apply the accepted pre-release package, service, type, directory, and role renames as one repository-wide change.
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 .agents/notes/implemented/simplification/2026-06-26-fsspec-style-fs-seam.md
|
||||
2026-06-26-fsspec-style-fs-seam.md: 1cd31fa258b4ddcfe7908028dbe25a90c053e77d
|
||||
2026-06-26-fsspec-style-fs-seam.zh.md: 5b06066b30d40b809e9c16c07cca44ddf012de7e
|
||||
2026-06-26-fsspec-style-fs-seam.md: ec3ee9bd0781b64b804f58c32cb59afc7f05e0d4
|
||||
2026-06-26-fsspec-style-fs-seam.zh.md: a4e50c401e56854933bcaebd24cf9955949b3c31
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Agent Note: Split the filesystem seam — provider text mutations plus the `dsh-fs-policy` plugin
|
||||
# Agent Note: Split the filesystem seam — provider text mutations plus the `dsh-fs-observation-policy` plugin
|
||||
|
||||
Status: implemented
|
||||
|
||||
@@ -15,7 +15,7 @@ That makes every future backend reimplement model-facing read semantics and obse
|
||||
|
||||
This also creates a real UX dead-end: a windowed read records `view: partial`, and partial views cannot authorize `edit`. A model that reads lines 100-150 of a large file therefore cannot edit line 120 unless it first gets a `full` read, which may be impossible for a file past the read cap. Literal edit only needs freshness: the bytes being matched must still be from the version the model read.
|
||||
|
||||
The old Agent Note already deferred a separate `@deepseek-ai/dsh-fs-policy` package. This decision builds that layer and keeps `ctx.fs` close to fsspec-style storage primitives (`info`/`cat`/`open`), without turning it into full fsspec.
|
||||
The old Agent Note already deferred a separate `@deepseek-ai/dsh-fs-observation-policy` package. This decision builds that layer and keeps `ctx.fs` close to fsspec-style storage primitives (`info`/`cat`/`open`), without turning it into full fsspec.
|
||||
|
||||
## Decision
|
||||
|
||||
@@ -23,14 +23,14 @@ Split the stack into four layers:
|
||||
|
||||
```text
|
||||
tool dsh-tool-fs model-facing schemas + read windowing + text rendering; the EXECUTOR (reads/writes/edits via ctx.fs, dispatches the fs/* events)
|
||||
policy dsh-fs-policy observed-state + read-before-edit + write/edit freshness, contributed through the fs/* event gate (no service)
|
||||
policy dsh-fs-observation-policy observed-state + read-before-edit + write/edit freshness, contributed through the fs/* event gate (no service)
|
||||
provider contract dsh-fs ctx.fs: text IO + atomic mutation primitives (optional version guard)
|
||||
provider dsh-fs-local local implementation of ctx.fs
|
||||
```
|
||||
|
||||
`dsh-tool-fs` keeps the same model-facing `read`/`write`/`edit` schemas. It is the executor: it injects `fs` (not a policy service) and reaches `ctx.fs` directly, owns read windowing, and dispatches the `fs/*` events so `dsh-fs-policy` can gate and record.
|
||||
`dsh-tool-fs` keeps the same model-facing `read`/`write`/`edit` schemas. It is the executor: it injects `fs` (not a policy service) and reaches `ctx.fs` directly, owns read windowing, and dispatches the `fs/*` events so `dsh-fs-observation-policy` can gate and record.
|
||||
|
||||
This Agent Note decided the four-layer split, the provider contract, and the freshness policy. The tool↔policy COUPLING was then refined by [the event-gate Agent Note](../architecture/2026-06-26-file-context-as-event-gate.md): `dsh-fs-policy` is a gate PLUGIN that participates through the `fs/*` events rather than a `ctx.fileContext` method service, so the tool is not method-coupled to it and read windowing + the fs I/O live in `dsh-tool-fs`. This document describes that landed event-gate shape; the provider's version guard is optional (omit = unconditional bare provider).
|
||||
This Agent Note decided the four-layer split, the provider contract, and the freshness policy. The tool↔policy COUPLING was then refined by [the event-gate Agent Note](../architecture/2026-06-26-file-context-as-event-gate.md): `dsh-fs-observation-policy` is a gate PLUGIN that participates through the `fs/*` events rather than a `ctx.fileContext` method service, so the tool is not method-coupled to it and read windowing + the fs I/O live in `dsh-tool-fs`. This document describes that landed event-gate shape; the provider's version guard is optional (omit = unconditional bare provider).
|
||||
|
||||
## Provider Contract
|
||||
|
||||
@@ -69,9 +69,9 @@ Deleted from `dsh-fs`: `readPage`, `FsExpectation`, `FsView`, `FsStateSource`, `
|
||||
|
||||
## Policy Contract
|
||||
|
||||
`@deepseek-ai/dsh-fs-policy` is a plugin, not a service: it registers no `ctx.*` key and injects nothing. It owns the write/edit freshness policy and observed-state that do not belong on the `FileSystem` provider base class (where a sandboxed/remote backend would otherwise inherit model-facing observation policy it has no business carrying). It contributes that policy through the `fs/*` event gate the executor dispatches.
|
||||
`@deepseek-ai/dsh-fs-observation-policy` is a plugin, not a service: it registers no `ctx.*` key and injects nothing. It owns the write/edit freshness policy and observed-state that do not belong on the `FileSystem` provider base class (where a sandboxed/remote backend would otherwise inherit model-facing observation policy it has no business carrying). It contributes that policy through the `fs/*` event gate the executor dispatches.
|
||||
|
||||
Observed state lives here as `WeakMap<owner, Map<targetKey, FsVersion>>`. An entry exists iff the owner has read, written, OR edited that target (every success emits `fs/observed`), so its presence *is* the prior-observation record — there is no separate `hasRead` flag. The owner is derived structurally from the opaque event actor (`{ agent?: { session? } }`), a shape that lives in `dsh-fs-policy`, not `dsh-fs`.
|
||||
Observed state lives here as `WeakMap<owner, Map<targetKey, FsVersion>>`. An entry exists iff the owner has read, written, OR edited that target (every success emits `fs/observed`), so its presence *is* the prior-observation record — there is no separate `hasRead` flag. The owner is derived structurally from the opaque event actor (`{ agent?: { session? } }`), a shape that lives in `dsh-fs-observation-policy`, not `dsh-fs`.
|
||||
|
||||
The plugin decides three `fs/*` events:
|
||||
|
||||
@@ -85,9 +85,9 @@ The plugin does NO filesystem I/O: "have you observed this file?" is a `WeakMap`
|
||||
|
||||
`dsh-tool-fs` keeps the same schemas and prompt entry. `read` still exposes `file_path`, `offset`, and `limit`; `write` and `edit` are unchanged. It is the executor: it validates model args, reads/writes/edits through `ctx.fs` directly, owns line windowing and result rendering (`N: text`, footer, `<path>/<content>` envelope), and dispatches the `fs/*` events.
|
||||
|
||||
Each mutation dispatches its intent waterfall with an `undefined` bare-provider default, then calls `ctx.fs`, then emits `fs/observed`: e.g. `write` does `ctx.waterfall('fs/write-intent', target, exec, () => undefined)` → `ctx.fs.writeText(target, content, intent)` → `ctx.emit('fs/observed', …)`. A `read` stats once, reads/streams, builds the window, and emits `fs/observed`. Passing `exec` as the actor lets `dsh-fs-policy` derive the owner without the tool reaching into the policy.
|
||||
Each mutation dispatches its intent waterfall with an `undefined` bare-provider default, then calls `ctx.fs`, then emits `fs/observed`: e.g. `write` does `ctx.waterfall('fs/write-intent', target, exec, () => undefined)` → `ctx.fs.writeText(target, content, intent)` → `ctx.emit('fs/observed', …)`. A `read` stats once, reads/streams, builds the window, and emits `fs/observed`. Passing `exec` as the actor lets `dsh-fs-observation-policy` derive the owner without the tool reaching into the policy.
|
||||
|
||||
Because the policy is contributed through events with an `undefined` default, `dsh-tool-fs` is not method-coupled to `dsh-fs-policy`: with the plugin absent, every intent waterfall falls through to `undefined` (unconditional bare-provider write/edit) and `fs/observed` has no listener. Loading the plugin back layers the read-before-write/edit policy on.
|
||||
Because the policy is contributed through events with an `undefined` default, `dsh-tool-fs` is not method-coupled to `dsh-fs-observation-policy`: with the plugin absent, every intent waterfall falls through to `undefined` (unconditional bare-provider write/edit) and `fs/observed` has no listener. Loading the plugin back layers the read-before-write/edit policy on.
|
||||
|
||||
## Concurrency Boundary
|
||||
|
||||
@@ -101,7 +101,7 @@ Cross-process writes are best-effort freshness plus atomic replacement: `mtime:s
|
||||
|
||||
This Agent Note reverses two decisions from [filesystem-capability-seam](../architecture/2026-06-17-filesystem-capability-seam.md) and narrows a third:
|
||||
|
||||
- Read-before-write/edit policy moves out of `ctx.fs` and into the `dsh-fs-policy` plugin (on the `fs/*` event gate).
|
||||
- Read-before-write/edit policy moves out of `ctx.fs` and into the `dsh-fs-observation-policy` plugin (on the `fs/*` event gate).
|
||||
- Text reads no longer return backend-numbered line records or `full`/`partial` views; authorization is based on version freshness, so a windowed read can authorize edit when the file is unchanged.
|
||||
- Literal edit no longer sits behind the old `applyEdit` API that mixed backend mutation with seam-owned observation policy. It remains a provider primitive as `editText`, because version guard + literal match + atomic rewrite must stay inside the provider's mutation critical section.
|
||||
|
||||
@@ -109,7 +109,7 @@ It keeps the Service Definition / Service provider / Consumer discipline, consum
|
||||
|
||||
## Verification
|
||||
|
||||
`dsh-fs` exposes exactly `resolve`/`stat`/`readText`/`streamText`/`writeText`/`editText` (`stat` returning `FsInfo | undefined`, `writeText` taking `FsWriteIntent`), with the removed types/primitives gone; `dsh-fs-local` carries no line, view, or `formatReadBody` logic; model-facing schemas stayed byte-for-byte unchanged. Tests pin that a windowed read authorizes a later edit of an unchanged file, that an edit based on a stale read reports `FS_STALE_VERSION` before attempting literal matching, that version-CAS behavior is preserved, and that the observation contract holds (a `read`-tool read records observed-state; a direct `ctx.fs` read does not); `dsh-fs-policy` has HMR/disposal coverage.
|
||||
`dsh-fs` exposes exactly `resolve`/`stat`/`readText`/`streamText`/`writeText`/`editText` (`stat` returning `FsInfo | undefined`, `writeText` taking `FsWriteIntent`), with the removed types/primitives gone; `dsh-fs-local` carries no line, view, or `formatReadBody` logic; model-facing schemas stayed byte-for-byte unchanged. Tests pin that a windowed read authorizes a later edit of an unchanged file, that an edit based on a stale read reports `FS_STALE_VERSION` before attempting literal matching, that version-CAS behavior is preserved, and that the observation contract holds (a `read`-tool read records observed-state; a direct `ctx.fs` read does not); `dsh-fs-observation-policy` has HMR/disposal coverage.
|
||||
|
||||
## Later extension
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Agent Note: 拆分文件系统 seam——提供方文本变更操作与 `dsh-fs-policy` 插件
|
||||
# Agent Note: 拆分文件系统 seam——提供方文本变更操作与 `dsh-fs-observation-policy` 插件
|
||||
|
||||
Status: implemented
|
||||
|
||||
@@ -15,7 +15,7 @@ Status: implemented
|
||||
|
||||
这还造成了一个真实的用户体验死胡同:窗口化读取记录 `view: partial`,而 partial 视图无法授权 `edit`。一个模型读取了大文件的第 100-150 行,如果想编辑第 120 行,就必须先获取一次 `full` 读取,而对于超过读取上限的文件这可能做不到。字面编辑实际上只需要新鲜度:被匹配的字节仍然来自模型所读取的那个版本即可。
|
||||
|
||||
旧 Agent Note 已经推迟了独立的 `@deepseek-ai/dsh-fs-policy` 包。本决策构建该层,使 `ctx.fs` 保持接近 fsspec 风格的存储原语(`info`/`cat`/`open`),但不把它变成完整的 fsspec。
|
||||
旧 Agent Note 已经推迟了独立的 `@deepseek-ai/dsh-fs-observation-policy` 包。本决策构建该层,使 `ctx.fs` 保持接近 fsspec 风格的存储原语(`info`/`cat`/`open`),但不把它变成完整的 fsspec。
|
||||
|
||||
## 决策
|
||||
|
||||
@@ -23,14 +23,14 @@ Status: implemented
|
||||
|
||||
```text
|
||||
tool dsh-tool-fs model-facing schemas + read windowing + text rendering; the EXECUTOR (reads/writes/edits via ctx.fs, dispatches the fs/* events)
|
||||
policy dsh-fs-policy observed-state + read-before-edit + write/edit freshness, contributed through the fs/* event gate (no service)
|
||||
policy dsh-fs-observation-policy observed-state + read-before-edit + write/edit freshness, contributed through the fs/* event gate (no service)
|
||||
provider contract dsh-fs ctx.fs: text IO + atomic mutation primitives (optional version guard)
|
||||
provider dsh-fs-local local implementation of ctx.fs
|
||||
```
|
||||
|
||||
`dsh-tool-fs` 保持相同的面向模型的 `read`/`write`/`edit` schema。它是执行器:注入 `fs`(不是策略服务)并直接访问 `ctx.fs`,拥有读取窗口化逻辑,并分发 `fs/*` 事件以便 `dsh-fs-policy` 进行门控和记录。
|
||||
`dsh-tool-fs` 保持相同的面向模型的 `read`/`write`/`edit` schema。它是执行器:注入 `fs`(不是策略服务)并直接访问 `ctx.fs`,拥有读取窗口化逻辑,并分发 `fs/*` 事件以便 `dsh-fs-observation-policy` 进行门控和记录。
|
||||
|
||||
本 Agent Note 决定了四层拆分、提供方约定和新鲜度策略。随后,[事件门禁 Agent Note](../architecture/2026-06-26-file-context-as-event-gate.md) 细化了工具↔策略耦合:`dsh-fs-policy` 是通过 `fs/*` 事件参与的门禁插件,而非 `ctx.fileContext` 方法服务,因此工具不会在方法层与其耦合;读取窗口和 fs I/O 位于 `dsh-tool-fs`。本文描述已经落地的事件门禁形状;提供方的版本守卫可选(省略即无条件裸提供方)。
|
||||
本 Agent Note 决定了四层拆分、提供方约定和新鲜度策略。随后,[事件门禁 Agent Note](../architecture/2026-06-26-file-context-as-event-gate.md) 细化了工具↔策略耦合:`dsh-fs-observation-policy` 是通过 `fs/*` 事件参与的门禁插件,而非 `ctx.fileContext` 方法服务,因此工具不会在方法层与其耦合;读取窗口和 fs I/O 位于 `dsh-tool-fs`。本文描述已经落地的事件门禁形状;提供方的版本守卫可选(省略即无条件裸提供方)。
|
||||
|
||||
## 提供方约定
|
||||
|
||||
@@ -69,9 +69,9 @@ type FsWriteIntent =
|
||||
|
||||
## 策略约定
|
||||
|
||||
`@deepseek-ai/dsh-fs-policy` 是插件,而非服务:它不注册任何 `ctx.*` 键,也不注入任何内容。它拥有不应位于 `FileSystem` 提供方基类上的写入/编辑新鲜度策略和 observed state(否则沙箱/远程后端会继承不该由其承载的面向模型观察策略)。它通过执行器分派的 `fs/*` 事件门禁贡献该策略。
|
||||
`@deepseek-ai/dsh-fs-observation-policy` 是插件,而非服务:它不注册任何 `ctx.*` 键,也不注入任何内容。它拥有不应位于 `FileSystem` 提供方基类上的写入/编辑新鲜度策略和 observed state(否则沙箱/远程后端会继承不该由其承载的面向模型观察策略)。它通过执行器分派的 `fs/*` 事件门禁贡献该策略。
|
||||
|
||||
观测状态以 `WeakMap<owner, Map<targetKey, FsVersion>>` 的形式存放于此。当且仅当 owner 读取、写入或编辑过该目标时,条目才存在(每次成功都会发出 `fs/observed`),因此条目的存在*本身就是*先前观测的记录——没有单独的 `hasRead` 标志。owner 从不透明的事件 actor(`{ agent?: { session? } }`)结构化派生,该形状定义在 `dsh-fs-policy` 中而非 `dsh-fs` 中。
|
||||
观测状态以 `WeakMap<owner, Map<targetKey, FsVersion>>` 的形式存放于此。当且仅当 owner 读取、写入或编辑过该目标时,条目才存在(每次成功都会发出 `fs/observed`),因此条目的存在*本身就是*先前观测的记录——没有单独的 `hasRead` 标志。owner 从不透明的事件 actor(`{ agent?: { session? } }`)结构化派生,该形状定义在 `dsh-fs-observation-policy` 中而非 `dsh-fs` 中。
|
||||
|
||||
该插件决定三个 `fs/*` 事件:
|
||||
|
||||
@@ -85,9 +85,9 @@ type FsWriteIntent =
|
||||
|
||||
`dsh-tool-fs` 保持相同的 schema 和提示词表面。`read` 仍然暴露 `file_path`、`offset` 和 `limit`;`write` 和 `edit` 不变。它是执行器:验证模型参数,通过 `ctx.fs` 直接读取/写入/编辑,拥有行窗口化和结果渲染(`N: text`、页脚、`<path>/<content>` 封装),并分发 `fs/*` 事件。
|
||||
|
||||
每个变更操作先分发其 intent waterfall(瀑布式事件),带有 `undefined` 裸提供方默认值,然后调用 `ctx.fs`,再发出 `fs/observed`。例如 `write` 执行 `ctx.waterfall('fs/write-intent', target, exec, () => undefined)` → `ctx.fs.writeText(target, content, intent)` → `ctx.emit('fs/observed', …)`。`read` 先 stat 一次,然后读取/流式读取,构建窗口,最后发出 `fs/observed`。将 `exec` 作为 actor 传递,让 `dsh-fs-policy` 无需工具深入策略即可派生 owner。
|
||||
每个变更操作先分发其 intent waterfall(瀑布式事件),带有 `undefined` 裸提供方默认值,然后调用 `ctx.fs`,再发出 `fs/observed`。例如 `write` 执行 `ctx.waterfall('fs/write-intent', target, exec, () => undefined)` → `ctx.fs.writeText(target, content, intent)` → `ctx.emit('fs/observed', …)`。`read` 先 stat 一次,然后读取/流式读取,构建窗口,最后发出 `fs/observed`。将 `exec` 作为 actor 传递,让 `dsh-fs-observation-policy` 无需工具深入策略即可派生 owner。
|
||||
|
||||
由于策略通过带有 `undefined` 默认值的事件贡献,`dsh-tool-fs` 不与 `dsh-fs-policy` 产生方法耦合:在插件缺席时,每个 intent waterfall 都落到 `undefined`(无条件裸提供方写入/编辑),`fs/observed` 没有监听器。加载插件后即可叠加读后写/编辑策略。
|
||||
由于策略通过带有 `undefined` 默认值的事件贡献,`dsh-tool-fs` 不与 `dsh-fs-observation-policy` 产生方法耦合:在插件缺席时,每个 intent waterfall 都落到 `undefined`(无条件裸提供方写入/编辑),`fs/observed` 没有监听器。加载插件后即可叠加读后写/编辑策略。
|
||||
|
||||
## 并发边界
|
||||
|
||||
@@ -101,7 +101,7 @@ type FsWriteIntent =
|
||||
|
||||
本 Agent Note 推翻[文件系统能力 seam](../architecture/2026-06-17-filesystem-capability-seam.md)中的两项决策,并收窄第三项:
|
||||
|
||||
- 读后写/编辑策略从 `ctx.fs` 移出,进入 `dsh-fs-policy` 插件(通过 `fs/*` 事件门控)。
|
||||
- 读后写/编辑策略从 `ctx.fs` 移出,进入 `dsh-fs-observation-policy` 插件(通过 `fs/*` 事件门控)。
|
||||
- 文本读取不再返回后端编号的行记录或 `full`/`partial` 视图;授权基于版本新鲜度,因此窗口化读取在文件未变时即可授权编辑。
|
||||
- 字面编辑不再位于旧的 `applyEdit` API 之后(该 API 混合了后端变更与 seam 拥有的观测策略)。它作为 `editText` 保留为提供方原语,因为版本守卫 + 字面匹配 + 原子重写必须留在提供方的变更临界区内。
|
||||
|
||||
@@ -109,7 +109,7 @@ type FsWriteIntent =
|
||||
|
||||
## 验证
|
||||
|
||||
`dsh-fs` 精确暴露 `resolve`/`stat`/`readText`/`streamText`/`writeText`/`editText`(`stat` 返回 `FsInfo | undefined`,`writeText` 接受 `FsWriteIntent`),已删除的类型/原语不再存在;`dsh-fs-local` 不包含行、视图或 `formatReadBody` 逻辑;面向模型的 schema 保持逐字节不变。测试固定了以下行为:窗口化读取授权对未变文件的后续编辑;基于陈旧读取的编辑在尝试字面匹配之前报告 `FS_STALE_VERSION`;版本 CAS 行为得以保留;观测约定成立(`read` 工具的读取记录观测状态;直接 `ctx.fs` 读取不记录);`dsh-fs-policy` 具有 HMR(热模块替换)/dispose(资源释放)测试覆盖。
|
||||
`dsh-fs` 精确暴露 `resolve`/`stat`/`readText`/`streamText`/`writeText`/`editText`(`stat` 返回 `FsInfo | undefined`,`writeText` 接受 `FsWriteIntent`),已删除的类型/原语不再存在;`dsh-fs-local` 不包含行、视图或 `formatReadBody` 逻辑;面向模型的 schema 保持逐字节不变。测试固定了以下行为:窗口化读取授权对未变文件的后续编辑;基于陈旧读取的编辑在尝试字面匹配之前报告 `FS_STALE_VERSION`;版本 CAS 行为得以保留;观测约定成立(`read` 工具的读取记录观测状态;直接 `ctx.fs` 读取不记录);`dsh-fs-observation-policy` 具有 HMR(热模块替换)/dispose(资源释放)测试覆盖。
|
||||
|
||||
## 后续扩展
|
||||
|
||||
|
||||
@@ -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 .agents/notes/implemented/simplification/2026-07-04-tighten-hook-protocol-contract.md
|
||||
2026-07-04-tighten-hook-protocol-contract.md: eecec255d930d2896fc1ea3dee85bb2efa582a19
|
||||
2026-07-04-tighten-hook-protocol-contract.zh.md: 6e004db844f4e3d60d0eace8537ad23a8f22e34a
|
||||
2026-07-04-tighten-hook-protocol-contract.md: 70cf7a15fe0ec0c43c0ef768667ef4741aee27d2
|
||||
2026-07-04-tighten-hook-protocol-contract.zh.md: 6518ce429105e8338c1171fb136962a15d9b291d
|
||||
|
||||
@@ -10,8 +10,8 @@ Four pieces of the `dsh-hook-protocol`/bridge contract missed the discipline the
|
||||
|
||||
1. **`HookDialect`'s `'native'` variant** (`packages/hooks/hook-protocol/src/types.ts`) had zero producers — the bridges stamp `'claude'` and `'codex'`; the only `'native'` constructor anywhere was the lib's own unit test. The field's own JSDoc defines `dialect` as "the bridge that ran it", and native is not a bridge: the [interception extension-points Agent Note](../feature/2026-06-30-interception-extension-points.md) records that native hooks are not a package and that "a native plugin can already use the typed Decisions" without the durable hook log, and the flagship native-plugin worked example asserts exactly that (no `hook/*` events at all).
|
||||
2. **`HookOutput.suppressOutput`** (same file) was parsed by the codec and discarded on every path: no bridge branch, no merge fold, no warn, no deferred-list row — uniquely among its parsed-but-unhonored siblings, each of which carries a stated deferral (`updatedInput` → a logged warn plus the [pre-tool-input-rewrite proposal](../../proposed/feature/2026-06-30-pre-tool-input-rewrite.md); `systemMessage` → a logged warn plus a README deferred row; `continue`/`stopReason` → a `TODO(hook-continue-false)` anchor plus the `'stop'` decision record). Structurally there is nothing to suppress: hook stdout never enters any transcript (context flows only via `additionalContext`; the log records only `decision`/`stderrSummary`), so a hook author setting `suppressOutput: true` got silent nothing with no warn.
|
||||
3. **`defaultTimeoutMs` was double-defaulted in both bridge configs with a floating literal** — a schema `.default(600_000)` AND a `?? 600_000` fallback (`packages/hooks/hooks-claude/src/index.ts`, `packages/hooks/hooks-codex/src/index.ts`), two homes per bridge for one protocol-level constant, so the bridges could silently drift apart on the shared default. *The knob stays as explicit bridge-owned config per the no-hardcoded-tunables rule (with `stderrSummaryMaxChars` beside it); the fix is the literal's home.*
|
||||
4. **The `hook/result` semantics lived in the bridges, twice, not in the lib that owns the event.** `summarize()` — the stderr truncation rule — was byte-identical in `packages/hooks/hooks-claude/src/index.ts` and `packages/hooks/hooks-codex/src/index.ts`, and so was the decision-string rule `output.decision ?? (output.continue === false ? 'stop' : 'pass')`; yet `dsh-hook-protocol` declared `hook/result`, documented `stderrSummary` as "truncated" without owning the truncation, and documented the decision values without owning the mapping. If one bridge drifted (a different cap, a different fallback), the shared durable event's semantics would fork silently.
|
||||
3. **`defaultTimeoutMs` was double-defaulted in both bridge configs with a floating literal** — a schema `.default(600_000)` AND a `?? 600_000` fallback (`packages/hooks/hooks-claude-code/src/index.ts`, `packages/hooks/hooks-codex/src/index.ts`), two homes per bridge for one protocol-level constant, so the bridges could silently drift apart on the shared default. *The knob stays as explicit bridge-owned config per the no-hardcoded-tunables rule (with `stderrSummaryMaxChars` beside it); the fix is the literal's home.*
|
||||
4. **The `hook/result` semantics lived in the bridges, twice, not in the lib that owns the event.** `summarize()` — the stderr truncation rule — was byte-identical in `packages/hooks/hooks-claude-code/src/index.ts` and `packages/hooks/hooks-codex/src/index.ts`, and so was the decision-string rule `output.decision ?? (output.continue === false ? 'stop' : 'pass')`; yet `dsh-hook-protocol` declared `hook/result`, documented `stderrSummary` as "truncated" without owning the truncation, and documented the decision values without owning the mapping. If one bridge drifted (a different cap, a different fallback), the shared durable event's semantics would fork silently.
|
||||
|
||||
## Decision
|
||||
|
||||
|
||||
@@ -10,8 +10,8 @@ Status: implemented
|
||||
|
||||
1. **`HookDialect` 的 `'native'` 变体**(`packages/hooks/hook-protocol/src/types.ts`)没有生产者——bridge 会标记 `'claude'` 和 `'codex'`;所有位置中唯一构造 `'native'` 的是该库自己的单元测试。字段自身的 JSDoc 将 `dialect` 定义为「运行它的 bridge」,而 native 不是 bridge:[拦截扩展点 Agent Note](../feature/2026-06-30-interception-extension-points.md) 记载 native 钩子不是一个包,并且「native 插件无需持久钩子日志即可使用类型化 Decision」;旗舰 native 插件实践示例恰好断言了这一点(完全没有 `hook/*` 事件)。
|
||||
2. **`HookOutput.suppressOutput`**(同一文件)被 codec 解析后在所有路径上均被丢弃:没有 bridge 分支处理它、没有合并 fold、没有 warn、没有 deferred-list 行——在所有「被解析但未兑现」的同类字段中它是唯一没有明确延期声明的(`updatedInput` → 一条 warn 日志加 [pre-tool-input-rewrite 提案](../../proposed/feature/2026-06-30-pre-tool-input-rewrite.md);`systemMessage` → 一条 warn 日志加 README deferred 行;`continue`/`stopReason` → 一个 `TODO(hook-continue-false)` 锚点加 `'stop'` decision 记录)。从结构上看根本无物可抑制:钩子 stdout 从不进入任何 transcript(文本记录);上下文仅通过 `additionalContext` 流入,日志也只记录 `decision`/`stderrSummary`。因此,钩子作者设置 `suppressOutput: true` 得到的是无声的空操作,且无任何警告。
|
||||
3. **`defaultTimeoutMs` 在两个 bridge 配置中都以游离的字面量重复设置了默认值**——schema 的 `.default(600_000)` 加上一个 `?? 600_000` 回退(`packages/hooks/hooks-claude/src/index.ts`、`packages/hooks/hooks-codex/src/index.ts`),一个协议级常量在每个 bridge 中有两个归属地,两个 bridge 可能在共享默认值上悄然分歧。*按 no-hardcoded-tunables 规则,该旋钮保留为 bridge 拥有的显式配置(旁边有 `stderrSummaryMaxChars`);要修的是字面量的归属地。*
|
||||
4. **`hook/result` 的语义存在于两个 bridge 中(各一份),而非拥有该事件的 lib。** `summarize()`——stderr 截断规则——在 `packages/hooks/hooks-claude/src/index.ts` 与 `packages/hooks/hooks-codex/src/index.ts` 中逐字节相同;decision 字符串规则 `output.decision ?? (output.continue === false ? 'stop' : 'pass')` 同样如此。然而 `dsh-hook-protocol` 声明了 `hook/result`、在文档中将 `stderrSummary` 描述为「已截断」却不拥有截断逻辑,记录了 decision 值却不拥有映射逻辑。如果某个 bridge 漂移(不同的上限、不同的回退),共享持久化事件的语义就会悄然分叉。
|
||||
3. **`defaultTimeoutMs` 在两个 bridge 配置中都以游离的字面量重复设置了默认值**——schema 的 `.default(600_000)` 加上一个 `?? 600_000` 回退(`packages/hooks/hooks-claude-code/src/index.ts`、`packages/hooks/hooks-codex/src/index.ts`),一个协议级常量在每个 bridge 中有两个归属地,两个 bridge 可能在共享默认值上悄然分歧。*按 no-hardcoded-tunables 规则,该旋钮保留为 bridge 拥有的显式配置(旁边有 `stderrSummaryMaxChars`);要修的是字面量的归属地。*
|
||||
4. **`hook/result` 的语义存在于两个 bridge 中(各一份),而非拥有该事件的 lib。** `summarize()`——stderr 截断规则——在 `packages/hooks/hooks-claude-code/src/index.ts` 与 `packages/hooks/hooks-codex/src/index.ts` 中逐字节相同;decision 字符串规则 `output.decision ?? (output.continue === false ? 'stop' : 'pass')` 同样如此。然而 `dsh-hook-protocol` 声明了 `hook/result`、在文档中将 `stderrSummary` 描述为「已截断」却不拥有截断逻辑,记录了 decision 值却不拥有映射逻辑。如果某个 bridge 漂移(不同的上限、不同的回退),共享持久化事件的语义就会悄然分叉。
|
||||
|
||||
## 决策
|
||||
|
||||
|
||||
@@ -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 .agents/notes/implemented/simplification/2026-07-20-remove-stdio-and-echo-agents.md
|
||||
2026-07-20-remove-stdio-and-echo-agents.md: fcfd0399bc6cf040287a057c56339690c8d92d8a
|
||||
2026-07-20-remove-stdio-and-echo-agents.zh.md: 6f31d11885aad43b5f2ea236efb95c950290033a
|
||||
2026-07-20-remove-stdio-and-echo-agents.md: 8761c360e492d6d315e738ed93441929584d1e20
|
||||
2026-07-20-remove-stdio-and-echo-agents.zh.md: 7c25d6686f7da851ce4244090bfee5ac6eb65ff0
|
||||
|
||||
@@ -20,7 +20,7 @@ The remaining application roles are explicit:
|
||||
|
||||
- `@deepseek-ai/dsh-tui` owns terminal-interactive execution. It rejects non-TTY streams before Loader boot; `apps/cli/config/base.cordis.yml` plus the `tui.cordis.yml` overlay own the complete coding composition, with PTY plus terminal-snapshot coverage in `apps/cli/tests/`.
|
||||
- [`dsh --profile headless`](../../../../apps/cli/README.md) owns non-interactive execution. Its `headless` profile is the product composition; `examples/headless-agent` owns replay snapshots, generic real-agent suites, and an unexported keyless Loader driver.
|
||||
- [`@deepseek-ai/dsh-acp-demo`](../../../../packages/examples/acp-demo/README.md) and `@deepseek-ai/dsh-jsonrpc` own their framed protocol integrations.
|
||||
- [`@deepseek-ai/dsh-acp-demo`](../../../../packages/examples/acp-demo/README.md) and `@deepseek-ai/dsh-sdk-jsonrpc-server` own their framed protocol integrations.
|
||||
|
||||
The SDK project model that carried the `stdio` run-interface option is deleted by the [SDK project toolchain removal](2026-08-11-remove-sdk-project-toolchain.md). Repository-facing demo documentation requires a DeepSeek API key and leads with a current runnable product.
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ DeepSeek Harness 在 TUI 和 Headless coding agent 之外,还提供了两个
|
||||
|
||||
- `@deepseek-ai/dsh-tui` 负责终端交互式执行。它会在 Loader 启动前拒绝非 TTY 流;`apps/cli/config/base.cordis.yml` 与 `tui.cordis.yml` overlay 拥有完整 coding 组装,PTY 与终端快照覆盖则位于 `apps/cli/tests/`。
|
||||
- [`dsh --profile headless`](../../../../apps/cli/README.md) 负责非交互式执行。其 `headless` profile 是产品组装;`examples/headless-agent` 负责回放快照、通用真实 agent 测试套件和未导出的无密钥 Loader driver。
|
||||
- [`@deepseek-ai/dsh-acp-demo`](../../../../packages/examples/acp-demo/README.md) 和 `@deepseek-ai/dsh-jsonrpc` 负责各自的分帧协议集成。
|
||||
- [`@deepseek-ai/dsh-acp-demo`](../../../../packages/examples/acp-demo/README.md) 和 `@deepseek-ai/dsh-sdk-jsonrpc-server` 负责各自的分帧协议集成。
|
||||
|
||||
承载 `stdio` 运行接口选项的 SDK 项目模型已由 [SDK 项目工具链移除决策](2026-08-11-remove-sdk-project-toolchain.md)删除。仓库中的演示文档要求 DeepSeek API key,并优先引导到当前可运行的产品。
|
||||
|
||||
|
||||
@@ -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 .agents/notes/implemented/simplification/2026-07-20-unwrap-injected-content-envelopes.md
|
||||
2026-07-20-unwrap-injected-content-envelopes.md: aff458d76027bf5518497328ee1bd852869793b9
|
||||
2026-07-20-unwrap-injected-content-envelopes.zh.md: 727ce5e490b6115ad0b3ce383059aabcffd4c7b0
|
||||
2026-07-20-unwrap-injected-content-envelopes.md: c1a7376c0c9ba737daf4e35af5808afb74aa3026
|
||||
2026-07-20-unwrap-injected-content-envelopes.zh.md: 3b1644636b9938836debe224a12bbdefb7f743ff
|
||||
|
||||
@@ -11,13 +11,13 @@ Two families of injected session content rendered into the model transcript wrap
|
||||
Two problems:
|
||||
|
||||
- **No model is trained on these tags.** `<steering>` and `<context>` are arbitrary markup no model was taught to read, so the framing adds tokens without a reliable effect and can actively mislead — recorded transcripts show a model treating a `<steering>` instruction as third-party metadata and refusing it while answering only the original prompt.
|
||||
- **The session surface is the wrong layer for framing.** The surface projects the durable log into the model transcript; deciding how content is worded is not its job. A caller that wants a particular frame formats its own content before injecting it — which the one heavy producer (`workspace-context`) already does, owning its complete `<system-reminder>` frame and opting out of the `<context>` wrapper with `envelope: 'raw'`. The remaining tag machinery (`ContextEnvelope`, an `envelope` field threaded through `InjectOptions`, `HookContext`, the `context/message` event, and the loop) served a distinction that belongs to the caller.
|
||||
- **The session surface is the wrong layer for framing.** The surface projects the durable log into the model transcript; deciding how content is worded is not its job. A caller that wants a particular frame formats its own content before injecting it — which the one heavy producer (`agent-instructions`) already does, owning its complete `<system-reminder>` frame and opting out of the `<context>` wrapper with `envelope: 'raw'`. The remaining tag machinery (`ContextEnvelope`, an `envelope` field threaded through `InjectOptions`, `HookContext`, the `context/message` event, and the loop) served a distinction that belongs to the caller.
|
||||
|
||||
## Decision
|
||||
|
||||
Injected session content projects verbatim; the caller owns any framing. `deriveEventMessage` renders `user/message` content blocks to the model unchanged; `source` stays in the durable event log but does not render.
|
||||
|
||||
The `ContextEnvelope` type and every `envelope` field are removed — `context/message` in `SessionEventMap`, `InjectOptions`, `HookContext`, and the `inject()`/`additionalContexts` plumbing in `dsh-agent-loop`. `workspace-context` no longer requests `'raw'`; its self-framed content renders as before. The `renderTagged`/`renderContextEnvelope` helpers are deleted. `context/message.meta` still carries durable, model-hidden JSON state.
|
||||
The `ContextEnvelope` type and every `envelope` field are removed — `context/message` in `SessionEventMap`, `InjectOptions`, `HookContext`, and the `inject()`/`additionalContexts` plumbing in `dsh-agent-loop`. `agent-instructions` no longer requests `'raw'`; its self-framed content renders as before. The `renderTagged`/`renderContextEnvelope` helpers are deleted. `context/message.meta` still carries durable, model-hidden JSON state.
|
||||
|
||||
The `source` attribution the envelopes carried is not lost — it remains on the durable events; it simply no longer renders into the transcript.
|
||||
|
||||
@@ -36,6 +36,6 @@ The `source` attribution the envelopes carried is not lost — it remains on the
|
||||
|
||||
## Deferred
|
||||
|
||||
`workspace-context` already frames its own content: it emits a complete `<system-reminder>…</system-reminder>` block as the message content instead of leaning on a surface-level wrapper. That caller-owned pattern is the one to keep — the surface passes content through verbatim, and any framing lives in the producer's own content.
|
||||
`agent-instructions` already frames its own content: it emits a complete `<system-reminder>…</system-reminder>` block as the message content instead of leaning on a surface-level wrapper. That caller-owned pattern is the one to keep — the surface passes content through verbatim, and any framing lives in the producer's own content.
|
||||
|
||||
Two framing paths existed — caller-baked framing (`workspace-context`'s `<system-reminder>`) and surface-level wrapping (`<context>`/`<steering>` added by `deriveEventMessage`). This change removes the second, leaving only caller-owned framing. If labeled framing is wanted again, unify it through the event's `meta` map — the producer-attached, model-hidden metadata field — consumed by a dedicated renderer or adapter, rather than re-hardcoding a tag in `deriveEventMessage`. A producer declares the frame it wants in `meta`; one renderer applies it; the session-surface projection stays a verbatim pass-through.
|
||||
Two framing paths existed — caller-baked framing (`agent-instructions`'s `<system-reminder>`) and surface-level wrapping (`<context>`/`<steering>` added by `deriveEventMessage`). This change removes the second, leaving only caller-owned framing. If labeled framing is wanted again, unify it through the event's `meta` map — the producer-attached, model-hidden metadata field — consumed by a dedicated renderer or adapter, rather than re-hardcoding a tag in `deriveEventMessage`. A producer declares the frame it wants in `meta`; one renderer applies it; the session-surface projection stays a verbatim pass-through.
|
||||
|
||||
@@ -11,13 +11,13 @@ Status: implemented
|
||||
两个问题:
|
||||
|
||||
- **没有模型在这些标签上训练过。** `<steering>` 和 `<context>` 是任何模型都未被教会去读的任意标记,因此这层框架只是徒增 token 而没有可靠效果,还可能起反作用——已录制的 transcript 显示,模型会把 `<steering>` 指令当成第三方元数据而拒绝服从,只回答原始提示词。
|
||||
- **会话表层是承载框架的错误层次。** 表层的职责是把持久日志投影为模型 transcript;决定内容如何措辞并不是它的事。想要特定框架的调用方可以在注入前自行格式化内容——唯一的重度生产方(`workspace-context`)本就这样做,它自带完整的 `<system-reminder>` 框架,并用 `envelope: 'raw'` 退出 `<context>` 封套。剩下的标签机制(`ContextEnvelope` 类型,以及贯穿 `InjectOptions`、`HookContext`、`context/message` 事件和 agent loop(智能体循环)的 `envelope` 字段)所服务的区分,本应归属调用方。
|
||||
- **会话表层是承载框架的错误层次。** 表层的职责是把持久日志投影为模型 transcript;决定内容如何措辞并不是它的事。想要特定框架的调用方可以在注入前自行格式化内容——唯一的重度生产方(`agent-instructions`)本就这样做,它自带完整的 `<system-reminder>` 框架,并用 `envelope: 'raw'` 退出 `<context>` 封套。剩下的标签机制(`ContextEnvelope` 类型,以及贯穿 `InjectOptions`、`HookContext`、`context/message` 事件和 agent loop(智能体循环)的 `envelope` 字段)所服务的区分,本应归属调用方。
|
||||
|
||||
## 决策
|
||||
|
||||
注入的会话内容逐字投影,框架由调用方自行负责。`deriveEventMessage` 把 `user/message` 的内容块原样送达模型;`source` 保留在持久事件日志中,但不渲染。
|
||||
|
||||
`ContextEnvelope` 类型和所有 `envelope` 字段都被移除——包括 `SessionEventMap` 中的 `context/message`、`InjectOptions`、`HookContext`,以及 `dsh-agent-loop` 中 `inject()`/`additionalContexts` 的相关管线。`workspace-context` 不再请求 `'raw'`;它自带框架的内容渲染方式不变。`renderTagged`/`renderContextEnvelope` 辅助函数被删除。`context/message.meta` 仍携带持久的、对模型隐藏的 JSON 状态。
|
||||
`ContextEnvelope` 类型和所有 `envelope` 字段都被移除——包括 `SessionEventMap` 中的 `context/message`、`InjectOptions`、`HookContext`,以及 `dsh-agent-loop` 中 `inject()`/`additionalContexts` 的相关管线。`agent-instructions` 不再请求 `'raw'`;它自带框架的内容渲染方式不变。`renderTagged`/`renderContextEnvelope` 辅助函数被删除。`context/message.meta` 仍携带持久的、对模型隐藏的 JSON 状态。
|
||||
|
||||
封套曾携带的 `source` 来源信息并未丢失——它仍保留在持久事件上;只是不再渲染进 transcript。
|
||||
|
||||
@@ -36,6 +36,6 @@ Status: implemented
|
||||
|
||||
## 推迟事项
|
||||
|
||||
`workspace-context` 已经自行为内容加框架:它把一个完整的 `<system-reminder>…</system-reminder>` 块作为消息内容发出,而不依赖表层封套。这种调用方自有的模式才是应保留的——表层逐字透传内容,任何框架都住在生产方自己的内容里。
|
||||
`agent-instructions` 已经自行为内容加框架:它把一个完整的 `<system-reminder>…</system-reminder>` 块作为消息内容发出,而不依赖表层封套。这种调用方自有的模式才是应保留的——表层逐字透传内容,任何框架都住在生产方自己的内容里。
|
||||
|
||||
曾经存在两条框架路径——调用方自行加框架(`workspace-context` 的 `<system-reminder>`),以及表层封套(`deriveEventMessage` 加上的 `<context>`/`<steering>`)。本次变更移除了后者,只留下调用方自有的框架。如果未来又需要带标签的框架,应由事件的 `meta` map(生产方附加、对模型隐藏的元数据字段)来统一它,交给专门的渲染器或适配器消费,而不是在 `deriveEventMessage` 中重新硬编码标签。生产方在 `meta` 中声明所需的框架,由一个渲染器统一施加;会话表层的投影始终保持逐字透传。
|
||||
曾经存在两条框架路径——调用方自行加框架(`agent-instructions` 的 `<system-reminder>`),以及表层封套(`deriveEventMessage` 加上的 `<context>`/`<steering>`)。本次变更移除了后者,只留下调用方自有的框架。如果未来又需要带标签的框架,应由事件的 `meta` map(生产方附加、对模型隐藏的元数据字段)来统一它,交给专门的渲染器或适配器消费,而不是在 `deriveEventMessage` 中重新硬编码标签。生产方在 `meta` 中声明所需的框架,由一个渲染器统一施加;会话表层的投影始终保持逐字透传。
|
||||
|
||||
@@ -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 .agents/notes/implemented/simplification/2026-07-22-plan-specific-collaboration-state.md
|
||||
2026-07-22-plan-specific-collaboration-state.md: a4984463d759c229def7497905b09c99d4d47073
|
||||
2026-07-22-plan-specific-collaboration-state.md: a85e7ced9f2792dfd447cb22e9fea08ce67256df
|
||||
2026-07-22-plan-specific-collaboration-state.zh.md: 2b1be7c442c7ca3010112fc263a70c40326134c8
|
||||
|
||||
@@ -30,9 +30,9 @@ The active state contributes the deployment's section at prompt order 50. Inacti
|
||||
|
||||
### Reviewed exit
|
||||
|
||||
`exit_plan_mode` requires a calling agent in active plan mode and a non-empty markdown plan beginning with a heading. The user-interaction question carries that exact plan as detail and offers `Approve` or `Keep planning` plus free-text feedback. Only one `Approve` selection with no custom text consents; every other answer stays in plan mode and returns corrective feedback to the model. An approved exit becomes a silent pending selection, leaving plan guidance active for the rest of the current tool batch and removing it before the next request.
|
||||
`exit_plan_mode` requires a calling agent in active plan mode and a non-empty markdown plan beginning with a heading. The user-questions question carries that exact plan as detail and offers `Approve` or `Keep planning` plus free-text feedback. Only one `Approve` selection with no custom text consents; every other answer stays in plan mode and returns corrective feedback to the model. An approved exit becomes a silent pending selection, leaving plan guidance active for the rest of the current tool batch and removing it before the next request.
|
||||
|
||||
The tool renders the submitted plan as a generic card titled by its first heading. An absent or failed user-interaction provider, a failed review, or plugin disposal while review is pending fails closed and leaves manual `/plan off` as the human escape path.
|
||||
The tool renders the submitted plan as a generic card titled by its first heading. An absent or failed user-questions provider, a failed review, or plugin disposal while review is pending fails closed and leaves manual `/plan off` as the human escape path.
|
||||
|
||||
## Deleted API
|
||||
|
||||
@@ -55,7 +55,7 @@ The tool renders the submitted plan as a generic card titled by its first headin
|
||||
|
||||
**Filter tools by a per-plan name allowlist or a global policy stack.** Rejected because mutability is a property of each tool, including future and MCP tools, rather than a list that every plan deployment must maintain. Effects metadata can establish a shared policy only when a concrete consumer exists; until then plan mode is guidance, not a security boundary.
|
||||
|
||||
**Review through the approval seam or prose.** Rejected because a plan review is not a permission decision, needs the exact artifact and corrective free text, and must have a logged tool call as its structured transition. The user-interaction seam supplies that contract.
|
||||
**Review through the approval seam or prose.** Rejected because a plan review is not a permission decision, needs the exact artifact and corrective free text, and must have a logged tool call as its structured transition. The user-questions seam supplies that contract.
|
||||
|
||||
## Verification
|
||||
|
||||
|
||||
@@ -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 .agents/notes/implemented/simplification/2026-07-23-acp-automation-only-protocol.md
|
||||
2026-07-23-acp-automation-only-protocol.md: 56dcaf8b4327a008f26b884264958cac02d6541a
|
||||
2026-07-23-acp-automation-only-protocol.md: e7a6670a44db9d4fd2bba5f013083e35d60fc313
|
||||
2026-07-23-acp-automation-only-protocol.zh.md: cdefe80c3e868a41541c7ebea42e3811c7fb9249
|
||||
|
||||
@@ -20,7 +20,7 @@ The bridge emits only committed `assistant/message` text. Reasoning, raw chunks,
|
||||
|
||||
One-shot `session/request_permission` remains. It is a machine policy channel for bridge-owned agents, not a human approval UI: the answerer accepts only an exact agent object in the bridge's live session map, delegates foreign or call-less requests, and maps failed RPCs to the fail-closed unavailable outcome. The client chooses allow once, reject once, or cancel, and the bridge never turns that response into a durable grant. Asking policy stays in the approval seam and its producers; [`dsh-subagent-acp`](../../../../packages/subagent/subagent-acp/README.md) uses this channel programmatically.
|
||||
|
||||
The app composition contains the agent spine, persistence, checkpoint policy, and ACP transport. It does not mount command, session-query, session-reference, plan-mode, permission-picker, or user-interaction services for ACP.
|
||||
The app composition contains the agent spine, persistence, checkpoint policy, and ACP transport. It does not mount command, session-query, session-reference, plan-mode, permission-picker, or user-questions services for ACP.
|
||||
|
||||
The transport programs interface-level agent, session, and approval services rather than the concrete agent loop. Tool execution stays inside the harness; ACP never delegates shell execution to an editor. stdout carries framed JSON-RPC only, so the app mounts no stdout logger and the bridge does not monkey-patch process output.
|
||||
|
||||
@@ -36,7 +36,7 @@ Protocol and lifecycle tests pin stop-reason and prompt codecs, version negotiat
|
||||
|
||||
**Keep ACP as an editor UI until Web reaches parity.** Rejected because it leaves two interactive contracts to evolve and keeps editor conventions in the automation boundary.
|
||||
|
||||
**Keep the earlier editor bridge behind disciplined service boundaries.** Rejected even though that bridge correctly used interface services, tool-owned render intents, approval and user-interaction answerers, harness-owned execution, and a stdout-pure composition. Its terminal cards were capability-gated, display-only Zed `_meta` projections with a text fallback rather than ACP `terminal/create`, so shell execution never left the harness. The projection derived each display terminal id from the stable per-call id to prevent collisions and recovered exit code or signal from the rendered status markers because the pure result presenter received content blocks rather than a structured exit; marker round-trip tests and an explicit no-capability `console` fallback test pinned both contracts. Those boundaries were coherent but could not make editor cards, session navigation, configuration pickers, and human elicitation belong in an automation protocol.
|
||||
**Keep the earlier editor bridge behind disciplined service boundaries.** Rejected even though that bridge correctly used interface services, tool-owned render intents, approval and user-questions answerers, harness-owned execution, and a stdout-pure composition. Its terminal cards were capability-gated, display-only Zed `_meta` projections with a text fallback rather than ACP `terminal/create`, so shell execution never left the harness. The projection derived each display terminal id from the stable per-call id to prevent collisions and recovered exit code or signal from the rendered status markers because the pure result presenter received content blocks rather than a structured exit; marker round-trip tests and an explicit no-capability `console` fallback test pinned both contracts. Those boundaries were coherent but could not make editor cards, session navigation, configuration pickers, and human elicitation belong in an automation protocol.
|
||||
|
||||
**Replace ACP with a private subagent RPC.** Rejected because ACP already supplies a typed, interoperable process protocol and is used by the out-of-process subagent backend.
|
||||
|
||||
|
||||
@@ -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 .agents/notes/implemented/simplification/2026-07-26-merge-subagent-control-service.md
|
||||
2026-07-26-merge-subagent-control-service.md: 2757ba678863a355e0f9c4bb324035ba666796be
|
||||
2026-07-26-merge-subagent-control-service.zh.md: 0a894342ca5429532dc4f428f339186ac84ef287
|
||||
2026-07-26-merge-subagent-control-service.md: 1d8aadc6a90dc1bea5ea4b743bee6c112bf0c2ca
|
||||
2026-07-26-merge-subagent-control-service.zh.md: 4d894d52221c4fdf5ca50a2f808cab0bcf4bd010
|
||||
|
||||
@@ -8,15 +8,15 @@ The public operation set is refined by [Intent-named subagent continuation opera
|
||||
|
||||
## Problem
|
||||
|
||||
Continuable-child orchestration originally lived in a separate `ctx.subagentControl` service above the raw `ctx.subagents` provider contract. That split kept provider dispatch independent of Tasks and persistence, and gave model and human adapters one orchestration contract. In practice the two services described one capability family, every continuable caller needed both, and the provider-bound delegation tool had to infer policy from `provider.resume` and inspect whether the control service and `send_message` tool happened to be loaded. This made sibling plugin presence decide execution semantics and coupled starting continuable work to an optional follow-up surface.
|
||||
Continuable-child orchestration originally lived in a separate `ctx.subagentControl` service above the raw `ctx.subagents` provider contract. That split kept provider dispatch independent of Jobs and persistence, and gave model and human adapters one orchestration contract. In practice the two services described one capability family, every continuable caller needed both, and the provider-bound delegation tool had to infer policy from `provider.resume` and inspect whether the control service and `send_message` tool happened to be loaded. This made sibling plugin presence decide execution semantics and coupled starting continuable work to an optional follow-up surface.
|
||||
|
||||
## Decision
|
||||
|
||||
`SubagentService` is the only public service. It exposes ordinary `start(name, request)`, Task-backed `startContinuable(spec)`, and intent-named `followup(...)`; provider resume dispatch remains private to its continuation manager. The standalone `@deepseek-ai/dsh-subagent-control` package and `ctx.subagentControl` key are absent; the optional `@deepseek-ai/dsh-tool-subagent-control` package injects `ctx.subagents` directly.
|
||||
`SubagentRuntime` is the only public service. It exposes ordinary `start(name, request)`, Task-backed `startContinuable(spec)`, and intent-named `followup(...)`; provider resume dispatch remains private to its continuation manager. The standalone `@deepseek-ai/dsh-subagent-control` package and `ctx.subagentControl` key are absent; the optional `@deepseek-ai/dsh-tool-subagent-control` package injects `ctx.subagents` directly.
|
||||
|
||||
The merged service and its providers expose one `SubagentError` taxonomy. Stable codes distinguish provider lookup and capability failures from continuation routing, authorization, cancellation, persistence, and delivery failures; the removed service does not retain a separate error class.
|
||||
|
||||
The continuation implementation remains an internal manager rather than expanding the provider registry's core state. `SubagentService` creates it through `ctx.inject(['tasks', 'agents'], ...)`, so the injected Cordis child fiber owns its Task completion listener and teardown effects. Loading the provider registry does not require Tasks or persistence. The manager exists only while Tasks and Agents are available, and each continuation operation resolves session persistence at the point it needs durability. Disposing that fiber cancels and settles active continuations before releasing their associations.
|
||||
The continuation implementation remains an internal manager rather than expanding the provider registry's core state. `SubagentRuntime` creates it through `ctx.inject(['tasks', 'agents'], ...)`, so the injected Cordis child fiber owns its Task completion listener and teardown effects. Loading the provider registry does not require Jobs or persistence. The manager exists only while Jobs and Agents are available, and each continuation operation resolves session persistence at the point it needs durability. Disposing that fiber cancels and settles active continuations before releasing their associations.
|
||||
|
||||
`startContinuable` remains distinct from raw `start` because it has a different ownership and timing contract: it allocates the durable child id, creates the Task, and returns both ids synchronously while startup continues inside the Task. Raw `start` instead awaits provider publication and transfers a holder-owned run. Folding the method onto `start` through flags or return unions would broaden the low-level contract and create more change than keeping the existing explicit entry.
|
||||
|
||||
@@ -34,8 +34,8 @@ Each `@deepseek-ai/dsh-tool-subagent` instance selects `backgroundMode: 'one-sho
|
||||
|
||||
## Consequences
|
||||
|
||||
- The service topology has one public key and one package fewer while raw provider dispatch remains usable without Tasks or persistence.
|
||||
- Continuable mode fails at provider mount when the configured provider lacks `resume`; missing Tasks, Agents, or persistence still fail at the earliest operation that requires them.
|
||||
- The service topology has one public key and one package fewer while raw provider dispatch remains usable without Jobs or persistence.
|
||||
- Continuable mode fails at provider mount when the configured provider lacks `resume`; missing Jobs, Agents, or persistence still fail at the earliest operation that requires them.
|
||||
- Follow-up delivery remains optional. Deployments may start and collect continuable work through Task tools without exposing `send_message`.
|
||||
- The continuation manager is still Task- and persistence-aware inside the `dsh-subagent` package, so the package declares optional peer dependencies on those services even though ordinary `start` callers do not need them.
|
||||
- Existing continuation races, authorization, durability, cancellation, and settle-then-dispose semantics are unchanged and remain pinned by the migrated `subagent` tests.
|
||||
|
||||
@@ -12,11 +12,11 @@ Status: implemented
|
||||
|
||||
## 决策
|
||||
|
||||
`SubagentService` 是唯一的公开服务。它公开普通的 `start(name, request)`、由 Task 支撑的 `startContinuable(spec)`,以及按意图命名的 `followup(...)`;提供方的 resume 分发仍封装在其继续执行管理器内部。独立的 `@deepseek-ai/dsh-subagent-control` 包和 `ctx.subagentControl` 键均不存在;可选的 `@deepseek-ai/dsh-tool-subagent-control` 包则直接注入 `ctx.subagents`。
|
||||
`SubagentRuntime` 是唯一的公开服务。它公开普通的 `start(name, request)`、由 Task 支撑的 `startContinuable(spec)`,以及按意图命名的 `followup(...)`;提供方的 resume 分发仍封装在其继续执行管理器内部。独立的 `@deepseek-ai/dsh-subagent-control` 包和 `ctx.subagentControl` 键均不存在;可选的 `@deepseek-ai/dsh-tool-subagent-control` 包则直接注入 `ctx.subagents`。
|
||||
|
||||
合并后的服务及其提供方公开一套 `SubagentError` 分类体系。稳定错误码把提供方查找失败和能力相关失败,与继续执行路由、鉴权、取消、持久化和送达失败区分开来;已移除的服务不保留单独的错误类。
|
||||
|
||||
继续执行的实现仍是内部管理器,不会扩展提供方注册表的核心状态。`SubagentService` 通过 `ctx.inject(['tasks', 'agents'], ...)` 创建该管理器,因此注入的 Cordis child fiber 拥有自身的 Task 完成监听器和拆卸 effect。加载提供方注册表不要求 Task 或持久化。只有 Task 和 Agent 可用时,该管理器才会存在;每项继续执行操作都在需要持久性时解析会话持久化服务。dispose(资源释放)该 fiber 会先取消并结算活跃的继续执行,再释放其关联。
|
||||
继续执行的实现仍是内部管理器,不会扩展提供方注册表的核心状态。`SubagentRuntime` 通过 `ctx.inject(['tasks', 'agents'], ...)` 创建该管理器,因此注入的 Cordis child fiber 拥有自身的 Task 完成监听器和拆卸 effect。加载提供方注册表不要求 Task 或持久化。只有 Task 和 Agent 可用时,该管理器才会存在;每项继续执行操作都在需要持久性时解析会话持久化服务。dispose(资源释放)该 fiber 会先取消并结算活跃的继续执行,再释放其关联。
|
||||
|
||||
`startContinuable` 与底层 `start` 保持分离,因为二者的所有权与时序约定不同:前者分配持久化 child id、创建 Task,并同步返回两个 id,而启动过程继续在 Task 内运行;底层 `start` 则等待提供方发布,并移交一个由持有方负责的 run。若通过标志或返回值联合类型将该方法并入 `start`,会扩大底层约定,改动反而多于保留现有的显式入口。
|
||||
|
||||
|
||||
@@ -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 .agents/notes/implemented/simplification/2026-07-27-intent-named-subagent-continuation-operations.md
|
||||
2026-07-27-intent-named-subagent-continuation-operations.md: e74d62b7582e92f8e5ce68327a677259c8453d24
|
||||
2026-07-27-intent-named-subagent-continuation-operations.zh.md: ae7b370441d8e0ee045f4d0fcf851d28d055b295
|
||||
2026-07-27-intent-named-subagent-continuation-operations.md: 72fb042978cdb8faa238d7483c17eb7c7fce80bd
|
||||
2026-07-27-intent-named-subagent-continuation-operations.zh.md: 09af0a3ba343a441306d9c1eec3f00c582d510e6
|
||||
|
||||
@@ -14,9 +14,9 @@ The durability boundary also exposed both `SessionStore.flush()` and `flushRequi
|
||||
|
||||
## Decision
|
||||
|
||||
`SubagentService` separates four execution intents: `start(name, request)` returns an ordinary holder-owned one-shot run; `startContinuable(spec)` establishes a durable child and returns its id plus the accepted initial `MessageId`; `followup(parent, childId, content, { source, signal })` sends later parent content; and `reportFrom(child, content, { delivery, signal })` sends selected child content to its direct parent. `followup` matches `Agent.followup()`, while `SubagentRun.steer()` remains the narrower confirmed live-run capability. The model-facing tools keep their stable `send_message` and `report` names and delegate routing to the corresponding intent methods.
|
||||
`SubagentRuntime` separates four execution intents: `start(name, request)` returns an ordinary holder-owned one-shot run; `startContinuable(spec)` establishes a durable child and returns its id plus the accepted initial `MessageId`; `followup(parent, childId, content, { source, signal })` sends later parent content; and `reportFrom(child, content, { delivery, signal })` sends selected child content to its direct parent. `followup` matches `Agent.followup()`, while `SubagentRun.steer()` remains the narrower confirmed live-run capability. The model-facing tools keep their stable `send_message` and `report` names and delegate routing to the corresponding intent methods.
|
||||
|
||||
Caller and provider requests are distinct. `SubagentStartRequest` contains caller-supplied one-shot data; `ResolvedSubagentStartRequest` adds the service-resolved descriptor before `SubagentProvider.start()`. For continuable creation, the manager passes a `ContinuableCreateRequest` to optional `SubagentProvider.prepareContinuable()` and receives detached creation data only. `SubagentService.resume()` and provider resume dispatch are absent: the continuation manager loads the descriptor, authorizes the parent, and owns Agent materialization, prompt delivery, cold resume, and teardown.
|
||||
Caller and provider requests are distinct. `SubagentStartRequest` contains caller-supplied one-shot data; `ResolvedSubagentStartRequest` adds the service-resolved descriptor before `SubagentProvider.start()`. For continuable creation, the manager passes a `ContinuableCreateRequest` to optional `SubagentProvider.prepareContinuable()` and receives detached creation data only. `SubagentRuntime.resume()` and provider resume dispatch are absent: the continuation manager loads the descriptor, authorizes the parent, and owns Agent materialization, prompt delivery, cold resume, and teardown.
|
||||
|
||||
`SessionStore.flush(session)` is the single durability barrier and returns `Promise<boolean>`. It resolves `true` after at least one scoped listener participates successfully, resolves `false` for an empty listener snapshot, and rejects with the first registered listener failure after all listeners settle. Participation cannot identify whether a selected persistence backend stored the state. Ordinary checkpoints may ignore the boolean; the continuation manager also treats its final flush as a best-effort barrier, deliberately ignores participation, logs rejection, and still disposes the child and releases ownership.
|
||||
|
||||
|
||||
@@ -14,9 +14,9 @@ Status: implemented
|
||||
|
||||
## 决策
|
||||
|
||||
`SubagentService` 分离四种执行意图:`start(name, request)` 返回普通的、由持有方负责的 one-shot run;`startContinuable(spec)` 建立持久化 child,并返回其 id 与已接受的初始 `MessageId`;`followup(parent, childId, content, { source, signal })` 发送后续 parent 内容;`reportFrom(child, content, { delivery, signal })` 将选定的 child 内容发送给其直接 parent。`followup` 与 `Agent.followup()` 一致,而 `SubagentRun.steer()` 仍是范围更窄的能力,仅向已确认仍在运行的 run 提供 steering(中途引导)。面向模型的工具保留稳定的 `send_message` 与 `report` 名称,并将路由委托给对应的意图方法。
|
||||
`SubagentRuntime` 分离四种执行意图:`start(name, request)` 返回普通的、由持有方负责的 one-shot run;`startContinuable(spec)` 建立持久化 child,并返回其 id 与已接受的初始 `MessageId`;`followup(parent, childId, content, { source, signal })` 发送后续 parent 内容;`reportFrom(child, content, { delivery, signal })` 将选定的 child 内容发送给其直接 parent。`followup` 与 `Agent.followup()` 一致,而 `SubagentRun.steer()` 仍是范围更窄的能力,仅向已确认仍在运行的 run 提供 steering(中途引导)。面向模型的工具保留稳定的 `send_message` 与 `report` 名称,并将路由委托给对应的意图方法。
|
||||
|
||||
调用方请求与提供方请求相互分离。`SubagentStartRequest` 包含调用方提供的 one-shot 数据;`ResolvedSubagentStartRequest` 会在调用 `SubagentProvider.start()` 前加入由服务解析的描述符。创建可继续 child 时,管理器将 `ContinuableCreateRequest` 传给可选的 `SubagentProvider.prepareContinuable()`,且只接收分离的创建数据。`SubagentService.resume()` 与提供方恢复分发均不存在:继续执行管理器加载描述符、对 parent 进行鉴权,并负责 Agent 实体化、提示词投递、冷恢复与 teardown。
|
||||
调用方请求与提供方请求相互分离。`SubagentStartRequest` 包含调用方提供的 one-shot 数据;`ResolvedSubagentStartRequest` 会在调用 `SubagentProvider.start()` 前加入由服务解析的描述符。创建可继续 child 时,管理器将 `ContinuableCreateRequest` 传给可选的 `SubagentProvider.prepareContinuable()`,且只接收分离的创建数据。`SubagentRuntime.resume()` 与提供方恢复分发均不存在:继续执行管理器加载描述符、对 parent 进行鉴权,并负责 Agent 实体化、提示词投递、冷恢复与 teardown。
|
||||
|
||||
`SessionStore.flush(session)` 是唯一的持久性屏障,并返回 `Promise<boolean>`。至少一个作用域内监听器成功参与后,它解析为 `true`;监听器快照为空时解析为 `false`;所有监听器结算后,如有失败,则以注册顺序最靠前的监听器错误拒绝。参与结果无法表明所选的持久化后端是否已经存储状态。普通检查点可以忽略该布尔值;继续执行管理器同样将最终 flush 视为 best-effort 屏障,有意忽略参与结果,记录拒绝日志,并仍会对 child 执行 dispose(资源释放)并释放所有权。
|
||||
|
||||
|
||||
@@ -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 .agents/notes/implemented/simplification/2026-07-27-request-error-retry-action.md
|
||||
2026-07-27-request-error-retry-action.md: 18ae9bc4ba26d1ad3cb7d1328d9e9d3e8de8377c
|
||||
2026-07-27-request-error-retry-action.zh.md: 5b36a38b6baed9b954fb04bca9e49c24edf9a969
|
||||
2026-07-27-request-error-retry-action.md: 9853d7d0439093afd3e7a611e360a6b32517ca4c
|
||||
2026-07-27-request-error-retry-action.zh.md: e0c4749b7ed6bddcbc44f3ab512df542f33831bd
|
||||
|
||||
@@ -26,4 +26,4 @@ The loop reads the action after the waterfall settles, closes the failed turn, a
|
||||
|
||||
Recovery ownership, asynchronous repair, and the retry decision share one typed return path. The live-agent interface and concrete loop lose the idle resummon capability and retry-window state. Callers cannot restart arbitrary failed non-request work without submitting a later prompt, while transient and context-overflow policies retain numbered retry turns, durable-history reconstruction, finite private budgets, and cancellation precedence.
|
||||
|
||||
Focused agent-loop tests pin retry chaining, terminal fallthrough, recovery failure, and cancellation races. The llm-retry and compact-basic suites pin their policy-owned action returns, and the ACP, goal-session, and plan-mode integrations pin successor-turn adoption.
|
||||
Focused agent-loop tests pin retry chaining, terminal fallthrough, recovery failure, and cancellation races. The llm-retry and compaction-basic suites pin their policy-owned action returns, and the ACP, goal-round-driver, and plan-mode integrations pin successor-turn adoption.
|
||||
|
||||
@@ -26,4 +26,4 @@ waterfall 结算后,循环读取该动作,关闭失败轮次,并从持久
|
||||
|
||||
恢复归属、异步修复和重试决策共用一条类型化返回路径。活跃 agent 接口与具体循环不再具有空闲无提示词再运行能力和重试窗口状态。调用方如果不提交后续提示词,就无法重启任意失败的非请求工作;瞬时策略与上下文溢出策略则保留编号重试轮次、从持久历史重建、有限的策略私有预算和取消优先级。
|
||||
|
||||
聚焦的 agent-loop 测试固定了重试链、未处理失败保持终态、恢复失败和取消竞态。llm-retry 与 compact-basic 测试套件固定其策略自有的动作返回,而 ACP(Agent Client Protocol)、goal-session 和 plan-mode 集成测试固定后继轮次承接。
|
||||
聚焦的 agent-loop 测试固定了重试链、未处理失败保持终态、恢复失败和取消竞态。llm-retry 与 compaction-basic 测试套件固定其策略自有的动作返回,而 ACP(Agent Client Protocol)、goal-round-driver 和 plan-mode 集成测试固定后继轮次承接。
|
||||
|
||||
@@ -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 .agents/notes/implemented/simplification/2026-07-28-remove-synthetic-log-only-turns.md
|
||||
2026-07-28-remove-synthetic-log-only-turns.md: cb4925fb7897d95898b3f620c5034b3082a72a0d
|
||||
2026-07-28-remove-synthetic-log-only-turns.zh.md: f9017afa13326aa574a54ccb646cbf66551c0f87
|
||||
2026-07-28-remove-synthetic-log-only-turns.md: 2b0add1a916021cfc1790c8f4fd5684305d81be8
|
||||
2026-07-28-remove-synthetic-log-only-turns.zh.md: 6ce185c102190b6a9dcb889ed813405d3329be26
|
||||
|
||||
@@ -18,7 +18,7 @@ The generic helper also duplicated domain policy. Its marker map said which plug
|
||||
|
||||
Core session invariants continue to enforce core-owned execution relations: turn and step numbering, enclosure of steering, assistant, tool, todo, and request-header events, and same-step tool call/result pairing. Core permits merge-extensible events between turns because only their declaring plugin knows whether they are execution-scoped or standalone. Plugin invariant companions remain responsible for their own event relations.
|
||||
|
||||
The title service appends `session/title` directly after its existing service, revision, cancellation, and live-session checks. The bundled model helper appends its literal `session/title-llm-request` record before dispatch. Persistence admits both through the bounded `session/event` path and drains them at ordinary checkpoints and lifecycle teardown; neither append forces a flush merely because it is between turns. A fallback, auxiliary request record, or accepted provider title may therefore appear after `turn/end` and before the next `turn/start`. Manual compaction uses the same between-turn capability for a `compact/* { turn: null }` bracket, but explicitly flushes the closed attempt because `/compact` promises durability before releasing queued prompt admission.
|
||||
The title service appends `session/title` directly after its existing service, revision, cancellation, and live-session checks. The bundled model helper appends its literal `session/title-llm-request` record before dispatch. Persistence admits both through the bounded `session/event` path and drains them at ordinary checkpoints and lifecycle teardown; neither append forces a flush merely because it is between turns. A fallback, auxiliary request record, or accepted provider title may therefore appear after `turn/end` and before the next `turn/start`. Manual compaction uses the same between-turn capability for a `compaction/* { turn: null }` bracket, but explicitly flushes the closed attempt because `/compact` promises durability before releasing queued prompt admission.
|
||||
|
||||
A session fork may end at any stable event position outside an open turn, not only at `turn/end`. This preserves standalone title and other plugin-owned log-only records in a default fork while still rejecting a prefix cut through active execution.
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ Status: implemented
|
||||
|
||||
核心会话不变量继续强制核心所属的执行关系:轮次与步骤编号、steering(中途引导)、助手、工具、待办和请求头事件的封闭,以及同一步骤内的工具调用/结果配对。核心允许可合并扩展事件位于轮次之间,因为只有声明它们的插件知道这些事件受执行作用域约束,还是可以独立存在。插件的不变量配套组件仍负责其自身的事件关系。
|
||||
|
||||
标题服务会在完成既有的服务状态、修订、取消和活跃会话检查后,直接追加 `session/title`。随附模型辅助函数会在发起调用前追加其字面量 `session/title-llm-request` 记录。持久化通过有界 `session/event` 路径接纳两者,并在常规检查点与生命周期结束时排空;二者都不会仅因为位于轮次之间就强制刷写。因此,回退标题、辅助请求记录或已接受的提供方标题可以出现在 `turn/end` 之后、下一个 `turn/start` 之前。手动压缩(compaction)利用同一项轮次间能力记录 `compact/* { turn: null }` 标记对,但会显式刷写已闭合的尝试,因为 `/compact` 承诺在放行排队中的提示词前完成持久化。
|
||||
标题服务会在完成既有的服务状态、修订、取消和活跃会话检查后,直接追加 `session/title`。随附模型辅助函数会在发起调用前追加其字面量 `session/title-llm-request` 记录。持久化通过有界 `session/event` 路径接纳两者,并在常规检查点与生命周期结束时排空;二者都不会仅因为位于轮次之间就强制刷写。因此,回退标题、辅助请求记录或已接受的提供方标题可以出现在 `turn/end` 之后、下一个 `turn/start` 之前。手动压缩(compaction)利用同一项轮次间能力记录 `compaction/* { turn: null }` 标记对,但会显式刷写已闭合的尝试,因为 `/compact` 承诺在放行排队中的提示词前完成持久化。
|
||||
|
||||
会话 fork 可以结束于开放轮次之外的任意稳定事件位置,而不限于 `turn/end`。这样,默认 fork 会保留独立标题和其他插件所属的纯日志记录,同时仍拒绝在活跃执行过程中截断前缀。
|
||||
|
||||
|
||||
@@ -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 .agents/notes/implemented/simplification/2026-07-29-shared-base-config-overlays.md
|
||||
2026-07-29-shared-base-config-overlays.md: bbffcda725e3e2c5c0d0ce56a4ee0f1f61559806
|
||||
2026-07-29-shared-base-config-overlays.zh.md: 9cf8833f2000451f6eef1ca5df154fff9c9ddb2a
|
||||
2026-07-29-shared-base-config-overlays.md: 5cce8e1b7578ed3317b6e6dd92ed3dc24a4c91f2
|
||||
2026-07-29-shared-base-config-overlays.zh.md: 3c88e782a5fdca2cb31f571d352ad36490ac9901
|
||||
|
||||
@@ -10,7 +10,7 @@ English | [中文](2026-07-29-shared-base-config-overlays.zh.md)
|
||||
|
||||
Neither file was what its location claimed. `examples/tui-agent` was not an example: `apps/cli/src/tui.ts` hardcoded it as the product's default config, and it owned the TUI PTY smoke, the eight terminal snapshot scenarios, and the PTY harness the `cordis-agent` leaf imported. `dsh-tui-demo` was not a demo either — it was the application, mounted by the shipped binary from `packages/examples/`.
|
||||
|
||||
The duplication was the load-bearing problem. Of the 43 shared rows, 38 were byte-identical and 5 differed for a defensible per-surface reason, so every capability change had to be made twice and could silently drift. The bundle also inverted a default: `composeTuiApp` read `config.goals ?? {}`, so the shipped TUI mounted goals, `tool-goal`, `goal-session`, and `/goal` although no config key requested them.
|
||||
The duplication was the load-bearing problem. Of the 43 shared rows, 38 were byte-identical and 5 differed for a defensible per-surface reason, so every capability change had to be made twice and could silently drift. The bundle also inverted a default: `composeTuiApp` read `config.goals ?? {}`, so the shipped TUI mounted goals, `tool-goal`, `goal-round-driver`, and `/goal` although no config key requested them.
|
||||
|
||||
## Decision
|
||||
|
||||
@@ -24,7 +24,7 @@ Precedence is list order, last write winning per row: base, then the surface ove
|
||||
|
||||
A patch replaces its target row's whole `config` rather than merging. Therefore, a row whose value differs per surface lives in the overlays, never in the base, so no row is patched by three layers at once. Session identity cannot ride a config key at all — it moved to `dsh-agent-loop`'s `CONFIGURED_AGENT_IDENTITIES_KEY`, as the launcher-owned identity record documented.
|
||||
|
||||
`examples/tui-agent`, `examples/cordis-agent`, `examples/code-mode`, and `packages/examples/tui-demo` are deleted. The TUI tests move to `apps/cli/tests/`, the cordis-toolset e2e to `packages/self-modification/tool-cordis/tests/`, and the supported Code Mode demo remains the ACP overlay at `examples/acp-agent/code-mode.cordis.yml`.
|
||||
`examples/tui-agent`, `examples/cordis-agent`, `examples/code-mode`, and `packages/examples/tui-demo` are deleted. The TUI tests move to `apps/cli/tests/`, the cordis-toolset e2e to `packages/extensions/tool-cordis/tests/`, and the supported Code Mode demo remains the ACP overlay at `examples/acp-agent/code-mode.cordis.yml`.
|
||||
|
||||
## Alternatives considered
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ Status: implemented
|
||||
|
||||
这两份文件都名不副实。`examples/tui-agent` 并不是示例:`apps/cli/src/tui.ts` 把它硬编码为产品的默认配置;它还拥有 TUI 的 PTY 冒烟测试、八个终端快照场景,以及被 `cordis-agent` 叶节点 import 的 PTY harness。`dsh-tui-demo` 也不是 demo——它就是应用本身,由交付的二进制从 `packages/examples/` 中挂载。
|
||||
|
||||
真正决定性的问题是重复。43 个共享配置项中,38 个逐字节相同,5 个因各 surface 的正当理由而不同;因此每次能力改动都必须改两处,而且可能无声漂移。该组合包还反转了一个默认值:`composeTuiApp` 读取 `config.goals ?? {}`,于是交付的 TUI 挂载了 goals、`tool-goal`、`goal-session` 和 `/goal`——尽管没有任何配置键要求它们。
|
||||
真正决定性的问题是重复。43 个共享配置项中,38 个逐字节相同,5 个因各 surface 的正当理由而不同;因此每次能力改动都必须改两处,而且可能无声漂移。该组合包还反转了一个默认值:`composeTuiApp` 读取 `config.goals ?? {}`,于是交付的 TUI 挂载了 goals、`tool-goal`、`goal-round-driver` 和 `/goal`——尽管没有任何配置键要求它们。
|
||||
|
||||
## 决策
|
||||
|
||||
@@ -24,7 +24,7 @@ Status: implemented
|
||||
|
||||
patch 会整体替换目标配置项的 `config` 而不合并。因此,取值因 surface 而异的配置项住在 overlay 中,绝不住在 base 里,从而没有任何配置项会被三层同时 patch。会话身份根本不能经由配置键传递——它迁移到了 `dsh-agent-loop` 的 `CONFIGURED_AGENT_IDENTITIES_KEY`,正如启动器持有身份的记录所述。
|
||||
|
||||
`examples/tui-agent`、`examples/cordis-agent`、`examples/code-mode` 与 `packages/examples/tui-demo` 均被删除。TUI 测试迁往 `apps/cli/tests/`,cordis 工具集的 e2e 迁入 `packages/self-modification/tool-cordis/tests/`,受支持的 Code Mode demo 则保留为 `examples/acp-agent/code-mode.cordis.yml` 中的 ACP(Agent Client Protocol)overlay。
|
||||
`examples/tui-agent`、`examples/cordis-agent`、`examples/code-mode` 与 `packages/examples/tui-demo` 均被删除。TUI 测试迁往 `apps/cli/tests/`,cordis 工具集的 e2e 迁入 `packages/extensions/tool-cordis/tests/`,受支持的 Code Mode demo 则保留为 `examples/acp-agent/code-mode.cordis.yml` 中的 ACP(Agent Client Protocol)overlay。
|
||||
|
||||
## 备选方案
|
||||
|
||||
|
||||
@@ -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 .agents/notes/implemented/simplification/2026-07-31-one-route-to-add-a-workspace.md
|
||||
2026-07-31-one-route-to-add-a-workspace.md: 4de79b7db30853a13319aa016d4eb5fb5d9ec1f1
|
||||
2026-07-31-one-route-to-add-a-workspace.zh.md: 24039dbe46a129419148618ca037ef3b6dac5052
|
||||
2026-07-31-one-route-to-add-a-workspace.md: bcbe77899d4050bd898b256e81f88183099bdebf
|
||||
2026-07-31-one-route-to-add-a-workspace.zh.md: edd17af03ca6454be321a4b267ff1761ece2d03f
|
||||
|
||||
@@ -27,7 +27,7 @@ The direct-open path carries the busy rule the menu entry states: while a pick i
|
||||
|
||||
## Wire and CLI surface
|
||||
|
||||
`workspace.create` accepts only `{ path }`; the wire schema and `WorkspaceApi` have no `name` member. The gateway has no `workspaceRoot` config, the client contract exposes only path adoption through `WorkspaceCreateInput`, `WorkspacesService.create`, and `intentName`, and `dsh web` has no `--workspace-root` flag. `workspace-name-conflict` remains on the wire as `workspace.rename`'s duplicate-title error.
|
||||
`workspace.create` accepts only `{ path }`; the wire schema and `WorkspaceApi` have no `name` member. The gateway has no `workspaceRoot` config, the client contract exposes only path adoption through `WorkspaceCreateInput`, `WorkspaceRuntime.create`, and `intentName`, and `dsh web` has no `--workspace-root` flag. `workspace-name-conflict` remains on the wire as `workspace.rename`'s duplicate-title error.
|
||||
|
||||
## Testing
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ Status: implemented
|
||||
|
||||
## Wire 与 CLI(命令行界面)表层
|
||||
|
||||
`workspace.create` 只接受 `{ path }`;wire schema 与 `WorkspaceApi` 没有 `name` 成员。网关没有 `workspaceRoot` 配置;客户端约定只通过 `WorkspaceCreateInput`、`WorkspacesService.create` 与 `intentName` 提供按路径接纳,`dsh web` 没有 `--workspace-root` flag。`workspace-name-conflict` 仍保留在 wire 上,作为 `workspace.rename` 的标题重名错误。
|
||||
`workspace.create` 只接受 `{ path }`;wire schema 与 `WorkspaceApi` 没有 `name` 成员。网关没有 `workspaceRoot` 配置;客户端约定只通过 `WorkspaceCreateInput`、`WorkspaceRuntime.create` 与 `intentName` 提供按路径接纳,`dsh web` 没有 `--workspace-root` flag。`workspace-name-conflict` 仍保留在 wire 上,作为 `workspace.rename` 的标题重名错误。
|
||||
|
||||
## 测试
|
||||
|
||||
|
||||
@@ -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 .agents/notes/implemented/simplification/2026-08-04-remove-tui-package.md
|
||||
2026-08-04-remove-tui-package.md: 4b89e45f009c2b240f88db40875d77d1a809e721
|
||||
2026-08-04-remove-tui-package.zh.md: b2f88c5bc8f6f9959ad442d275d458c39bdd8689
|
||||
2026-08-04-remove-tui-package.md: 2834ec10aa86a3dcf612a1fe9067732ca93d921f
|
||||
2026-08-04-remove-tui-package.zh.md: 9f8fe08d1b574b83514dfd097153b09e0c8260fd
|
||||
|
||||
@@ -14,7 +14,7 @@ The package also made the repository's supported application inventory misleadin
|
||||
|
||||
The `packages/ui/tui` package is deleted without a compatibility package or alias. Its source, package tests, terminal snapshots, dependency declarations, patched `pi-tui` artifact, workspace references, generated service catalog entry, and documentation are removed together. Generic host and agent-loop capabilities remain unchanged.
|
||||
|
||||
The SDK project toolchain that remained as the TUI package's final consumer is deleted by the [toolchain removal decision](2026-08-11-remove-sdk-project-toolchain.md). Host applications may still mount the provider-neutral `dsh-user-interaction`, `dsh-commands`, and presentation services directly.
|
||||
The SDK project toolchain that remained as the TUI package's final consumer is deleted by the [toolchain removal decision](2026-08-11-remove-sdk-project-toolchain.md). Host applications may still mount the provider-neutral `dsh-user-questions`, `dsh-commands`, and presentation services directly.
|
||||
|
||||
This decision supersedes the reusable-package retention in [the explicit-config `dsh` entrypoint decision](../../archived/simplification/2026-08-03-explicit-config-dsh-entrypoint.md) and the current applicability of the archived TUI implementation notes. Their historical records remain frozen, but they are not authority for the supported package or application inventory.
|
||||
|
||||
@@ -36,4 +36,4 @@ Repository searches and generated catalogs contain no TUI package, dependency pa
|
||||
|
||||
DeepSeek Harness has no terminal UI package. Existing imports and `cordis.yml` rows that depend on the package fail instead of being translated. Web remains the shipped interactive surface; ACP, JSON-RPC, and one-shot CLI remain the non-Web entry points.
|
||||
|
||||
The provider-neutral command, user-interaction, approval, tool-presentation, PTY, and session-projection capabilities remain available to other hosts. Reintroducing a terminal frontend requires a named product or deployment, an explicit package boundary, a concrete interaction provider, and assembled lifecycle and transcript acceptance for that frontend.
|
||||
The provider-neutral command, user-questions, approval, tool-presentation, PTY, and session-projection capabilities remain available to other hosts. Reintroducing a terminal frontend requires a named product or deployment, an explicit package boundary, a concrete interaction provider, and assembled lifecycle and transcript acceptance for that frontend.
|
||||
|
||||
@@ -14,7 +14,7 @@ Status: implemented
|
||||
|
||||
删除 `packages/ui/tui` 包,不提供兼容包或别名。其源码、包测试、终端快照、依赖声明、已打补丁的 `pi-tui` 产物、workspace 引用、生成的服务目录条目和文档会一并移除。通用宿主能力与 agent loop(智能体循环)能力保持不变。
|
||||
|
||||
作为 TUI 包最后消费方的 SDK 项目工具链已由[工具链移除决策](2026-08-11-remove-sdk-project-toolchain.md)删除。宿主应用仍可直接挂载提供方无关的 `dsh-user-interaction`、`dsh-commands` 和呈现服务。
|
||||
作为 TUI 包最后消费方的 SDK 项目工具链已由[工具链移除决策](2026-08-11-remove-sdk-project-toolchain.md)删除。宿主应用仍可直接挂载提供方无关的 `dsh-user-questions`、`dsh-commands` 和呈现服务。
|
||||
|
||||
本决策取代[显式配置 `dsh` 入口决策](../../archived/simplification/2026-08-03-explicit-config-dsh-entrypoint.md)中保留可复用包的决定,也使已归档 TUI 实现记录不再适用于当前状态。这些历史记录继续保持冻结,但不再作为受支持包或应用清单的依据。
|
||||
|
||||
|
||||
@@ -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 .agents/notes/implemented/simplification/2026-08-06-buffer-free-feedback-telemetry.md
|
||||
2026-08-06-buffer-free-feedback-telemetry.md: 008bebdcb59f7ef4fe49f8e731aad77861368d5c
|
||||
2026-08-06-buffer-free-feedback-telemetry.zh.md: 7052e075921f4470864f5ea1c4aed5cf6201becf
|
||||
2026-08-06-buffer-free-feedback-telemetry.md: a17ad586fbf94c1622a29dc158050a308147a206
|
||||
2026-08-06-buffer-free-feedback-telemetry.zh.md: 1253b2e64057b99d3cec055c971fa8163719f41f
|
||||
|
||||
@@ -10,7 +10,7 @@ Feedback-only telemetry must upload the session-log prefix only after recorded f
|
||||
|
||||
## Decision
|
||||
|
||||
The telemetry coordinator provides `live` and `on-demand` capture. On-demand capture registers no session, flush, or operational-event listeners and retains no projected records. `captureSession(session, throughSeq?)` reads the canonical session log after the handoff cursor through an optional inclusive sequence boundary, applies the fixed projection, deep-copies each accepted event, runs the current `telemetry/record` waterfall, and hands the result to the backend.
|
||||
The telemetry coordinator provides `live` and `on-demand` capture. On-demand capture registers no session, flush, or operational-event listeners and retains no projected records. `captureSession(session, throughSeq?)` reads the canonical session log after the handoff cursor through an optional inclusive sequence boundary, applies the fixed projection, deep-copies each accepted event, runs the current `session-telemetry/record` waterfall, and hands the result to the backend.
|
||||
|
||||
`FEEDBACK_ONLY` invokes that method with the `feedback/record` event's sequence. The append is already committed when `session/event` listeners run, so the replay contains the feedback event and cannot include a later suffix. The existing handoff cursor distinguishes later replays without another pending-record index.
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ Status: implemented
|
||||
|
||||
## 决策
|
||||
|
||||
遥测协调器提供 `live` 与 `on-demand` 捕获。按需捕获不注册会话、flush 或运维事件监听器,也不保留投影记录。`captureSession(session, throughSeq?)` 从 handoff 游标之后读取权威会话日志,直至可选的序列号边界(含边界),应用固定投影、深拷贝每个已接受事件、运行当前的 `telemetry/record` waterfall(瀑布式事件),并将结果交给后端。
|
||||
遥测协调器提供 `live` 与 `on-demand` 捕获。按需捕获不注册会话、flush 或运维事件监听器,也不保留投影记录。`captureSession(session, throughSeq?)` 从 handoff 游标之后读取权威会话日志,直至可选的序列号边界(含边界),应用固定投影、深拷贝每个已接受事件、运行当前的 `session-telemetry/record` waterfall(瀑布式事件),并将结果交给后端。
|
||||
|
||||
`FEEDBACK_ONLY` 以 `feedback/record` 事件的序列号调用该方法。`session/event` 监听器运行时,追加已经提交,因此回放包含该反馈事件,且无法包含后续后缀。现有 handoff 游标可区分后续回放,无需另一个待处理记录索引。
|
||||
|
||||
|
||||
@@ -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 .agents/notes/implemented/simplification/2026-08-09-conversational-schedule-delivery.md
|
||||
2026-08-09-conversational-schedule-delivery.md: ee58ae25abf125ed5507f3cd27ee2ba09b1711ec
|
||||
2026-08-09-conversational-schedule-delivery.zh.md: 15fe0d1bba2590119b1457fc0a8437a40f7d75d2
|
||||
2026-08-09-conversational-schedule-delivery.md: 8795d9da1d08bf6e74ecd6b374db69e3c143ad7d
|
||||
2026-08-09-conversational-schedule-delivery.zh.md: c9688f80cb67a936aa331f09a2a03200c3be2af8
|
||||
|
||||
@@ -16,7 +16,7 @@ A due reminder waits for the Agent's idle maintenance phase and calls `followup(
|
||||
|
||||
`schedule/change` remains the only durable Schedule state. Its dispatch operation records that the follow-up was synchronously queued, which prevents ordinary restart replay after the dispatch is durable. Dispatch does not claim model success, user acknowledgement, or an external notification. The narrow crash interval between enqueue and durable dispatch remains at-least-once.
|
||||
|
||||
Schedule exposes no presentation projection, Host sidecar, browser event node, keyed event slot, or client renderer. Session persistence retains its shared `flush()` contract and has no Schedule-driven success event. The opt-in Web overlay loads only `@deepseek-ai/dsh-tool-schedule`.
|
||||
Schedule exposes no presentation projection, Host sidecar, browser event node, keyed event slot, or client renderer. Session persistence retains its shared `flush()` contract and has no Schedule-driven success event. The opt-in Web overlay loads only `@deepseek-ai/dsh-schedule`.
|
||||
|
||||
## Alternatives considered
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ Schedule 已经通过将普通的 agent(智能体)后续轮次排入队列
|
||||
|
||||
`schedule/change` 仍是唯一持久 Schedule 状态。其 dispatch 操作记录后续轮次已同步入队,这会在 dispatch 持久化后阻止普通的重启回放。dispatch 不表示模型成功、用户确认或外部通知。入队与持久 dispatch 之间的狭窄崩溃窗口仍保留至少一次语义。
|
||||
|
||||
Schedule 不公开呈现投影、Host 伴随数据、浏览器事件节点、按事件键控的 slot 或客户端渲染器。会话持久化保留共享的 `flush()` 约定,且不存在由 Schedule 驱动的成功事件。显式启用的 Web overlay 只加载 `@deepseek-ai/dsh-tool-schedule`。
|
||||
Schedule 不公开呈现投影、Host 伴随数据、浏览器事件节点、按事件键控的 slot 或客户端渲染器。会话持久化保留共享的 `flush()` 约定,且不存在由 Schedule 驱动的成功事件。显式启用的 Web overlay 只加载 `@deepseek-ai/dsh-schedule`。
|
||||
|
||||
## 已考虑的替代方案
|
||||
|
||||
|
||||
@@ -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 .agents/notes/implemented/simplification/2026-08-09-remove-repository-plugin.md
|
||||
2026-08-09-remove-repository-plugin.md: 8ac6fd18b756e227f8dabc82eb4926a51702c5a1
|
||||
2026-08-09-remove-repository-plugin.zh.md: 6e504a151a88b87b8093a91a91ede1cb919a0b45
|
||||
2026-08-09-remove-repository-plugin.md: f9d1c00849219d30b4c0441246b2810ebca9fd6a
|
||||
2026-08-09-remove-repository-plugin.zh.md: 7f89f7f20f0f08533fef1d0de99e0ae6c827c93a
|
||||
|
||||
@@ -16,7 +16,7 @@ DeepSeek Harness has one standalone external-Plugin distribution path: installab
|
||||
|
||||
The `@deepseek-ai/dsh-repository-plugin` package, `.dsh-plugin` authoring format, `dsh-plugin-prepare` executable, generated wrapper, immutable repository cache, base `repository-plugins` row, and dedicated GitHub acceptance lane are removed. The unused vendored `@cordisjs/plugin-loader/repository` subpath and its bundled pnpm dependency are removed with their only consumer. Existing repository cache directories are inert user data; DSH neither reads nor deletes them.
|
||||
|
||||
Bundles compose existing owners directly. A bundle that contributes Skills mounts `@deepseek-ai/dsh-skill-local`; one that contributes MCP servers mounts `@deepseek-ai/dsh-mcp-client`; native behavior mounts an ordinary compiled Cordis Plugin. These packages retain their own validation, lifecycle, registration, and teardown contracts. No compatibility parser or migration from `.dsh-plugin` is retained under the pre-release compatibility policy.
|
||||
Bundles compose existing owners directly. A bundle that contributes Skills mounts `@deepseek-ai/dsh-skill-filesystem`; one that contributes MCP servers mounts `@deepseek-ai/dsh-mcp-client`; native behavior mounts an ordinary compiled Cordis Plugin. These packages retain their own validation, lifecycle, registration, and teardown contracts. No compatibility parser or migration from `.dsh-plugin` is retained under the pre-release compatibility policy.
|
||||
|
||||
This note consolidates the removed repository cache, static format, config-only integration, npm-backed preparation, and trusted code-entry decisions. Their original motivation survives here: standalone users need package-manager-owned external composition, Git and npm dependencies may execute trusted lifecycle code, static Skill and MCP contributions should reuse their existing owners, and source identity belongs in the profile dependency specification and lockfile. Their implementation-specific wrappers, cache generations, and preparation protocol no longer constrain the product.
|
||||
|
||||
@@ -37,7 +37,7 @@ This note consolidates the removed repository cache, static format, config-only
|
||||
- Profile installation requires `pnpm` on the host `PATH`. This is acceptable for an explicit package-management operation and avoids shipping the removed cache's pinned package-manager runtime solely for configuration-time activation.
|
||||
- `.dsh-plugin` packages and existing repository source-list patches stop working. Their cache files remain removable by the user but are not migrated or automatically deleted.
|
||||
- The dedicated pnpm runtime, preparation executable, wrapper generator, Git credential CI setup, repository cache, and repository-specific tests disappear.
|
||||
- Package-relative static assets need a bundle-owned path form so a declarative bundle can point `dsh-skill-local`, `dsh-mcp-client`, or another Plugin at files it ships without custom runtime glue. That capability is owned by the bundle format rather than a repository adapter.
|
||||
- Package-relative static assets need a bundle-owned path form so a declarative bundle can point `dsh-skill-filesystem`, `dsh-mcp-client`, or another Plugin at files it ships without custom runtime glue. That capability is owned by the bundle format rather than a repository adapter.
|
||||
|
||||
## Testing
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ DeepSeek Harness 只保留一种独立的外部插件分发路径:可安装的
|
||||
|
||||
移除 `@deepseek-ai/dsh-repository-plugin` 包、`.dsh-plugin` 编写格式、`dsh-plugin-prepare` 可执行文件、生成的包装层、不可变 repository 缓存、base 中的 `repository-plugins` 配置项,以及专用 GitHub 验收流水线。vendor 中未再使用的 `@cordisjs/plugin-loader/repository` 子路径及其随附的 pnpm 依赖,也随唯一消费方一并移除。现有 repository 缓存目录只是不会再产生作用的用户数据;DSH 既不会读取,也不会删除这些目录。
|
||||
|
||||
组合包直接组合现有归属方。提供 skill 的组合包挂载 `@deepseek-ai/dsh-skill-local`;提供 MCP 服务器的组合包挂载 `@deepseek-ai/dsh-mcp-client`;原生行为则挂载普通的已编译 Cordis 插件。这些包继续保有各自的校验、生命周期、注册和 teardown 契约。根据预发布兼容政策,不保留针对 `.dsh-plugin` 的兼容解析器或迁移机制。
|
||||
组合包直接组合现有归属方。提供 skill 的组合包挂载 `@deepseek-ai/dsh-skill-filesystem`;提供 MCP 服务器的组合包挂载 `@deepseek-ai/dsh-mcp-client`;原生行为则挂载普通的已编译 Cordis 插件。这些包继续保有各自的校验、生命周期、注册和 teardown 契约。根据预发布兼容政策,不保留针对 `.dsh-plugin` 的兼容解析器或迁移机制。
|
||||
|
||||
本说明整合了已移除的 repository 缓存、静态格式、纯配置集成、由 npm 支持的准备流程和受信任代码入口决策。其原始动机保留于此:独立用户需要由包管理器负责的外部组合方式;Git 和 npm 依赖可以执行受信任的生命周期代码;静态 skill 与 MCP 贡献应复用现有归属方;来源标识应位于 profile 的依赖说明符和锁文件中。相应实现特有的包装层、缓存 generation 和准备协议不再约束产品。
|
||||
|
||||
@@ -37,7 +37,7 @@ DeepSeek Harness 只保留一种独立的外部插件分发路径:可安装的
|
||||
- 安装 profile 时,宿主机的 `PATH` 中必须提供 `pnpm`。对于显式的包管理操作,这一要求可以接受,并且可避免仅为配置阶段激活而随产品交付已移除缓存所使用的固定版本包管理器运行时。
|
||||
- `.dsh-plugin` 包和现有 repository 源列表 patch 停止工作。用户仍可自行删除其缓存文件,但系统不会迁移或自动删除这些文件。
|
||||
- 专用 pnpm 运行时、准备工作可执行文件、包装层生成器、Git 凭据 CI 设置、repository 缓存和 repository 专用测试全部消失。
|
||||
- 静态资源需要一种由组合包拥有、可相对于包解析的路径形式,使声明式组合包可以将 `dsh-skill-local`、`dsh-mcp-client` 或其他插件指向它随包交付的文件,而无需定制运行时代码。该能力归组合包格式所有,而不是 repository 适配器。
|
||||
- 静态资源需要一种由组合包拥有、可相对于包解析的路径形式,使声明式组合包可以将 `dsh-skill-filesystem`、`dsh-mcp-client` 或其他插件指向它随包交付的文件,而无需定制运行时代码。该能力归组合包格式所有,而不是 repository 适配器。
|
||||
|
||||
## 测试
|
||||
|
||||
|
||||
@@ -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 .agents/notes/implemented/simplification/2026-08-11-remove-sdk-project-toolchain.md
|
||||
2026-08-11-remove-sdk-project-toolchain.md: 5c147560a2a9a796d6ca98d1acaf648049ae6a9f
|
||||
2026-08-11-remove-sdk-project-toolchain.zh.md: 07624052e936d8705958e826c68f39f34dcd146a
|
||||
2026-08-11-remove-sdk-project-toolchain.md: cfaf0df46e34d419f5d0b2d5a2a25580675fdc31
|
||||
2026-08-11-remove-sdk-project-toolchain.zh.md: 3b563007bf92f94adab6c68eeaa9c6c1522815e4
|
||||
|
||||
@@ -16,7 +16,7 @@ The same `scaffold/` group also contained the independently used SDK protocol, T
|
||||
|
||||
The SDK project toolchain is deleted. The `@deepseek-ai/create-sdk`, `@deepseek-ai/dsh-scripts`, `@deepseek-ai/dsh-helper`, and `@deepseek-ai/dsh-telemetry` packages, their binaries, tests, templates, feature catalog, project-editing model, package-manager support, launcher telemetry, and repository creation skill have no replacement or compatibility layer. Their workspace, build, test, packaging, documentation-generator, vendoring-rescope, and dependency records are removed with them.
|
||||
|
||||
The runtime SDK remains. `@deepseek-ai/dsh-sdk-client`, `@deepseek-ai/dsh-sdk-protocol`, and `@deepseek-ai/dsh-jsonrpc` move unchanged from `packages/scaffold/` to `packages/sdk/`; their npm names and wire behavior do not change. Consumers continue to provide an executable plus an external `cordis.yml`, and the JSON-RPC server remains an ordinary plugin selected by that configuration.
|
||||
The runtime SDK remains. `@deepseek-ai/dsh-sdk-client`, `@deepseek-ai/dsh-sdk-protocol`, and `@deepseek-ai/dsh-sdk-jsonrpc-server` move unchanged from `packages/scaffold/` to `packages/sdk/`; their npm names and wire behavior do not change. Consumers continue to provide an executable plus an external `cordis.yml`, and the JSON-RPC server remains an ordinary plugin selected by that configuration.
|
||||
|
||||
The canceled developer-project, project-editing, and follow-up-capabilities proposals are deleted rather than retained as active or rejected records. This note preserves the motivation they shared, the decision not to ship that product, the capability given up, and the condition for reconsideration. Frozen archived Agent Notes remain historical snapshots and are not edited.
|
||||
|
||||
@@ -32,7 +32,7 @@ The workspace contains none of the four deleted package names or either removed
|
||||
|
||||
**Delete the runtime SDK stack too.** Rejected because the Python SDK, the out-of-process Harness subagent provider, and the JSON-RPC example are current consumers of the protocol, client, and server.
|
||||
|
||||
**Leave the runtime stack under `packages/scaffold/`.** Rejected because nothing left in that group scaffolds a project. `packages/sdk/` states the surviving role directly even though the repository as a whole is also an SDK.
|
||||
**Leave the runtime stack under `packages/scaffold/`.** Rejected because nothing left in that group scaffolds a project. `packages/sdk/` states the surviving role directly because `SDK` has one repository meaning: the JSON-RPC client/server protocol used by the supported Python and TypeScript SDKs. DeepSeek Harness itself is not an SDK project.
|
||||
|
||||
## Consequences
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ Status: implemented
|
||||
|
||||
删除 SDK 项目工具链。`@deepseek-ai/create-sdk`、`@deepseek-ai/dsh-scripts`、`@deepseek-ai/dsh-helper` 和 `@deepseek-ai/dsh-telemetry` 包及其二进制文件、测试、模板、功能目录、项目编辑模型、包管理器支持、启动器遥测和仓库项目创建 skill 均不提供替代实现或兼容层。与其对应的 workspace、构建、测试、打包、文档生成器、vendor scope 重写和依赖记录也一并移除。
|
||||
|
||||
保留运行时 SDK。`@deepseek-ai/dsh-sdk-client`、`@deepseek-ai/dsh-sdk-protocol` 和 `@deepseek-ai/dsh-jsonrpc` 保持原样,从 `packages/scaffold/` 移至 `packages/sdk/`;其 npm 名称和协议交互行为保持不变。消费方继续提供一个可执行文件和一份外置 `cordis.yml`,JSON-RPC 服务器仍是由该配置选择的普通插件。
|
||||
保留运行时 SDK。`@deepseek-ai/dsh-sdk-client`、`@deepseek-ai/dsh-sdk-protocol` 和 `@deepseek-ai/dsh-sdk-jsonrpc-server` 保持原样,从 `packages/scaffold/` 移至 `packages/sdk/`;其 npm 名称和协议交互行为保持不变。消费方继续提供一个可执行文件和一份外置 `cordis.yml`,JSON-RPC 服务器仍是由该配置选择的普通插件。
|
||||
|
||||
被取消的开发者项目、项目编辑和后续能力提案予以删除,而不是保留为活跃或已否决记录。本 Agent Note 保留这些提案共有的动机、不交付该产品的决策、放弃的能力,以及重新考虑这一决定的条件。已冻结的归档 Agent Note 仍是历史快照,不作修改。
|
||||
|
||||
@@ -32,7 +32,7 @@ workspace 中不再存在上述 4 个已删除包名或 2 套已移除的命令
|
||||
|
||||
**同时删除运行时 SDK 栈。** 不予采纳,因为 Python SDK、进程外 Harness subagent 提供方和 JSON-RPC 示例目前仍是协议、客户端和服务器的消费方。
|
||||
|
||||
**将运行时栈继续留在 `packages/scaffold/` 下。** 不予采纳,因为该分组剩余内容均不再负责搭建项目。尽管整个仓库同样是一套 SDK,`packages/sdk/` 仍直接说明了保留内容的职责。
|
||||
**将运行时栈继续留在 `packages/scaffold/` 下。** 不予采纳,因为该分组剩余内容均不再负责搭建项目。`packages/sdk/` 直接说明了保留内容的职责,因为 `SDK` 在仓库中只有一个含义:受支持的 Python 与 TypeScript SDK 所使用的 JSON-RPC 客户端/服务器协议。DeepSeek 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 .agents/notes/implemented/simplification/2026-08-12-separate-source-launch-from-build.md
|
||||
2026-08-12-separate-source-launch-from-build.md: d3f2d21c9bf74c699c1468f99074d88ab2138cb4
|
||||
2026-08-12-separate-source-launch-from-build.zh.md: 2cc2decd0a46bd1c30bd7113a665740dcf359947
|
||||
2026-08-12-separate-source-launch-from-build.md: 639e9de0ce13a571e7f83929d9c76eac2664daab
|
||||
2026-08-12-separate-source-launch-from-build.zh.md: bbb55123860f2bba0209e95e803fa7f49328bdef
|
||||
|
||||
@@ -14,7 +14,7 @@ Source modules reached through tsx and browser modules reached through built bun
|
||||
|
||||
The root `dsh` script only runs `node --import tsx/esm apps/cli/src/bin.ts`. `pnpm run build` remains the separate operation that generates package and frontend artifacts. Source users run the build before the first production-like launch and whenever frontend or client-plugin artifacts need refreshing.
|
||||
|
||||
Missing TypeRT host artifacts fail profile boot through module-resolution errors without a build instruction. Once those host artifacts exist, missing frontend and client-plugin artifacts fail at startup with diagnostics that direct the user to `pnpm run build`. The launcher does not validate artifact freshness: existing stale frontend or client-plugin bundles are accepted and can run older browser code until the next build. After package Node halves have been built once, `pnpm run dev:web` rebuilds only packages that declare `dsh.client`; it keeps client-plugin bundles current and activates their hot-reload path, but does not rebuild the frontend shell.
|
||||
Missing Typert host artifacts fail profile boot through module-resolution errors without a build instruction. Once those host artifacts exist, missing frontend and client-plugin artifacts fail at startup with diagnostics that direct the user to `pnpm run build`. The launcher does not validate artifact freshness: existing stale frontend or client-plugin bundles are accepted and can run older browser code until the next build. After package Node halves have been built once, `pnpm run dev:web` rebuilds only packages that declare `dsh.client`; it keeps client-plugin bundles current and activates their hot-reload path, but does not rebuild the frontend shell.
|
||||
|
||||
This decision owns build scheduling only. The [tsx ESM source-launch decision](../architecture/2026-07-29-dsh-source-launch-tsx-esm.md) owns TypeScript transformation and workspace resolution, the [source-run decision](2026-08-10-source-run-without-managed-installer.md) owns repository scripts as the supported checkout entry points, and the [personal-config decision](../feature/2026-07-20-dsh-cli-personal-config.md) owns the machine-level configuration layer.
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ TypeScript 源码启动器无需在每次调用前完成整个仓库的构建。
|
||||
|
||||
根目录的 `dsh` 脚本只运行 `node --import tsx/esm apps/cli/src/bin.ts`。`pnpm run build` 仍是生成包与前端产物的独立操作。源码用户在首次进行类生产启动前运行构建,并在前端或 Client plugin 产物需要刷新时再次运行。
|
||||
|
||||
TypeRT Host 产物缺失时,profile 启动会因不含构建指引的模块解析错误而失败。这些 Host 产物存在后,如果前端或 Client plugin 产物缺失,启动会失败,诊断信息会指示用户运行 `pnpm run build`。启动器不会验证产物是否为最新:已有的陈旧前端或 Client plugin 组合包仍会被接受,并可能继续运行旧版浏览器代码,直至下次构建。各包的 Node 半侧至少构建过一次后,`pnpm run dev:web` 只重建声明了 `dsh.client` 的包;它会保持 Client plugin 组合包为最新状态并启用其热重载路径,但不会重建前端 shell。
|
||||
Typert Host 产物缺失时,profile 启动会因不含构建指引的模块解析错误而失败。这些 Host 产物存在后,如果前端或 Client plugin 产物缺失,启动会失败,诊断信息会指示用户运行 `pnpm run build`。启动器不会验证产物是否为最新:已有的陈旧前端或 Client plugin 组合包仍会被接受,并可能继续运行旧版浏览器代码,直至下次构建。各包的 Node 半侧至少构建过一次后,`pnpm run dev:web` 只重建声明了 `dsh.client` 的包;它会保持 Client plugin 组合包为最新状态并启用其热重载路径,但不会重建前端 shell。
|
||||
|
||||
本决策仅规定构建调度。[tsx ESM 源码启动决策](../architecture/2026-07-29-dsh-source-launch-tsx-esm.md)规定 TypeScript 转换与 workspace 解析,[源码运行决策](2026-08-10-source-run-without-managed-installer.md)规定以仓库脚本作为受支持的检出入口,[个人配置决策](../feature/2026-07-20-dsh-cli-personal-config.md)规定机器级配置层。
|
||||
|
||||
|
||||
Reference in New Issue
Block a user