@zhushanwen/pi-subagent-workflow 8.5.0 → 8.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (336) hide show
  1. package/package.json +13 -16
  2. package/relay/relay.mjs +1 -1
  3. package/src/host/__tests__/pi-host.test.ts +214 -0
  4. package/src/host/pi-host.ts +109 -0
  5. package/src/index.ts +142 -32
  6. package/src/injectors/__tests__/engine-awareness.test.ts +292 -0
  7. package/src/injectors/__tests__/engine-section-stability.test.ts +341 -0
  8. package/src/injectors/__tests__/subagent-list-injector.test.ts +5 -5
  9. package/src/injectors/__tests__/workflow-list-injector.test.ts +5 -5
  10. package/src/injectors/engine-awareness.ts +123 -0
  11. package/src/injectors/model-list-injector.ts +1 -1
  12. package/src/injectors/subagent-list-injector.ts +8 -8
  13. package/src/injectors/workflow-list-injector.ts +8 -8
  14. package/src/interface/__tests__/detectors.test.ts +3 -3
  15. package/src/interface/__tests__/subagent-tool-path-guard.test.ts +157 -0
  16. package/src/interface/__tests__/subagent-tool-prompt.test.ts +12 -0
  17. package/src/interface/__tests__/tool-workflow-script-generate.test.ts +2 -2
  18. package/src/interface/__tests__/tool-workflow-throw-paths.test.ts +1 -1
  19. package/src/interface/bg-notify-render.ts +34 -13
  20. package/src/interface/commands.ts +3 -3
  21. package/src/interface/format.ts +2 -2
  22. package/src/interface/helpers.ts +3 -3
  23. package/src/interface/list-component.ts +4 -4
  24. package/src/interface/list-shared.ts +2 -2
  25. package/src/interface/list-view.ts +2 -2
  26. package/src/interface/subagent-actions.ts +288 -19
  27. package/src/interface/subagent-tool-schema.ts +163 -0
  28. package/src/interface/subagent-tool.ts +65 -129
  29. package/src/interface/subagents.ts +5 -5
  30. package/src/interface/tool-render.ts +34 -2
  31. package/src/interface/tool-workflow-script.ts +4 -4
  32. package/src/interface/tool-workflow.ts +6 -6
  33. package/src/interface/views/WorkflowsView.ts +5 -5
  34. package/src/interface/views/__tests__/WorkflowsView-signature.test.ts +3 -3
  35. package/src/interface/views/__tests__/detail-content-session-file.test.ts +2 -2
  36. package/src/interface/views/detail-content.ts +4 -4
  37. package/src/interface/views/format.ts +4 -4
  38. package/src/{orchestration/jsonl-run-store.ts → jsonl-run-store.ts} +132 -18
  39. package/src/execution/__tests__/__fixtures__/notifier-golden-snapshots.json +0 -53
  40. package/src/execution/__tests__/__fixtures__/truncline.snapshot.json +0 -1
  41. package/src/execution/__tests__/agent-registry.test.ts +0 -363
  42. package/src/execution/__tests__/agent-result-mapper.test.ts +0 -150
  43. package/src/execution/__tests__/alive-store.test.ts +0 -147
  44. package/src/execution/__tests__/ask-user-transit-e2e.test.ts +0 -490
  45. package/src/execution/__tests__/before-agent-start-injection.test.ts +0 -132
  46. package/src/execution/__tests__/bg-notify-render.test.ts +0 -256
  47. package/src/execution/__tests__/channel-registry-handshake.test.ts +0 -243
  48. package/src/execution/__tests__/chat-engine-routing.test.ts +0 -597
  49. package/src/execution/__tests__/chatmode-first-round-closure-service.test.ts +0 -365
  50. package/src/execution/__tests__/chatmode-first-round-closure-spawn.test.ts +0 -190
  51. package/src/execution/__tests__/chatmode-round-notify-real-chain.test.ts +0 -215
  52. package/src/execution/__tests__/concurrency-pool.test.ts +0 -250
  53. package/src/execution/__tests__/config.test.ts +0 -110
  54. package/src/execution/__tests__/conversation-wiring.test.ts +0 -198
  55. package/src/execution/__tests__/crash-recovery.test.ts +0 -320
  56. package/src/execution/__tests__/delivery-methods.test.ts +0 -385
  57. package/src/execution/__tests__/dialog-queue.test.ts +0 -299
  58. package/src/execution/__tests__/epipe-fallback.test.ts +0 -241
  59. package/src/execution/__tests__/execute-and-await-worktree.test.ts +0 -243
  60. package/src/execution/__tests__/execute-nesting.test.ts +0 -323
  61. package/src/execution/__tests__/execute-options-mapper.test.ts +0 -189
  62. package/src/execution/__tests__/execution-record.test.ts +0 -1284
  63. package/src/execution/__tests__/finalize-record.test.ts +0 -368
  64. package/src/execution/__tests__/finalized-marker.test.ts +0 -82
  65. package/src/execution/__tests__/format-schema-instruction.test.ts +0 -138
  66. package/src/execution/__tests__/format.test.ts +0 -458
  67. package/src/execution/__tests__/gc-timer.test.ts +0 -184
  68. package/src/execution/__tests__/get-record-for-action-restart.test.ts +0 -254
  69. package/src/execution/__tests__/gui-mode-dispatch.test.ts +0 -59
  70. package/src/execution/__tests__/helpers/mock-extension-api.ts +0 -30
  71. package/src/execution/__tests__/helpers/spawn-mock.ts +0 -238
  72. package/src/execution/__tests__/host-mode.test.ts +0 -87
  73. package/src/execution/__tests__/index-session-start-identity.test.ts +0 -371
  74. package/src/execution/__tests__/index-session-start.test.ts +0 -593
  75. package/src/execution/__tests__/lifecycle-manager-lock.test.ts +0 -211
  76. package/src/execution/__tests__/lifecycle-manager.test.ts +0 -337
  77. package/src/execution/__tests__/lifecycle-predicates.test.ts +0 -116
  78. package/src/execution/__tests__/list-component.test.ts +0 -401
  79. package/src/execution/__tests__/list-fields.test.ts +0 -109
  80. package/src/execution/__tests__/manifest-parentid.test.ts +0 -117
  81. package/src/execution/__tests__/model-resolver.test.ts +0 -413
  82. package/src/execution/__tests__/nested-visibility-env-propagation.test.ts +0 -287
  83. package/src/execution/__tests__/nested-visibility.test.ts +0 -325
  84. package/src/execution/__tests__/notifier-flush.test.ts +0 -574
  85. package/src/execution/__tests__/notifier-golden-snapshot.test.ts +0 -261
  86. package/src/execution/__tests__/one-shot-upgrade.test.ts +0 -205
  87. package/src/execution/__tests__/output-collector.test.ts +0 -61
  88. package/src/execution/__tests__/parent-child-matrix.test.ts +0 -336
  89. package/src/execution/__tests__/path-encoding.test.ts +0 -104
  90. package/src/execution/__tests__/pi-invocation.test.ts +0 -134
  91. package/src/execution/__tests__/record-store.test.ts +0 -1057
  92. package/src/execution/__tests__/records-cwd-isolation.test.ts +0 -91
  93. package/src/execution/__tests__/recursive-visibility-baseline.test.ts +0 -339
  94. package/src/execution/__tests__/recursive-visibility-env.test.ts +0 -273
  95. package/src/execution/__tests__/relay-agent.test.ts +0 -448
  96. package/src/execution/__tests__/relay-env.test.ts +0 -42
  97. package/src/execution/__tests__/resource-policy.test.ts +0 -109
  98. package/src/execution/__tests__/rpc-mode.test.ts +0 -89
  99. package/src/execution/__tests__/run-and-finalize-chatmode.test.ts +0 -267
  100. package/src/execution/__tests__/run-spawn-chatmode-settled.test.ts +0 -253
  101. package/src/execution/__tests__/run-spawn-edges.test.ts +0 -644
  102. package/src/execution/__tests__/run-spawn-integration.test.ts +0 -933
  103. package/src/execution/__tests__/run-spawn-resume.test.ts +0 -322
  104. package/src/execution/__tests__/run-spawn-rpc-mode.test.ts +0 -196
  105. package/src/execution/__tests__/sdk-contract.test.ts +0 -273
  106. package/src/execution/__tests__/session-context-resolver.test.ts +0 -167
  107. package/src/execution/__tests__/session-file-gc.test.ts +0 -293
  108. package/src/execution/__tests__/session-pending.test.ts +0 -197
  109. package/src/execution/__tests__/session-reconstructor.test.ts +0 -379
  110. package/src/execution/__tests__/session-runner-epipe.test.ts +0 -178
  111. package/src/execution/__tests__/session-runner-schema-env.test.ts +0 -311
  112. package/src/execution/__tests__/session-start-reaper.test.ts +0 -233
  113. package/src/execution/__tests__/spawn-args.test.ts +0 -434
  114. package/src/execution/__tests__/spawn-event-adapter-rpc.test.ts +0 -189
  115. package/src/execution/__tests__/spawn-event-adapter.test.ts +0 -167
  116. package/src/execution/__tests__/spawn-worktree-guidance.test.ts +0 -207
  117. package/src/execution/__tests__/spawned-children.test.ts +0 -92
  118. package/src/execution/__tests__/startup-config-declaration.test.ts +0 -35
  119. package/src/execution/__tests__/status-refactor.test.ts +0 -345
  120. package/src/execution/__tests__/stdin-writer.test.ts +0 -463
  121. package/src/execution/__tests__/stream-sink-retirement.test.ts +0 -261
  122. package/src/execution/__tests__/subagent-service-abort.test.ts +0 -60
  123. package/src/execution/__tests__/subagent-service-message-close.test.ts +0 -629
  124. package/src/execution/__tests__/subagent-service-parent-guard.test.ts +0 -180
  125. package/src/execution/__tests__/subagent-service.test.ts +0 -788
  126. package/src/execution/__tests__/subprocess-agent-runner-routing.test.ts +0 -310
  127. package/src/execution/__tests__/subprocess-agent-runner.test.ts +0 -506
  128. package/src/execution/__tests__/temp-prompt.test.ts +0 -53
  129. package/src/execution/__tests__/timeout-integration.test.ts +0 -397
  130. package/src/execution/__tests__/tombstone-store.test.ts +0 -73
  131. package/src/execution/__tests__/tool-action.test.ts +0 -385
  132. package/src/execution/__tests__/truncline-snapshot.test.ts +0 -81
  133. package/src/execution/__tests__/turn-limiter-semantics.test.ts +0 -194
  134. package/src/execution/__tests__/turn-limiter.test.ts +0 -65
  135. package/src/execution/__tests__/ui-channels.test.ts +0 -187
  136. package/src/execution/__tests__/ui-interaction-model.test.ts +0 -67
  137. package/src/execution/__tests__/ui-request-handler-factory.test.ts +0 -370
  138. package/src/execution/__tests__/ui-request-handler.test.ts +0 -204
  139. package/src/execution/__tests__/ui-request-observability.test.ts +0 -114
  140. package/src/execution/__tests__/ui-request-queue.test.ts +0 -133
  141. package/src/execution/__tests__/worktree-manager.test.ts +0 -633
  142. package/src/execution/__tests__/worktree-pid-registration.integration.test.ts +0 -229
  143. package/src/execution/__tests__/worktree-reconcile.integration.test.ts +0 -181
  144. package/src/execution/__tests__/worktree-registry.test.ts +0 -199
  145. package/src/execution/agent-registry.ts +0 -193
  146. package/src/execution/agent-result-mapper.ts +0 -90
  147. package/src/execution/alive-store.ts +0 -92
  148. package/src/execution/argv-mirror.ts +0 -109
  149. package/src/execution/best-effort.ts +0 -38
  150. package/src/execution/channel-registry-access.ts +0 -144
  151. package/src/execution/concurrency-pool.ts +0 -116
  152. package/src/execution/config.ts +0 -96
  153. package/src/execution/dialog-queue.ts +0 -330
  154. package/src/execution/engine/__tests__/common/data-dir.test.ts +0 -53
  155. package/src/execution/engine/__tests__/common/errors.test.ts +0 -132
  156. package/src/execution/engine/__tests__/common/event-journal.test.ts +0 -177
  157. package/src/execution/engine/__tests__/common/kill-chain.test.ts +0 -192
  158. package/src/execution/engine/__tests__/common/nesting-guard.test.ts +0 -81
  159. package/src/execution/engine/__tests__/common/persona-router.test.ts +0 -123
  160. package/src/execution/engine/__tests__/common/pool-manager.test.ts +0 -154
  161. package/src/execution/engine/__tests__/common/schema-emulation.test.ts +0 -128
  162. package/src/execution/engine/__tests__/conformance/__fixtures__/pi-golden-events.json +0 -28
  163. package/src/execution/engine/__tests__/conformance/agent-event-invariants.ts +0 -141
  164. package/src/execution/engine/__tests__/conformance/contract.abort.test.ts +0 -109
  165. package/src/execution/engine/__tests__/conformance/contract.agent-events.test.ts +0 -101
  166. package/src/execution/engine/__tests__/conformance/contract.probe.test.ts +0 -77
  167. package/src/execution/engine/__tests__/conformance/contract.read-degradation.test.ts +0 -104
  168. package/src/execution/engine/__tests__/conformance/contract.relay.test.ts +0 -342
  169. package/src/execution/engine/__tests__/conformance/engine-conformance.live.test.ts +0 -201
  170. package/src/execution/engine/__tests__/conformance/golden-replay.pi.test.ts +0 -76
  171. package/src/execution/engine/__tests__/conformance/golden-replay.zcode.test.ts +0 -79
  172. package/src/execution/engine/__tests__/engine-discovery.test.ts +0 -87
  173. package/src/execution/engine/__tests__/engines-declaration.test.ts +0 -36
  174. package/src/execution/engine/__tests__/model-prompt.test.ts +0 -85
  175. package/src/execution/engine/__tests__/paths.test.ts +0 -39
  176. package/src/execution/engine/__tests__/registry.test.ts +0 -120
  177. package/src/execution/engine/__tests__/routing.test.ts +0 -231
  178. package/src/execution/engine/common/data-dir.ts +0 -62
  179. package/src/execution/engine/common/errors.ts +0 -183
  180. package/src/execution/engine/common/event-journal.ts +0 -254
  181. package/src/execution/engine/common/journal-replay.ts +0 -62
  182. package/src/execution/engine/common/kill-chain.ts +0 -221
  183. package/src/execution/engine/common/nesting-guard.ts +0 -50
  184. package/src/execution/engine/common/persona-router.ts +0 -108
  185. package/src/execution/engine/common/pool-manager.ts +0 -226
  186. package/src/execution/engine/common/schema-emulation.ts +0 -189
  187. package/src/execution/engine/common/session-view-projection.ts +0 -51
  188. package/src/execution/engine/engine-discovery.ts +0 -65
  189. package/src/execution/engine/engines/pi/__tests__/pi-engine.test.ts +0 -469
  190. package/src/execution/engine/engines/pi/__tests__/reader.test.ts +0 -155
  191. package/src/execution/engine/engines/pi/__tests__/task-spec-mapper.test.ts +0 -164
  192. package/src/execution/engine/engines/pi/pi-engine.ts +0 -415
  193. package/src/execution/engine/engines/pi/reader.ts +0 -48
  194. package/src/execution/engine/engines/pi/registration.ts +0 -35
  195. package/src/execution/engine/engines/pi/task-spec-mapper.ts +0 -100
  196. package/src/execution/engine/engines/zcode/__tests__/__fixtures__/zcode-golden-spawn.json +0 -39
  197. package/src/execution/engine/engines/zcode/__tests__/launcher.test.ts +0 -150
  198. package/src/execution/engine/engines/zcode/__tests__/parser.test.ts +0 -246
  199. package/src/execution/engine/engines/zcode/__tests__/preparer.test.ts +0 -228
  200. package/src/execution/engine/engines/zcode/__tests__/reader.test.ts +0 -210
  201. package/src/execution/engine/engines/zcode/__tests__/registration.test.ts +0 -64
  202. package/src/execution/engine/engines/zcode/__tests__/zcode-engine.live.test.ts +0 -127
  203. package/src/execution/engine/engines/zcode/__tests__/zcode-engine.test.ts +0 -567
  204. package/src/execution/engine/engines/zcode/constants.ts +0 -43
  205. package/src/execution/engine/engines/zcode/golden-sample.ts +0 -39
  206. package/src/execution/engine/engines/zcode/launcher.ts +0 -161
  207. package/src/execution/engine/engines/zcode/parser.ts +0 -436
  208. package/src/execution/engine/engines/zcode/preparer.ts +0 -363
  209. package/src/execution/engine/engines/zcode/reader.ts +0 -381
  210. package/src/execution/engine/engines/zcode/registration.ts +0 -37
  211. package/src/execution/engine/engines/zcode/zcode-engine.ts +0 -648
  212. package/src/execution/engine/host-task-spec.ts +0 -47
  213. package/src/execution/engine/model-prompt.ts +0 -59
  214. package/src/execution/engine/paths.ts +0 -42
  215. package/src/execution/engine/port.ts +0 -153
  216. package/src/execution/engine/registry.ts +0 -123
  217. package/src/execution/engine/routing.ts +0 -218
  218. package/src/execution/engine/types.ts +0 -304
  219. package/src/execution/execute-options-mapper.ts +0 -111
  220. package/src/execution/execution-record.ts +0 -906
  221. package/src/execution/finalize-record.ts +0 -251
  222. package/src/execution/finalized-marker.ts +0 -51
  223. package/src/execution/get-state-handshake.ts +0 -104
  224. package/src/execution/host-mode.ts +0 -56
  225. package/src/execution/idle-gc.ts +0 -47
  226. package/src/execution/lifecycle-manager.ts +0 -491
  227. package/src/execution/lifecycle-predicates.ts +0 -65
  228. package/src/execution/manifest-store.ts +0 -256
  229. package/src/execution/model-config-service.ts +0 -204
  230. package/src/execution/model-resolver.ts +0 -276
  231. package/src/execution/notifier.ts +0 -260
  232. package/src/execution/output-collector.ts +0 -88
  233. package/src/execution/path-encoding.ts +0 -60
  234. package/src/execution/pi-invocation.ts +0 -120
  235. package/src/execution/record-entry.ts +0 -132
  236. package/src/execution/record-store.ts +0 -1234
  237. package/src/execution/relay-env.ts +0 -37
  238. package/src/execution/session-context-resolver.ts +0 -64
  239. package/src/execution/session-file-gc.ts +0 -120
  240. package/src/execution/session-pending.ts +0 -164
  241. package/src/execution/session-reconstructor.ts +0 -678
  242. package/src/execution/session-runner.ts +0 -1548
  243. package/src/execution/sessions-index.ts +0 -304
  244. package/src/execution/spawn-event-adapter.ts +0 -363
  245. package/src/execution/stdin-writer.ts +0 -198
  246. package/src/execution/stream-sink.ts +0 -126
  247. package/src/execution/subagent-service.ts +0 -2119
  248. package/src/execution/subprocess-agent-runner.ts +0 -303
  249. package/src/execution/temp-prompt.ts +0 -62
  250. package/src/execution/tombstone-store.ts +0 -72
  251. package/src/execution/turn-limiter.ts +0 -102
  252. package/src/execution/types.ts +0 -910
  253. package/src/execution/ui-channels.ts +0 -216
  254. package/src/execution/ui-interaction-model.ts +0 -48
  255. package/src/execution/ui-request-handler-factory.ts +0 -212
  256. package/src/execution/ui-request-observability.ts +0 -81
  257. package/src/execution/ui-request-queue.ts +0 -174
  258. package/src/execution/worktree-manager.ts +0 -688
  259. package/src/execution/worktree-registry.ts +0 -222
  260. package/src/orchestration/__tests__/__fixtures__/worker-template.snapshot.txt +0 -336
  261. package/src/orchestration/__tests__/agent-call-catch-fallback.test.ts +0 -208
  262. package/src/orchestration/__tests__/agent-call-stream.test.ts +0 -157
  263. package/src/orchestration/__tests__/args-validator.test.ts +0 -143
  264. package/src/orchestration/__tests__/concurrency-gate.test.ts +0 -125
  265. package/src/orchestration/__tests__/config-loader.test.ts +0 -503
  266. package/src/orchestration/__tests__/error-recovery-handlers.test.ts +0 -724
  267. package/src/orchestration/__tests__/error-recovery-postmessage-defense.test.ts +0 -405
  268. package/src/orchestration/__tests__/error-recovery-serialize-failed-result.test.ts +0 -56
  269. package/src/orchestration/__tests__/error-recovery-workflow-call.test.ts +0 -166
  270. package/src/orchestration/__tests__/execute-agent-call.test.ts +0 -314
  271. package/src/orchestration/__tests__/jsonl-run-store-loadall-sources.test.ts +0 -171
  272. package/src/orchestration/__tests__/jsonl-run-store-session-file.test.ts +0 -1030
  273. package/src/orchestration/__tests__/launcher-nested-workflow.test.ts +0 -277
  274. package/src/orchestration/__tests__/lifecycle-runid-injection.test.ts +0 -96
  275. package/src/orchestration/__tests__/lifecycle.test.ts +0 -625
  276. package/src/orchestration/__tests__/review-fix-loop-e2e.test.ts +0 -1971
  277. package/src/orchestration/__tests__/script-lint.test.ts +0 -831
  278. package/src/orchestration/__tests__/skill-discovery.test.ts +0 -157
  279. package/src/orchestration/__tests__/test-mocks.ts +0 -197
  280. package/src/orchestration/__tests__/worker-host.test.ts +0 -120
  281. package/src/orchestration/__tests__/worker-returnmeta-passthrough.test.ts +0 -164
  282. package/src/orchestration/__tests__/worker-script-builder-runtime.test.ts +0 -520
  283. package/src/orchestration/__tests__/worker-script-builder.test.ts +0 -309
  284. package/src/orchestration/__tests__/worker-script-template-snapshot.test.ts +0 -110
  285. package/src/orchestration/__tests__/workflow-nesting-e2e.test.ts +0 -317
  286. package/src/orchestration/__tests__/workflow-script-lint-memo.test.ts +0 -110
  287. package/src/orchestration/__tests__/workflows-e2e.test.ts +0 -537
  288. package/src/orchestration/agent-opts-resolver.ts +0 -93
  289. package/src/orchestration/args-validator.ts +0 -127
  290. package/src/orchestration/concurrency-gate.ts +0 -69
  291. package/src/orchestration/config-loader.ts +0 -315
  292. package/src/orchestration/error-recovery.ts +0 -862
  293. package/src/orchestration/execute-agent-call.ts +0 -217
  294. package/src/orchestration/launcher.ts +0 -410
  295. package/src/orchestration/lifecycle.ts +0 -396
  296. package/src/orchestration/models/__tests__/budget.test.ts +0 -367
  297. package/src/orchestration/models/__tests__/trace.test.ts +0 -408
  298. package/src/orchestration/models/agent-call.ts +0 -83
  299. package/src/orchestration/models/budget.ts +0 -148
  300. package/src/orchestration/models/ports.ts +0 -164
  301. package/src/orchestration/models/run-runtime.ts +0 -89
  302. package/src/orchestration/models/run-spec.ts +0 -83
  303. package/src/orchestration/models/run-state.ts +0 -44
  304. package/src/orchestration/models/trace.ts +0 -183
  305. package/src/orchestration/models/types.ts +0 -292
  306. package/src/orchestration/models/workflow-run.ts +0 -254
  307. package/src/orchestration/models/workflow-script-registry.ts +0 -35
  308. package/src/orchestration/models/workflow-script.ts +0 -117
  309. package/src/orchestration/script-lint.ts +0 -766
  310. package/src/orchestration/skill-discovery.ts +0 -92
  311. package/src/orchestration/worker-handle.ts +0 -115
  312. package/src/orchestration/worker-host.ts +0 -98
  313. package/src/orchestration/worker-script-builder.ts +0 -408
  314. package/src/orchestration/workflow-files.ts +0 -85
  315. package/src/orchestration/workflow-script-registry-impl.ts +0 -133
  316. package/src/shared/__tests__/agent-ref.test.ts +0 -34
  317. package/src/shared/__tests__/meta-parser.test.ts +0 -304
  318. package/src/shared/__tests__/resource-discovery-manifest-cache.test.ts +0 -280
  319. package/src/shared/__tests__/resource-discovery.test.ts +0 -736
  320. package/src/shared/__tests__/resource-meta.test.ts +0 -51
  321. package/src/shared/__tests__/schema-jsonify.test.ts +0 -81
  322. package/src/shared/agent-event.ts +0 -13
  323. package/src/shared/agent-ref.ts +0 -57
  324. package/src/shared/meta-parser.ts +0 -261
  325. package/src/shared/resource-discovery.ts +0 -811
  326. package/src/shared/resource-meta.ts +0 -65
  327. package/src/shared/schema-jsonify.ts +0 -56
  328. package/src/shared/xml-injection.ts +0 -35
  329. package/workflows/README.md +0 -81
  330. package/workflows/_shared/agent-refs.cjs +0 -40
  331. package/workflows/chain.js +0 -131
  332. package/workflows/map-reduce.js +0 -174
  333. package/workflows/parallel.js +0 -148
  334. package/workflows/review-fix-loop-utils.cjs +0 -1307
  335. package/workflows/review-fix-loop.js +0 -1396
  336. package/workflows/scatter-gather.js +0 -178
