@khorsheed/dsh-local-agent-dsh-headless 0.2.0-rc.3 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,8 +1,18 @@
1
1
  # 变更记录
2
2
 
3
+ ## 0.2.1(2026-10-27)
4
+
5
+ - 随 local-agent 家族同版发布,无自身变更。
6
+
7
+ ## 0.2.0(2026-10-26)
8
+
9
+ - **版本号去掉 rc**:社区包不再发 prerelease 版本(docs/publishing.md「版本号规则」),家族各包同版 0.2.0;本节原记为 0.2.0-rc.4。
10
+ - 随 local-agent 家族同版发布(家族七包必须同一版本线),本包无变更。
11
+ - **README 重写**:改为大白话说明,完整参考移到 `docs/`;去掉过时的验收说明、失效链接和并不存在的 `dsh preflight` 命令。
12
+
3
13
  ## 0.2.0-rc.3(2026-10-22)
4
14
 
5
- (版本号说明:本包 0.2.0-rc.2 曾被发布后又撤回,npm 永久烧掉该版本号——发布侧报错「Cannot stage previously published version」——为守住家族「同一版本线」纪律(check:release-groups),本波家族整体上移 rc.3,内容即 rc.2 计划稿。)
15
+ (版本号说明:本包 0.2.0-rc.2 版本号已被 npm 发布流水线占用(重复利用被 409 拒绝:「Cannot stage previously published version」——曾发布后被撤回或曾被暂存,外部无法区分,结果相同:该号不可复用);为守住家族「同一版本线」纪律(check:release-groups),本波家族整体上移 rc.3,内容即 rc.2 计划稿。)
6
16
 
7
17
  - **persona 行适配宿主 0.2.1**:子 dsh patch 的 system-prompt 行不再引用 `{{cwd}}`——0.2.1 删除了该 prompt 变量,严格引用直接让模型请求失败(prod 3080 切到 0.2.1-rc.1 后的轮次失败即此);工作目录改由宿主 working-directory 服务作为必需用户上下文提供,该条款随之删除。合并的 `persona` 字段按 0.2.1 的拆分改写为 `personaPrefix`/`personaSuffix`。
8
18
  - **适配宿主 0.2.1(host-021)**:`engines.dsh` 声明为 `>=0.1.5-rc.1 <0.3.0-0`(声明即独占兼容裁决,未验证的宿主线安装期 fail-closed);invariant 伴随件随官方删除 invariants 体系退役。运行时与 peer 区间加宽——`@deepseek-ai/dsh-cmdline`、`@deepseek-ai/dsh-ptc-runtime-node` 等补齐 0.2.0 与 0.2.1 分支(semver 预发布只覆盖比较子点名的三元组,原 `^0.1.x-rc.x` 区间在严格 npm/pnpm 解析下不覆盖 0.2.x 宿主)。`dsh.compat.verifiedHost` 前移至 0.2.1-rc.1(host-021 波对 0.2.1-rc.1 树全量构建+测试绿)。
package/README.en.md CHANGED
@@ -2,111 +2,30 @@
2
2
 
3
3
  English | [中文](README.md)
4
4
 
5
- A headless dsh at the caller's beck and call: the session id is yours to name, it runs the task, prints the final answer on stdout, and exits — or, with `--serve`, stays resident and takes turn after turn.
5
+ The interface-less DSH behind 「本机 DSH 委派」 (Local DSH Delegation). You never install it yourself: it comes along automatically with the 「多Agent协作」 (multi-agent) suite or the Dev Suite.
6
6
 
7
- The local-agent family delegates tasks to a standalone dsh process and needs to continue the *same* sub-session across rounds — which requires the session id to belong to the caller, not to a runner that generates its own. This bundle is the sibling of the official `@deepseek-ai/dsh-headless`: the same direct-Agent composition over `dsh-base`, differing only in who owns the id — here it arrives as `--session-id` / `--resume`, and stdout never carries a parseable session marker. You never install or mount it yourself: the parent `@khorsheed/dsh-local-agent-dsh` provider auto-provisions its sub-profile at runtime.
7
+ ## What it is
8
8
 
