@zhushanwen/subagent-core 0.6.0 → 0.7.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.
Files changed (278) hide show
  1. package/README.md +1 -1
  2. package/dist/chunk-KXVBIVOU.js +16 -0
  3. package/dist/chunk-T2SYBW3I.js +24 -0
  4. package/dist/chunk-VURUAGMM.js +2305 -0
  5. package/dist/{types-C3fE3R9x.d.cts → engine-discovery-scan-ocpM8FmI.d.cts} +770 -668
  6. package/dist/{types-C3fE3R9x.d.ts → engine-discovery-scan-ocpM8FmI.d.ts} +770 -668
  7. package/dist/execution/engine/engine-discovery-scan.cjs +2210 -0
  8. package/dist/execution/engine/engine-discovery-scan.d.cts +4 -0
  9. package/dist/execution/engine/engine-discovery-scan.d.ts +4 -0
  10. package/dist/execution/engine/engine-discovery-scan.js +20 -0
  11. package/dist/execution/engine/paths.cjs +6 -23
  12. package/dist/execution/engine/paths.d.cts +1 -8
  13. package/dist/execution/engine/paths.d.ts +1 -8
  14. package/dist/execution/engine/paths.js +1 -1
  15. package/dist/execution/relay-env.cjs +12 -28
  16. package/dist/execution/relay-env.d.cts +1 -33
  17. package/dist/execution/relay-env.d.ts +1 -33
  18. package/dist/execution/relay-env.js +5 -3
  19. package/dist/index.cjs +14045 -16059
  20. package/dist/index.d.cts +1035 -1579
  21. package/dist/index.d.ts +1035 -1579
  22. package/dist/index.js +9838 -13800
  23. package/dist.bundle/index.cjs +28875 -30323
  24. package/package.json +20 -26
  25. package/src/__tests__/record-store-last-line.test.ts +16 -7
  26. package/src/core/error-message.ts +7 -6
  27. package/src/core/host-services.ts +8 -2
  28. package/src/core/notify-ports.ts +17 -2
  29. package/src/execution/__tests__/chat-engine-routing.test.ts +62 -28
  30. package/src/execution/__tests__/chat-round-first-round-watchdog.test.ts +255 -0
  31. package/src/execution/__tests__/collect-coordinator-service.test.ts +36 -47
  32. package/src/execution/__tests__/collect-mixed-dispatch.test.ts +20 -53
  33. package/src/execution/__tests__/delivery-methods.test.ts +179 -237
  34. package/src/execution/__tests__/execute-and-await-worktree.test.ts +0 -2
  35. package/src/execution/__tests__/execute-nesting.test.ts +20 -13
  36. package/src/execution/__tests__/explicit-agent-ref-guard.test.ts +20 -33
  37. package/src/execution/__tests__/gc-timer.test.ts +7 -22
  38. package/src/execution/__tests__/get-record-for-action-restart.test.ts +20 -10
  39. package/src/execution/__tests__/helpers/fake-engine-port.ts +187 -0
  40. package/src/execution/__tests__/helpers/spawn-mock.ts +36 -9
  41. package/src/execution/__tests__/helpers/subagent-service-mocks.ts +1 -1
  42. package/src/execution/__tests__/idle-gc.test.ts +200 -0
  43. package/src/execution/__tests__/lifecycle-predicates.test.ts +26 -21
  44. package/src/execution/__tests__/nested-visibility-env-propagation.test.ts +5 -2
  45. package/src/execution/__tests__/reconcile-sweep.test.ts +200 -0
  46. package/src/execution/__tests__/record-store.test.ts +60 -6
  47. package/src/execution/__tests__/recursive-visibility-baseline.test.ts +20 -13
  48. package/src/execution/__tests__/round-supervisor.test.ts +293 -0
  49. package/src/execution/__tests__/session-pending.test.ts +119 -17
  50. package/src/execution/__tests__/stream-sink-retirement.test.ts +10 -10
  51. package/src/execution/__tests__/subagent-service-multiproc-guard.test.ts +1 -11
  52. package/src/execution/__tests__/subagent-service-notify-gate.test.ts +33 -30
  53. package/src/execution/__tests__/subagent-service-parent-guard.test.ts +2 -9
  54. package/src/execution/__tests__/subagent-service-recovery-bounds.test.ts +59 -129
  55. package/src/execution/__tests__/subagent-service.test.ts +2 -2
  56. package/src/execution/__tests__/subprocess-agent-runner-routing.test.ts +109 -7
  57. package/src/execution/__tests__/subprocess-agent-runner.test.ts +108 -36
  58. package/src/execution/__tests__/sync-collect-recovery.test.ts +21 -36
  59. package/src/execution/__tests__/workflow-state-root.test.ts +232 -0
  60. package/src/execution/agent-registry.ts +6 -2
  61. package/src/execution/dialog-queue.ts +6 -73
  62. package/src/execution/engine/__tests__/common/capability-gate.test.ts +115 -15
  63. package/src/execution/engine/__tests__/common/errors.test.ts +11 -18
  64. package/src/execution/engine/__tests__/common/kill-chain.test.ts +7 -0
  65. package/src/execution/engine/__tests__/common/nesting-guard.test.ts +9 -3
  66. package/src/execution/engine/__tests__/common/session-view-service.test.ts +90 -3
  67. package/src/execution/engine/__tests__/conformance/H9-test-disposition.md +116 -0
  68. package/src/execution/engine/__tests__/conformance/__fixtures__/engine-protocol/fake-engine-protocol.mjs +377 -0
  69. package/src/execution/engine/__tests__/conformance/__fixtures__/engine-protocol/recorded.fixture.json +331 -0
  70. package/src/execution/engine/__tests__/conformance/__fixtures__/engine-protocol/smoke-run.fixture.json +213 -0
  71. package/src/execution/engine/__tests__/conformance/chat-round-protocol.test.ts +230 -0
  72. package/src/execution/engine/__tests__/conformance/contract.abort.test.ts +69 -42
  73. package/src/execution/engine/__tests__/conformance/contract.agent-events.test.ts +4 -81
  74. package/src/execution/engine/__tests__/conformance/contract.probe.test.ts +67 -44
  75. package/src/execution/engine/__tests__/conformance/contract.read-degradation.test.ts +81 -54
  76. package/src/execution/engine/__tests__/conformance/engine-conformance.live.test.ts +133 -70
  77. package/src/execution/engine/__tests__/conformance/engine-crash.test.ts +143 -0
  78. package/src/execution/engine/__tests__/conformance/fake-engine-capabilities.ts +19 -0
  79. package/src/execution/engine/__tests__/conformance/golden-replay.pi.test.ts +7 -2
  80. package/src/execution/engine/__tests__/conformance/golden-replay.zcode.test.ts +6 -5
  81. package/src/execution/engine/__tests__/conformance/protocol-blackbox.test.ts +398 -0
  82. package/src/execution/engine/__tests__/conformance/registry-fork-filter.test.ts +221 -0
  83. package/src/execution/engine/__tests__/conformance/round-liveness-supervisor.test.ts +302 -0
  84. package/src/execution/engine/__tests__/conformance/static-spawn-guard.test.ts +58 -0
  85. package/src/execution/engine/__tests__/engine-discovery-scan.test.ts +558 -0
  86. package/src/execution/engine/__tests__/host-task-spec.test.ts +45 -0
  87. package/src/execution/engine/__tests__/model-ref-split.test.ts +74 -0
  88. package/src/execution/engine/__tests__/registry.test.ts +206 -1
  89. package/src/execution/engine/__tests__/routing.test.ts +97 -0
  90. package/src/execution/engine/__tests__/run-failure-worktree-cleanup.test.ts +97 -0
  91. package/src/execution/engine/client/__tests__/__fixtures__/fake-engine.mjs +294 -0
  92. package/src/execution/engine/client/__tests__/engine-client.test.ts +516 -0
  93. package/src/execution/engine/client/__tests__/mirror.test.ts +67 -0
  94. package/src/execution/engine/client/__tests__/pid-file.test.ts +257 -0
  95. package/src/execution/engine/client/__tests__/protocol-closure.test.ts +95 -0
  96. package/src/execution/engine/client/__tests__/remote-engine.test.ts +457 -0
  97. package/src/execution/engine/client/client-options.ts +103 -0
  98. package/src/execution/engine/client/engine-client.ts +687 -0
  99. package/src/execution/engine/client/mirror.ts +167 -0
  100. package/src/execution/engine/client/pid-file.ts +392 -0
  101. package/src/execution/engine/client/reaper.ts +95 -0
  102. package/src/execution/engine/client/remote-engine.ts +441 -0
  103. package/src/execution/engine/client/reverse-router.ts +255 -0
  104. package/src/execution/engine/common/capability-gate.ts +109 -22
  105. package/src/execution/engine/common/errors.ts +9 -25
  106. package/src/execution/engine/common/event-journal.ts +4 -50
  107. package/src/execution/engine/common/journal-replay.ts +3 -1
  108. package/src/execution/engine/common/kill-chain.ts +18 -251
  109. package/src/execution/engine/common/nesting-guard.ts +18 -114
  110. package/src/execution/engine/common/pool-manager.ts +4 -0
  111. package/src/execution/engine/common/session-view-service.ts +56 -84
  112. package/src/execution/engine/config.ts +151 -0
  113. package/src/execution/engine/d8-compat.ts +346 -0
  114. package/src/execution/engine/engine-discovery-roots.ts +107 -0
  115. package/src/execution/engine/engine-discovery-scan.ts +269 -0
  116. package/src/execution/engine/engine-discovery.ts +30 -7
  117. package/src/execution/engine/engine-inspect-package.ts +282 -0
  118. package/src/execution/engine/engine-manifest.ts +244 -0
  119. package/src/execution/engine/host/__tests__/host-askuser-endpoint.test.ts +123 -0
  120. package/src/execution/engine/host/__tests__/host-bridge.test.ts +135 -0
  121. package/src/execution/engine/host/__tests__/spawned-children.test.ts +73 -0
  122. package/src/execution/engine/host/host-bridge.ts +150 -0
  123. package/src/execution/engine/host/host-ui-endpoint.ts +47 -0
  124. package/src/execution/engine/host/pi-host-binding.ts +91 -0
  125. package/src/execution/engine/host/spawned-children.ts +139 -0
  126. package/src/execution/engine/host-task-spec.ts +29 -2
  127. package/src/execution/engine/model-validation.ts +55 -2
  128. package/src/execution/engine/paths.ts +16 -41
  129. package/src/execution/engine/port.ts +48 -2
  130. package/src/execution/engine/registry.ts +153 -34
  131. package/src/execution/engine/routing.ts +225 -52
  132. package/src/execution/engine/types.ts +25 -200
  133. package/src/execution/finalize-record.ts +25 -0
  134. package/src/execution/idle-gc.ts +89 -14
  135. package/src/execution/lifecycle-manager.ts +10 -7
  136. package/src/execution/lifecycle-predicates.ts +10 -8
  137. package/src/execution/record-store.ts +29 -2
  138. package/src/execution/relay-env.ts +19 -36
  139. package/src/execution/round-supervisor/domain.ts +89 -0
  140. package/src/execution/round-supervisor/index.ts +33 -0
  141. package/src/execution/round-supervisor/notify-accounting.ts +87 -0
  142. package/src/execution/round-supervisor/reconcile-sweep.ts +235 -0
  143. package/src/execution/round-supervisor/service-binding.test.ts +661 -0
  144. package/src/execution/round-supervisor/service-binding.ts +273 -0
  145. package/src/execution/round-supervisor/supervisor.ts +426 -0
  146. package/src/execution/session-pending.ts +68 -74
  147. package/src/execution/settled-watchdog.ts +39 -0
  148. package/src/execution/subagent-service.ts +746 -400
  149. package/src/execution/subprocess-agent-runner.ts +16 -8
  150. package/src/execution/types.ts +41 -117
  151. package/src/execution/ui-channels.ts +13 -214
  152. package/src/execution/ui-request-handler-factory.ts +7 -4
  153. package/src/execution/ui-request-observability.ts +2 -2
  154. package/src/execution/workflow-state-root.ts +67 -0
  155. package/src/execution/worktree-git-ops.ts +5 -0
  156. package/src/execution/worktree-manager.ts +11 -1
  157. package/src/index.ts +55 -26
  158. package/src/orchestration/__tests__/execute-agent-call.test.ts +1 -1
  159. package/src/orchestration/execute-agent-call.ts +1 -1
  160. package/src/orchestration/file-run-store.ts +144 -59
  161. package/src/orchestration/models/types.ts +43 -42
  162. package/src/shared/zcode-model-ref.ts +31 -0
  163. package/dist/chunk-43ONBFZX.js +0 -240
  164. package/dist/chunk-A4IVZWVX.js +0 -31
  165. package/dist/chunk-APZY4IME.js +0 -27
  166. package/dist/chunk-V3VHZ2VX.js +0 -59
  167. package/dist/execution/engine/engines/zcode/constants.cjs +0 -107
  168. package/dist/execution/engine/engines/zcode/constants.d.cts +0 -135
  169. package/dist/execution/engine/engines/zcode/constants.d.ts +0 -135
  170. package/dist/execution/engine/engines/zcode/constants.js +0 -54
  171. package/dist/execution/engine/engines/zcode/reader.cjs +0 -276
  172. package/dist/execution/engine/engines/zcode/reader.d.cts +0 -21
  173. package/dist/execution/engine/engines/zcode/reader.d.ts +0 -21
  174. package/dist/execution/engine/engines/zcode/reader.js +0 -9
  175. package/src/__tests__/append-system-prompt-assembly.test.ts +0 -243
  176. package/src/__tests__/fr4-get-state-handshake.test.ts +0 -125
  177. package/src/__tests__/session-runner.test.ts +0 -69
  178. package/src/execution/__tests__/ask-user-transit-e2e.test.ts +0 -490
  179. package/src/execution/__tests__/chatmode-first-round-closure-spawn.test.ts +0 -191
  180. package/src/execution/__tests__/descendant-sweep-guards.test.ts +0 -220
  181. package/src/execution/__tests__/descendant-sweep.test.ts +0 -269
  182. package/src/execution/__tests__/engine-model-validation.test.ts +0 -741
  183. package/src/execution/__tests__/epipe-fallback.test.ts +0 -248
  184. package/src/execution/__tests__/execution-runtime-face.test.ts +0 -272
  185. package/src/execution/__tests__/get-state-handshake.test.ts +0 -127
  186. package/src/execution/__tests__/helpers/session-runner-mocks.ts +0 -113
  187. package/src/execution/__tests__/keep-alive-no-progress.test.ts +0 -280
  188. package/src/execution/__tests__/kill-all-escalation.test.ts +0 -196
  189. package/src/execution/__tests__/max-turns-to-watchdog-ms.test.ts +0 -113
  190. package/src/execution/__tests__/output-collector.test.ts +0 -469
  191. package/src/execution/__tests__/pi-invocation.test.ts +0 -166
  192. package/src/execution/__tests__/recursive-visibility-env.test.ts +0 -149
  193. package/src/execution/__tests__/rpc-mode.test.ts +0 -89
  194. package/src/execution/__tests__/run-and-finalize-anchoring.test.ts +0 -387
  195. package/src/execution/__tests__/run-and-finalize-chatmode.test.ts +0 -277
  196. package/src/execution/__tests__/run-spawn-chatmode-settled.test.ts +0 -254
  197. package/src/execution/__tests__/run-spawn-edges.test.ts +0 -1079
  198. package/src/execution/__tests__/run-spawn-integration.test.ts +0 -933
  199. package/src/execution/__tests__/run-spawn-resume.test.ts +0 -322
  200. package/src/execution/__tests__/run-spawn-rpc-mode.test.ts +0 -196
  201. package/src/execution/__tests__/run-spawn-stdout-callback-throw.test.ts +0 -207
  202. package/src/execution/__tests__/service-kill-escalation.test.ts +0 -91
  203. package/src/execution/__tests__/session-runner-branch-cache-lru.test.ts +0 -110
  204. package/src/execution/__tests__/session-runner-close-prune.test.ts +0 -181
  205. package/src/execution/__tests__/session-runner-dispatch.test.ts +0 -489
  206. package/src/execution/__tests__/session-runner-epipe.test.ts +0 -179
  207. package/src/execution/__tests__/session-runner-heartbeat-idle-fallback.test.ts +0 -227
  208. package/src/execution/__tests__/session-runner-lifecycle-helpers.test.ts +0 -290
  209. package/src/execution/__tests__/session-runner-schema-env.test.ts +0 -352
  210. package/src/execution/__tests__/settled-watchdog.test.ts +0 -524
  211. package/src/execution/__tests__/spawn-args.test.ts +0 -446
  212. package/src/execution/__tests__/spawn-event-adapter-rpc.test.ts +0 -192
  213. package/src/execution/__tests__/spawn-event-adapter.test.ts +0 -167
  214. package/src/execution/__tests__/spawn-worktree-guidance.test.ts +0 -208
  215. package/src/execution/__tests__/spawned-children.test.ts +0 -236
  216. package/src/execution/__tests__/start-sync-model-guard.test.ts +0 -150
  217. package/src/execution/__tests__/stdin-writer.test.ts +0 -464
  218. package/src/execution/__tests__/subagent-service-message-close.test.ts +0 -693
  219. package/src/execution/__tests__/temp-prompt.test.ts +0 -53
  220. package/src/execution/__tests__/timeout-integration.test.ts +0 -616
  221. package/src/execution/__tests__/turn-limiter-semantics.test.ts +0 -194
  222. package/src/execution/__tests__/turn-limiter.test.ts +0 -65
  223. package/src/execution/__tests__/ui-request-handler.test.ts +0 -205
  224. package/src/execution/__tests__/ui-request-queue.test.ts +0 -298
  225. package/src/execution/__tests__/worktree-pid-registration.integration.test.ts +0 -230
  226. package/src/execution/engine/__tests__/common/schema-emulation.test.ts +0 -128
  227. package/src/execution/engine/__tests__/common/session-view-service-zcode-dbpath.test.ts +0 -149
  228. package/src/execution/engine/__tests__/conformance/__fixtures__/pi-golden-events.json +0 -28
  229. package/src/execution/engine/__tests__/conformance/zcode-appserver-harness.ts +0 -128
  230. package/src/execution/engine/__tests__/paths.test.ts +0 -39
  231. package/src/execution/engine/common/schema-emulation.ts +0 -189
  232. package/src/execution/engine/common/session-view-projection.ts +0 -51
  233. package/src/execution/engine/engines/pi/__tests__/pi-engine.test.ts +0 -615
  234. package/src/execution/engine/engines/pi/__tests__/reader.test.ts +0 -155
  235. package/src/execution/engine/engines/pi/__tests__/spawn-opts-direct.test.ts +0 -257
  236. package/src/execution/engine/engines/pi/argv-mirror.ts +0 -135
  237. package/src/execution/engine/engines/pi/get-state-handshake.ts +0 -179
  238. package/src/execution/engine/engines/pi/output-collector.ts +0 -317
  239. package/src/execution/engine/engines/pi/pi-engine.ts +0 -811
  240. package/src/execution/engine/engines/pi/pi-invocation.ts +0 -120
  241. package/src/execution/engine/engines/pi/reader.ts +0 -59
  242. package/src/execution/engine/engines/pi/registration.ts +0 -37
  243. package/src/execution/engine/engines/pi/session-runner.ts +0 -2840
  244. package/src/execution/engine/engines/pi/spawn-event-adapter.ts +0 -404
  245. package/src/execution/engine/engines/pi/stdin-writer.ts +0 -199
  246. package/src/execution/engine/engines/pi/temp-prompt.ts +0 -62
  247. package/src/execution/engine/engines/pi/turn-limiter.ts +0 -102
  248. package/src/execution/engine/engines/zcode/__tests__/__fixtures__/fake-appserver.mjs +0 -301
  249. package/src/execution/engine/engines/zcode/__tests__/__fixtures__/zcode-golden-appserver.json +0 -36
  250. package/src/execution/engine/engines/zcode/__tests__/appserver-launcher.test.ts +0 -321
  251. package/src/execution/engine/engines/zcode/__tests__/connection.test.ts +0 -473
  252. package/src/execution/engine/engines/zcode/__tests__/parser.test.ts +0 -43
  253. package/src/execution/engine/engines/zcode/__tests__/preparer.test.ts +0 -124
  254. package/src/execution/engine/engines/zcode/__tests__/reader.test.ts +0 -210
  255. package/src/execution/engine/engines/zcode/__tests__/registration.test.ts +0 -71
  256. package/src/execution/engine/engines/zcode/__tests__/session-channel-dispose-harvest.test.ts +0 -247
  257. package/src/execution/engine/engines/zcode/__tests__/session-channel-turn-timers.test.ts +0 -451
  258. package/src/execution/engine/engines/zcode/__tests__/session-channel.test.ts +0 -792
  259. package/src/execution/engine/engines/zcode/__tests__/zcode-engine-appserver.test.ts +0 -782
  260. package/src/execution/engine/engines/zcode/__tests__/zcode-engine-degrade.test.ts +0 -168
  261. package/src/execution/engine/engines/zcode/__tests__/zcode-engine-dispose.test.ts +0 -210
  262. package/src/execution/engine/engines/zcode/__tests__/zcode-engine-retry.test.ts +0 -353
  263. package/src/execution/engine/engines/zcode/__tests__/zcode-engine-status.test.ts +0 -282
  264. package/src/execution/engine/engines/zcode/__tests__/zcode-engine-timeout.test.ts +0 -372
  265. package/src/execution/engine/engines/zcode/__tests__/zcode-engine.live.test.ts +0 -145
  266. package/src/execution/engine/engines/zcode/appserver-launcher.ts +0 -167
  267. package/src/execution/engine/engines/zcode/connection.ts +0 -598
  268. package/src/execution/engine/engines/zcode/constants.ts +0 -176
  269. package/src/execution/engine/engines/zcode/golden-sample.ts +0 -47
  270. package/src/execution/engine/engines/zcode/parser.ts +0 -102
  271. package/src/execution/engine/engines/zcode/preparer.ts +0 -235
  272. package/src/execution/engine/engines/zcode/reader.ts +0 -381
  273. package/src/execution/engine/engines/zcode/registration.ts +0 -37
  274. package/src/execution/engine/engines/zcode/session-channel.ts +0 -968
  275. package/src/execution/engine/engines/zcode/zcode-engine.ts +0 -1431
  276. package/src/execution/round-settlement.ts +0 -93
  277. package/src/execution/ui-request-queue.ts +0 -215
  278. package/src/shared/schema-env.ts +0 -44
