@zhushanwen/subagent-core 0.4.0 → 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (272) hide show
  1. package/dist/{chunk-4KN4TTG7.js → chunk-43ONBFZX.js} +1 -1
  2. package/dist/{chunk-X7SZ5HLQ.js → chunk-V3VHZ2VX.js} +20 -27
  3. package/dist/execution/engine/engines/zcode/constants.cjs +29 -41
  4. package/dist/execution/engine/engines/zcode/constants.d.cts +65 -53
  5. package/dist/execution/engine/engines/zcode/constants.d.ts +65 -53
  6. package/dist/execution/engine/engines/zcode/constants.js +17 -27
  7. package/dist/execution/engine/engines/zcode/reader.d.cts +2 -1
  8. package/dist/execution/engine/engines/zcode/reader.d.ts +2 -1
  9. package/dist/execution/engine/engines/zcode/reader.js +2 -2
  10. package/dist/index.cjs +16418 -14070
  11. package/dist/index.d.cts +5136 -4016
  12. package/dist/index.d.ts +5136 -4016
  13. package/dist/index.js +16343 -14048
  14. package/dist/{types-DpUO16pj.d.cts → types-Dv4QhSJ_.d.cts} +215 -112
  15. package/dist/{types-DpUO16pj.d.ts → types-Dv4QhSJ_.d.ts} +215 -112
  16. package/package.json +3 -3
  17. package/src/__tests__/agent-opts-resolver-schema-prompt.test.ts +0 -1
  18. package/src/__tests__/append-system-prompt-assembly.test.ts +8 -7
  19. package/src/__tests__/fr4-get-state-handshake.test.ts +2 -2
  20. package/src/__tests__/m2-append-content-probe.test.ts +0 -1
  21. package/src/__tests__/manifest-store.test.ts +10 -9
  22. package/src/__tests__/record-store-cache.test.ts +0 -2
  23. package/src/__tests__/record-store-index.test.ts +1 -2
  24. package/src/__tests__/review-fix-loop-script.test.ts +4 -2
  25. package/src/__tests__/review-fix-loop-utils.test.ts +10 -10
  26. package/src/__tests__/robustness-low-batch1.test.ts +6 -5
  27. package/src/__tests__/robustness-medium-batch1.test.ts +2 -2
  28. package/src/__tests__/robustness-medium-batch2.test.ts +17 -6
  29. package/src/__tests__/robustness-medium-batch4.test.ts +2 -2
  30. package/src/__tests__/session-runner.test.ts +1 -1
  31. package/src/core/__tests__/host-services.test.ts +2 -2
  32. package/src/core/__tests__/logger.test.ts +1 -1
  33. package/src/core/error-message.ts +9 -0
  34. package/src/core/host-services.ts +21 -5
  35. package/src/core/notify-ports.ts +18 -5
  36. package/src/execution/__tests__/agent-profile.test.ts +4 -4
  37. package/src/execution/__tests__/agent-registry.test.ts +370 -0
  38. package/src/execution/__tests__/agent-result-mapper.test.ts +18 -2
  39. package/src/execution/__tests__/agents-assembly.test.ts +2 -2
  40. package/src/execution/__tests__/ask-user-transit-e2e.test.ts +6 -6
  41. package/src/execution/__tests__/chat-engine-routing.test.ts +68 -33
  42. package/src/execution/__tests__/chatmode-first-round-closure-spawn.test.ts +4 -4
  43. package/src/execution/__tests__/cold-resurrect.test.ts +283 -0
  44. package/src/execution/__tests__/delivery-methods.test.ts +75 -62
  45. package/src/execution/__tests__/descendant-sweep-guards.test.ts +220 -0
  46. package/src/execution/__tests__/descendant-sweep.test.ts +6 -6
  47. package/src/execution/__tests__/dialog-queue.test.ts +2 -2
  48. package/src/execution/__tests__/engine-model-validation.test.ts +741 -0
  49. package/src/execution/__tests__/epipe-fallback.test.ts +29 -24
  50. package/src/execution/__tests__/execute-and-await-worktree.test.ts +21 -91
  51. package/src/execution/__tests__/execute-nesting.test.ts +34 -188
  52. package/src/execution/__tests__/execution-record.test.ts +217 -60
  53. package/src/execution/__tests__/execution-runtime-face.test.ts +5 -5
  54. package/src/execution/__tests__/explicit-agent-ref-guard.test.ts +3 -3
  55. package/src/execution/__tests__/finalize-record.test.ts +173 -17
  56. package/src/execution/__tests__/gc-timer.test.ts +55 -2
  57. package/src/execution/__tests__/get-record-for-action-restart.test.ts +12 -12
  58. package/src/execution/__tests__/get-state-handshake.test.ts +1 -1
  59. package/src/execution/__tests__/helpers/mock-extension-api.ts +1 -1
  60. package/src/execution/__tests__/helpers/session-runner-mocks.ts +113 -0
  61. package/src/execution/__tests__/helpers/spawn-mock.ts +150 -3
  62. package/src/execution/__tests__/helpers/subagent-service-mocks.ts +137 -0
  63. package/src/execution/__tests__/keep-alive-no-progress.test.ts +50 -148
  64. package/src/execution/__tests__/kill-all-escalation.test.ts +2 -2
  65. package/src/execution/__tests__/lifecycle-predicates.test.ts +1 -2
  66. package/src/execution/__tests__/manifest-parentid.test.ts +4 -4
  67. package/src/execution/__tests__/max-turns-to-watchdog-ms.test.ts +1 -1
  68. package/src/execution/__tests__/model-resolver.test.ts +2 -2
  69. package/src/execution/__tests__/nested-visibility-env-propagation.test.ts +23 -121
  70. package/src/execution/__tests__/nested-visibility.test.ts +2 -0
  71. package/src/execution/__tests__/notify-ledger.test.ts +973 -18
  72. package/src/execution/__tests__/output-collector.test.ts +117 -6
  73. package/src/execution/__tests__/pi-invocation.test.ts +1 -1
  74. package/src/execution/__tests__/record-store.test.ts +10 -9
  75. package/src/execution/__tests__/records-cwd-isolation.test.ts +2 -2
  76. package/src/execution/__tests__/recursive-visibility-baseline.test.ts +29 -132
  77. package/src/execution/__tests__/recursive-visibility-env.test.ts +55 -179
  78. package/src/execution/__tests__/rpc-mode.test.ts +2 -2
  79. package/src/execution/__tests__/run-and-finalize-anchoring.test.ts +387 -0
  80. package/src/execution/__tests__/run-and-finalize-chatmode.test.ts +15 -9
  81. package/src/execution/__tests__/run-spawn-chatmode-settled.test.ts +4 -4
  82. package/src/execution/__tests__/run-spawn-edges.test.ts +17 -15
  83. package/src/execution/__tests__/run-spawn-integration.test.ts +2 -2
  84. package/src/execution/__tests__/run-spawn-resume.test.ts +2 -2
  85. package/src/execution/__tests__/run-spawn-rpc-mode.test.ts +2 -2
  86. package/src/execution/__tests__/run-spawn-stdout-callback-throw.test.ts +4 -4
  87. package/src/execution/__tests__/service-kill-escalation.test.ts +7 -5
  88. package/src/execution/__tests__/session-context-resolver.test.ts +0 -2
  89. package/src/execution/__tests__/session-file-gc.test.ts +35 -0
  90. package/src/execution/__tests__/session-pending.test.ts +257 -14
  91. package/src/execution/__tests__/session-reconstructor.test.ts +366 -15
  92. package/src/execution/__tests__/session-runner-branch-cache-lru.test.ts +1 -1
  93. package/src/execution/__tests__/session-runner-close-prune.test.ts +2 -2
  94. package/src/execution/__tests__/session-runner-dispatch.test.ts +489 -0
  95. package/src/execution/__tests__/session-runner-epipe.test.ts +3 -3
  96. package/src/execution/__tests__/session-runner-heartbeat-idle-fallback.test.ts +5 -4
  97. package/src/execution/__tests__/session-runner-lifecycle-helpers.test.ts +290 -0
  98. package/src/execution/__tests__/session-runner-schema-env.test.ts +7 -5
  99. package/src/execution/__tests__/settled-watchdog.test.ts +282 -47
  100. package/src/execution/__tests__/spawn-args.test.ts +11 -10
  101. package/src/execution/__tests__/spawn-event-adapter-rpc.test.ts +26 -23
  102. package/src/execution/__tests__/spawn-event-adapter.test.ts +1 -1
  103. package/src/execution/__tests__/spawn-worktree-guidance.test.ts +5 -4
  104. package/src/execution/__tests__/spawned-children.test.ts +2 -1
  105. package/src/execution/__tests__/start-sync-model-guard.test.ts +4 -4
  106. package/src/execution/__tests__/stdin-writer.test.ts +6 -4
  107. package/src/execution/__tests__/stream-sink-retirement.test.ts +6 -6
  108. package/src/execution/__tests__/subagent-actions-core.test.ts +42 -22
  109. package/src/execution/__tests__/subagent-service-message-close.test.ts +29 -26
  110. package/src/execution/__tests__/subagent-service-multiproc-guard.test.ts +8 -8
  111. package/src/execution/__tests__/subagent-service-notify-gate.test.ts +27 -22
  112. package/src/execution/__tests__/subagent-service-parent-guard.test.ts +30 -29
  113. package/src/execution/__tests__/subagent-service-recovery-bounds.test.ts +45 -20
  114. package/src/execution/__tests__/subagent-service.test.ts +37 -30
  115. package/src/execution/__tests__/subprocess-agent-runner-routing.test.ts +88 -12
  116. package/src/execution/__tests__/subprocess-agent-runner-timeout.test.ts +75 -0
  117. package/src/execution/__tests__/subprocess-agent-runner.test.ts +22 -20
  118. package/src/execution/__tests__/temp-prompt.test.ts +1 -1
  119. package/src/execution/__tests__/timeout-integration.test.ts +6 -5
  120. package/src/execution/__tests__/turn-limiter-semantics.test.ts +1 -1
  121. package/src/execution/__tests__/turn-limiter.test.ts +1 -1
  122. package/src/execution/__tests__/ui-request-handler-factory.test.ts +12 -6
  123. package/src/execution/__tests__/ui-request-handler.test.ts +4 -3
  124. package/src/execution/__tests__/ui-request-queue.test.ts +142 -2
  125. package/src/execution/__tests__/worktree-manager.test.ts +14 -5
  126. package/src/execution/__tests__/worktree-pid-registration.integration.test.ts +4 -3
  127. package/src/execution/agent-registry.ts +74 -30
  128. package/src/execution/agent-result-mapper.ts +3 -0
  129. package/src/execution/cold-resurrect.ts +199 -0
  130. package/src/execution/engine/__tests__/common/capability-gate.test.ts +91 -0
  131. package/src/execution/engine/__tests__/common/data-dir.test.ts +1 -1
  132. package/src/execution/engine/__tests__/common/journal-wiring.test.ts +84 -0
  133. package/src/execution/engine/__tests__/common/kill-chain.test.ts +34 -7
  134. package/src/execution/engine/__tests__/common/persona-router.test.ts +4 -5
  135. package/src/execution/engine/__tests__/common/pool-manager.test.ts +211 -51
  136. package/src/execution/engine/__tests__/common/session-view-service-zcode-dbpath.test.ts +149 -0
  137. package/src/execution/engine/__tests__/common/session-view-service.test.ts +499 -0
  138. package/src/execution/engine/__tests__/conformance/contract.abort.test.ts +22 -112
  139. package/src/execution/engine/__tests__/conformance/contract.agent-events.test.ts +15 -28
  140. package/src/execution/engine/__tests__/conformance/engine-conformance.live.test.ts +12 -12
  141. package/src/execution/engine/__tests__/conformance/golden-replay.pi.test.ts +1 -1
  142. package/src/execution/engine/__tests__/conformance/golden-replay.zcode.test.ts +21 -82
  143. package/src/execution/engine/__tests__/conformance/zcode-appserver-harness.ts +4 -7
  144. package/src/execution/engine/__tests__/engine-discovery.test.ts +15 -3
  145. package/src/execution/engine/__tests__/model-prompt.test.ts +15 -3
  146. package/src/execution/engine/__tests__/registry.test.ts +4 -2
  147. package/src/execution/engine/__tests__/routing.test.ts +115 -6
  148. package/src/execution/engine/common/capability-gate.ts +92 -0
  149. package/src/execution/engine/common/journal-wiring.ts +97 -0
  150. package/src/execution/engine/common/kill-chain.ts +42 -10
  151. package/src/execution/engine/common/nesting-guard.ts +66 -0
  152. package/src/execution/engine/common/persona-router.ts +18 -8
  153. package/src/execution/engine/common/pool-manager.ts +332 -78
  154. package/src/execution/engine/common/session-view-service.ts +506 -0
  155. package/src/execution/engine/common/session-view-types.ts +125 -0
  156. package/src/execution/engine/engines/pi/__tests__/pi-engine.test.ts +174 -28
  157. package/src/execution/engine/engines/pi/__tests__/spawn-opts-direct.test.ts +257 -0
  158. package/src/execution/{argv-mirror.ts → engine/engines/pi/argv-mirror.ts} +43 -21
  159. package/src/execution/{get-state-handshake.ts → engine/engines/pi/get-state-handshake.ts} +1 -1
  160. package/src/execution/{output-collector.ts → engine/engines/pi/output-collector.ts} +108 -19
  161. package/src/execution/engine/engines/pi/pi-engine.ts +434 -38
  162. package/src/execution/{pi-invocation.ts → engine/engines/pi/pi-invocation.ts} +2 -2
  163. package/src/execution/engine/engines/pi/reader.ts +11 -0
  164. package/src/execution/engine/engines/pi/registration.ts +4 -2
  165. package/src/execution/{session-runner.ts → engine/engines/pi/session-runner.ts} +617 -412
  166. package/src/execution/{spawn-event-adapter.ts → engine/engines/pi/spawn-event-adapter.ts} +75 -34
  167. package/src/execution/{stdin-writer.ts → engine/engines/pi/stdin-writer.ts} +10 -9
  168. package/src/execution/{temp-prompt.ts → engine/engines/pi/temp-prompt.ts} +2 -2
  169. package/src/execution/{turn-limiter.ts → engine/engines/pi/turn-limiter.ts} +1 -1
  170. package/src/execution/engine/engines/zcode/__tests__/__fixtures__/fake-appserver.mjs +54 -27
  171. package/src/execution/engine/engines/zcode/__tests__/appserver-launcher.test.ts +321 -0
  172. package/src/execution/engine/engines/zcode/__tests__/connection.test.ts +13 -12
  173. package/src/execution/engine/engines/zcode/__tests__/parser.test.ts +7 -210
  174. package/src/execution/engine/engines/zcode/__tests__/preparer.test.ts +6 -110
  175. package/src/execution/engine/engines/zcode/__tests__/session-channel-dispose-harvest.test.ts +247 -0
  176. package/src/execution/engine/engines/zcode/__tests__/session-channel-turn-timers.test.ts +451 -0
  177. package/src/execution/engine/engines/zcode/__tests__/session-channel.test.ts +20 -8
  178. package/src/execution/engine/engines/zcode/__tests__/zcode-engine-appserver.test.ts +77 -110
  179. package/src/execution/engine/engines/zcode/__tests__/zcode-engine-degrade.test.ts +25 -319
  180. package/src/execution/engine/engines/zcode/__tests__/zcode-engine-dispose.test.ts +210 -0
  181. package/src/execution/engine/engines/zcode/__tests__/zcode-engine-retry.test.ts +353 -0
  182. package/src/execution/engine/engines/zcode/__tests__/zcode-engine-status.test.ts +282 -0
  183. package/src/execution/engine/engines/zcode/__tests__/zcode-engine-timeout.test.ts +372 -0
  184. package/src/execution/engine/engines/zcode/__tests__/zcode-engine.live.test.ts +48 -148
  185. package/src/execution/engine/engines/zcode/appserver-launcher.ts +167 -0
  186. package/src/execution/engine/engines/zcode/connection.ts +24 -11
  187. package/src/execution/engine/engines/zcode/constants.ts +75 -71
  188. package/src/execution/engine/engines/zcode/golden-sample.ts +8 -46
  189. package/src/execution/engine/engines/zcode/parser.ts +12 -346
  190. package/src/execution/engine/engines/zcode/preparer.ts +12 -143
  191. package/src/execution/engine/engines/zcode/session-channel.ts +351 -39
  192. package/src/execution/engine/engines/zcode/zcode-engine.ts +665 -760
  193. package/src/execution/engine/host-task-spec.ts +32 -36
  194. package/src/execution/engine/model-validation.ts +177 -0
  195. package/src/execution/engine/port.ts +24 -6
  196. package/src/execution/engine/routing.ts +75 -1
  197. package/src/execution/engine/types.ts +30 -103
  198. package/src/execution/execution-record.ts +222 -144
  199. package/src/execution/finalize-record.ts +131 -93
  200. package/src/execution/idle-gc.ts +28 -0
  201. package/src/execution/lifecycle-predicates.ts +1 -1
  202. package/src/execution/notify-host.ts +212 -0
  203. package/src/execution/record-store.ts +119 -73
  204. package/src/execution/round-settlement.ts +93 -0
  205. package/src/execution/session-file-gc.ts +96 -62
  206. package/src/execution/session-reconstructor.ts +256 -143
  207. package/src/execution/sessions-index.ts +117 -47
  208. package/src/execution/settled-watchdog.ts +264 -52
  209. package/src/execution/subagent-actions-core.ts +22 -22
  210. package/src/execution/subagent-service.ts +815 -963
  211. package/src/execution/subprocess-agent-runner.ts +144 -97
  212. package/src/execution/types.ts +9 -0
  213. package/src/execution/ui-request-handler-factory.ts +102 -74
  214. package/src/execution/ui-request-queue.ts +50 -19
  215. package/src/index.ts +442 -316
  216. package/src/orchestration/__tests__/agent-call-catch-fallback.test.ts +1 -1
  217. package/src/orchestration/__tests__/agent-call-stream.test.ts +2 -2
  218. package/src/orchestration/__tests__/args-validator.test.ts +14 -0
  219. package/src/orchestration/__tests__/error-recovery-rebuild-failure.test.ts +1 -1
  220. package/src/orchestration/__tests__/error-recovery-terminal-hardening.test.ts +11 -9
  221. package/src/orchestration/__tests__/execute-agent-call.test.ts +151 -11
  222. package/src/orchestration/__tests__/file-run-store.test.ts +7 -6
  223. package/src/orchestration/__tests__/launcher-nested-workflow.test.ts +2 -2
  224. package/src/orchestration/__tests__/lifecycle-abort-broadcast-signal.test.ts +1 -1
  225. package/src/orchestration/__tests__/lifecycle-runid-injection.test.ts +3 -3
  226. package/src/orchestration/__tests__/lifecycle.test.ts +7 -5
  227. package/src/orchestration/__tests__/non-cloneable-return-e2e.test.ts +1 -1
  228. package/src/orchestration/__tests__/run-snapshot.test.ts +8 -5
  229. package/src/orchestration/__tests__/script-lint.test.ts +17 -0
  230. package/src/orchestration/__tests__/test-mocks.ts +2 -0
  231. package/src/orchestration/__tests__/worker-exit-without-result.test.ts +7 -6
  232. package/src/orchestration/__tests__/worker-message-pump-finalize-run.test.ts +229 -0
  233. package/src/orchestration/__tests__/{error-recovery-handlers.test.ts → worker-message-pump-handlers.test.ts} +27 -19
  234. package/src/orchestration/__tests__/{error-recovery-postmessage-defense.test.ts → worker-message-pump-postmessage-defense.test.ts} +8 -8
  235. package/src/orchestration/__tests__/{error-recovery-serialize-failed-result.test.ts → worker-message-pump-serialize-failed-result.test.ts} +2 -2
  236. package/src/orchestration/__tests__/{error-recovery-workflow-call.test.ts → worker-message-pump-workflow-call.test.ts} +1 -1
  237. package/src/orchestration/__tests__/worker-pending-timeout-abort.test.ts +1 -1
  238. package/src/orchestration/__tests__/worker-script-builder.test.ts +1 -1
  239. package/src/orchestration/__tests__/workflow-nesting-e2e.test.ts +1 -1
  240. package/src/orchestration/agent-opts-resolver.ts +1 -1
  241. package/src/orchestration/args-validator.ts +53 -31
  242. package/src/orchestration/execute-agent-call.ts +19 -73
  243. package/src/orchestration/launcher.ts +128 -81
  244. package/src/orchestration/lifecycle.ts +43 -45
  245. package/src/orchestration/models/ports.ts +4 -4
  246. package/src/orchestration/models/run-runtime.ts +2 -2
  247. package/src/orchestration/models/trace.ts +1 -1
  248. package/src/orchestration/models/types.ts +111 -22
  249. package/src/orchestration/models/workflow-run.ts +1 -1
  250. package/src/orchestration/script-lint.ts +91 -58
  251. package/src/orchestration/{error-recovery.ts → worker-message-pump.ts} +157 -150
  252. package/src/shared/__tests__/meta-parser.test.ts +151 -0
  253. package/src/shared/__tests__/resource-discovery.test.ts +258 -215
  254. package/src/shared/atomic-write.ts +12 -9
  255. package/src/shared/meta-parser.ts +158 -84
  256. package/src/shared/model-ref.ts +15 -2
  257. package/src/shared/resource-discovery.ts +9 -183
  258. package/src/shared/schema-jsonify.ts +1 -1
  259. package/workflows/review-fix-loop-utils.cjs +151 -89
  260. package/workflows/review-fix-loop-utils.d.cts +287 -0
  261. package/src/execution/__tests__/execute-options-mapper.test.ts +0 -200
  262. package/src/execution/engine/engines/pi/__tests__/task-spec-mapper.test.ts +0 -164
  263. package/src/execution/engine/engines/pi/task-spec-mapper.ts +0 -100
  264. package/src/execution/engine/engines/zcode/__tests__/__fixtures__/zcode-golden-spawn.json +0 -39
  265. package/src/execution/engine/engines/zcode/__tests__/launcher.test.ts +0 -150
  266. package/src/execution/engine/engines/zcode/__tests__/preparer-appserver.test.ts +0 -387
  267. package/src/execution/engine/engines/zcode/__tests__/zcode-engine.test.ts +0 -710
  268. package/src/execution/engine/engines/zcode/appserver-home.ts +0 -442
  269. package/src/execution/engine/engines/zcode/appserver-probe.ts +0 -141
  270. package/src/execution/engine/engines/zcode/launcher.ts +0 -161
  271. package/src/execution/execute-options-mapper.ts +0 -115
  272. package/src/shared/__tests__/resource-discovery-manifest-cache.test.ts +0 -288
