@zhushanwen/subagent-core 0.5.1 → 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 (318) 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-Dv4QhSJ_.d.cts → engine-discovery-scan-ocpM8FmI.d.cts} +835 -668
  6. package/dist/{types-Dv4QhSJ_.d.ts → engine-discovery-scan-ocpM8FmI.d.ts} +835 -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 +14123 -15467
  20. package/dist/index.d.cts +1179 -1540
  21. package/dist/index.d.ts +1179 -1540
  22. package/dist/index.js +7694 -10986
  23. package/dist.bundle/index.cjs +33485 -0
  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__/agent-registry.test.ts +1 -1
  30. package/src/execution/__tests__/alive-store.test.ts +1 -1
  31. package/src/execution/__tests__/chat-engine-routing.test.ts +62 -28
  32. package/src/execution/__tests__/chat-round-first-round-watchdog.test.ts +255 -0
  33. package/src/execution/__tests__/collect-budget.test.ts +291 -0
  34. package/src/execution/__tests__/collect-coordinator-service.test.ts +316 -0
  35. package/src/execution/__tests__/collect-coordinator.test.ts +371 -0
  36. package/src/execution/__tests__/collect-mixed-dispatch.test.ts +229 -0
  37. package/src/execution/__tests__/config-collect-sync.test.ts +120 -0
  38. package/src/execution/__tests__/config.test.ts +1 -1
  39. package/src/execution/__tests__/delivery-methods.test.ts +179 -237
  40. package/src/execution/__tests__/execute-and-await-worktree.test.ts +0 -2
  41. package/src/execution/__tests__/execute-nesting.test.ts +20 -13
  42. package/src/execution/__tests__/explicit-agent-ref-guard.test.ts +21 -34
  43. package/src/execution/__tests__/finalized-marker.test.ts +1 -1
  44. package/src/execution/__tests__/gc-timer.test.ts +8 -23
  45. package/src/execution/__tests__/get-record-for-action-restart.test.ts +20 -10
  46. package/src/execution/__tests__/helpers/fake-engine-port.ts +187 -0
  47. package/src/execution/__tests__/helpers/spawn-mock.ts +36 -9
  48. package/src/execution/__tests__/helpers/subagent-service-mocks.ts +1 -1
  49. package/src/execution/__tests__/idle-gc.test.ts +200 -0
  50. package/src/execution/__tests__/lifecycle-predicates.test.ts +26 -21
  51. package/src/execution/__tests__/manifest-store-tmp-recovery.test.ts +1 -1
  52. package/src/execution/__tests__/nested-visibility-env-propagation.test.ts +5 -2
  53. package/src/execution/__tests__/notify-batch.test.ts +500 -0
  54. package/src/execution/__tests__/notify-ledger.test.ts +27 -0
  55. package/src/execution/__tests__/reconcile-sweep.test.ts +200 -0
  56. package/src/execution/__tests__/record-entry-collect.test.ts +102 -0
  57. package/src/execution/__tests__/record-store.test.ts +60 -6
  58. package/src/execution/__tests__/recursive-visibility-baseline.test.ts +20 -13
  59. package/src/execution/__tests__/relay-env.test.ts +12 -1
  60. package/src/execution/__tests__/round-supervisor.test.ts +293 -0
  61. package/src/execution/__tests__/session-file-gc.test.ts +1 -1
  62. package/src/execution/__tests__/session-pending.test.ts +119 -17
  63. package/src/execution/__tests__/start-collect-guard.test.ts +255 -0
  64. package/src/execution/__tests__/stream-sink-retirement.test.ts +10 -10
  65. package/src/execution/__tests__/subagent-actions-core.test.ts +6 -0
  66. package/src/execution/__tests__/subagent-service-multiproc-guard.test.ts +14 -11
  67. package/src/execution/__tests__/subagent-service-notify-gate.test.ts +33 -30
  68. package/src/execution/__tests__/subagent-service-parent-guard.test.ts +2 -9
  69. package/src/execution/__tests__/subagent-service-recovery-bounds.test.ts +59 -129
  70. package/src/execution/__tests__/subagent-service.test.ts +2 -2
  71. package/src/execution/__tests__/subprocess-agent-runner-routing.test.ts +109 -7
  72. package/src/execution/__tests__/subprocess-agent-runner.test.ts +108 -36
  73. package/src/execution/__tests__/sync-collect-recovery.test.ts +1102 -0
  74. package/src/execution/__tests__/tombstone-store.test.ts +1 -1
  75. package/src/execution/__tests__/workflow-state-root.test.ts +232 -0
  76. package/src/execution/__tests__/worktree-git-ops.test.ts +1 -1
  77. package/src/execution/__tests__/worktree-reconcile-aging.test.ts +2 -2
  78. package/src/execution/__tests__/worktree-reconcile.integration.test.ts +1 -1
  79. package/src/execution/__tests__/worktree-registry.test.ts +1 -1
  80. package/src/execution/agent-registry.ts +6 -2
  81. package/src/execution/collect-coordinator.ts +200 -0
  82. package/src/execution/config.ts +77 -5
  83. package/src/execution/dialog-queue.ts +6 -73
  84. package/src/execution/engine/__tests__/common/capability-gate.test.ts +115 -15
  85. package/src/execution/engine/__tests__/common/errors.test.ts +11 -18
  86. package/src/execution/engine/__tests__/common/event-journal.test.ts +1 -1
  87. package/src/execution/engine/__tests__/common/kill-chain.test.ts +7 -0
  88. package/src/execution/engine/__tests__/common/nesting-guard.test.ts +9 -3
  89. package/src/execution/engine/__tests__/common/pool-manager.test.ts +2 -2
  90. package/src/execution/engine/__tests__/common/session-view-service.test.ts +90 -3
  91. package/src/execution/engine/__tests__/conformance/H9-test-disposition.md +116 -0
  92. package/src/execution/engine/__tests__/conformance/__fixtures__/engine-protocol/fake-engine-protocol.mjs +377 -0
  93. package/src/execution/engine/__tests__/conformance/__fixtures__/engine-protocol/recorded.fixture.json +331 -0
  94. package/src/execution/engine/__tests__/conformance/__fixtures__/engine-protocol/smoke-run.fixture.json +213 -0
  95. package/src/execution/engine/__tests__/conformance/chat-round-protocol.test.ts +230 -0
  96. package/src/execution/engine/__tests__/conformance/contract.abort.test.ts +69 -42
  97. package/src/execution/engine/__tests__/conformance/contract.agent-events.test.ts +4 -81
  98. package/src/execution/engine/__tests__/conformance/contract.probe.test.ts +67 -44
  99. package/src/execution/engine/__tests__/conformance/contract.read-degradation.test.ts +82 -55
  100. package/src/execution/engine/__tests__/conformance/engine-conformance.live.test.ts +133 -70
  101. package/src/execution/engine/__tests__/conformance/engine-crash.test.ts +143 -0
  102. package/src/execution/engine/__tests__/conformance/fake-engine-capabilities.ts +19 -0
  103. package/src/execution/engine/__tests__/conformance/golden-replay.pi.test.ts +8 -3
  104. package/src/execution/engine/__tests__/conformance/golden-replay.zcode.test.ts +6 -5
  105. package/src/execution/engine/__tests__/conformance/protocol-blackbox.test.ts +398 -0
  106. package/src/execution/engine/__tests__/conformance/registry-fork-filter.test.ts +221 -0
  107. package/src/execution/engine/__tests__/conformance/round-liveness-supervisor.test.ts +302 -0
  108. package/src/execution/engine/__tests__/conformance/static-spawn-guard.test.ts +58 -0
  109. package/src/execution/engine/__tests__/engine-discovery-scan.test.ts +558 -0
  110. package/src/execution/engine/__tests__/host-task-spec.test.ts +45 -0
  111. package/src/execution/engine/__tests__/model-ref-split.test.ts +74 -0
  112. package/src/execution/engine/__tests__/registry.test.ts +206 -1
  113. package/src/execution/engine/__tests__/routing.test.ts +97 -0
  114. package/src/execution/engine/__tests__/run-failure-worktree-cleanup.test.ts +97 -0
  115. package/src/execution/engine/client/__tests__/__fixtures__/fake-engine.mjs +294 -0
  116. package/src/execution/engine/client/__tests__/engine-client.test.ts +516 -0
  117. package/src/execution/engine/client/__tests__/mirror.test.ts +67 -0
  118. package/src/execution/engine/client/__tests__/pid-file.test.ts +257 -0
  119. package/src/execution/engine/client/__tests__/protocol-closure.test.ts +95 -0
  120. package/src/execution/engine/client/__tests__/remote-engine.test.ts +457 -0
  121. package/src/execution/engine/client/client-options.ts +103 -0
  122. package/src/execution/engine/client/engine-client.ts +687 -0
  123. package/src/execution/engine/client/mirror.ts +167 -0
  124. package/src/execution/engine/client/pid-file.ts +392 -0
  125. package/src/execution/engine/client/reaper.ts +95 -0
  126. package/src/execution/engine/client/remote-engine.ts +441 -0
  127. package/src/execution/engine/client/reverse-router.ts +255 -0
  128. package/src/execution/engine/common/capability-gate.ts +109 -22
  129. package/src/execution/engine/common/errors.ts +9 -25
  130. package/src/execution/engine/common/event-journal.ts +4 -50
  131. package/src/execution/engine/common/journal-replay.ts +3 -1
  132. package/src/execution/engine/common/kill-chain.ts +18 -251
  133. package/src/execution/engine/common/nesting-guard.ts +18 -114
  134. package/src/execution/engine/common/pool-manager.ts +4 -0
  135. package/src/execution/engine/common/session-view-service.ts +56 -84
  136. package/src/execution/engine/config.ts +151 -0
  137. package/src/execution/engine/d8-compat.ts +346 -0
  138. package/src/execution/engine/engine-discovery-roots.ts +107 -0
  139. package/src/execution/engine/engine-discovery-scan.ts +269 -0
  140. package/src/execution/engine/engine-discovery.ts +30 -7
  141. package/src/execution/engine/engine-inspect-package.ts +282 -0
  142. package/src/execution/engine/engine-manifest.ts +244 -0
  143. package/src/execution/engine/host/__tests__/host-askuser-endpoint.test.ts +123 -0
  144. package/src/execution/engine/host/__tests__/host-bridge.test.ts +135 -0
  145. package/src/execution/engine/host/__tests__/spawned-children.test.ts +73 -0
  146. package/src/execution/engine/host/host-bridge.ts +150 -0
  147. package/src/execution/engine/host/host-ui-endpoint.ts +47 -0
  148. package/src/execution/engine/host/pi-host-binding.ts +91 -0
  149. package/src/execution/engine/host/spawned-children.ts +139 -0
  150. package/src/execution/engine/host-task-spec.ts +29 -2
  151. package/src/execution/engine/model-validation.ts +55 -2
  152. package/src/execution/engine/paths.ts +16 -41
  153. package/src/execution/engine/port.ts +48 -2
  154. package/src/execution/engine/registry.ts +153 -34
  155. package/src/execution/engine/routing.ts +225 -52
  156. package/src/execution/engine/types.ts +25 -200
  157. package/src/execution/execution-record.ts +7 -0
  158. package/src/execution/finalize-record.ts +40 -11
  159. package/src/execution/idle-gc.ts +89 -14
  160. package/src/execution/lifecycle-manager.ts +10 -7
  161. package/src/execution/lifecycle-predicates.ts +10 -8
  162. package/src/execution/notifier.ts +234 -25
  163. package/src/execution/notify-host.ts +21 -1
  164. package/src/execution/record-entry.ts +16 -0
  165. package/src/execution/record-store.ts +189 -31
  166. package/src/execution/relay-env.ts +19 -36
  167. package/src/execution/round-supervisor/domain.ts +89 -0
  168. package/src/execution/round-supervisor/index.ts +33 -0
  169. package/src/execution/round-supervisor/notify-accounting.ts +87 -0
  170. package/src/execution/round-supervisor/reconcile-sweep.ts +235 -0
  171. package/src/execution/round-supervisor/service-binding.test.ts +661 -0
  172. package/src/execution/round-supervisor/service-binding.ts +273 -0
  173. package/src/execution/round-supervisor/supervisor.ts +426 -0
  174. package/src/execution/session-pending.ts +68 -74
  175. package/src/execution/settled-watchdog.ts +39 -0
  176. package/src/execution/subagent-actions-core.ts +62 -6
  177. package/src/execution/subagent-service.ts +1161 -406
  178. package/src/execution/subprocess-agent-runner.ts +16 -8
  179. package/src/execution/sync-rebuild.ts +87 -0
  180. package/src/execution/types.ts +104 -117
  181. package/src/execution/ui-channels.ts +13 -214
  182. package/src/execution/ui-request-handler-factory.ts +7 -4
  183. package/src/execution/ui-request-observability.ts +2 -2
  184. package/src/execution/workflow-state-root.ts +67 -0
  185. package/src/execution/worktree-git-ops.ts +5 -0
  186. package/src/execution/worktree-manager.ts +11 -1
  187. package/src/index.ts +55 -26
  188. package/src/orchestration/__tests__/config-loader.test.ts +1 -1
  189. package/src/orchestration/__tests__/execute-agent-call.test.ts +1 -1
  190. package/src/orchestration/__tests__/file-run-store-prune.test.ts +1 -1
  191. package/src/orchestration/__tests__/file-run-store-throttle.test.ts +1 -1
  192. package/src/orchestration/__tests__/review-fix-loop-scriptpath-failfast.test.ts +1 -1
  193. package/src/orchestration/__tests__/script-generate.test.ts +5 -5
  194. package/src/orchestration/__tests__/skill-discovery.test.ts +2 -2
  195. package/src/orchestration/__tests__/workflow-files.test.ts +10 -10
  196. package/src/orchestration/__tests__/workflow-script-registry-impl.test.ts +1 -1
  197. package/src/orchestration/execute-agent-call.ts +1 -1
  198. package/src/orchestration/file-run-store.ts +144 -59
  199. package/src/orchestration/models/types.ts +43 -42
  200. package/src/shared/__tests__/atomic-write.test.ts +1 -1
  201. package/src/shared/__tests__/resource-discovery-host-roots.test.ts +5 -5
  202. package/src/shared/zcode-model-ref.ts +31 -0
  203. package/dist/chunk-43ONBFZX.js +0 -240
  204. package/dist/chunk-A4IVZWVX.js +0 -31
  205. package/dist/chunk-APZY4IME.js +0 -27
  206. package/dist/chunk-V3VHZ2VX.js +0 -59
  207. package/dist/execution/engine/engines/zcode/constants.cjs +0 -107
  208. package/dist/execution/engine/engines/zcode/constants.d.cts +0 -135
  209. package/dist/execution/engine/engines/zcode/constants.d.ts +0 -135
  210. package/dist/execution/engine/engines/zcode/constants.js +0 -54
  211. package/dist/execution/engine/engines/zcode/reader.cjs +0 -276
  212. package/dist/execution/engine/engines/zcode/reader.d.cts +0 -21
  213. package/dist/execution/engine/engines/zcode/reader.d.ts +0 -21
  214. package/dist/execution/engine/engines/zcode/reader.js +0 -9
  215. package/src/__tests__/append-system-prompt-assembly.test.ts +0 -243
  216. package/src/__tests__/fr4-get-state-handshake.test.ts +0 -125
  217. package/src/__tests__/session-runner.test.ts +0 -69
  218. package/src/execution/__tests__/ask-user-transit-e2e.test.ts +0 -490
  219. package/src/execution/__tests__/chatmode-first-round-closure-spawn.test.ts +0 -191
  220. package/src/execution/__tests__/descendant-sweep-guards.test.ts +0 -220
  221. package/src/execution/__tests__/descendant-sweep.test.ts +0 -269
  222. package/src/execution/__tests__/engine-model-validation.test.ts +0 -741
  223. package/src/execution/__tests__/epipe-fallback.test.ts +0 -248
  224. package/src/execution/__tests__/execution-runtime-face.test.ts +0 -272
  225. package/src/execution/__tests__/get-state-handshake.test.ts +0 -127
  226. package/src/execution/__tests__/helpers/session-runner-mocks.ts +0 -113
  227. package/src/execution/__tests__/keep-alive-no-progress.test.ts +0 -280
  228. package/src/execution/__tests__/kill-all-escalation.test.ts +0 -196
  229. package/src/execution/__tests__/max-turns-to-watchdog-ms.test.ts +0 -113
  230. package/src/execution/__tests__/output-collector.test.ts +0 -469
  231. package/src/execution/__tests__/pi-invocation.test.ts +0 -134
  232. package/src/execution/__tests__/recursive-visibility-env.test.ts +0 -149
  233. package/src/execution/__tests__/rpc-mode.test.ts +0 -89
  234. package/src/execution/__tests__/run-and-finalize-anchoring.test.ts +0 -387
  235. package/src/execution/__tests__/run-and-finalize-chatmode.test.ts +0 -277
  236. package/src/execution/__tests__/run-spawn-chatmode-settled.test.ts +0 -254
  237. package/src/execution/__tests__/run-spawn-edges.test.ts +0 -1079
  238. package/src/execution/__tests__/run-spawn-integration.test.ts +0 -933
  239. package/src/execution/__tests__/run-spawn-resume.test.ts +0 -322
  240. package/src/execution/__tests__/run-spawn-rpc-mode.test.ts +0 -196
  241. package/src/execution/__tests__/run-spawn-stdout-callback-throw.test.ts +0 -207
  242. package/src/execution/__tests__/service-kill-escalation.test.ts +0 -91
  243. package/src/execution/__tests__/session-runner-branch-cache-lru.test.ts +0 -110
  244. package/src/execution/__tests__/session-runner-close-prune.test.ts +0 -181
  245. package/src/execution/__tests__/session-runner-dispatch.test.ts +0 -489
  246. package/src/execution/__tests__/session-runner-epipe.test.ts +0 -179
  247. package/src/execution/__tests__/session-runner-heartbeat-idle-fallback.test.ts +0 -227
  248. package/src/execution/__tests__/session-runner-lifecycle-helpers.test.ts +0 -290
  249. package/src/execution/__tests__/session-runner-schema-env.test.ts +0 -352
  250. package/src/execution/__tests__/settled-watchdog.test.ts +0 -524
  251. package/src/execution/__tests__/spawn-args.test.ts +0 -446
  252. package/src/execution/__tests__/spawn-event-adapter-rpc.test.ts +0 -192
  253. package/src/execution/__tests__/spawn-event-adapter.test.ts +0 -167
  254. package/src/execution/__tests__/spawn-worktree-guidance.test.ts +0 -208
  255. package/src/execution/__tests__/spawned-children.test.ts +0 -236
  256. package/src/execution/__tests__/start-sync-model-guard.test.ts +0 -150
  257. package/src/execution/__tests__/stdin-writer.test.ts +0 -464
  258. package/src/execution/__tests__/subagent-service-message-close.test.ts +0 -693
  259. package/src/execution/__tests__/temp-prompt.test.ts +0 -53
  260. package/src/execution/__tests__/timeout-integration.test.ts +0 -616
  261. package/src/execution/__tests__/turn-limiter-semantics.test.ts +0 -194
  262. package/src/execution/__tests__/turn-limiter.test.ts +0 -65
  263. package/src/execution/__tests__/ui-request-handler.test.ts +0 -205
  264. package/src/execution/__tests__/ui-request-queue.test.ts +0 -298
  265. package/src/execution/__tests__/worktree-pid-registration.integration.test.ts +0 -230
  266. package/src/execution/engine/__tests__/common/schema-emulation.test.ts +0 -128
  267. package/src/execution/engine/__tests__/common/session-view-service-zcode-dbpath.test.ts +0 -149
  268. package/src/execution/engine/__tests__/conformance/__fixtures__/pi-golden-events.json +0 -28
  269. package/src/execution/engine/__tests__/conformance/zcode-appserver-harness.ts +0 -128
  270. package/src/execution/engine/__tests__/paths.test.ts +0 -39
  271. package/src/execution/engine/common/schema-emulation.ts +0 -189
  272. package/src/execution/engine/common/session-view-projection.ts +0 -51
  273. package/src/execution/engine/engines/pi/__tests__/pi-engine.test.ts +0 -615
  274. package/src/execution/engine/engines/pi/__tests__/reader.test.ts +0 -155
  275. package/src/execution/engine/engines/pi/__tests__/spawn-opts-direct.test.ts +0 -257
  276. package/src/execution/engine/engines/pi/argv-mirror.ts +0 -135
  277. package/src/execution/engine/engines/pi/get-state-handshake.ts +0 -179
  278. package/src/execution/engine/engines/pi/output-collector.ts +0 -317
  279. package/src/execution/engine/engines/pi/pi-engine.ts +0 -811
  280. package/src/execution/engine/engines/pi/pi-invocation.ts +0 -120
  281. package/src/execution/engine/engines/pi/reader.ts +0 -59
  282. package/src/execution/engine/engines/pi/registration.ts +0 -37
  283. package/src/execution/engine/engines/pi/session-runner.ts +0 -2840
  284. package/src/execution/engine/engines/pi/spawn-event-adapter.ts +0 -404
  285. package/src/execution/engine/engines/pi/stdin-writer.ts +0 -199
  286. package/src/execution/engine/engines/pi/temp-prompt.ts +0 -62
  287. package/src/execution/engine/engines/pi/turn-limiter.ts +0 -102
  288. package/src/execution/engine/engines/zcode/__tests__/__fixtures__/fake-appserver.mjs +0 -301
  289. package/src/execution/engine/engines/zcode/__tests__/__fixtures__/zcode-golden-appserver.json +0 -36
  290. package/src/execution/engine/engines/zcode/__tests__/appserver-launcher.test.ts +0 -321
  291. package/src/execution/engine/engines/zcode/__tests__/connection.test.ts +0 -473
  292. package/src/execution/engine/engines/zcode/__tests__/parser.test.ts +0 -43
  293. package/src/execution/engine/engines/zcode/__tests__/preparer.test.ts +0 -124
  294. package/src/execution/engine/engines/zcode/__tests__/reader.test.ts +0 -210
  295. package/src/execution/engine/engines/zcode/__tests__/registration.test.ts +0 -71
  296. package/src/execution/engine/engines/zcode/__tests__/session-channel-dispose-harvest.test.ts +0 -247
  297. package/src/execution/engine/engines/zcode/__tests__/session-channel-turn-timers.test.ts +0 -451
  298. package/src/execution/engine/engines/zcode/__tests__/session-channel.test.ts +0 -792
  299. package/src/execution/engine/engines/zcode/__tests__/zcode-engine-appserver.test.ts +0 -782
  300. package/src/execution/engine/engines/zcode/__tests__/zcode-engine-degrade.test.ts +0 -168
  301. package/src/execution/engine/engines/zcode/__tests__/zcode-engine-dispose.test.ts +0 -210
  302. package/src/execution/engine/engines/zcode/__tests__/zcode-engine-retry.test.ts +0 -353
  303. package/src/execution/engine/engines/zcode/__tests__/zcode-engine-status.test.ts +0 -282
  304. package/src/execution/engine/engines/zcode/__tests__/zcode-engine-timeout.test.ts +0 -372
  305. package/src/execution/engine/engines/zcode/__tests__/zcode-engine.live.test.ts +0 -145
  306. package/src/execution/engine/engines/zcode/appserver-launcher.ts +0 -167
  307. package/src/execution/engine/engines/zcode/connection.ts +0 -598
  308. package/src/execution/engine/engines/zcode/constants.ts +0 -176
  309. package/src/execution/engine/engines/zcode/golden-sample.ts +0 -47
  310. package/src/execution/engine/engines/zcode/parser.ts +0 -102
  311. package/src/execution/engine/engines/zcode/preparer.ts +0 -235
  312. package/src/execution/engine/engines/zcode/reader.ts +0 -381
  313. package/src/execution/engine/engines/zcode/registration.ts +0 -37
  314. package/src/execution/engine/engines/zcode/session-channel.ts +0 -968
  315. package/src/execution/engine/engines/zcode/zcode-engine.ts +0 -1431
  316. package/src/execution/round-settlement.ts +0 -93
  317. package/src/execution/ui-request-queue.ts +0 -215
  318. package/src/shared/schema-env.ts +0 -44