@@ -0,0 +1,255 @@
1
+ // src/execution/engine/client/reverse-router.ts
2
+ //
3
+ // 反向请求(帧④)路由器(EngineClient 的内部模块,从 engine-client.ts 拆出——
4
+ // max-lines 纪律 + 职责域独立:超时域二分(R9-2/R9-2b)的全部 core 侧语义都
5
+ // 落在本文件)。
6
+ //
7
+ // 超时域划分(impl-plan §2.2 / 设计 §3.3 帧④注释):
8
+ // - 快答数据面(host/log / host/streamDelta / host/poolResolved / host/handleReady /
9
+ // host/childSpawned / host/childStateChanged):分发 + 回 {ok:true},10s 应答守卫
10
+ // ——10s 未答 = 引擎故障 → 杀进程 + 在途 run 失败(REVERSE_REQUEST_TIMEOUT_MS);
11
+ // - 人机交互面(host/askUser / host/permission):ack 两阶段——收即回 {ack:true},
12
+ // handler 结果异步补帧②,**不计时**(R9-2:已 ack 的等待不参与任何 in-flight
13
+ // 超时,ADR-0047 静默 ≠ 卡死);未注入 handler → {unsupported:true};
14
+ // - 未知 host/* 通道 → {unsupported:true}(引擎自行降级,不重试)。
15
+
16
+ import {
17
+ getLogger,
18
+ REVERSE_CHANNEL_TIMEOUT_CLASS,
19
+ REVERSE_REQUEST_TIMEOUT_MS,
20
+ type HostAskUserParams,
21
+ type HostChildSpawnedParams,
22
+ type HostChildStateChangedParams,
23
+ type HostHandleReadyParams,
24
+ type HostLogParams,
25
+ type HostPermissionParams,
26
+ type HostPoolResolvedParams,
27
+ type HostRoundLifecycleParams,
28
+ type HostStreamDeltaParams,
29
+ type UiRequest,
30
+ } from "@zhushanwen/subagent-engine-sdk";
31
+
32
+ import type { SpawnedChildrenMirror } from "./mirror.ts";
33
+ import type { RunRoute } from "./engine-client.ts";
34
+
35
+ const logger = getLogger("subagents");
36
+
37
+ /** 路由器依赖面(EngineClient 注入;全部单向引用,无回环持有)。 */
38
+ export interface ReverseRouterDeps {
39
+ engineId: string;
40
+ /** host/askUser 应答端([D4-④] subagent-service init.uiRequestHandler 注入点)。 */
41
+ uiRequestHandler?: (req: UiRequest) => Promise<unknown>;
42
+ /** host/permission 应答端(v1 骨架注入点)。 */
43
+ permissionHandler?: (params: HostPermissionParams) => Promise<unknown>;
44
+ /** host/log 落宿主日志(缺省 SDK logger facade)。 */
45
+ log?: (params: HostLogParams) => void;
46
+ /** run 作用域通知路由表(EngineClient 持有,EngineClient 生命周期内同一引用)。 */
47
+ runRoutes: Map<string, RunRoute>;
48
+ /** [W3 v1.x] chat 轮次路由表(recordId 键;EngineClient 持有,同上)。 */
49
+ recordRoutes: Map<string, RunRoute>;
50
+ /** childSpawned/childStateChanged 的镜像落点。 */
51
+ mirror: SpawnedChildrenMirror;
52
+ /** handleReady 的 partial handle 回填(崩溃合成 handle 数据源)。 */
53
+ setPartialHandle: (partial: { sessionRef: Record<string, string>; poolKey: string }) => void;
54
+ /** 帧②应答出口。 */
55
+ sendResponse: (id: string, result: unknown) => boolean;
56
+ /** 引擎故障拉起杀链(killAll)。 */
57
+ failEngine: (reason: string) => Promise<void>;
58
+ /** 客户端是否已停机(停机后不再写帧)。 */
59
+ isDisposed: () => boolean;
60
+ }
61
+
62
+ /** 帧④入口(EngineClient.handleLine 委托)。 */
63
+ export function routeReverseRequest(deps: ReverseRouterDeps, frame: { id: string; method: string; params: unknown }): void {
64
+ if (frame.method === "host/askUser") {
65
+ handleInteractionRequest(deps, frame, deps.uiRequestHandler, (params) => (params as HostAskUserParams).request as UiRequest);
66
+ return;
67
+ }
68
+ if (frame.method === "host/permission") {
69
+ handleInteractionRequest(deps, frame, deps.permissionHandler, (params) => params as HostPermissionParams);
70
+ return;
71
+ }
72
+ const timeoutClass = (REVERSE_CHANNEL_TIMEOUT_CLASS as Record<string, string | undefined>)[frame.method];
73
+ if (timeoutClass === "data-plane") {
74
+ handleDataPlaneRequest(deps, frame);
75
+ return;
76
+ }
77
+ deps.sendResponse(frame.id, { unsupported: true });
78
+ }
79
+
80
+ /**
81
+ * 人机交互面 ack 两阶段(R9-2):先回 {ack:true},handler 结果异步补帧②。
82
+ * **不计时**——已 ack 的等待属人机交互异步等待,不参与任何 in-flight 超时;
83
+ * handler 永不 resolve(用户不答)也不判引擎故障。handler 抛错兜底
84
+ * {cancelled:true}(dialog-queue 应答链同款语义)。
85
+ */
86
+ function handleInteractionRequest<P>(
87
+ deps: ReverseRouterDeps,
88
+ frame: { id: string; method: string; params: unknown },
89
+ handler: ((input: P) => Promise<unknown>) | undefined,
90
+ extract: (params: unknown) => P,
91
+ ): void {
92
+ if (handler === undefined) {
93
+ deps.sendResponse(frame.id, { unsupported: true });
94
+ return;
95
+ }
96
+ deps.sendResponse(frame.id, { ack: true });
97
+ let input: P;
98
+ try {
99
+ input = extract(frame.params);
100
+ } catch {
101
+ deps.sendResponse(frame.id, { cancelled: true });
102
+ return;
103
+ }
104
+ void (async () => {
105
+ try {
106
+ const result = await handler(input);
107
+ if (!deps.isDisposed()) deps.sendResponse(frame.id, result);
108
+ } catch (err) {
109
+ logger.warn(
110
+ `[engine-client:${deps.engineId}] ${frame.method} handler failed, answering cancelled: ${
111
+ err instanceof Error ? err.message : String(err)
112
+ }`,
113
+ );
114
+ if (!deps.isDisposed()) deps.sendResponse(frame.id, { cancelled: true });
115
+ }
116
+ })();
117
+ }
118
+
119
+ /**
120
+ * 快答数据面(R9-2 二分):分发 + 回 {ok:true},全程受 10s 应答守卫——10s 未答 =
121
+ * 引擎故障 → 杀进程 + 在途 run 失败。当前各通道分发都是快操作;守卫覆盖的是
122
+ * 「handler 返回的 promise 挂住」的演化面(挂起 = 宿主消费方死锁,须 fail-fast
123
+ * 拉起杀链,防止 core 被单通道拖死)。分发抛错不判引擎故障(宿主回调 bug 不该
124
+ * 杀引擎),仍回 {ok:true} + warn 留痕。
125
+ */
126
+ function handleDataPlaneRequest(deps: ReverseRouterDeps, frame: { id: string; method: string; params: unknown }): void {
127
+ let settled = false;
128
+ const guardTimer = setTimeout(() => {
129
+ if (settled) return;
130
+ logger.error(
131
+ `[engine-client:${deps.engineId}] data-plane reverse request ${frame.method} `
132
+ + `unanswered for ${REVERSE_REQUEST_TIMEOUT_MS}ms — treating as engine fault`,
133
+ );
134
+ void deps.failEngine(
135
+ `data-plane reverse request ${frame.method} unanswered for ${REVERSE_REQUEST_TIMEOUT_MS}ms (engine fault)`,
136
+ );
137
+ }, REVERSE_REQUEST_TIMEOUT_MS);
138
+ void Promise.resolve()
139
+ .then(() => dispatchDataPlane(deps, frame.method, frame.params))
140
+ .then(
141
+ () => {
142
+ settled = true;
143
+ clearTimeout(guardTimer);
144
+ deps.sendResponse(frame.id, { ok: true });
145
+ },
146
+ (err: unknown) => {
147
+ settled = true;
148
+ clearTimeout(guardTimer);
149
+ logger.warn(
150
+ `[engine-client:${deps.engineId}] ${frame.method} dispatch failed (answering ok, host-side bug): ${
151
+ err instanceof Error ? err.message : String(err)
152
+ }`,
153
+ );
154
+ deps.sendResponse(frame.id, { ok: true });
155
+ },
156
+ );
157
+ }
158
+
159
+ /** 数据面单通道分发器(params 形态自证——发帧前引擎侧自检,分发层只做强转)。 */
160
+ type DataPlaneHandler = (deps: ReverseRouterDeps, params: unknown) => Promise<void> | void;
161
+
162
+ /** method → 分发器表(表外 method = 无操作;路由面准入已由 REVERSE_CHANNEL_TIMEOUT_CLASS 收敛为 data-plane)。 */
163
+ const DATA_PLANE_HANDLERS: Record<string, DataPlaneHandler> = {
164
+ "host/log": (deps, params) => dispatchLog(deps, params as HostLogParams),
165
+ "host/streamDelta": (deps, params) => dispatchStreamDelta(deps, params as HostStreamDeltaParams),
166
+ "host/roundLifecycle": (deps, params) => dispatchRoundLifecycle(deps, params as HostRoundLifecycleParams),
167
+ "host/poolResolved": (deps, params) => dispatchPoolResolved(deps, params as HostPoolResolvedParams),
168
+ "host/handleReady": (deps, params) => dispatchHandleReady(deps, params as HostHandleReadyParams),
169
+ "host/childSpawned": (deps, params) => dispatchChildSpawned(deps, params as HostChildSpawnedParams),
170
+ "host/childStateChanged": (deps, params) => dispatchChildStateChanged(deps, params as HostChildStateChangedParams),
171
+ };
172
+
173
+ async function dispatchDataPlane(deps: ReverseRouterDeps, method: string, params: unknown): Promise<void> {
174
+ await DATA_PLANE_HANDLERS[method]?.(deps, params);
175
+ }
176
+
177
+ function dispatchLog(deps: ReverseRouterDeps, p: HostLogParams): void {
178
+ if (deps.log !== undefined) deps.log(p);
179
+ else logger[p.level](`[engine:${p.component}] ${p.message}`, p.data);
180
+ }
181
+
182
+ async function dispatchStreamDelta(deps: ReverseRouterDeps, p: HostStreamDeltaParams): Promise<void> {
183
+ // [W3 v1.x 接线(W1 偏差 #1 的替换落地)] 关联键分路(D1-A):
184
+ // - recordId 键 = chat 续聊轮 delta(interact 发起,无独立 runId)→ recordRoutes;
185
+ // - runId 键 = run 域轮(含 run 会话形态首轮)→ runRoutes(v1 现状不变)。
186
+ // 无注册路由 = 该轮宿主消费面未挂(诊断形态)——静默丢弃(数据面已 ack,
187
+ // 引擎侧不重发;路由缺席非引擎故障)。
188
+ if (p.recordId !== undefined) {
189
+ await deps.recordRoutes.get(p.recordId)?.onStreamDelta?.(p.delta);
190
+ return;
191
+ }
192
+ await deps.runRoutes.get(p.runId)?.onStreamDelta?.(p.delta);
193
+ }
194
+
195
+ async function dispatchRoundLifecycle(deps: ReverseRouterDeps, p: HostRoundLifecycleParams): Promise<void> {
196
+ // [W3 v1.x] 第 9 反向通道消费(settled/idle/failed 三相位 × runId|recordId 键)。
197
+ // 相位语义(arm/disarm/交棒/失败分诊)归宿主编排层——本路由只做键分发,
198
+ // 消费方 = RunContext.onRoundLifecycle(首轮 runId 键)与 registerChatRoundRoute
199
+ // 路由(续聊轮 recordId 键)。载荷形状自证(isHostRoundLifecycleParams)归
200
+ // 引擎侧发帧前自检 + 消费方,分发层不重复判别(与 streamDelta 一致的薄分发)。
201
+ if (p.recordId !== undefined) {
202
+ await deps.recordRoutes.get(p.recordId)?.onRoundLifecycle?.(p);
203
+ return;
204
+ }
205
+ await deps.runRoutes.get(p.runId)?.onRoundLifecycle?.(p);
206
+ }
207
+
208
+ async function dispatchPoolResolved(deps: ReverseRouterDeps, p: HostPoolResolvedParams): Promise<void> {
209
+ await deps.runRoutes.get(p.runId)?.onPoolResolved?.(p.poolKey);
210
+ }
211
+
212
+ async function dispatchHandleReady(deps: ReverseRouterDeps, p: HostHandleReadyParams): Promise<void> {
213
+ deps.setPartialHandle({ sessionRef: p.sessionRef, poolKey: p.poolKey });
214
+ await deps.runRoutes.get(p.runId)?.onHandleReady?.({
215
+ sessionRef: p.sessionRef,
216
+ poolKey: p.poolKey,
217
+ });
218
+ }
219
+
220
+ function dispatchChildSpawned(deps: ReverseRouterDeps, p: HostChildSpawnedParams): void {
221
+ deps.mirror.recordSpawned(p.pid, p.recordId);
222
+ // POSIX 运行时组探测(W10 / A3 前提②):任务子进程应与引擎同组(收割 =
223
+ // 引擎进程组级 kill(-enginePid))。kill(-pid, 0) 成功 ⟺ 该 pid 自成进程组
224
+ // (setsid/detached 后代)⟺ 不在引擎收割组内 → 告警留痕(不判 fail——
225
+ // detached 后代是设计 §3.9 已接受代价)。Windows 无外部判据(无 kill(-pid,0)
226
+ // 等价物),仅 SDK 层 spawnEngineChild 形态保证,真机面挂 A3 手动门。
227
+ if (process.platform !== "win32" && typeof p.pid === "number") {
228
+ try {
229
+ process.kill(-p.pid, 0);
230
+ logger.warn(
231
+ `[engine-client:${deps.engineId}] childSpawned pid ${p.pid} leads its own process group `
232
+ + `(not in the engine harvest group — one-generation children + in-group descendants `
233
+ + `only; engine-detached descendants are an accepted cost per design §3.9)`,
234
+ );
235
+ } catch (err) {
236
+ // ESRCH = pid 非组长 = 在引擎组内(预期形态);其余 errno 同按预期形态
237
+ // debug 留痕(组探测异常排查时的最低可见性,不判 fail)。
238
+ logger.debug(
239
+ `[engine-client:${deps.engineId}] childSpawned pid ${p.pid} group probe settled ` +
240
+ `(${err instanceof Error ? err.message : String(err)})`,
241
+ );
242
+ }
243
+ }
244
+ }
245
+
246
+ function dispatchChildStateChanged(deps: ReverseRouterDeps, p: HostChildStateChangedParams): void {
247
+ deps.mirror.recordStateChanged({
248
+ pid: p.pid,
249
+ recordId: p.recordId,
250
+ state: p.state,
251
+ killed: p.killed,
252
+ exitCode: p.exitCode,
253
+ signal: p.signal,
254
+ });
255
+ }
@@ -1,26 +1,49 @@
1
1
  // src/execution/engine/common/capability-gate.ts