9
- ## Features
9
+ A headless DSH composition: the caller names the session id, it runs the task, prints the final answer to stdout, and exits; with `--serve` it stays up and takes work round after round. `@khorsheed/dsh-local-agent-dsh` uses it to start "the second DSH" and to continue the same session in later rounds.
10
10
 
11
- - **Caller-supplied session id** — `--session-id <id>` creates a fresh session with exactly that id, `--resume <id>` continues it; the id travels as a launch flag, never parsed out of stdout.
12
- - **One-shot semantics** — drives the task to quiescence, flushes the log, prints the final assistant text on stdout; exit 0 on a `completed` turn, exit 1 otherwise (the error summary goes to stderr).
13
- - **Resident serve mode** — `--serve` switches the runner into a long-lived loop: `turn/start` takes a round, `session/event` streams every event as it lands, `session/assistant-stream` forwards the native assistant frames, `session/idle` closes the round after the log flush, `turn/interrupt` lands as an in-process `Agent.cancel` graceful interrupt (the process survives, the session stays continuable), and `shutdown` or stdin EOF exits gracefully. The wire contract lives in `src/wire.ts`.
14
- - **Per-launch model and reasoning effort** — `--model <provider/model>` overrides this launch's model selection and `--effort <value>` pins the native reasoning effort (validated against what the model advertises — an unrecognized value fails loud); absent either flag, the instance's own selection carries over byte for byte.
15
- - **Isolated session store** — sub-dsh sessions live under their own scoped `$DSH_HOME` and never appear in the parent instance's session list.
16
- - **Zero manual mounting** — the parent provider provisions the `headless-local-agent-dsh` sub-profile for you; nothing to mount by hand.
17
-
18
- ## Install
19
-
20
- This bundle is never installed or mounted on its own — it comes in transitively with the parent harness, and the parent provider provisions the sub-profile at runtime:
21
-
22
- ```sh
23
- dsh plugin --profile web add @khorsheed/dsh-local-agent @khorsheed/dsh-local-agent-dsh
24
- ```
25
-
26
- Restart the web instance to activate. Uninstalling goes through the parent package, and this bundle leaves with it:
27
-
28
- ```sh
29
- dsh plugin --profile web remove @khorsheed/dsh-local-agent-dsh
30
- ```
31
-
32
- There is nothing to uninstall separately — this package has no row in any interactive profile, and must never have one (see Known Limitations).
33
-
34
- ## Usage
35
-
36
- Normally the parent provider spawns this bundle; direct invocation works too:
37
-
38
- ```sh
39
- dsh --profile headless-local-agent-dsh --session-id 6ba7... "run the tests" # fresh
40
- dsh --profile headless-local-agent-dsh --resume 6ba7... "run the rest" # resume
41
- dsh --profile headless-local-agent-dsh --serve # resident live-driver mode (stdio wire)
42
- ```
43
-
44
- With neither flag the runner generates its own `session-<uuid>` id (the official headless behavior), so it stays a drop-in replacement for one-shot use. `--serve` takes neither a task nor session flags — turns and session ids all travel over the wire.
45
-
46
- ### `--model <provider/model>` and `--effort <value>`
47
-
48
- Overrides this launch's model selection:
11
+ The only difference from the official `@deepseek-ai/dsh-headless` is who owns the session id: here the caller passes it with `--session-id` / `--resume`, and the output never carries a session marker.
49
12
 
50
13
  ```sh
51
- dsh --profile headless-local-agent-dsh --model deepseek-official/deepseek-v4-pro --session-id 6ba7... "run the tests"
14
+ dsh --profile headless-local-agent-dsh --session-id 6ba7... "run the tests" # new
15
+ dsh --profile headless-local-agent-dsh --resume 6ba7... "run the rest" # continue
16
+ dsh --profile headless-local-agent-dsh --serve # resident
52
17
  ```
53
18
 
