@zhushanwen/subagent-engine-sdk 0.2.0

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.
Files changed (139) hide show
  1. package/dist/best-effort.cjs +88 -0
  2. package/dist/best-effort.d.cts +14 -0
  3. package/dist/best-effort.d.ts +14 -0
  4. package/dist/best-effort.js +8 -0
  5. package/dist/chunk-2DIMPZCQ.js +0 -0
  6. package/dist/chunk-365AUV6N.js +67 -0
  7. package/dist/chunk-3K2P2CM2.js +127 -0
  8. package/dist/chunk-75QEMUGV.js +58 -0
  9. package/dist/chunk-7I4XGL5J.js +95 -0
  10. package/dist/chunk-A75XDJIC.js +227 -0
  11. package/dist/chunk-DSQ7JQKM.js +39 -0
  12. package/dist/chunk-EJMF63R5.js +19 -0
  13. package/dist/chunk-GT6YLLN4.js +46 -0
  14. package/dist/chunk-HYES77BR.js +109 -0
  15. package/dist/chunk-JSBRDJBE.js +30 -0
  16. package/dist/chunk-LEOBWKRM.js +128 -0
  17. package/dist/chunk-N3RL6OVM.js +38 -0
  18. package/dist/chunk-OPMY4G4M.js +27 -0
  19. package/dist/chunk-PPEPBVCC.js +120 -0
  20. package/dist/chunk-PYO3YR7W.js +16 -0
  21. package/dist/chunk-RDH3ZOV6.js +46 -0
  22. package/dist/chunk-RULLX6C6.js +11 -0
  23. package/dist/chunk-X24SFZYW.js +6646 -0
  24. package/dist/chunk-YFSN3D5N.js +216 -0
  25. package/dist/chunk-ZOFFJNJD.js +136 -0
  26. package/dist/chunk-ZXEAW25V.js +132 -0
  27. package/dist/cli-entry.cjs +119 -0
  28. package/dist/cli-entry.d.cts +24 -0
  29. package/dist/cli-entry.d.ts +24 -0
  30. package/dist/cli-entry.js +8 -0
  31. package/dist/contract-types-sSlgppBC.d.cts +352 -0
  32. package/dist/contract-types-sSlgppBC.d.ts +352 -0
  33. package/dist/data-dir.cjs +117 -0
  34. package/dist/data-dir.d.cts +20 -0
  35. package/dist/data-dir.d.ts +20 -0
  36. package/dist/data-dir.js +12 -0
  37. package/dist/env.cjs +205 -0
  38. package/dist/env.d.cts +73 -0
  39. package/dist/env.d.ts +73 -0
  40. package/dist/env.js +16 -0
  41. package/dist/error-codes-DHco5-i_.d.cts +118 -0
  42. package/dist/error-codes-Dhss2Kmk.d.ts +118 -0
  43. package/dist/error-message.cjs +40 -0
  44. package/dist/error-message.d.cts +3 -0
  45. package/dist/error-message.d.ts +3 -0
  46. package/dist/error-message.js +7 -0
  47. package/dist/index.cjs +8375 -0
  48. package/dist/index.d.cts +23 -0
  49. package/dist/index.d.ts +23 -0
  50. package/dist/index.js +268 -0
  51. package/dist/journal-io.cjs +70 -0
  52. package/dist/journal-io.d.cts +12 -0
  53. package/dist/journal-io.d.ts +12 -0
  54. package/dist/journal-io.js +7 -0
  55. package/dist/journal-replay.cjs +256 -0
  56. package/dist/journal-replay.d.cts +45 -0
  57. package/dist/journal-replay.d.ts +45 -0
  58. package/dist/journal-replay.js +17 -0
  59. package/dist/kill-chain.cjs +221 -0
  60. package/dist/kill-chain.d.cts +74 -0
  61. package/dist/kill-chain.d.ts +74 -0
  62. package/dist/kill-chain.js +23 -0
  63. package/dist/logger.cjs +84 -0
  64. package/dist/logger.d.cts +24 -0
  65. package/dist/logger.d.ts +24 -0
  66. package/dist/logger.js +11 -0
  67. package/dist/logs/stderr-rotation.cjs +151 -0
  68. package/dist/logs/stderr-rotation.d.cts +37 -0
  69. package/dist/logs/stderr-rotation.d.ts +37 -0
  70. package/dist/logs/stderr-rotation.js +23 -0
  71. package/dist/nesting-guard.cjs +95 -0
  72. package/dist/nesting-guard.d.cts +65 -0
  73. package/dist/nesting-guard.d.ts +65 -0
  74. package/dist/nesting-guard.js +15 -0
  75. package/dist/node-executor.cjs +180 -0
  76. package/dist/node-executor.d.cts +63 -0
  77. package/dist/node-executor.d.ts +63 -0
  78. package/dist/node-executor.js +16 -0
  79. package/dist/paths.cjs +55 -0
  80. package/dist/paths.d.cts +8 -0
  81. package/dist/paths.d.ts +8 -0
  82. package/dist/paths.js +15 -0
  83. package/dist/port-contract.cjs +35 -0
  84. package/dist/port-contract.d.cts +88 -0
  85. package/dist/port-contract.d.ts +88 -0
  86. package/dist/port-contract.js +7 -0
  87. package/dist/protocol/index.cjs +400 -0
  88. package/dist/protocol/index.d.cts +685 -0
  89. package/dist/protocol/index.d.ts +685 -0
  90. package/dist/protocol/index.js +89 -0
  91. package/dist/relay-env.cjs +71 -0
  92. package/dist/relay-env.d.cts +37 -0
  93. package/dist/relay-env.d.ts +37 -0
  94. package/dist/relay-env.js +23 -0
  95. package/dist/schema-emulation.cjs +6680 -0
  96. package/dist/schema-emulation.d.cts +39 -0
  97. package/dist/schema-emulation.d.ts +39 -0
  98. package/dist/schema-emulation.js +11 -0
  99. package/dist/spawn.cjs +200 -0
  100. package/dist/spawn.d.cts +80 -0
  101. package/dist/spawn.d.ts +80 -0
  102. package/dist/spawn.js +16 -0
  103. package/dist/ui-channels.cjs +120 -0
  104. package/dist/ui-channels.d.cts +60 -0
  105. package/dist/ui-channels.d.ts +60 -0
  106. package/dist/ui-channels.js +9 -0
  107. package/dist/ui-types.cjs +18 -0
  108. package/dist/ui-types.d.cts +62 -0
  109. package/dist/ui-types.d.ts +62 -0
  110. package/dist/ui-types.js +1 -0
  111. package/package.json +58 -0
  112. package/src/best-effort.ts +37 -0
  113. package/src/cli-entry.ts +77 -0
  114. package/src/data-dir.ts +88 -0
  115. package/src/env.ts +265 -0
  116. package/src/error-message.ts +22 -0
  117. package/src/index.ts +63 -0
  118. package/src/journal-io.ts +82 -0
  119. package/src/journal-replay.ts +432 -0
  120. package/src/kill-chain.ts +265 -0
  121. package/src/logger.ts +105 -0
  122. package/src/logs/stderr-rotation.ts +166 -0
  123. package/src/nesting-guard.ts +140 -0
  124. package/src/node-executor.ts +272 -0
  125. package/src/paths.ts +48 -0
  126. package/src/port-contract.ts +117 -0
  127. package/src/protocol/contract-types.ts +378 -0
  128. package/src/protocol/engine-protocol.ts +81 -0
  129. package/src/protocol/error-codes.ts +179 -0
  130. package/src/protocol/frames.ts +145 -0
  131. package/src/protocol/index.ts +12 -0
  132. package/src/protocol/methods.ts +229 -0
  133. package/src/protocol/reverse-channels.ts +274 -0
  134. package/src/protocol/schema.ts +154 -0
  135. package/src/relay-env.ts +60 -0
  136. package/src/schema-emulation.ts +192 -0
  137. package/src/spawn.ts +246 -0
  138. package/src/ui-channels.ts +219 -0
  139. package/src/ui-types.ts +84 -0