2
2
  //
3
3
  // [D3-④ 预检 capabilities 化] 调用前预检的唯一实现(capabilities 驱动,无引擎 id
4
- // 特判)。设计权威源:docs/design/subagent-dual-track-convergence.md §3.3 D3-④ + r3 裁定
5
- // (EngineCapabilities 新增 maxTurns 能力位,不保留硬编码 shape 检查)+ §3.4 错误规格
6
- // 1 行(engine_capability_unsupported,既有错误族)。
4
+ // 特判)。设计权威源(现行):docs/design/subagent-engine-protocolization.md §3.3
5
+ // 「能力位」段(manifest 权威 + 方向判定表)+ 历史源 docs/design/subagent-dual-track-
6
+ // convergence.md §3.3 D3-④ + r3 裁定(EngineCapabilities 新增 maxTurns 能力位)+
7
+ // 错误规格(engine_capability_unsupported / engine_capability_mismatch)。
7
8
  //
8
9
  // 判据形态:每个引擎拦截「自己 capabilities 声明不支持的能力」——
9
10
  // - conversation → capabilities.conversation === 'unsupported'
10
11
  // - fork/forkFromSessionFile → session 分叉通道族全缺(steer 与 conversation 均
11
- // 'unsupported',见下方 fork 判据说明)
12
+ // 'unsupported',OR 语义任一可用即放行,见下方 fork 判据说明)
12
13
  // - maxTurns → capabilities.maxTurns === false(r3 扩位)