@@ -1,20 +1,19 @@
1
1
  // contract.agent-events.test.ts —— conformance C3(AgentEvent 不变量)+ 负例自证
2
2
  // (A12「套件有牙」:故意破坏一个不变量的样本必须被断言器检出——用「注入坏
3
- // parser」的形态:wrap 真实 synthesizeCoarseEvents 抽掉 message_end,断言套件转红;
3
+ // parser」的形态:构造抽掉 message_end / usage 半映射的坏样本流,断言套件转红;
4
4
  // 若断言器检不出破坏则本元测试失败)。
5
5
  //
6
- // [R6] 双模式口径:zcode 引擎 conformance 覆盖两种通道形态——spawn 钉扎(golden
7
- // stdout 语料 + coarse 合成,下方原有用例)与 app-server 常驻(fake 常驻引擎全链 +
8
- // stream 口径,文件末新增 describe;app-server 设计 §3.4 不变量 1:text_delta 拼接 ==
9
- // read 全文、终态唯一 turn.terminal 权威、message_end.usage 完整、tool 事件不合成
10
- // ——granularity 声明与实际流出一致)。
6
+ // 2026-09 重构(共享宿主 HOME):CLI spawn 链删除后引擎只剩 app-server 常驻链,
7
+ // C3 的引擎全链覆盖只走 app-server 形态(fake 常驻引擎 + stream 口径,文件末
8
+ // describe;app-server 设计 §3.4 不变量 1:text_delta 拼接 == read 全文、终态唯一
9
+ // turn.terminal 权威、message_end.usage 完整、tool 事件不合成——granularity 声明
10
+ // 与实际流出一致)。断言器行为正反例与负例自证均为手工构造样本,不依赖引擎实现。
11
11
 