54
- - No `--model` means the sub-instance's own `agentDefaultModel` selection — byte for byte the behavior before the flag existed.
55
- - The value splits at the FIRST `/` into provider and model, so a model id containing one survives; a bare id (no `/`) names the model only and keeps the instance's provider. A leading or trailing slash is not a split point — that would produce an empty half the agent cannot route — so the whole value is the model.
56
- - Everything else on the selection is carried through: a model swap is not a config reset.
57
- - `--effort <value>` pins this launch's native reasoning effort: the value is validated against the effort list the selected model advertises, an unrecognized value fails loud, and an empty one is a usage error.
58
- - Both flags are orthogonal to both modes: a one-shot launch binds that round, and `--serve` binds EVERY session the resident process hosts — a runtime's model is a process fact, which is exactly why the parent refuses a per-delegation model on the live path.
19
+ ## Notes
59
20
 
60
- The parent `local-agent-dsh` provider supplies `--model` automatically: the harness's `model` plugin-config key, or one delegation's own `DelegationCallOptions.model`, both land as this flag.
21
+ - Don't add it to an interactive profile: its configuration is meant only for the sub-DSH, would leak into real sessions, and fails loudly at boot.
22
+ - Installing it directly with `dsh plugin add` mounts nothing; install it through `@khorsheed/dsh-local-agent-dsh`, which prepares the sub-profile for you.
61
23
 
62
24
  ## Compatibility
63
25
 
