@zhushanwen/subagent-core 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (330) hide show
  1. package/README.md +114 -0
  2. package/dist/chunk-3VOERJPJ.js +22 -0
  3. package/dist/chunk-A4IVZWVX.js +31 -0
  4. package/dist/execution/engine/engines/zcode/constants.cjs +54 -0
  5. package/dist/execution/engine/engines/zcode/constants.d.cts +31 -0
  6. package/dist/execution/engine/engines/zcode/constants.d.ts +31 -0
  7. package/dist/execution/engine/engines/zcode/constants.js +22 -0
  8. package/dist/execution/engine/engines/zcode/reader.cjs +276 -0
  9. package/dist/execution/engine/engines/zcode/reader.d.cts +20 -0
  10. package/dist/execution/engine/engines/zcode/reader.d.ts +20 -0
  11. package/dist/execution/engine/engines/zcode/reader.js +239 -0
  12. package/dist/execution/engine/paths.cjs +55 -0
  13. package/dist/execution/engine/paths.d.cts +8 -0
  14. package/dist/execution/engine/paths.d.ts +8 -0
  15. package/dist/execution/engine/paths.js +26 -0
  16. package/dist/execution/relay-env.cjs +62 -0
  17. package/dist/execution/relay-env.d.cts +33 -0
  18. package/dist/execution/relay-env.d.ts +33 -0
  19. package/dist/execution/relay-env.js +20 -0
  20. package/dist/index.cjs +1852 -0
  21. package/dist/index.d.cts +1184 -0
  22. package/dist/index.d.ts +1184 -0
  23. package/dist/index.js +1810 -0
  24. package/dist/types-BxyAidGf.d.cts +875 -0
  25. package/dist/types-BxyAidGf.d.ts +875 -0
  26. package/package.json +99 -0
  27. package/src/__tests__/agent-opts-resolver-schema-prompt.test.ts +142 -0
  28. package/src/__tests__/append-system-prompt-assembly.test.ts +242 -0
  29. package/src/__tests__/builtin-workflows-structure.test.ts +180 -0
  30. package/src/__tests__/derive-closed-display-parity.test.ts +93 -0
  31. package/src/__tests__/fr4-get-state-handshake.test.ts +125 -0
  32. package/src/__tests__/fr8-orphan-recovery.test.ts +259 -0
  33. package/src/__tests__/m2-append-content-probe.test.ts +73 -0
  34. package/src/__tests__/manifest-store.test.ts +368 -0
  35. package/src/__tests__/record-store-cache.test.ts +335 -0
  36. package/src/__tests__/record-store-index.test.ts +560 -0
  37. package/src/__tests__/record-store-last-line.test.ts +129 -0
  38. package/src/__tests__/review-fix-loop-script.test.ts +288 -0
  39. package/src/__tests__/review-fix-loop-utils.test.ts +2377 -0
  40. package/src/__tests__/robustness-low-batch1.test.ts +286 -0
  41. package/src/__tests__/robustness-medium-batch1.test.ts +69 -0
  42. package/src/__tests__/robustness-medium-batch2.test.ts +65 -0
  43. package/src/__tests__/robustness-medium-batch3.test.ts +30 -0
  44. package/src/__tests__/robustness-medium-batch4.test.ts +358 -0
  45. package/src/__tests__/session-runner.test.ts +69 -0
  46. package/src/__tests__/smoke.test.ts +9 -0
  47. package/src/__tests__/stream-sink.test.ts +203 -0
  48. package/src/core/__tests__/host-services.test.ts +144 -0
  49. package/src/core/__tests__/logger.test.ts +78 -0
  50. package/src/core/__tests__/notify-ports.test.ts +104 -0
  51. package/src/core/host-services.ts +97 -0
  52. package/src/core/logger.ts +47 -0
  53. package/src/core/notify-ports.ts +126 -0
  54. package/src/execution/__tests__/__fixtures__/notifier-golden-snapshots.json +53 -0
  55. package/src/execution/__tests__/__fixtures__/truncline.snapshot.json +1 -0
  56. package/src/execution/__tests__/agent-result-mapper.test.ts +150 -0
  57. package/src/execution/__tests__/alive-store.test.ts +147 -0
  58. package/src/execution/__tests__/ask-user-transit-e2e.test.ts +490 -0
  59. package/src/execution/__tests__/channel-registry-handshake.test.ts +242 -0
  60. package/src/execution/__tests__/chat-engine-routing.test.ts +610 -0
  61. package/src/execution/__tests__/chatmode-first-round-closure-spawn.test.ts +190 -0
  62. package/src/execution/__tests__/concurrency-pool.test.ts +250 -0
  63. package/src/execution/__tests__/config.test.ts +302 -0
  64. package/src/execution/__tests__/delivery-methods.test.ts +424 -0
  65. package/src/execution/__tests__/dialog-queue.test.ts +299 -0
  66. package/src/execution/__tests__/epipe-fallback.test.ts +241 -0
  67. package/src/execution/__tests__/execute-and-await-worktree.test.ts +243 -0
  68. package/src/execution/__tests__/execute-nesting.test.ts +323 -0
  69. package/src/execution/__tests__/execute-options-mapper.test.ts +200 -0
  70. package/src/execution/__tests__/execution-record.test.ts +1394 -0
  71. package/src/execution/__tests__/explicit-agent-ref-guard.test.ts +171 -0
  72. package/src/execution/__tests__/finalize-record.test.ts +367 -0
  73. package/src/execution/__tests__/finalized-marker.test.ts +104 -0
  74. package/src/execution/__tests__/format-schema-instruction.test.ts +169 -0
  75. package/src/execution/__tests__/gc-timer.test.ts +184 -0
  76. package/src/execution/__tests__/get-record-for-action-restart.test.ts +254 -0
  77. package/src/execution/__tests__/helpers/mock-extension-api.ts +30 -0
  78. package/src/execution/__tests__/helpers/spawn-mock.ts +242 -0
  79. package/src/execution/__tests__/host-mode.test.ts +87 -0
  80. package/src/execution/__tests__/lifecycle-manager-lock.test.ts +211 -0
  81. package/src/execution/__tests__/lifecycle-manager.test.ts +383 -0
  82. package/src/execution/__tests__/lifecycle-predicates.test.ts +116 -0
  83. package/src/execution/__tests__/manifest-parentid.test.ts +117 -0
  84. package/src/execution/__tests__/model-resolver.test.ts +465 -0
  85. package/src/execution/__tests__/nested-visibility-env-propagation.test.ts +287 -0
  86. package/src/execution/__tests__/nested-visibility.test.ts +325 -0
  87. package/src/execution/__tests__/output-collector.test.ts +358 -0
  88. package/src/execution/__tests__/path-encoding.test.ts +104 -0
  89. package/src/execution/__tests__/pi-invocation.test.ts +134 -0
  90. package/src/execution/__tests__/record-store.test.ts +1057 -0
  91. package/src/execution/__tests__/records-cwd-isolation.test.ts +91 -0
  92. package/src/execution/__tests__/recursive-visibility-baseline.test.ts +339 -0
  93. package/src/execution/__tests__/recursive-visibility-env.test.ts +273 -0
  94. package/src/execution/__tests__/relay-env.test.ts +42 -0
  95. package/src/execution/__tests__/resource-policy.test.ts +109 -0
  96. package/src/execution/__tests__/rpc-mode.test.ts +89 -0
  97. package/src/execution/__tests__/run-and-finalize-chatmode.test.ts +267 -0
  98. package/src/execution/__tests__/run-spawn-chatmode-settled.test.ts +253 -0
  99. package/src/execution/__tests__/run-spawn-edges.test.ts +687 -0
  100. package/src/execution/__tests__/run-spawn-integration.test.ts +933 -0
  101. package/src/execution/__tests__/run-spawn-resume.test.ts +322 -0
  102. package/src/execution/__tests__/run-spawn-rpc-mode.test.ts +196 -0
  103. package/src/execution/__tests__/run-spawn-stdout-callback-throw.test.ts +199 -0
  104. package/src/execution/__tests__/session-context-resolver.test.ts +167 -0
  105. package/src/execution/__tests__/session-file-gc.test.ts +293 -0
  106. package/src/execution/__tests__/session-reconstructor.test.ts +379 -0
  107. package/src/execution/__tests__/session-runner-epipe.test.ts +178 -0
  108. package/src/execution/__tests__/session-runner-schema-env.test.ts +350 -0
  109. package/src/execution/__tests__/spawn-args.test.ts +445 -0
  110. package/src/execution/__tests__/spawn-event-adapter-rpc.test.ts +189 -0
  111. package/src/execution/__tests__/spawn-event-adapter.test.ts +167 -0
  112. package/src/execution/__tests__/spawn-worktree-guidance.test.ts +207 -0
  113. package/src/execution/__tests__/spawned-children.test.ts +92 -0
  114. package/src/execution/__tests__/start-sync-model-guard.test.ts +150 -0
  115. package/src/execution/__tests__/stdin-writer.test.ts +462 -0
  116. package/src/execution/__tests__/stream-sink-retirement.test.ts +261 -0
  117. package/src/execution/__tests__/subagent-service-abort.test.ts +60 -0
  118. package/src/execution/__tests__/subagent-service-message-close.test.ts +629 -0
  119. package/src/execution/__tests__/subagent-service-parent-guard.test.ts +180 -0
  120. package/src/execution/__tests__/subagent-service.test.ts +788 -0
  121. package/src/execution/__tests__/subprocess-agent-runner-routing.test.ts +310 -0
  122. package/src/execution/__tests__/subprocess-agent-runner.test.ts +612 -0
  123. package/src/execution/__tests__/temp-prompt.test.ts +53 -0
  124. package/src/execution/__tests__/timeout-integration.test.ts +615 -0
  125. package/src/execution/__tests__/tombstone-store.test.ts +73 -0
  126. package/src/execution/__tests__/turn-limiter-semantics.test.ts +194 -0
  127. package/src/execution/__tests__/turn-limiter.test.ts +65 -0
  128. package/src/execution/__tests__/ui-channels.test.ts +187 -0
  129. package/src/execution/__tests__/ui-interaction-model.test.ts +67 -0
  130. package/src/execution/__tests__/ui-request-handler-factory.test.ts +369 -0
  131. package/src/execution/__tests__/ui-request-handler.test.ts +204 -0
  132. package/src/execution/__tests__/ui-request-observability.test.ts +113 -0
  133. package/src/execution/__tests__/ui-request-queue.test.ts +133 -0
  134. package/src/execution/__tests__/worktree-manager.test.ts +633 -0
  135. package/src/execution/__tests__/worktree-pid-registration.integration.test.ts +229 -0
  136. package/src/execution/__tests__/worktree-reconcile.integration.test.ts +181 -0
  137. package/src/execution/__tests__/worktree-registry.test.ts +199 -0
  138. package/src/execution/agent-registry.ts +199 -0
  139. package/src/execution/agent-result-mapper.ts +90 -0
  140. package/src/execution/alive-store.ts +119 -0
  141. package/src/execution/argv-mirror.ts +113 -0
  142. package/src/execution/best-effort.ts +38 -0
  143. package/src/execution/channel-registry-access.ts +144 -0
  144. package/src/execution/concurrency-pool.ts +116 -0
  145. package/src/execution/config.ts +165 -0
  146. package/src/execution/dialog-queue.ts +330 -0
  147. package/src/execution/engine/__tests__/common/data-dir.test.ts +78 -0
  148. package/src/execution/engine/__tests__/common/errors.test.ts +132 -0
  149. package/src/execution/engine/__tests__/common/event-journal.test.ts +177 -0
  150. package/src/execution/engine/__tests__/common/kill-chain.test.ts +192 -0
  151. package/src/execution/engine/__tests__/common/nesting-guard.test.ts +81 -0
  152. package/src/execution/engine/__tests__/common/persona-router.test.ts +123 -0
  153. package/src/execution/engine/__tests__/common/pool-manager.test.ts +154 -0
  154. package/src/execution/engine/__tests__/common/schema-emulation.test.ts +128 -0
  155. package/src/execution/engine/__tests__/conformance/__fixtures__/pi-golden-events.json +28 -0
  156. package/src/execution/engine/__tests__/conformance/agent-event-invariants.ts +141 -0
  157. package/src/execution/engine/__tests__/conformance/contract.abort.test.ts +109 -0
  158. package/src/execution/engine/__tests__/conformance/contract.agent-events.test.ts +101 -0
  159. package/src/execution/engine/__tests__/conformance/contract.probe.test.ts +77 -0
  160. package/src/execution/engine/__tests__/conformance/contract.read-degradation.test.ts +104 -0
  161. package/src/execution/engine/__tests__/conformance/engine-conformance.live.test.ts +201 -0
  162. package/src/execution/engine/__tests__/conformance/golden-replay.pi.test.ts +76 -0
  163. package/src/execution/engine/__tests__/conformance/golden-replay.zcode.test.ts +79 -0
  164. package/src/execution/engine/__tests__/engine-discovery.test.ts +87 -0
  165. package/src/execution/engine/__tests__/model-prompt.test.ts +197 -0
  166. package/src/execution/engine/__tests__/paths.test.ts +39 -0
  167. package/src/execution/engine/__tests__/registry.test.ts +120 -0
  168. package/src/execution/engine/__tests__/routing.test.ts +231 -0
  169. package/src/execution/engine/common/data-dir.ts +66 -0
  170. package/src/execution/engine/common/errors.ts +183 -0
  171. package/src/execution/engine/common/event-journal.ts +254 -0
  172. package/src/execution/engine/common/journal-replay.ts +62 -0
  173. package/src/execution/engine/common/kill-chain.ts +221 -0
  174. package/src/execution/engine/common/nesting-guard.ts +50 -0
  175. package/src/execution/engine/common/persona-router.ts +108 -0
  176. package/src/execution/engine/common/pool-manager.ts +226 -0
  177. package/src/execution/engine/common/schema-emulation.ts +189 -0
  178. package/src/execution/engine/common/session-view-projection.ts +51 -0
  179. package/src/execution/engine/engine-discovery.ts +65 -0
  180. package/src/execution/engine/engines/pi/__tests__/pi-engine.test.ts +469 -0
  181. package/src/execution/engine/engines/pi/__tests__/reader.test.ts +155 -0
  182. package/src/execution/engine/engines/pi/__tests__/task-spec-mapper.test.ts +164 -0
  183. package/src/execution/engine/engines/pi/pi-engine.ts +415 -0
  184. package/src/execution/engine/engines/pi/reader.ts +48 -0
  185. package/src/execution/engine/engines/pi/registration.ts +35 -0
  186. package/src/execution/engine/engines/pi/task-spec-mapper.ts +100 -0
  187. package/src/execution/engine/engines/zcode/__tests__/__fixtures__/zcode-golden-spawn.json +39 -0
  188. package/src/execution/engine/engines/zcode/__tests__/launcher.test.ts +150 -0
  189. package/src/execution/engine/engines/zcode/__tests__/parser.test.ts +246 -0
  190. package/src/execution/engine/engines/zcode/__tests__/preparer.test.ts +228 -0
  191. package/src/execution/engine/engines/zcode/__tests__/reader.test.ts +210 -0
  192. package/src/execution/engine/engines/zcode/__tests__/registration.test.ts +71 -0
  193. package/src/execution/engine/engines/zcode/__tests__/zcode-engine.live.test.ts +127 -0
  194. package/src/execution/engine/engines/zcode/__tests__/zcode-engine.test.ts +580 -0
  195. package/src/execution/engine/engines/zcode/constants.ts +43 -0
  196. package/src/execution/engine/engines/zcode/golden-sample.ts +39 -0
  197. package/src/execution/engine/engines/zcode/launcher.ts +161 -0
  198. package/src/execution/engine/engines/zcode/parser.ts +436 -0
  199. package/src/execution/engine/engines/zcode/preparer.ts +363 -0
  200. package/src/execution/engine/engines/zcode/reader.ts +381 -0
  201. package/src/execution/engine/engines/zcode/registration.ts +37 -0
  202. package/src/execution/engine/engines/zcode/zcode-engine.ts +658 -0
  203. package/src/execution/engine/host-task-spec.ts +47 -0
  204. package/src/execution/engine/model-prompt.ts +158 -0
  205. package/src/execution/engine/paths.ts +42 -0
  206. package/src/execution/engine/port.ts +153 -0
  207. package/src/execution/engine/registry.ts +133 -0
  208. package/src/execution/engine/routing.ts +218 -0
  209. package/src/execution/engine/types.ts +309 -0
  210. package/src/execution/execute-options-mapper.ts +115 -0
  211. package/src/execution/execution-record.ts +984 -0
  212. package/src/execution/finalize-record.ts +254 -0
  213. package/src/execution/finalized-marker.ts +70 -0
  214. package/src/execution/get-state-handshake.ts +104 -0
  215. package/src/execution/host-mode.ts +56 -0
  216. package/src/execution/idle-gc.ts +47 -0
  217. package/src/execution/lifecycle-manager.ts +513 -0
  218. package/src/execution/lifecycle-predicates.ts +65 -0
  219. package/src/execution/manifest-store.ts +256 -0
  220. package/src/execution/model-config-service.ts +250 -0
  221. package/src/execution/model-resolver.ts +248 -0
  222. package/src/execution/notifier.ts +372 -0
  223. package/src/execution/notify-ledger.ts +580 -0
  224. package/src/execution/output-collector.ts +228 -0
  225. package/src/execution/path-encoding.ts +60 -0
  226. package/src/execution/pi-invocation.ts +120 -0
  227. package/src/execution/record-entry.ts +132 -0
  228. package/src/execution/record-store.ts +1265 -0
  229. package/src/execution/relay-env.ts +37 -0
  230. package/src/execution/session-context-resolver.ts +64 -0
  231. package/src/execution/session-file-gc.ts +120 -0
  232. package/src/execution/session-pending.ts +170 -0
  233. package/src/execution/session-reconstructor.ts +678 -0
  234. package/src/execution/session-runner.ts +1789 -0
  235. package/src/execution/sessions-index.ts +304 -0
  236. package/src/execution/spawn-event-adapter.ts +363 -0
  237. package/src/execution/stdin-writer.ts +198 -0
  238. package/src/execution/stream-sink.ts +126 -0
  239. package/src/execution/subagent-service.ts +2268 -0
  240. package/src/execution/subprocess-agent-runner.ts +317 -0
  241. package/src/execution/temp-prompt.ts +62 -0
  242. package/src/execution/tombstone-store.ts +72 -0
  243. package/src/execution/turn-limiter.ts +102 -0
  244. package/src/execution/types.ts +1023 -0
  245. package/src/execution/ui-channels.ts +216 -0
  246. package/src/execution/ui-interaction-model.ts +48 -0
  247. package/src/execution/ui-request-handler-factory.ts +243 -0
  248. package/src/execution/ui-request-observability.ts +81 -0
  249. package/src/execution/ui-request-queue.ts +184 -0
  250. package/src/execution/worktree-manager.ts +688 -0
  251. package/src/execution/worktree-registry.ts +279 -0
  252. package/src/index.ts +87 -0
  253. package/src/orchestration/__tests__/__fixtures__/worker-template.snapshot.txt +349 -0
  254. package/src/orchestration/__tests__/agent-call-catch-fallback.test.ts +202 -0
  255. package/src/orchestration/__tests__/agent-call-stream.test.ts +152 -0
  256. package/src/orchestration/__tests__/args-validator.test.ts +143 -0
  257. package/src/orchestration/__tests__/config-loader.test.ts +503 -0
  258. package/src/orchestration/__tests__/error-recovery-handlers.test.ts +809 -0
  259. package/src/orchestration/__tests__/error-recovery-postmessage-defense.test.ts +404 -0
  260. package/src/orchestration/__tests__/error-recovery-serialize-failed-result.test.ts +56 -0
  261. package/src/orchestration/__tests__/error-recovery-workflow-call.test.ts +166 -0
  262. package/src/orchestration/__tests__/execute-agent-call.test.ts +451 -0
  263. package/src/orchestration/__tests__/launcher-nested-workflow.test.ts +600 -0
  264. package/src/orchestration/__tests__/lifecycle-runid-injection.test.ts +96 -0
  265. package/src/orchestration/__tests__/lifecycle.test.ts +659 -0
  266. package/src/orchestration/__tests__/non-cloneable-return-e2e.test.ts +95 -0
  267. package/src/orchestration/__tests__/review-fix-loop-scriptpath-failfast.test.ts +183 -0
  268. package/src/orchestration/__tests__/script-lint.test.ts +831 -0
  269. package/src/orchestration/__tests__/skill-discovery.test.ts +210 -0
  270. package/src/orchestration/__tests__/test-mocks.ts +197 -0
  271. package/src/orchestration/__tests__/worker-exit-without-result.test.ts +368 -0
  272. package/src/orchestration/__tests__/worker-host.test.ts +120 -0
  273. package/src/orchestration/__tests__/worker-returnmeta-passthrough.test.ts +164 -0
  274. package/src/orchestration/__tests__/worker-script-builder-runtime.test.ts +563 -0
  275. package/src/orchestration/__tests__/worker-script-builder.test.ts +309 -0
  276. package/src/orchestration/__tests__/worker-script-template-snapshot.test.ts +129 -0
  277. package/src/orchestration/__tests__/workflow-nesting-e2e.test.ts +317 -0
  278. package/src/orchestration/__tests__/workflow-script-lint-memo.test.ts +110 -0
  279. package/src/orchestration/agent-opts-resolver.ts +174 -0
  280. package/src/orchestration/args-validator.ts +127 -0
  281. package/src/orchestration/config-loader.ts +316 -0
  282. package/src/orchestration/error-recovery.ts +1018 -0
  283. package/src/orchestration/execute-agent-call.ts +256 -0
  284. package/src/orchestration/launcher.ts +455 -0
  285. package/src/orchestration/lifecycle.ts +399 -0
  286. package/src/orchestration/models/__tests__/budget.test.ts +307 -0
  287. package/src/orchestration/models/agent-call.ts +83 -0
  288. package/src/orchestration/models/budget.ts +118 -0
  289. package/src/orchestration/models/ports.ts +164 -0
  290. package/src/orchestration/models/run-runtime.ts +104 -0
  291. package/src/orchestration/models/run-spec.ts +83 -0
  292. package/src/orchestration/models/run-state.ts +44 -0
  293. package/src/orchestration/models/trace.ts +183 -0
  294. package/src/orchestration/models/types.ts +301 -0
  295. package/src/orchestration/models/workflow-run.ts +254 -0
  296. package/src/orchestration/models/workflow-script-registry.ts +35 -0
  297. package/src/orchestration/models/workflow-script.ts +117 -0
  298. package/src/orchestration/script-lint.ts +766 -0
  299. package/src/orchestration/skill-discovery.ts +135 -0
  300. package/src/orchestration/worker-handle.ts +115 -0
  301. package/src/orchestration/worker-host.ts +98 -0
  302. package/src/orchestration/worker-script-builder.ts +421 -0
  303. package/src/orchestration/workflow-files.ts +85 -0
  304. package/src/orchestration/workflow-script-registry-impl.ts +133 -0
  305. package/src/shared/__tests__/agent-ref.test.ts +34 -0
  306. package/src/shared/__tests__/meta-parser.test.ts +304 -0
  307. package/src/shared/__tests__/model-ref.test.ts +306 -0
  308. package/src/shared/__tests__/resource-discovery-manifest-cache.test.ts +288 -0
  309. package/src/shared/__tests__/resource-discovery.test.ts +749 -0
  310. package/src/shared/__tests__/resource-meta.test.ts +51 -0
  311. package/src/shared/__tests__/schema-jsonify.test.ts +81 -0
  312. package/src/shared/__tests__/timer-delay.test.ts +61 -0
  313. package/src/shared/agent-event.ts +13 -0
  314. package/src/shared/agent-ref.ts +57 -0
  315. package/src/shared/meta-parser.ts +261 -0
  316. package/src/shared/model-ref.ts +286 -0
  317. package/src/shared/resource-discovery.ts +835 -0
  318. package/src/shared/resource-meta.ts +65 -0
  319. package/src/shared/schema-env.ts +44 -0
  320. package/src/shared/schema-jsonify.ts +58 -0
  321. package/src/shared/timer-delay.ts +54 -0
  322. package/src/shared/xml-injection.ts +35 -0
  323. package/workflows/README.md +81 -0
  324. package/workflows/_shared/agent-refs.cjs +40 -0
  325. package/workflows/chain.js +137 -0
  326. package/workflows/map-reduce.js +180 -0
  327. package/workflows/parallel.js +154 -0
  328. package/workflows/review-fix-loop-utils.cjs +1542 -0
  329. package/workflows/review-fix-loop.js +1605 -0
  330. package/workflows/scatter-gather.js +184 -0