12
12
  import { describe, expect, it } from "vitest";
13
13
 
14
14
  import type { AgentEvent } from "../../../types.ts";
15
- import { ZCODE_APPSERVER_GOLDEN, ZCODE_GOLDEN_STDOUT } from "../../engines/zcode/golden-sample.ts";
16
- import { ZCODE_APPSERVER_POOL_KEY } from "../../engines/zcode/constants.ts";
17
- import { parseZcodeTerminal, synthesizeCoarseEvents } from "../../engines/zcode/parser.ts";
15
+ import { ZCODE_APPSERVER_GOLDEN } from "../../engines/zcode/golden-sample.ts";
16
+ import { ZCODE_SHARED_POOL_KEY } from "../../engines/zcode/constants.ts";
18
17
  import {
19
18
  goldenReadFullText,
20
19
  makeAppserverHarness,
@@ -24,18 +23,7 @@ import {
24
23
  checkAgentEventInvariants,
25
24
  } from "./agent-event-invariants.ts";
26
25
 
27
- /** golden 实录的 coarse 事件流(真实 parser + 真实合成器的产出)。 */
28
- function goldenCoarseEvents(): AgentEvent[] {
29
- const terminal = parseZcodeTerminal(ZCODE_GOLDEN_STDOUT);
30
- if (!terminal.ok) throw new Error("golden 样本解析失败");
31
- return synthesizeCoarseEvents(terminal.payload.response, terminal.payload.usage);
32
- }
33
-
34
- describe("conformance C3:AgentEvent 不变量(真实 parser 产出全绿)", () => {
35
- it("zcode golden 合成事件满足五条不变量(coarse 口径)", () => {
36
- assertAgentEventInvariants(goldenCoarseEvents(), { granularity: "coarse" });
37
- });
38
-
26
+ describe("conformance C3:AgentEvent 不变量(断言器正反例,手工构造样本)", () => {
39
27
  it("不变量 1:终态唯一——turn_end 后不得再出现非 error 事件", () => {
40
28
  const events: AgentEvent[] = [{ type: "message_end" }, { type: "turn_end" }, { type: "text_delta", delta: "x" }];
41
29
  const findings = checkAgentEventInvariants(events, { granularity: "coarse" });
@@ -113,8 +101,7 @@ describe("conformance 负例自证(A12:套件有牙)", () => {
113
101
 
114
102
  // ============================================================
115
103
  // [R6] C3 app-server 口径:fake 常驻引擎全链(eventGranularity=stream 形态)
116
- // —— spawn 钉扎用例见上方 coarse describe,两形态各自覆盖不合并(D2 降级链保留
117
- // spawn 路径,双通道都是生产可达形态)。
104
+ // —— 2026-09 重构后唯一生产形态(CLI spawn 降级链已删,无钉扎/降级双通道)。
118
105
  // ============================================================
119
106
 
120
107
  describe("conformance C3:zcode app-server 常驻通道(stream 口径,fake 常驻全链)", () => {
@@ -123,7 +110,7 @@ describe("conformance C3:zcode app-server 常驻通道(stream 口径,fake
123
110
  try {
124
111
  const events: AgentEvent[] = [];
125
112
  const { outcome } = await h.engine.run(
126
- { task: "做点什么", slug: "c3-appserver", model: "conformance-provider/m1", cwd: h.workspace },
113
+ { prompt: "做点什么", description: "c3-appserver", model: "conformance-provider/m1", cwd: h.workspace },
127
114
  { taskId: "sa-c3-appserver", poolKey: "", onEvent: (e) => events.push(e) },
128
115
  );
129
116
 
@@ -158,7 +145,7 @@ describe("conformance C3:zcode app-server 常驻通道(stream 口径,fake
158
145
  try {
159
146
  const events: AgentEvent[] = [];
160
147
  const { outcome } = await h.engine.run(
161
- { task: "做点什么", slug: "c3-no-terminal", model: "conformance-provider/m1", cwd: h.workspace },
148
+ { prompt: "做点什么", description: "c3-no-terminal", model: "conformance-provider/m1", cwd: h.workspace },
162
149
  { taskId: "sa-c3-no-terminal", poolKey: "", onEvent: (e) => events.push(e) },
163
150
  );
164
151
  expect(outcome.error).toBeUndefined();
@@ -169,14 +156,14 @@ describe("conformance C3:zcode app-server 常驻通道(stream 口径,fake
169
156
  }
170
157
  }, 20_000);
171
158
 
172
- it("handle 锚定常驻 HOME(poolKey=home-appserver,①级读取钥匙随 handle 走)", async () => {
159
+ it("handle 携带 shared poolKey(共享宿主 HOME 的 journal 分组锚点,①级读取钥匙随 handle 走)", async () => {
173
160
  const h = makeAppserverHarness();
174
161
  try {
175
162
  const { handle, outcome } = await h.engine.run(
176
- { task: "做点什么", slug: "c3-anchor", model: "conformance-provider/m1", cwd: h.workspace },
163
+ { prompt: "做点什么", description: "c3-anchor", model: "conformance-provider/m1", cwd: h.workspace },
177
164
  { taskId: "sa-c3-anchor", poolKey: "" },
178
165
  );
179
- expect(handle.data.poolKey).toBe(ZCODE_APPSERVER_POOL_KEY);
166
+ expect(handle.data.poolKey).toBe(ZCODE_SHARED_POOL_KEY);
180
167
  expect(handle.data.sessionRef["sessionId"]).toBe(outcome.sessionId);
181
168
  // golden 锚点:sessionId 来自 session.sessionId(projection.sessionId 恒 "unknown" 勿用)
182
169
  expect(JSON.parse(ZCODE_APPSERVER_GOLDEN.createResponse).session.sessionId).toBe("sess_golden_r3_01");
@@ -25,10 +25,11 @@ import { describe, expect, it } from "vitest";
25
25
  import { getSubagentService } from "../../../subagent-service.ts";
26
26
  import { ZcodeEngine } from "../../engines/zcode/zcode-engine.ts";
27
27
  import { createPiEngine } from "../../engines/pi/registration.ts";
28
+ import type { PiEngineService } from "../../engines/pi/pi-engine.ts";
28
29
  import type { RunContext } from "../../port.ts";
29
30
  import type { AgentEvent } from "../../../types.ts";
30
- import type { AgentTaskSpec } from "../../types.ts";
31
- import { getPiInvocation } from "../../../pi-invocation.ts";
31
+ import type { AgentCallOpts } from "../../../../orchestration/models/types.ts";
32
+ import { getPiInvocation } from "../../engines/pi/pi-invocation.ts";
32
33
  import {
33
34
  RELAY_ENV_NODE,
34
35
  RELAY_ENV_RECORD_ID,
@@ -51,8 +52,8 @@ describe.skipIf(!LIVE)("conformance run 层(真实 spawn,手动门)", () =
51
52
  testCtx.skip("SubagentService 未装配(需在真实会话进程内运行)");
52
53
  return;
53
54
  }
54
- const engine = createPiEngine(() => service);
55
- const task: AgentTaskSpec = { task: "Reply with the single word: ok", slug: "live-c2" };
55
+ const engine = createPiEngine(() => service as unknown as PiEngineService);
56
+ const task: AgentCallOpts = { prompt: "Reply with the single word: ok", description: "live-c2" };
56
57
  const ctx: RunContext = { taskId: "sa-live-pi-c2", poolKey: "shared" };
57
58
  const { outcome } = await engine.run(task, ctx);
58
59
  expect(outcome.error).toBeUndefined();
@@ -68,9 +69,9 @@ describe.skipIf(!LIVE)("conformance run 层(真实 spawn,手动门)", () =
68
69
  }, 60_000);
69
70
 
70
71
  // [R6] 常驻通道的 conformance run 层(RA8「C1-C8 适配后全绿」的 live 面):
71
- // XYZ_ZCODE_MODE=appserver 定向(不探不降)跑最小任务——C2 outcome + C3 stream
72
- // 不变量(app-server 设计 §3.4 不变量 1)。更深断言(schema/abort/进程锚定)由
73
- // engines/zcode/__tests__/zcode-engine.live.test.ts 的 [R6] app-server 段承载。
72
+ // 跑最小任务——C2 outcome + C3 stream 不变量(app-server 设计 §3.4 不变量 1)。
73
+ // 2026-09 起单一 app-server 形态即缺省路径(无模式钉扎 env);更深断言(schema/
74
+ // abort/进程锚定)由 engines/zcode/__tests__/zcode-engine.live.test.ts 承载。
74
75
  it("zcode:app-server 常驻通道 run 全链(C2 outcome 无 error + C3 stream 事件不变量)", async (testCtx) => {
75
76
  const model = process.env["ZCODE_E2E_MODEL"];
76
77
  if (model === undefined || model === "") {
@@ -79,11 +80,10 @@ describe.skipIf(!LIVE)("conformance run 层(真实 spawn,手动门)", () =
79
80
  }
80
81
  const engine = new ZcodeEngine({
81
82
  engineDataDir: () => "/tmp/zcode-conformance-live-appserver",
82
- processEnv: { ...process.env, XYZ_ZCODE_MODE: "appserver" },
83
83
  });
84
84
  const events: AgentEvent[] = [];
85
85
  const { outcome } = await engine.run(
86
- { task: "Reply with the single word: ok", slug: "live-appserver-c2", model, cwd: "/tmp" },
86
+ { prompt: "Reply with the single word: ok", description: "live-appserver-c2", model, cwd: "/tmp" },
87
87
  { taskId: "sa-live-zcode-appserver", poolKey: "", onEvent: (e) => events.push(e) },
88
88
  );
89
89
  expect(outcome.error).toBeUndefined();
@@ -200,9 +200,9 @@ describe.skipIf(!LIVE)("conformance relay 变体(经代理 spawn 全链,手
200
200
  }
201
201
 
202
202
  try {
203
- const engine = createPiEngine(() => service);
203
+ const engine = createPiEngine(() => service as unknown as PiEngineService);
204
204
  const events: AgentEvent[] = [];
205
- const task: AgentTaskSpec = { task: "Reply with the single word: ok", slug: "live-relay-c2" };
205
+ const task: AgentCallOpts = { prompt: "Reply with the single word: ok", description: "live-relay-c2" };
206
206
  const ctx: RunContext = {
207
207
  taskId: "sa-live-pi-relay",
208
208
  poolKey: "shared",
@@ -221,7 +221,7 @@ describe.skipIf(!LIVE)("conformance relay 变体(经代理 spawn 全链,手
221
221
  else process.env[key] = savedEnv[key];
222
222
  }
223
223
  await new Promise<void>((resolve) => server.close(() => resolve()));
224
- fs.rmSync(tmpDir, { recursive: true, force: true });
224
+ fs.rmSync(tmpDir, { recursive: true, force: true, maxRetries: 5, retryDelay: 20 });
225
225
  }
226
226
  });
227
227
  });
@@ -15,7 +15,7 @@ import { describe, expect, it } from "vitest";
15
15
 
16
16
  import type { AgentEvent } from "../../../types.ts";
17
17
  import { JournalWriter, replayJournal } from "../../common/event-journal.ts";
18
- import { parseSpawnLine } from "../../../spawn-event-adapter.ts";
18
+ import { parseSpawnLine } from "../../engines/pi/spawn-event-adapter.ts";
19
19
  import { assertAgentEventInvariants } from "./agent-event-invariants.ts";
20
20
 
21
21
  interface PiGoldenFile {
@@ -1,13 +1,13 @@
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
- // 更新样本必须同步两处,探针的干跑校验消费内嵌副本,两处不一致 = 探针在测旧契约)。
1
+ // golden-replay.zcode.test.ts —— zcode 引擎 golden 回放层(conformance 免 LLM 默认
2
+ // CI 层)。语料 = app-server NDJSON 帧序列(附录 A.2 任务生命周期帧序;采集 SSOT
3
+ // = engines/zcode/__tests__/__fixtures__/zcode-golden-appserver.json)——双副本 diff
4
+ // 校验(fixture 与 golden-sample.ts 内嵌副本一致,防漂移:更新样本必须同步两处,
5
+ // 探针的干跑校验消费内嵌副本,两处不一致 = 探针在测旧契约)+ 帧形态不变量断言
6
+ // (A8「golden 帧序列语料 diff 通过」的本仓段)。
6
7
  //
7
- // [R6] 双语料并存:① spawn stdout(上方,spawn 降级路径 + probe 干跑的回归语料——
8
- // 生产消费方存在故保留,去留决策见 impl-plan R6 偏差登记)② app-server 帧序列
9
- // (下方新增,常驻通道主力语料——双副本 diff 自 session-channel 测试接管到 conformance
10
- // 回放层,帧形态不变量在此断言;A8「golden 帧序列语料 diff 通过」的本仓段)。
8
+ // 2026-09 重构(共享宿主 HOME):CLI spawn 链删除后 spawn stdout 语料(单 JSON
9
+ // 样本及其 fixture)随 spawn 回归一并删除,本层仅保留 app-server 帧序列语料
10
+ // (常驻通道唯一生产形态)。
11
11
 
12
12
  import { readFileSync } from "node:fs";
13
13
  import { dirname, join } from "node:path";
@@ -15,86 +15,21 @@ import { fileURLToPath } from "node:url";
15
15
 
16
16
  import { describe, expect, it } from "vitest";
17
17
 
18
- import { ZCODE_APPSERVER_GOLDEN, ZCODE_GOLDEN_STDOUT } from "../../engines/zcode/golden-sample.ts";
19
- import {
20
- mapZcodeOutcomeUsage,
21
- parseZcodeTerminal,
22
- synthesizeCoarseEvents,
23
- } from "../../engines/zcode/parser.ts";
24
- import { assertAgentEventInvariants } from "./agent-event-invariants.ts";
18
+ import { ZCODE_APPSERVER_GOLDEN } from "../../engines/zcode/golden-sample.ts";
25
19
 
26
20
  // conformance 目录(engine/__tests__/conformance)→ engine 根的相对定位
27
- const fixturePath = join(
21
+ const appserverFixturePath = join(
28
22
  dirname(fileURLToPath(import.meta.url)),
29
23
  "..", "..", // conformance → __tests__ → engine
30
- "engines", "zcode", "__tests__", "__fixtures__", "zcode-golden-spawn.json",
24
+ "engines", "zcode", "__tests__", "__fixtures__", "zcode-golden-appserver.json",
31
25
  );
32
26
 
33
- interface ZcodeGoldenFile {
34
- _meta: { engineVersion: string; exitCode: number };
35
- stdoutRaw: string;
36
- }
37
-
38
- const fixture = JSON.parse(readFileSync(fixturePath, "utf8")) as ZcodeGoldenFile;
39
-
40
- // [R6] app-server 帧序列语料 fixture(采集 SSOT——双副本 diff 的另一副本)
41
- const appserverFixturePath = join(dirname(fixturePath), "zcode-golden-appserver.json");
42
27
  interface AppServerGoldenFile {
43
28
  _meta: Record<string, unknown>;
44
29
  frames: { createResponse: string; pushStream: string[]; terminal: string[]; readResponse: string };
45
30
  }
46
31
  const appserverFixture = JSON.parse(readFileSync(appserverFixturePath, "utf8")) as AppServerGoldenFile;
47
32
 
48
- describe("zcode golden 回放(conformance C3,免 LLM/免二进制)", () => {
49
- it("双副本 diff:golden-sample.ts 内嵌副本 === fixture stdoutRaw(防漂移,A12)", () => {
50
- expect(ZCODE_GOLDEN_STDOUT).toBe(fixture.stdoutRaw);
51
- });
52
-
53
- it("parser 对实录样本回归:sessionId/response/usage 形状完整(探针干跑同源断言)", () => {
54
- const terminal = parseZcodeTerminal(fixture.stdoutRaw);
55
- expect(terminal.ok).toBe(true);
56
- if (!terminal.ok) return;
57
- expect(terminal.payload.sessionId).toMatch(/^sess_/);
58
- expect(terminal.payload.response).toBe("ok");
59
- expect(terminal.payload.usage).toEqual({ input: 12599, output: 17, cacheRead: 512, cacheWrite: 0 });
60
- // 终态层 usage(orchestration 版):cost 显式 0(无来源不给残缺)、contextTokens 取 projection
61
- expect(terminal.payload.outcomeUsage).toEqual({
62
- input: 12599,
63
- output: 17,
64
- cacheRead: 512,
65
- cacheWrite: 0,
66
- cost: 0,
67
- contextTokens: 12616,
68
- turns: 1,
69
- });
70
- });
71
-
72
- it("coarse 事件合成满足产出不变量(turn_end 最后、其前必有 message_end、usage 完整)", () => {
73
- const terminal = parseZcodeTerminal(fixture.stdoutRaw);
74
- if (!terminal.ok) throw new Error("golden 样本解析失败(上一用例应已转红)");
75
- const events = synthesizeCoarseEvents(terminal.payload.response, terminal.payload.usage);
76
- assertAgentEventInvariants(events, { granularity: "coarse" });
77
- expect(events.map((e) => e.type)).toEqual(["message_end", "turn_end"]);
78
- });
79
-
80
- it("usage 无来源样本:mapZcodeOutcomeUsage 显式缺省(不给残缺对象,不变量 2 的输入面)", () => {
81
- expect(mapZcodeOutcomeUsage(undefined, undefined)).toBeUndefined();
82
- expect(mapZcodeOutcomeUsage({ inputTokens: 1 }, undefined)).toEqual({
83
- input: 1,
84
- output: 0,
85
- cacheRead: 0,
86
- cacheWrite: 0,
87
- cost: 0,
88
- contextTokens: 0,
89
- turns: 1,
90
- });
91
- });
92
- });
93
-
94
- // ============================================================
95
- // [R6] app-server 帧序列语料回放(常驻通道主力语料,conformance 层接管双副本 diff)
96
- // ============================================================
97
-
98
33
  describe("zcode app-server golden 回放(conformance C3 帧形态,免 LLM/免二进制)", () => {
99
34
  it("双副本 diff:golden-sample.ts 内嵌副本 === fixture frames(防漂移,A8 本仓段)", () => {
100
35
  expect(ZCODE_APPSERVER_GOLDEN.createResponse).toBe(appserverFixture.frames.createResponse);
@@ -141,7 +76,7 @@ describe("zcode app-server golden 回放(conformance C3 帧形态,免 LLM/
141
76
  expect(readText).toBe(deltaJoined); // text_delta 拼接 == 收尾帧 == read 全文
142
77
  });
143
78
 
144
- it("终态权威帧:turn.terminal 存在且先于收尾帧;usage 与 spawn golden 同值(漂移对照锚点)", () => {
79
+ it("终态权威帧:turn.terminal 存在且先于收尾帧;usage 与 read step-finish tokens 同源(漂移对照锚点)", () => {
145
80
  const terminal = ZCODE_APPSERVER_GOLDEN.terminal.map((l) => JSON.parse(l) as {
146
81
  params?: { kind?: string; status?: string; payload?: { usage?: { inputTokens: number; outputTokens: number } } };
147
82
  });
@@ -151,11 +86,15 @@ describe("zcode app-server golden 回放(conformance C3 帧形态,免 LLM/
151
86
  expect(terminal[terminalFrameIdx]?.params?.status).toBe("success");
152
87
  expect(terminalFrameIdx).toBeLessThan(closingFrameIdx);
153
88
 
154
- const spawnUsage = parseZcodeTerminal(ZCODE_GOLDEN_STDOUT);
155
- if (!spawnUsage.ok) throw new Error("spawn golden 样本解析失败");
89
+ // usage 跨源对照锚点:收尾帧 usage == read 应答 step-finish tokens(同一轮真实
90
+ // 计费面,两源必须一致;旧 spawn golden 对照随语料删除)
91
+ const read = JSON.parse(ZCODE_APPSERVER_GOLDEN.readResponse) as {
92
+ messages: Array<{ info: { role: string }; parts: Array<{ type: string; tokens?: { input: number; output: number } }> }>;
93
+ };
94
+ const stepFinish = (read.messages.at(-1)?.parts ?? []).find((p) => p.type === "step-finish");
156
95
  expect(terminal[closingFrameIdx]?.params?.payload?.usage).toEqual({
157
- inputTokens: spawnUsage.payload.usage.input,
158
- outputTokens: spawnUsage.payload.usage.output,
96
+ inputTokens: stepFinish?.tokens?.input,
97
+ outputTokens: stepFinish?.tokens?.output,
159
98
  });
160
99
  });
161
100
  });
@@ -38,7 +38,7 @@ export interface AppserverHarness {
38
38
  dispose(): Promise<void>;
39
39
  }
40
40
 
41
- /** 组装连到 fake-appserver 的常驻引擎(XYZ_ZCODE_MODE=appserver 定向——不探不降)。 */
41
+ /** 组装连到 fake-appserver 的常驻引擎(单一 app-server 形态——2026-09 起无模式分派)。 */
42
42
  export function makeAppserverHarness(opts: AppserverHarnessOptions = {}): AppserverHarness {
43
43
  const tmpRoot = fs.mkdtempSync(path.join(os.tmpdir(), "zcode-conformance-appserver-"));
44
44
  const dataDir = path.join(tmpRoot, "data");
@@ -78,9 +78,6 @@ export function makeAppserverHarness(opts: AppserverHarnessOptions = {}): Appser
78
78
  sources: { v2ConfigPath: v2Path },
79
79
  processEnv: {
80
80
  PATH: process.env.PATH ?? "",
81
- // 定向 appserver(R5 D2①):不探不降——conformance 测常驻路径本体,缺省路径的
82
- // 探针门控/降级链归 zcode-engine-degrade.test.ts
83
- XYZ_ZCODE_MODE: "appserver",
84
81
  FAKE_STATE_FILE: stateFile,
85
82
  FAKE_SESSION_SCENARIO: scenarioFile,
86
83
  },
@@ -92,7 +89,7 @@ export function makeAppserverHarness(opts: AppserverHarnessOptions = {}): Appser
92
89
  dataDir,
93
90
  dispose: () =>
94
91
  engine.dispose().finally(() => {
95
- fs.rmSync(tmpRoot, { recursive: true, force: true });
92
+ fs.rmSync(tmpRoot, { recursive: true, force: true, maxRetries: 5, retryDelay: 20 });
96
93
  }),
97
94
  };
98
95
  }
@@ -114,8 +111,8 @@ export function readFakeState(file: string): Array<Record<string, unknown>> {
114
111
  /** fake 收到的客户端帧方法名序列(abort 链「stop 先于杀链」断言面)。 */
115
112
  export function sentMethodNames(stateFile: string): string[] {
116
113
  return readFakeState(stateFile)
117
- .map((e) => e["frame"])
118
- .filter((f): f is Record<string, unknown> => typeof f === "object" && f !== null && typeof f["method"] === "string")
114
+ .map((e) => (e as Record<string, unknown>)["frame"])
115
+ .filter((f): f is Record<string, unknown> => typeof f === "object" && f !== null && typeof (f as Record<string, unknown>)["method"] === "string")
119
116
  .map((f) => f["method"] as string);
120
117
  }
121
118
 
@@ -15,8 +15,20 @@ import { clearEngines, registerEngine } from "../registry.ts";
15
15
  function stubEngine(id: string): EnginePort {
16
16
  return {
17
17
  id,
18
- capabilities: () => ({ conversation: "unsupported", steer: "unsupported", sandbox: "none" }),
19
- probe: async () => ({ ok: true, engineVersion: "test" }),
18
+ capabilities: () => ({
19
+ schemaEnforcement: "emulated",
20
+ steer: "unsupported",
21
+ conversation: "unsupported",
22
+ personaInjection: "prompt",
23
+ eventGranularity: "coarse",
24
+ sandbox: "none",
25
+ sessionRead: "outcome-only",
26
+ resume: "unsupported",
27
+ interrupt: "kill-only",
28
+ permissionMode: "ignored",
29
+ maxTurns: false,
30
+ }),
31
+ probe: async () => ({ ok: true, engineVersion: "test", checks: [] }),
20
32
  run: async () => {
21
33
  throw new Error("unused");
22
34
  },
@@ -38,7 +50,7 @@ beforeEach(() => {
38
50
 
39
51
  afterEach(() => {
40
52
  clearEngines();
41
- fs.rmSync(tmpRoot, { recursive: true, force: true });
53
+ fs.rmSync(tmpRoot, { recursive: true, force: true, maxRetries: 5, retryDelay: 20 });
42
54
  });
43
55
 
44
56
  describe("syncEnginesFile", () => {
@@ -16,8 +16,20 @@ import { buildEngineModelsPromptAppend, buildSubagentEngineSection } from "../mo
16
16
  function fakeEngine(id: string, models: Array<{ id: string; name?: string }> | null): EnginePort {
17
17
  return {
18
18
  id,
19
- capabilities: () => ({ conversation: "unsupported", steer: "unsupported", sandbox: "none" }),
20
- probe: async () => ({ ok: true, engineVersion: "test" }),
19
+ capabilities: () => ({
20
+ schemaEnforcement: "emulated",
21
+ steer: "unsupported",
22
+ conversation: "unsupported",
23
+ personaInjection: "prompt",
24
+ eventGranularity: "coarse",
25
+ sandbox: "none",
26
+ sessionRead: "outcome-only",
27
+ resume: "unsupported",
28
+ interrupt: "kill-only",
29
+ permissionMode: "ignored",
30
+ maxTurns: false,
31
+ }),
32
+ probe: async () => ({ ok: true, engineVersion: "test", checks: [] }),
21
33
  run: async () => {
22
34
  throw new Error("not used in this test");
23
35
  },
@@ -38,7 +50,7 @@ beforeEach(() => {
38
50
 
39
51
  afterEach(() => {
40
52
  clearEngines();
41
- fs.rmSync(tmpRoot, { recursive: true, force: true });
53
+ fs.rmSync(tmpRoot, { recursive: true, force: true, maxRetries: 5, retryDelay: 20 });
42
54
  });
43
55
 
44
56
  describe("buildEngineModelsPromptAppend(defaultEngine 开关语义)", () => {
@@ -17,7 +17,8 @@ import {
17
17
  listEngines,
18
18
  registerEngine,
19
19
  } from "../registry.ts";
20
- import type { AgentTaskSpec, SessionView } from "../types.ts";
20
+ import type { SessionView } from "../types.ts";
21
+ import type { AgentCallOpts } from "../../../orchestration/models/types.ts";
21
22
 
22
23
  /** 最小可运行假引擎(完整实现 EnginePort 五面——不 cast,防接口漂移失检)。 */
23
24
  function makeFakeEngine(id: string): EnginePort {
@@ -34,10 +35,11 @@ function makeFakeEngine(id: string): EnginePort {
34
35
  resume: "unsupported",
35
36
  interrupt: "kill-only",
36
37
  permissionMode: "fixed",
38
+ maxTurns: false,
37
39
  }),
38
40
  probe: () =>
39
41
  Promise.resolve({ ok: true, engineVersion: "0.0.0-test", checks: [{ name: "stub", ok: true }] }),
40
- run: (_task: AgentTaskSpec, _ctx: RunContext) =>
42
+ run: (_task: AgentCallOpts, _ctx: RunContext) =>
41
43
  Promise.reject(new Error("fake engine: run not implemented")),
42
44
  interact: () => Promise.resolve({ ok: false, code: "engine_capability_unsupported", message: "stub" }),
43
45
  read: (_handle: Parameters<EnginePort["read"]>[0]): Promise<SessionView> =>
@@ -6,8 +6,10 @@ import { describe, expect, it } from "vitest";
6
6
  import type { EnginePort, RunContext } from "../port.ts";
7
7
  import { DEFAULT_ENGINE_ID, EngineNotFoundError } from "../registry.ts";
8
8
  import { EngineError } from "../common/errors.ts";
9
- import type { AgentTaskSpec, ProbeReport, SessionView } from "../types.ts";
10
- import { resolveEngineRouting, routeEngine, type EngineRouteOptions } from "../routing.ts";
9
+ import type { ProbeReport, SessionView } from "../types.ts";
10
+ import type { AgentCallOpts } from "../../../orchestration/models/types.ts";
11
+ import { resolveEngineRouting, routeEngine, routeEngineForHost, type EngineRouteOptions, type HostRouteOptions } from "../routing.ts";
12
+ import type { EngineRouteResult } from "../routing.ts";
11
13
 
12
14
  /** 最小可运行假引擎(probe 结果可注入)。 */
13
15
  function makeFakeEngine(id: string, probeOk: boolean): EnginePort {
@@ -24,6 +26,7 @@ function makeFakeEngine(id: string, probeOk: boolean): EnginePort {
24
26
  resume: "cold",
25
27
  interrupt: "kill-only",
26
28
  permissionMode: "native",
29
+ maxTurns: false,
27
30
  }),
28
31
  probe: () =>
29
32
  Promise.resolve(
@@ -36,7 +39,7 @@ function makeFakeEngine(id: string, probeOk: boolean): EnginePort {
36
39
  error: { code: "engine_probe_failed", recovery: "reinstall the engine binary and retry the probe" },
37
40
  } satisfies ProbeReport,
38
41
  ),
39
- run: (_t: AgentTaskSpec, _c: RunContext) => Promise.reject(new Error("fake: run not implemented")),
42
+ run: (_t: AgentCallOpts, _c: RunContext) => Promise.reject(new Error("fake: run not implemented")),
40
43
  interact: () => Promise.resolve({ ok: false, code: "engine_capability_unsupported", message: "stub" }),
41
44
  read: (_h: Parameters<EnginePort["read"]>[0]): Promise<SessionView> =>
42
45
  Promise.resolve({ engineId: id, turns: [], source: "outcome-only" }),
@@ -162,9 +165,10 @@ describe("routeEngine:路由 + 探针 + 守卫编排(验收 1/2)", () => {
162
165
  expect((err as EngineError).recovery).toContain("retry the probe");
163
166
  });
164
167
 
165
- it("守卫 b(首期与 a 合流):AgentTaskSpec.requires 形状预留存在且不参与首期判定", async () => {
166
- // 形状预留的类型面由 typecheck 守护;行为面:frontmatter 指定(非 call)+ 无 model
167
- // 仍走 fallback(守卫 b 独立生效留给 requires 下钻,见 types.ts 注释)
168
+ it("守卫 b(首期与 a 合流):能力依赖声明无独立载体,frontmatter 指定(非 call)不阻断 fallback", async () => {
169
+ // [D6 合流] AgentTaskSpec.requires 形状预留已随任务形状合流裁撤(无生产写入方,
170
+ // AgentCallOpts 类型注释);行为面保持:frontmatter 指定(非 call)+ model
171
+ // → 仍走 fallback(能力依赖声明的独立载体留给将来下钻)
168
172
  const { opts } = makeRoute({ zcodeProbeOk: false, routing: { agentEngine: "zcode" } });
169
173
  const result = await routeEngine(opts);
170
174
  expect(result.engineFallback).toBeDefined();
@@ -229,3 +233,108 @@ describe("routeEngine:路由 + 探针 + 守卫编排(验收 1/2)", () => {
229
233
  expect(result.engineFallback).toEqual({ from: "zcode", reason: "engine_probe_failed" });
230
234
  });
231
235
  });
236
+
237
+ // ============================================================
238
+ // routeEngineForHost(D3-② 路由单点:宿主两调用点的统一编排)
239
+ // ============================================================
240
+
241
+ describe("routeEngineForHost:宿主统一路由(D3-②)", () => {
242
+ /** 本地 pi 引擎实例替身(chat 域 = chatPiEngine / workflow 域 = SAR per-session DI)。 */
243
+ function makeLocalPi(): EnginePort {
244
+ return makeFakeEngine("local-pi", true);
245
+ }
246
+
247
+ /** 装配 host 路由参数(registry 面复用 makeRoute 的注入件)。 */
248
+ function makeHostRoute(overrides?: Parameters<typeof makeRoute>[0]): {
249
+ hostOpts: HostRouteOptions;
250
+ probeCalls: string[];
251
+ engines: Map<string, EnginePort>;
252
+ piEngine: EnginePort;
253
+ } {
254
+ const { opts, probeCalls, engines } = makeRoute(overrides);
255
+ const piEngine = makeLocalPi();
256
+ const { routing, ...rest } = opts;
257
+ return { hostOpts: { ...rest, routing: routing ?? {}, piEngine }, probeCalls, engines, piEngine };
258
+ }
259
+
260
+ it("pi 请求(缺省):同步短路——返回值不是 Promise(零微任务,缺省路径时序契约)且免探", () => {
261
+ const { hostOpts, piEngine, probeCalls } = makeHostRoute();
262
+ const routed = routeEngineForHost(hostOpts);
263
+ expect(routed).not.toBeInstanceOf(Promise);
264
+ const route = routed as EngineRouteResult;
265
+ expect(route.engine).toBe(piEngine); // 本地 pi 实例接管,不经 registry
266
+ expect(route.engineId).toBe("pi");
267
+ expect(route.source).toBe("default");
268
+ expect(probeCalls).toEqual([]); // pi 免探(D7 轻量口径,缺省路径零 probe 开销)
269
+ });
270
+
271
+ it("显式 engine='pi':同步短路同形(call source 留痕)", () => {
272
+ const { hostOpts, piEngine } = makeHostRoute({ routing: { callEngine: "pi" } });
273
+ const routed = routeEngineForHost(hostOpts);
274
+ expect(routed).not.toBeInstanceOf(Promise);
275
+ const route = routed as EngineRouteResult;
276
+ expect(route.engine).toBe(piEngine);
277
+ expect(route.source).toBe("call");
278
+ });
279
+
280
+ it("非 pi 请求(frontmatter zcode + probe ok):返回 Promise,resolve 经注入获取引擎", async () => {
281
+ const { hostOpts, engines } = makeHostRoute({ routing: { agentEngine: "zcode" } });
282
+ const routed = routeEngineForHost(hostOpts);
283
+ expect(routed).toBeInstanceOf(Promise);
284
+ const route = await routed;
285
+ expect(route.engine).toBe(engines.get("zcode"));
286
+ expect(route.engineId).toBe("zcode");
287
+ });
288
+
289
+ it("probe 失败兜底回 pi:本地 pi 实例接管(不依赖 registry 的 pi 注册态)+ fallback 留痕", async () => {
290
+ const { hostOpts, piEngine } = makeHostRoute({
291
+ zcodeProbeOk: false,
292
+ routing: { agentEngine: "zcode" },
293
+ });
294
+ const route = await routeEngineForHost(hostOpts);
295
+ expect(route.engine).toBe(piEngine);
296
+ expect(route.engineId).toBe("pi");
297
+ expect(route.requestedEngineId).toBe("zcode");
298
+ expect(route.engineFallback).toEqual({ from: "zcode", reason: "engine_probe_failed" });
299
+ });
300
+
301
+ it("registry 面未注册 pi:兜底/清单两口径都不把本地 pi 漏报(SAR 单测 mock 形态)", async () => {
302
+ // registry 面只含 zcode(probe 失败)——模拟「本地 pi 不在全局注册表」的注入形态
303
+ const engines = new Map<string, EnginePort>([["zcode", makeFakeEngine("zcode", false)]]);
304
+ const probeCalls: string[] = [];
305
+ const piEngine = makeLocalPi();
306
+ const mkOpts = (): HostRouteOptions => ({
307
+ routing: { agentEngine: "zcode" },
308
+ strict: false,
309
+ probe: (id) => {
310
+ probeCalls.push(id);
311
+ return engines.get(id)!.probe();
312
+ },
313
+ piEngine,
314
+ getEngineFn: (id) => {
315
+ const e = engines.get(id);
316
+ if (e === undefined) throw new EngineNotFoundError(id, [...engines.keys()]);
317
+ return e;
318
+ },
319
+ hasEngineFn: (id) => engines.has(id),
320
+ listEnginesFn: () => [...engines.keys()],
321
+ });
322
+ // probe 失败 + 无守卫 → 兜底回 pi:本地实例接管,不触 registry 的 pi 缺失
323
+ const route = await routeEngineForHost(mkOpts());
324
+ expect(route.engine).toBe(piEngine);
325
+ expect(route.engineId).toBe("pi");
326
+ expect(probeCalls).toEqual(["zcode"]);
327
+ // 未注册 id:engine_not_found 文案清单含 pi(本地 pi 恒可用,不漏报)
328
+ const ghostOpts: HostRouteOptions = {
329
+ ...mkOpts(),
330
+ routing: { callEngine: "ghost" },
331
+ };
332
+ const err = await Promise.resolve(routeEngineForHost(ghostOpts)).then(
333
+ (r: unknown) => r,
334
+ (e: unknown) => e,
335
+ );
336
+ expect(err).toBeInstanceOf(Error);
337
+ expect((err as Error).message).toContain("engine_not_found");
338
+ expect((err as EngineNotFoundError).registered).toContain("pi");
339
+ });
340
+ });