@@ -1,615 +0,0 @@
1
- // src/execution/engine/engines/pi/__tests__/pi-engine.test.ts
2
- //
3
- // PiEngine 引擎边界测试(D2 后形态):capabilities 链路接通口径(D3)、run 双分支
4
- //(chat ticket 交接 / workflow executeAndAwait 委托)与中立声明→ExecuteOptions 映射、
5
- // interact message 原生协议(stdin 直写 + 冷路径续轮回调;D2 协议知识下沉)、close/cancel
6
- // 直通、read 三级降级、probe 形状(C1)。fake PiEngineService 注入,不 spawn 真进程。
7
-
8
- import * as fs from "node:fs";
9
- import * as os from "node:os";
10
- import * as path from "node:path";
11
-
12
- import type { ChildProcess } from "node:child_process";
13
- import { PassThrough } from "node:stream";
14
-
15
- import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
16
-
17
- import { createRecord } from "../../../../execution-record.ts";
18
- import type { ExecuteOptions, SubagentRecord } from "../../../../types.ts";
19
- import { IDENTITY_CUSTOM_TYPE } from "../../../../session-reconstructor.ts";
20
- import { SubagentStream } from "../../../../stream-sink.ts";
21
- import type { RunContext } from "../../../port.ts";
22
- import type { AgentEvent, EngineHandle } from "../../../types.ts";
23
- import type { AgentCallOpts } from "../../../../../orchestration/models/types.ts";
24
- import { spawnedChildren } from "../session-runner.ts";
25
- import { resetAllEpipeFailures } from "../stdin-writer.ts";
26
- import {
27
- PI_ADAPTER_VERSION,
28
- PI_ENGINE_ID,
29
- PiEngine,
30
- type ChatRoundTicket,
31
- type PiEngineService,
32
- } from "../pi-engine.ts";
33
-
34
- // ── fake 编排服务(结构子集,记录调用) ──
35
-
36
- interface ServiceCalls {
37
- executeOpts?: ExecuteOptions;
38
- signal?: AbortSignal;
39
- onEvent?: (event: AgentEvent) => void;
40
- stream?: unknown;
41
- resumeChatRound?: { recordId: string; text: string };
42
- close?: { recordId: string; force: boolean };
43
- cancelId?: string;
44
- getRecordId?: string;
45
- reportedTransitionIds?: string[];
46
- }
47
-
48
- /** 构造最小完整 SubagentRecord(collectRecords 返回项——resolveRecordId 只读 id/sessionFile)。 */
49
- function makeListedRecord(id: string, sessionFile: string | undefined): SubagentRecord {
50
- return {
51
- id,
52
- agent: "reviewer",
53
- task: "t",
54
- slug: "s",
55
- status: "running",
56
- mode: "background",
57
- startedAt: 1,
58
- rootSessionId: undefined,
59
- parentRecordId: undefined,
60
- depth: 0,
61
- endedAt: undefined,
62
- turns: 0,
63
- totalTokens: 0,
64
- model: "p/m",
65
- thinkingLevel: undefined,
66
- eventLog: [],
67
- displayItems: [],
68
- ...(sessionFile !== undefined ? { sessionFile } : {}),
69
- };
70
- }
71
-
72
- function makeFakeService(overrides?: {
73
- executeResult?: Awaited<ReturnType<PiEngineService["executeAndAwait"]>>;
74
- executeReject?: Error;
75
- recordsById?: Map<string, ReturnType<typeof createRecord>>;
76
- listed?: Array<{ id: string; sessionFile?: string }>;
77
- cancelResult?: boolean;
78
- resumeReject?: Error;
79
- }): { service: PiEngineService; calls: ServiceCalls } {
80
- const calls: ServiceCalls = { reportedTransitionIds: [] };
81
- const record = createRecord("sa-fake", {
82
- agent: "reviewer",
83
- model: "p/m",
84
- thinkingLevel: "high",
85
- mode: "background",
86
- task: "t",
87
- slug: "s",
88
- startedAt: 1,
89
- controller: new AbortController(),
90
- });
91
- const service: PiEngineService = {
92
- executeAndAwait: async (opts, signal, onEvent, stream) => {
93
- calls.executeOpts = opts;
94
- calls.signal = signal;
95
- calls.onEvent = onEvent;
96
- calls.stream = stream;
97
- if (overrides?.executeReject) throw overrides.executeReject;
98
- return (
99
- overrides?.executeResult ?? {
100
- content: "done",
101
- durationMs: 42,
102
- toolCalls: [],
103
- }
104
- );
105
- },
106
- getRecordForAction: (id) => {
107
- calls.getRecordId = id;
108
- // recordsById 提供时严格查表(模拟「record 不存在 → throw」路径);缺省返回默认 record
109
- if (overrides?.recordsById) {
110
- const found = overrides.recordsById.get(id);
111
- if (!found) {
112
- throw new Error(`subagent not found or not owned: ${id}`);
113
- }
114
- return found;
115
- }
116
- return record;
117
- },
118
- resumeChatRound: (rec, text) => {
119
- calls.resumeChatRound = { recordId: rec.id, text };
120
- if (overrides?.resumeReject) throw overrides.resumeReject;
121
- },
122
- reportRecordTransition: (rec) => {
123
- calls.reportedTransitionIds?.push(rec.id);
124
- },
125
- closeSubagent: async (rec, force) => {
126
- calls.close = { recordId: rec.id, force };
127
- },
128
- cancel: (id) => {
129
- calls.cancelId = id;
130
- return overrides?.cancelResult ?? true;
131
- },
132
- collectRecords: () =>
133
- (overrides?.listed ?? []).map(({ id, sessionFile }) => makeListedRecord(id, sessionFile)),
134
- };
135
- return { service, calls };
136
- }
137
-
138
- /** 全字段任务声明(D6 合流形状 AgentCallOpts,覆盖全部直出映射点)。 */
139
- function makeSpec(): AgentCallOpts {
140
- return {
141
- prompt: "do the thing",
142
- description: "do-thing",
143
- agent: "reviewer",
144
- model: "zai-coding-cn/glm-5.2",
145
- thinkingLevel: "high",
146
- skillPath: "/skills/r/SKILL.md",
147
- appendSystemPrompt: ["extra"],
148
- schema: { type: "object", properties: { ok: { type: "boolean" } } },
149
- maxTurns: 5,
150
- graceTurns: 2,
151
- fork: true,
152
- worktree: false,
153
- cwd: "/work",
154
- conversation: true,
155
- idleTimeoutMs: 99,
156
- };
157
- }
158
-
159
- function makeHandle(sessionRef: Record<string, string>): EngineHandle {
160
- return { data: { v: 1, engineId: PI_ENGINE_ID, sessionRef, poolKey: "shared", adapterVersion: PI_ADAPTER_VERSION } };
161
- }
162
-
163
- function makeRunCtx(overrides?: Partial<RunContext>): RunContext {
164
- return { taskId: "sa-test", poolKey: "shared", ...overrides };
165
- }
166
-
167
- // ── capabilities ──
168
-
169
- describe("PiEngine.capabilities(D3 链路接通口径)", () => {
170
- it("id = 'pi';声明本仓 subagent 链路实际接通的能力(非 RPC 理论能力)", () => {
171
- const engine = new PiEngine({ getService: () => null });
172
- expect(engine.id).toBe(PI_ENGINE_ID);
173
- expect(engine.capabilities()).toEqual({
174
- schemaEnforcement: "native", // PI_WORKFLOW_SCHEMA env 注入链路(方案 A 唯一权威)
175
- steer: "unsupported", // RPC 有 steer 但 spawn 链路未接通(session-runner no-op)
176
- conversation: "native", // chatMode idle 复用 + message/close/cancel 已接通
177
- personaInjection: "flag", // --skill / --append-system-prompt
178
- eventGranularity: "stream", // 30+ 事件流
179
- sandbox: "emulated", // 无 OS sandbox;worktree 隔离
180
- sessionRead: "full", // pi JSONL 完整重建
181
- resume: "native", // 同进程 idle 复用 + --session 冷续写
182
- interrupt: "kill-only", // 现链路 abort = SIGTERM,rpc abort 未接通
183
- permissionMode: "native", // argv-mirror 镜像主进程 flag
184
- maxTurns: true, // [D3-④] turn limiter 兑现
185
- });
186
- });
187
- });
188
-
189
- // ── run ──
190
-
191
- describe("PiEngine.run", () => {
192
- it("[D6 一次直出] 合流声明 AgentCallOpts → ExecuteOptions + signal/onEvent/stream/ctxModel 直通(行为零变化)", async () => {
193
- const { service, calls } = makeFakeService();
194
- const engine = new PiEngine({ getService: () => service });
195
- const spec = makeSpec();
196
- const ctxModel = { id: "m", name: "M", provider: "p", reasoning: false };
197
- const onEvent = (e: unknown) => void e;
198
- const stream = new SubagentStream("sa-test", { setWidget: () => {} });
199
- const signal = new AbortController().signal;
200
-
201
- const { handle, outcome } = await engine.run(spec, makeRunCtx({ signal, onEvent, ctxModel, stream }));
202
-
203
- // 传参恒等(SAR 接线前的直接调用形态)
204
- expect(calls.signal).toBe(signal);
205
- expect(calls.onEvent).toBe(onEvent);
206
- expect(calls.stream).toBe(stream);
207
- expect(calls.executeOpts).toEqual({
208
- task: "do the thing",
209
- slug: "do-thing",
210
- agent: "reviewer",
211
- model: "zai-coding-cn/glm-5.2",
212
- thinkingLevel: "high", // effort 恒等映射回
213
- skillPath: "/skills/r/SKILL.md", // persona 还原
214
- appendSystemPrompt: ["extra"],
215
- schema: spec.schema,
216
- schemaEnv: JSON.stringify(spec.schema), // schema 派生(D-A6 bridge)
217
- maxTurns: 5,
218
- graceTurns: 2,
219
- ctxModel,
220
- fork: true,
221
- worktree: false,
222
- cwd: "/work",
223
- conversation: true,
224
- idleTimeoutMs: 99,
225
- engine: "pi", // P4 引擎留痕(D9①):实际执行引擎 id 进 record 投影链
226
- });
227
-
228
- // outcome 字段全集映射(缺字段在此转红——SAR outcomeToRunnerResult 的完整性依赖)
229
- expect(outcome).toEqual({
230
- content: "done",
231
- parsedOutput: undefined,
232
- usage: undefined,
233
- durationMs: 42,
234
- error: undefined,
235
- sessionId: undefined,
236
- sessionFile: undefined,
237
- worktreePath: undefined,
238
- toolCalls: [],
239
- engineId: "pi",
240
- });
241
-
242
- // handle 自描述(engineId + sessionRef + poolKey + adapter 版本)
243
- expect(handle.data).toMatchObject({
244
- v: 1,
245
- engineId: "pi",
246
- poolKey: "shared",
247
- adapterVersion: PI_ADAPTER_VERSION,
248
- });
249
- });
250
-
251
- it("sessionFile 回填进 handle.sessionRef;ctx.poolKey 透传(空串兜底 'shared')", async () => {
252
- const { service } = makeFakeService({
253
- executeResult: { content: "x", sessionFile: "/tmp/s.jsonl", sessionId: "sess-1", toolCalls: [] },
254
- });
255
- const engine = new PiEngine({ getService: () => service });
256
- const r1 = await engine.run({ prompt: "t" }, makeRunCtx({ poolKey: "agent-reviewer" }));
257
- expect(r1.handle.data.sessionRef).toEqual({ sessionFile: "/tmp/s.jsonl" });
258
- expect(r1.handle.data.poolKey).toBe("agent-reviewer");
259
- expect(r1.outcome.sessionId).toBe("sess-1");
260
- const r2 = await engine.run({ prompt: "t" }, makeRunCtx({ poolKey: "" }));
261
- expect(r2.handle.data.poolKey).toBe("shared");
262
- });
263
-
264
- it("[D2 chat 分支] ctx.taskId 命中交接包 → runChatRound 执行 + handle/outcome 形态(chat 编排不消费,形态完备)", async () => {
265
- const ticketCalls: string[] = [];
266
- const record = createRecord("sa-chat-ticket", {
267
- agent: "reviewer", model: "p/m", thinkingLevel: "high", mode: "background",
268
- task: "t", slug: "s", startedAt: 1, controller: new AbortController(),
269
- });
270
- const chatTicket: ChatRoundTicket = {
271
- record,
272
- opts: { task: "chat task", slug: "s" },
273
- identity: { agent: "reviewer", agentConfig: undefined, resolved: { model: { id: "m", name: "M", provider: "p", reasoning: false }, thinkingLevel: "high" } },
274
- ctx: { cwd: "/w" } as ChatRoundTicket["ctx"],
275
- signal: undefined,
276
- priority: 1000,
277
- };
278
- const service: PiEngineService = {
279
- executeAndAwait: async () => { throw new Error("workflow branch must not run"); },
280
- getRecordForAction: () => { throw new Error("unused"); },
281
- closeSubagent: async () => {},
282
- cancel: () => true,
283
- collectRecords: () => [],
284
- takeChatRound: (taskId) => {
285
- ticketCalls.push(taskId);
286
- return taskId === "sa-chat-ticket" ? chatTicket : undefined;
287
- },
288
- runChatRound: async (t) => ({
289
- text: "chat done", turns: 2, durationMs: 7, success: true,
290
- sessionId: t.record.id, toolCalls: [],
291
- }),
292
- };
293
- const engine = new PiEngine({ getService: () => service });
294
- const { handle, outcome } = await engine.run({ prompt: "t" }, makeRunCtx({ taskId: "sa-chat-ticket" }));
295
- expect(ticketCalls).toEqual(["sa-chat-ticket"]);
296
- expect(handle.data.sessionRef).toEqual({ recordId: "sa-chat-ticket" });
297
- expect(handle.data.poolKey).toBe("shared");
298
- expect(outcome).toMatchObject({ content: "chat done", engineId: "pi", sessionId: "sa-chat-ticket" });
299
- // 非 chat taskId(workflow 域)→ 交接 miss → executeAndAwait 分支(此处 throw 证明未误入)
300
- await expect(engine.run({ prompt: "t" }, makeRunCtx({ taskId: "sa-workflow" }))).rejects.toThrow(
301
- "workflow branch must not run",
302
- );
303
- });
304
-
305
- it("[D6/u-3b 硬验收] chat pi fork-from 零回归:ticket.opts.forkFromSessionFile 经 run chat 分支 lossless 送达 runChatRound", async () => {
306
- // 链路(合流前后均不经任务声明):壳 fork-from action → ExecuteOptions.forkFromSessionFile
307
- // → kickOffChatRound 挂 ticket → 本分支 → runChatRound(ticket) → runAndFinalize →
308
- // runSpawn(fork-from 源优先于 fork 推导)。本用例锁定合流后 ticket 交接不丢该字段。
309
- const record = createRecord("sa-forkfrom", {
310
- agent: "reviewer", model: "p/m", thinkingLevel: "high", mode: "background",
311
- task: "t", slug: "s", startedAt: 1, controller: new AbortController(),
312
- });
313
- const ticketOpts: ExecuteOptions = { task: "continue", slug: "s", forkFromSessionFile: "/sessions/parent.jsonl" };
314
- const chatTicket: ChatRoundTicket = {
315
- record,
316
- opts: ticketOpts,
317
- identity: { agent: "reviewer", agentConfig: undefined, resolved: { model: { id: "m", name: "M", provider: "p", reasoning: false }, thinkingLevel: "high" } },
318
- ctx: { cwd: "/w" } as ChatRoundTicket["ctx"],
319
- signal: undefined,
320
- priority: 1000,
321
- };
322
- let received: ChatRoundTicket | undefined;
323
- const service: PiEngineService = {
324
- executeAndAwait: async () => { throw new Error("workflow branch must not run"); },
325
- getRecordForAction: () => { throw new Error("unused"); },
326
- closeSubagent: async () => {},
327
- cancel: () => true,
328
- collectRecords: () => [],
329
- takeChatRound: (taskId) => (taskId === "sa-forkfrom" ? chatTicket : undefined),
330
- runChatRound: async (t) => {
331
- received = t;
332
- return { content: "ok", text: "ok", sessionId: "sess-1", turns: 1, durationMs: 1, success: true, toolCalls: [] };
333
- },
334
- };
335
- const engine = new PiEngine({ getService: () => service });
336
- await engine.run({ prompt: "t" }, makeRunCtx({ taskId: "sa-forkfrom" }));
337
- // lossless 断言:runChatRound 收到同一 ticket 对象,opts 引用与 forkFromSessionFile 原值逐字节保持
338
- expect(received).toBe(chatTicket);
339
- expect(received?.opts).toBe(ticketOpts);
340
- expect(received?.opts.forkFromSessionFile).toBe("/sessions/parent.jsonl");
341
- });
342
-
343
- it("服务不可用(prepare 期)→ reject 且不产生 handle", async () => {
344
- const engine = new PiEngine({ getService: () => null });
345
- await expect(engine.run({ prompt: "t" }, makeRunCtx())).rejects.toThrow(/SubagentService unavailable/);
346
- });
347
-
348
- it("executeAndAwait throw(嵌套超限等创建期异常)→ 向上传播(SAR catch 兜底不变)", async () => {
349
- const { service } = makeFakeService({ executeReject: new Error("nesting depth exceeded") });
350
- const engine = new PiEngine({ getService: () => service });
351
- await expect(engine.run({ prompt: "t" }, makeRunCtx())).rejects.toThrow("nesting depth exceeded");
352
- });
353
- });
354
-
355
- // ── interact ──
356
-
357
- describe("PiEngine.interact(chatMode 交互面:message 原生协议 + close/cancel 直通)", () => {
358
- // [D2] message 分支原生实现(PiEngine.deliverPrompt):热路径经真实 session-runner
359
- // spawnedChildren Map + 真实 stdin-writer(不 mock——端到端验证协议字节)。
360
- let hotChildren: Map<string, unknown>;
361
- beforeEach(() => {
362
- hotChildren = spawnedChildren as unknown as Map<string, unknown>;
363
- hotChildren.clear();
364
- });
365
- afterEach(() => {
366
- hotChildren.clear();
367
- resetAllEpipeFailures();
368
- });
369
-
370
- function makeHotChild(): { child: ChildProcess; stdin: PassThrough } {
371
- // exitCode/signalCode 缺省 null([race-F5] 写后死检测的判据,undefined 会被误判已死)
372
- const stdin = new PassThrough();
373
- const child = { stdin, pid: 4242, exitCode: null, signalCode: null, killed: false } as unknown as ChildProcess;
374
- return { child, stdin };
375
- }
376
-
377
- function readStdinLines(stdin: PassThrough): Array<Record<string, unknown>> {
378
- stdin.pause();
379
- // PassThrough.read() 类型 any——显式标注 string,下游 split/map 参数可推导
380
- const text: string = stdin.read()?.toString() ?? "";
381
- return text
382
- .split("\n")
383
- .map((l) => l.trim())
384
- .filter((l) => l.length > 0)
385
- .map((l) => JSON.parse(l) as Record<string, unknown>);
386
- }
387
-
388
- it("message 热路径(进程活):sendPromptCommand 直写 stdin,interrupt → streamingBehavior(steer/followUp)+ record 迁移上报", async () => {
389
- const { service, calls } = makeFakeService();
390
- const engine = new PiEngine({ getService: () => service });
391
- const { child, stdin } = makeHotChild();
392
- hotChildren.set("sa-fake", child);
393
-
394
- const res = await engine.interact(makeHandle({ recordId: "sa-fake" }), { kind: "message", payload: "next round", interrupt: true });
395
- expect(res).toEqual({ ok: true, delivered: true });
396
-
397
- const lines = readStdinLines(stdin);
398
- expect(lines).toHaveLength(1);
399
- expect(lines[0]).toMatchObject({ type: "prompt", message: "next round", streamingBehavior: "steer" });
400
-
401
- // 无 interrupt → followUp;record 迁移经 reportRecordTransition 上报(轮始信号清除后)
402
- const { stdin: stdin2 } = makeHotChild();
403
- hotChildren.set("sa-fake", { stdin: stdin2, pid: 4243, exitCode: null, signalCode: null, killed: false } as unknown as ChildProcess);
404
- await engine.interact(makeHandle({ recordId: "sa-fake" }), { kind: "message", payload: "queued" });
405
- const lines2 = readStdinLines(stdin2);
406
- expect(lines2[0]).toMatchObject({ type: "prompt", message: "queued", streamingBehavior: "followUp" });
407
- expect(calls.reportedTransitionIds).toEqual(["sa-fake", "sa-fake"]);
408
- });
409
-
410
- it("message 冷路径(进程死):resumeChatRound 编排回调(重开 session 续聊)", async () => {
411
- const { service, calls } = makeFakeService();
412
- const engine = new PiEngine({ getService: () => service });
413
- // spawnedChildren 无该 record → getChildByRecord undefined → 冷路径
414
- const res = await engine.interact(makeHandle({ recordId: "sa-fake" }), { kind: "message", payload: "resume msg" });
415
- expect(res).toEqual({ ok: true, delivered: true });
416
- expect(calls.resumeChatRound).toEqual({ recordId: "sa-fake", text: "resume msg" });
417
- });
418
-
419
- it("close → closeSubagent(record, force);payload 缺省 force=false", async () => {
420
- const { service, calls } = makeFakeService();
421
- const engine = new PiEngine({ getService: () => service });
422
- await engine.interact(makeHandle({ recordId: "sa-fake" }), { kind: "close", payload: { force: true } });
423
- expect(calls.close).toEqual({ recordId: "sa-fake", force: true });
424
- await engine.interact(makeHandle({ recordId: "sa-fake" }), { kind: "close" });
425
- expect(calls.close).toEqual({ recordId: "sa-fake", force: false });
426
- });
427
-
428
- it("cancel → service.cancel(recordId)", async () => {
429
- const { service, calls } = makeFakeService();
430
- const engine = new PiEngine({ getService: () => service });
431
- const res = await engine.interact(makeHandle({ recordId: "sa-fake" }), { kind: "cancel" });
432
- expect(res).toEqual({ ok: true, delivered: true });
433
- expect(calls.cancelId).toBe("sa-fake");
434
- });
435
-
436
- it("cancel 返回 false → engine_interact_failed", async () => {
437
- const { service } = makeFakeService({ cancelResult: false });
438
- const engine = new PiEngine({ getService: () => service });
439
- const res = await engine.interact(makeHandle({ recordId: "sa-fake" }), { kind: "cancel" });
440
- expect(res.ok).toBe(false);
441
- if (!res.ok) {
442
- expect(res.code).toBe("engine_interact_failed");
443
- expect(res.message).toContain("sa-fake");
444
- }
445
- });
446
-
447
- it("recordId 缺席时按 sessionFile 扫描兜底定位(collectRecords 匹配)", async () => {
448
- const { service, calls } = makeFakeService({ listed: [{ id: "sa-scan-hit", sessionFile: "/x/s.jsonl" }] });
449
- const engine = new PiEngine({ getService: () => service });
450
- await engine.interact(makeHandle({ sessionFile: "/x/s.jsonl" }), { kind: "message", payload: "hi" });
451
- expect(calls.getRecordId).toBe("sa-scan-hit");
452
- });
453
-
454
- it("死 handle(无定位符 / record 不存在)→ engine_session_not_resumable(含 cold resume 指引)", async () => {
455
- const { service } = makeFakeService();
456
- const engine = new PiEngine({ getService: () => service });
457
- const res = await engine.interact(makeHandle({}), { kind: "message", payload: "hi" });
458
- expect(res.ok).toBe(false);
459
- if (!res.ok) {
460
- expect(res.code).toBe("engine_session_not_resumable");
461
- expect(res.message).toContain("--session");
462
- }
463
- });
464
-
465
- it("交互面 throw → engine_interact_failed(message 文案透传行动语言)", async () => {
466
- const { service } = makeFakeService({
467
- resumeReject: new Error("subagent sa-fake is not ready for a new message"),
468
- });
469
- const engine = new PiEngine({ getService: () => service });
470
- const res = await engine.interact(makeHandle({ recordId: "sa-fake" }), { kind: "message", payload: "hi" });
471
- expect(res).toEqual({
472
- ok: false,
473
- code: "engine_interact_failed",
474
- message: "subagent sa-fake is not ready for a new message",
475
- });
476
- });
477
-
478
- it("服务不可用 → reject", async () => {
479
- const engine = new PiEngine({ getService: () => null });
480
- await expect(
481
- engine.interact(makeHandle({ recordId: "x" }), { kind: "message", payload: "hi" }),
482
- ).rejects.toThrow(/SubagentService unavailable/);
483
- });
484
- });
485
-
486
- // ── read ──
487
-
488
- describe("PiEngine.read(D6 降级链)", () => {
489
- let tmpDir: string;
490
-
491
- beforeEach(() => {
492
- tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "pi-engine-read-"));
493
- });
494
- afterEach(() => {
495
- fs.rmSync(tmpDir, { recursive: true, force: true, maxRetries: 5, retryDelay: 20 });
496
- });
497
-
498
- it("无 sessionRef → outcome-only(turns 空)", async () => {
499
- const engine = new PiEngine({ getService: () => null });
500
- const view = await engine.read(makeHandle({}));
501
- expect(view).toEqual({ engineId: "pi", turns: [], source: "outcome-only" });
502
- });
503
-
504
- it("sessionFile 不存在/损坏 → outcome-only(不 throw)", async () => {
505
- const engine = new PiEngine({ getService: () => null });
506
- const view = await engine.read(makeHandle({ sessionFile: path.join(tmpDir, "missing.jsonl") }));
507
- expect(view.source).toBe("outcome-only");
508
- });
509
-
510
- it("pi JSONL 原生读取(第①级):turns 重建 + usage 聚合 + 内部态剥离", async () => {
511
- const file = path.join(tmpDir, "s.jsonl");
512
- const lines: string[] = [
513
- JSON.stringify({ type: "session", version: 3, id: "sess-uuid", timestamp: "2026-01-01T00:00:00.000Z", cwd: tmpDir }),
514
- JSON.stringify({
515
- type: "custom", id: "id-1", parentId: null, timestamp: "2026-01-01T00:00:00.000Z",
516
- customType: IDENTITY_CUSTOM_TYPE,
517
- data: { id: "bg-1", agent: "worker", mode: "background", task: "t", startedAt: 500 },
518
- }),
519
- JSON.stringify({
520
- type: "message", id: "msg-1", parentId: "id-1", timestamp: new Date(1000).toISOString(),
521
- message: {
522
- role: "assistant",
523
- content: [
524
- { type: "text", text: "hello" },
525
- { type: "toolCall", id: "tc-1", name: "bash", arguments: { cmd: "ls" } },
526
- ],
527
- usage: { input: 10, output: 20, cacheRead: 1, cacheWrite: 2, totalTokens: 33, cost: { total: 0.5 } },
528
- stopReason: "stop",
529
- timestamp: 1000,
530
- },
531
- }),
532
- JSON.stringify({
533
- type: "message", id: "tr-1", parentId: "msg-1", timestamp: new Date(2000).toISOString(),
534
- message: {
535
- role: "toolResult", toolCallId: "tc-1", toolName: "bash",
536
- content: [{ type: "text", text: "ok" }], timestamp: 2000,
537
- },
538
- }),
539
- ];
540
- fs.writeFileSync(file, lines.join("\n") + "\n");
541
-
542
- const engine = new PiEngine({ getService: () => null });
543
- const view = await engine.read(makeHandle({ sessionFile: file }));
544
- expect(view.source).toBe("native");
545
- expect(view.engineId).toBe("pi");
546
- expect(view.sessionId).toBe("bg-1");
547
- expect(view.turns).toHaveLength(1);
548
- expect(view.turns[0].text).toBe("hello");
549
- expect(view.turns[0].closed).toBe(true);
550
- // toolCall 纯净形状(无 _status/startedTs 内部态)
551
- expect(view.turns[0].toolCalls[0]).toMatchObject({ toolName: "bash" });
552
- expect("_status" in view.turns[0].toolCalls[0]).toBe(false);
553
- // usage 聚合(turn usageDelta → AgentUsageTotal)
554
- expect(view.usage).toEqual({ input: 10, output: 20, cacheRead: 1, cacheWrite: 2, cost: 0.5, total: 33 });
555
- });
556
- });
557
-
558
- // ── probe ──
559
-
560
- describe("PiEngine.probe(D7 + C1 形状)", () => {
561
- const originalArgv = process.argv;
562
- const originalExecPath = process.execPath;
563
- const originalPath = process.env.PATH;
564
-
565
- afterEach(() => {
566
- Object.defineProperty(process, "argv", { value: originalArgv, configurable: true });
567
- Object.defineProperty(process, "execPath", { value: originalExecPath, configurable: true });
568
- process.env.PATH = originalPath;
569
- });
570
-
571
- it("探针成功:invocation + version 两级 check,engineVersion 实测", async () => {
572
- const probeVersion = vi.fn(async () => "pi 0.84.1");
573
- const engine = new PiEngine({ getService: () => null, probeVersion });
574
- const report = await engine.probe();
575
- expect(report.ok).toBe(true);
576
- expect(report.engineVersion).toBe("pi 0.84.1");
577
- expect(report.checks.map((c) => c.name)).toEqual(["invocation", "version"]);
578
- expect(report.error).toBeUndefined();
579
- expect(probeVersion).toHaveBeenCalledTimes(1);
580
- });
581
-
582
- it("版本探测失败 → ok=false 且 error.recovery 非空(C1:恢复指引必填)", async () => {
583
- const engine = new PiEngine({ getService: () => null, probeVersion: async () => undefined });
584
- const report = await engine.probe();
585
- expect(report.ok).toBe(false);
586
- expect(report.error?.code).toBe("engine_probe_failed");
587
- expect(report.error?.recovery.length).toBeGreaterThan(0);
588
- });
589
-
590
- it("结果缓存:二次 probe 不重跑;force 强探重跑", async () => {
591
- const probeVersion = vi.fn(async () => "pi 1.0");
592
- const engine = new PiEngine({ getService: () => null, probeVersion });
593
- const first = await engine.probe();
594
- const second = await engine.probe();
595
- expect(second).toBe(first); // 缓存命中(同对象)
596
- expect(probeVersion).toHaveBeenCalledTimes(1);
597
- await engine.probe({ force: true });
598
- expect(probeVersion).toHaveBeenCalledTimes(2);
599
- });
600
-
601
- it("invocation 不可解析(PATH 无 pi)→ 第 1 check 失败,不再尝试版本探测", async () => {
602
- // bun 虚拟脚本 + 通用 runtime → 退化为 PATH 依赖形态(command === "pi")
603
- Object.defineProperty(process, "argv", { value: ["bun", "/$bunfs/root/pi"], configurable: true });
604
- Object.defineProperty(process, "execPath", { value: "/usr/bin/bun", configurable: true });
605
- process.env.PATH = path.join(os.tmpdir(), `no-pi-here-${Date.now()}`);
606
- const probeVersion = vi.fn(async () => "should-not-run");
607
- const engine = new PiEngine({ getService: () => null, probeVersion });
608
- const report = await engine.probe();
609
- expect(report.ok).toBe(false);
610
- expect(report.checks).toHaveLength(1);
611
- expect(report.checks[0].name).toBe("invocation");
612
- expect(report.checks[0].ok).toBe(false);
613
- expect(probeVersion).not.toHaveBeenCalled();
614
- });
615
- });