@deepseek-ai/dsh-subagent-acp 0.2.0-rc.2 → 0.2.1-alpha.2

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/README.i18n.yaml CHANGED
@@ -21,8 +21,8 @@
21
21
  en: 56946ab7637bb8f9
22
22
  zh: c54c4da98092dd9c
23
23
  /deepseek-ai-dsh-subagent-acp/use-this-package/configuration:
24
- en: 6f5fdf9a955841ce
25
- zh: 7193a3c07fc54199
24
+ en: 8d725f040ad14301
25
+ zh: 379664b42a38b3e5
26
26
  /deepseek-ai-dsh-subagent-acp/use-this-package/what-you-get:
27
27
  en: 0a809bd7b8cebddc
28
28
  zh: e2c1edf8f485e8f8
@@ -39,8 +39,8 @@
39
39
  en: c98e6bcd784bbf8a
40
40
  zh: b96218f6e639755c
41
41
  /deepseek-ai-dsh-subagent-acp/understand-the-implementation/start-and-ownership-flow:
42
- en: c0f7f78468f84c3b
43
- zh: c9cb417d84d868ce
42
+ en: 4a857c21457f917d
43
+ zh: ef980a48deb2fca0
44
44
  /deepseek-ai-dsh-subagent-acp/understand-the-implementation/stop-reason-mapping:
45
45
  en: 88bb2b62c6e2f114
46
46
  zh: f6b720ca8ce90343
@@ -69,11 +69,11 @@
69
69
  en: 75d061c2151624a7
70
70
  zh: 71e58e020b60ff90
71
71
  /deepseek-ai-dsh-subagent-acp/model-experience/parent-tool-result-indirectly/what-the-model-sees:
72
- en: 6779463ffe1c724c
73
- zh: 9797d5910039fd3a
72
+ en: 5e4e3efe7517f782
73
+ zh: 0465e164583f6bc3
74
74
  /deepseek-ai-dsh-subagent-acp/model-experience/parent-tool-result-indirectly/token-effect:
75
- en: 600ac97cdd163650
76
- zh: cd26bb541f56699b
75
+ en: 4fae54e2ff0c4874
76
+ zh: 1574bfe874f66a1f
77
77
  /deepseek-ai-dsh-subagent-acp/model-experience/parent-tool-result-indirectly/kv-cache-effect:
78
78
  en: f5d5f97a153f7c70
79
79
  zh: 3d6bc05b9eadfbc2
@@ -81,5 +81,5 @@
81
81
  en: 53a0e7fb49fe6e23
82
82
  zh: 51096d4eb51fb4ba
83
83
  /deepseek-ai-dsh-subagent-acp/known-limitations-and-deferred-work/dev-note:
84
- en: 5f1add5d61924059
85
- zh: ab32b1c9d3433681
84
+ en: d8323b3200db6ac9
85
+ zh: f8a5273b887b9552
package/README.md CHANGED
@@ -38,7 +38,6 @@ Choose this backend when the child must run with its own runtime, model, and too
38
38
  | `providerName` | `acp` | Registry name on `ctx.subagents` |
39
39
  | `command` | required | Executable spawned for each run (the child ACP agent) |
40
40
  | `args` | `[]` | Command arguments |
41
- | `cwd` | parent session cwd | Working-directory override for the child process and its ACP session |
42
41
  | `permission` | `reject` | Auto-answer permission requests by rejecting, or choosing the first `allow_once` or `allow_always` option (`allow`) |
43
42
  | `env` | `{}` | Explicit child environment layered over the credential-scrubbed parent environment |
44
43
  | `disposeEofGraceMs` | `6000` | Grace after stdin EOF before platform termination |
@@ -91,7 +90,7 @@ This section explains how the backend drives a child over ACP and where the obse
91
90
 
92
91
  ### Start and ownership flow
93
92
 