@@ -0,0 +1,77 @@
1
+ // contract.probe.test.ts —— conformance C1(probe 形状):ProbeReport 字段完整;
2
+ // ok=false 时 error.recovery 非空(§3.3.5——恢复指引是错误闭环「错误→权威源→重试」
3
+ // 的载体,空指引 = 拦截了但不知道怎么修)。pi/zcode 双引擎都过(任何 adapter 必过)。
4
+ //
5
+ // fake 注入(不依赖真机):pi 用 probeVersion fake + 不可解析 invocation 场景;
6
+ // zcode 用不存在的 cliPath 构造 binary check 失败。
7
+
8
+ import * as fs from "node:fs";
9
+ import * as os from "node:os";
10
+ import * as path from "node:path";
11
+ import { fileURLToPath } from "node:url";
12
+
13
+ import { describe, expect, it } from "vitest";
14
+
15
+ import { PiEngine } from "../../engines/pi/pi-engine.ts";
16
+ import { ZcodeEngine } from "../../engines/zcode/zcode-engine.ts";
17
+ import type { ProbeReport } from "../../types.ts";
18
+
19
+ function assertProbeShape(report: ProbeReport): void {
20
+ expect(typeof report.ok).toBe("boolean");
21
+ expect(typeof report.engineVersion).toBe("string");
22
+ expect(Array.isArray(report.checks)).toBe(true);
23
+ expect(report.checks.length).toBeGreaterThan(0);
24
+ for (const c of report.checks) {
25
+ expect(typeof c.name).toBe("string");
26
+ expect(typeof c.ok).toBe("boolean");
27
+ }
28
+ if (!report.ok) {
29
+ expect(report.error).toBeDefined();
30
+ expect(report.error?.code).toBe("engine_probe_failed");
31
+ // 恢复指引非空且指向动作(C1 断言核心——空指引直接 fail)
32
+ expect(report.error?.recovery).toBeTruthy();
33
+ expect(report.error?.recovery.length).toBeGreaterThan(20);
34
+ }
35
+ }
36
+
37
+ describe("conformance C1:probe 形状(ProbeReport 字段完整 + 失败含恢复指引)", () => {
38
+ it("pi:成功路径形状(fake 版本探测,不 spawn 真进程)", async () => {
39
+ const engine = new PiEngine({ getService: () => null, probeVersion: async () => "0.84.1" });
40
+ const report = await engine.probe();
41
+ assertProbeShape(report);
42
+ expect(report.ok).toBe(true);
43
+ expect(report.engineVersion).toBe("0.84.1");
44
+ });
45
+
46
+ it("pi:失败路径(invocation 不可解析)——error.recovery 非空", async () => {
47
+ const engine = new PiEngine({
48
+ getService: () => null,
49
+ probeVersion: async () => undefined,
50
+ });
51
+ // PATH 无 pi 时 invocation check 失败;有 pi 时版本 fake 失败——两形态都走断言器
52
+ const report = await engine.probe({ force: true });
53
+ assertProbeShape(report);
54
+ });
55
+
56
+ it("zcode:成功路径形状(存在的 cliPath + fake 版本探测)", async () => {
57
+ const engine = new ZcodeEngine({
58
+ engineDataDir: () => fs.mkdtempSync(path.join(os.tmpdir(), "probe-zcode-")),
59
+ cliPath: fileURLToPath(import.meta.url),
60
+ probeVersion: async () => "0.16.5",
61
+ });
62
+ const report = await engine.probe();
63
+ assertProbeShape(report);
64
+ expect(report.ok).toBe(true);
65
+ });
66
+
67
+ it("zcode:失败路径(二进制不存在)——error.recovery 非空且含重探指引", async () => {
68
+ const engine = new ZcodeEngine({
69
+ engineDataDir: () => fs.mkdtempSync(path.join(os.tmpdir(), "probe-zcode-")),
70
+ cliPath: "/nonexistent/zcode.cjs",
71
+ });
72
+ const report = await engine.probe();
73
+ assertProbeShape(report);
74
+ expect(report.ok).toBe(false);
75
+ expect(report.error?.recovery).toContain("--version");
76
+ });
77
+ });
@@ -0,0 +1,104 @@
1
+ // contract.read-degradation.test.ts —— conformance C5(read 降级链):三级都不 throw、
2
+ // 坏 handle → 结构化降级(outcome-only)非崩溃、②级 journal 重放与 live 一致
3
+ // (重放等价性,§3.3.6——journal 重放与 live 通路共用同一 reducer 的断言面)。
4
+ //
5
+ // fake 注入:zcode 用坏 dbPath(①级失败)+ 临时 journal 文件(②级命中);pi 用
6
+ // 不存在的 sessionFile(①级失败)+ journal(②级)。
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 { afterEach, beforeEach, describe, expect, it } from "vitest";
13
+
14
+ import { eventsToSessionView } from "../../common/journal-replay.ts";
15
+ import { JournalWriter } from "../../common/event-journal.ts";
16
+ import { PiEngine } from "../../engines/pi/pi-engine.ts";
17
+ import { ZcodeEngine } from "../../engines/zcode/zcode-engine.ts";
18
+ import type { AgentEvent, EngineHandle } from "../../types.ts";
19
+
20
+ function makeHandle(engineId: string, sessionRef: Record<string, string>, journalPath?: string): EngineHandle {
21
+ return {
22
+ data: {
23
+ v: 1,
24
+ engineId,
25
+ sessionRef,
26
+ poolKey: "shared",
27
+ ...(journalPath !== undefined ? { journalPath } : {}),
28
+ adapterVersion: "1.0.0-test",
29
+ },
30
+ };
31
+ }
32
+
33
+ /** live 通路形态的事件序列(与 golden 回放同源——重放等价性的比对基准)。 */
34
+ const liveEvents: AgentEvent[] = [
35
+ { type: "text_delta", delta: "part one. " },
36
+ { type: "text_delta", delta: "part two." },
37
+ { type: "message_end", usage: { input: 10, output: 5, cacheRead: 0, cacheWrite: 0 } },
38
+ { type: "turn_end" },
39
+ ];
40
+
41
+ describe("conformance C5:read 降级链(三级都不 throw)", () => {
42
+ let dataDir: string;
43
+ beforeEach(() => {
44
+ dataDir = fs.mkdtempSync(path.join(os.tmpdir(), "c5-read-"));
45
+ });
46
+ afterEach(() => {
47
+ fs.rmSync(dataDir, { recursive: true, force: true });
48
+ });
49
+
50
+ it("重放等价性:journal 重放 turns 与 live 累积一致(共用 updateFromEvent reducer)", () => {
51
+ const live = eventsToSessionView(liveEvents, "pi", "sess-x");
52
+ expect(live.source).toBe("journal");
53
+ expect(live.turns).toHaveLength(1);
54
+ expect(live.turns[0]?.text).toBe("part one. part two.");
55
+ expect(live.usage?.total).toBe(15);
56
+ });
57
+
58
+ it("zcode:①级坏 handle(dbPath 指向不存在文件)→ ②级 journal 命中(source=journal)", async () => {
59
+ const journalPath = path.join(dataDir, "journal-sa-c5.jsonl");
60
+ const writer = new JournalWriter({ path: journalPath, taskId: "sa-c5", engineId: "zcode" });
61
+ for (const ev of liveEvents) writer.append(ev);
62
+ await writer.close();
63
+
64
+ const engine = new ZcodeEngine({ engineDataDir: () => dataDir });
65
+ const handle = makeHandle("zcode", { sessionId: "sess-c5", dbPath: ".zcode/cli/db/db.sqlite" }, journalPath);
66
+ const view = await engine.read(handle); // ①级 db 不存在 → catch → ②级重放
67
+ expect(view.source).toBe("journal");
68
+ expect(view.engineId).toBe("zcode");
69
+ expect(view.turns[0]?.text).toBe("part one. part two.");
70
+ });
71
+
72
+ it("zcode:②级也不可达(无 journalPath)→ ③级 outcome-only,不 throw", async () => {
73
+ const engine = new ZcodeEngine({ engineDataDir: () => dataDir });
74
+ const handle = makeHandle("zcode", { sessionId: "sess-c5", dbPath: ".zcode/cli/db/db.sqlite" });
75
+ const view = await engine.read(handle);
76
+ expect(view.source).toBe("outcome-only");
77
+ expect(view.turns).toEqual([]);
78
+ });
79
+
80
+ it("zcode:跨引擎 handle(engineId 不符)→ 结构化 outcome-only,不 throw", async () => {
81
+ const engine = new ZcodeEngine({ engineDataDir: () => dataDir });
82
+ const view = await engine.read(makeHandle("pi", {}));
83
+ expect(view.source).toBe("outcome-only");
84
+ });
85
+
86
+ it("pi:①级坏 handle(sessionFile 不存在)→ ②级 journal 命中;②级缺省 → ③级", async () => {
87
+ const engine = new PiEngine({ getService: () => null });
88
+
89
+ const journalPath = path.join(dataDir, "journal-sa-pi-c5.jsonl");
90
+ const writer = new JournalWriter({ path: journalPath, taskId: "sa-pi-c5", engineId: "pi" });
91
+ for (const ev of liveEvents) writer.append(ev);
92
+ await writer.close();
93
+
94
+ const viaJournal = await engine.read(
95
+ makeHandle("pi", { sessionFile: "/nonexistent/session.jsonl" }, journalPath),
96
+ );
97
+ expect(viaJournal.source).toBe("journal");
98
+ expect(viaJournal.turns[0]?.text).toBe("part one. part two.");
99
+
100
+ const outcomeOnly = await engine.read(makeHandle("pi", { sessionFile: "/nonexistent/session.jsonl" }));
101
+ expect(outcomeOnly.source).toBe("outcome-only");
102
+ expect(outcomeOnly.turns).toEqual([]);
103
+ });
104
+ });
@@ -0,0 +1,201 @@
1
+ // engine-conformance.live.test.ts —— conformance run 层(真实 spawn 简单任务,C2/C4
2
+ // 真机面)。手动门(设计 §3.3.8:run 层需已装引擎 + 有效凭据,不进默认 CI):
3
+ //
4
+ // cd extensions/universal/subagent-workflow
5
+ // ENGINE_CONFORMANCE_LIVE=1 pnpm vitest run src/execution/engine/__tests__/conformance/engine-conformance.live.test.ts
6
+ //
7
+ // pi 部分复用 PiEngine 的服务面注入(真实 executeAndAwait 需要完整 SubagentService 装配,
8
+ // live 场景从进程单例取——未初始化时该用例 skip 并说明);zcode 部分与 P3 的
9
+ // zcode-engine.live.test.ts 互补(后者已覆盖 schema 全链,此处只跑 conformance 最小面)。
10
+ //
11
+ // relay 变体(E 方案 §2.3)= 同一契约 × 不同 spawn 通道:测试内起伪 runtime(net server,
12
+ // 按 E-2 协议收握手回 accept / down 帧转写真实 pi stdin / pi stdout 回发 up 帧 / exit 帧收尾),
13
+ // 经 PiEngine 真实 run 全链断言 C2+C3。前置 = SubagentService 已装配(真实会话进程内),
14
+ // 且需真实模型凭据(最小任务由 LLM 完成)——纯 CI/无凭据环境必然 skip,属预期。
15
+
16
+ import { spawn as childSpawn, type ChildProcess } from "node:child_process";
17
+ import * as fs from "node:fs";
18
+ import * as net from "node:net";
19
+ import * as os from "node:os";
20
+ import * as path from "node:path";
21
+ import { fileURLToPath } from "node:url";
22
+
23
+ import { describe, expect, it } from "vitest";
24
+
25
+ import { getSubagentService } from "../../../subagent-service.ts";
26
+ import { ZcodeEngine } from "../../engines/zcode/zcode-engine.ts";
27
+ import { createPiEngine } from "../../engines/pi/registration.ts";
28
+ import type { RunContext } from "../../port.ts";
29
+ import type { AgentEvent } from "../../../types.ts";
30
+ import type { AgentTaskSpec } from "../../types.ts";
31
+ import { getPiInvocation } from "../../../pi-invocation.ts";
32
+ import {
33
+ RELAY_ENV_NODE,
34
+ RELAY_ENV_RECORD_ID,
35
+ RELAY_ENV_SCRIPT,
36
+ RELAY_ENV_SESSION_ID,
37
+ RELAY_ENV_SOCKET,
38
+ RELAY_PROTOCOL_VERSION,
39
+ isRelayActive,
40
+ } from "../../../relay-env.ts";
41
+ import { assertAgentEventInvariants } from "./agent-event-invariants.ts";
42
+
43
+ const LIVE = process.env["ENGINE_CONFORMANCE_LIVE"] === "1";
44
+
45
+ describe.skipIf(!LIVE)("conformance run 层(真实 spawn,手动门)", () => {
46
+ it("pi:简单任务全链(C2:outcome 无 error、content 非空、engineId=pi)", async (testCtx) => {
47
+ const service = getSubagentService();
48
+ if (service === null) {
49
+ // 服务装配是 pi live 的前置(完整 SubagentService + modelRegistry 注入)——
50
+ // live 门内说明跳过原因而非静默 pass(失败要出声)
51
+ testCtx.skip("SubagentService 未装配(需在真实会话进程内运行)");
52
+ return;
53
+ }
54
+ const engine = createPiEngine(() => service);
55
+ const task: AgentTaskSpec = { task: "Reply with the single word: ok", slug: "live-c2" };
56
+ const ctx: RunContext = { taskId: "sa-live-pi-c2", poolKey: "shared" };
57
+ const { outcome } = await engine.run(task, ctx);
58
+ expect(outcome.error).toBeUndefined();
59
+ expect(outcome.content.trim().length).toBeGreaterThan(0);
60
+ expect(outcome.engineId).toBe("pi");
61
+ }, 120_000);
62
+
63
+ it("zcode:probe 真机(C1 live 面:三项 check 全过)", async () => {
64
+ const engine = new ZcodeEngine({ engineDataDir: () => "/tmp/zcode-conformance-live" });
65
+ const report = await engine.probe();
66
+ expect(report.ok).toBe(true);
67
+ expect(report.engineVersion).toMatch(/^0\.\d+\.\d+$/);
68
+ }, 60_000);
69
+ });
70
+
71
+ // ── relay 变体(E 方案 §2.3:同一契约 × 不同 spawn 通道,手动门内的手动门)──
72
+ //
73
+ // 前置:①ENGINE_CONFORMANCE_LIVE=1(套件级门);②SubagentService 已装配(真实会话
74
+ // 进程内跑——vitest 裸进程恒 null → skip 说明,与上方 pi live 用例同形态);③真实模型
75
+ // 凭据(最小任务由 LLM 完成)。relay 三 env 由测试自构(socket 指向测试内伪 runtime,
76
+ // 执行器 = 本进程 node,脚本 = 包根 relay/relay.mjs),不依赖外部注入。
77
+
78
+ describe.skipIf(!LIVE)("conformance relay 变体(经代理 spawn 全链,手动门)", () => {
79
+ /** 伪 runtime:按 E-2 协议扮演 runtime 侧——握手 accept + spawn 真实 pi + 双向转发 + exit 传播。 */
80
+ function startFakeRuntime(socketPath: string): Promise<net.Server> {
81
+ return new Promise((resolve, reject) => {
82
+ const server = net.createServer((conn) => {
83
+ let lineBuf = "";
84
+ let pi: ChildProcess | null = null;
85
+ const send = (frame: Record<string, unknown>): void => {
86
+ conn.write(`${JSON.stringify(frame)}\n`);
87
+ };
88
+ const handleLine = (line: string): void => {
89
+ let frame: Record<string, unknown>;
90
+ try {
91
+ const parsed: unknown = JSON.parse(line);
92
+ if (typeof parsed !== "object" || parsed === null) return;
93
+ frame = parsed as Record<string, unknown>;
94
+ } catch {
95
+ return;
96
+ }
97
+ if (frame.kind === "handshake" && pi === null) {
98
+ // 按 E-2 协议回 accept,随后按握手帧 spawn 真实 pi(直连——本进程 env 已带
99
+ // relay 三 env,必须 relay:false 防二次代理死循环);env 剥离 relay 五键
100
+ //(对齐 E-2 registry 剥离逻辑,防孙进程嵌套 relay 时旧值误导)。
101
+ send({ v: RELAY_PROTOCOL_VERSION, kind: "accept" });
102
+ const argv = Array.isArray(frame.argv) ? (frame.argv as string[]) : [];
103
+ const invocation = getPiInvocation(argv, { relay: false });
104
+ const env: NodeJS.ProcessEnv = { ...(frame.env as NodeJS.ProcessEnv) };
105
+ for (const key of [RELAY_ENV_SOCKET, RELAY_ENV_NODE, RELAY_ENV_SCRIPT, RELAY_ENV_SESSION_ID, RELAY_ENV_RECORD_ID]) {
106
+ delete env[key];
107
+ }
108
+ pi = childSpawn(invocation.command, invocation.args, {
109
+ env,
110
+ cwd: typeof frame.cwd === "string" ? frame.cwd : undefined,
111
+ stdio: ["pipe", "pipe", "pipe"],
112
+ });
113
+ pi.stdout?.on("data", (c: Buffer) => {
114
+ send({ v: RELAY_PROTOCOL_VERSION, kind: "data", dir: "up", b64: c.toString("base64") });
115
+ });
116
+ pi.stderr?.on("data", (c: Buffer) => {
117
+ send({ v: RELAY_PROTOCOL_VERSION, kind: "data", dir: "up-stderr", b64: c.toString("base64") });
118
+ });
119
+ pi.on("close", (code, signal) => {
120
+ send(signal !== null ? { kind: "exit", signal } : { kind: "exit", code: code ?? 1 });
121
+ });
122
+ return;
123
+ }
124
+ if (frame.kind === "data" && frame.dir === "down" && typeof frame.b64 === "string") {
125
+ pi?.stdin?.write(Buffer.from(frame.b64, "base64"));
126
+ }
127
+ };
128
+ conn.setEncoding("utf8");
129
+ conn.on("data", (chunk: string) => {
130
+ lineBuf += chunk;
131
+ let nl: number;
132
+ while ((nl = lineBuf.indexOf("\n")) >= 0) {
133
+ const line = lineBuf.slice(0, nl);
134
+ lineBuf = lineBuf.slice(nl + 1);
135
+ if (line.trim()) handleLine(line);
136
+ }
137
+ });
138
+ // 断连即杀(对齐 E-2 registry 语义)——防代理死/测试收尾后真实 pi 变孤儿
139
+ conn.on("close", () => {
140
+ if (pi !== null && !pi.killed) pi.kill("SIGTERM");
141
+ });
142
+ });
143
+ server.once("error", reject);
144
+ server.listen(socketPath, () => resolve(server));
145
+ });
146
+ }
147
+
148
+ it("pi × relay:伪 runtime 环回全链(C2 outcome 无 error + C3 事件不变量经代理转发全等)", { timeout: 180_000 }, async (testCtx) => {
149
+ const service = getSubagentService();
150
+ if (service === null) {
151
+ testCtx.skip(
152
+ "SubagentService 未装配(需在真实会话进程内运行,如 pi --extension 挂载本包后触发)" +
153
+ "——relay 变体与直连 live 用例共享该前置;纯 vitest 进程恒 skip,全链真机另由 " +
154
+ "packages/runtime relay-integration.test.ts(已存在)+ §9 人工验收承载",
155
+ );
156
+ return;
157
+ }
158
+
159
+ // 测试自构 relay env:socket 指向伪 runtime,执行器 = 本进程 node,脚本 = 包根 relay.mjs
160
+ const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "relay-conformance-live-"));
161
+ const socketPath = path.join(tmpDir, "relay.sock");
162
+ const server = await startFakeRuntime(socketPath);
163
+ const savedEnv: Record<string, string | undefined> = {};
164
+ const restoreKeys = [RELAY_ENV_SOCKET, RELAY_ENV_NODE, RELAY_ENV_SCRIPT];
165
+ for (const key of restoreKeys) savedEnv[key] = process.env[key];
166
+ process.env[RELAY_ENV_SOCKET] = socketPath;
167
+ process.env[RELAY_ENV_NODE] = process.execPath;
168
+ process.env[RELAY_ENV_SCRIPT] = path.resolve(
169
+ path.dirname(fileURLToPath(import.meta.url)),
170
+ "../../../../../relay/relay.mjs",
171
+ );
172
+ if (!isRelayActive(process.env)) {
173
+ throw new Error("relay env 自构后仍不激活——前置断言失败");
174
+ }
175
+
176
+ try {
177
+ const engine = createPiEngine(() => service);
178
+ const events: AgentEvent[] = [];
179
+ const task: AgentTaskSpec = { task: "Reply with the single word: ok", slug: "live-relay-c2" };
180
+ const ctx: RunContext = {
181
+ taskId: "sa-live-pi-relay",
182
+ poolKey: "shared",
183
+ onEvent: (event) => events.push(event),
184
+ };
185
+ const { outcome } = await engine.run(task, ctx);
186
+ // C2:outcome 无 error + engineId 仍为 pi(relay 是 spawn 通道不是引擎身份)
187
+ expect(outcome.error).toBeUndefined();
188
+ expect(outcome.content.trim().length).toBeGreaterThan(0);
189
+ expect(outcome.engineId).toBe("pi");
190
+ // C3:事件不变量五条对「经代理转发的子进程 stdout」逐一成立(同一 parser 消费同一字节流)
191
+ assertAgentEventInvariants(events, { granularity: "stream", content: outcome.content });
192
+ } finally {
193
+ for (const key of restoreKeys) {
194
+ if (savedEnv[key] === undefined) delete process.env[key];
195
+ else process.env[key] = savedEnv[key];
196
+ }
197
+ await new Promise<void>((resolve) => server.close(() => resolve()));
198
+ fs.rmSync(tmpDir, { recursive: true, force: true });
199
+ }
200
+ });
201
+ });
@@ -0,0 +1,76 @@
1
+ // golden-replay.pi.test.ts —— pi 引擎 golden 回放层(conformance 免 LLM 默认 CI 层,
2
+ // 设计 §3.3.8 两层结构的第一层)。锚定物 = 统一 AgentEvent 序列(onEvent 出口 / journal
3
+ // 落盘形态)——pi 的完整翻译链(parseSpawnLine → handleSdkEvent)闭包在 session-runner
4
+ // 的 spawn 状态里,conformance 以「翻译产物」为契约锚点(journal/record 消费的正是它)。
5
+ //
6
+ // 覆盖:C3 不变量(流式口径)+ journal 往返保真(replayJournal === golden 序列)+
7
+ // parseSpawnLine 对实录行形态的回归(pi parser 的纯函数面)。
8
+
9
+ import { mkdtempSync, readFileSync, rmSync } from "node:fs";
10
+ import { tmpdir } from "node:os";
11
+ import { dirname, join } from "node:path";
12
+ import { fileURLToPath } from "node:url";
13
+
14
+ import { describe, expect, it } from "vitest";
15
+
16
+ import type { AgentEvent } from "../../../types.ts";
17
+ import { JournalWriter, replayJournal } from "../../common/event-journal.ts";
18
+ import { parseSpawnLine } from "../../../spawn-event-adapter.ts";
19
+ import { assertAgentEventInvariants } from "./agent-event-invariants.ts";
20
+
21
+ interface PiGoldenFile {
22
+ events: AgentEvent[];
23
+ content: string;
24
+ }
25
+
26
+ const fixturePath = join(dirname(fileURLToPath(import.meta.url)), "__fixtures__", "pi-golden-events.json");
27
+ const golden = JSON.parse(readFileSync(fixturePath, "utf8")) as PiGoldenFile;
28
+
29
+ describe("pi golden 回放(conformance C3/C5-journal,免 LLM)", () => {
30
+ it("golden 事件序列满足全部产出不变量(流式口径:text_delta 拼接 === content)", () => {
31
+ assertAgentEventInvariants(golden.events, { granularity: "stream", content: golden.content });
32
+ });
33
+
34
+ it("journal 往返保真:golden 序列经 JournalWriter 落盘后 replayJournal 逐事件相等", async () => {
35
+ const dir = mkdtempSync(join(tmpdir(), "pi-golden-journal-"));
36
+ const writer = new JournalWriter({
37
+ path: join(dir, "journal-sa-pi-golden.jsonl"),
38
+ taskId: "sa-pi-golden",
39
+ engineId: "pi",
40
+ });
41
+ try {
42
+ for (const ev of golden.events) writer.append(ev);
43
+ await writer.close();
44
+ const replayed = replayJournal(writer.path);
45
+ // 深比较(含 undefined 键缺省语义——JSON 序列化后 undefined 字段自然消失,两侧同构)
46
+ expect(replayed).toEqual(golden.events);
47
+ } finally {
48
+ rmSync(dir, { recursive: true, force: true });
49
+ }
50
+ });
51
+
52
+ it("parseSpawnLine 回归:实录行形态(header/事件行)识别不漂移", () => {
53
+ const header = parseSpawnLine(
54
+ JSON.stringify({ type: "session", id: "sess-pi-golden", timestamp: "2026-08-25T01:00:00.000Z", cwd: "/tmp" }),
55
+ );
56
+ expect(header?.kind).toBe("header");
57
+
58
+ const toolLine = parseSpawnLine(
59
+ JSON.stringify({ type: "tool_execution_start", toolName: "bash", toolCallId: "c1", args: { command: "ls" } }),
60
+ );
61
+ expect(toolLine?.kind).toBe("event");
62
+ if (toolLine?.kind === "event") expect(toolLine.event.type).toBe("tool_execution_start");
63
+
64
+ const msgEnd = parseSpawnLine(
65
+ JSON.stringify({
66
+ type: "message_end",
67
+ message: { role: "assistant", usage: { input: 100, output: 50, cacheRead: 0, cacheWrite: 0 } },
68
+ }),
69
+ );
70
+ expect(msgEnd?.kind).toBe("event");
71
+ if (msgEnd?.kind === "event") expect(msgEnd.event.type).toBe("message_end");
72
+
73
+ const turnEnd = parseSpawnLine(JSON.stringify({ type: "turn_end" }));
74
+ expect(turnEnd?.kind).toBe("event");
75
+ });
76
+ });
@@ -0,0 +1,79 @@
1
+ // golden-replay.zcode.test.ts —— zcode 引擎 golden 回放层(conformance 免 LLM 默认 CI
2
+ // 层)。数据源 = P3 验收前置门的真机实录(engines/zcode/__tests__/__fixtures__/
3
+ // zcode-golden-spawn.json 的 stdoutRaw)——parser 对实录样本回归 + coarse 事件合成
4
+ // 不变量断言 + 双副本 diff 校验(fixture 与 golden-sample.ts 内嵌副本一致,防漂移:
5
+ // 更新样本必须同步两处,探针的干跑校验消费内嵌副本,两处不一致 = 探针在测旧契约)。
6
+
7
+ import { readFileSync } from "node:fs";
8
+ import { dirname, join } from "node:path";
9
+ import { fileURLToPath } from "node:url";
10
+
11
+ import { describe, expect, it } from "vitest";
12
+
13
+ import { ZCODE_GOLDEN_STDOUT } from "../../engines/zcode/golden-sample.ts";
14
+ import {
15
+ mapZcodeOutcomeUsage,
16
+ parseZcodeTerminal,
17
+ synthesizeCoarseEvents,
18
+ } from "../../engines/zcode/parser.ts";
19
+ import { assertAgentEventInvariants } from "./agent-event-invariants.ts";
20
+
21
+ // conformance 目录(engine/__tests__/conformance)→ engine 根的相对定位
22
+ const fixturePath = join(
23
+ dirname(fileURLToPath(import.meta.url)),
24
+ "..", "..", // conformance → __tests__ → engine
25
+ "engines", "zcode", "__tests__", "__fixtures__", "zcode-golden-spawn.json",
26
+ );
27
+
28
+ interface ZcodeGoldenFile {
29
+ _meta: { engineVersion: string; exitCode: number };
30
+ stdoutRaw: string;
31
+ }
32
+
33
+ const fixture = JSON.parse(readFileSync(fixturePath, "utf8")) as ZcodeGoldenFile;
34
+
35
+ describe("zcode golden 回放(conformance C3,免 LLM/免二进制)", () => {
36
+ it("双副本 diff:golden-sample.ts 内嵌副本 === fixture stdoutRaw(防漂移,A12)", () => {
37
+ expect(ZCODE_GOLDEN_STDOUT).toBe(fixture.stdoutRaw);
38
+ });
39
+
40
+ it("parser 对实录样本回归:sessionId/response/usage 形状完整(探针干跑同源断言)", () => {
41
+ const terminal = parseZcodeTerminal(fixture.stdoutRaw);
42
+ expect(terminal.ok).toBe(true);
43
+ if (!terminal.ok) return;
44
+ expect(terminal.payload.sessionId).toMatch(/^sess_/);
45
+ expect(terminal.payload.response).toBe("ok");
46
+ expect(terminal.payload.usage).toEqual({ input: 12599, output: 17, cacheRead: 512, cacheWrite: 0 });
47
+ // 终态层 usage(orchestration 版):cost 显式 0(无来源不给残缺)、contextTokens 取 projection
48
+ expect(terminal.payload.outcomeUsage).toEqual({
49
+ input: 12599,
50
+ output: 17,
51
+ cacheRead: 512,
52
+ cacheWrite: 0,
53
+ cost: 0,
54
+ contextTokens: 12616,
55
+ turns: 1,
56
+ });
57
+ });
58
+
59
+ it("coarse 事件合成满足产出不变量(turn_end 最后、其前必有 message_end、usage 完整)", () => {
60
+ const terminal = parseZcodeTerminal(fixture.stdoutRaw);
61
+ if (!terminal.ok) throw new Error("golden 样本解析失败(上一用例应已转红)");
62
+ const events = synthesizeCoarseEvents(terminal.payload.response, terminal.payload.usage);
63
+ assertAgentEventInvariants(events, { granularity: "coarse" });
64
+ expect(events.map((e) => e.type)).toEqual(["message_end", "turn_end"]);
65
+ });
66
+
67
+ it("usage 无来源样本:mapZcodeOutcomeUsage 显式缺省(不给残缺对象,不变量 2 的输入面)", () => {
68
+ expect(mapZcodeOutcomeUsage(undefined, undefined)).toBeUndefined();
69
+ expect(mapZcodeOutcomeUsage({ inputTokens: 1 }, undefined)).toEqual({
70
+ input: 1,
71
+ output: 0,
72
+ cacheRead: 0,
73
+ cacheWrite: 0,
74
+ cost: 0,
75
+ contextTokens: 0,
76
+ turns: 1,
77
+ });
78
+ });
79
+ });
@@ -0,0 +1,87 @@
1
+ // engine-discovery.test.ts —— [U7] registry → engines.json 同步(幂等/原子/兜底)。
2
+
3
+ import * as fs from "node:fs";
4
+ import * as os from "node:os";
5
+ import * as path from "node:path";
6
+
7
+ import { afterEach, beforeEach, describe, expect, it } from "vitest";
8
+
9
+ import type { SubagentEnginesFile } from "@xyz-agent/extension-protocol";
10
+
11
+ import { getEnginesFilePath, syncEnginesFile } from "../engine-discovery.ts";
12
+ import type { EnginePort } from "../port.ts";
13
+ import { clearEngines, registerEngine } from "../registry.ts";
14
+
15
+ function stubEngine(id: string): EnginePort {
16
+ return {
17
+ id,
18
+ capabilities: () => ({ conversation: "unsupported", steer: "unsupported", sandbox: "none" }),
19
+ probe: async () => ({ ok: true, engineVersion: "test" }),
20
+ run: async () => {
21
+ throw new Error("unused");
22
+ },
23
+ interact: async () => {
24
+ throw new Error("unused");
25
+ },
26
+ read: async () => ({ engineId: id, turns: [], source: "outcome-only" }),
27
+ };
28
+ }
29
+
30
+ let tmpRoot: string;
31
+ let agentDir: string;
32
+
33
+ beforeEach(() => {
34
+ tmpRoot = fs.mkdtempSync(path.join(os.tmpdir(), "engine-discovery-"));
35
+ agentDir = path.join(tmpRoot, "agent");
36
+ clearEngines();
37
+ });
38
+
39
+ afterEach(() => {
40
+ clearEngines();
41
+ fs.rmSync(tmpRoot, { recursive: true, force: true });
42
+ });
43
+
44
+ describe("syncEnginesFile", () => {
45
+ it("注册表引擎列表落盘(v=1 + 注册序)", () => {
46
+ registerEngine("pi", () => stubEngine("pi"));
47
+ registerEngine("zcode", () => stubEngine("zcode"));
48
+ syncEnginesFile(agentDir);
49
+ const file = JSON.parse(fs.readFileSync(getEnginesFilePath(agentDir), "utf8")) as SubagentEnginesFile;
50
+ expect(file.v).toBe(1);
51
+ expect(file.engines).toEqual(["pi", "zcode"]);
52
+ expect(typeof file.updatedAt).toBe("number");
53
+ });
54
+
55
+ it("幂等:引擎清单未变时第二次零写(mtime 不动)", () => {
56
+ registerEngine("pi", () => stubEngine("pi"));
57
+ syncEnginesFile(agentDir);
58
+ const p = getEnginesFilePath(agentDir);
59
+ const statAfterFirst = fs.statSync(p);
60
+ syncEnginesFile(agentDir);
61
+ expect(fs.statSync(p).mtimeMs).toBe(statAfterFirst.mtimeMs);
62
+ });
63
+
64
+ it("新增引擎注册后内容变化触发重写", () => {
65
+ registerEngine("pi", () => stubEngine("pi"));
66
+ syncEnginesFile(agentDir);
67
+ registerEngine("acp", () => stubEngine("acp"));
68
+ syncEnginesFile(agentDir);
69
+ const file = JSON.parse(fs.readFileSync(getEnginesFilePath(agentDir), "utf8")) as SubagentEnginesFile;
70
+ expect(file.engines).toEqual(["pi", "acp"]);
71
+ });
72
+
73
+ it("现文件损坏(torn write 形态)时重建", () => {
74
+ registerEngine("pi", () => stubEngine("pi"));
75
+ const p = getEnginesFilePath(agentDir);
76
+ fs.mkdirSync(path.dirname(p), { recursive: true });
77
+ fs.writeFileSync(p, "{ torn", "utf8");
78
+ syncEnginesFile(agentDir);
79
+ expect(() => JSON.parse(fs.readFileSync(p, "utf8"))).not.toThrow();
80
+ });
81
+
82
+ it("agentDir 不存在时建目录写入;IO 异常吞掉不抛(fail-safe)", () => {
83
+ registerEngine("pi", () => stubEngine("pi"));
84
+ expect(() => syncEnginesFile(agentDir)).not.toThrow();
85
+ expect(fs.existsSync(getEnginesFilePath(agentDir))).toBe(true);
86
+ });
87
+ });