package/src/env.ts ADDED
@@ -0,0 +1,265 @@
1
+ // src/env.ts
2
+ //
3
+ // 引擎子进程 env 契约(W12,impl-plan §2.12 / 设计 §3.5.1 D7)。
4
+ //
5
+ // F9:buildOutboundChildEnv 在 @xyz-agent/shared,而 SDK 的消费面(zsw 宿主、引擎
6
+ // CLI 包)不可依赖 shared——SDK 是跨宿主 SSOT,故本文件自持全部常量与构建器,
7
+ // 不 import @xyz-agent/shared。
8
+ //
9
+ // 基座常量单源:下方 ENGINE_ENV_PREFIXES / ENGINE_ENV_DENY_LIST 是
10
+ // packages/shared/src/constants.ts 同名 SSOT 的构建期生成物(逐项相等由
11
+ // .githooks/check_env_whitelist_sync.py 断言);改动必须两处同批提交。
12
+
13
+ import { getLogger } from "./logger.ts";
14
+
15
+ const logger = getLogger("engine-sdk/env");
16
+
17
+ // ─────────────────────────────────────────────────────────────────────────────
18
+ // 基座常量(@generated 镜像:packages/shared/src/constants.ts ENGINE_ENV_* SSOT)
19
+ // ─────────────────────────────────────────────────────────────────────────────
20
+
21
+ /** L2 manifest 放行的保留前缀拒绝表(镜像 shared SSOT,勿单独改动)。 */
22
+ export const ENGINE_ENV_PREFIXES: readonly string[] = [
23
+ 'XYZ_', 'XYZ_AGENT_', 'XYZ_SUBAGENT_',
24
+ ];
25
+
26
+ /** L1 deny + 显式剥除键集(镜像 shared SSOT,勿单独改动)。 */
27
+ export const ENGINE_ENV_DENY_LIST: readonly string[] = [
28
+ 'XYZ_AGENT_PACKAGED',
29
+ 'XYZ_RUNTIME_TOKEN',
30
+ 'XYZ_AGENT_API_KEY',
31
+ 'XYZ_SUBAGENT_RELAY_SESSION_ID',
32
+ 'XYZ_SUBAGENT_RELAY_RECORD_ID',
33
+ 'XYZ_SUBAGENT_RELAY_STDIN',
34
+ 'XYZ_SUBAGENT_RELAY_STDOUT',
35
+ 'XYZ_SUBAGENT_RELAY_STDERR',
36
+ ];
37
+
38
+ // ─────────────────────────────────────────────────────────────────────────────
39
+ // L0 基础设施键(core 过滤之后显式注入,不受放行/剥除约束)
40
+ // ─────────────────────────────────────────────────────────────────────────────
41
+
42
+ /** L0 注入的全部基础设施键(守卫断言:与 ENGINE_ENV_DENY_LIST 交集 = ∅)。 */
43
+ export const ENGINE_ENV_L0_INFRA_KEYS: readonly string[] = [
44
+ 'XYZ_AGENT_DATA_DIR',
45
+ 'XYZ_AGENT_ENGINE_NODE',
46
+ 'ELECTRON_RUN_AS_NODE',
47
+ 'XYZ_AGENT_SUBAGENT',
48
+ 'XYZ_SUBAGENT_RELAY_SOCKET',
49
+ 'XYZ_SUBAGENT_RELAY_NODE',
50
+ 'XYZ_SUBAGENT_RELAY_SCRIPT',
51
+ ];
52
+
53
+ /** relay 三键形态(宿主 relay 激活时三值同时非空,全有或全无)。 */
54
+ export interface EngineRelayEnv {
55
+ socket: string;
56
+ node: string;
57
+ script: string;
58
+ }
59
+
60
+ /** buildEngineChildEnv 入参。 */
61
+ export interface EngineChildEnvOptions {
62
+ /** L0:引擎数据根(隔离库/池/journal 都相对它推导;缺 env 且无注入引擎侧须显式报错) */
63
+ dataDir: string;
64
+ /** L0:执行器路径(descriptor command 的执行体;Electron 二进制时另置 electronRunAsNode) */
65
+ engineNode?: string;
66
+ /** L0:执行器为 Electron 二进制 → 注入 ELECTRON_RUN_AS_NODE=1 */
67
+ electronRunAsNode?: boolean;
68
+ /** L0:relay 三键(宿主 relay 激活时传;三键必经 L0——L1 拒绝 XYZ_SUBAGENT_ 前缀、L2 是 manifest 面,两层都到不了) */
69
+ relay?: EngineRelayEnv;
70
+ /** L0:引擎侧身份 env(PI_SUBAGENT_ROOT_SESSION_ID 等,base-tool-enhance / 递归可见性消费) */
71
+ identityEnv?: Record<string, string | undefined>;
72
+ /** L2:manifest 声明的 envPrefixes 放行清单(保留前缀拒绝 + 形态校验) */
73
+ envPrefixes?: readonly string[];
74
+ /** L2:引擎私有 env 候选(descriptor processEnv;未声明前缀不放行 + warn) */
75
+ processEnv?: Record<string, string | undefined>;
76
+ }
77
+
78
+ /** manifest envPrefixes 合法形态:大小写不敏感的 [A-Za-z0-9_]+(如 `MYENGINE_`)。 */
79
+ const ENV_PREFIX_SHAPE_RE = /^[A-Za-z0-9_]+$/;
80
+
81
+ /** deny/剥除键删除:Windows env 键语义不区分大小写,统一 lower-case 比较。 */
82
+ function deleteEnvKeysCaseInsensitive(
83
+ env: Record<string, string>,
84
+ names: readonly string[],
85
+ ): void {
86
+ const lowered = new Set(names.map((n) => n.toLowerCase()));
87
+ for (const key of Object.keys(env)) {
88
+ if (lowered.has(key.toLowerCase())) delete env[key];
89
+ }
90
+ }
91
+
92
+ /**
93
+ * L2 前缀清单武装:逐条形态校验 + 保留前缀拒绝,非法条目丢弃 + warn(包继续可用)。
94
+ * 返回 null 表示无任何合法前缀(跳过 processEnv 放行)。
95
+ */
96
+ function compileManifestPrefixes(prefixes: readonly string[]): string[] | null {
97
+ const compiled: string[] = [];
98
+ for (const raw of prefixes) {
99
+ if (!ENV_PREFIX_SHAPE_RE.test(raw)) {
100
+ logger.warn(
101
+ `envPrefixes entry dropped (invalid shape, expected ^[A-Za-z0-9_]+$): ${JSON.stringify(raw)}`,
102
+ );
103
+ continue;
104
+ }
105
+ const lowered = raw.toLowerCase();
106
+ if (ENGINE_ENV_PREFIXES.some((reserved) => lowered.startsWith(reserved.toLowerCase()))) {
107
+ logger.warn(
108
+ `envPrefixes entry dropped (reserved prefix, infra keys are L0-only): ${JSON.stringify(raw)}`,
109
+ );
110
+ continue;
111
+ }
112
+ compiled.push(lowered);
113
+ }
114
+ return compiled.length > 0 ? compiled : null;
115
+ }
116
+
117
+ /**
118
+ * L2 manifest 放行:envPrefixes 声明前缀的 processEnv 键写入 env。
119
+ * - 双侧声明(processEnv + envPrefixes)→ compile 后按前缀放行,未命中 warn;
120
+ * - 只声明 processEnv(manifest 无 envPrefixes)→ 全部不放行 + warn。
121
+ */
122
+ function applyManifestAllowlist(
123
+ env: Record<string, string>,
124
+ opts: EngineChildEnvOptions,
125
+ ): void {
126
+ if (opts.processEnv !== undefined && opts.envPrefixes !== undefined) {
127
+ const compiled = compileManifestPrefixes(opts.envPrefixes);
128
+ if (compiled !== null) {
129
+ for (const [key, value] of Object.entries(opts.processEnv)) {
130
+ if (value === undefined) continue;
131
+ const loweredKey = key.toLowerCase();
132
+ if (compiled.some((prefix) => loweredKey.startsWith(prefix))) {
133
+ env[key] = value;
134
+ } else {
135
+ logger.warn(`processEnv key not allowed (no declared envPrefix matches): ${key}`);
136
+ }
137
+ }
138
+ }
139
+ } else if (opts.processEnv !== undefined) {
140
+ for (const key of Object.keys(opts.processEnv)) {
141
+ logger.warn(`processEnv key not allowed (manifest declares no envPrefixes): ${key}`);
142
+ }
143
+ }
144
+ }
145
+
146
+ /**
147
+ * L0 基础设施键显式注入(最后写入 = 最高优先级):dataDir 恒注入 / engineNode、
148
+ * ELECTRON_RUN_AS_NODE(条件)/ XYZ_AGENT_SUBAGENT=1 / relay 三键 / identityEnv。
149
+ */
150
+ function injectL0InfraKeys(
151
+ env: Record<string, string>,
152
+ opts: EngineChildEnvOptions,
153
+ ): void {
154
+ env.XYZ_AGENT_DATA_DIR = opts.dataDir;
155
+ if (opts.engineNode !== undefined) env.XYZ_AGENT_ENGINE_NODE = opts.engineNode;
156
+ if (opts.electronRunAsNode === true) env.ELECTRON_RUN_AS_NODE = "1";
157
+ env.XYZ_AGENT_SUBAGENT = "1";
158
+ if (opts.relay !== undefined) {
159
+ env.XYZ_SUBAGENT_RELAY_SOCKET = opts.relay.socket;
160
+ env.XYZ_SUBAGENT_RELAY_NODE = opts.relay.node;
161
+ env.XYZ_SUBAGENT_RELAY_SCRIPT = opts.relay.script;
162
+ }
163
+ if (opts.identityEnv !== undefined) {
164
+ for (const [key, value] of Object.entries(opts.identityEnv)) {
165
+ if (value !== undefined) env[key] = value;
166
+ }
167
+ }
168
+ }
169
+
170
+ /**
171
+ * `buildEngineChildEnv(baseEnv, opts)`:引擎子进程 env 三层契约(高者覆盖低者)。
172
+ *
173
+ * 写死次序 = 先过滤(L1 deny/剥除 → L2 manifest 放行)后 L0 显式注入——
174
+ * 「core 过滤之后显式注入」即此语义:baseEnv 是调用方(core/宿主)已过滤的基座,
175
+ * L0 键在最后写入故不受 L1 剥除影响(交集为 ∅ 由测试断言兜底)。
176
+ *
177
+ * 层规格(impl-plan §2.12 三层表):
178
+ * - L0 基础设施键:XYZ_AGENT_DATA_DIR / XYZ_AGENT_ENGINE_NODE / ELECTRON_RUN_AS_NODE
179
+ * (条件)/ XYZ_AGENT_SUBAGENT=1 / relay 三键 / identityEnv(injectL0InfraKeys);
180
+ * - L1 deny + 显式剥除(恒高于 manifest 放行):ENGINE_ENV_DENY_LIST
181
+ * (deny 两键 + 凭证键 + 父身份键 + 防御性剥除三死名);
182
+ * - L2 manifest 放行:envPrefixes 声明前缀的 processEnv 键(保留前缀拒绝、
183
+ * 形态校验、未声明前缀不放行 + warn)(applyManifestAllowlist)。
184
+ *
185
+ * 红线:纯函数,不读写 process.env 本体,不 mutate 入参。
186
+ */
187
+ export function buildEngineChildEnv(
188
+ baseEnv: Record<string, string | undefined>,
189
+ opts: EngineChildEnvOptions,
190
+ ): Record<string, string> {
191
+ // 基座拷贝(undefined 值不进 env——Node spawn 的 env 值须是 string)
192
+ const env: Record<string, string> = {};
193
+ for (const [key, value] of Object.entries(baseEnv)) {
194
+ if (value !== undefined) env[key] = value;
195
+ }
196
+
197
+ // L1:deny + 显式剥除(先于 L0,保证 L0 注入不受剥除影响)
198
+ deleteEnvKeysCaseInsensitive(env, ENGINE_ENV_DENY_LIST);
199
+
200
+ // L2:manifest envPrefixes 放行 processEnv(在 L0 之前——L0 基础设施键优先级最高)
201
+ applyManifestAllowlist(env, opts);
202
+
203
+ // L1 兜底复检:deny/剥除恒高于 manifest 放行——宽前缀(如声明 XYZ_ 形态的
204
+ // 宽松清单)也不允许经 L2 把 deny 键带回来。L0 未注入,此轮只清 L2 写入面。
205
+ deleteEnvKeysCaseInsensitive(env, ENGINE_ENV_DENY_LIST);
206
+
207
+ // L0:基础设施键显式注入(最后写入 = 最高优先级)
208
+ injectL0InfraKeys(env, opts);
209
+
210
+ return env;
211
+ }
212
+
213
+ // ─────────────────────────────────────────────────────────────────────────────
214
+ // 通用出站构建器(引擎无关 deny 剥离)
215
+ // ─────────────────────────────────────────────────────────────────────────────
216
+
217
+ /** buildOutboundChildEnv 入参(SDK 版;语义见函数头注释)。 */
218
+ export interface BuildOutboundChildEnvOptions {
219
+ /** 父进程 env 快照(调用方显式注入;本函数绝不改写它) */
220
+ parentEnv: Record<string, string | undefined>;
221
+ /** 强制注入/覆盖的键;值为 undefined 时执行删除语义 */
222
+ extras?: Record<string, string | undefined>;
223
+ /** 基座前缀白名单;缺省 = 全量继承父 env(与 shared 版的差异,见函数头注释) */
224
+ prefixes?: readonly string[];
225
+ }
226
+
227
+ /**
228
+ * 通用子进程出站 env 构建器(deny 剥离终态),供非引擎 spawn 面(worktree git
229
+ * execFile 等)复用——R3 MF-C:deny 键不进 git 子进程,git hooks 等后代不再可能
230
+ * 消费生命周期标志 / WS 令牌。
231
+ *
232
+ * 与 @xyz-agent/shared 版 buildOutboundChildEnv 的刻意差异:**缺省不做白名单过滤**
233
+ * (prefixes 省略 = 全量继承父 env + deny 剥除)。理由:SDK 消费面(zsw 宿主 /
234
+ * 引擎 CLI)不可 import shared 的 ENV_WHITELIST_PREFIXES;引擎 spawn 的基座由
235
+ * 调用方先经 buildEngineChildEnv 组装(其 baseEnv 已是 core 过滤后的),而
236
+ * ambient 场景(git 需要 GIT_* / proxy / ssh 等)必须保留继承面——deny-by-default
237
+ * 兜底语义与 shared 版一致。需要白名单形态时调用方显式传 prefixes。
238
+ *
239
+ * 红线:纯函数,不读写 process.env 本体,不 mutate 入参。
240
+ */
241
+ export function buildOutboundChildEnv(
242
+ opts: BuildOutboundChildEnvOptions,
243
+ ): Record<string, string> {
244
+ const out: Record<string, string> = {};
245
+ const loweredPrefixes =
246
+ opts.prefixes !== undefined
247
+ ? opts.prefixes.map((p) => p.toLowerCase())
248
+ : null;
249
+ for (const [key, value] of Object.entries(opts.parentEnv)) {
250
+ if (value === undefined) continue;
251
+ if (loweredPrefixes !== null) {
252
+ const loweredKey = key.toLowerCase();
253
+ if (!loweredPrefixes.some((prefix) => loweredKey.startsWith(prefix))) continue;
254
+ }
255
+ out[key] = value;
256
+ }
257
+ if (opts.extras !== undefined) {
258
+ for (const [key, value] of Object.entries(opts.extras)) {
259
+ if (value !== undefined) out[key] = value;
260
+ else delete out[key];
261
+ }
262
+ }
263
+ deleteEnvKeysCaseInsensitive(out, ENGINE_ENV_DENY_LIST);
264
+ return out;
265
+ }
@@ -0,0 +1,22 @@
1
+ // src/error-message.ts
2
+ //
3
+ // 「错误 → 可读字符串」兜底单源(round1-reuse R11 微副本收编:core
4
+ // src/core/error-message.ts、pi/zcode 引擎包 error-message.ts 副本自本模块
5
+ // re-export 收编;此前各包自持先例见各自 shim 注释)。
6
+ //
7
+ // [A8 修复](round1 business-logic S1 登记,可操作性修复):非 Error 的 object
8
+ // 入参改 JSON.stringify 结构化文本(协议错误帧 {code,message,recovery} 直进文案,
9
+ // 不再退化为 "[object Object]");stringify 抛错(循环引用等)兜底 String(e)。
10
+ // Error 入参输出逐字节不变。
11
+
12
+ export function toErrorMessage(e: unknown): string {
13
+ if (e instanceof Error) return e.message;
14
+ if (typeof e === "object" && e !== null) {
15
+ try {
16
+ return JSON.stringify(e);
17
+ } catch {
18
+ return String(e);
19
+ }
20
+ }
21
+ return String(e);
22
+ }
package/src/index.ts ADDED
@@ -0,0 +1,63 @@
1
+ // src/index.ts
2
+ //
3
+ // @zhushanwen/subagent-engine-sdk 主 barrel。
4
+ // u-foundation 契约根(impl-plan §2 W1 行):协议面(帧型/方法/错误码/版本协商/
5
+ // JSON Schema/契约类型)+ 引擎侧原语(7 模块)+ UI 请求契约类型。
6
+ // 不变量(设计 §3.10.1):本包禁止 import @zhushanwen/subagent-core——守卫
7
+ // .githooks/check-engine-sdk-boundary.mjs(SDK 源码 + dist 双侧扫描)。
8
+
9
+ // ── 协议面(契约类型 SSOT + 帧型 + 方法 + 反向通道 + 错误码 + 版本协商 + schema)──
10
+ export * from "./protocol/index.ts";
11
+
12
+ // ── 引擎侧原语(7 模块,impl-plan §2.1 原语迁移处置表)──
13
+ export * from "./schema-emulation.ts";
14
+ export * from "./nesting-guard.ts";
15
+ export * from "./logger.ts";
16
+ export * from "./kill-chain.ts";
17
+ export * from "./journal-replay.ts";
18
+ export * from "./data-dir.ts";
19
+ export * from "./paths.ts";
20
+
21
+ // ── UI 请求契约类型(host/askUser 反向通道载荷;core 反向 re-export 保消费面)──
22
+ export * from "./ui-types.ts";
23
+
24
+ // UI channel 提取 + channel 注册表(round1-reuse R1:core execution 与 pi 引擎 CLI
25
+ // 的逐字等价副本自本模块 re-export 收编)。
26
+ export * from "./ui-channels.ts";
27
+
28
+ // W12 落地(impl-plan §2.12):env/spawn 原语(三层 env 契约 + 引擎子进程唯一 spawn
29
+ // 入口 + 宿主死亡自灭守卫)。
30
+ export * from "./env.ts";
31
+ export * from "./spawn.ts";
32
+
33
+ // W9 落地(impl-plan §2.9):引擎 CLI 启动解析(宿主 × 平台二维矩阵 + node 执行器探针,
34
+ // 探针复刻 runtime relay-env 先例)。
35
+ export * from "./node-executor.ts";
36
+
37
+ // stderr tee 轮转/清理单源(前缀参数化;两引擎包的 logs/stderr-rotation.ts 为薄包装)。
38
+ export * from "./logs/stderr-rotation.ts";
39
+
40
+ // relay 通道 env 名与协议常量 SSOT(round1-reuse R9:core ./relay-env 子入口与 pi
41
+ // 引擎包副本自本模块 re-export 收编,消 5 个 XYZ_SUBAGENT_RELAY_* env 名双副本)。
42
+ export * from "./relay-env.ts";
43
+
44
+ // 「错误 → 可读字符串」与 best-effort 吞错 helper 单源(round1-reuse R11:core 与
45
+ // pi/zcode 引擎包的微副本 re-export 收编;toErrorMessage 含 A8 修复——非 Error
46
+ // object 入参 JSON.stringify 结构化文本,Error 入参逐字节不变)。
47
+ export * from "./error-message.ts";
48
+ export * from "./best-effort.ts";
49
+
50
+ // journal 文件读取单源(S4 簇 6:core event-journal 与 zcode journal-io 的
51
+ // replayJournal/parseLine 逐字等价双副本收编;JournalWriter 写侧留 core)。
52
+ export * from "./journal-io.ts";
53
+
54
+ // 引擎进程内契约面七符号 + parseCtxModel(S4 簇 1:pi/zcode port-types.ts 本地镜像
55
+ // 收编,两包改 re-export shim。收主 barrel 不进 protocol/——RunContext 含
56
+ // AbortSignal/回调非序列化成员;全量任务声明名 EngineAgentCallOpts 避让 protocol
57
+ // 的 AgentCallOpts 引擎面子集)。
58
+ export * from "./port-contract.ts";
59
+
60
+ // 引擎 CLI 进程入口样板(S4 簇 2:pi/zcode main.ts 同构段收编,两包收缩为薄层;
61
+ // armEngineSelfDestruct 与 server 构造留薄层,configureLoggerSink/readline 循环
62
+ // 在此)。
63
+ export * from "./cli-entry.ts";
@@ -0,0 +1,82 @@
1
+ // src/journal-io.ts
2
+ //
3
+ // journal 文件读取单源(S4 簇 6 收编:core execution/engine/common/event-journal.ts
4
+ // 与 zcode-subagent-cli journal-io.ts 的 replayJournal/parseLine 逐字等价双副本收编
5
+ // ——引擎包不能 import core,单源只能落 SDK;core 改 re-export、zcode 改 import,
6
+ // 消费方路径零改动)。
7
+ //
8
+ // 实现以 core 版为准逐字迁移:私有 JournalLine 保留全字段形态({v,ts,taskId,
9
+ // engineId,seq,event}——taskId/engineId 仅诊断字段,重放在 map event 前即丢弃,
10
+ // 故 zcode 旧副本的缩略行形态与本版无行为差异)。行格式权威:docs/architecture/
11
+ // subagent-engine-abstraction.md §3.3.6(JSONL 中立 v1);格式漂移面由 W10 golden
12
+ // 往返测试 + core/zcode 各自 journal 直测覆盖。
13
+ //
14
+ // 分层:只承接读侧(replay)。JournalWriter(写入器)留 core——宿主侧职责;本模块
15
+ // 与 journal-replay.ts(纯投影,无 fs)分离,保持投影层零文件系统依赖。
16
+
17
+ import { readFileSync } from "node:fs";
18
+
19
+ import type { AgentEvent } from "./protocol/contract-types.ts";
20
+
21
+ /** journal 单行(JSONL,§3.3.6 v1)。私有形态:重放只消费 seq/event。 */
22
+ interface JournalLine {
23
+ v: 1;
24
+ /** host 落盘时刻(Date.now(),ms)。 */
25
+ ts: number;
26
+ /** = RunContext.taskId = record.id(journal 文件名与池引用计数 key)。 */
27
+ taskId: string;
28
+ engineId: string;
29
+ seq: number;
30
+ /** AgentEvent 原样(onEvent 回调对象的 JSON.stringify 直接产物,无二次变换)。 */
31
+ event: AgentEvent;
32
+ }
33
+
34
+ /**
35
+ * 重放 journal:读取路径下全部事件,重放即得 AgentEvent 流(read 第②级)。
36
+ *
37
+ * - 文件不存在 → [](降级链语义:②级不可达不算错误,调用方落 ③级);
38
+ * - 损坏行跳过(追加写产物末行可能截断;跳过优于整体失败——设计 C5「三级都不 throw」);
39
+ * - 按 seq 稳定排序后返回(重放顺序权威是 seq,不依赖文件行序的隐式保证,§3.3.6)。
40
+ */
41
+ export function replayJournal(path: string): AgentEvent[] {
42
+ let raw: string;
43
+ try {
44
+ raw = readFileSync(path, "utf8");
45
+ } catch {
46
+ return [];
47
+ }
48
+ const lines: JournalLine[] = [];
49
+ for (const row of raw.split("\n")) {
50
+ const trimmed = row.trim();
51
+ if (trimmed === "") continue;
52
+ const parsed = parseLine(trimmed);
53
+ if (parsed !== undefined) lines.push(parsed);
54
+ }
55
+ lines.sort((a, b) => a.seq - b.seq);
56
+ return lines.map((l) => l.event);
57
+ }
58
+
59
+ /** 单行 parse + 结构 guard(v=1 + event 形状最小判别:object 且 type 为 string)。 */
60
+ function parseLine(trimmed: string): JournalLine | undefined {
61
+ let parsed: unknown;
62
+ try {
63
+ parsed = JSON.parse(trimmed);
64
+ } catch {
65
+ return undefined;
66
+ }
67
+ if (typeof parsed !== "object" || parsed === null) return undefined;
68
+ const rec = parsed as Record<string, unknown>;
69
+ const event = rec.event;
70
+ if (rec.v !== 1 || typeof rec.ts !== "number" || typeof rec.seq !== "number") return undefined;
71
+ if (typeof event !== "object" || event === null || typeof (event as Record<string, unknown>).type !== "string") {
72
+ return undefined;
73
+ }
74
+ return {
75
+ v: 1,
76
+ ts: rec.ts,
77
+ taskId: typeof rec.taskId === "string" ? rec.taskId : "",
78
+ engineId: typeof rec.engineId === "string" ? rec.engineId : "",
79
+ seq: rec.seq,
80
+ event: event as AgentEvent,
81
+ };
82
+ }