@zhushanwen/subagent-core 0.2.0 → 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (347) hide show
  1. package/README.md +45 -2
  2. package/agents/analyst.md +60 -0
  3. package/agents/coder.md +69 -0
  4. package/agents/debugger.md +66 -0
  5. package/agents/doc-reviewer.md +49 -0
  6. package/agents/explorer.md +63 -0
  7. package/agents/general-purpose.md +32 -0
  8. package/agents/orchestrator.md +61 -0
  9. package/agents/planner.md +53 -0
  10. package/agents/researcher.md +67 -0
  11. package/agents/reviewer.md +73 -0
  12. package/dist/chunk-43ONBFZX.js +240 -0
  13. package/dist/chunk-APZY4IME.js +27 -0
  14. package/dist/chunk-V3VHZ2VX.js +59 -0
  15. package/dist/execution/engine/engines/zcode/constants.cjs +62 -9
  16. package/dist/execution/engine/engines/zcode/constants.d.cts +116 -12
  17. package/dist/execution/engine/engines/zcode/constants.d.ts +116 -12
  18. package/dist/execution/engine/engines/zcode/constants.js +39 -7
  19. package/dist/execution/engine/engines/zcode/reader.d.cts +2 -1
  20. package/dist/execution/engine/engines/zcode/reader.d.ts +2 -1
  21. package/dist/execution/engine/engines/zcode/reader.js +4 -234
  22. package/dist/execution/engine/paths.js +7 -19
  23. package/dist/index.cjs +18234 -1277
  24. package/dist/index.d.cts +5646 -673
  25. package/dist/index.d.ts +5646 -673
  26. package/dist/index.js +17828 -1277
  27. package/dist/{types-BxyAidGf.d.cts → types-Dv4QhSJ_.d.cts} +678 -112
  28. package/dist/{types-BxyAidGf.d.ts → types-Dv4QhSJ_.d.ts} +678 -112
  29. package/package.json +7 -4
  30. package/src/__tests__/agent-opts-resolver-schema-prompt.test.ts +0 -1
  31. package/src/__tests__/append-system-prompt-assembly.test.ts +8 -7
  32. package/src/__tests__/fr4-get-state-handshake.test.ts +2 -2
  33. package/src/__tests__/m2-append-content-probe.test.ts +0 -1
  34. package/src/__tests__/manifest-store.test.ts +10 -9
  35. package/src/__tests__/record-store-cache.test.ts +0 -2
  36. package/src/__tests__/record-store-index.test.ts +1 -2
  37. package/src/__tests__/review-fix-loop-script.test.ts +93 -3
  38. package/src/__tests__/review-fix-loop-utils.test.ts +31 -10
  39. package/src/__tests__/robustness-low-batch1.test.ts +6 -5
  40. package/src/__tests__/robustness-medium-batch1.test.ts +2 -2
  41. package/src/__tests__/robustness-medium-batch2.test.ts +17 -6
  42. package/src/__tests__/robustness-medium-batch4.test.ts +2 -2
  43. package/src/__tests__/session-runner.test.ts +1 -1
  44. package/src/__tests__/smoke.test.ts +9 -2
  45. package/src/core/__tests__/host-services.test.ts +2 -2
  46. package/src/core/__tests__/logger.test.ts +1 -1
  47. package/src/core/error-message.ts +9 -0
  48. package/src/core/host-services.ts +21 -5
  49. package/src/core/notify-ports.ts +18 -5
  50. package/src/execution/__tests__/agent-profile.test.ts +232 -0
  51. package/src/execution/__tests__/agent-registry.test.ts +370 -0
  52. package/src/execution/__tests__/agent-result-mapper.test.ts +18 -2
  53. package/src/execution/__tests__/agents-assembly.test.ts +219 -0
  54. package/src/execution/__tests__/ask-user-transit-e2e.test.ts +6 -6
  55. package/src/execution/__tests__/chat-engine-routing.test.ts +136 -30
  56. package/src/execution/__tests__/chatmode-first-round-closure-spawn.test.ts +5 -4
  57. package/src/execution/__tests__/cold-resurrect.test.ts +283 -0
  58. package/src/execution/__tests__/create-concurrency-pool.test.ts +194 -0
  59. package/src/execution/__tests__/delivery-methods.test.ts +77 -62
  60. package/src/execution/__tests__/descendant-sweep-guards.test.ts +220 -0
  61. package/src/execution/__tests__/descendant-sweep.test.ts +269 -0
  62. package/src/execution/__tests__/dialog-queue.test.ts +199 -1
  63. package/src/execution/__tests__/engine-model-validation.test.ts +741 -0
  64. package/src/execution/__tests__/epipe-fallback.test.ts +31 -24
  65. package/src/execution/__tests__/execute-and-await-worktree.test.ts +21 -91
  66. package/src/execution/__tests__/execute-nesting.test.ts +34 -188
  67. package/src/execution/__tests__/execution-record.test.ts +217 -60
  68. package/src/execution/__tests__/execution-runtime-face.test.ts +272 -0
  69. package/src/execution/__tests__/explicit-agent-ref-guard.test.ts +3 -3
  70. package/src/execution/__tests__/finalize-record.test.ts +278 -16
  71. package/src/execution/__tests__/gc-timer.test.ts +57 -2
  72. package/src/execution/__tests__/get-record-for-action-restart.test.ts +14 -12
  73. package/src/execution/__tests__/get-state-handshake.test.ts +127 -0
  74. package/src/execution/__tests__/helpers/mock-extension-api.ts +1 -1
  75. package/src/execution/__tests__/helpers/session-runner-mocks.ts +113 -0
  76. package/src/execution/__tests__/helpers/spawn-mock.ts +150 -3
  77. package/src/execution/__tests__/helpers/subagent-service-mocks.ts +137 -0
  78. package/src/execution/__tests__/keep-alive-no-progress.test.ts +280 -0
  79. package/src/execution/__tests__/kill-all-escalation.test.ts +196 -0
  80. package/src/execution/__tests__/lifecycle-manager-idle-timer-identity.test.ts +117 -0
  81. package/src/execution/__tests__/lifecycle-manager.test.ts +39 -0
  82. package/src/execution/__tests__/lifecycle-predicates.test.ts +1 -2
  83. package/src/execution/__tests__/manifest-parentid.test.ts +4 -4
  84. package/src/execution/__tests__/manifest-store-tmp-recovery.test.ts +99 -0
  85. package/src/execution/__tests__/max-turns-to-watchdog-ms.test.ts +113 -0
  86. package/src/execution/__tests__/model-resolver.test.ts +2 -2
  87. package/src/execution/__tests__/nested-visibility-env-propagation.test.ts +23 -121
  88. package/src/execution/__tests__/nested-visibility.test.ts +2 -0
  89. package/src/execution/__tests__/notify-ledger.test.ts +1249 -0
  90. package/src/execution/__tests__/output-collector.test.ts +117 -6
  91. package/src/execution/__tests__/pi-invocation.test.ts +1 -1
  92. package/src/execution/__tests__/record-store-orphan-revive.test.ts +159 -0
  93. package/src/execution/__tests__/record-store.test.ts +89 -5
  94. package/src/execution/__tests__/records-cwd-isolation.test.ts +2 -2
  95. package/src/execution/__tests__/recursive-visibility-baseline.test.ts +29 -132
  96. package/src/execution/__tests__/recursive-visibility-env.test.ts +55 -179
  97. package/src/execution/__tests__/rpc-mode.test.ts +2 -2
  98. package/src/execution/__tests__/run-and-finalize-anchoring.test.ts +387 -0
  99. package/src/execution/__tests__/run-and-finalize-chatmode.test.ts +19 -9
  100. package/src/execution/__tests__/run-spawn-chatmode-settled.test.ts +5 -4
  101. package/src/execution/__tests__/run-spawn-edges.test.ts +418 -26
  102. package/src/execution/__tests__/run-spawn-integration.test.ts +3 -3
  103. package/src/execution/__tests__/run-spawn-resume.test.ts +2 -2
  104. package/src/execution/__tests__/run-spawn-rpc-mode.test.ts +2 -2
  105. package/src/execution/__tests__/run-spawn-stdout-callback-throw.test.ts +16 -8
  106. package/src/execution/__tests__/service-kill-escalation.test.ts +91 -0
  107. package/src/execution/__tests__/session-context-resolver.test.ts +0 -2
  108. package/src/execution/__tests__/session-file-gc.test.ts +35 -0
  109. package/src/execution/__tests__/session-pending.test.ts +471 -0
  110. package/src/execution/__tests__/session-reconstructor.test.ts +366 -15
  111. package/src/execution/__tests__/session-runner-branch-cache-lru.test.ts +110 -0
  112. package/src/execution/__tests__/session-runner-close-prune.test.ts +181 -0
  113. package/src/execution/__tests__/session-runner-dispatch.test.ts +489 -0
  114. package/src/execution/__tests__/session-runner-epipe.test.ts +4 -3
  115. package/src/execution/__tests__/session-runner-heartbeat-idle-fallback.test.ts +227 -0
  116. package/src/execution/__tests__/session-runner-lifecycle-helpers.test.ts +290 -0
  117. package/src/execution/__tests__/session-runner-schema-env.test.ts +7 -5
  118. package/src/execution/__tests__/settled-watchdog.test.ts +524 -0
  119. package/src/execution/__tests__/spawn-args.test.ts +11 -10
  120. package/src/execution/__tests__/spawn-event-adapter-rpc.test.ts +26 -23
  121. package/src/execution/__tests__/spawn-event-adapter.test.ts +1 -1
  122. package/src/execution/__tests__/spawn-worktree-guidance.test.ts +5 -4
  123. package/src/execution/__tests__/spawned-children.test.ts +155 -11
  124. package/src/execution/__tests__/start-sync-model-guard.test.ts +4 -4
  125. package/src/execution/__tests__/stdin-writer.test.ts +6 -4
  126. package/src/execution/__tests__/stream-sink-retirement.test.ts +6 -6
  127. package/src/execution/__tests__/subagent-actions-core.test.ts +1017 -0
  128. package/src/execution/__tests__/subagent-service-message-close.test.ts +99 -35
  129. package/src/execution/__tests__/subagent-service-multiproc-guard.test.ts +174 -0
  130. package/src/execution/__tests__/subagent-service-notify-gate.test.ts +271 -0
  131. package/src/execution/__tests__/subagent-service-parent-guard.test.ts +33 -29
  132. package/src/execution/__tests__/subagent-service-recovery-bounds.test.ts +341 -0
  133. package/src/execution/__tests__/subagent-service.test.ts +37 -30
  134. package/src/execution/__tests__/subprocess-agent-runner-routing.test.ts +88 -12
  135. package/src/execution/__tests__/subprocess-agent-runner-timeout.test.ts +75 -0
  136. package/src/execution/__tests__/subprocess-agent-runner.test.ts +22 -20
  137. package/src/execution/__tests__/temp-prompt.test.ts +1 -1
  138. package/src/execution/__tests__/timeout-integration.test.ts +13 -12
  139. package/src/execution/__tests__/turn-limiter-semantics.test.ts +1 -1
  140. package/src/execution/__tests__/turn-limiter.test.ts +1 -1
  141. package/src/execution/__tests__/ui-request-handler-factory.test.ts +107 -7
  142. package/src/execution/__tests__/ui-request-handler.test.ts +4 -3
  143. package/src/execution/__tests__/ui-request-queue.test.ts +167 -2
  144. package/src/execution/__tests__/worktree-git-ops.test.ts +571 -0
  145. package/src/execution/__tests__/worktree-manager.test.ts +14 -5
  146. package/src/execution/__tests__/worktree-pid-registration.integration.test.ts +4 -3
  147. package/src/execution/__tests__/worktree-reconcile-aging.test.ts +204 -0
  148. package/src/execution/agent-registry.ts +298 -30
  149. package/src/execution/agent-result-mapper.ts +3 -0
  150. package/src/execution/agents-assembly.ts +88 -0
  151. package/src/execution/cold-resurrect.ts +199 -0
  152. package/src/execution/concurrency-pool.ts +71 -9
  153. package/src/execution/dialog-queue.ts +101 -3
  154. package/src/execution/engine/__tests__/common/capability-gate.test.ts +91 -0
  155. package/src/execution/engine/__tests__/common/data-dir.test.ts +1 -1
  156. package/src/execution/engine/__tests__/common/journal-wiring.test.ts +84 -0
  157. package/src/execution/engine/__tests__/common/kill-chain.test.ts +34 -7
  158. package/src/execution/engine/__tests__/common/persona-router.test.ts +4 -5
  159. package/src/execution/engine/__tests__/common/pool-manager.test.ts +211 -51
  160. package/src/execution/engine/__tests__/common/session-view-service-zcode-dbpath.test.ts +149 -0
  161. package/src/execution/engine/__tests__/common/session-view-service.test.ts +499 -0
  162. package/src/execution/engine/__tests__/conformance/contract.abort.test.ts +56 -99
  163. package/src/execution/engine/__tests__/conformance/contract.agent-events.test.ts +88 -15
  164. package/src/execution/engine/__tests__/conformance/engine-conformance.live.test.ts +33 -7
  165. package/src/execution/engine/__tests__/conformance/golden-replay.pi.test.ts +1 -1
  166. package/src/execution/engine/__tests__/conformance/golden-replay.zcode.test.ts +75 -54
  167. package/src/execution/engine/__tests__/conformance/zcode-appserver-harness.ts +128 -0
  168. package/src/execution/engine/__tests__/engine-discovery.test.ts +15 -3
  169. package/src/execution/engine/__tests__/model-prompt.test.ts +15 -3
  170. package/src/execution/engine/__tests__/registry.test.ts +94 -3
  171. package/src/execution/engine/__tests__/routing.test.ts +115 -6
  172. package/src/execution/engine/common/capability-gate.ts +92 -0
  173. package/src/execution/engine/common/journal-wiring.ts +97 -0
  174. package/src/execution/engine/common/kill-chain.ts +42 -10
  175. package/src/execution/engine/common/nesting-guard.ts +66 -0
  176. package/src/execution/engine/common/persona-router.ts +18 -8
  177. package/src/execution/engine/common/pool-manager.ts +332 -78
  178. package/src/execution/engine/common/session-view-service.ts +506 -0
  179. package/src/execution/engine/common/session-view-types.ts +125 -0
  180. package/src/execution/engine/engine-discovery.ts +12 -16
  181. package/src/execution/engine/engines/pi/__tests__/pi-engine.test.ts +174 -28
  182. package/src/execution/engine/engines/pi/__tests__/spawn-opts-direct.test.ts +257 -0
  183. package/src/execution/{argv-mirror.ts → engine/engines/pi/argv-mirror.ts} +43 -21
  184. package/src/execution/{get-state-handshake.ts → engine/engines/pi/get-state-handshake.ts} +86 -11
  185. package/src/execution/{output-collector.ts → engine/engines/pi/output-collector.ts} +108 -19
  186. package/src/execution/engine/engines/pi/pi-engine.ts +434 -38
  187. package/src/execution/{pi-invocation.ts → engine/engines/pi/pi-invocation.ts} +2 -2
  188. package/src/execution/engine/engines/pi/reader.ts +11 -0
  189. package/src/execution/engine/engines/pi/registration.ts +4 -2
  190. package/src/execution/engine/engines/pi/session-runner.ts +2840 -0
  191. package/src/execution/{spawn-event-adapter.ts → engine/engines/pi/spawn-event-adapter.ts} +75 -34
  192. package/src/execution/{stdin-writer.ts → engine/engines/pi/stdin-writer.ts} +10 -9
  193. package/src/execution/{temp-prompt.ts → engine/engines/pi/temp-prompt.ts} +2 -2
  194. package/src/execution/{turn-limiter.ts → engine/engines/pi/turn-limiter.ts} +1 -1
  195. package/src/execution/engine/engines/zcode/__tests__/__fixtures__/fake-appserver.mjs +301 -0
  196. package/src/execution/engine/engines/zcode/__tests__/__fixtures__/zcode-golden-appserver.json +36 -0
  197. package/src/execution/engine/engines/zcode/__tests__/appserver-launcher.test.ts +321 -0
  198. package/src/execution/engine/engines/zcode/__tests__/connection.test.ts +473 -0
  199. package/src/execution/engine/engines/zcode/__tests__/parser.test.ts +7 -210
  200. package/src/execution/engine/engines/zcode/__tests__/preparer.test.ts +6 -110
  201. package/src/execution/engine/engines/zcode/__tests__/session-channel-dispose-harvest.test.ts +247 -0
  202. package/src/execution/engine/engines/zcode/__tests__/session-channel-turn-timers.test.ts +451 -0
  203. package/src/execution/engine/engines/zcode/__tests__/session-channel.test.ts +792 -0
  204. package/src/execution/engine/engines/zcode/__tests__/zcode-engine-appserver.test.ts +782 -0
  205. package/src/execution/engine/engines/zcode/__tests__/zcode-engine-degrade.test.ts +168 -0
  206. package/src/execution/engine/engines/zcode/__tests__/zcode-engine-dispose.test.ts +210 -0
  207. package/src/execution/engine/engines/zcode/__tests__/zcode-engine-retry.test.ts +353 -0
  208. package/src/execution/engine/engines/zcode/__tests__/zcode-engine-status.test.ts +282 -0
  209. package/src/execution/engine/engines/zcode/__tests__/zcode-engine-timeout.test.ts +372 -0
  210. package/src/execution/engine/engines/zcode/__tests__/zcode-engine.live.test.ts +65 -47
  211. package/src/execution/engine/engines/zcode/appserver-launcher.ts +167 -0
  212. package/src/execution/engine/engines/zcode/connection.ts +598 -0
  213. package/src/execution/engine/engines/zcode/constants.ts +147 -14
  214. package/src/execution/engine/engines/zcode/golden-sample.ts +42 -34
  215. package/src/execution/engine/engines/zcode/parser.ts +12 -346
  216. package/src/execution/engine/engines/zcode/preparer.ts +22 -150
  217. package/src/execution/engine/engines/zcode/session-channel.ts +968 -0
  218. package/src/execution/engine/engines/zcode/zcode-engine.ts +1023 -250
  219. package/src/execution/engine/host-task-spec.ts +32 -36
  220. package/src/execution/engine/model-validation.ts +177 -0
  221. package/src/execution/engine/port.ts +65 -7
  222. package/src/execution/engine/registry.ts +53 -0
  223. package/src/execution/engine/routing.ts +75 -1
  224. package/src/execution/engine/types.ts +30 -103
  225. package/src/execution/execution-record.ts +222 -144
  226. package/src/execution/finalize-record.ts +181 -85
  227. package/src/execution/idle-gc.ts +28 -0
  228. package/src/execution/lifecycle-manager.ts +27 -3
  229. package/src/execution/lifecycle-predicates.ts +1 -1
  230. package/src/execution/manifest-store.ts +53 -62
  231. package/src/execution/notifier.ts +10 -10
  232. package/src/execution/notify-host.ts +212 -0
  233. package/src/execution/notify-ledger.ts +117 -16
  234. package/src/execution/record-entry.ts +8 -2
  235. package/src/execution/record-store.ts +148 -88
  236. package/src/execution/round-settlement.ts +93 -0
  237. package/src/execution/session-file-gc.ts +96 -62
  238. package/src/execution/session-pending.ts +213 -62
  239. package/src/execution/session-reconstructor.ts +256 -143
  240. package/src/execution/sessions-index.ts +127 -102
  241. package/src/execution/settled-watchdog.ts +311 -0
  242. package/src/execution/subagent-actions-core.ts +686 -0
  243. package/src/execution/subagent-service.ts +1106 -907
  244. package/src/execution/subprocess-agent-runner.ts +144 -97
  245. package/src/execution/types.ts +9 -0
  246. package/src/execution/ui-request-handler-factory.ts +126 -75
  247. package/src/execution/ui-request-queue.ts +50 -19
  248. package/src/execution/worktree-git-ops.ts +397 -0
  249. package/src/execution/worktree-manager.ts +72 -10
  250. package/src/execution/worktree-registry.ts +6 -12
  251. package/src/index.ts +542 -9
  252. package/src/orchestration/__tests__/__fixtures__/worker-template.snapshot.txt +42 -3
  253. package/src/orchestration/__tests__/agent-call-catch-fallback.test.ts +3 -5
  254. package/src/orchestration/__tests__/agent-call-stream.test.ts +2 -2
  255. package/src/orchestration/__tests__/args-meta.test.ts +358 -0
  256. package/src/orchestration/__tests__/args-validator.test.ts +14 -0
  257. package/src/orchestration/__tests__/error-recovery-rebuild-failure.test.ts +312 -0
  258. package/src/orchestration/__tests__/error-recovery-terminal-hardening.test.ts +384 -0
  259. package/src/orchestration/__tests__/execute-agent-call.test.ts +151 -11
  260. package/src/orchestration/__tests__/file-run-store-prune.test.ts +168 -0
  261. package/src/orchestration/__tests__/file-run-store-throttle.test.ts +168 -0
  262. package/src/orchestration/__tests__/file-run-store.test.ts +390 -0
  263. package/src/orchestration/__tests__/helpers/flush-microtasks.ts +13 -0
  264. package/src/orchestration/__tests__/launcher-nested-workflow.test.ts +34 -0
  265. package/src/orchestration/__tests__/lifecycle-abort-broadcast-signal.test.ts +357 -0
  266. package/src/orchestration/__tests__/lifecycle-recover-crashed.test.ts +289 -0
  267. package/src/orchestration/__tests__/lifecycle-runid-injection.test.ts +3 -3
  268. package/src/orchestration/__tests__/lifecycle.test.ts +84 -5
  269. package/src/orchestration/__tests__/non-cloneable-return-e2e.test.ts +1 -1
  270. package/src/orchestration/__tests__/run-snapshot.test.ts +356 -0
  271. package/src/orchestration/__tests__/script-generate.test.ts +314 -0
  272. package/src/orchestration/__tests__/script-lint.test.ts +17 -0
  273. package/src/orchestration/__tests__/test-mocks.ts +2 -0
  274. package/src/orchestration/__tests__/worker-exit-without-result.test.ts +7 -6
  275. package/src/orchestration/__tests__/worker-message-pump-finalize-run.test.ts +229 -0
  276. package/src/orchestration/__tests__/{error-recovery-handlers.test.ts → worker-message-pump-handlers.test.ts} +28 -27
  277. package/src/orchestration/__tests__/{error-recovery-postmessage-defense.test.ts → worker-message-pump-postmessage-defense.test.ts} +8 -8
  278. package/src/orchestration/__tests__/{error-recovery-serialize-failed-result.test.ts → worker-message-pump-serialize-failed-result.test.ts} +2 -2
  279. package/src/orchestration/__tests__/{error-recovery-workflow-call.test.ts → worker-message-pump-workflow-call.test.ts} +1 -1
  280. package/src/orchestration/__tests__/worker-pending-timeout-abort.test.ts +275 -0
  281. package/src/orchestration/__tests__/worker-script-builder-runtime.test.ts +78 -1
  282. package/src/orchestration/__tests__/worker-script-builder.test.ts +1 -1
  283. package/src/orchestration/__tests__/workflow-files.test.ts +186 -0
  284. package/src/orchestration/__tests__/workflow-nesting-e2e.test.ts +1 -1
  285. package/src/orchestration/__tests__/workflow-run-summary.test.ts +119 -0
  286. package/src/orchestration/__tests__/workflow-script-registry-impl.test.ts +124 -0
  287. package/src/orchestration/agent-opts-resolver.ts +8 -8
  288. package/src/orchestration/args-meta.ts +198 -0
  289. package/src/orchestration/args-validator.ts +53 -31
  290. package/src/orchestration/execute-agent-call.ts +25 -79
  291. package/src/orchestration/file-run-store.ts +327 -0
  292. package/src/orchestration/launcher.ts +160 -104
  293. package/src/orchestration/lifecycle.ts +371 -97
  294. package/src/orchestration/models/agent-call.ts +7 -7
  295. package/src/orchestration/models/budget.ts +5 -5
  296. package/src/orchestration/models/ports.ts +4 -4
  297. package/src/orchestration/models/run-runtime.ts +15 -15
  298. package/src/orchestration/models/trace.ts +9 -9
  299. package/src/orchestration/models/types.ts +111 -22
  300. package/src/orchestration/models/workflow-run.ts +28 -28
  301. package/src/orchestration/models/workflow-script.ts +4 -4
  302. package/src/orchestration/run-snapshot.ts +266 -0
  303. package/src/orchestration/script-generate.ts +154 -0
  304. package/src/orchestration/script-lint.ts +123 -90
  305. package/src/orchestration/worker-handle.ts +10 -10
  306. package/src/orchestration/worker-host.ts +10 -10
  307. package/src/orchestration/{error-recovery.ts → worker-message-pump.ts} +498 -221
  308. package/src/orchestration/worker-script-builder.ts +401 -346
  309. package/src/orchestration/workflow-files.ts +37 -11
  310. package/src/orchestration/workflow-run-summary.ts +69 -0
  311. package/src/orchestration/workflow-script-registry-impl.ts +31 -9
  312. package/src/shared/__tests__/agent-ref.test.ts +209 -2
  313. package/src/shared/__tests__/atomic-write.test.ts +267 -0
  314. package/src/shared/__tests__/bounded-serialize.test.ts +236 -0
  315. package/src/shared/__tests__/injection-render.test.ts +518 -0
  316. package/src/shared/__tests__/meta-parser.test.ts +151 -0
  317. package/src/shared/__tests__/resource-discovery-host-roots.test.ts +474 -0
  318. package/src/shared/__tests__/resource-discovery.test.ts +261 -217
  319. package/src/shared/agent-ref.ts +143 -2
  320. package/src/shared/atomic-write.ts +323 -0
  321. package/src/shared/bounded-serialize.ts +154 -0
  322. package/src/shared/injection-render.ts +279 -0
  323. package/src/shared/meta-parser.ts +182 -68
  324. package/src/shared/model-ref.ts +15 -2
  325. package/src/shared/resource-discovery.ts +97 -204
  326. package/src/shared/resource-meta.ts +14 -0
  327. package/src/shared/schema-jsonify.ts +1 -1
  328. package/src/shared/xml-injection.ts +9 -9
  329. package/workflows/README.md +9 -9
  330. package/workflows/chain.js +4 -2
  331. package/workflows/map-reduce.js +5 -3
  332. package/workflows/parallel.js +5 -3
  333. package/workflows/review-fix-loop-utils.cjs +165 -92
  334. package/workflows/review-fix-loop-utils.d.cts +287 -0
  335. package/workflows/review-fix-loop.js +18 -7
  336. package/workflows/scatter-gather.js +4 -2
  337. package/dist/chunk-3VOERJPJ.js +0 -22
  338. package/src/execution/__tests__/execute-options-mapper.test.ts +0 -200
  339. package/src/execution/engine/engines/pi/__tests__/task-spec-mapper.test.ts +0 -164
  340. package/src/execution/engine/engines/pi/task-spec-mapper.ts +0 -100
  341. package/src/execution/engine/engines/zcode/__tests__/__fixtures__/zcode-golden-spawn.json +0 -39
  342. package/src/execution/engine/engines/zcode/__tests__/launcher.test.ts +0 -150
  343. package/src/execution/engine/engines/zcode/__tests__/zcode-engine.test.ts +0 -580
  344. package/src/execution/engine/engines/zcode/launcher.ts +0 -161
  345. package/src/execution/execute-options-mapper.ts +0 -115
  346. package/src/execution/session-runner.ts +0 -1789
  347. package/src/shared/__tests__/resource-discovery-manifest-cache.test.ts +0 -288