@@ -1,368 +0,0 @@
1
- // doFinalizeRecord — manifest status 透传测试(M3: 4 态方案)。
2
- //
3
- // 验证 finalize-record.ts 的 status 映射:done→completed, failed→failed, cancelled→cancelled
4
- // (cancelled 不再归并 failed)。crashed 不进 finalize 入参(TS 签名锁定 done/failed/cancelled)。
5
- //
6
- // 测试策略:record 不带 sessionFile/worktreeHandle,跳过 Step 0 (collectPatch) 和 Step 3
7
- // (finalized/tombstone/aliveMarker/worktree cleanup) 的文件操作,聚焦 Step 4 writeManifest
8
- // 的 status 产出。FinalizeDeps 用 stub 注入(manifestStore 为真实实例,指向 tmpDir)。
9
-
10
- import * as fs from "node:fs";
11
- import * as os from "node:os";
12
- import * as path from "node:path";
13
- import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
14
-
15
- // Mock 共享 logger,让 logger.error 可被 spy(源码已从 console.error 改为 logger.error)
16
- const { loggerMock } = vi.hoisted(() => ({
17
- loggerMock: {
18
- debug: vi.fn(),
19
- warn: vi.fn(),
20
- error: vi.fn(),
21
- info: vi.fn(),
22
- },
23
- }));
24
- vi.mock("@zhushanwen/pi-extension-logger", () => ({
25
- getLogger: () => loggerMock,
26
- }));
27
-
28
- import { doFinalizeRecord, doFinalizeRoundToIdle } from "../finalize-record.ts";
29
- import { ManifestStore } from "../manifest-store.ts";
30
- import type { AgentResult, ExecutionRecord } from "../types.ts";
31
-
32
- function makeMinimalRecord(overrides: Partial<ExecutionRecord> = {}): ExecutionRecord {
33
- return {
34
- id: "finalize-test",
35
- agent: "worker",
36
- model: "test/model",
37
- thinkingLevel: undefined,
38
- mode: "background",
39
- task: "test",
40
- slug: "test",
41
- startedAt: 1000,
42
- rootSessionId: "session-main",
43
- parentRecordId: undefined,
44
- depth: 0,
45
- status: "running",
46
- turns: [],
47
- turnCount: 0,
48
- totalTokens: 0,
49
- lastError: undefined,
50
- endedAt: undefined,
51
- result: undefined,
52
- error: undefined,
53
- agentResult: undefined,
54
- sessionFile: undefined,
55
- controller: undefined,
56
- ...overrides,
57
- } as ExecutionRecord;
58
- }
59
-
60
- function makeMinimalResult(): AgentResult {
61
- return {
62
- text: "done",
63
- turns: 1,
64
- durationMs: 100,
65
- success: true,
66
- sessionId: "sess-1",
67
- toolCalls: [],
68
- };
69
- }
70
-
71
- describe("doFinalizeRecord — manifest status 透传 (M3 4 态)", () => {
72
- let tmpDir: string;
73
- let manifestStore: ManifestStore;
74
-
75
- beforeEach(() => {
76
- tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "finalize-test-"));
77
- manifestStore = new ManifestStore(tmpDir);
78
- });
79
-
80
- afterEach(() => {
81
- fs.rmSync(tmpDir, { recursive: true, force: true });
82
- });
83
-
84
- /** 构造最小 FinalizeDeps:record 无 sessionFile/worktreeHandle,跳过 Step 0/3 文件操作。 */
85
- function makeDeps() {
86
- return {
87
- manifestStore,
88
- worktreeManager: {} as never,
89
- store: { archive: vi.fn(), reportRecordTransition: vi.fn() } as never,
90
- modelService: {} as never,
91
- pi: { appendEntry: vi.fn() },
92
- emitUnregister: vi.fn(),
93
- };
94
- }
95
-
96
- it("status=closed + cancelled reason → manifest 写 closed(v4 B-1:cancelled 折入 closed)", async () => {
97
- const record = makeMinimalRecord({ id: "rec-cancelled" });
98
- await doFinalizeRecord(makeDeps(), record, makeMinimalResult(), "closed", "cancelled");
99
-
100
- const manifest = await manifestStore.readManifest("rec-cancelled");
101
- expect(manifest).not.toBeNull();
102
- // 关键断言:v4 B-1 manifest status 恒写 closed(cancelled 区分靠 tombstone sidecar)
103
- expect(manifest?.status).toBe("closed");
104
- });
105
-
106
- it("status=closed + user-close → manifest 写 closed", async () => {
107
- const record = makeMinimalRecord({ id: "rec-done" });
108
- await doFinalizeRecord(makeDeps(), record, makeMinimalResult(), "closed", "user-close");
109
-
110
- const manifest = await manifestStore.readManifest("rec-done");
111
- expect(manifest?.status).toBe("closed");
112
- });
113
-
114
- it("status=closed + gc → manifest 写 closed", async () => {
115
- const record = makeMinimalRecord({ id: "rec-failed" });
116
- await doFinalizeRecord(makeDeps(), record, makeMinimalResult(), "closed", "gc");
117
-
118
- const manifest = await manifestStore.readManifest("rec-failed");
119
- expect(manifest?.status).toBe("closed");
120
- });
121
-
122
- it("manifest write 抛错时 cleanup-first 顺序仍执行(Step 3 before Step 4 throw)", async () => {
123
- // record 带 sessionFile 让 Step 3 finalized/aliveMarker 走真实路径;
124
- // 不设 worktreeHandle → Step 0 (collectPatch) 和 Step 3 worktree cleanup 都跳过。
125
- const sessionFile = path.join(tmpDir, "session.jsonl");
126
- const record = makeMinimalRecord({ id: "rec-cleanup-first", sessionFile });
127
-
128
- // 预写 .alive marker(让 removeAliveMarker 真实生效;不预写因 ENOENT 静默也 OK,
129
- // 但预写后用 fs.existsSync 验证更直观)。
130
- fs.writeFileSync(
131
- `${sessionFile}.alive`,
132
- `${JSON.stringify({ pid: 99999, id: "rec-cleanup-first", startedAt: 1000 })}\n`,
133
- "utf-8",
134
- );
135
-
136
- // mock writeManifest 抛错(模拟 disk full)。在 mock 内捕获「writeManifest 被调用时
137
- // finalized marker 是否已存在」——这是 cleanup-first 顺序的关键断言:若有人把
138
- // manifest write 前移到 cleanup 之前,本标志会是 false([Critical #1] 反例)。
139
- const finalizedBeforeManifestWrite = { value: false };
140
- vi.spyOn(manifestStore, "writeManifest").mockImplementation(async () => {
141
- finalizedBeforeManifestWrite.value = fs.existsSync(`${sessionFile}.finalized`);
142
- throw new Error("disk full");
143
- });
144
- loggerMock.error.mockClear();
145
-
146
- const deps = makeDeps();
147
-
148
- // ── 核心 claim 1:不抛错(cleanup-first → manifest write 失败不 throw)──
149
- await expect(
150
- doFinalizeRecord(deps, record, makeMinimalResult(), "closed"),
151
- ).resolves.toBeUndefined();
152
-
153
- // ── 核心 claim 2:Step 3 cleanup 先执行 —— finalized marker 真实写入 ──
154
- expect(fs.existsSync(`${sessionFile}.finalized`)).toBe(true);
155
-
156
- // ── 核心 claim 3:Step 3 aliveMarker 被移除(预写的 .alive 不再存在)──
157
- expect(fs.existsSync(`${sessionFile}.alive`)).toBe(false);
158
-
159
- // ── 核心 claim 4:pending-notifications 注销仍触发(emitUnregister)──
160
- expect(deps.emitUnregister).toHaveBeenCalledWith("rec-cleanup-first", "closed");
161
-
162
- // ── 核心 claim 5:manifest 写失败被 logger.error 记录(含 record id + error)──
163
- expect(loggerMock.error).toHaveBeenCalledWith(expect.stringContaining("manifest 写入失败"));
164
- const errMsg = loggerMock.error.mock.calls[0]?.[0];
165
- expect(errMsg).toContain("rec-cleanup-first");
166
- expect(errMsg).toContain("disk full");
167
-
168
- // ── 核心 claim 6:pi.appendEntry 记录 "subagent:manifest-write-failed" 事件 ──
169
- expect(deps.pi.appendEntry).toHaveBeenCalledWith(
170
- "subagent:manifest-write-failed",
171
- expect.objectContaining({ id: "rec-cleanup-first", error: "disk full" }),
172
- );
173
-
174
- // ── 核心 claim 7:manifest 实际未写入(writeManifest 抛错被吞咽)──
175
- expect(await manifestStore.readManifest("rec-cleanup-first")).toBeNull();
176
-
177
- // ── 核心 claim 8:[Critical #1] cleanup 在 manifest 写之前 —— 顺序锁定 ──
178
- // 若有人把 manifest write 前移到 cleanup 之前,本标志会是 false(mock 捕获时刻
179
- // .finalized 尚未被 Step 3 写入),保护 Critical #1 时序不变量。
180
- expect(finalizedBeforeManifestWrite.value).toBe(true);
181
-
182
- // 清理 mock 调用记录防污染
183
- loggerMock.error.mockClear();
184
- });
185
- });
186
-
187
- // ============================================================
188
- // doFinalizeRoundToIdle — chatMode 轮次完成进 idle(M2-A)
189
- // ============================================================
190
-
191
- describe("doFinalizeRoundToIdle — chatMode 轮次完成进 idle (M2-A)", () => {
192
- let tmpDir: string;
193
- let manifestStore: ManifestStore;
194
-
195
- beforeEach(() => {
196
- tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "finalize-idle-test-"));
197
- manifestStore = new ManifestStore(tmpDir);
198
- });
199
-
200
- afterEach(() => {
201
- fs.rmSync(tmpDir, { recursive: true, force: true });
202
- });
203
-
204
- /** 构造 FinalizeDeps:worktreeManager.cleanup / store.archive 为 vi.fn 以断言「不调」。 */
205
- function makeDeps() {
206
- return {
207
- manifestStore,
208
- worktreeManager: { cleanup: vi.fn(), collectPatch: vi.fn() } as never,
209
- store: { archive: vi.fn(), reportRecordTransition: vi.fn() } as never,
210
- modelService: {} as never,
211
- pi: { appendEntry: vi.fn() } as never,
212
- emitUnregister: vi.fn(),
213
- };
214
- }
215
-
216
- it("record 带 sessionFile → 删 .alive + record.status=running + round 0→1", async () => {
217
- const sessionFile = path.join(tmpDir, "session.jsonl");
218
- // 预写 .alive marker(验证 doFinalizeRoundToIdle 删除它——进程已回收)
219
- fs.writeFileSync(
220
- `${sessionFile}.alive`,
221
- `${JSON.stringify({ pid: 99999, id: "rec-idle", startedAt: 1000 })}\n`,
222
- "utf-8",
223
- );
224
- const record = makeMinimalRecord({ id: "rec-idle", sessionFile, chatMode: true, round: 0 });
225
- // tryTransition 已把 status 设为 done,模拟 runAndFinalize 调用前的状态
226
- record.status = "closed";
227
-
228
- await doFinalizeRoundToIdle(makeDeps(), record, makeMinimalResult());
229
-
230
- // 状态机(v4 B-1:旧 idle 折入 running,finalizeRoundToIdle 设 running)
231
- expect(record.status).toBe("running");
232
- expect(record.round).toBe(1);
233
- // .alive marker 被删(进程已 SIGTERM 回收)
234
- expect(fs.existsSync(`${sessionFile}.alive`)).toBe(false);
235
- });
236
-
237
- it("W16: 轮终上报 reportRecordTransition(record-store 类外恢复写点迁移落 entry)", async () => {
238
- const deps = makeDeps();
239
- const record = makeMinimalRecord({ id: "rec-report", chatMode: true, round: 2 });
240
- record.status = "closed";
241
- await doFinalizeRoundToIdle(deps, record, makeMinimalResult());
242
- expect(deps.store.reportRecordTransition).toHaveBeenCalledTimes(1);
243
- // 上报发生在 round 推进之后(entry 携带新轮计数,重建源不滞后)
244
- expect(record.round).toBe(3);
245
- expect(record.status).toBe("running");
246
- });
247
-
248
- it("record.round 已为 N → round 变 N+1", async () => {
249
- const record = makeMinimalRecord({ id: "rec-round", round: 3 });
250
- record.status = "closed";
251
- await doFinalizeRoundToIdle(makeDeps(), record, makeMinimalResult());
252
- expect(record.round).toBe(4);
253
- expect(record.status).toBe("running");
254
- });
255
-
256
- it("不调 store.archive(record 留内存,getMutable 仍可查)", async () => {
257
- const deps = makeDeps();
258
- const record = makeMinimalRecord({ id: "rec-noarchive" });
259
- record.status = "closed";
260
- await doFinalizeRoundToIdle(deps, record, makeMinimalResult());
261
- expect(deps.store.archive).not.toHaveBeenCalled();
262
- });
263
-
264
- it("不 cleanup worktree(即使 record 带 worktreeHandle——保留对话模式工作目录)", async () => {
265
- const deps = makeDeps();
266
- const record = makeMinimalRecord({
267
- id: "rec-noworktree",
268
- worktreeHandle: { path: "/tmp/x", branch: "b", baseCommit: "c", mainCwd: "/tmp" } as never,
269
- });
270
- record.status = "closed";
271
- await doFinalizeRoundToIdle(deps, record, makeMinimalResult());
272
- expect(deps.worktreeManager.cleanup).not.toHaveBeenCalled();
273
- });
274
-
275
- it("emitUnregister 被调(status=running,进程已死从 pending 活跃差集移除)", async () => {
276
- const deps = makeDeps();
277
- const record = makeMinimalRecord({ id: "rec-emit" });
278
- record.status = "closed";
279
- await doFinalizeRoundToIdle(deps, record, makeMinimalResult());
280
- expect(deps.emitUnregister).toHaveBeenCalledWith("rec-emit", "running");
281
- });
282
-
283
- it("不调 completeRecord:record 不冻结(endedAt / agentResult 仍 undefined)", async () => {
284
- const record = makeMinimalRecord({ id: "rec-nofreeze" });
285
- record.status = "closed";
286
- await doFinalizeRoundToIdle(makeDeps(), record, makeMinimalResult());
287
- expect(record.endedAt).toBeUndefined();
288
- expect(record.agentResult).toBeUndefined();
289
- });
290
-
291
- it("不写 manifest(idle 非终态,manifest 是终态诊断辅助)", async () => {
292
- const record = makeMinimalRecord({ id: "rec-nomanifest" });
293
- record.status = "closed";
294
- await doFinalizeRoundToIdle(makeDeps(), record, makeMinimalResult());
295
- const manifest = await manifestStore.readManifest("rec-nomanifest");
296
- expect(manifest).toBeNull();
297
- });
298
-
299
- it("MF-2: record.result 设为 result.text(否则 notifier idle 回复恒为 (empty),G1/G2 不成立)", async () => {
300
- const record = makeMinimalRecord({ id: "rec-result" });
301
- record.status = "closed";
302
- const result = makeMinimalResult();
303
- result.text = "review done, found 3 issues";
304
- await doFinalizeRoundToIdle(makeDeps(), record, result);
305
- // MF-2:record.result 被 result.text 填充,notifier idle 分支读取后携带回复正文
306
- expect(record.result).toBe("review done, found 3 issues");
307
- });
308
-
309
- it("MF-2 兑底:失败轮次 result.text 为空时用 result.error 填 record.result(MF-6 回退 idle 路径)", async () => {
310
- const record = makeMinimalRecord({ id: "rec-result-err" });
311
- record.status = "closed";
312
- const result = makeMinimalResult();
313
- result.text = "";
314
- result.success = false;
315
- result.error = "spawn timeout";
316
- await doFinalizeRoundToIdle(makeDeps(), record, result);
317
- // 失败轮次的 notify 需可读:result.text 空 → 兑底用 error
318
- expect(record.result).toBe("round did not complete: spawn timeout");
319
- });
320
-
321
- it("C1TC10: chatMode 空增量轮占位——record.result 固定 (no output this round),不含上一轮文本(D5)", async () => {
322
- const record = makeMinimalRecord({ id: "rec-increment-empty", chatMode: true });
323
- record.status = "closed";
324
- // 预置上一轮通知文本(模拟增量语义前的 record.result 残留)
325
- record.result = "PREV-ROUND-TEXT";
326
- const result = makeMinimalResult();
327
- result.text = "";
328
- result.success = true;
329
- result.error = undefined;
330
- await doFinalizeRoundToIdle(makeDeps(), record, result);
331
- // 空增量轮通知不含上一轮文本:沿用旧值会让父 agent 误读为原样重复回复(D5 判定)
332
- expect(record.result).toBe("(no output this round)");
333
- expect(record.result).not.toContain("PREV-ROUND-TEXT");
334
- });
335
-
336
- it("C1TC11 [R2-1]: one-shot 首轮空文本成功完成 → record.result 补占位「(empty)」(非 undefined)", async () => {
337
- const record = makeMinimalRecord({ id: "rec-oneshot-empty", chatMode: false });
338
- record.status = "closed";
339
- // one-shot 成功空文本完成路径(collectResult getFullText 返回 ""、success=true):
340
- // result 前值 undefined
341
- record.result = undefined;
342
- const result = makeMinimalResult();
343
- result.text = "";
344
- result.success = true;
345
- result.error = undefined;
346
- await doFinalizeRoundToIdle(makeDeps(), record, result);
347
- // [R2-1] 轮终信号优先:占位非 undefined 是 renderer hasRunning 排除轮终 running 的
348
- // 判据——保持 undefined 会让完成注入后末位 turn 永久「工作中」
349
- expect(record.result).toBe("(empty)");
350
- // notifier buildLlmContent 的 record.result ?? "(empty)" 兜底同款措辞:
351
- // 通知文案逐字节产出 "completed. Result:\n(empty)"(G4 保持)
352
- expect(record.result ?? "(empty)").toBe("(empty)");
353
- });
354
-
355
- it("C1TC11b [R2-1]: one-shot 续轮空文本 → 沿用前值(one-shot 无增量语义,不覆盖为占位)", async () => {
356
- const record = makeMinimalRecord({ id: "rec-oneshot-cont", chatMode: false });
357
- record.status = "closed";
358
- // 上一轮真实产出(续轮 record.result 前值)
359
- record.result = "first round output";
360
- const result = makeMinimalResult();
361
- result.text = "";
362
- result.success = true;
363
- result.error = undefined;
364
- await doFinalizeRoundToIdle(makeDeps(), record, result);
365
- // one-shot record.result = 该 subagent 最终输出:空文本续轮沿用前值,不被 "(empty)" 覆盖
366
- expect(record.result).toBe("first round output");
367
- });
368
- });
@@ -1,82 +0,0 @@
1
- // src/__tests__/finalized-marker.test.ts
2
- //
3
- // finalized-marker 专属测试。
4
- // 覆盖:write→read 往返 / 缺 sidecar → false / best-effort IO 错静默 / .cancelled 互斥。
5
-
6
- import * as fs from "node:fs";
7
- import * as os from "node:os";
8
- import * as path from "node:path";
9
-
10
- import { afterEach, beforeEach, describe, expect, it } from "vitest";
11
-
12
- import { readFinalized, writeFinalized } from "../finalized-marker.ts";
13
-
14
- describe("finalized-marker", () => {
15
- let tmpDir: string;
16
- let sessionFile: string;
17
-
18
- beforeEach(() => {
19
- tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "fm-test-"));
20
- sessionFile = path.join(tmpDir, "2026-01-01_uuid.jsonl");
21
- });
22
- afterEach(() => {
23
- fs.rmSync(tmpDir, { recursive: true, force: true });
24
- });
25
-
26
- describe("write → read 往返", () => {
27
- it("写入后 readFinalized 返回 true", () => {
28
- writeFinalized(sessionFile);
29
- expect(readFinalized(sessionFile)).toBe(true);
30
- });
31
-
32
- it("sidecar 路径 = sessionFile + '.finalized'", () => {
33
- writeFinalized(sessionFile);
34
- expect(fs.existsSync(`${sessionFile}.finalized`)).toBe(true);
35
- });
36
-
37
- it("sidecar 内容为空(存在性即信号)", () => {
38
- writeFinalized(sessionFile);
39
- const content = fs.readFileSync(`${sessionFile}.finalized`, "utf-8");
40
- expect(content).toBe("");
41
- });
42
- });
43
-
44
- describe("readFinalized 无 sidecar", () => {
45
- it("不存在 → false", () => {
46
- expect(readFinalized(sessionFile)).toBe(false);
47
- });
48
- });
49
-
50
- describe("best-effort IO 错静默", () => {
51
- it("writeFileSync 抛错时不抛出", () => {
52
- // 目标路径不存在(不存在的父目录),writeFileSync 会抛
53
- const badPath = path.join(tmpDir, "nonexistent-sub", "session.jsonl");
54
- expect(() => writeFinalized(badPath)).not.toThrow();
55
- });
56
-
57
- it("写失败后 readFinalized 返回 false(sidecar 未写入)", () => {
58
- const badPath = path.join(tmpDir, "nonexistent-sub", "session.jsonl");
59
- writeFinalized(badPath); // 静默失败
60
- expect(readFinalized(badPath)).toBe(false);
61
- });
62
- });
63
-
64
- describe("与 .cancelled 互斥(BC-4)", () => {
65
- it("写 finalized 时删除已有的 .cancelled", () => {
66
- // 先写一个 .cancelled sidecar
67
- fs.writeFileSync(`${sessionFile}.cancelled`, `{"id":"bg-1","status":"cancelled","agent":"w","startedAt":1,"endedAt":2}\n`, "utf-8");
68
- expect(fs.existsSync(`${sessionFile}.cancelled`)).toBe(true);
69
-
70
- // 写 finalized → .cancelled 应被删除
71
- writeFinalized(sessionFile);
72
- expect(fs.existsSync(`${sessionFile}.cancelled`)).toBe(false);
73
- expect(readFinalized(sessionFile)).toBe(true);
74
- });
75
-
76
- it("无 .cancelled 时写 finalized 正常(不报错)", () => {
77
- expect(fs.existsSync(`${sessionFile}.cancelled`)).toBe(false);
78
- expect(() => writeFinalized(sessionFile)).not.toThrow();
79
- expect(readFinalized(sessionFile)).toBe(true);
80
- });
81
- });
82
- });
@@ -1,138 +0,0 @@
1
- // src/__tests__/format-schema-instruction.test.ts
2
- //
3
- // 锁定 formatSchemaInstruction 契约:构造 schema enforcement 的 MANDATORY 指令。
4
- // 该字符串被拼入 task 末尾(runSpawn 的 fullTask = task + instruction),且 steer
5
- // reminder 复用同一文本。一旦漏掉 "MUST call structured-output" 关键词或 JSON
6
- // 序列化漂移,schema 模式会静默失效——agent 可能直接把 JSON 写进文本响应。
7
- //
8
- // 纯函数测试:不依赖 Pi 运行时、不 spawn 进程、不 mock。只 import 被测函数。
9
- import { describe, expect, it } from "vitest";
10
-
11
- import { formatSchemaInstruction } from "../session-runner.ts";
12
-
13
- describe("formatSchemaInstruction", () => {
14
- // ── 指令文本契约 ──────────────────────────────────────────────
15
-
16
- it("contains the structured-output tool keyword", () => {
17
- const out = formatSchemaInstruction({ type: "object" });
18
- expect(out).toContain("structured-output");
19
- });
20
-
21
- it("emits a MANDATORY structured-output directive (not free-form JSON)", () => {
22
- const out = formatSchemaInstruction({ type: "object" });
23
- expect(out).toContain("MANDATORY");
24
- expect(out).toContain("MUST");
25
- expect(out).toContain("MUST call the `structured-output` tool");
26
- expect(out).toContain("Do NOT output the JSON directly");
27
- });
28
-
29
- // ── schema 序列化 ─────────────────────────────────────────────
30
-
31
- it("embeds the schema as pretty-printed JSON (indent=2) inside a fenced block", () => {
32
- const schema: Record<string, unknown> = {
33
- type: "object",
34
- properties: { name: { type: "string" } },
35
- };
36
- const out = formatSchemaInstruction(schema);
37
- // 必须包含 JSON.stringify(schema, null, 2) 的完整结果
38
- expect(out).toContain(JSON.stringify(schema, null, 2));
39
- expect(out).toContain("```json");
40
- expect(out).toContain("```");
41
- // indent=2 的可观察证据:属性键前有恰好两个空格(object 第一层缩进)
42
- expect(out).toContain('\n "type": "object"');
43
- });
44
-
45
- it("locks the full output structure for a minimal schema", () => {
46
- const out = formatSchemaInstruction({ type: "object" });
47
- // 完整结构快照——任何指令措辞/顺序/缩进漂移都会被捕获。
48
- // 注意第三行末尾的 em-dash(—),防止有人把它替换成普通连字符。
49
- // [HISTORICAL] 方案 A 后文案更新:告知 LLM schema 由系统注入,只需传 data。
50
- expect(out).toBe(
51
- [
52
- "MANDATORY: Structured Output Requirement",
53
- "You MUST call the `structured-output` tool with your final answer.",
54
- "Do NOT output the JSON directly in your text response — you MUST use the structured-output tool.",
55
- "The schema is enforced by the system — call structured-output with ONLY the `data` parameter.",
56
- "Do NOT pass a `schema` parameter; the system validates `data` against the authoritative schema automatically.",
57
- "The schema for your `data` is:",
58
- "```json",
59
- '{',
60
- ' "type": "object"',
61
- '}',
62
- "```",
63
- ].join("\n"),
64
- );
65
- });
66
-
67
- // ── 特殊字符转义(注入风险路径)──────────────────────────────
68
-
69
- it("escapes double quotes inside schema string values", () => {
70
- const schema: Record<string, unknown> = { prompt: 'say "hi"' };
71
- const out = formatSchemaInstruction(schema);
72
- // JSON.stringify 会把内层 " 转义为 \"
73
- expect(out).toContain('say \\"hi\\"');
74
- // 原始未转义形式(含成对字面双引号)绝不能回流进 JSON 体内
75
- expect(out).not.toContain('say "hi"');
76
- });
77
-
78
- it("escapes newlines inside schema string values", () => {
79
- const schema: Record<string, unknown> = { text: "line1\nline2" };
80
- const out = formatSchemaInstruction(schema);
81
- // 换行被序列化为字面反斜杠-n,不能是真实换行符
82
- expect(out).toContain("line1\\nline2");
83
- expect(out).not.toContain("line1\nline2");
84
- });
85
-
86
- it("escapes backslashes inside schema string values", () => {
87
- const schema: Record<string, unknown> = { path: "C:\\Users\\x" };
88
- const out = formatSchemaInstruction(schema);
89
- // 单反斜杠被序列化为 \\,避免后续解析误把转义序列当指令
90
- expect(out).toContain("C:\\\\Users\\\\x");
91
- expect(out).not.toContain("C:\\Users\\x");
92
- });
93
-
94
- // ── 边界值 ────────────────────────────────────────────────────
95
-
96
- it("handles empty schema object", () => {
97
- const out = formatSchemaInstruction({});
98
- expect(out).toContain("structured-output");
99
- expect(out).toContain("{}");
100
- });
101
-
102
- it("preserves null values in schema", () => {
103
- const schema: Record<string, unknown> = { default: null };
104
- const out = formatSchemaInstruction(schema);
105
- // JSON.stringify 对 null 保留字面 "null"(不会 omit 键,也不会变字符串)
106
- expect(out).toContain('"default": null');
107
- });
108
-
109
- it("serializes nested objects and arrays", () => {
110
- const schema: Record<string, unknown> = {
111
- type: "object",
112
- required: ["name", "age"],
113
- properties: {
114
- name: { type: "string" },
115
- age: { type: "integer", minimum: 0 },
116
- },
117
- };
118
- const out = formatSchemaInstruction(schema);
119
- expect(out).toContain(JSON.stringify(schema, null, 2));
120
- // 嵌套结构 indent 正确(第二层 4 空格)
121
- expect(out).toContain(' "name": {');
122
- expect(out).toContain(' "type": "string"');
123
- });
124
-
125
- // ── 确定性 ────────────────────────────────────────────────────
126
-
127
- it("is deterministic — same schema produces identical output", () => {
128
- const schema: Record<string, unknown> = { a: 1, b: [2, 3] };
129
- expect(formatSchemaInstruction(schema)).toBe(formatSchemaInstruction(schema));
130
- });
131
-
132
- it("is deterministic across different object key insertion (value-equal schemas)", () => {
133
- // JSON.stringify 按对象自身属性顺序序列化;同序构造的等价 schema 应产出相同指令
134
- const a: Record<string, unknown> = { x: 1, y: 2 };
135
- const b: Record<string, unknown> = { x: 1, y: 2 };
136
- expect(formatSchemaInstruction(a)).toBe(formatSchemaInstruction(b));
137
- });
138
- });