13
14
  // - worktree(boolean true 或 WorktreeHandle)→ capabilities.sandbox === 'none'
14
15
  //
16
+ // [W3 协议化能力位方向判定(设计 §3.3 能力位表)] capabilities 的同步源 = manifest
17
+ // 快照(注册期读,非握手缓存)。被 gate 四类判据涉及的能力位(conversation /
18
+ // steer+conversation / maxTurns / sandbox)**manifest 是权威契约**,双向处置:
19
+ // - **少声明**(任务要求的能力 manifest 未声明)→ 本文件同步拒
20
+ // engine_capability_unsupported;恢复指引 = 「修 manifest / 升级引擎包」——不写
21
+ // 「先派一次任务触发握手」(gate 读 manifest,握手不参与同步判据,那个指引是空转);
22
+ // - **多声明**(manifest 声明支持而引擎实际不支持)→ gate 读不到(同步面只有
23
+ // manifest),由首个 run 的协议握手 `initialize` 发现 → engine_capability_mismatch
24
+ // 该 run 失败 + record 标 failed + **清理 run 前已建的前置副作用**(worktree:
25
+ // executeViaEngine 在 kickOffEngineRun 前创建,经 finalizeFailed → finalizeRecord
26
+ // Step 3b cleanupWorktreeIfBound 清理)。判定函数 = 本文件
27
+ // assertGateCapabilitiesMatched(core 侧判据;协议客户端握手尾接线归 client/ 领地)。
28
+ // - **非 gate 位**(personaInjection / eventGranularity / sessionRead / resume /
29
+ // interrupt / permissionMode / schemaEnforcement)不一致(无论强弱)→ 一律 warn
30
+ // 留痕不阻断(诊断面 = EngineClient.warnOnManifestDiagnostics)。
31
+ //
15
32
  // 调用点仍是两处(单点的是实现,不是调用点):