94
- A start resolves the child's working directory (the configured `cwd` override, else the parent session's cwd), spawns the command through the subprocess seam, performs the ACP `initialize` and `newSession` handshake, and only then publishes the run. Fulfillment means a remote session is ready and ownership has transferred to the caller. Disposal is idempotent: it closes stdin and waits a configured grace for cooperative quiescence, then escalates through SIGTERM to SIGKILL and awaits whole-range exit. Cleanup failures remain observable as ordered safe facts and never claim quiescence.
93
+ Startup uses the manager-resolved child directory (the request override, else the parent's current effective directory), spawns the command through the subprocess seam, performs the ACP `initialize` and `newSession` handshake, and only then publishes the run. Fulfillment transfers the external run to the activation manager. The startup signal detaches after acceptance; the activation receipt cancels execution and awaits cleanup. Each activation runs one task and cannot receive follow-up input. Disposal is idempotent: it closes stdin and waits a configured grace for cooperative quiescence, then escalates through SIGTERM to SIGKILL and awaits whole-range exit. Cleanup failures remain observable as ordered safe facts and never claim quiescence.
95
94
 
96
95
  ### Stop-reason mapping
97
96
 
@@ -139,11 +138,11 @@ Independent of the parent request cache. Each ACP child can reuse only prefixes
139
138
 
140
139
  #### What the model sees
141
140
 
142
- Through `dsh-tool-subagent`, the parent receives only the child's final streamed assistant text or that consumer's exact stop-reason error, not intermediate messages or tool traffic. Non-completed results present the safe diagnostic before separately preserved partial assistant output. A request already cancelled before publication becomes exactly `Error: subagent request was aborted before the ACP child started`; another start failure contains only the fixed `Subagent failure (...)` line.
141
+ Through `dsh-tool-subagent`, the parent model first receives a child id, then a completion notice containing final or partial assistant text, the stop reason, and safe diagnostic. The parent Session independently retains the external task identity and complete terminal result. Intermediate messages and tool traffic do not enter the parent Session.
143
142
 
144
143
  #### Token effect
145
144
 
146
- Parent input grows only by the final result or error, which is data-dependent and retained until compaction. This provider adds no parent schema itself.
145
+ Parent input grows by the start acknowledgement and completion notice, whose size depends on the result data and persists until compaction. This provider adds no parent schema by itself.
147
146
 
148
147
  #### KV Cache effect
149
148
 
@@ -175,5 +174,3 @@ This Dev Note is working context for maintainers: open questions and undecided d
175
174
  - **Continuable ACP children** — would require persisting the remote session id and a per-child continuation advertisement.
176
175
 
177
176
  </details>
178
-
179
- **Runtime invariant:** No companion is published. This package exposes no independent event sequence or mutable data relation beyond contracts enforced at its owning seam.
package/README.zh.md CHANGED
@@ -38,7 +38,6 @@ kind: "package-reference"
38
38
  | `providerName` | `acp` | `ctx.subagents` 上的注册表名称 |
39
39
  | `command` | 必填 | 每次运行时 spawn 的可执行文件(子 ACP agent) |
40
40
  | `args` | `[]` | 命令参数 |
41
- | `cwd` | 父会话 cwd | 子进程及其 ACP 会话的工作目录覆盖值 |
42
41
  | `permission` | `reject` | 自动应答权限请求:拒绝,或选择第一个 `allow_once` 或 `allow_always` 选项(`allow`) |
43
42
  | `env` | `{}` | 叠加在已清理凭据的父环境之上的显式子环境 |
44
43
  | `disposeEofGraceMs` | `6000` | stdin EOF 之后、平台终止之前的宽限 |
@@ -91,7 +90,7 @@ spawn、初始化或新建会话失败会在发布前拒绝,通常先证明 ma
91
90
 
92
91
  ### 启动与所有权流程
93
92
 
94
- 一次启动先解析子 agent 的工作目录(配置的 `cwd` 覆盖值,否则取父会话 cwd),经子进程 seam spawn 命令,完成 ACP `initialize` 与 `newSession` 握手,然后才发布运行。兑现意味着远程会话已就绪、所有权已转移给调用方。dispose(资源释放)是幂等的:先关闭 stdin 并按配置的宽限等待协作式完全停稳,再经 SIGTERM 升级到 SIGKILL,并等待整个 managed range 退出。清理失败会作为有序的安全事实保持可观察,且绝不声称已经完全停稳。
93
+ 一次启动使用管理器解析的子 agent 工作目录(请求覆盖值,否则取父级当前有效目录),经子进程 seam spawn 命令,完成 ACP `initialize` 与 `newSession` 握手,然后才发布运行。兑现后,外部运行由 activation 管理器接管。任务接受后会解除启动信号的绑定;activation 凭据用于取消执行并等待清理。每个 activation 只运行一个任务,不能接收追加输入。dispose(资源释放)是幂等的:先关闭 stdin 并按配置的宽限等待协作式完全停稳,再经 SIGTERM 升级到 SIGKILL,并等待整个 managed range 退出。清理失败会作为有序的安全事实保持可观察,且绝不声称已经完全停稳。
95
94
 
96
95
  ### 停止原因映射
97
96
 
@@ -139,11 +138,11 @@ spawn、初始化或新建会话失败会在发布前拒绝,通常先证明 ma
139
138
 
140
139
  #### 模型看到什么
141
140
 
142
- 通过 `dsh-tool-subagent`,父级只接收子 agent 最终的流式 assistant 文本或该消费方给出的精确停止原因错误,不接收中间消息或工具流量。未完成的结果会先呈现安全诊断,再单独保留部分 assistant 输出。发布前已经取消的请求会精确变为 `Error: subagent request was aborted before the ACP child started`;其他启动失败只包含固定的 `Subagent failure (...)` 行。
141
+ 通过 `dsh-tool-subagent`,父模型先收到 child id,随后收到含最终或部分 assistant 文本、停止原因与安全诊断的完成通知。父会话独立保存外部任务身份和完整终态结果。中间消息与工具通信不进入父会话。
143
142
 
144
143
  #### Token 影响
145
144
 
146
- 父级输入只增加最终结果或错误,其内容依赖数据,并保留到压缩(compaction)为止。本提供方自身不会添加父级 schema。
145
+ 父级输入增加启动确认与完成通知,内容大小取决于数据,并保留到压缩(compaction)为止。本提供方自身不会向父级添加任何 schema。
147
146
 
148
147
  #### KV Cache 影响
149
148
 
@@ -175,5 +174,3 @@ spawn、初始化或新建会话失败会在发布前拒绝,通常先证明 ma
175
174
  - **可继续执行的 ACP 子 agent**——需要持久化远程会话 id,并为每个子 agent 声明继续执行能力。
176
175
 
177
176
  </details>
178
-
179
- **运行时不变式:** 不发布伴生入口。本包没有独立事件序列或可变数据关系,相关约定在所属 seam 强制执行。
package/lib/index.js CHANGED
@@ -1,12 +1,10 @@
1
- import { accessSync, constants, statSync } from "node:fs";
2
- import { isAbsolute, resolve } from "node:path";
3
1
  import z from "@deepseek-ai/schemastery";
2
+ import { AssistantOutputFold, assertUsableCwd, settleRunResult, subprocessRunHandle } from "@deepseek-ai/dsh-subagent";
4
3
  import { MAX_TIMER_DELAY_MS } from "@deepseek-ai/dsh-timeout";
5
4
  import { randomUUID } from "node:crypto";
6
5
  import { Readable, Writable } from "node:stream";
7
6
  import { PROTOCOL_VERSION, client, methods, ndJsonStream } from "@agentclientprotocol/sdk";
8
7
  import { brandString } from "@deepseek-ai/dsh-brand";
9
- import { AssistantOutputFold, settleRunResult, subprocessRunHandle } from "@deepseek-ai/dsh-subagent";
10
8
  //#region lib/types/run.js
11
9
  /**
12
10
  * Fresh-process ACP subagent client. Drives one child session and owns cancellation and
@@ -434,8 +432,8 @@ async function startAcpRun(request, spec) {
434
432
  /**
435
433
  * Out-of-process ACP subagent backend. Each child has its own process, session, model, and
436
434
  * tools, so it shares no Cordis context and advertises no parent-enforced start capabilities;
437
- * the ONE thing it reads off `request.parent` is the session's workspace cwd (see
438
- * {@link resolveCwd}). This plugin uses named exports only; a default would hide its
435
+ * the subagent service supplies the captured child working directory. This plugin uses
436
+ * named exports only; a default would hide its
439
437
  * loader metadata (see `docs/postmortem/0001-acp-default-export-drops-inject.md`).
440
438
  * @module @deepseek-ai/dsh-subagent-acp
441
439
  */
@@ -445,7 +443,6 @@ const Config = z.object({
445
443
  providerName: z.string().default("acp"),
446
444
  command: z.string().required(),
447
445
  args: z.array(z.string()).default([]),
448
- cwd: z.string(),
449
446
  permission: z.union(["allow", "reject"]).default("reject"),
450
447
  env: z.dict(z.string()).default({}),
451
448
  disposeEofGraceMs: z.number().default(DEFAULT_DISPOSE_EOF_GRACE_MS),
@@ -456,48 +453,6 @@ function assertPositiveFinite(name, value) {
456
453
  if (!Number.isFinite(value) || value <= 0 || value > MAX_TIMER_DELAY_MS) throw new Error(`subagent-acp: ${name} must be a positive finite number no greater than ${MAX_TIMER_DELAY_MS}`);
457
454
  }
458
455
  /**
459
- * Whether `path` names an existing directory the harness can ENTER. The
460
- * search-permission probe matters: `statSync().isDirectory()` is true for a
461
- * mode-600 directory, but a subprocess cwd needs `X_OK` or spawn fails EACCES.
462
- */
463
- function isDirectory(path) {
464
- try {
465
- if (!statSync(path).isDirectory()) return false;
466
- accessSync(path, constants.X_OK);
467
- return true;
468
- } catch {
469
- return false;
470
- }
471
- }
472
- /**
473
- * Assert `cwd` can actually host the child: absolute (it doubles as the ACP
474
- * session workspace, and a relative path would be re-anchored to the server
475
- * process's launch directory) and an existing directory (fail here, before the
476
- * process boundary, instead of as an ambiguous spawn ENOENT).
477
- * @param label - which source supplied the value, for the diagnostic.
478
- * @param cwd - the candidate working directory.
479
- * @returns `cwd`, validated.
480
- */
481
- function assertUsableCwd(label, cwd) {
482
- if (!isAbsolute(cwd)) throw new Error(`subagent-acp: ${label} must be an absolute path: ${cwd}`);
483
- if (!isDirectory(cwd)) throw new Error(`subagent-acp: ${label} is not an accessible directory: ${cwd}`);
484
- return cwd;
485
- }
486
- /**
487
- * Resolve the child's working directory: the deployment `cwd` override when
488
- * configured (already validated at load), else the parent session's workspace
489
- * cwd (validated here, its earliest resolvable point). Fails loud when neither
490
- * exists — falling back to the harness process cwd would silently bind the
491
- * child to the server's launch directory instead of the delegating session's
492
- * workspace (one server process serves many sessions, each with its own cwd).
493
- */
494
- function resolveCwd(configured, request) {
495
- if (configured !== void 0) return configured;
496
- const parentCwd = request.parent.session.header.cwd;
497
- if (parentCwd === void 0) throw new Error("subagent-acp: no working directory for the child — configure `cwd` or delegate from a parent session that has one");
498
- return assertUsableCwd("parent session cwd", parentCwd);
499
- }
500
- /**
501
456
  * The ACP provider. Advertises NO start-time capabilities: an out-of-process
502
457
  * child cannot honor `agentOptions`/`outputSchema`/`maxDepth`/`toolFilter`/
503
458
  * `persona` (the service rejects a request needing any before `start` runs).
@@ -523,7 +478,7 @@ var AcpProvider = class {
523
478
  if (request.signal.aborted) throw new Error("subagent request was aborted before the ACP child started");
524
479
  let cwd;
525
480
  try {
526
- cwd = resolveCwd(this.config.cwd, request);
481
+ cwd = assertUsableCwd("subagent-acp", "child cwd", request.cwd);
527
482
  } catch (error) {
528
483
  const failure = acpConfigurationFailure(error);
529
484
  this.ctx.logger.warn(`subagent-acp "${this.name}": child start failed: %o`, error);
@@ -548,12 +503,7 @@ function apply(ctx, config) {
548
503
  const resolved = config;
549
504
  assertPositiveFinite("disposeEofGraceMs", resolved.disposeEofGraceMs);
550
505
  assertPositiveFinite("disposeGraceMs", resolved.disposeGraceMs);
551
- if (resolved.cwd === "") throw new Error("subagent-acp: config cwd must not be empty — omit the key to inherit the parent session cwd");
552
- const validated = resolved.cwd === void 0 ? resolved : {
553
- ...resolved,
554
- cwd: assertUsableCwd("config cwd", resolve(resolved.cwd))
555
- };
556
- ctx.subagents.registerProvider(new AcpProvider(validated.providerName, ctx, validated));
506
+ ctx.subagents.registerProvider(new AcpProvider(resolved.providerName, ctx, resolved));
557
507
  }
558
508
  //#endregion
559
509
  export { Config, apply, inject, name };
@@ -1,8 +1,8 @@
1
1
  /**
2
2
  * Out-of-process ACP subagent backend. Each child has its own process, session, model, and
3
3
  * tools, so it shares no Cordis context and advertises no parent-enforced start capabilities;
4
- * the ONE thing it reads off `request.parent` is the session's workspace cwd (see
5
- * {@link resolveCwd}). This plugin uses named exports only; a default would hide its
4
+ * the subagent service supplies the captured child working directory. This plugin uses
5
+ * named exports only; a default would hide its
6
6
  * loader metadata (see `docs/postmortem/0001-acp-default-export-drops-inject.md`).
7
7
  * @module @deepseek-ai/dsh-subagent-acp
8
8
  */
@@ -19,14 +19,6 @@ export interface Config {
19
19
  command: string;
20
20
  /** Arguments passed to {@link command}. */
21
21
  args: string[];
22
- /**
23
- * Working directory override for the child process and its ACP session.
24
- * Must be non-empty; a relative path resolves against the harness launch
25
- * directory at load, and the result must be an existing directory. When
26
- * omitted, each child inherits its delegating parent session's cwd — and
27
- * starting one from a parent session that has no cwd fails.
28
- */
29
- cwd?: string;
30
22
  /**
31
23
  * How to auto-answer the child's `session/request_permission` prompts:
32
24
  * `reject` (default — decline every prompt) or `allow` (approve via the first
@@ -18,8 +18,7 @@ export interface AcpRunSpec {
18
18
  args: string[];
19
19
  /**
20
20
  * Absolute working directory for the child process AND its ACP session
21
- * `cwd`. The provider resolves it before this spec exists: config override,
22
- * else the delegating parent session's workspace.
21
+ * `cwd`, selected before provider startup.
23
22
  */
24
23
  cwd: string;
25
24
  /** How to auto-answer the child's permission prompts. */
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@deepseek-ai/dsh-subagent-acp",
3
3
  "description": "Out-of-process ACP subagent backend: drives a child agent in a spawned subprocess over the Agent Client Protocol",
4
- "version": "0.2.0-rc.2",
4
+ "version": "0.2.1-alpha.2",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
@@ -27,34 +27,34 @@
27
27
  ],
28
28
  "license": "MIT",
29
29
  "peerDependencies": {
30
- "@deepseek-ai/dsh-agent": "0.2.0-rc.2",
31
- "@deepseek-ai/dsh-llm": "0.2.0-rc.2",
32
- "@deepseek-ai/dsh-session": "0.2.0-rc.2",
33
- "@deepseek-ai/dsh-subagent": "0.2.0-rc.2",
34
- "@deepseek-ai/cordis": "~4.0.4",
35
- "@deepseek-ai/dsh-subprocess": "0.2.0-rc.2",
36
- "@deepseek-ai/dsh-timeout": "0.2.0-rc.2"
30
+ "@deepseek-ai/cordis": "~4.0.5-alpha.1",
31
+ "@deepseek-ai/dsh-agent": "0.2.1-alpha.2",
32
+ "@deepseek-ai/dsh-llm": "0.2.1-alpha.2",
33
+ "@deepseek-ai/dsh-session": "0.2.1-alpha.2",
34
+ "@deepseek-ai/dsh-subagent": "0.2.1-alpha.2",
35
+ "@deepseek-ai/dsh-subprocess": "0.2.1-alpha.2",
36
+ "@deepseek-ai/dsh-timeout": "0.2.1-alpha.2"
37
37
  },
38
38
  "dependencies": {
39
39
  "@agentclientprotocol/sdk": "1.4.0",
40
- "@deepseek-ai/schemastery": "~3.18.4",
41
- "@deepseek-ai/dsh-brand": "0.2.0-rc.2"
40
+ "@deepseek-ai/dsh-brand": "0.2.1-alpha.2",
41
+ "@deepseek-ai/schemastery": "~3.18.5-alpha.1"
42
42
  },
43
43
  "devDependencies": {
44
- "@deepseek-ai/cordis": "~4.0.4",
45
- "@deepseek-ai/cordis-plugin-loader": "~1.0.5",
46
- "@deepseek-ai/dsh-agent": "0.2.0-rc.2",
47
- "@deepseek-ai/dsh-app-boot": "0.2.0-rc.2",
48
- "@deepseek-ai/dsh-llm": "0.2.0-rc.2",
49
- "@deepseek-ai/dsh-loader-smoke": "0.2.0-rc.2",
50
- "@deepseek-ai/dsh-session": "0.2.0-rc.2",
51
- "@deepseek-ai/dsh-session-projection": "0.2.0-rc.2",
52
- "@deepseek-ai/dsh-subagent": "0.2.0-rc.2",
53
- "@deepseek-ai/dsh-session-checkpoint-policy": "0.2.0-rc.2",
54
- "@deepseek-ai/dsh-subprocess-local": "0.2.0-rc.2",
55
- "@deepseek-ai/dsh-subprocess": "0.2.0-rc.2",
56
- "@deepseek-ai/dsh-timeout": "0.2.0-rc.2",
57
- "@deepseek-ai/dsh-tool-subagent": "0.2.0-rc.2",
58
- "@deepseek-ai/dsh-session-persistence-jsonl": "0.2.0-rc.2"
44
+ "@deepseek-ai/cordis": "~4.0.5-alpha.1",
45
+ "@deepseek-ai/cordis-plugin-loader": "~1.0.6-alpha.1",
46
+ "@deepseek-ai/dsh-agent": "0.2.1-alpha.2",
47
+ "@deepseek-ai/dsh-app-boot": "0.2.1-alpha.2",
48
+ "@deepseek-ai/dsh-llm": "0.2.1-alpha.2",
49
+ "@deepseek-ai/dsh-loader-smoke": "0.2.1-alpha.2",
50
+ "@deepseek-ai/dsh-session": "0.2.1-alpha.2",
51
+ "@deepseek-ai/dsh-session-checkpoint-policy": "0.2.1-alpha.2",
52
+ "@deepseek-ai/dsh-session-persistence-jsonl": "0.2.1-alpha.2",
53
+ "@deepseek-ai/dsh-session-projection": "0.2.1-alpha.2",
54
+ "@deepseek-ai/dsh-subagent": "0.2.1-alpha.2",
55
+ "@deepseek-ai/dsh-subprocess": "0.2.1-alpha.2",
56
+ "@deepseek-ai/dsh-subprocess-local": "0.2.1-alpha.2",
57
+ "@deepseek-ai/dsh-timeout": "0.2.1-alpha.2",
58
+ "@deepseek-ai/dsh-tool-subagent": "0.2.1-alpha.2"
59
59
  }
60
60
  }