@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,93 @@
1
+ // derive-closed-display-parity — 成败推导单一权威守卫(core 侧)
2
+ //
3
+ // 同构成败推导收敛到 execution-record.ts 的 deriveOutcome/projectOutcome(单一权威)。
4
+ // 本文件锚定:① 权威函数行为;② core 侧消费方(execution/notifier.ts buildLlmContent)
5
+ // 源码不得写回手写同构 switch。
6
+ // [u1-move 拆分] 壳侧消费方(interface/bg-notify-render.ts renderRecordLines)的守卫段
7
+ // 随壳件留守 pi extension 包:src/__tests__/derive-closed-display-parity-interface.test.ts。
8
+
9
+ import { readFileSync } from "node:fs";
10
+ import { dirname, join } from "node:path";
11
+ import { fileURLToPath } from "node:url";
12
+
13
+ import { describe, expect, it } from "vitest";
14
+
15
+ import { deriveOutcome, projectOutcome } from "../execution/execution-record.ts";
16
+
17
+ const here = dirname(fileURLToPath(import.meta.url));
18
+
19
+ /** core 侧消费方源码(同构成败推导已收敛删除)。 */
20
+ const CONSUMER_SOURCES = {
21
+ notifier: join(here, "..", "execution", "notifier.ts"),
22
+ } as const;
23
+
24
+ /**
25
+ * 手写同构 switch 的源码特征 token:出现即说明有人把收敛删除的成败推导又写了回去。
26
+ * ("gc" 是 ClosedReason 专属字面量——outcome 三态不含它,消费方合法代码不会出现
27
+ * `?? "gc"` / `=== "gc"`;cancelled 比较不列入——outcome === "cancelled" 是合法消费。)
28
+ */
29
+ const LEGACY_DERIVATION_TOKENS = [/\?\?\s*["']gc["']/, /===\s*["']gc["']/] as const;
30
+
31
+ describe("deriveOutcome — 单一权威终态派生(行为锚定)", () => {
32
+ it("completed:closed + 无 error(gc / user-close 正常完成)", () => {
33
+ expect(deriveOutcome("gc", undefined)).toBe("completed");
34
+ expect(deriveOutcome("user-close", undefined)).toBe("completed");
35
+ });
36
+
37
+ it("failed:closed + error 非空(gc 失败)", () => {
38
+ expect(deriveOutcome("gc", "spawn EPIPE")).toBe("failed");
39
+ });
40
+
41
+ it("[D6 显式取舍] parent-shutdown/parent-fork/parent-new 合成关闭 → failed(勿改 cancelled)", () => {
42
+ // disposeAllRecords 合成 result 恒写 error:"closed due to ${reason}"——
43
+ // 「父进程关闭时子 agent 未完成即失败」为选定行为而非疏漏。
44
+ expect(deriveOutcome("parent-shutdown", "closed due to parent-shutdown")).toBe("failed");
45
+ expect(deriveOutcome("parent-fork", "closed due to parent-fork")).toBe("failed");
46
+ expect(deriveOutcome("parent-new", "closed due to parent-new")).toBe("failed");
47
+ });
48
+
49
+ it("cancelled:取消优先于 error(abort 合成 result 可能带 error)", () => {
50
+ expect(deriveOutcome("cancelled", undefined)).toBe("cancelled");
51
+ expect(deriveOutcome("cancelled", "aborted by user")).toBe("cancelled");
52
+ });
53
+
54
+ it("空串 error 不构成 failed(与旧同构 `record.error &&` truthy 判定逐字对齐)", () => {
55
+ expect(deriveOutcome("gc", "")).toBe("completed");
56
+ });
57
+ });
58
+
59
+ describe("projectOutcome — 投影唯一出口(行为锚定)", () => {
60
+ it("running → undefined;closed 一等 outcome 直读;存量 record 兜底派生", () => {
61
+ expect(projectOutcome({ status: "running" })).toBeUndefined();
62
+ expect(projectOutcome({ status: "closed", outcome: "failed" })).toBe("failed");
63
+ expect(projectOutcome({ status: "closed", closedReason: "gc", error: "boom" })).toBe("failed");
64
+ expect(projectOutcome({ status: "closed" })).toBe("completed");
65
+ });
66
+ });
67
+
68
+ describe("同构 switch 残留守卫(收敛删除后不得写回)— core 消费方", () => {
69
+ it("消费方源码可读(fail-loud 前置)", () => {
70
+ for (const [key, p] of Object.entries(CONSUMER_SOURCES)) {
71
+ expect(() => readFileSync(p, "utf-8"), `源文件不可读:${key} ${p}`).not.toThrow();
72
+ }
73
+ });
74
+
75
+ it("notifier buildLlmContent 无 closedReason 成败推导特征", () => {
76
+ for (const [key, p] of Object.entries(CONSUMER_SOURCES)) {
77
+ const src = readFileSync(p, "utf-8");
78
+ for (const token of LEGACY_DERIVATION_TOKENS) {
79
+ expect(
80
+ token.test(src),
81
+ `[${key}] 检出旧同构推导特征 /${token.source}/——成败判定应只读 outcome 或调用 ` +
82
+ `execution-record.ts 的 deriveOutcome/projectOutcome(单一权威),禁止手写 switch 写回。` +
83
+ `若确属新增合法用法,请同步更新本护栏的特征提取。`,
84
+ ).toBe(false);
85
+ }
86
+ // 消费方必须经由单一权威实现
87
+ expect(
88
+ src.includes("deriveOutcome"),
89
+ `[${key}] 未引用 deriveOutcome——outcome 兜底派生必须复用单一权威函数。`,
90
+ ).toBe(true);
91
+ }
92
+ });
93
+ });
@@ -0,0 +1,125 @@
1
+ import { describe, expect,it } from "vitest";
2
+
3
+ import { parseSpawnLine } from "../execution/spawn-event-adapter";
4
+ import { sendGetStateCommand } from "../execution/stdin-writer";
5
+
6
+ describe("FR-4: get_state RPC handshake", () => {
7
+ describe("sendGetStateCommand", () => {
8
+ it("should write get_state command to stdin", () => {
9
+ const written: string[] = [];
10
+ const child = {
11
+ stdin: {
12
+ destroyed: false,
13
+ write: (data: string) => {
14
+ written.push(data);
15
+ return true;
16
+ },
17
+ },
18
+ } as unknown as Parameters<typeof sendGetStateCommand>[0];
19
+
20
+ const id = sendGetStateCommand(child);
21
+
22
+ expect(written).toHaveLength(1);
23
+ const parsed = JSON.parse(written[0]);
24
+ expect(parsed.type).toBe("get_state");
25
+ expect(parsed.id).toBe(id);
26
+ expect(typeof parsed.id).toBe("string");
27
+ });
28
+
29
+ it("should not write if stdin is destroyed", () => {
30
+ const written: string[] = [];
31
+ const child = {
32
+ stdin: {
33
+ destroyed: true,
34
+ write: (data: string) => {
35
+ written.push(data);
36
+ return true;
37
+ },
38
+ },
39
+ } as unknown as Parameters<typeof sendGetStateCommand>[0];
40
+
41
+ sendGetStateCommand(child);
42
+
43
+ expect(written).toHaveLength(0);
44
+ });
45
+
46
+ it("should not write if stdin is null", () => {
47
+ const child = {
48
+ stdin: null,
49
+ } as unknown as Parameters<typeof sendGetStateCommand>[0];
50
+
51
+ // Should not throw
52
+ sendGetStateCommand(child);
53
+ });
54
+ });
55
+
56
+ describe("parseSpawnLine: get_state response", () => {
57
+ it("should parse get_state response correctly", () => {
58
+ const line = JSON.stringify({
59
+ type: "response",
60
+ command: "get_state",
61
+ success: true,
62
+ id: "test-req-id",
63
+ data: {
64
+ sessionFile: "/path/to/session.jsonl",
65
+ sessionId: "session-123",
66
+ },
67
+ });
68
+
69
+ const parsed = parseSpawnLine(line);
70
+ expect(parsed).not.toBeNull();
71
+ expect(parsed!.kind).toBe("response");
72
+ if (parsed!.kind === "response") {
73
+ expect(parsed!.command).toBe("get_state");
74
+ expect(parsed!.success).toBe(true);
75
+ expect(parsed!.id).toBe("test-req-id");
76
+ expect(parsed!.data).toEqual({
77
+ sessionFile: "/path/to/session.jsonl",
78
+ sessionId: "session-123",
79
+ });
80
+ }
81
+ });
82
+
83
+ it("should parse get_state error response", () => {
84
+ const line = JSON.stringify({
85
+ type: "response",
86
+ command: "get_state",
87
+ success: false,
88
+ id: "test-req-id",
89
+ error: "session not initialized",
90
+ });
91
+
92
+ const parsed = parseSpawnLine(line);
93
+ expect(parsed).not.toBeNull();
94
+ expect(parsed!.kind).toBe("response");
95
+ if (parsed!.kind === "response") {
96
+ expect(parsed!.command).toBe("get_state");
97
+ expect(parsed!.success).toBe(false);
98
+ expect(parsed!.error).toBe("session not initialized");
99
+ }
100
+ });
101
+
102
+ it("should parse get_state response without id (notification)", () => {
103
+ const line = JSON.stringify({
104
+ type: "response",
105
+ command: "get_state",
106
+ success: true,
107
+ data: { sessionId: "session-456" },
108
+ });
109
+
110
+ const parsed = parseSpawnLine(line);
111
+ expect(parsed).not.toBeNull();
112
+ expect(parsed!.kind).toBe("response");
113
+ if (parsed!.kind === "response") {
114
+ expect(parsed!.command).toBe("get_state");
115
+ expect(parsed!.id).toBeUndefined();
116
+ }
117
+ });
118
+ });
119
+
120
+ // [B8] 删除原 "get_state response matching in stdout pump" describe 块(3 case):
121
+ // 该块在测试内重写了 session-runner 的 response matching 逻辑(Map<id,resolver> +
122
+ // delete + resolver(data)),断言的是测试自己复制的逻辑而非生产代码——tautological
123
+ // (同义反复),无法捕获生产 matching 的真实回归。真实覆盖已在 run-spawn-rpc-mode.test.ts
124
+ // (端到端跑 session-runner 的 stdout pump + get_state 握手)中存在,此处删除不减覆盖。
125
+ });
@@ -0,0 +1,259 @@
1
+ import * as fs from "fs";
2
+ import * as os from "os";
3
+ import * as path from "path";
4
+ import { afterEach,beforeEach, describe, expect, it } from "vitest";
5
+
6
+ import { ManifestStore } from "../execution/manifest-store";
7
+ import { RecordStore } from "../execution/record-store";
8
+
9
+ describe("FR-8: Orphan Recovery from Manifest", () => {
10
+ let rootDir: string;
11
+ let sessionsDir: string;
12
+ let recordsDir: string;
13
+ let manifestStore: ManifestStore;
14
+
15
+ beforeEach(() => {
16
+ // [DS3] 两层布局:sessions/records 平级落同一 rootDir(对齐生产 <enc> 段结构),
17
+ // sessions-index.json 落 dirname(sessionsDir)=rootDir 内随 afterEach 清理,
18
+ // 不写 os.tmpdir() 本身(并行 worker 共写会跨文件污染)。
19
+ rootDir = fs.mkdtempSync(path.join(os.tmpdir(), "fr8-"));
20
+ sessionsDir = path.join(rootDir, "sessions");
21
+ fs.mkdirSync(sessionsDir);
22
+ recordsDir = path.join(rootDir, "records");
23
+ manifestStore = new ManifestStore(recordsDir);
24
+ });
25
+
26
+ afterEach(() => {
27
+ // maxRetries:fire-and-forget 的 sessions-index 写可能与删除并发(ENOTEMPTY 竞态)
28
+ fs.rmSync(rootDir, { recursive: true, force: true, maxRetries: 5, retryDelay: 20 });
29
+ });
30
+
31
+ it("should recover orphan records from manifest when session.jsonl is missing", async () => {
32
+ const store = new RecordStore(sessionsDir, manifestStore);
33
+
34
+ // Write a manifest record (simulating a subagent that crashed before writing session.jsonl)
35
+ await manifestStore.writeManifest({
36
+ id: "orphan-1",
37
+ rootSessionId: "session-main",
38
+ agentName: "worker",
39
+ status: "closed",
40
+ createdAt: 1000,
41
+ completedAt: 2000,
42
+ sessionFile: "/path/to/session.jsonl",
43
+ });
44
+
45
+ const records = store.collectRecords(100, "all", "session-main");
46
+ expect(records).toHaveLength(1);
47
+ expect(records[0].id).toBe("orphan-1");
48
+ expect(records[0].agent).toBe("worker");
49
+ expect(records[0].status).toBe("closed");
50
+ expect(records[0].startedAt).toBe(1000);
51
+ expect(records[0].endedAt).toBe(2000);
52
+ });
53
+
54
+ it("should not duplicate records already recovered from session.jsonl", async () => {
55
+ // Create a session.jsonl with an identity entry (simulating successful reconstruction)
56
+ const sessionId = "existing-session";
57
+ const timestamp = "2026-07-18T12-00-00-000Z";
58
+ const sessionFile = path.join(sessionsDir, `${timestamp.replace(/[:.]/g, "-")}_${sessionId}.jsonl`);
59
+ fs.writeFileSync(sessionFile, JSON.stringify({
60
+ type: "custom",
61
+ customType: "subagent-identity",
62
+ data: {
63
+ id: "existing-1",
64
+ agent: "explorer",
65
+ mode: "background",
66
+ task: "explore codebase",
67
+ slug: "explore",
68
+ startedAt: 1000,
69
+ rootSessionId: "session-main",
70
+ depth: 0,
71
+ },
72
+ }) + "\n");
73
+
74
+ // Also write a manifest for the same record
75
+ await manifestStore.writeManifest({
76
+ id: "existing-1",
77
+ rootSessionId: "session-main",
78
+ agentName: "explorer",
79
+ status: "closed",
80
+ createdAt: 1000,
81
+ completedAt: 2000,
82
+ sessionFile,
83
+ });
84
+
85
+ const store = new RecordStore(sessionsDir, manifestStore);
86
+ const records = store.collectRecords(100, "all", "session-main");
87
+
88
+ // Should have exactly 1 record (no duplication)
89
+ const matching = records.filter((r) => r.id === "existing-1");
90
+ expect(matching).toHaveLength(1);
91
+ });
92
+
93
+ it("should filter manifest records by rootSessionId", async () => {
94
+ const store = new RecordStore(sessionsDir, manifestStore);
95
+
96
+ await manifestStore.writeManifest({
97
+ id: "orphan-session-a",
98
+ rootSessionId: "session-a",
99
+ agentName: "worker",
100
+ status: "closed",
101
+ createdAt: 1000,
102
+ });
103
+
104
+ await manifestStore.writeManifest({
105
+ id: "orphan-session-b",
106
+ rootSessionId: "session-b",
107
+ agentName: "worker",
108
+ status: "closed",
109
+ createdAt: 2000,
110
+ });
111
+
112
+ const recordsA = store.collectRecords(100, "all", "session-a");
113
+ expect(recordsA).toHaveLength(1);
114
+ expect(recordsA[0].id).toBe("orphan-session-a");
115
+
116
+ const recordsB = store.collectRecords(100, "all", "session-b");
117
+ expect(recordsB).toHaveLength(1);
118
+ expect(recordsB[0].id).toBe("orphan-session-b");
119
+ });
120
+
121
+ it("should map manifest status correctly (4-state)", async () => {
122
+ const store = new RecordStore(sessionsDir, manifestStore);
123
+
124
+ await manifestStore.writeManifest({
125
+ id: "status-completed",
126
+ rootSessionId: "session-main",
127
+ agentName: "worker",
128
+ status: "closed",
129
+ createdAt: 1000,
130
+ });
131
+
132
+ await manifestStore.writeManifest({
133
+ id: "status-failed",
134
+ rootSessionId: "session-main",
135
+ agentName: "worker",
136
+ status: "closed",
137
+ createdAt: 2000,
138
+ });
139
+
140
+ await manifestStore.writeManifest({
141
+ id: "status-cancelled",
142
+ rootSessionId: "session-main",
143
+ agentName: "worker",
144
+ status: "cancelled",
145
+ createdAt: 3000,
146
+ });
147
+
148
+ // M3: ManifestRecord.status 4 态(running/completed/failed/cancelled)。
149
+ // v4 B-1:cancelled 不再是独立 ExecutionStatus——mapManifestStatus 映射为 closed
150
+ //(closedReason='cancelled' 由重建路径 sidecar 推断)。crashed 不进 manifest
151
+ //(crashed 是重启重建时靠 sidecar 四分支推断的派生态,见 record-store.ts reconstructAll)。
152
+ const records = store.collectRecords(100, "all", "session-main");
153
+ expect(records).toHaveLength(3);
154
+
155
+ expect(records.find((r) => r.id === "status-completed")?.status).toBe("closed");
156
+ expect(records.find((r) => r.id === "status-failed")?.status).toBe("closed");
157
+ expect(records.find((r) => r.id === "status-cancelled")?.status).toBe("closed");
158
+ });
159
+
160
+ it("mapManifestStatus 越界值返回 null:collectRecords 跳过损坏 record(不降级 failed)", async () => {
161
+ const store = new RecordStore(sessionsDir, manifestStore);
162
+
163
+ // 合法 record(对照组)
164
+ await manifestStore.writeManifest({
165
+ id: "good",
166
+ rootSessionId: "session-main",
167
+ agentName: "worker",
168
+ status: "closed",
169
+ createdAt: 1000,
170
+ });
171
+
172
+ // 直接写磁盘注入非法 status(绕过 writeManifest 的 TS 类型守卫)。
173
+ // crashed 不在 4 态枚举——它是重建派生态,意外出现在 manifest 应被跳过而非降级 failed。
174
+ fs.writeFileSync(path.join(recordsDir, "bad-crashed.json"), JSON.stringify({
175
+ id: "bad-crashed", rootSessionId: "session-main", agentName: "worker",
176
+ status: "crashed", createdAt: 2000,
177
+ }));
178
+ // 未知值同样跳过
179
+ fs.writeFileSync(path.join(recordsDir, "bad-unknown.json"), JSON.stringify({
180
+ id: "bad-unknown", rootSessionId: "session-main", agentName: "worker",
181
+ status: "totally-unknown", createdAt: 3000,
182
+ }));
183
+
184
+ const records = store.collectRecords(100, "all", "session-main");
185
+ // 只剩 good——损坏 record 被跳过 + console.warn,不误显示为 failed
186
+ expect(records).toHaveLength(1);
187
+ expect(records[0].id).toBe("good");
188
+ expect(records[0].status).toBe("closed");
189
+ });
190
+
191
+ it("in-memory records should take priority over manifest records", async () => {
192
+ const store = new RecordStore(sessionsDir, manifestStore);
193
+
194
+ // Register a running record in memory
195
+ store.register({
196
+ id: "memory-record",
197
+ agent: "worker",
198
+ model: "test/model",
199
+ thinkingLevel: undefined,
200
+ mode: "background",
201
+ task: "test task",
202
+ slug: "test",
203
+ startedAt: 1000,
204
+ rootSessionId: "session-main",
205
+ parentRecordId: undefined,
206
+ depth: 0,
207
+ status: "running",
208
+ turns: [],
209
+ turnCount: 0,
210
+ totalTokens: 0,
211
+ lastError: undefined,
212
+ endedAt: undefined,
213
+ result: undefined,
214
+ error: undefined,
215
+ agentResult: undefined,
216
+ sessionFile: undefined,
217
+ controller: undefined,
218
+ });
219
+
220
+ // Also write a manifest with the same id but different status
221
+ await manifestStore.writeManifest({
222
+ id: "memory-record",
223
+ rootSessionId: "session-main",
224
+ agentName: "different-agent",
225
+ status: "closed",
226
+ createdAt: 500,
227
+ });
228
+
229
+ const records = store.collectRecords(100, "all", "session-main");
230
+ expect(records).toHaveLength(1);
231
+ // Memory record should win
232
+ expect(records[0].status).toBe("running");
233
+ expect(records[0].agent).toBe("worker");
234
+ });
235
+
236
+ it("should work without manifestStore (backward compatible)", () => {
237
+ const store = new RecordStore(sessionsDir); // No manifestStore
238
+
239
+ // Should not throw
240
+ const records = store.collectRecords(100, "all", "session-main");
241
+ expect(records).toHaveLength(0);
242
+ });
243
+
244
+ it("should handle manifest with status=running as running", async () => {
245
+ const store = new RecordStore(sessionsDir, manifestStore);
246
+
247
+ await manifestStore.writeManifest({
248
+ id: "still-running",
249
+ rootSessionId: "session-main",
250
+ agentName: "worker",
251
+ status: "running",
252
+ createdAt: Date.now(),
253
+ });
254
+
255
+ const records = store.collectRecords(100, "all", "session-main");
256
+ expect(records).toHaveLength(1);
257
+ expect(records[0].status).toBe("running");
258
+ });
259
+ });
@@ -0,0 +1,73 @@
1
+ // M2 修复回归探针(design.json TC1)。
2
+ //
3
+ // M2 bug:旧实现把 schema SO 指令写成临时文件,push 文件路径(而非内容)给下游,
4
+ // 下游 mapper/session-runner 把路径当文本拼进最终 append 文件,子进程收到
5
+ // "/var/folders/.../xxx.md" 路径垃圾,schema 指令从未进入子进程。
6
+ //
7
+ // 修复后:schema 指令内容直传 appendSystemPrompt(不写盘、无临时文件)。
8
+ // 本探针锁死:appendSystemPrompt 只含 SO 指令内容,不含 agent 正文,无路径,无临时文件。
9
+
10
+ import { describe, expect, it } from "vitest";
11
+
12
+ import type { AgentCallOpts } from "../orchestration/models/types.ts";
13
+ import { resolveAgentOpts } from "../orchestration/agent-opts-resolver.ts";
14
+
15
+ describe("M2 append-content probe (design.json TC1)", () => {
16
+ it("schema + agent 时 appendSystemPrompt 只含 SO 指令内容(非路径、非 agent 正文)", () => {
17
+ const opts: AgentCallOpts = {
18
+ prompt: "run task",
19
+ schema: { type: "object", properties: { ok: { type: "boolean" } } },
20
+ agent: "/fake/agents/worker.md",
21
+ description: "m2-probe",
22
+ slug: "m2-probe",
23
+ };
24
+
25
+ const result = resolveAgentOpts(opts);
26
+
27
+ expect(result.error).toBeUndefined();
28
+ // 只含 SO 指令(length === 1)
29
+ expect(result.opts.appendSystemPrompt).toBeDefined();
30
+ expect(result.opts.appendSystemPrompt!.length).toBe(1);
31
+
32
+ const item = result.opts.appendSystemPrompt![0];
33
+
34
+ // 内容语义:含 structured-output 关键词
35
+ expect(item).toContain("structured-output");
36
+ // 非路径:不匹配 /tmp 或 var/folders(M2 bug 旧实现 push 路径字符串)
37
+ expect(item).not.toMatch(/\/tmp\/|\/var\/folders\//);
38
+ // 不以 .md 结尾(路径特征)
39
+ expect(item).not.toMatch(/\.md$/);
40
+ // 不含 agent 正文/路径标识词(agent 正文不经 appendSystemPrompt,防双重注入回归)
41
+ expect(item).not.toContain("/fake/agents/worker.md");
42
+ // agent ref 原样保留(resolveAgentOpts 不消费 agent,交 resolveIdentity)
43
+ expect(result.opts.agent).toBe("/fake/agents/worker.md");
44
+ // model/thinkingLevel 未被修改(agent 处理移交,不在此提升层级)
45
+ expect(result.opts.model).toBeUndefined();
46
+ expect(result.opts.thinkingLevel).toBeUndefined();
47
+ // schemaEnv 设置(PI_WORKFLOW_SCHEMA 契约)
48
+ expect(result.opts.schemaEnv).toBe(JSON.stringify(opts.schema));
49
+ });
50
+
51
+ it("agent 存在不影响 appendSystemPrompt 内容(agent 正文结构性不泄漏)", () => {
52
+ const schema = { type: "object" } as Record<string, unknown>;
53
+ const withAgent = resolveAgentOpts({ prompt: "x", schema, agent: "/fake/a.md" });
54
+ const withoutAgent = resolveAgentOpts({ prompt: "x", schema });
55
+
56
+ // 加不加 agent,appendSystemPrompt 完全相同——证明 agent 不被消费、正文不注入
57
+ expect(withAgent.opts.appendSystemPrompt).toEqual(withoutAgent.opts.appendSystemPrompt);
58
+ });
59
+
60
+ it("无 schema 时 appendSystemPrompt undefined", () => {
61
+ const result = resolveAgentOpts({ prompt: "x", agent: "/fake/a.md" });
62
+ expect(result.opts.appendSystemPrompt).toBeUndefined();
63
+ });
64
+
65
+ it("resolveAgentOpts 无文件副作用——同 schema 多次调用结果字节相同(无随机路径)", () => {
66
+ const schema = { type: "object" } as Record<string, unknown>;
67
+ const a = resolveAgentOpts({ prompt: "x", schema });
68
+ const b = resolveAgentOpts({ prompt: "x", schema });
69
+
70
+ // 旧实现每次写入随机 UUID 文件名路径,结果不同;内容直传后结果确定性相同
71
+ expect(a.opts.appendSystemPrompt).toEqual(b.opts.appendSystemPrompt);
72
+ });
73
+ });