16
33
  // - chat 域:executeViaEngine 同步段、record 创建前(engine.capabilities() 同步可得,
17
34
  // 承接「全部同步拒绝发生在 record 创建前、不产生孤儿 record」不变量);
18
35
  // - workflow 域:SAR.run 路由后、engine.run 前(同模块调用)。
19
36
  //
20
- // 行为变化声明(§3.4):唯一有意行为变化 = workflow 域 zcode+worktree 由漏拦变拦截
21
- // (修复双轨清单 #7 的跨域缺口);pi 的 maxTurns/fork/conversation/worktree 等既有
22
- // 合法能力零拦截(V4⑤ 反向守护——pi 声明 conversation='native'、sandbox='emulated'、
23
- // maxTurns=true,全数放行)。
37
+ // 行为变化声明:唯一有意行为变化 = workflow 域 zcode+worktree 由漏拦变拦截(修复
38
+ // 双轨清单 #7 的跨域缺口);pi 的 maxTurns/fork/conversation/worktree 等既有合法
39
+ // 能力零拦截(V4⑤ 反向守护)。[W3] recovery 文案随协议化变更:恢复指引主指向
40
+ // 「修 manifest / 升级引擎包」(引擎包是能力声明的载体,换引擎不再是缺省指引——
41
+ // 协议化后不存在「内置 pi 恒可用」兜底)。
42
+
43
+ // [W3 v1.x] conversation gate 判据单源 = SDK 具名构造器(W1 交接防漂移项:core 内联
44
+ // 文案与引擎侧 assertChatConversationSupported 双份文案的漂移面消除——同一构造器
45
+ // 两侧消费,错误码/恢复指引契约单一权威)。
46
+ import { engineConversationUnsupportedError } from "@zhushanwen/subagent-engine-sdk";
24
47
 