64
- - npm release line (`@deepseek-ai/dsh@0.1.5-rc.1`): ✅ full — the first release line (0.1.0-rc.6) is already based on the 0.1.5-rc.1 API surface, full build+test green; minHost is 0.1.5-rc.1, and older hosts have no earlier release line to fall back to.
65
- - source line (deepseek-harness master): ✅ (verifiedHost: 0.2.1-rc.1) — host-021 wave: full build+test green against the 0.2.1-rc.1 tree; the family delegation migrated to `ctx.subagents.startActivation` (managed activation), and run.id is now unique per execution.
66
-
67
- ## Known Limitations
68
-
69
- - **Never add this bundle to an interactive profile's `bundles`** — its sub-profile-only patch rows (persona override, `hmr` off, `tools` mode, the `ptc-runtime` insert, the member-bridge MCP) collide with an interactive composition and leak overrides into real user sessions. The package **declares no `dsh.bundle`** (the manifest carries `dsh.composition.component: "sub-profile-patch"`), so a hand-added bundles row fails loud at boot in `loadProfile` — that is the gate.
70
- - **`dsh plugin add` on this package mounts nothing, and need not** — the package used to declare `dsh.bundle`, and reconcilePlugins auto-mounts every `dsh.bundle`-declaring direct dependency into the composition's layer stack: both the 2026-08-23 P0 (duplicate `code-runtime` in the prod web profile) and its 2026-09-03 G3 recurrence were exactly that mount. The declaration is gone (T6); installing the package directly now earns only a "plain dependency" warning and mounts nothing — a **transitive** install through `@khorsheed/dsh-local-agent-dsh` suffices, and the parent provider auto-provisions.
71
- - No other `local-agent` family bundles belong in this composition — nothing here spawns another dsh.
72
- - Patch edits must be boot-verified before landing (`dsh preflight` or one real sub-dsh launch): the `!!js` tag is scalar-only, and a mistagged collection fails at profile boot.
73
-
74
- ## How it works
75
-
76
- <details>
77
- <summary>Internals (click to expand)</summary>
78
-
79
- The local-agent family needs to continue the *same* dsh conversation across delegations: the parent provider generates one uuid and passes it on every round — the fresh round creates the sub-dsh session with it, each resume round continues exactly that session.
80
-
81
- The bundle's patch rides over `dsh-base` in its own profile (e.g. `headless-local-agent-dsh`); at its core are two insert rows (excerpted from the package's `cordis.patch.yml`):
82
-
83
- ```yaml
84
- - insert:
85
- - id: local-agent-dsh-headless-startup
86
- name: '@khorsheed/dsh-local-agent-dsh-headless/startup'
87
-
88
- - id: local-agent-dsh-headless-runner
89
- name: '@khorsheed/dsh-local-agent-dsh-headless'
90
- inject: [localAgentDshHeadlessStartup]
91
- config:
92
- task: !!js ctx.localAgentDshHeadlessStartup.task
93
- sessionId: !!js ctx.localAgentDshHeadlessStartup.sessionId
94
- resumeSessionId: !!js ctx.localAgentDshHeadlessStartup.resumeSessionId
95
- serve: !!js ctx.localAgentDshHeadlessStartup.serve ?? false
96
- model: !!js ctx.localAgentDshHeadlessStartup.model
97
- effort: !!js ctx.localAgentDshHeadlessStartup.effort
98
- ```
99
-
100
- The startup provider parses the task positional plus the mutually exclusive `--session-id` / `--resume` flags (or `--serve`) and publishes the invocation; the runner waits for the whole loader tree to settle before touching an agent — one-shot mode creates or resumes the session via `agents.create` / `agents.resume`, drives the task, prints the final assistant text, and exits, while serve mode switches to the resident wire loop (`src/serve.ts`). The sub-profile itself is provisioned at runtime by the parent provider (`provisionDshSubProfile`) under the dsh harness's scoped home: the manifest lists only `@deepseek-ai/dsh-base`, this bundle's patch is copied byte-for-byte from the package's `cordis.patch.yml` into that profile's own patch layer (the bundle declares no `dsh.bundle`, see Known Limitations), plus one symlink resolving this bundle for the loader's insert rows.
101
-
102
- **The wire contract (`src/wire.ts`).** A family-internal newline-delimited JSON-RPC 2.0 over stdio, framed exactly like the official `JsonRpcLineTransport` (`@deepseek-ai/dsh-sdk-protocol`), so a future swap to the official SDK server stays mechanical — the official server is not usable today: its wire has no turn-level interrupt (`initialize` / `session/prompt` / `shutdown` only), and graceful interrupt is the live driver's reason to exist (upstream seam registry: the SDK-wire-interrupt entry). Every request is a quick acknowledgement (`turn/start` answers "accepted"), and the round's events and terminal outcome flow back as notifications, so a long turn never holds a request open. `turn/start` carries the parent's round number and echoes it on every `session/event` / `session/idle` of that round, so the parent can drop a cancelled round's late unwind instead of letting it mis-settle the next round; `session/idle` fires only AFTER the session log is flushed, so a file-based reconciliation right after it sees the complete round. The handshake (`initialize`) reports the server name and protocol version (currently 1); `session/prepare` lets the parent validate the resident process's bound model and reasoning effort before starting a round — no session created, nothing generated. One resident process serves one member.
103
-
104
- **Preset roster (optional).** When the sub-profile's patch carries an extra `@deepseek-ai/dsh-agent-presets` layer — written by the parent provider when the scope declares a preset — the agent loader joins it inside `setup`, before the agent is published, so the preset's tools and prompt sections exist before the first prompt assembly. Without the layer it does nothing: the model-facing rows stay in the host plane and the agent reads the global layer, byte for byte the behavior before this existed. A roster that REFUSES (unknown preset id, broken composition) is not degraded past: the mount rejects, the agent creation rolls back, and the launch fails naming the preset — a sub-dsh that silently ran the global layer after being told to run preset X would attribute the round to a capability face it never had.
105
-
106
- **Exports.** `.` exports the runner plugin body (`apply` / `inject` / `Config`); `/startup` exports the command-line provider (the source of the `localAgentDshHeadlessStartup` service); `/wire` exports the wire contract types and protocol constants.
107
-
108
- </details>
26
+ Needs DSH 0.1.5-rc.1 or later; verified on 0.2.1-rc.1.
109
27
 
110
- ## Development
28
+ ## For developers
111
29
 
