@zhushanwen/subagent-core 0.6.0 → 0.7.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 (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
@@ -43,6 +43,16 @@ export interface FinalizeDeps {
43
43
  pi: { appendEntry?: (type: string, data: unknown) => void } | null;
44
44
  /** pending-notifications 终态注销(绑定 pi.events.emit,由调用方闭包提供)。 */
45
45
  emitUnregister(id: string, status: string): void;
46
+ /**
47
+ * [F-5 修复] record 终态化的宿主侧收口钩子(recordId;best-effort,抛错由调用方
48
+ * 在闭包内自行兜底)。单一汇聚点:doFinalizeRecord 是全部 closed 终态的必经路径
49
+ * (finalizeRecord / closeChatIdle / closeAfterRoundSettled / finalizeFailed /
50
+ * finalizeAborted / consumeCloseAfterRound),chat 轮路由注销(chatRoundRoutes)
51
+ * 由 SubagentService 经此钩子统一执行——此前仅 cancelBackground 注销,chat record
52
+ * 经 finalize 链终态化后路由闭包(持 record/stream/守护引用)泄漏。finalizeRoundToIdle
53
+ * 不经本钩子(回 idle 非终态,续聊仍需路由)。
54
+ */
55
+ onFinalized?: (recordId: string) => void;
46
56
  /**
47
57
  * [T1/PS-9] subagent sessionDir(getSubagentSessionDir(agentDir, rootCwd),调用方注入)。
48
58
  *
@@ -254,8 +264,23 @@ export async function doFinalizeRecord(
254
264
  removeAliveMarkerIfPresent(record);
255
265
 
256
266
  // pending-notifications:终态注销(只记 registry 状态,通知由 BgNotifier 发)
267
+ // [W4 发射点枚举归属①] 注销合法发射点枚举(设计 D2)第 ① 处:subagent record
268
+ // 终态化(finalizeRecord 路径,含监督器放弃)。其余合法发射点:② chatMode 轮末
269
+ // idle(doFinalizeRoundToIdle,本文件下方);③ workflow run 终态迁移
270
+ // (transition("done") 路径);④ 监督器显式放弃(走本路径,终态化+注销同批);
271
+ // ⑤ 注册对账 sweep 补发(round-supervisor/reconcile-sweep.ts)。进程退出本身
272
+ // 永远不是注销理由(subagent-service disposeAllRecords 的 emit 属①——其同批
273
+ // completeRecord+archive 终态化)。
257
274
  deps.emitUnregister(record.id, status);
258
275
 
276
+ // [F-5 修复] 宿主侧终态收口钩子(chat 轮路由注销的单一汇聚点,见 FinalizeDeps
277
+ // .onFinalized 注释)。fire-and-forget:钩子失败不阻断 manifest 收尾(闭包内自查)。
278
+ try {
279
+ deps.onFinalized?.(record.id);
280
+ } catch (err) {
281
+ bestEffort(err, "onFinalized hook (finalizeRecord)");
282
+ }
283
+
259
284
  // ── Step 4 (last): manifest 持久化(best-effort,不阻断、不 throw)──
260
285
  await writeManifestBestEffort(deps, record);
261
286
  }
@@ -2,6 +2,22 @@
2
2
  * SP-4 idle record GC:30 天 TTL 定时归档,防 idle record 永久驻留内存。
3
3
  * 从 subagent-service.ts 抽出(文件超 max-lines;逻辑自包含:interval + TTL 扫描)。
4
4
  * 启动幂等由调用方(service.startGcTimer)守卫;返回 stop 函数供 dispose 清理。
5
+ *
6
+ * [W4 idle-gc 扩展 · 设计 chat-domain-v1x-liveness-governance D4 连带面 2③]
7
+ * 注册翻 process 档后的兜底通道扩展(与翻档同批生效,先行期 = 无兜底挂账窗口):
8
+ * 1. 锚扩展:无 idleSince 的 resumable record 以 startedAt(创建时确定,
9
+ * types.ts ExecutionRecord.startedAt)为锚——兜底定位 30 天量级终态归档,
10
+ * 创建时锚的精度损失在该量级可接受(同设计对 record startedAt 的 rationale)。
11
+ * 2. **只归档不补注销**:被归档 record 的注册注销统一交 core 注册对账 sweep
12
+ * (判据含「已归档 = 视同终态」)——归档跨 session record 时 appendEntry 只达
13
+ * 当前 session entries(写达域无效),且 archive 不走 finalizeRecord、无注销
14
+ * 发射枚举身份(发射点枚举 D2 的 5 处不含 GC)。
15
+ * 3. WorkflowRun store 同批纳入:running 且 meta.startedAt 超 IDLE_TTL_MS 的 run
16
+ * 终态化归档(transition("done","time_limited") + save 持久化终态)。时间锚选择
17
+ * = WorkflowRunMeta.startedAt(run 创建时刻,ISO string)——run 状态无 idleSince
18
+ * 等价物,meta.completedAt 仅终态存在,startedAt 是唯一创建时确定的锚(与
19
+ * record startedAt 同 rationale)。**只终态化不补注销**同上:run 的注销发射
20
+ * 身份在 transition("done") 路径(发射点③)与其宿主收口链,GC 不越权补发。
5
21
  */
6
22
  import { getLogger } from "../core/logger.ts";
7
23
  import { getEngineDataDir } from "./engine/common/data-dir.ts";
@@ -16,17 +32,32 @@ const logger = getLogger("subagents");
16
32
  /** GC 扫描间隔:1 小时。 */
17
33
  // eslint-disable-next-line no-magic-numbers -- 60*60*1000 = 1h 的毫秒换算常数
18
34
  const GC_INTERVAL_MS = 60 * 60 * 1000;
19
- /** idle record TTL:30 天(超龄归档)。 */
35
+ /** idle record TTL:30 天(超龄归档)。record 锚窗与 workflow run 锚窗共用。 */
20
36
  // eslint-disable-next-line no-magic-numbers -- 30*24*60*60*1000 = 30d 的毫秒换算常数
21
37
  const IDLE_TTL_MS = 30 * 24 * 60 * 60 * 1000;
22
38
  /** 毫秒/天(GC 日志的 d 换算)。 */
23
39
  // eslint-disable-next-line no-magic-numbers -- 24*60*60*1000 = 1d 的毫秒换算常数
24
40
  const MS_PER_DAY = 24 * 60 * 60 * 1000;
25
41
 
42
+ /**
43
+ * WorkflowRun GC 窄口(idle-gc 对 WorkflowRun store 的最小依赖面,结构类型——
44
+ * 调用方传 FileRunStore 实例即可,不 import orchestration 具体类,保持本模块
45
+ * 可独立编译 + 单测)。loadAll 失败(宿主未 configureCore / IO 错)由实现侧
46
+ * 或本模块 catch 吞掉,单轮跳过下轮重试。
47
+ */
48
+ export interface WorkflowRunGcStore {
49
+ loadAll(): Promise<Array<{ runId: string; state: { status: string }; meta: { startedAt: string }; transition(target: "done", reason?: string): void }>>;
50
+ save(run: unknown): Promise<void>;
51
+ }
52
+
26
53
  /**
27
54
  * 启动 idle record GC 定时器,返回 stop 函数(清理 interval;幂等)。
28
- * 每个扫描周期:对 store 内全部 active record 中 resumable 且带 idleSince 的,
29
- * 超过 IDLE_TTL_MS 的归档(archive 单条失败不阻断其余,bestEffort 留痕)。
55
+ * 每个扫描周期:
56
+ * - record 面:对 store 内全部 active record 中 resumable 的,锚点(idleSince
57
+ * 优先,缺失回退 startedAt——[W4 锚扩展])超过 IDLE_TTL_MS 的归档。archive
58
+ * 单条失败不阻断其余(bestEffort 留痕)。**只归档不补注销**(见文件头注)。
59
+ * - workflow 面(注入 workflowRuns 时):running 且 meta.startedAt 超
60
+ * IDLE_TTL_MS 的 run 终态化归档(transition + save),单 run 失败不阻断。
30
61
  *
31
62
  * [D8 池引用计数接线] 归档时同步释放该 record 的引擎池引用(releasePoolRef:
32
63
  * journal 跟随 record 删除 + refs 移除 + 归零删池内原生状态)。锚点选在 30 天 TTL
@@ -37,28 +68,72 @@ const MS_PER_DAY = 24 * 60 * 60 * 1000;
37
68
  * 不能删——那是 record 主数据死亡(主 session 文件被 pi 侧删除,core 无触发点)才有
38
69
  * 的处置,由 cleanupExpiredPoolRefs 的 TTL 兜底覆盖。
39
70
  */
40
- export function startIdleGc(store: RecordStore): () => void {
71
+ export function startIdleGc(store: RecordStore, workflowRuns?: WorkflowRunGcStore): () => void {
41
72
  const timer = setInterval(() => {
42
73
  const now = Date.now();
43
74
  for (const record of store.listAllActive()) {
44
- if (isResumable(record) && record.idleSince) {
45
- const age = now - record.idleSince;
46
- if (age > IDLE_TTL_MS) {
47
- logger.warn(`[subagents] GC: archiving idle record ${record.id} (idle for ${Math.round(age / MS_PER_DAY)}d)`);
48
- try {
49
- store.archive(record);
50
- } catch (err) {
51
- bestEffort(err, `GC archive record ${record.id}`);
52
- }
53
- releaseEnginePoolRef(record);
75
+ if (!isResumable(record)) continue;
76
+ // [W4 锚扩展] idleSince(轮终写点)优先;缺失(resumable 但无轮终信号的
77
+ // 存量/异常形态)回退 startedAt(创建时确定)——两锚同为「最晚活性证据」,
78
+ // 30 天量级下 created 锚的精度损失可接受。
79
+ const anchorMs = record.idleSince ?? record.startedAt;
80
+ const age = now - anchorMs;
81
+ if (age > IDLE_TTL_MS) {
82
+ logger.warn(
83
+ `[subagents] GC: archiving idle record ${record.id} (idle for ${Math.round(age / MS_PER_DAY)}d)`,
84
+ );
85
+ try {
86
+ store.archive(record);
87
+ } catch (err) {
88
+ bestEffort(err, `GC archive record ${record.id}`);
54
89
  }
90
+ releaseEnginePoolRef(record);
55
91
  }
56
92
  }
93
+ if (workflowRuns !== undefined) {
94
+ void gcWorkflowRuns(workflowRuns, now);
95
+ }
57
96
  }, GC_INTERVAL_MS);
58
97
  timer.unref?.();
59
98
  return () => clearInterval(timer);
60
99
  }
61
100
 
101
+ /**
102
+ * [W4 WorkflowRun store 纳入] running 且 startedAt 超锚窗的 run 终态化归档。
103
+ * reason = "time_limited"(超龄归档语义的 DoneReason,对主 agent 的语义是
104
+ * 「run 因超时被收口」而非 completed)。失败吞错留痕(清理是旁路维护,不能
105
+ * 拖垮 GC interval;下轮重试)。
106
+ */
107
+ async function gcWorkflowRuns(workflowRuns: WorkflowRunGcStore, now: number): Promise<void> {
108
+ let runs: Awaited<ReturnType<WorkflowRunGcStore["loadAll"]>>;
109
+ try {
110
+ runs = await workflowRuns.loadAll();
111
+ } catch (err) {
112
+ // 宿主未 configureCore(core_host_not_configured)/ IO 失败:本轮跳过,
113
+ // 下轮重试。debug 留痕——这是「workflow 域未启用」的正常形态,warn 会噪声。
114
+ logger.debug(`[subagents] GC: workflow run store loadAll failed (skipped this cycle): ${
115
+ err instanceof Error ? err.message : String(err)
116
+ }`);
117
+ return;
118
+ }
119
+ for (const run of runs) {
120
+ if (run.state.status !== "running") continue;
121
+ const startedMs = Date.parse(run.meta.startedAt);
122
+ if (!Number.isFinite(startedMs)) continue; // 畸形锚不过判(宁挂账不失明)
123
+ const age = now - startedMs;
124
+ if (age <= IDLE_TTL_MS) continue;
125
+ logger.warn(
126
+ `[subagents] GC: terminating stale running workflow run ${run.runId} (started ${Math.round(age / MS_PER_DAY)}d ago)`,
127
+ );
128
+ try {
129
+ run.transition("done", "time_limited");
130
+ await workflowRuns.save(run);
131
+ } catch (err) {
132
+ bestEffort(err, `GC terminate workflow run ${run.runId}`);
133
+ }
134
+ }
135
+ }
136
+
62
137
  /**
63
138
  * 释放 record 的引擎池引用(best-effort)。无 engineHandle(存量 record / 非引擎
64
139
  * 路径)时 no-op;engine 缺省投影 'pi'(与读侧存量 entry 零迁移口径一致)。
@@ -13,7 +13,10 @@
13
13
  // 【已接线:session-runner.ts agent_settled arm / subagent-service 新 turn disarm】
14
14
  // 2. 全局 ceiling —— 活进程上限,超限时按 LRU 挤出最久空闲(决策 4)【未接线,deferred】
15
15
  // 3. shutdown 收割 —— 父进程 shutdown 时显式 SIGTERM 全部 activation(决策 7 防线 i)
16
- // 【未接线:index.ts reapSpawnedChildrenOnShutdown + killAllSpawnedChildren 已等价覆盖】
16
+ // 【未接线:[F-7 纠偏] 旧注「reapSpawnedChildrenOnShutdown 已等价覆盖」失实——
17
+ // 该 hook 调 killAllSpawnedChildren,后者协议化后仅镜像置死 no-op(见
18
+ // engine/host/spawned-children.ts 与 src/index.ts 导出注释);引擎进程实际回收 =
19
+ // stdin-EOF 自灭链,本职责仍无显式收割接线】
17
20
  // 4. 孤儿扫描 —— 父进程启动时按持久化 PID 扫收上次崩溃遗留的孤儿(决策 7 防线 ii)【deferred】
18
21
  // 5. activate 互斥 —— 同 recordId 的并发 activate 串行化(决策 7 防线 iii,防双写者)
19
22
  // 【已接线:subagent-service.ts 冷路径 resume 前 acquireActivateLock】
@@ -295,12 +298,12 @@ export function getActiveProcessCount(): number {
295
298
  // ============================================================
296
299
  // 职责 3:shutdown 收割(防线 i)
297
300
  //
298
- // [状态:未接线,已有等价实现] reapAllAliveProcesses 无生产调用方——shutdown 收割
299
- // 已由 index.ts reapSpawnedChildrenOnShutdown(process hook → killAllSpawnedChildren
300
- // SIGTERM,覆盖 SIGTERM/SIGINT/beforeExit 挂点)等价覆盖;后者直接遍历
301
- // spawnedChildren 句柄表,不依赖本模块 activeProcesses 记账的 register 时机,覆盖面
302
- // 更可靠。本函数保留作跨模块等价实现;若未来接入须先保证 register/unregister 记账
303
- // 完整,否则收割列表不全。
301
+ // [状态:未接线,无等价实现([F-7 纠偏])] reapAllAliveProcesses 无生产调用方。旧注
302
+ // 「shutdown 收割已由 reapSpawnedChildrenOnShutdown 等价覆盖」失实:该 hook
303
+ // killAllSpawnedChildren,而后者协议化后(engines/pi inproc 删除、子进程活在引擎
304
+ // 进程内)仅做 core 侧镜像置死,不发任何 SIGTERM(_signal 参数被忽略)——引擎进程
305
+ // 实际回收走 stdin-EOF 自灭链,本职责的显式收割仍无接线。本函数保留作未来接入面;
306
+ // 若未来接入须先保证 register/unregister 记账完整,否则收割列表不全。
304
307
  // ============================================================
305
308
 
306
309
  /**
@@ -4,8 +4,8 @@
4
4
  //
5
5
  // 旧三态(running/idle/cancelled)折为两态(running/closed)后,「对话模式等待续聊」
6
6
  //(旧 idle)和「正在执行」都是 status="running",需派生谓词区分。谓词复用已有
7
- // lifecycle-manager.hasIdleTimer(idle timer 是否 armed)与 session-runner.getChildByRecord
8
- //(活进程句柄是否存在),不新增状态记账。
7
+ // lifecycle-manager.hasIdleTimer(idle timer 是否 armed)与 core 侧 spawnedChildren
8
+ // 状态镜像读点(engine/host/spawned-children.ts,活进程句柄是否存在),不新增状态记账。
9
9
  //
10
10
  // 两种 running 子态(v4 B-1):
11
11
  // - 等待续聊(旧 idle):isIdle=true(timer armed)。又分两路:
@@ -20,19 +20,21 @@
20
20
  // 故 isIdle 在这两个检查点恒为 true——notify 守卫与 early-return 判据可靠。
21
21
 
22
22
  import { hasIdleTimer } from "./lifecycle-manager.ts";
23
- import { getChildByRecord } from "./engine/engines/pi/session-runner.ts";
23
+ // [W6 拆依赖] 活进程句柄读点改经 core 侧 spawnedChildren 状态镜像公共面
24
+ // (engine/host/spawned-children.ts),不再深路径 import inproc pi 引擎目录 内部——
25
+ // 行为不变(镜像 ∪ inproc 权威 map 并读,见该文件头注释的过渡桥语义)。
26
+ import { hasLiveProcessHandleCore } from "./engine/host/spawned-children.ts";
24
27
  import type { ExecutionRecord } from "./types.ts";
25
28
 
26
29
  /**
27
30
  * 活进程句柄是否存在(isResumable 子判据)。
28
31
  *
29
- * 复用 session-runner.spawnedChildrengetChildByRecord 查询。child 存在且未 kill
30
- * = 有活进程句柄(正在执行 / Path A 保活)。进程 close spawnedChildren 已 delete,
31
- * 返回 undefined = 无活进程(Path B / 跨重启)。
32
+ * [W6] 改读 core 侧状态镜像(engine/host/spawned-children.tshasLiveProcessHandleCore:
33
+ * 镜像项存在且未 killed = 有活进程句柄;迁移期内建 pi inproc 权威 map 并读兜底)。
34
+ * 进程 close / 被杀后镜像项置死或移除 = 无活进程(Path B / 跨重启)。
32
35
  */
33
36
  export function hasLiveProcessHandle(recordId: string): boolean {
34
- const child = getChildByRecord(recordId);
35
- return child !== undefined && !child.killed;
37
+ return hasLiveProcessHandleCore(recordId);
36
38
  }
37
39
 
38
40
  /**
@@ -744,7 +744,13 @@ export class RecordStore {
744
744
  // 的修补对 async/chat 成员同样生效——拉齐 light 重建丢 result/model 与 full 重建的
745
745
  // 既有形态不一致(v2 D3 影响面诚实口径),不触碰任何通知文案(golden 不锁 entry 字节)。
746
746
  const rec0 = lastEntry === undefined ? rec : RecordStore.mergeOrphanLastEntry(rec, lastEntry);
747
- if (rec0.chatMode === true) {
747
+ // [W4 boot 分区 · 裁决表行 3] 重认领保留分支的判据 = resumable 且**无完成产出**:
748
+ // 非 chatMode 的 resumable=true 且 result 有值是 SP-5 one-shot 完成态(任务已完成,
749
+ // 直断 closed/gc 无损);resumable=true 且 result 缺失是「监督器死亡纳管态」(W4:
750
+ // 引擎死亡接管后宿主重启)——保留 running 交 round-supervisor boot 重认领继续
751
+ // 管辖(注册存续 process 档),误判 closed 会制造「指向已终态 record 的残留注册」
752
+ // 并损失 resume 可能。
753
+ if (rec0.chatMode === true || (rec0.resumable === true && rec0.result === undefined)) {
748
754
  // chat 会话跨重启等续聊:保留 running(可续聊),仅落执行态信号。
749
755
  this.reportSubagentRecord({ ...rec0, resumable: true });
750
756
  return;
@@ -769,12 +775,29 @@ export class RecordStore {
769
775
  // 真实 reason 同层——否则无 reason sidecar 在磁盘重建时被兜底为 disconnected,
770
776
  // 把正常完成的记录误标成断联。
771
777
  writeFinalized(sessionFile, "gc");
778
+ // [F3 boot 直断语义,设计表 3 行 2] 走到本分支的 record 分两类:
779
+ // - resumable=true(上方分流保证此时 result 有值)= SP-5 one-shot 完成态——任务
780
+ // 真实完成,直断 closed/gc 无 error,投影 completed 不变;
781
+ // - 其余 = in-flight(重启前在途、无 resumable 信号)——宿主重启中断了在途任务,
782
+ // 投影不得是 completed(事故环 3:异常中断被谎报完成,违反 G3)。error 载体经
783
+ // deriveOutcome("gc", error) = "failed"(execution-record.ts 唯一权威派生)
784
+ // 投影 failed,文案供 GUI/主 agent 明确「任务因宿主重启中断」;ClosedReason
785
+ // 枚举不扩展(sidecar/entry 面最小改动,"gc" 由 error 载体补足失败语义)。
786
+ const inFlightAbortedError =
787
+ rec0.resumable === true
788
+ ? undefined
789
+ : "orphan recovery: task aborted by host restart (in-flight at shutdown; session context lost" +
790
+ (parseOk ? "" : "; truncated last line") + ")";
772
791
  this.reportSubagentRecord({
773
792
  ...rec0,
774
793
  status: "closed",
775
794
  closedReason: "gc",
776
795
  endedAt: Date.now(),
777
- ...(parseOk ? {} : { error: "orphan recovery: subagent session ended abnormally (truncated last line)" }),
796
+ ...(inFlightAbortedError !== undefined
797
+ ? { error: inFlightAbortedError }
798
+ : parseOk
799
+ ? {}
800
+ : { error: "orphan recovery: subagent session ended abnormally (truncated last line)" }),
778
801
  });
779
802
  }
780
803
 
@@ -794,6 +817,10 @@ export class RecordStore {
794
817
  // `?? ""`),pickStr 签名宽返回 string|undefined —— `?? ""` 运行时不可达,
795
818
  // 仅满足 model 非可选类型,空串回退语义不变(cur 空 → src,src 也空 → "")。
796
819
  model: pickStr(rec.model, last.model) ?? "",
820
+ // [W4 boot 分区] 轮终执行态信号(resumable)随末条 entry 保留:子文件侧重建
821
+ // (reconstructAll)不带该信号,boot 分区的「already-resumable-idle 重认领 vs
822
+ // in-flight 直断」分流(finalizeOrphanRecord)只能从主 session 末条 entry 取证。
823
+ resumable: rec.resumable ?? last.resumable,
797
824
  };
798
825
  }
799
826
 
@@ -1,37 +1,20 @@
1
- /**
2
- * subagent relay 通道 env 名与协议常量 SSOT(docs/architecture/subagent-realtime-channel.md §5.1/§5.2/§3.1)。
3
- *
4
- * 为什么独立成模块:三方消费同一份常量——extension 侧(pi-invocation 激活判定 /
5
- * buildChildEnv 归属写入)、runtime 侧(env 注入与镜像校验)、代理脚本 relay.mjs
6
- * (零依赖脚本不能 import workspace 包,只能内嵌镜像,镜像一致性由 conformance
7
- * relay 变体断言锁定)。禁各处手写字符串。
8
- *
9
- * env 语义:SOCKET/NODE/SCRIPT 三者同时非空 = relay 激活(全有或全无,无中间态);
10
- * SESSION_ID/RECORD_ID 是 tee 帧归属键(缺失由代理握手前自检拒绝,退出码 13)。
11
- */
1
+ // src/execution/relay-env.ts
2
+ //
3
+ // relay 通道 env 名与协议常量的 re-export shim——单源在
4
+ // @zhushanwen/subagent-engine-sdk(round1-reuse R9 跨进程契约副本收编;core SDK
5
+ // 边界正向合法)。本文件保持路径不变以保住 package.json exports ./relay-env
6
+ // 子入口(semver 面:runtime/extension 消费方 import specifier 不变)。
7
+ // 代理脚本 relay.mjs 仍为内嵌镜像(零依赖脚本不能 import workspace 包),
8
+ // 镜像一致性由 conformance relay 变体断言锁定。
12
9
 
13
- export const RELAY_ENV_SOCKET = 'XYZ_SUBAGENT_RELAY_SOCKET';
14
- export const RELAY_ENV_NODE = 'XYZ_SUBAGENT_RELAY_NODE';
15
- export const RELAY_ENV_SCRIPT = 'XYZ_SUBAGENT_RELAY_SCRIPT';
16
- export const RELAY_ENV_SESSION_ID = 'XYZ_SUBAGENT_RELAY_SESSION_ID';
17
- export const RELAY_ENV_RECORD_ID = 'XYZ_SUBAGENT_RELAY_RECORD_ID';
18
-
19
- /** relay 协议版本(握手帧 v 字段;runtime 与代理同包分发,不匹配=安装损坏)。 */
20
- export const RELAY_PROTOCOL_VERSION = 1;
21
-
22
- /** 代理专用退出码(extension 侧表现为「子进程非零退出」→ engine_run_failed 语义)。 */
23
- export const RELAY_EXIT_CODES = {
24
- /** 握手被拒:协议版本不匹配(安装损坏,重装应用)。 */
25
- VERSION_MISMATCH: 10,
26
- /** relay socket 不可达(runtime 未运行或已重启)。 */
27
- SOCKET_UNREACHABLE: 11,
28
- /** socket 中途断开(runtime 崩溃等)——代理生命线断即退。 */
29
- SOCKET_CLOSED: 12,
30
- /** 归属 env(SESSION_ID/RECORD_ID)缺失——防无归属帧污染广播。 */
31
- MISSING_IDENTITY: 13,
32
- } as const;
33
-
34
- /** 激活判定:三 env 同时非空才走 relay,任一缺失回落直连 spawn 真实 pi(TUI/独立 pi 零回归)。 */
35
- export function isRelayActive(env: NodeJS.ProcessEnv | Record<string, string | undefined>): boolean {
36
- return Boolean(env[RELAY_ENV_SOCKET] && env[RELAY_ENV_NODE] && env[RELAY_ENV_SCRIPT]);
37
- }
10
+ export {
11
+ RELAY_ENV_SOCKET,
12
+ RELAY_ENV_NODE,
13
+ RELAY_ENV_SCRIPT,
14
+ RELAY_ENV_SESSION_ID,
15
+ RELAY_ENV_RECORD_ID,
16
+ RELAY_PROTOCOL_VERSION,
17
+ RELAY_EXIT_CODES,
18
+ isRelayActive,
19
+ readRelayForwardEnv,
20
+ } from "@zhushanwen/subagent-engine-sdk";
@@ -0,0 +1,89 @@
1
+ // src/execution/round-supervisor/domain.ts
2
+ //
3
+ // [W4] 轮次活性监督器——域分类与 record 级判据谓词(单一权威源)。
4
+ //
5
+ // 设计权威源:docs/design/chat-domain-v1x-liveness-governance.md §3.2 D2 前置 1
6
+ // (轮次域分类)+ 前置 2(record 去向单一裁决表)+ 三态判定表。
7
+ //
8
+ // 判定域 = run 域一次性任务(resumable 且无进程驱动)+ workflow 域 resumable run。
9
+ // chat/conversation 形态(record.chatMode)**豁免**出 no-progress 判定域:轮终 idle
10
+ // 现状(doFinalizeRoundToIdle + idle timer / idle-gc)+ settled-watchdog 两段守护
11
+ // 管辖——豁免只意味着「监督器不重复管」,不意味着无界(D2 R2 精确化)。
12
+ //
13
+ // 判据状态源钉死为 record 级(R3 核正):pi 引擎 poolKey 恒 'shared' 单进程、
14
+ // ensureConnected 被动重建会重填「镜像整体置死」——镜像不能作持续判据;run 终态
15
+ // failed 即「驱动死亡」的证据,不随引擎进程重建翻转。镜像置死仅作死亡事件的
16
+ // 触发信号(supervisor 的死亡事件纳管入口),不作持续判据。
17
+ //
18
+ // 纳管模型(R3 显式化):死亡事件纳管(run failed / 引擎 exited 事件把 record 纳入
19
+ // 监督域),**重建不解管**(引擎进程重建不解除已纳管 record 的监督,监督器按
20
+ // record 级状态持续判定直至终态或放弃)。本文件的谓词全部只读 record 级状态,
21
+ // 不读引擎镜像——结构性保证「重建不解管」(模拟引擎重建后监督器仍按 record 级判)。
22
+
23
+ import type { ExecutionRecord } from "../types.ts";
24
+
25
+ /** 监督器域分类(判定域先收窄,再谈三态)。 */
26
+ export type SupervisorDomain =
27
+ /** run 域一次性任务(非 chatMode record)——监督域主体。 */
28
+ | "run"
29
+ /** conversation/chat 形态(record.chatMode)——豁免,现状机制管辖。 */
30
+ | "conversation";
31
+
32
+ /**
33
+ * 域分类(裁决表 conversation 行「任何触发不入监督域」的判定锚)。
34
+ * record.chatMode === true → conversation(豁免);否则 → run 域。
35
+ * (workflow 域 resumable run 的活性收口 = kill-9 恢复链 + idle-gc startedAt 锚
36
+ * 归档 + sweep 对账——workflow run 不在 RecordStore,其注册对账按 type=workflow
37
+ * 保守跳过,见 reconcile-sweep.ts。)
38
+ */
39
+ export function classifySupervisorDomain(record: Pick<ExecutionRecord, "chatMode">): SupervisorDomain {
40
+ return record.chatMode === true ? "conversation" : "run";
41
+ }
42
+
43
+ /**
44
+ * 三态判定「该唤醒」的 record 级判据(设计三态表逐字实现):
45
+ * **resumable 未终态 且 无在途 run / 无进程驱动**。
46
+ *
47
+ * 入参解耦说明:谓词只消费 record 级状态与两个布尔(在途 run / 活进程句柄),
48
+ * 由调用方(supervisor)供给——「有在途 run」来自 supervisor 的在途记账
49
+ * (subagent-service 报告),「有进程驱动」来自生命周期镜像谓词。本函数不读
50
+ * 镜像/引擎状态,结构性满足「镜像置死只作触发信号不作持续判据」。
51
+ *
52
+ * 「已有完成产出」(SP-5 upgrade 等待态)不在本谓词域——由调用方以 hasResult
53
+ * 单独判定(视图投影面分离,supervisor.evaluate 消费)。
54
+ */
55
+ export function isAwakeWarrantedShape(
56
+ record: { status: string; resumable: boolean; chatMode: boolean },
57
+ hasInFlightRun: boolean,
58
+ hasLiveProcess: boolean,
59
+ ): boolean {
60
+ if (record.chatMode === true) return false; // conversation 豁免
61
+ if (record.status !== "running") return false; // 终态 = 已收口
62
+ if (hasInFlightRun || hasLiveProcess) return false; // 该等(有驱动)→ 不干预
63
+ return record.resumable === true;
64
+ }
65
+
66
+ /**
67
+ * boot 分区「already-resumable-idle」重认领谓词(与裁决表同源——设计 D4 连带面 2
68
+ * ②「启动扫描 resumable 且无进程驱动且非 conversation 形态」的单一权威实现;
69
+ * cold-resurrect.ts 的 message 冷查链不承载启动扫描,本谓词即其「cold-resurrect
70
+ * 扩展」落点,record-store 孤儿恢复的保留分支与 supervisor.bootPartition 共同消费
71
+ * 同一判据形态)。
72
+ *
73
+ * 判据:非 conversation 形态 + status=running + resumable 信号(轮终写点 set true;
74
+ * 冷路径续轮清除——重启前已在途的 record resumable=undefined,走 boot 直断分支
75
+ * 而非重认领)+ **无完成产出**(resumable=true 且 result 有值是 SP-5 one-shot
76
+ * 完成态——任务已完成,直断 closed/gc 无损,不属重认领域;result 缺失才是 W4
77
+ * 死亡纳管态跨重启的形态)。
78
+ */
79
+ export function isBootReadoptable(record: {
80
+ status: string;
81
+ resumable: boolean;
82
+ chatMode: boolean;
83
+ hasResult: boolean;
84
+ }): boolean {
85
+ if (record.chatMode) return false;
86
+ if (record.status !== "running") return false;
87
+ if (!record.resumable) return false;
88
+ return !record.hasResult;
89
+ }
@@ -0,0 +1,33 @@
1
+ // src/execution/round-supervisor/index.ts
2
+ //
3
+ // [W4] 轮次活性监督器公共出口(SubagentService 与测试的消费面)。
4
+
5
+ export {
6
+ classifySupervisorDomain,
7
+ isAwakeWarrantedShape,
8
+ isBootReadoptable,
9
+ type SupervisorDomain,
10
+ } from "./domain.ts";
11
+ export {
12
+ classifyReplacement,
13
+ type ReplacementCandidate,
14
+ type ReplacementProbeSubject,
15
+ type ReplacementVerdict,
16
+ } from "./notify-accounting.ts";
17
+ export {
18
+ runReconcileSweep,
19
+ type ReconcileSweepDeps,
20
+ type ReconcileSweepResult,
21
+ type SupervisedRecordState,
22
+ } from "./reconcile-sweep.ts";
23
+ export {
24
+ ROUND_SUPERVISOR_WATCHDOG_DEFAULT_MS,
25
+ ROUND_SUPERVISOR_WATCHDOG_ENV,
26
+ RoundSupervisor,
27
+ getSupervisorWatchdogMs,
28
+ isSupervisorGiveUpDisabled,
29
+ type GiveUpKind,
30
+ type RoundSupervisorDeps,
31
+ type SupervisorCandidateRecord,
32
+ type SupervisorRecordView,
33
+ } from "./supervisor.ts";
@@ -0,0 +1,87 @@
1
+ // src/execution/round-supervisor/notify-accounting.ts
2
+ //
3
+ // [W4] 通知对账——送决策指引前查替代的两级启发式(纯函数)。
4
+ //
5
+ // 设计权威源:docs/design/chat-domain-v1x-liveness-governance.md §3.2 D2「通知对账」
6
+ // (R3 增补、R4 钉死判定语义)。
7
+ //
8
+ // 背景:主 agent 是唯一决策者,重派不被禁止——即使合并单条通知,它仍可能在收到
9
+ // 指引前合法地重派了新任务。record 数据模型不存在重派关联字段(parentRecordId 是
10
+ // 层级语义、rootSessionId 是 session 隔离语义、task 是完整 prompt 非关联键),
11
+ // 对账判定为显式定义的两级启发式:
12
+ // - 高置信替代:同 rootSessionId + 同 agent 名 + 同 slug(≤35 字符短标签,重派
13
+ // 同任务大概率同 slug)+ 决策指引看门狗窗内新建 → 撤销指引,改送「原任务已被
14
+ // 新任务替代」终止通知 + 对原 record 走该放弃路径(终态化 + 注销);
15
+ // - 低置信(仅 agent 同名):不撤销指引,但指引内容自带豁免声明(「若你已重新
16
+ // 派发或不再需要此任务,忽略本指引;原任务将在看门狗到期后自动终止」)。
17
+ //
18
+ // 两失败方向登记(设计四要素):
19
+ // - 漏判(重派改写 slug/task 致高置信不命中)→ 残余双执行;重审触发 = 实测双执行
20
+ // 反馈 ≥1 例 → 升级「supersedes 显式引用」方案;
21
+ // - 误判(无关任务撞 slug)→ 原任务被错误终态化——危害轻(原任务本已无进程驱动,
22
+ // 仅损失 resume 可能)。
23
+
24
+ /** 对账候选(重派承接任务的 record 投影面)。 */
25
+ export interface ReplacementCandidate {
26
+ id: string;
27
+ rootSessionId: string | undefined;
28
+ /** agent 类型名(record.agent)。 */
29
+ agentName: string;
30
+ /** 人类短标签(record.slug,≤35 字符)。 */
31
+ slug: string;
32
+ /** 创建时刻(record.startedAt,ms)。 */
33
+ startedAt: number;
34
+ }
35
+
36
+ /** 被接管 record 的对账基准面。 */
37
+ export interface ReplacementProbeSubject {
38
+ id: string;
39
+ rootSessionId: string | undefined;
40
+ agentName: string;
41
+ slug: string;
42
+ }
43
+
44
+ /** 对账结论(两级 + 无命中)。 */
45
+ export type ReplacementVerdict =
46
+ | { kind: "high-confidence"; replacementId: string }
47
+ | { kind: "low-confidence" }
48
+ | { kind: "none" };
49
+
50
+ /**
51
+ * 高置信判定窗口:候选的 startedAt 必须落在「决策指引看门狗窗」内(接管时刻起
52
+ * 算——窗内新建才有「承接了同一任务」的置信;窗外的同名任务与本接管无时序关联)。
53
+ * 窗值由调用方传(= 监督器决策看门狗窗,同源常量)。
54
+ */
55
+ export function classifyReplacement(
56
+ subject: ReplacementProbeSubject,
57
+ candidates: readonly ReplacementCandidate[],
58
+ windowMs: number,
59
+ now: number,
60
+ ): ReplacementVerdict {
61
+ let sameAgentOnly = false;
62
+ for (const candidate of candidates) {
63
+ if (candidate.id === subject.id) continue;
64
+ if (candidate.agentName !== subject.agentName) continue;
65
+ // 时序窗(高/低置信共同的门):窗内新建才参与对账——窗外的同名任务与本接管
66
+ // 无时序关联(预先存在的同名任务不是替代者)。
67
+ if (now - candidate.startedAt > windowMs) continue;
68
+ // [契约锚] sameRoot 在生产装配下恒真——此检查是对供给侧不变量的防御性复认,
69
+ // 不是独立生效的第四要素。生产候选源 = supervisor.deps.listCandidateRecords
70
+ // (service-binding.ts supervisorCandidates 已按 getSessionRootId() 预过滤,
71
+ // 循环内候选恒同 root)。约束力悄悄依赖该供给不变量:若换候选源为未按 root
72
+ // 预过滤的全量集,sameRoot 从恒真退化为真实约束——跨 root 命中会从
73
+ // high-confidence 降为 low-confidence(替代收口弱化:原任务改由看门狗到期才
74
+ // 放弃),行为静默漂移。换候选源必须保「root 预过滤」或显式重审本判定语义。
75
+ const sameRoot =
76
+ candidate.rootSessionId !== undefined &&
77
+ candidate.rootSessionId === subject.rootSessionId;
78
+ const sameSlug =
79
+ subject.slug !== "" &&
80
+ candidate.slug === subject.slug;
81
+ if (sameRoot && sameSlug) {
82
+ return { kind: "high-confidence", replacementId: candidate.id };
83
+ }
84
+ sameAgentOnly = true;
85
+ }
86
+ return sameAgentOnly ? { kind: "low-confidence" } : { kind: "none" };
87
+ }