25
48
  import { EngineError } from "./errors.ts";
26
49
  import type { EngineCapabilities } from "../types.ts";
@@ -38,17 +61,20 @@ export interface TaskShapeForGate {
38
61
  maxTurns?: number;
39
62
  }
40
63
 
64
+ /** 恢复指引公共尾段(W3 协议化口径:能力声明的修复通道 = manifest / 引擎包升级)。 */
65
+ const MANIFEST_RECOVERY_TAIL = "修 manifest capabilities / 升级引擎包(若引擎实际支持该能力)";
66
+
41
67
  /**
42
68
  * 调用前预检:任务形状中的能力参数对引擎 capabilities 逐一对照,声明不支持即抛
43
69
  * EngineError(engine_capability_unsupported)——同步 throw、进程/record 创建前,文案
44
- * 含 capabilities 依据与恢复指引(换参数/换引擎)。
70
+ * 含 capabilities 依据与恢复指引(修 manifest / 升级引擎包 / 调整任务参数)。
45
71
  *
46
72
  * fork 判据说明(借位裁定):fork 依赖父 session 上下文继承(父会话文件作为分叉源),
47
73
  * 引擎具备该语义的能力面信号 = 会话分叉/交互通道族(steer 或 conversation 任一非
48
- * 'unsupported')。pi conversation='native'(chatMode idle 复用)→ 放行;zcode 双
49
- * 'unsupported'(argv-only spawn 单轮,无父 session 分叉通道)→ 拒绝。仅凭 steer 判
50
- * 会误拦 pi(pi 的 steer 声明 'unsupported'——RPC 有但 spawn 链路未接通,与 fork 的
51
- * 初始上下文继承是两条轴),故取通道族任一可用即支持的分寸。
74
+ * 'unsupported',**OR 语义任一可用即放行**)。pi conversation='native'(chatMode idle
75
+ * 复用)→ 放行;zcode 双 'unsupported'(argv-only spawn 单轮,无父 session 分叉通道)
76
+ * 拒绝。仅凭 steer 判会误拦 pi(pi 的 steer 声明 'unsupported'——RPC 有但 spawn
77
+ * 链路未接通,与 fork 的初始上下文继承是两条轴),故取通道族任一可用即支持的分寸。
52
78
  */