112
- Part of the [dsh-plugins](https://github.com/Khorsheed/dsh-plugins) monorepo (`packages/local-agent-dsh-headless`). Issues and contributions welcome there.
30
+ - Command-line flags (`--model` / `--effort`), the resident-mode wire protocol, and what the sub-profile contains and why are in the [full reference](https://github.com/Khorsheed/dsh-plugins/blob/main/packages/local-agent-dsh-headless/docs/reference.en.md); the protocol is defined in `src/wire.ts`.
31
+ - Part of [dsh-plugins](https://github.com/Khorsheed/dsh-plugins); open issues and contributions there. Changes: [CHANGELOG.md](CHANGELOG.md).
package/README.i18n.yaml CHANGED
@@ -2,5 +2,5 @@
2
2
  # last confirmed-consistent state. Both languages carry equal authority; after
3
3
  # editing either side, bring the other along and re-record with:
4
4
  # pnpm run verify-translation-pairing --write packages/local-agent-dsh-headless/README.en.md
5
- packages/local-agent-dsh-headless/README.en.md: 870b188762ff71253b0d1e2e473e8a78c1ed228d
6
- packages/local-agent-dsh-headless/README.md: 0ae196a948c3d01ea55b63019f53a222d228e446
5
+ packages/local-agent-dsh-headless/README.en.md: 2739a6c771c9157d39a04b45ec61d1a310550e87
6
+ packages/local-agent-dsh-headless/README.md: f9d6ca8d42b83ae1187204b48d68cc482c4d634a
package/README.md CHANGED
@@ -2,111 +2,30 @@
2
2
 
3
3
  [English](README.en.md) | 中文
4
4
 
5
- 一个随叫随到的无头 dsh:会话 id 由调用方点名,跑完任务把最终回答打到 stdout 就退出;加 `--serve` 则常驻下来,一轮一轮地接活。
5
+ 「本机 DSH 委派」背后那个不带界面的 DSH。你不需要单独安装它:装「多Agent协作」或开发套件时它会自动跟着装好。
6
6
 
7
- local-agent 家族要把任务委派给一个独立的 dsh 进程,还要跨多轮续接**同一个**子会话——这要求会话 id 归调用方所有,而不是由 runner 自己生成。本 bundle 是官方 `@deepseek-ai/dsh-headless` 的兄弟版本:同样是 `dsh-base` 之上直驱 Agent 的无头组合,唯一区别就是 id 归谁——这里 id 以 `--session-id` / `--resume` 传入,stdout 永不携带可解析的会话标记。你永远不需要自己安装或挂载它:父侧 `@khorsheed/dsh-local-agent-dsh` provider 会在运行时把子 profile 自动 provision 好。
7
+ ## 它是什么
8
8
 
9
- ## 特性
9
+ 一个无头的 DSH 组合:调用方给定会话编号,它把任务跑完、把最终回答打到标准输出就退出;加 `--serve` 则常驻下来,一轮一轮接活。`@khorsheed/dsh-local-agent-dsh` 用它来启动「另一个 DSH」,并在之后的轮次里续上同一个会话。
10
10
 
11
- - **调用方点名的会话 id**——`--session-id <id>` 以该确切 id 新建会话,`--resume <id>` 续接它;id 走启动参数,绝不从 stdout 解析。
12
- - **一次性语义**——驱动任务到落定、落盘 flush、把最终助手文本打上 stdout;轮次 `completed` 退出 0,其余一律退出 1(错误摘要走 stderr)。
13
- - **常驻 serve 模式**——`--serve` 把 runner 切成长驻循环:`turn/start` 接一轮、`session/event` 逐事件推流、`session/assistant-stream` 转发原生 assistant 帧、`session/idle` 在日志落盘之后关轮、`turn/interrupt` 落地为进程内 `Agent.cancel` 优雅中断(进程不死、会话可续)、`shutdown` 或 stdin EOF 优雅退出。wire 契约见 `src/wire.ts`。
14
- - **按启动点名模型与推理强度**——`--model <provider/model>` 覆盖本次启动的模型选择,`--effort <value>` 指定原生推理强度(按模型自报的列表校验,不认即响亮失败);不给就逐字节沿用实例自己的选择。
15
- - **隔离的会话存储**——子 dsh 会话住在自己的 scoped `$DSH_HOME` 下,绝不出现在父实例的会话列表里。
16
- - **零手工挂载**——`headless-local-agent-dsh` 子 profile 由父级 provider 自动 provision,任何地方都不需要手工挂载。
17
-
18
- ## 安装
19
-
20
- 本 bundle 从不单独安装或挂载——它随父级 harness 一起进门(传递依赖),父级 provider 在运行时 provision 子 profile:
21
-
22
- ```sh
23
- dsh plugin --profile web add @khorsheed/dsh-local-agent @khorsheed/dsh-local-agent-dsh
24
- ```
25
-
26
- 重启 web 实例后生效。卸载走父级包,本 bundle 随之离开:
27
-
28
- ```sh
29
- dsh plugin --profile web remove @khorsheed/dsh-local-agent-dsh
30
- ```
31
-
32
- 没有需要单独卸载的东西——本包在任何交互式 profile 里都没有行,也不许有(见已知限制)。
33
-
34
- ## 用法
35
-
36
- 通常由父级 provider 拉起本 bundle;直接调用也可以:
11
+ 和官方的 `@deepseek-ai/dsh-headless` 唯一的区别是会话编号归谁:这里由调用方用 `--session-id` / `--resume` 传入,输出里从不带会话标记。
37
12
 
38
13
  ```sh
39
14
  dsh --profile headless-local-agent-dsh --session-id 6ba7... "run the tests" # 新建
40
15
  dsh --profile headless-local-agent-dsh --resume 6ba7... "run the rest" # 续接
41
- dsh --profile headless-local-agent-dsh --serve # 常驻 live-driver 模式(stdio wire)
42
- ```
43
-
44
- 两个 flag 都不给时,runner 自生成 `session-<uuid>` id(官方 headless 行为),因此仍是一次性使用的即插即用替代。`--serve` 不接受 task 与 session flag——turn 与会话 id 全部走 wire。
45
-
46
- ### `--model <provider/model>` 与 `--effort <value>`
47
-
48
- 覆盖本次启动的模型选择:
49
-
50
- ```sh
51
- dsh --profile headless-local-agent-dsh --model deepseek-official/deepseek-v4-pro --session-id 6ba7... "run the tests"
16
+ dsh --profile headless-local-agent-dsh --serve # 常驻
52
17
  ```
53
18
 
54
- - 不给 `--model` = 用子实例自己 `agentDefaultModel` 的当前选择,与这个 flag 出现之前逐字节相同。
55
- - 按**第一个** `/` 切分 provider 与 model,所以模型 id 里再带斜杠也不会被切坏;只写模型名(没有 `/`)则只换模型、沿用实例的 provider。开头或结尾的斜杠不算切分点——那会切出一个空的一半,agent 路由不了——整个值当模型名。
56
- - 选择的其余部分原样带过:换模型不是重置配置。
57
- - `--effort <value>` 指定本次启动的原生推理强度:值按所选模型自报的 effort 列表校验,模型不认就响亮拒绝,空值是用法错误。
58
- - 两个 flag 与两种模式都正交:一次性轮次绑定这一轮,`--serve` 绑定这个常驻进程托管的**每一个**会话——runtime 的模型是进程事实,这正是父侧拒绝在长驻路径上按次给模型的原因。
19
+ ## 注意
59
20
 
60
- 父侧 `local-agent-dsh` provider 自动带上 `--model`:harness 的 `model` 插件配置键、或某一次委派自带的 `DelegationCallOptions.model`,都落成这个 flag。
21
+ - 不要把它加进交互式 profile:它的配置专供子 DSH 使用,混进来会影响真实会话,启动时会直接报错。
22
+ - 用 `dsh plugin add` 直接装它不会挂载任何东西;经 `@khorsheed/dsh-local-agent-dsh` 间接安装即可,子 profile 会自动准备好。
61
23
 
62
24
  ## Compatibility
63
25
 
64
- - npm 发布线(`@deepseek-ai/dsh@0.1.5-rc.1`):✅ 完整——首个发布线(0.1.0-rc.6)即以 0.1.5-rc.1 API 面为基线,全量构建测试通过;minHost 即 0.1.5-rc.1,更旧的宿主没有可回退的旧发布线。
65
- - 源码线(deepseek-harness master):✅(verifiedHost: 0.2.1-rc.1)——host-021 波对 0.2.1-rc.1 树全量 build+test 绿;家族委派迁移到 `ctx.subagents.startActivation`(受管 activation),run.id 改为每次执行唯一。
66
-
67
- ## 已知限制
68
-
69
- - **绝不要把这个 bundle 加进交互式 profile 的 `bundles`**——它的子 profile 专属 patch 行(persona 覆盖、`hmr` 禁用、`tools` mode、`ptc-runtime` insert、member-bridge MCP)会撞交互式组合,并把覆盖泄漏进真实用户会话。本包**不声明 `dsh.bundle`**(manifest 里标的是 `dsh.composition.component: "sub-profile-patch"`),手工加的 bundles 行在 boot 的 `loadProfile` 处直接 fail loud,这正是一道闸。
70
- - **`dsh plugin add` 它本身不挂载任何东西,也无需如此**——本包曾声明 `dsh.bundle`,而 reconcilePlugins 会把声明该字段的直接依赖自动挂进组合的 layer 栈:2026-08-23 P0(prod web profile 撞 duplicate `code-runtime`)与 2026-09-03 G3 复发都是这一挂。声明已撤(T6),装成直接依赖只会得到一条 "plain dependency" 警告、不挂载——经 `@khorsheed/dsh-local-agent-dsh` **传递**安装即可,父级 provider 会自动 provision。
71
- - 此 composition 里不装任何其他 `local-agent` 家族 bundle——这里没有任何东西再 spawn 一个 dsh。
72
- - patch 改动落地前必须做启动级验证(`dsh preflight` 或真实拉起一次子 dsh):`!!js` 标签只支持标量,误标集合会在 profile 启动时直接失败。
73
-
74
- ## 实现原理
75
-
76
- <details>
77
- <summary>内部结构(点击展开)</summary>
78
-
79
- local-agent 家族需要在多次委派之间续接**同一个** dsh 对话:父级 provider 生成一个 uuid,并在每一轮传同一个值——fresh 轮用它创建子 dsh 会话,之后的 resume 轮恰好续接该会话。
80
-
81
- 本 bundle 的 patch 在自己的 profile 之上叠加 `dsh-base`(如 `headless-local-agent-dsh`),核心是两行 insert(节选自包内 `cordis.patch.yml`):
82
-
83
- ```yaml
84
- - insert:
85
- - id: local-agent-dsh-headless-startup
86
- name: '@khorsheed/dsh-local-agent-dsh-headless/startup'
87
-
88
- - id: local-agent-dsh-headless-runner
89
- name: '@khorsheed/dsh-local-agent-dsh-headless'
90
- inject: [localAgentDshHeadlessStartup]
91
- config:
92
- task: !!js ctx.localAgentDshHeadlessStartup.task
93
- sessionId: !!js ctx.localAgentDshHeadlessStartup.sessionId
94
- resumeSessionId: !!js ctx.localAgentDshHeadlessStartup.resumeSessionId
95
- serve: !!js ctx.localAgentDshHeadlessStartup.serve ?? false
96
- model: !!js ctx.localAgentDshHeadlessStartup.model
97
- effort: !!js ctx.localAgentDshHeadlessStartup.effort
98
- ```
99
-
100
- startup provider 解析 task 位置参数与互斥的 `--session-id` / `--resume`(或 `--serve`)并发布调用;runner 等整棵 loader 树落定之后才动 agent——一次性模式经 `agents.create` / `agents.resume` 创建或续接该会话、驱动任务、打印最终助手文本并退出,serve 模式转入常驻 wire 循环(`src/serve.ts`)。子 profile 本身由父级 provider 在运行时 provision(`provisionDshSubProfile`)到 dsh harness 的 scoped home 下:manifest 只列 `@deepseek-ai/dsh-base`,本 bundle 的 patch 从包内 `cordis.patch.yml` 逐字节拷贝为该 profile 自己的 patch 层(本包不声明 `dsh.bundle`,见已知限制),另加一条解析本 bundle 的符号链接供 loader 解析 insert 行。
101
-
102
- **wire 契约(`src/wire.ts`)。** 家族内部的行分隔 JSON-RPC 2.0 over stdio,帧格式与官方 `JsonRpcLineTransport`(`@deepseek-ai/dsh-sdk-protocol`)完全一致,将来换成官方 SDK server 只是机械替换——官方 server 今天用不了:它的 wire 没有轮次级中断(只有 `initialize` / `session/prompt` / `shutdown`),而优雅中断正是 live driver 存在的理由(上游 seam 登记:SDK-wire-interrupt 条目)。所有请求都是快速应答(`turn/start` 应答即「已受理」),轮次的事件与结局全部以通知回流,长轮永远不占着一条请求。`turn/start` 带上父侧的轮次号,并在本轮每个 `session/event` / `session/idle` 通知上回显,父侧借此丢弃被取消轮次的迟到收尾,不让它误 settle 下一轮;`session/idle` 在会话日志落盘**之后**才发出,父侧随即做的文件对账看到的就是完整一轮。握手(`initialize`)回报 server 名与协议版本(当前 1);`session/prepare` 让父侧在起轮之前校验常驻进程绑定的模型与推理强度——不建会话、不生成。一个常驻进程服务一个成员。
103
-
104
- **preset roster(可选)。** 如果子 profile 的 patch 多带一层 `@deepseek-ai/dsh-agent-presets`(父级 provider 在这个 scope 声明了 preset 时写进去的),agent loader 会在 `setup` 里 join 它——在 agent 发布之前,因此 preset 的工具与提示词片段在第一次组装提示词之前就在位。没有这一层就什么也不做,模型可见的行留在宿主面、agent 从全局层读,与本行为出现之前逐字节相同。roster **拒绝**(preset id 不存在、composition 坏了)不会被降级绕过:mount 抛错,agent 创建回滚,启动带着 preset 名失败——一个被要求跑 preset X 却悄悄跑了全局层的子 dsh,会把这一轮归到它从没有过的能力面上。
105
-
106
- **导出。** `.` 导出 runner 插件本体(`apply` / `inject` / `Config`);`/startup` 导出命令行 provider(`localAgentDshHeadlessStartup` 服务的来源);`/wire` 导出 wire 契约类型与协议常量。
107
-
108
- </details>
26
+ 需要 DSH 0.1.5-rc.1 或更新版本,已在 0.2.1-rc.1 上验证。
109
27
 
110
- ## 开发
28
+ ## 给开发者
111
29
 
112
- 隶属 [dsh-plugins](https://github.com/Khorsheed/dsh-plugins) monorepo(`packages/local-agent-dsh-headless`)。问题与贡献请移步该仓库。
30
+ - 命令行参数(`--model` / `--effort`)、常驻模式的通信协议、子 profile 的组成与限制,都在[完整参考](https://github.com/Khorsheed/dsh-plugins/blob/main/packages/local-agent-dsh-headless/docs/reference.md)里;通信协议的定义在 `src/wire.ts`。
31
+ - 隶属 [dsh-plugins](https://github.com/Khorsheed/dsh-plugins),问题与贡献请到该仓库;变更见 [CHANGELOG.md](CHANGELOG.md)。
package/package.json CHANGED
@@ -8,7 +8,7 @@
8
8
  "local-agent",
9
9
  "headless"
10
10
  ],
11
- "version": "0.2.0-rc.3",
11
+ "version": "0.2.1",
12
12
  "type": "module",
13
13
  "main": "lib/index.js",
14
14
  "types": "lib/types/index.d.ts",