@@ -87,7 +87,7 @@ function makeRunningRealRun(
87
87
  const terminate = vi.fn(async () => {});
88
88
  const controller = new AbortController();
89
89
  const abort = vi.spyOn(controller, "abort");
90
- const worker = { terminate, postMessage: vi.fn() } as unknown as Parameters<typeof RunRuntime.prototype.constructor>[0];
90
+ const worker = { terminate, postMessage: vi.fn() } as unknown as ConstructorParameters<typeof RunRuntime>[0];
91
91
  const runtime = new RunRuntime(
92
92
  worker as never,
93
93
  controller,
@@ -217,7 +217,7 @@ describe("runWorkflow", () => {
217
217
  const deps = makeDeps();
218
218
  // worker.start 被调时探测 runs 注册状态——证明 runs.set 在 assignRuntime 之后
219
219
  let runsSizeAtWorkerStart = -1;
220
- deps.workerHost.start = vi.fn(() => {
220
+ vi.spyOn(deps.workerHost, "start").mockImplementation(() => {
221
221
  runsSizeAtWorkerStart = deps.runs.size;
222
222
  return { postMessage: vi.fn(), terminate: vi.fn(async () => {}) };
223
223
  });
@@ -251,6 +251,44 @@ describe("runWorkflow", () => {
251
251
  expect(deps.eventBus.emit).not.toHaveBeenCalled();
252
252
  });
253
253
 
254
+ // OR-1 顺序修复(unbounded-wait-audit §4.1):budgetTimeMs 溢出的 fail-fast
255
+ // 必须发生在首个副作用(workerHost.start)之前——旧顺序 worker 先启动、
256
+ // scheduleTimeBudget 后 throw,产生「worker 已跑、run 永不可达」的孤儿。
257
+ it("OR-1: budgetTimeMs 溢出(>2^31-1)→ worker 启动前 fail-fast(zero side effects)", async () => {
258
+ const deps = makeDeps();
259
+ const spec = makeSpec({ budgetTimeMs: 3_000_000_000 });
260
+
261
+ await expect(runWorkflow(spec, deps)).rejects.toThrow(/2147483647/);
262
+
263
+ // fail-fast 先于全部副作用:worker 未启动、run 未注册、未落盘、未通知
264
+ expect(deps.workerHost.start).not.toHaveBeenCalled();
265
+ expect(deps.runs.size).toBe(0);
266
+ expect(deps.store.save).not.toHaveBeenCalled();
267
+ expect(deps.eventBus.emit).not.toHaveBeenCalled();
268
+ });
269
+
270
+ // OR-1 前移的代价:start 抛错时已挂的 timeBudget timer 会残留(到期对从未注册的
271
+ // runId 触发 abortRun → not found)——start 抛错路径必须就地 clearTimeout。
272
+ it("OR-1: budgetTimeMs 有效但 worker.start 抛错 → timeBudget timer 被清理(无残留)", async () => {
273
+ const deps = makeDeps();
274
+ deps.workerHost.start = vi.fn(() => {
275
+ throw new Error("worker boot failed");
276
+ });
277
+
278
+ // fake timers:用例开始时无 pending 计时器(beforeEach 全新安装)
279
+ expect(vi.getTimerCount()).toBe(0);
280
+
281
+ await expect(
282
+ runWorkflow(makeSpec({ budgetTimeMs: 60_000 }), deps),
283
+ ).rejects.toThrow("worker boot failed");
284
+
285
+ // timer 挂载(scheduleTimeBudget)发生在 start 之前 → 抛错路径必须回收:
286
+ // 无 pending 计时器残留(若漏清理此处为 1,到期会误 abort 幽灵 runId)
287
+ expect(vi.getTimerCount()).toBe(0);
288
+ // 既有行为不回归:runs Map 无该 runId 条目(abortRun not found 语义保持)
289
+ expect(deps.runs.size).toBe(0);
290
+ });
291
+
254
292
  it("带 budgetTimeMs 时调度时间预算计时器(真实消费:挂载 + 到期 time_limited)", async () => {
255
293
  const deps = makeDeps();
256
294
  const spec = makeSpec({ budgetTimeMs: 3000 });
@@ -359,6 +397,25 @@ describe("abortRun", () => {
359
397
  expect(deps.onRunDone).not.toHaveBeenCalled();
360
398
  });
361
399
 
400
+ it("[B-4] eventBus listener 抛错 → abortRun 不向调用方抛错、onRunDone 仍执行", async () => {
401
+ const { run, terminate } = makeRunningRealRun("wf-abort-fence");
402
+ const deps = makeDeps();
403
+ deps.eventBus.emit = vi.fn(() => {
404
+ throw new Error("listener exploded");
405
+ });
406
+ deps.runs.set("wf-abort-fence", run);
407
+
408
+ // 旧实现裸调 emit:listener 抛错 → 异常上抛给 tool 调用方 + 跳过 onRunDone
409
+ await expect(abortRun("wf-abort-fence", deps, "abort with broken bus")).resolves.toBeUndefined();
410
+
411
+ // abort 成功语义保持:终态转换/terminate 照常完成
412
+ expect(run.state.status).toBe("done");
413
+ expect(run.state.reason).toBe("aborted");
414
+ expect(terminate).toHaveBeenCalledTimes(1);
415
+ // 独立围栏:onRunDone 不被 emit 故障跳过
416
+ expect(deps.onRunDone).toHaveBeenCalledTimes(1);
417
+ });
418
+
362
419
  it("自定义 doneReason(time_limited)", async () => {
363
420
  const { run } = makeRunningRealRun("wf-abort-3");
364
421
  const deps = makeDeps();
@@ -436,7 +493,27 @@ describe("terminateRunningRuns", () => {
436
493
  expect(deps.store.save).toHaveBeenCalledWith(run);
437
494
  });
438
495
 
439
- it(" run save 抛错不中断其余(其余 run 仍落盘 + unregister)", async () => {
496
+ it("[B-4] eventBus listener 抛错 → 不向调用方抛错、本 run 收尾完成、其余 run 继续终止", async () => {
497
+ const { run: a, terminate: terminateA } = makeRunningRealRun("wf-term-fence-a");
498
+ const { run: b, terminate: terminateB } = makeRunningRealRun("wf-term-fence-b");
499
+ const deps = makeDeps();
500
+ deps.runs.set("wf-term-fence-a", a);
501
+ deps.runs.set("wf-term-fence-b", b);
502
+ // 仅 runA 的 emit 抛错(按 payload id 判别模拟坏 listener)
503
+ deps.eventBus.emit = vi.fn((_event: string, payload: { id: string }) => {
504
+ if (payload.id === "wf-term-fence-a") throw new Error("listener exploded");
505
+ });
506
+
507
+ await expect(terminateRunningRuns(deps, "Session switched: run terminated")).resolves.toBeUndefined();
508
+
509
+ // 两个 run 都走到终态(runA 的 emit 故障不中断批量收尾)
510
+ expect(a.state.reason).toBe("failed");
511
+ expect(b.state.reason).toBe("failed");
512
+ expect(terminateA).toHaveBeenCalledTimes(1);
513
+ expect(terminateB).toHaveBeenCalledTimes(1);
514
+ });
515
+
516
+ it("单 run save 抛错不中断其余(save best-effort:unregister 仍发 + 其余 run 照常落盘)", async () => {
440
517
  const { run: bad } = makeRunningRealRun("wf-term-err");
441
518
  const { run: good } = makeRunningRealRun("wf-term-ok");
442
519
  const deps = makeDeps();
@@ -448,10 +525,12 @@ describe("terminateRunningRuns", () => {
448
525
 
449
526
  await terminateRunningRuns(deps, "Session shutdown: run terminated");
450
527
 
451
- // 抛错的 run:transition 先于 save,状态已转 done,但 unregister save 失败短路
528
+ // [D5-② finalizeRun 收敛] save 失败 best-effort(SW-DATA-3 统一):transition 先于
529
+ // save,状态已转 done;与收敛前不同,unregister 不再被 save 失败短路——否则
530
+ // pending 通知幽灵注销(列表残留永不清理的 running 条目)
452
531
  expect(bad.state.status).toBe("done");
453
532
  expect(bad.state.reason).toBe("failed");
454
- expect(deps.eventBus.emit).not.toHaveBeenCalledWith("pending:unregister", {
533
+ expect(deps.eventBus.emit).toHaveBeenCalledWith("pending:unregister", {
455
534
  id: "wf-term-err",
456
535
  reason: "failed",
457
536
  });
@@ -80,7 +80,7 @@ describe("[F1] 不可克隆 return → run failed(非悬挂)— e2e", () =>
80
80
  // worker 侧 fallback 消息经 handleScriptError 超限路径写入归因
81
81
  expect(run.state.error).toContain("structured-clone failed");
82
82
  // 终态副作用齐全(对比 SW-DATA-3/F1 前的幽灵悬挂:无 unregister / 无 onRunDone)
83
- expect(deps.eventBus.emit).toHaveBeenCalledWith(
83
+ expect(deps.eventBus!.emit).toHaveBeenCalledWith(
84
84
  "pending:unregister",
85
85
  expect.objectContaining({ id: runId, reason: "failed" }),
86
86
  );
@@ -0,0 +1,356 @@
1
+ // run-snapshot.test.ts —— WorkflowRun 快照 codec 单源(下沉收口 D4/U8)。
2
+ //
3
+ // 四视角:
4
+ // ①使用者——toRunSnapshot/fromRunSnapshot 往返等值(含 pi 壳 jsonl-run-store
5
+ // serializeRun 现网形态样本:键序/v 字段/无 live 的快照行,⛔5 往返逐字节一致);
6
+ // ②隔离者——live-strip 产出新对象,不 mutate 内存中的 run(save 后 run 可继续跑);
7
+ // ③幸存者——版本 guard(D4 裁决③:v 不匹配即拒,字符串无大小序)+ 形状校验
8
+ // 全分支不抛(返回 undefined);
9
+ // ④接线者——「缺 v 宽容」不内聚进 codec(D4 裁决②归属:store 层预处理职责,
10
+ // codec 层缺 v 即拒,保 pi 侧 v1 存量静默跳过语义)+ spec.budgetRef 剔除。
11
+ //
12
+ // 纯内存测试:无 configureCore 依赖(codec 不触 host-services)。
13
+
14
+ import { describe, expect, it } from "vitest";
15
+
16
+ import type { ExecutionRecord } from "../../execution/types.ts";
17
+ import type { ExecutionTraceNode } from "../models/types.ts";
18
+ import { AgentCall } from "../models/agent-call.ts";
19
+ import { Budget } from "../models/budget.ts";
20
+ import { Trace } from "../models/trace.ts";
21
+ import { WorkflowRun } from "../models/workflow-run.ts";
22
+ import { SNAPSHOT_VERSION, fromRunSnapshot, toRunSnapshot } from "../run-snapshot.ts";
23
+
24
+ /** 构造可持久化的 WorkflowRun(对齐 file-run-store.test.ts makeRun 模式)。 */
25
+ function makeRun(runId: string, opts: { status?: "running" | "done" } = {}): WorkflowRun {
26
+ const status = opts.status ?? "running";
27
+ return WorkflowRun.reconstruct(
28
+ runId,
29
+ {
30
+ scriptSource: "export function execute() { return 'ok'; }",
31
+ args: { topic: "demo", count: 2 },
32
+ scriptName: "test-script",
33
+ scriptPath: "/fake/test.js",
34
+ parameters: { type: "object" },
35
+ budgetTokens: 1000,
36
+ },
37
+ {
38
+ status,
39
+ ...(status === "done" ? { reason: "completed" as const } : {}),
40
+ budget: new Budget({ maxTokens: 1000, usedTokens: 42, usedCost: 0.5, totalCallCount: 3 }),
41
+ calls: new Map(),
42
+ trace: new Trace(),
43
+ errorLogs: [],
44
+ scriptResult: status === "done" ? { summary: "done-value" } : undefined,
45
+ },
46
+ { startedAt: "2026-08-30T00:00:00.000Z" },
47
+ );
48
+ }
49
+
50
+ /** 最小 live 执行进度对象(strip 逻辑只解构键名,值形态不可达——对齐
51
+ * lifecycle-predicates.test.ts 的 partial + as ExecutionRecord 先例)。 */
52
+ function makeLiveRecord(id: string): ExecutionRecord {
53
+ return {
54
+ id,
55
+ agent: "coder",
56
+ model: "test-model",
57
+ thinkingLevel: undefined,
58
+ mode: "background",
59
+ task: "do work",
60
+ slug: "do-work",
61
+ startedAt: 0,
62
+ rootSessionId: undefined,
63
+ parentRecordId: undefined,
64
+ depth: 0,
65
+ status: "running",
66
+ turns: [],
67
+ turnCount: 0,
68
+ totalTokens: 0,
69
+ lastError: undefined,
70
+ round: 0,
71
+ endedAt: undefined,
72
+ result: undefined,
73
+ error: undefined,
74
+ agentResult: undefined,
75
+ controller: undefined,
76
+ } as ExecutionRecord;
77
+ }
78
+
79
+ /**
80
+ * pi 壳 jsonl-run-store.ts serializeRun 现网形态样本(顶层 done 前的 running
81
+ * run,字段集/键序按其对象字面量排列)。pi 存量行恒无 live(其 serializeRun
82
+ * strip)、恒带 v="wf-run-v2"(D4 裁决①:版本值沿用)。
83
+ */
84
+ const PI_FORM_SNAPSHOT = {
85
+ v: "wf-run-v2",
86
+ runId: "wf-pi-form-1",
87
+ spec: {
88
+ scriptSource: "export function execute() { return 'pi-form'; }",
89
+ args: { topic: "demo" },
90
+ scriptName: "test-script",
91
+ scriptPath: "/fake/test.js",
92
+ budgetTokens: 1000,
93
+ },
94
+ state: {
95
+ status: "running",
96
+ budget: { maxTokens: 1000, usedTokens: 42, usedCost: 0.5, totalCallCount: 3 },
97
+ calls: [
98
+ {
99
+ id: 0,
100
+ opts: { prompt: "do work" },
101
+ status: "done",
102
+ attempts: 1,
103
+ result: { content: "ok" },
104
+ sessionId: "sess-1",
105
+ sessionFile: "/tmp/sess-1.jsonl",
106
+ traceNode: {
107
+ stepIndex: 0,
108
+ agent: "coder",
109
+ task: "do work",
110
+ model: "test-model",
111
+ status: "completed",
112
+ startedAt: "2026-08-30T00:00:01.000Z",
113
+ completedAt: "2026-08-30T00:00:02.000Z",
114
+ sessionId: "sess-1",
115
+ sessionFile: "/tmp/sess-1.jsonl",
116
+ },
117
+ },
118
+ ],
119
+ trace: [
120
+ {
121
+ stepIndex: 0,
122
+ agent: "coder",
123
+ task: "do work",
124
+ model: "test-model",
125
+ status: "completed",
126
+ startedAt: "2026-08-30T00:00:01.000Z",
127
+ completedAt: "2026-08-30T00:00:02.000Z",
128
+ sessionId: "sess-1",
129
+ sessionFile: "/tmp/sess-1.jsonl",
130
+ },
131
+ ],
132
+ errorLogs: [{ level: "warn", message: "retrying" }],
133
+ },
134
+ meta: { startedAt: "2026-08-30T00:00:00.000Z" },
135
+ };
136
+
137
+ describe("run-snapshot — toRunSnapshot/fromRunSnapshot 往返等值", () => {
138
+ it("⛔5 pi 现网形态样本:重水合 → 再序列化与原行逐字节一致(v 字段保持 wf-run-v2)", () => {
139
+ const line = JSON.stringify(PI_FORM_SNAPSHOT);
140
+
141
+ const run = fromRunSnapshot(JSON.parse(line));
142
+ expect(run).toBeDefined();
143
+ expect(run!.runId).toBe("wf-pi-form-1");
144
+
145
+ // 逐字节一致:pi 切换本 codec 后存量行往返不变(u-sw-store ⛔5 前提)
146
+ expect(JSON.stringify(toRunSnapshot(run!))).toBe(line);
147
+ });
148
+
149
+ it("running 快照往返:runId/spec/budget/trace/meta 字段保真", () => {
150
+ const run = makeRun("wf-snap-1");
151
+ const back = fromRunSnapshot(JSON.parse(JSON.stringify(toRunSnapshot(run))));
152
+
153
+ expect(back).toBeDefined();
154
+ expect(back!.runId).toBe("wf-snap-1");
155
+ expect(back!.state.status).toBe("running");
156
+ expect(back!.spec.scriptName).toBe("test-script");
157
+ expect(back!.spec.args).toEqual({ topic: "demo", count: 2 });
158
+ expect(back!.state.budget).toBeInstanceOf(Budget);
159
+ expect(back!.state.budget.usedTokens).toBe(42);
160
+ expect(back!.state.budget.totalCallCount).toBe(3);
161
+ expect(back!.state.trace).toBeInstanceOf(Trace);
162
+ expect(back!.runtime).toBeUndefined();
163
+ expect(back!.meta.startedAt).toBe("2026-08-30T00:00:00.000Z");
164
+ });
165
+
166
+ it("done 快照往返:reason/scriptResult/errorLogs/completedAt 保真", () => {
167
+ const run = makeRun("wf-snap-2", { status: "done" });
168
+ run.state.errorLogs.push({ level: "warn", message: "transient" });
169
+ run.meta.workerErrorCount = 1;
170
+
171
+ const back = fromRunSnapshot(JSON.parse(JSON.stringify(toRunSnapshot(run))));
172
+
173
+ expect(back!.state.status).toBe("done");
174
+ expect(back!.state.reason).toBe("completed");
175
+ expect(back!.state.scriptResult).toEqual({ summary: "done-value" });
176
+ expect(back!.state.errorLogs).toEqual([{ level: "warn", message: "transient" }]);
177
+ expect(back!.meta.completedAt).toBe(run.meta.completedAt);
178
+ expect(back!.meta.workerErrorCount).toBe(1);
179
+ });
180
+
181
+ it("含 calls 的往返:calls Map 逐项保真 + traceNode 回链 Trace 副本(D-10 尽力恢复)", () => {
182
+ const run = makeRun("wf-snap-3");
183
+ const node = {
184
+ stepIndex: 0,
185
+ agent: "coder",
186
+ task: "do work",
187
+ model: "test-model",
188
+ status: "completed" as const,
189
+ };
190
+ run.state.trace.append(node);
191
+ const call = new AgentCall(0, { prompt: "do work" }, node);
192
+ call.status = "done";
193
+ call.attempts = 1;
194
+ run.state.calls.set(0, call);
195
+
196
+ const back = fromRunSnapshot(JSON.parse(JSON.stringify(toRunSnapshot(run))))!;
197
+
198
+ expect(back.state.calls.size).toBe(1);
199
+ const restored = back.state.calls.get(0)!;
200
+ expect(restored.opts.prompt).toBe("do work");
201
+ expect(restored.status).toBe("done");
202
+ expect(restored.attempts).toBe(1);
203
+ expect(restored.traceNode).toBe(back.state.trace.toArray()[0]);
204
+ });
205
+ });
206
+
207
+ describe("run-snapshot — live-strip 防御内聚", () => {
208
+ it("trace 节点与 calls[].traceNode 带 live → 序列化输出无 live 键", () => {
209
+ const run = makeRun("wf-live-1");
210
+ const node: ExecutionTraceNode = {
211
+ stepIndex: 0,
212
+ agent: "coder",
213
+ task: "do work",
214
+ model: "test-model",
215
+ status: "running",
216
+ live: undefined,
217
+ };
218
+ run.state.trace.append(node);
219
+ const call = new AgentCall(0, { prompt: "do work" }, node);
220
+ run.state.calls.set(0, call);
221
+ // D-10:call.traceNode 与 trace 节点共享引用,live 挂节点上(running 态常态)
222
+ node.live = makeLiveRecord("run-0");
223
+
224
+ const snap = toRunSnapshot(run);
225
+ const serialized = JSON.stringify(snap);
226
+
227
+ expect(serialized.includes('"live"')).toBe(false);
228
+ expect(snap.state.trace[0]).not.toHaveProperty("live");
229
+ expect(snap.state.calls[0].traceNode).not.toHaveProperty("live");
230
+ });
231
+
232
+ it("strip 产出新对象,不 mutate 内存 run(save 后 run 可继续跑)", () => {
233
+ const run = makeRun("wf-live-2");
234
+ const node: ExecutionTraceNode = {
235
+ stepIndex: 0,
236
+ agent: "coder",
237
+ task: "do work",
238
+ model: "test-model",
239
+ status: "running",
240
+ live: undefined,
241
+ };
242
+ run.state.trace.append(node);
243
+ node.live = makeLiveRecord("run-0");
244
+
245
+ toRunSnapshot(run);
246
+
247
+ // 原节点 live 保留(运行期消费面——TUI 事件流投影——不受落盘影响)
248
+ expect(node.live).toBeDefined();
249
+ expect(node.live!.id).toBe("run-0");
250
+ });
251
+ });
252
+
253
+ describe("run-snapshot — spec.budgetRef 剔除", () => {
254
+ it("spec.budgetRef(进程内共享引用)不落盘,spec 其余字段保真", () => {
255
+ const run = makeRun("wf-ref-1");
256
+ const specWithRef = { ...run.spec, budgetRef: new Budget({ maxTokens: 500 }) };
257
+ // reconstruct 直造带 budgetRef 的聚合(嵌套 workflow 的 run 形态)
258
+ const nested = WorkflowRun.reconstruct("wf-ref-1", specWithRef, run.state, run.meta);
259
+
260
+ const snap = toRunSnapshot(nested);
261
+
262
+ expect(nested.spec.budgetRef).toBeDefined(); // 内存对象不受影响
263
+ expect(snap.spec).not.toHaveProperty("budgetRef");
264
+ expect(snap.spec.scriptName).toBe("test-script");
265
+
266
+ const back = fromRunSnapshot(JSON.parse(JSON.stringify(snap)))!;
267
+ expect(back.spec).not.toHaveProperty("budgetRef");
268
+ expect(back.spec.budgetTokens).toBe(1000);
269
+ });
270
+ });
271
+
272
+ describe("run-snapshot — 版本 guard(D4 裁决③)", () => {
273
+ it(`v = 当前版本(${SNAPSHOT_VERSION})通过`, () => {
274
+ const run = fromRunSnapshot(JSON.parse(JSON.stringify(toRunSnapshot(makeRun("wf-v-ok")))));
275
+ expect(run).toBeDefined();
276
+ });
277
+
278
+ it("未知更高版本(wf-run-v3)拒绝——字符串版本无大小序,不引入比较逻辑", () => {
279
+ const snap = { ...JSON.parse(JSON.stringify(toRunSnapshot(makeRun("wf-v-3")))), v: "wf-run-v3" };
280
+ expect(fromRunSnapshot(snap)).toBeUndefined();
281
+ });
282
+
283
+ it("旧版本 wf-run-v1 拒绝(pi 存量静默跳过语义的数据防线)", () => {
284
+ const snap = { ...JSON.parse(JSON.stringify(toRunSnapshot(makeRun("wf-v-1")))), v: "wf-run-v1" };
285
+ expect(fromRunSnapshot(snap)).toBeUndefined();
286
+ });
287
+
288
+ it("缺 v 字段拒绝——「缺 v 宽容」是 store 层预处理职责,不内聚进 codec(D4 裁决②)", () => {
289
+ const { v: _v, ...legacy } = JSON.parse(JSON.stringify(toRunSnapshot(makeRun("wf-v-none"))));
290
+ expect(fromRunSnapshot(legacy)).toBeUndefined();
291
+ });
292
+
293
+ it("v 为非字符串脏值(数字/null)拒绝", () => {
294
+ const base = JSON.parse(JSON.stringify(toRunSnapshot(makeRun("wf-v-dirty"))));
295
+ expect(fromRunSnapshot({ ...base, v: 2 })).toBeUndefined();
296
+ expect(fromRunSnapshot({ ...base, v: null })).toBeUndefined();
297
+ });
298
+ });
299
+
300
+ describe("run-snapshot — 形状校验(损坏行不抛,返回 undefined)", () => {
301
+ it("null / 非对象 / 缺 runId / 空 runId 拒绝", () => {
302
+ expect(fromRunSnapshot(null)).toBeUndefined();
303
+ expect(fromRunSnapshot("not an object")).toBeUndefined();
304
+ expect(fromRunSnapshot({ v: SNAPSHOT_VERSION, spec: {}, state: {}, meta: {} })).toBeUndefined();
305
+ expect(fromRunSnapshot({ v: SNAPSHOT_VERSION, runId: "", spec: {}, state: {}, meta: {} })).toBeUndefined();
306
+ });
307
+
308
+ it("缺 spec / 缺 state / 非法 status / 缺 budget / 缺 calls / 缺 trace / 缺 meta 拒绝", () => {
309
+ const ok = () => JSON.parse(JSON.stringify(toRunSnapshot(makeRun("wf-shape-base"))));
310
+ const without = (keys: string[]) => {
311
+ const snap = ok();
312
+ for (const k of keys) delete snap[k];
313
+ return snap;
314
+ };
315
+
316
+ expect(fromRunSnapshot(without(["spec"]))).toBeUndefined();
317
+ expect(fromRunSnapshot(without(["state"]))).toBeUndefined();
318
+ expect(fromRunSnapshot(without(["meta"]))).toBeUndefined();
319
+
320
+ const badStatus = ok();
321
+ badStatus.state.status = "paused"; // v1 三态残留在 v2 快照 = 形状损坏
322
+ expect(fromRunSnapshot(badStatus)).toBeUndefined();
323
+
324
+ const noBudget = ok();
325
+ delete noBudget.state.budget;
326
+ expect(fromRunSnapshot(noBudget)).toBeUndefined();
327
+
328
+ const noCalls = ok();
329
+ delete noCalls.state.calls;
330
+ expect(fromRunSnapshot(noCalls)).toBeUndefined();
331
+
332
+ const noTrace = ok();
333
+ delete noTrace.state.trace;
334
+ expect(fromRunSnapshot(noTrace)).toBeUndefined();
335
+ });
336
+
337
+ it("calls 内残缺条目跳过不炸整个 run(traceNode 缺失条目被丢弃)", () => {
338
+ const run = makeRun("wf-shape-call");
339
+ const node = {
340
+ stepIndex: 0,
341
+ agent: "coder",
342
+ task: "do work",
343
+ model: "test-model",
344
+ status: "completed" as const,
345
+ };
346
+ run.state.trace.append(node);
347
+ run.state.calls.set(0, new AgentCall(0, { prompt: "do work" }, node));
348
+
349
+ const snap = JSON.parse(JSON.stringify(toRunSnapshot(run)));
350
+ snap.state.calls.push({ notACall: true });
351
+
352
+ const back = fromRunSnapshot(snap)!;
353
+ expect(back).toBeDefined();
354
+ expect(back.state.calls.size).toBe(1);
355
+ });
356
+ });