53
79
  export function assertTaskShapeSupported(
54
80
  engineId: string,
@@ -56,12 +82,11 @@ export function assertTaskShapeSupported(
56
82
  task: TaskShapeForGate,
57
83
  ): void {
58
84
  if (task.conversation === true && caps.conversation === "unsupported") {
59
- throw new EngineError(
60
- "engine_capability_unsupported",
61
- `engine '${engineId}' 不支持 conversationcapabilities.conversation = 'unsupported',` +
62
- `spawn 单轮模式无同进程 idle 复用,message/close 交互控制面不可用)`,
63
- `改用 engine: pi(支持 conversation 续聊),或不传该参数(一次性任务默认形态)`,
64
- );
85
+ // [W1 交接防漂移项落地] 文案/错误码/恢复指引单源 = SDK engineConversationUnsupportedError
86
+ // (引擎侧 server.run chat gate 消费同一构造器——A6 双向负向面两侧同文案)。
87
+ // 错误载体类型 EngineError → EngineSdkErrorcode/recovery/message 前缀契约不变,
88
+ // name 变化不影响消费方——无 instanceof EngineError 判据,code 字段分流)。
89
+ throw engineConversationUnsupportedError(engineId);
65
90
  }
66
91
  if (task.fork === true || task.forkFromSessionFile !== undefined) {
67
92
  if (caps.steer === "unsupported" && caps.conversation === "unsupported") {
@@ -69,7 +94,7 @@ export function assertTaskShapeSupported(
69
94
  "engine_capability_unsupported",
70
95
  `engine '${engineId}' 不支持 fork${task.forkFromSessionFile !== undefined ? "(fork-from 同为父 session 上下文继承)" : ""}(fork 依赖父 session 上下文继承,` +
71
96
  `capabilities.steer = '${caps.steer}' / conversation = '${caps.conversation}'——引擎无父 session 分叉通道)`,
72
- `把所需父上下文写进 task 正文后不传 fork,或改用 engine: pi`,
97
+ `把所需父上下文写进 task 正文后不传 fork,或 ${MANIFEST_RECOVERY_TAIL}`,
73
98
  );
74
99
  }
75
100
  }
@@ -78,7 +103,7 @@ export function assertTaskShapeSupported(
78
103
  "engine_capability_unsupported",
79
104
  `engine '${engineId}' 不支持 maxTurns(capabilities.maxTurns = false,轮数上限依赖 turn_end 事件流,` +
80
105
  `本引擎无此语义——静默丢弃会造成「传了上限却失控」的假象)`,
81
- `去掉 maxTurns 参数重派,或改用 engine: pi(turn limiter 执行轮数上限)`,
106
+ `去掉 maxTurns 参数重派,或 ${MANIFEST_RECOVERY_TAIL}`,
82
107
  );
83
108
  }
84
109
  if ((task.worktree === true || typeof task.worktree === "object") && caps.sandbox === "none") {
@@ -86,7 +111,69 @@ export function assertTaskShapeSupported(
86
111
  "engine_capability_unsupported",
87
112
  `engine '${engineId}' 不支持 worktree 隔离(capabilities.sandbox = 'none',` +
88
113
  `引擎未接文件系统隔离层)`,
89
- `改用 engine: pi(worktree 隔离可用),或不传该参数(在 parent cwd 执行)`,
114
+ `去掉 worktree 参数(在 parent cwd 执行),或 ${MANIFEST_RECOVERY_TAIL}`,
90
115
  );
91
116
  }
92
117
  }
