@zhushanwen/subagent-engine-sdk 0.4.0 → 0.5.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/dist/{chunk-7I4XGL5J.js → chunk-BBWUC2EE.js} +2 -2
- package/dist/{chunk-3K2P2CM2.js → chunk-CNQTJJF5.js} +1 -1
- package/dist/{chunk-DSQ7JQKM.js → chunk-GSW3DMN6.js} +5 -5
- package/dist/{chunk-BBVZK5DL.js → chunk-M3SZTDNQ.js} +20 -20
- package/dist/{chunk-HYES77BR.js → chunk-MSAJXIP4.js} +2 -2
- package/dist/{chunk-FJT23P7T.js → chunk-QOEXD2FT.js} +1 -1
- package/dist/{chunk-LKPVN3N6.js → chunk-W3BKI2BP.js} +3 -3
- package/dist/{chunk-WS4WAZVR.js → chunk-WMDMXP2F.js} +2 -2
- package/dist/{chunk-GT6YLLN4.js → chunk-X24IVQKJ.js} +5 -5
- package/dist/{chunk-365AUV6N.js → chunk-YE472JOO.js} +2 -2
- package/dist/{contract-types-C71Sqq8d.d.cts → contract-types-Dsvy79r3.d.cts} +9 -10
- package/dist/{contract-types-C71Sqq8d.d.ts → contract-types-Dsvy79r3.d.ts} +9 -10
- package/dist/data-dir.cjs +6 -6
- package/dist/data-dir.d.cts +4 -4
- package/dist/data-dir.d.ts +4 -4
- package/dist/data-dir.js +3 -3
- package/dist/env.cjs +20 -20
- package/dist/env.d.cts +12 -5
- package/dist/env.d.ts +12 -5
- package/dist/env.js +1 -1
- package/dist/{error-codes-D5YlWaBx.d.ts → error-codes-DMGeUUUm.d.ts} +12 -12
- package/dist/{error-codes-CiOO3PMg.d.cts → error-codes-pT7RYTqQ.d.cts} +12 -12
- package/dist/index.cjs +42 -42
- package/dist/index.d.cts +3 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.js +12 -12
- package/dist/journal-io.d.cts +1 -1
- package/dist/journal-io.d.ts +1 -1
- package/dist/journal-replay.d.cts +1 -1
- package/dist/journal-replay.d.ts +1 -1
- package/dist/kill-chain.d.cts +2 -2
- package/dist/kill-chain.d.ts +2 -2
- package/dist/kill-chain.js +2 -2
- package/dist/logs/stderr-rotation.cjs +2 -2
- package/dist/logs/stderr-rotation.d.cts +1 -1
- package/dist/logs/stderr-rotation.d.ts +1 -1
- package/dist/logs/stderr-rotation.js +1 -1
- package/dist/nesting-guard.cjs +2 -2
- package/dist/nesting-guard.d.cts +2 -2
- package/dist/nesting-guard.d.ts +2 -2
- package/dist/nesting-guard.js +1 -1
- package/dist/node-executor.cjs +2 -2
- package/dist/node-executor.d.cts +5 -5
- package/dist/node-executor.d.ts +5 -5
- package/dist/node-executor.js +2 -2
- package/dist/port-contract.d.cts +1 -1
- package/dist/port-contract.d.ts +1 -1
- package/dist/protocol/index.cjs +2 -2
- package/dist/protocol/index.d.cts +5 -5
- package/dist/protocol/index.d.ts +5 -5
- package/dist/protocol/index.js +1 -1
- package/dist/relay-env.cjs +5 -5
- package/dist/relay-env.d.cts +5 -5
- package/dist/relay-env.d.ts +5 -5
- package/dist/relay-env.js +1 -1
- package/dist/spawn.cjs +1 -1
- package/dist/spawn.d.cts +2 -2
- package/dist/spawn.d.ts +2 -2
- package/dist/spawn.js +1 -1
- package/dist/ui-channels.cjs +2 -2
- package/dist/ui-channels.js +1 -1
- package/package.json +2 -4
- package/src/best-effort.ts +0 -37
- package/src/cli-entry.ts +0 -82
- package/src/data-dir.ts +0 -89
- package/src/env.ts +0 -265
- package/src/error-message.ts +0 -22
- package/src/index.ts +0 -63
- package/src/journal-io.ts +0 -82
- package/src/journal-replay.ts +0 -436
- package/src/kill-chain.ts +0 -265
- package/src/logger.ts +0 -105
- package/src/logs/stderr-rotation.ts +0 -166
- package/src/nesting-guard.ts +0 -140
- package/src/node-executor.ts +0 -272
- package/src/paths.ts +0 -48
- package/src/port-contract.ts +0 -150
- package/src/protocol/contract-types.ts +0 -379
- package/src/protocol/engine-protocol.ts +0 -91
- package/src/protocol/error-codes.ts +0 -180
- package/src/protocol/frames.ts +0 -143
- package/src/protocol/index.ts +0 -12
- package/src/protocol/methods.ts +0 -237
- package/src/protocol/reverse-channels.ts +0 -123
- package/src/protocol/schema.ts +0 -185
- package/src/relay-env.ts +0 -60
- package/src/schema-emulation.ts +0 -192
- package/src/spawn.ts +0 -246
- package/src/ui-channels.ts +0 -219
- package/src/ui-types.ts +0 -101
package/src/node-executor.ts
DELETED
|
@@ -1,272 +0,0 @@
|
|
|
1
|
-
// src/node-executor.ts
|
|
2
|
-
//
|
|
3
|
-
// 引擎 CLI 启动解析(W9,impl-plan §2.9「启动解析(宿主 × 平台二维矩阵)」)。
|
|
4
|
-
//
|
|
5
|
-
// 为什么在 SDK:core/引擎包都不可 import runtime(runtime 包只存在于 xyz-agent 宿主
|
|
6
|
-
// 链),而矩阵的三宿主(pi 扩展 / runtime sidecar / standalone)两侧都要消费同一套
|
|
7
|
-
// 解析规则——探针逻辑在 SDK 侧复刻 runtime 先例
|
|
8
|
-
// packages/runtime/src/infra/relay/relay-env.ts:47-90(行为保持一致:同超时、同
|
|
9
|
-
// --eval process.exit(0) 探针体、同 PATH/HOME 最小 env、同 Electron RUN_AS_NODE 条件)。
|
|
10
|
-
//
|
|
11
|
-
// 矩阵(规格逐行对应):
|
|
12
|
-
// ① pi 扩展宿主(打包):process.execPath 是 Bun standalone binary,再拉就是又起
|
|
13
|
-
// 一个 pi——必须用注入执行器 XYZ_AGENT_ENGINE_NODE(与 relay 的
|
|
14
|
-
// XYZ_SUBAGENT_RELAY_NODE 不复用,单一名字);执行器为 Electron 二进制时同时带
|
|
15
|
-
// ELECTRON_RUN_AS_NODE=1;首次使用前跑探针,失败 → engine_not_found + 指引。
|
|
16
|
-
// ② runtime sidecar:process.execPath + ELECTRON_RUN_AS_NODE=1(sidecar 本身由主进程
|
|
17
|
-
// 以该形态 spawn,其 execPath 即宿主 Electron / dev node)。
|
|
18
|
-
// ③ standalone pi / zsw:PATH node(缺 node → engine_not_found + 安装指引)。
|
|
19
|
-
// Windows:入口 .mjs 不需 shim;引擎声明 .cmd → 禁 shell:true,改显式
|
|
20
|
-
// cmd.exe /c + 参数数组。
|
|
21
|
-
//
|
|
22
|
-
// env 名常量与 packages/shared/src/constants.ts 的 W9 挂载块同源(SDK 不 import
|
|
23
|
-
// shared——F9,见 env.ts 头注释同款理由)。
|
|
24
|
-
|
|
25
|
-
import { spawn } from "node:child_process";
|
|
26
|
-
|
|
27
|
-
import { EngineSdkError } from "./protocol/error-codes.ts";
|
|
28
|
-
|
|
29
|
-
/** L0 注入的引擎执行器路径 env(与 relay 的 XYZ_SUBAGENT_RELAY_NODE 不复用)。 */
|
|
30
|
-
export const ENGINE_NODE_ENV = "XYZ_AGENT_ENGINE_NODE";
|
|
31
|
-
|
|
32
|
-
/** 探针超时(与 relay-env.ts 先例一致:spawn 执行器跑 --eval "process.exit(0)" 的上限)。 */
|
|
33
|
-
const PROBE_TIMEOUT_MS = 5_000;
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* 探针:验证执行器能以纯 node 语义执行 JS(先例 = runtime relay-env.ts:47-90)。
|
|
37
|
-
*
|
|
38
|
-
* 为什么需要:打包态 pi 宿主的候选执行器可能是 Electron 二进制——直接当 node 用会
|
|
39
|
-
* 拉起 GUI,必须同 env 注入 ELECTRON_RUN_AS_NODE=1 才是纯 node 模式;探针被证伪则
|
|
40
|
-
* 按矩阵① 报 engine_not_found(带可操作指引),不静默回落 PATH 探测。
|
|
41
|
-
*/
|
|
42
|
-
export function probeNodeExecutor(
|
|
43
|
-
execPath: string,
|
|
44
|
-
isElectron: boolean,
|
|
45
|
-
): Promise<boolean> {
|
|
46
|
-
return new Promise((resolve) => {
|
|
47
|
-
let settled = false;
|
|
48
|
-
let child: ReturnType<typeof spawn> | null = null;
|
|
49
|
-
const finish = (ok: boolean): void => {
|
|
50
|
-
if (settled) return;
|
|
51
|
-
settled = true;
|
|
52
|
-
clearTimeout(timer);
|
|
53
|
-
try {
|
|
54
|
-
child?.kill("SIGKILL");
|
|
55
|
-
} catch {
|
|
56
|
-
// 已退出,正常路径
|
|
57
|
-
void 0;
|
|
58
|
-
}
|
|
59
|
-
resolve(ok);
|
|
60
|
-
};
|
|
61
|
-
const env: Record<string, string> = {};
|
|
62
|
-
if (process.env.PATH !== undefined) env.PATH = process.env.PATH;
|
|
63
|
-
if (process.env.HOME !== undefined) env.HOME = process.env.HOME;
|
|
64
|
-
if (isElectron) env.ELECTRON_RUN_AS_NODE = "1";
|
|
65
|
-
|
|
66
|
-
try {
|
|
67
|
-
child = spawn(execPath, ["--eval", "process.exit(0)"], {
|
|
68
|
-
env,
|
|
69
|
-
stdio: "ignore",
|
|
70
|
-
windowsHide: true,
|
|
71
|
-
});
|
|
72
|
-
} catch {
|
|
73
|
-
resolve(false);
|
|
74
|
-
return;
|
|
75
|
-
}
|
|
76
|
-
const timer = setTimeout(() => finish(false), PROBE_TIMEOUT_MS);
|
|
77
|
-
timer.unref();
|
|
78
|
-
child.on("error", () => finish(false));
|
|
79
|
-
child.on("exit", (code) => finish(code === 0));
|
|
80
|
-
});
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
/** 探针结果缓存(key = execPath:isElectron)。失败也缓存——重试窗口留给宿主重启。 */
|
|
84
|
-
const probeCache = new Map<string, Promise<boolean>>();
|
|
85
|
-
|
|
86
|
-
function probeCached(execPath: string, isElectron: boolean): Promise<boolean> {
|
|
87
|
-
const key = `${execPath}:${isElectron}`;
|
|
88
|
-
let p = probeCache.get(key);
|
|
89
|
-
if (!p) {
|
|
90
|
-
p = probeNodeExecutor(execPath, isElectron);
|
|
91
|
-
probeCache.set(key, p);
|
|
92
|
-
}
|
|
93
|
-
return p;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
/** 测试钩子:清探针缓存(生产无调用方;与 relay-env resetRelayNodeProbeCache 同款)。 */
|
|
97
|
-
export function resetEngineNodeProbeCache(): void {
|
|
98
|
-
probeCache.clear();
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
/** 宿主形态(矩阵行;EngineClient 的 hostKind 自由字符串经 hostKindOf 归一)。 */
|
|
102
|
-
export type EngineNodeHostKind = "pi-extension" | "runtime-sidecar" | "standalone";
|
|
103
|
-
|
|
104
|
-
/** resolveEngineNodeLaunch 的可注入项(测试隔离用;生产全部走缺省推导)。 */
|
|
105
|
-
export interface EngineNodeLaunchOptions {
|
|
106
|
-
/** 引擎 CLI 入口绝对路径(descriptor command / staged bin 解析结果)。 */
|
|
107
|
-
entryPath: string;
|
|
108
|
-
/** 入口附加参数(descriptor args;缺省 [])。 */
|
|
109
|
-
args?: readonly string[];
|
|
110
|
-
/** 宿主形态。 */
|
|
111
|
-
hostKind: EngineNodeHostKind;
|
|
112
|
-
/** 读取 XYZ_AGENT_ENGINE_NODE 的 env 快照(矩阵① 的注入通道)。 */
|
|
113
|
-
env?: Record<string, string | undefined>;
|
|
114
|
-
/** 宿主 process.execPath(缺省 process.execPath)。 */
|
|
115
|
-
execPath?: string;
|
|
116
|
-
/** 宿主自身是否 Electron(缺省 process.versions.electron !== undefined)。 */
|
|
117
|
-
isElectronHost?: boolean;
|
|
118
|
-
/** 平台覆盖(缺省 process.platform)。 */
|
|
119
|
-
platform?: NodeJS.Platform;
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
/** 解析结果:spawn(command, args) 形态 + buildEngineChildEnv 的 electronRunAsNode 输入。 */
|
|
123
|
-
export interface EngineNodeLaunch {
|
|
124
|
-
command: string;
|
|
125
|
-
args: string[];
|
|
126
|
-
/** 执行器为 Electron 二进制 → true(L0 注入 ELECTRON_RUN_AS_NODE=1)。 */
|
|
127
|
-
electronRunAsNode: boolean;
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
function engineNotFound(detail: string, recovery: string): EngineSdkError {
|
|
131
|
-
return new EngineSdkError("engine_not_found", detail, recovery);
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
/** 传入 opts.env 的快照(矩阵① env 缺省 = 宿主 process.env)。 */
|
|
135
|
-
function launchEnvOf(opts: EngineNodeLaunchOptions): Record<string, string | undefined> {
|
|
136
|
-
return opts.env ?? process.env;
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
/**
|
|
140
|
-
* 矩阵①:pi 扩展宿主(打包)——必须用注入执行器 XYZ_AGENT_ENGINE_NODE
|
|
141
|
-
* (env 缺失 = engine_not_found;执行器探针失败 = engine_not_found + 指引)。
|
|
142
|
-
*/
|
|
143
|
-
async function resolvePiExtensionLaunch(
|
|
144
|
-
entryPath: string,
|
|
145
|
-
entryArgs: string[],
|
|
146
|
-
env: Record<string, string | undefined>,
|
|
147
|
-
): Promise<EngineNodeLaunch> {
|
|
148
|
-
const engineNode = env[ENGINE_NODE_ENV]?.trim();
|
|
149
|
-
if (engineNode === undefined || engineNode === "") {
|
|
150
|
-
throw engineNotFound(
|
|
151
|
-
`pi extension host must spawn engines via injected executor ${ENGINE_NODE_ENV}, `
|
|
152
|
-
+ `but it is not set (host process.execPath is the pi binary, not a node executor)`,
|
|
153
|
-
`The xyz-agent runtime injects ${ENGINE_NODE_ENV} when spawning the pi host. `
|
|
154
|
-
+ `If you are running the extension inside a packaged app, report this as a packaging `
|
|
155
|
-
+ `regression; standalone pi installs do not use the pi-extension host kind.`,
|
|
156
|
-
);
|
|
157
|
-
}
|
|
158
|
-
// 执行器为 Electron 二进制时宿主会同时注入 ELECTRON_RUN_AS_NODE=1(矩阵① 同点注入)
|
|
159
|
-
const isElectron = env.ELECTRON_RUN_AS_NODE === "1";
|
|
160
|
-
if (!(await probeCached(engineNode, isElectron))) {
|
|
161
|
-
throw engineNotFound(
|
|
162
|
-
`injected node executor failed the probe: ${engineNode} (isElectron=${isElectron})`,
|
|
163
|
-
`Verify the executor exists and can run plain node semantics `
|
|
164
|
-
+ `(Electron binaries need ELECTRON_RUN_AS_NODE=1). The host re-probes after restart.`,
|
|
165
|
-
);
|
|
166
|
-
}
|
|
167
|
-
return { command: engineNode, args: [entryPath, ...entryArgs], electronRunAsNode: isElectron };
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
/**
|
|
171
|
-
* 矩阵②:runtime sidecar——process.execPath + ELECTRON_RUN_AS_NODE=1(Electron 宿主时,
|
|
172
|
-
* 探针先行)。
|
|
173
|
-
*/
|
|
174
|
-
async function resolveRuntimeSidecarLaunch(
|
|
175
|
-
entryPath: string,
|
|
176
|
-
entryArgs: string[],
|
|
177
|
-
opts: EngineNodeLaunchOptions,
|
|
178
|
-
): Promise<EngineNodeLaunch> {
|
|
179
|
-
const execPath = opts.execPath ?? process.execPath;
|
|
180
|
-
const isElectron = opts.isElectronHost ?? process.versions.electron !== undefined;
|
|
181
|
-
if (!(await probeCached(execPath, isElectron))) {
|
|
182
|
-
throw engineNotFound(
|
|
183
|
-
`runtime sidecar executor failed the probe: ${execPath} (isElectron=${isElectron})`,
|
|
184
|
-
`The sidecar process.execPath must run plain node semantics (ELECTRON_RUN_AS_NODE=1 `
|
|
185
|
-
+ `for Electron binaries). Check how the runtime sidecar was spawned.`,
|
|
186
|
-
);
|
|
187
|
-
}
|
|
188
|
-
return { command: execPath, args: [entryPath, ...entryArgs], electronRunAsNode: isElectron };
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
/** 矩阵③:standalone——PATH node(探针被证伪 = engine_not_found + 安装指引)。 */
|
|
192
|
-
async function resolveStandaloneLaunch(
|
|
193
|
-
entryPath: string,
|
|
194
|
-
entryArgs: string[],
|
|
195
|
-
): Promise<EngineNodeLaunch> {
|
|
196
|
-
if (!(await probeCached("node", false))) {
|
|
197
|
-
throw engineNotFound(
|
|
198
|
-
`no usable 'node' on PATH for standalone engine launch (entry: ${entryPath})`,
|
|
199
|
-
`Install Node.js >= 22 and ensure 'node' is on PATH `
|
|
200
|
-
+ `(https://nodejs.org/), or configure the engine explicitly via subagents/config.json.`,
|
|
201
|
-
);
|
|
202
|
-
}
|
|
203
|
-
return { command: "node", args: [entryPath, ...entryArgs], electronRunAsNode: false };
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
/**
|
|
207
|
-
* 宿主 × 平台二维矩阵解析(W9 §2.9)。返回 spawn argv;不直接 spawn——调用方
|
|
208
|
-
* (EngineClient / runtime)持有各自的平台参数(detached / 进程组收割语义)。
|
|
209
|
-
*
|
|
210
|
-
* 判定序(与规格逐行对应):
|
|
211
|
-
* Windows + entry .cmd → cmd.exe /c 显式形态(禁 shell:true 的注入面);
|
|
212
|
-
* pi-extension → 注入执行器(env XYZ_AGENT_ENGINE_NODE),缺失/探针失败 =
|
|
213
|
-
* engine_not_found + 指引;
|
|
214
|
-
* runtime-sidecar → process.execPath(Electron 时 electronRunAsNode,探针先行);
|
|
215
|
-
* standalone → PATH node(探针失败 = engine_not_found + 安装指引);
|
|
216
|
-
* 其余(dev 形态、非 Windows 非 .cmd、无矩阵命中的兜底)→ 直接 spawn 入口本体
|
|
217
|
-
* (shebang `#!/usr/bin/env node` 走 PATH node——引擎 bin 均带 shebang,dev/独立
|
|
218
|
-
* 安装形态与现状一致)。
|
|
219
|
-
*/
|
|
220
|
-
export async function resolveEngineNodeLaunch(
|
|
221
|
-
opts: EngineNodeLaunchOptions,
|
|
222
|
-
): Promise<EngineNodeLaunch> {
|
|
223
|
-
const platform = opts.platform ?? process.platform;
|
|
224
|
-
const entryArgs = [...(opts.args ?? [])];
|
|
225
|
-
|
|
226
|
-
// Windows 规则:入口 .cmd → 显式 cmd.exe /c + 参数数组(禁 shell:true)
|
|
227
|
-
if (platform === "win32" && opts.entryPath.toLowerCase().endsWith(".cmd")) {
|
|
228
|
-
return {
|
|
229
|
-
command: "cmd.exe",
|
|
230
|
-
args: ["/c", opts.entryPath, ...entryArgs],
|
|
231
|
-
electronRunAsNode: false,
|
|
232
|
-
};
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
// 非 JS 入口(原生二进制 / shell 脚本形态的引擎或测试 fixture)自管理执行体:
|
|
236
|
-
// 不经 node 执行器改写、不探针——直接 spawn 入口本体(shebang/原生入口语义)。
|
|
237
|
-
if (!/\.(?:js|mjs|cjs|ts)$/i.test(opts.entryPath)) {
|
|
238
|
-
return { command: opts.entryPath, args: entryArgs, electronRunAsNode: false };
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
if (opts.hostKind === "pi-extension") {
|
|
242
|
-
return resolvePiExtensionLaunch(opts.entryPath, entryArgs, launchEnvOf(opts));
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
if (opts.hostKind === "runtime-sidecar") {
|
|
246
|
-
return resolveRuntimeSidecarLaunch(opts.entryPath, entryArgs, opts);
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
if (opts.hostKind === "standalone") {
|
|
250
|
-
return resolveStandaloneLaunch(opts.entryPath, entryArgs);
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
// 非矩阵形态(理论不可达——hostKind 是封闭联合);保守回落直接 spawn 入口本体。
|
|
254
|
-
return { command: opts.entryPath, args: entryArgs, electronRunAsNode: false };
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
/**
|
|
258
|
-
* EngineClient 的自由字符串 hostKind('pi' / 'runtime' / …)→ 矩阵行归一:
|
|
259
|
-
* 'runtime*' → runtime-sidecar(②:sidecar execPath 权威);
|
|
260
|
-
* env 已注入 XYZ_AGENT_ENGINE_NODE(打包态 runtime 注入主 pi 进程的通道)→
|
|
261
|
-
* pi-extension(①:必须用注入执行器——pi 宿主自身 execPath 是 pi binary);
|
|
262
|
-
* 其余 → standalone(③:PATH node)。
|
|
263
|
-
*/
|
|
264
|
-
export function hostKindOf(
|
|
265
|
-
hostKind: string,
|
|
266
|
-
env: Record<string, string | undefined> = process.env,
|
|
267
|
-
): EngineNodeHostKind {
|
|
268
|
-
if (hostKind.startsWith("runtime")) return "runtime-sidecar";
|
|
269
|
-
const engineNode = env[ENGINE_NODE_ENV]?.trim();
|
|
270
|
-
if (engineNode !== undefined && engineNode !== "") return "pi-extension";
|
|
271
|
-
return "standalone";
|
|
272
|
-
}
|
package/src/paths.ts
DELETED
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
import { join } from 'node:path';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* 引擎数据目录布局 SSOT(引擎侧原语,自 core execution/engine/paths.ts 移入
|
|
5
|
-
* @zhushanwen/subagent-engine-sdk,实现体逐字等价)。
|
|
6
|
-
* 迁移处置(impl-plan §2.1 paths 行):纯 `node:path` 模块 → **移入 SDK**(引擎侧需
|
|
7
|
-
* 自算池/journal 路径);core 侧引用切换已完成(core execution/engine/paths.ts 为
|
|
8
|
-
* re-export shim,子入口 ./engine/paths 解析路径不变)。
|
|
9
|
-
*
|
|
10
|
-
* 设计权威源:constraints.json C-ext-15(原 subagent-engine-abstraction.md D5/D6,已删,git 可追溯)。
|
|
11
|
-
*
|
|
12
|
-
* 为什么独立成模块:extension 写侧(journal 落盘 / preparer 池目录)与
|
|
13
|
-
* runtime 校验侧(subagent-extractor 前缀白名单)必须同源推导——
|
|
14
|
-
* 双方 import 同一份纯函数,禁止各自拼字符串漂移。
|
|
15
|
-
*
|
|
16
|
-
* 布局:`<dataDir>/engines/<engineId>/<pool-key>/journal-<taskId>.jsonl`
|
|
17
|
-
* 隔离池跨任务保留复用;journal 生命周期跟随 record,不随池删除(D5)。
|
|
18
|
-
*/
|
|
19
|
-
|
|
20
|
-
/** 路径段安全编码后的最大字符数(防超长段击穿文件名长度上限;runtime 校验侧同源)。 */
|
|
21
|
-
const MAX_SEG_CHARS = 80;
|
|
22
|
-
|
|
23
|
-
/** 路径段进入文件系统前的安全编码:路径穿越、分隔符、空白、超长全部归一。 */
|
|
24
|
-
export function sanitizeSeg(input: string): string {
|
|
25
|
-
const s = input.replace(/[^A-Za-z0-9-]+/g, '-').replace(/^-+|-+$/g, '');
|
|
26
|
-
return s.length > 0 ? s.slice(0, MAX_SEG_CHARS) : 'default';
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export function resolveEnginesRoot(dataDir: string): string {
|
|
30
|
-
return join(dataDir, 'engines');
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
export function resolveEngineDir(dataDir: string, engineId: string): string {
|
|
34
|
-
return join(resolveEnginesRoot(dataDir), sanitizeSeg(engineId));
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
export function resolvePoolDir(dataDir: string, engineId: string, poolKey: string): string {
|
|
38
|
-
return join(resolveEngineDir(dataDir, sanitizeSeg(engineId)), sanitizeSeg(poolKey));
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
export function resolveJournalPath(
|
|
42
|
-
dataDir: string,
|
|
43
|
-
engineId: string,
|
|
44
|
-
poolKey: string,
|
|
45
|
-
taskId: string,
|
|
46
|
-
): string {
|
|
47
|
-
return join(resolvePoolDir(dataDir, engineId, poolKey), `journal-${sanitizeSeg(taskId)}.jsonl`);
|
|
48
|
-
}
|
package/src/port-contract.ts
DELETED
|
@@ -1,150 +0,0 @@
|
|
|
1
|
-
// src/port-contract.ts
|
|
2
|
-
//
|
|
3
|
-
// 引擎进程内契约面七符号 + parseCtxModel 单源(S4 簇 1 收编:pi/zcode 两包
|
|
4
|
-
// port-types.ts 的逐字等价本地镜像收编——两包原地改 re-export shim,18 个 import
|
|
5
|
-
// 点零改写;server.ts 的 parseCtxModel 模块级纯函数同批收编,实现以 pi 版为基线
|
|
6
|
-
// 逐字迁移)。
|
|
7
|
-
//
|
|
8
|
-
// 为什么现在能收(W5/W7 双轨沉淀后的收编,不翻「SDK 类型闭包只收跨进程协议面」
|
|
9
|
-
// 的原始裁决):两包过渡期镜像经 W10 conformance 套件验证字段逐字等价、双轨已
|
|
10
|
-
// 稳定,收编即消除克隆而非改变边界语义。RunContext 含 AbortSignal/回调/EngineStream
|
|
11
|
-
// 等非序列化成员,跨进程面经各包 server.ts 帧映射——故收主 barrel,不进 protocol/
|
|
12
|
-
// 子入口(protocol/ 是 semver 收窄的跨进程可序列化面)。
|
|
13
|
-
//
|
|
14
|
-
// 命名:全量任务声明在 SDK 侧名 EngineAgentCallOpts(主 barrel 已有 protocol 的
|
|
15
|
-
// AgentCallOpts 引擎面子集——16 字段协议 run.params.task 形态,两者是子集/全量
|
|
16
|
-
// 关系,不可同名共存;两包 shim 转名 re-export 保本包消费面 AgentCallOpts 不变)。
|
|
17
|
-
//
|
|
18
|
-
// onChildSpawned 统一采窄载荷形态(host/childSpawned 载荷形态;ChildProcess 句柄
|
|
19
|
-
// 不跨协议面)——zcode 侧生产零调用(D6),形态统一无行为影响。
|
|
20
|
-
//
|
|
21
|
-
// 第三份镜像登记:core execution/engine/port.ts 的 RunContext 是宿主侧独立契约
|
|
22
|
-
// (onChildSpawned 全 ChildProcess 句柄,概念域 = 宿主进程内),不收编、不 import
|
|
23
|
-
// 本模块;漂移面由 W10 conformance 套件继续覆盖。
|
|
24
|
-
|
|
25
|
-
import type {
|
|
26
|
-
AgentCallOpts as SdkAgentCallOpts,
|
|
27
|
-
AgentEvent,
|
|
28
|
-
AgentOutcome,
|
|
29
|
-
EngineCapabilities,
|
|
30
|
-
EngineHandleData,
|
|
31
|
-
ProbeReport,
|
|
32
|
-
ResumeAnchor,
|
|
33
|
-
SessionView,
|
|
34
|
-
} from "./protocol/contract-types.ts";
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* 引擎进程内全量任务声明 = SDK AgentCallOpts 引擎面子集 + 协议 ctx 还原字段(model/
|
|
38
|
-
* cwd/schemaEnv——SDK 契约把它们从 task 移到 run.params.ctx,进程内接口合回单对象;
|
|
39
|
-
* server.ts 做 ctx→task 还原,与 core RemoteEngine.toSdkTaskSubset 镜像)。
|
|
40
|
-
*/
|
|
41
|
-
export type EngineAgentCallOpts = SdkAgentCallOpts & {
|
|
42
|
-
model?: string;
|
|
43
|
-
cwd?: string;
|
|
44
|
-
schemaEnv?: string;
|
|
45
|
-
};
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* 引擎进程内的 ctxModel 形态(core ModelInfo 的结构等价镜像——仅 id/provider 被引擎
|
|
49
|
-
* 消费,name/reasoning 等字段透传保留防测试/未来消费面漂移)。
|
|
50
|
-
*/
|
|
51
|
-
export interface EngineCtxModel {
|
|
52
|
-
id: string;
|
|
53
|
-
provider: string;
|
|
54
|
-
name?: string;
|
|
55
|
-
reasoning?: boolean;
|
|
56
|
-
thinkingLevelMap?: Record<string, unknown>;
|
|
57
|
-
contextWindow?: number;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
/** text_delta streaming 出口(协议 host/streamDelta 的本地承载)。 */
|
|
61
|
-
export interface EngineStream {
|
|
62
|
-
onDelta(delta: string): void;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
/**
|
|
66
|
-
* journal 固定分组 key(值锚定,**只承载、不透传**):journal 落盘路径
|
|
67
|
-
* `<dataDir>/engines/<engineId>/<本值>/journal-<taskId>.jsonl` 的第二段,两引擎恒
|
|
68
|
-
* 本值(pi / zcode 均无池化实现——pi = PI_CODING_AGENT_DIR 全局一份,zcode = 共享
|
|
69
|
-
* 宿主 HOME)。[池抽象降级 2026-09-13] poolKey 协议面(RunContext/run.params.ctx/
|
|
70
|
-
* host/poolResolved 通道/EngineHandleData)已整体退役,本常量不再跨进程透传,仅
|
|
71
|
-
* 三类消费:① journal 落盘路径构造(core journal-wiring);② 存量 record 兼容
|
|
72
|
-
* (zcode read 旧相对 dbPath 的锚定分支 + 持久化 engineHandle.poolKey 字段——
|
|
73
|
-
* record-store 读侧守卫要求非空,值恒本常量);③ 引擎数据目录布局 SSOT paths.ts
|
|
74
|
-
* 的签名参数。**值逐字不变**(存量 journal 落盘路径与记录含该值分段,改名不改值)。
|
|
75
|
-
*/
|
|
76
|
-
export const SHARED_POOL_KEY = "shared";
|
|
77
|
-
|
|
78
|
-
/** run 的运行期上下文(core RunContext 结构等价镜像;resume? = 会话形态参数,缺省一次性任务)。 */
|
|
79
|
-
export interface RunContext {
|
|
80
|
-
taskId: string;
|
|
81
|
-
signal?: AbortSignal;
|
|
82
|
-
onEvent?: (event: AgentEvent) => void;
|
|
83
|
-
ctxModel?: EngineCtxModel;
|
|
84
|
-
stream?: EngineStream;
|
|
85
|
-
schemaEnv?: string;
|
|
86
|
-
engineFallback?: { from: string; reason: string };
|
|
87
|
-
/**
|
|
88
|
-
* [F6] 根 session id(协议 run.params.ctx.sessionRootId 的进程内还原)——pi 引擎
|
|
89
|
-
* relay 归属键 SESSION_ID 的权威来源。additive 可选:宿主缺省不传。
|
|
90
|
-
*/
|
|
91
|
-
sessionRootId?: string;
|
|
92
|
-
/**
|
|
93
|
-
* [Option C 协议化] 权威 subagent session 目录(协议 run.params.ctx.sessionDir 的
|
|
94
|
-
* 进程内还原)——pi 引擎组装 `--session-dir` 的唯一权威值(宿主
|
|
95
|
-
* getSubagentSessionDir 推导,引擎不自推导)。additive 可选:缺省走引擎内
|
|
96
|
-
* [LEGACY] fallback(独立运行/测试形态)。
|
|
97
|
-
*/
|
|
98
|
-
sessionDir?: string;
|
|
99
|
-
onHandleReady?: (partial: Pick<EngineHandleData, "sessionRef">) => void;
|
|
100
|
-
/** 一次性子进程 pid 上报(host/childSpawned 载荷形态;ChildProcess 句柄不跨协议面)。 */
|
|
101
|
-
onChildSpawned?: (child: { pid: number | undefined; killed: boolean }) => void;
|
|
102
|
-
/**
|
|
103
|
-
* 子进程退出态上报(host/childStateChanged 载荷形态;SR-4 接线:宿主镜像据此取消
|
|
104
|
-
* 该 pid 的挂起 dialog)。引擎侧只在 exited 相位上报——running 由 onChildSpawned 覆盖。
|
|
105
|
-
*/
|
|
106
|
-
onChildStateChanged?: (p: {
|
|
107
|
-
pid: number;
|
|
108
|
-
recordId: string;
|
|
109
|
-
state: "running" | "exited";
|
|
110
|
-
killed: boolean;
|
|
111
|
-
exitCode?: number;
|
|
112
|
-
signal?: string;
|
|
113
|
-
}) => void;
|
|
114
|
-
/**
|
|
115
|
-
* [H1 U6 终态] resume 续聊参数(协议 run.params.resume 的进程内还原;唯一会话形态
|
|
116
|
-
* 键——原 v1.x `chat` 字段已随键切换退役)。recordId = 轮次关联键与镜像帧锚定键;
|
|
117
|
-
* resume 锚点存在 = 冷续(--session 续写原文件),不存在 = 首轮新建。缺省 =
|
|
118
|
-
* 一次性任务形态。
|
|
119
|
-
*/
|
|
120
|
-
resume?: { recordId: string; resume?: ResumeAnchor };
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
export interface EngineHandle {
|
|
124
|
-
readonly data: EngineHandleData;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
export interface EngineRunResult {
|
|
128
|
-
handle: EngineHandle;
|
|
129
|
-
outcome: AgentOutcome;
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
/** 引擎进程内的引擎契约点(core EnginePort 的结构等价镜像)。 */
|
|
133
|
-
export interface EnginePort {
|
|
134
|
-
readonly id: string;
|
|
135
|
-
capabilities(): EngineCapabilities;
|
|
136
|
-
probe(opts?: { force?: boolean }): Promise<ProbeReport>;
|
|
137
|
-
run(task: EngineAgentCallOpts, ctx: RunContext): Promise<EngineRunResult>;
|
|
138
|
-
read(handle: EngineHandle): Promise<SessionView>;
|
|
139
|
-
listModels?(): Array<{ id: string; name?: string }> | null;
|
|
140
|
-
validateModel?(modelRef: string | undefined): { canonicalRef: string };
|
|
141
|
-
dispose?(): Promise<void>;
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
/** ctx.ctxModel("provider/id" canonical 词形)→ EngineCtxModel。 */
|
|
145
|
-
export function parseCtxModel(ref: string | undefined): EngineCtxModel | undefined {
|
|
146
|
-
if (ref === undefined || ref.trim() === "") return undefined;
|
|
147
|
-
const slash = ref.indexOf("/");
|
|
148
|
-
if (slash <= 0 || slash === ref.length - 1) return undefined;
|
|
149
|
-
return { provider: ref.slice(0, slash), id: ref.slice(slash + 1) };
|
|
150
|
-
}
|