@deepseek-ai/dsh-subagent-acp 0.1.2-alpha.5 → 0.1.3-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 +2 -2
- package/README.md +2 -2
- package/README.zh.md +2 -2
- package/lib/index.js +37 -27
- package/lib/types/run.d.ts +6 -6
- package/package.json +22 -22
package/README.i18n.yaml
CHANGED
|
@@ -2,5 +2,5 @@
|
|
|
2
2
|
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
|
3
3
|
# after editing either side, bring the other along and re-record with:
|
|
4
4
|
# pnpm run verify-translation-pairing --write packages/subagent/subagent-acp/README.md
|
|
5
|
-
README.md:
|
|
6
|
-
README.zh.md:
|
|
5
|
+
README.md: 62b20bd1b35a80682dbacbe1e9ff4c0b1b7c3d63
|
|
6
|
+
README.zh.md: fa1b1c8897cee115de25240fa543ac0d496d8f22
|
package/README.md
CHANGED
|
@@ -67,7 +67,7 @@ A successful run returns the child's final streamed assistant text as the result
|
|
|
67
67
|
|
|
68
68
|
### Failure and recovery
|
|
69
69
|
|
|
70
|
-
A spawn, initialization, or new-session failure rejects before publication, ordinarily after the
|
|
70
|
+
A spawn, initialization, or new-session failure rejects before publication, ordinarily after the managed range is proven quiescent. If cleanup also fails, the rejection preserves ordered safe startup and teardown facts without claiming whole-range quiescence. Non-cancellation errors expose only fixed provider, stage, and category facts; the original failure stays on the internal cause chain and in Host diagnostics. After publication, a prompt, transport, or early-process failure resolves as `error` with a safe diagnostic, while local cancellation resolves as `aborted` without failure detail.
|
|
71
71
|
|
|
72
72
|
### Safe diagnostics
|
|
73
73
|
|
|
@@ -91,7 +91,7 @@ This section explains how the backend drives a child over ACP and where the obse
|
|
|
91
91
|
|
|
92
92
|
### Start and ownership flow
|
|
93
93
|
|
|
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-
|
|
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.
|
|
95
95
|
|
|
96
96
|
### Stop-reason mapping
|
|
97
97
|
|
package/README.zh.md
CHANGED
|
@@ -67,7 +67,7 @@ DeepSeek Harness 子进程使用产品启动器和一个显式的绝对路径 `D
|
|
|
67
67
|
|
|
68
68
|
### 失败与恢复
|
|
69
69
|
|
|
70
|
-
spawn
|
|
70
|
+
spawn、初始化或新建会话失败会在发布前拒绝,通常先证明 managed range 已经完全停稳。如果清理也失败,拒绝会保留有序、安全的启动与拆卸事实,但不会声称整个 range 已经停稳。非取消错误只暴露固定的提供方、阶段与类别事实;原始失败保留在内部 cause 链与 Host 诊断中。发布后,提示词、传输或进程提前退出会以携带安全诊断的 `error` 结算;本地取消则以不带失败详情的 `aborted` 结算。
|
|
71
71
|
|
|
72
72
|
### 安全诊断
|
|
73
73
|
|
|
@@ -91,7 +91,7 @@ spawn、初始化或新建会话失败会在发布前拒绝,通常先等待子
|
|
|
91
91
|
|
|
92
92
|
### 启动与所有权流程
|
|
93
93
|
|
|
94
|
-
一次启动先解析子 agent 的工作目录(配置的 `cwd` 覆盖值,否则取父会话 cwd),经子进程 seam spawn 命令,完成 ACP `initialize` 与 `newSession` 握手,然后才发布运行。兑现意味着远程会话已就绪、所有权已转移给调用方。dispose(资源释放)是幂等的:先关闭 stdin 并按配置的宽限等待协作式完全停稳,再经 SIGTERM 升级到 SIGKILL
|
|
94
|
+
一次启动先解析子 agent 的工作目录(配置的 `cwd` 覆盖值,否则取父会话 cwd),经子进程 seam spawn 命令,完成 ACP `initialize` 与 `newSession` 握手,然后才发布运行。兑现意味着远程会话已就绪、所有权已转移给调用方。dispose(资源释放)是幂等的:先关闭 stdin 并按配置的宽限等待协作式完全停稳,再经 SIGTERM 升级到 SIGKILL,并等待整个 managed range 退出。清理失败会作为有序的安全事实保持可观察,且绝不声称已经完全停稳。
|
|
95
95
|
|
|
96
96
|
### 停止原因映射
|
|
97
97
|
|
package/lib/index.js
CHANGED
|
@@ -74,8 +74,8 @@ function permissionRequestKind(kind) {
|
|
|
74
74
|
const candidate = kind ?? "unknown";
|
|
75
75
|
return ACP_TOOL_KINDS.has(candidate) ? candidate : "unknown";
|
|
76
76
|
}
|
|
77
|
-
/** Bounded
|
|
78
|
-
async function
|
|
77
|
+
/** Bounded managed-range exit wait: observes the handle's range until it is empty or `ms` elapses. */
|
|
78
|
+
async function rangeExitsWithin(child, ms) {
|
|
79
79
|
const controller = new AbortController();
|
|
80
80
|
const timer = setTimeout(() => {
|
|
81
81
|
controller.abort();
|
|
@@ -88,22 +88,31 @@ async function treeExitsWithin(child, ms) {
|
|
|
88
88
|
}
|
|
89
89
|
/**
|
|
90
90
|
* Cooperative teardown ladder for an out-of-process agent, over the seam's
|
|
91
|
-
* public verbs; resolves only at whole-
|
|
91
|
+
* public verbs; resolves only at whole-range quiescence: stdin EOF (the child's
|
|
92
92
|
* window to flush persistence and reap its own descendants), then the
|
|
93
93
|
* terminate() escalation (SIGTERM → spec grace → SIGKILL) and its
|
|
94
|
-
* whole-
|
|
94
|
+
* whole-range exit proof.
|
|
95
95
|
* @param child - the spawned ACP child's handle.
|
|
96
96
|
* @param eofGraceMs - tier-1 window after stdin EOF.
|
|
97
97
|
*/
|
|
98
98
|
async function disposeAcpChild(child, eofGraceMs) {
|
|
99
|
-
|
|
100
|
-
await child.done.catch(() => {});
|
|
101
|
-
return;
|
|
102
|
-
}
|
|
99
|
+
const failures = [];
|
|
103
100
|
child.stdin?.end();
|
|
104
|
-
|
|
101
|
+
let exited = false;
|
|
102
|
+
try {
|
|
103
|
+
exited = await rangeExitsWithin(child, eofGraceMs);
|
|
104
|
+
} catch (error) {
|
|
105
|
+
failures.push(toError(error));
|
|
106
|
+
}
|
|
107
|
+
if (exited) return;
|
|
105
108
|
child.terminate();
|
|
106
|
-
|
|
109
|
+
try {
|
|
110
|
+
await child.waitForExit();
|
|
111
|
+
} catch (error) {
|
|
112
|
+
failures.push(toError(error));
|
|
113
|
+
}
|
|
114
|
+
if (failures.length === 1) throw failures[0];
|
|
115
|
+
if (failures.length > 1) throw new AggregateError(failures, "ACP subprocess teardown failed");
|
|
107
116
|
}
|
|
108
117
|
/**
|
|
109
118
|
* Map an ACP {@link StopReason} to a harness {@link SubagentStopReason}.
|
|
@@ -154,11 +163,7 @@ function reportFailure(spec, error) {
|
|
|
154
163
|
} catch {}
|
|
155
164
|
}
|
|
156
165
|
/** Classify an unpublished failure from the active protocol operation and observed process facts. */
|
|
157
|
-
function startupFailure(error, stage,
|
|
158
|
-
if (child.pid <= 0) return new AcpRunFailure({
|
|
159
|
-
stage: "process",
|
|
160
|
-
category: "process-start"
|
|
161
|
-
}, error);
|
|
166
|
+
function startupFailure(error, stage, outcome) {
|
|
162
167
|
return new AcpRunFailure(
|
|
163
168
|
/* v8 ignore next -- Windows anonymous pipes cannot expose a live-child protocol close during startup. */
|
|
164
169
|
outcome === void 0 ? {
|
|
@@ -194,10 +199,10 @@ function terminalFailure(reason, permission) {
|
|
|
194
199
|
/**
|
|
195
200
|
* Start and publish one ACP child after initialization and session creation.
|
|
196
201
|
* Child failures resolve through the run result. Startup rejects with fixed
|
|
197
|
-
* safe facts after provider-owned cleanup; successful cleanup proves
|
|
198
|
-
*
|
|
202
|
+
* safe facts after provider-owned cleanup; successful cleanup proves managed
|
|
203
|
+
* range quiescence. Cleanup failure preserves startup plus teardown facts for an ordinary
|
|
199
204
|
* failure, or teardown alone after cancellation, without claiming quiescence.
|
|
200
|
-
* Disposal cancels,
|
|
205
|
+
* Disposal cancels, terminates, and settles the child's managed range.
|
|
201
206
|
* @param request - the start request; its signal is the cancellation channel.
|
|
202
207
|
* @param spec - the resolved spawn spec: command/args/cwd, env, permission
|
|
203
208
|
* policy, dispose graces, and the optional error sink.
|
|
@@ -230,18 +235,22 @@ async function startAcpRun(request, spec) {
|
|
|
230
235
|
if (child.stdin === void 0 || child.stdout === void 0) throw new Error("subagent-acp: subprocess implementation dropped a piped protocol stream");
|
|
231
236
|
/* v8 ignore stop */
|
|
232
237
|
let processOutcome;
|
|
238
|
+
let processFailure;
|
|
233
239
|
const processDone = child.done.then((outcome) => {
|
|
234
240
|
processOutcome = outcome;
|
|
235
241
|
return outcome;
|
|
242
|
+
}, (error) => {
|
|
243
|
+
processFailure = toError(error);
|
|
244
|
+
throw processFailure;
|
|
236
245
|
});
|
|
237
|
-
const
|
|
246
|
+
const processRejected = processDone.then(
|
|
238
247
|
/* v8 ignore next -- the success arm's never-settling executor is intentionally empty. */
|
|
239
248
|
() => new Promise(() => {}),
|
|
240
249
|
(err) => Promise.reject(toError(err))
|
|
241
250
|
);
|
|
242
|
-
|
|
251
|
+
processRejected.catch(() => {});
|
|
243
252
|
const observeProcessOutcome = async (signal) => {
|
|
244
|
-
if (processOutcome !== void 0
|
|
253
|
+
if (processOutcome !== void 0) return processOutcome;
|
|
245
254
|
const timeout = AbortSignal.timeout(Math.ceil(spec.disposeGraceMs));
|
|
246
255
|
const bound = signal === void 0 ? timeout : AbortSignal.any([signal, timeout]);
|
|
247
256
|
const aborted = Promise.withResolvers();
|
|
@@ -255,7 +264,6 @@ async function startAcpRun(request, spec) {
|
|
|
255
264
|
try {
|
|
256
265
|
return await Promise.race([processDone, aborted.promise]);
|
|
257
266
|
} catch {
|
|
258
|
-
/* v8 ignore next -- a published child.done cannot reject; spawn rejection is consumed before publication. */
|
|
259
267
|
return processOutcome;
|
|
260
268
|
} finally {
|
|
261
269
|
bound.removeEventListener("abort", onObservationAbort);
|
|
@@ -331,18 +339,20 @@ async function startAcpRun(request, spec) {
|
|
|
331
339
|
/* v8 ignore next -- cancelSettled wins the startup race before this post-response guard can settle it. */
|
|
332
340
|
if (flags.cancelled) throw new Error("subagent cancelled before the ACP session started");
|
|
333
341
|
})(),
|
|
334
|
-
|
|
342
|
+
processRejected,
|
|
335
343
|
cancelSettled.then(() => {
|
|
336
344
|
throw new Error("subagent cancelled before the ACP session started");
|
|
337
345
|
})
|
|
338
346
|
]);
|
|
339
347
|
} catch (error) {
|
|
340
348
|
request.signal.removeEventListener("abort", onAbort);
|
|
341
|
-
const
|
|
349
|
+
const cancelledBeforeCleanup = flags.cancelled;
|
|
350
|
+
const observedOutcome = !cancelledBeforeCleanup && !(error instanceof AcpRunFailure) ? await observeProcessOutcome() : void 0;
|
|
351
|
+
const startup = cancelledBeforeCleanup ? { kind: "cancelled" } : {
|
|
342
352
|
kind: "failed",
|
|
343
|
-
failure: error instanceof AcpRunFailure ? error : startupFailure(error, startupStage,
|
|
353
|
+
failure: error instanceof AcpRunFailure ? error : startupFailure(error, startupStage, observedOutcome)
|
|
344
354
|
};
|
|
345
|
-
if (startup.kind === "cancelled") {} else reportFailure(spec, error instanceof AcpRunFailure ? error.cause : error);
|
|
355
|
+
if (startup.kind === "cancelled") {} else reportFailure(spec, error instanceof AcpRunFailure ? error.cause : processFailure ?? error);
|
|
346
356
|
try {
|
|
347
357
|
await disposeProcess();
|
|
348
358
|
} catch (cleanupError) {
|
|
@@ -392,7 +402,7 @@ async function startAcpRun(request, spec) {
|
|
|
392
402
|
outcome
|
|
393
403
|
}, latestPermission);
|
|
394
404
|
}
|
|
395
|
-
throw error;
|
|
405
|
+
throw processFailure ?? error;
|
|
396
406
|
}
|
|
397
407
|
},
|
|
398
408
|
collectOutput,
|
package/lib/types/run.d.ts
CHANGED
|
@@ -51,7 +51,7 @@ export interface AcpRunSpec {
|
|
|
51
51
|
disposeGraceMs: number;
|
|
52
52
|
/**
|
|
53
53
|
* Spawn function from the subprocess seam (`ctx.subprocess.spawn`), so the
|
|
54
|
-
* child rides the shared scrub,
|
|
54
|
+
* child rides the shared scrub, managed-range teardown, and service-owned
|
|
55
55
|
* lifetime instead of a package-local child_process path.
|
|
56
56
|
*/
|
|
57
57
|
spawn: (spec: SubprocessSpawnSpec) => SubprocessHandle;
|
|
@@ -74,10 +74,10 @@ export declare const DEFAULT_DISPOSE_GRACE_MS = 3000;
|
|
|
74
74
|
export declare function acpConfigurationFailure(cause: unknown): Error;
|
|
75
75
|
/**
|
|
76
76
|
* Cooperative teardown ladder for an out-of-process agent, over the seam's
|
|
77
|
-
* public verbs; resolves only at whole-
|
|
77
|
+
* public verbs; resolves only at whole-range quiescence: stdin EOF (the child's
|
|
78
78
|
* window to flush persistence and reap its own descendants), then the
|
|
79
79
|
* terminate() escalation (SIGTERM → spec grace → SIGKILL) and its
|
|
80
|
-
* whole-
|
|
80
|
+
* whole-range exit proof.
|
|
81
81
|
* @param child - the spawned ACP child's handle.
|
|
82
82
|
* @param eofGraceMs - tier-1 window after stdin EOF.
|
|
83
83
|
*/
|
|
@@ -104,10 +104,10 @@ export declare function toAcpPrompt(prompt: ContentBlock[]): AcpContentBlock[];
|
|
|
104
104
|
/**
|
|
105
105
|
* Start and publish one ACP child after initialization and session creation.
|
|
106
106
|
* Child failures resolve through the run result. Startup rejects with fixed
|
|
107
|
-
* safe facts after provider-owned cleanup; successful cleanup proves
|
|
108
|
-
*
|
|
107
|
+
* safe facts after provider-owned cleanup; successful cleanup proves managed
|
|
108
|
+
* range quiescence. Cleanup failure preserves startup plus teardown facts for an ordinary
|
|
109
109
|
* failure, or teardown alone after cancellation, without claiming quiescence.
|
|
110
|
-
* Disposal cancels,
|
|
110
|
+
* Disposal cancels, terminates, and settles the child's managed range.
|
|
111
111
|
* @param request - the start request; its signal is the cancellation channel.
|
|
112
112
|
* @param spec - the resolved spawn spec: command/args/cwd, env, permission
|
|
113
113
|
* policy, dispose graces, and the optional error sink.
|
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.1.
|
|
4
|
+
"version": "0.1.3-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.1.
|
|
31
|
-
"@deepseek-ai/dsh-llm": "^0.1.
|
|
30
|
+
"@deepseek-ai/dsh-agent": "^0.1.3-alpha.2",
|
|
31
|
+
"@deepseek-ai/dsh-llm": "^0.1.3-alpha.2",
|
|
32
32
|
"@deepseek-ai/cordis": "^4.0.2",
|
|
33
|
-
"@deepseek-ai/dsh-
|
|
34
|
-
"@deepseek-ai/dsh-
|
|
35
|
-
"@deepseek-ai/dsh-
|
|
36
|
-
"@deepseek-ai/dsh-
|
|
33
|
+
"@deepseek-ai/dsh-subprocess": "^0.1.3-alpha.2",
|
|
34
|
+
"@deepseek-ai/dsh-timeout": "^0.1.3-alpha.2",
|
|
35
|
+
"@deepseek-ai/dsh-session": "^0.1.3-alpha.2",
|
|
36
|
+
"@deepseek-ai/dsh-subagent": "^0.1.3-alpha.2"
|
|
37
37
|
},
|
|
38
38
|
"dependencies": {
|
|
39
39
|
"@agentclientprotocol/sdk": "1.4.0",
|
|
40
|
-
"@deepseek-ai/
|
|
41
|
-
"@deepseek-ai/
|
|
40
|
+
"@deepseek-ai/schemastery": "^3.18.2",
|
|
41
|
+
"@deepseek-ai/dsh-brand": "^0.1.3-alpha.2"
|
|
42
42
|
},
|
|
43
43
|
"devDependencies": {
|
|
44
|
-
"@deepseek-ai/dsh-agent": "^0.1.2-alpha.5",
|
|
45
|
-
"@deepseek-ai/dsh-app-boot": "^0.1.2-alpha.5",
|
|
46
44
|
"@deepseek-ai/cordis": "^4.0.2",
|
|
47
45
|
"@deepseek-ai/cordis-plugin-loader": "^1.0.3",
|
|
48
|
-
"@deepseek-ai/dsh-
|
|
49
|
-
"@deepseek-ai/dsh-
|
|
50
|
-
"@deepseek-ai/dsh-
|
|
51
|
-
"@deepseek-ai/dsh-
|
|
52
|
-
"@deepseek-ai/dsh-session
|
|
53
|
-
"@deepseek-ai/dsh-session-
|
|
54
|
-
"@deepseek-ai/dsh-
|
|
55
|
-
"@deepseek-ai/dsh-
|
|
56
|
-
"@deepseek-ai/dsh-
|
|
57
|
-
"@deepseek-ai/dsh-
|
|
58
|
-
"@deepseek-ai/dsh-
|
|
46
|
+
"@deepseek-ai/dsh-agent": "^0.1.3-alpha.2",
|
|
47
|
+
"@deepseek-ai/dsh-app-boot": "^0.1.3-alpha.2",
|
|
48
|
+
"@deepseek-ai/dsh-llm": "^0.1.3-alpha.2",
|
|
49
|
+
"@deepseek-ai/dsh-loader-smoke": "^0.1.3-alpha.2",
|
|
50
|
+
"@deepseek-ai/dsh-session": "^0.1.3-alpha.2",
|
|
51
|
+
"@deepseek-ai/dsh-session-checkpoint-policy": "^0.1.3-alpha.2",
|
|
52
|
+
"@deepseek-ai/dsh-session-persistence-jsonl": "^0.1.3-alpha.2",
|
|
53
|
+
"@deepseek-ai/dsh-session-projection": "^0.1.3-alpha.2",
|
|
54
|
+
"@deepseek-ai/dsh-subagent": "^0.1.3-alpha.2",
|
|
55
|
+
"@deepseek-ai/dsh-subprocess": "^0.1.3-alpha.2",
|
|
56
|
+
"@deepseek-ai/dsh-subprocess-local": "^0.1.3-alpha.2",
|
|
57
|
+
"@deepseek-ai/dsh-timeout": "^0.1.3-alpha.2",
|
|
58
|
+
"@deepseek-ai/dsh-tool-subagent": "^0.1.3-alpha.2"
|
|
59
59
|
}
|
|
60
60
|
}
|