118
+
119
+ // ============================================================
120
+ // 能力位方向判定②:manifest 多声明 → run 期握手发现(engine_capability_mismatch)
121
+ // ============================================================
122
+
123
+ /**
124
+ * manifest vs `initialize` 应答的 gate 位方向判定(设计 §3.3 能力位表「多声明」行)。
125
+ *
126
+ * 只判被 gate 四类判据涉及的能力位中「manifest 声明可用、引擎应答不支持」的多声明
127
+ * 方向(少声明方向在派发前已被 assertTaskShapeSupported 同步拦,握手不会发生——gate
128
+ * 先行的结构性结果);命中抛 EngineError(engine_capability_mismatch),由首个 run 的
129
+ * 协议路径转化为该 run 失败 + record 标 failed + 清理前置副作用(契约变更⑤)。
130
+ * 非 gate 位不一致不进本判定(无论强弱一律 warn 留痕,归 EngineClient 诊断面)。
131
+ *
132
+ * 各 gate 位的「manifest 可用」取值语义与 assertTaskShapeSupported 逐条对偶:
133
+ * conversation 应答 'unsupported' 而 manifest 非 'unsupported' → mismatch;
134
+ * fork 通道族应答双 'unsupported' 而 manifest 任一可用 → mismatch(OR 对偶);
135
+ * maxTurns 应答 false 而 manifest true → mismatch;
136
+ * sandbox 应答 'none' 而 manifest 非 'none' → mismatch。
137
+ *
138
+ * 接线位置(领地边界声明):本函数是 core 侧判据单源;协议客户端在首个 run 前握手
139
+ * 后(EngineClient.initialize 应答 → RemoteEngine.run 发 run 帧前)调用——client/ 为
140
+ * W2 已交付领地,接线归后续单元(W8/W10),不属本文件。
141
+ */
142
+ export function assertGateCapabilitiesMatched(
143
+ engineId: string,
144
+ manifestCaps: EngineCapabilities,
145
+ answeredCaps: EngineCapabilities,
146
+ ): void {
147
+ if (
148
+ manifestCaps.conversation !== "unsupported" &&
149
+ answeredCaps.conversation === "unsupported"
150
+ ) {
151
+ throw gateMismatchError(engineId, "conversation", manifestCaps.conversation, answeredCaps.conversation);
152
+ }
153
+ const manifestForkCapable = manifestCaps.steer !== "unsupported" || manifestCaps.conversation !== "unsupported";
154
+ const answeredForkCapable = answeredCaps.steer !== "unsupported" || answeredCaps.conversation !== "unsupported";
155
+ if (manifestForkCapable && !answeredForkCapable) {
156
+ throw gateMismatchError(
157
+ engineId,
158
+ "fork(steer/conversation 通道族)",
159
+ `steer=${manifestCaps.steer}/conversation=${manifestCaps.conversation}`,
160
+ `steer=${answeredCaps.steer}/conversation=${answeredCaps.conversation}`,
161
+ );
162
+ }
163
+ if (manifestCaps.maxTurns === true && answeredCaps.maxTurns === false) {
164
+ throw gateMismatchError(engineId, "maxTurns", String(manifestCaps.maxTurns), String(answeredCaps.maxTurns));
165
+ }
166
+ if (manifestCaps.sandbox !== "none" && answeredCaps.sandbox === "none") {
167
+ throw gateMismatchError(engineId, "sandbox(worktree)", manifestCaps.sandbox, answeredCaps.sandbox);
168
+ }
169
+ }
170
+
171
+ /** engine_capability_mismatch 的统一构造(manifest 权威 + 恢复指引 = 修 manifest / 升级引擎包)。 */
172
+ function gateMismatchError(engineId: string, cap: string, declared: string, answered: string): EngineError {
173
+ return new EngineError(
174
+ "engine_capability_mismatch",
175
+ `engine '${engineId}' manifest 声明的能力位 '${cap}'(${declared})与引擎 initialize 应答不符(${answered})` +
176
+ `——manifest 多声明,任务按 gate 判据放行后引擎无法兑现`,
177
+ `修 manifest capabilities(与引擎实际能力对齐)或升级引擎包;若任务不再需要该能力,调整任务参数后重派`,
178
+ );
179
+ }
@@ -25,6 +25,7 @@ export const ENGINE_ERROR_CODES = [
25
25
  "schema_emulation_failed",
26
26
  "engine_timeout",
27
27
  "engine_capability_unsupported",
28
+ "engine_capability_mismatch",
28
29
  "engine_session_not_resumable",
29
30
  "model_not_available",
30
31
  "prompt_too_large",
@@ -94,8 +95,11 @@ export const DEFAULT_RECOVERY_HINTS: Record<EngineErrorCode, string> = {
94
95
  "The engine was killed by the host timeout chain. Inspect the captured stdout tail, then re-run with a larger " +
95
96
  "timeout, a narrower task, or `engine: pi`.",
96
97
  engine_capability_unsupported:
97
- "This engine declares the capability unsupported. Use a single-shot call instead of interactive steering, " +
98
- "or dispatch with `engine: pi` which supports it.",
98
+ "This engine declares the capability unsupported. Adjust the task parameters, " +
99
+ "or fix the manifest capabilities / upgrade the engine package if it actually supports it.",
100
+ engine_capability_mismatch:
101
+ "The engine manifest declares a capability its initialize handshake did not confirm. " +
102
+ "Fix the manifest capabilities (align with actual engine behavior) or upgrade the engine package.",
99
103
  engine_session_not_resumable:
100
104
  "Idle-process reuse does not survive a main-session reload. Use a cold resume path " +
101
105
  "(engine --resume / --session with the recorded session reference), or start a new subagent.",
@@ -113,9 +117,6 @@ export const DEFAULT_RECOVERY_HINTS: Record<EngineErrorCode, string> = {
113
117
  // P2 消费的具名构造器(动态参数进 detail/recovery)
114
118
  // ============================================================
115
119
 
116
- /** 错误回显长度上限(截断长输出,避免错误消息爆炸——对齐 structured-output echo 上限量级)。 */
117
- const DETAIL_ECHO_MAX_CHARS = 200;
118
-
119
120
  /** truncate(text, max):尾部截断 + 省略号标记(模板共用)。 */
120
121
  function truncate(text: string, max: number): string {
121
122
  return text.length <= max ? text : `${text.slice(0, max)}...`;
@@ -133,15 +134,6 @@ export function promptTooLargeError(actualBytes: number, limitBytes: number): En
133
134
  );
134
135
  }
135
136
 
136
- /** nested_spawn_rejected(nesting-guard 消费):文案说明防护规则 + 指向 task 内自行完成。 */
137
- export function nestedSpawnRejectedError(): EngineError {
138
- return new EngineError(
139
- "nested_spawn_rejected",
140
- "this process is already a subagent (XYZ_AGENT_SUBAGENT=1)",
141
- DEFAULT_RECOVERY_HINTS.nested_spawn_rejected,
142
- );
143
- }
144
-
145
137
  /** stdout 尾部回显上限(engine_timeout / engine_run_failed 的错误规格载体系数)。 */
146
138
  export const STDOUT_TAIL_ECHO_CHARS = 2000;
147
139
 
@@ -170,14 +162,6 @@ export function engineRunFailedDetail(reason: string, exitCode: number | null, s
170
162
  );
171
163
  }
172
164
 
173
- /**
174
- * schema_emulation_failed 的终报文案(宿主编排层「重试一次仍失败」后消费):
175
- * 与 structured-output 的重试语义对齐(重试一次 → 报错含原始输出尾部)。
176
- */
177
- export function schemaEmulationFailedDetail(error: string, tail: string): string {
178
- return (
179
- `structured output emulation failed after tolerant extraction and one host-side retry: ${error}. ` +
180
- `Raw output tail: ${truncate(tail, DETAIL_ECHO_MAX_CHARS)}. ` +
181
- `Recovery: ${DEFAULT_RECOVERY_HINTS.schema_emulation_failed}`
182
- );
183
- }
165
+ // schema_emulation_failed 的终报文案(schemaEmulationFailedDetail)已随 schema-emulation
166
+ // 死镜像收口删除:活体单源 SDK error-codes.ts(emulated 引擎的宿主编排层经 SDK 消费),
167
+ // core 侧此前仅剩自身测试引用(test-only)。
@@ -14,7 +14,6 @@
14
14
  // warn 一次 + 置 failed,后续 append 丢弃、close 立即返回(不静默:warn 有留痕)。
15
15
 
16
16
  import { appendFile, mkdir, open } from "node:fs/promises";
17
- import { readFileSync } from "node:fs";
18
17
  import { dirname } from "node:path";
19
18
 
20
19
  import { getLogger } from "../../../core/logger.ts";
@@ -203,52 +202,7 @@ function defaultWarn(msg: string): void {
203
202
  // 重放
204
203
  // ============================================================
205
204
 
206
- /**
207
- * 重放 journal:读取路径下全部事件,重放即得 AgentEvent 流(read 第②级)。
208
- *
209
- * - 文件不存在 [](降级链语义:②级不可达不算错误,调用方落 ③级);
210
- * - 损坏行跳过(追加写产物末行可能截断;跳过优于整体失败——设计 C5「三级都不 throw」);
211
- * - 按 seq 稳定排序后返回(重放顺序权威是 seq,不依赖文件行序的隐式保证,§3.3.6)。
212
- */
213
- export function replayJournal(path: string): AgentEvent[] {
214
- let raw: string;
215
- try {
216
- raw = readFileSync(path, "utf8");
217
- } catch {
218
- return [];
219
- }
220
- const lines: JournalLine[] = [];
221
- for (const row of raw.split("\n")) {
222
- const trimmed = row.trim();
223
- if (trimmed === "") continue;
224
- const parsed = parseLine(trimmed);
225
- if (parsed !== undefined) lines.push(parsed);
226
- }
227
- lines.sort((a, b) => a.seq - b.seq);
228
- return lines.map((l) => l.event);
229
- }
230
-
231
- /** 单行 parse + 结构 guard(v=1 + event 形状最小判别:object 且 type 为 string)。 */
232
- function parseLine(trimmed: string): JournalLine | undefined {
233
- let parsed: unknown;
234
- try {
235
- parsed = JSON.parse(trimmed);
236
- } catch {
237
- return undefined;
238
- }
239
- if (typeof parsed !== "object" || parsed === null) return undefined;
240
- const rec = parsed as Record<string, unknown>;
241
- const event = rec.event;
242
- if (rec.v !== 1 || typeof rec.ts !== "number" || typeof rec.seq !== "number") return undefined;
243
- if (typeof event !== "object" || event === null || typeof (event as Record<string, unknown>).type !== "string") {
244
- return undefined;
245
- }
246
- return {
247
- v: 1,
248
- ts: rec.ts,
249
- taskId: typeof rec.taskId === "string" ? rec.taskId : "",
250
- engineId: typeof rec.engineId === "string" ? rec.engineId : "",
251
- seq: rec.seq,
252
- event: event as AgentEvent,
253
- };
254
- }
205
+ // replayJournal/parseLine 已收编 SDK journal-io(S4 簇 6:zcode 引擎包不能 import
206
+ // core,双包逐字等价副本单源落 SDK;此处 re-export journal-replay.ts /
207
+ // session-view-service.ts 消费路径零改动)。行格式权威仍 §3.3.6,行为逐字等价。
208
+ export { replayJournal } from "@zhushanwen/subagent-engine-sdk";
@@ -10,9 +10,11 @@
10
10
 
11
11
  import { createRecord, updateFromEvent } from "../../execution-record.ts";
12
12
  import type { AgentEvent } from "../../types.ts";
13
+ // aggregateUsage / toReplayedTurn 单源 SDK(自 session-view-projection.ts 逐字等价
14
+ // 迁入后收口——双活副本删除,live ≡ replay 的投影语义不再靠双份手工同步)。
15
+ import { aggregateUsage, toReplayedTurn } from "@zhushanwen/subagent-engine-sdk";
13
16
  import type { EngineHandle, SessionView } from "../types.ts";
14
17
  import { replayJournal } from "./event-journal.ts";
15
- import { aggregateUsage, toReplayedTurn } from "./session-view-projection.ts";
16
18
 
17
19
  /**
18
20
  * journal → SessionView(read 第②级)。