@zhushanwen/subagent-core 0.2.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 (347) hide show
  1. package/README.md +45 -2
  2. package/agents/analyst.md +60 -0
  3. package/agents/coder.md +69 -0
  4. package/agents/debugger.md +66 -0
  5. package/agents/doc-reviewer.md +49 -0
  6. package/agents/explorer.md +63 -0
  7. package/agents/general-purpose.md +32 -0
  8. package/agents/orchestrator.md +61 -0
  9. package/agents/planner.md +53 -0
  10. package/agents/researcher.md +67 -0
  11. package/agents/reviewer.md +73 -0
  12. package/dist/chunk-43ONBFZX.js +240 -0
  13. package/dist/chunk-APZY4IME.js +27 -0
  14. package/dist/chunk-V3VHZ2VX.js +59 -0
  15. package/dist/execution/engine/engines/zcode/constants.cjs +62 -9
  16. package/dist/execution/engine/engines/zcode/constants.d.cts +116 -12
  17. package/dist/execution/engine/engines/zcode/constants.d.ts +116 -12
  18. package/dist/execution/engine/engines/zcode/constants.js +39 -7
  19. package/dist/execution/engine/engines/zcode/reader.d.cts +2 -1
  20. package/dist/execution/engine/engines/zcode/reader.d.ts +2 -1
  21. package/dist/execution/engine/engines/zcode/reader.js +4 -234
  22. package/dist/execution/engine/paths.js +7 -19
  23. package/dist/index.cjs +18234 -1277
  24. package/dist/index.d.cts +5646 -673
  25. package/dist/index.d.ts +5646 -673
  26. package/dist/index.js +17828 -1277
  27. package/dist/{types-BxyAidGf.d.cts → types-Dv4QhSJ_.d.cts} +678 -112
  28. package/dist/{types-BxyAidGf.d.ts → types-Dv4QhSJ_.d.ts} +678 -112
  29. package/package.json +7 -4
  30. package/src/__tests__/agent-opts-resolver-schema-prompt.test.ts +0 -1
  31. package/src/__tests__/append-system-prompt-assembly.test.ts +8 -7
  32. package/src/__tests__/fr4-get-state-handshake.test.ts +2 -2
  33. package/src/__tests__/m2-append-content-probe.test.ts +0 -1
  34. package/src/__tests__/manifest-store.test.ts +10 -9
  35. package/src/__tests__/record-store-cache.test.ts +0 -2
  36. package/src/__tests__/record-store-index.test.ts +1 -2
  37. package/src/__tests__/review-fix-loop-script.test.ts +93 -3
  38. package/src/__tests__/review-fix-loop-utils.test.ts +31 -10
  39. package/src/__tests__/robustness-low-batch1.test.ts +6 -5
  40. package/src/__tests__/robustness-medium-batch1.test.ts +2 -2
  41. package/src/__tests__/robustness-medium-batch2.test.ts +17 -6
  42. package/src/__tests__/robustness-medium-batch4.test.ts +2 -2
  43. package/src/__tests__/session-runner.test.ts +1 -1
  44. package/src/__tests__/smoke.test.ts +9 -2
  45. package/src/core/__tests__/host-services.test.ts +2 -2
  46. package/src/core/__tests__/logger.test.ts +1 -1
  47. package/src/core/error-message.ts +9 -0
  48. package/src/core/host-services.ts +21 -5
  49. package/src/core/notify-ports.ts +18 -5
  50. package/src/execution/__tests__/agent-profile.test.ts +232 -0
  51. package/src/execution/__tests__/agent-registry.test.ts +370 -0
  52. package/src/execution/__tests__/agent-result-mapper.test.ts +18 -2
  53. package/src/execution/__tests__/agents-assembly.test.ts +219 -0
  54. package/src/execution/__tests__/ask-user-transit-e2e.test.ts +6 -6
  55. package/src/execution/__tests__/chat-engine-routing.test.ts +136 -30
  56. package/src/execution/__tests__/chatmode-first-round-closure-spawn.test.ts +5 -4
  57. package/src/execution/__tests__/cold-resurrect.test.ts +283 -0
  58. package/src/execution/__tests__/create-concurrency-pool.test.ts +194 -0
  59. package/src/execution/__tests__/delivery-methods.test.ts +77 -62
  60. package/src/execution/__tests__/descendant-sweep-guards.test.ts +220 -0
  61. package/src/execution/__tests__/descendant-sweep.test.ts +269 -0
  62. package/src/execution/__tests__/dialog-queue.test.ts +199 -1
  63. package/src/execution/__tests__/engine-model-validation.test.ts +741 -0
  64. package/src/execution/__tests__/epipe-fallback.test.ts +31 -24
  65. package/src/execution/__tests__/execute-and-await-worktree.test.ts +21 -91
  66. package/src/execution/__tests__/execute-nesting.test.ts +34 -188
  67. package/src/execution/__tests__/execution-record.test.ts +217 -60
  68. package/src/execution/__tests__/execution-runtime-face.test.ts +272 -0
  69. package/src/execution/__tests__/explicit-agent-ref-guard.test.ts +3 -3
  70. package/src/execution/__tests__/finalize-record.test.ts +278 -16
  71. package/src/execution/__tests__/gc-timer.test.ts +57 -2
  72. package/src/execution/__tests__/get-record-for-action-restart.test.ts +14 -12
  73. package/src/execution/__tests__/get-state-handshake.test.ts +127 -0
  74. package/src/execution/__tests__/helpers/mock-extension-api.ts +1 -1
  75. package/src/execution/__tests__/helpers/session-runner-mocks.ts +113 -0
  76. package/src/execution/__tests__/helpers/spawn-mock.ts +150 -3
  77. package/src/execution/__tests__/helpers/subagent-service-mocks.ts +137 -0
  78. package/src/execution/__tests__/keep-alive-no-progress.test.ts +280 -0
  79. package/src/execution/__tests__/kill-all-escalation.test.ts +196 -0
  80. package/src/execution/__tests__/lifecycle-manager-idle-timer-identity.test.ts +117 -0
  81. package/src/execution/__tests__/lifecycle-manager.test.ts +39 -0
  82. package/src/execution/__tests__/lifecycle-predicates.test.ts +1 -2
  83. package/src/execution/__tests__/manifest-parentid.test.ts +4 -4
  84. package/src/execution/__tests__/manifest-store-tmp-recovery.test.ts +99 -0
  85. package/src/execution/__tests__/max-turns-to-watchdog-ms.test.ts +113 -0
  86. package/src/execution/__tests__/model-resolver.test.ts +2 -2
  87. package/src/execution/__tests__/nested-visibility-env-propagation.test.ts +23 -121
  88. package/src/execution/__tests__/nested-visibility.test.ts +2 -0
  89. package/src/execution/__tests__/notify-ledger.test.ts +1249 -0
  90. package/src/execution/__tests__/output-collector.test.ts +117 -6
  91. package/src/execution/__tests__/pi-invocation.test.ts +1 -1
  92. package/src/execution/__tests__/record-store-orphan-revive.test.ts +159 -0
  93. package/src/execution/__tests__/record-store.test.ts +89 -5
  94. package/src/execution/__tests__/records-cwd-isolation.test.ts +2 -2
  95. package/src/execution/__tests__/recursive-visibility-baseline.test.ts +29 -132
  96. package/src/execution/__tests__/recursive-visibility-env.test.ts +55 -179
  97. package/src/execution/__tests__/rpc-mode.test.ts +2 -2
  98. package/src/execution/__tests__/run-and-finalize-anchoring.test.ts +387 -0
  99. package/src/execution/__tests__/run-and-finalize-chatmode.test.ts +19 -9
  100. package/src/execution/__tests__/run-spawn-chatmode-settled.test.ts +5 -4
  101. package/src/execution/__tests__/run-spawn-edges.test.ts +418 -26
  102. package/src/execution/__tests__/run-spawn-integration.test.ts +3 -3
  103. package/src/execution/__tests__/run-spawn-resume.test.ts +2 -2
  104. package/src/execution/__tests__/run-spawn-rpc-mode.test.ts +2 -2
  105. package/src/execution/__tests__/run-spawn-stdout-callback-throw.test.ts +16 -8
  106. package/src/execution/__tests__/service-kill-escalation.test.ts +91 -0
  107. package/src/execution/__tests__/session-context-resolver.test.ts +0 -2
  108. package/src/execution/__tests__/session-file-gc.test.ts +35 -0
  109. package/src/execution/__tests__/session-pending.test.ts +471 -0
  110. package/src/execution/__tests__/session-reconstructor.test.ts +366 -15
  111. package/src/execution/__tests__/session-runner-branch-cache-lru.test.ts +110 -0
  112. package/src/execution/__tests__/session-runner-close-prune.test.ts +181 -0
  113. package/src/execution/__tests__/session-runner-dispatch.test.ts +489 -0
  114. package/src/execution/__tests__/session-runner-epipe.test.ts +4 -3
  115. package/src/execution/__tests__/session-runner-heartbeat-idle-fallback.test.ts +227 -0
  116. package/src/execution/__tests__/session-runner-lifecycle-helpers.test.ts +290 -0
  117. package/src/execution/__tests__/session-runner-schema-env.test.ts +7 -5
  118. package/src/execution/__tests__/settled-watchdog.test.ts +524 -0
  119. package/src/execution/__tests__/spawn-args.test.ts +11 -10
  120. package/src/execution/__tests__/spawn-event-adapter-rpc.test.ts +26 -23
  121. package/src/execution/__tests__/spawn-event-adapter.test.ts +1 -1
  122. package/src/execution/__tests__/spawn-worktree-guidance.test.ts +5 -4
  123. package/src/execution/__tests__/spawned-children.test.ts +155 -11
  124. package/src/execution/__tests__/start-sync-model-guard.test.ts +4 -4
  125. package/src/execution/__tests__/stdin-writer.test.ts +6 -4
  126. package/src/execution/__tests__/stream-sink-retirement.test.ts +6 -6
  127. package/src/execution/__tests__/subagent-actions-core.test.ts +1017 -0
  128. package/src/execution/__tests__/subagent-service-message-close.test.ts +99 -35
  129. package/src/execution/__tests__/subagent-service-multiproc-guard.test.ts +174 -0
  130. package/src/execution/__tests__/subagent-service-notify-gate.test.ts +271 -0
  131. package/src/execution/__tests__/subagent-service-parent-guard.test.ts +33 -29
  132. package/src/execution/__tests__/subagent-service-recovery-bounds.test.ts +341 -0
  133. package/src/execution/__tests__/subagent-service.test.ts +37 -30
  134. package/src/execution/__tests__/subprocess-agent-runner-routing.test.ts +88 -12
  135. package/src/execution/__tests__/subprocess-agent-runner-timeout.test.ts +75 -0
  136. package/src/execution/__tests__/subprocess-agent-runner.test.ts +22 -20
  137. package/src/execution/__tests__/temp-prompt.test.ts +1 -1
  138. package/src/execution/__tests__/timeout-integration.test.ts +13 -12
  139. package/src/execution/__tests__/turn-limiter-semantics.test.ts +1 -1
  140. package/src/execution/__tests__/turn-limiter.test.ts +1 -1
  141. package/src/execution/__tests__/ui-request-handler-factory.test.ts +107 -7
  142. package/src/execution/__tests__/ui-request-handler.test.ts +4 -3
  143. package/src/execution/__tests__/ui-request-queue.test.ts +167 -2
  144. package/src/execution/__tests__/worktree-git-ops.test.ts +571 -0
  145. package/src/execution/__tests__/worktree-manager.test.ts +14 -5
  146. package/src/execution/__tests__/worktree-pid-registration.integration.test.ts +4 -3
  147. package/src/execution/__tests__/worktree-reconcile-aging.test.ts +204 -0
  148. package/src/execution/agent-registry.ts +298 -30
  149. package/src/execution/agent-result-mapper.ts +3 -0
  150. package/src/execution/agents-assembly.ts +88 -0
  151. package/src/execution/cold-resurrect.ts +199 -0
  152. package/src/execution/concurrency-pool.ts +71 -9
  153. package/src/execution/dialog-queue.ts +101 -3
  154. package/src/execution/engine/__tests__/common/capability-gate.test.ts +91 -0
  155. package/src/execution/engine/__tests__/common/data-dir.test.ts +1 -1
  156. package/src/execution/engine/__tests__/common/journal-wiring.test.ts +84 -0
  157. package/src/execution/engine/__tests__/common/kill-chain.test.ts +34 -7
  158. package/src/execution/engine/__tests__/common/persona-router.test.ts +4 -5
  159. package/src/execution/engine/__tests__/common/pool-manager.test.ts +211 -51
  160. package/src/execution/engine/__tests__/common/session-view-service-zcode-dbpath.test.ts +149 -0
  161. package/src/execution/engine/__tests__/common/session-view-service.test.ts +499 -0
  162. package/src/execution/engine/__tests__/conformance/contract.abort.test.ts +56 -99
  163. package/src/execution/engine/__tests__/conformance/contract.agent-events.test.ts +88 -15
  164. package/src/execution/engine/__tests__/conformance/engine-conformance.live.test.ts +33 -7
  165. package/src/execution/engine/__tests__/conformance/golden-replay.pi.test.ts +1 -1
  166. package/src/execution/engine/__tests__/conformance/golden-replay.zcode.test.ts +75 -54
  167. package/src/execution/engine/__tests__/conformance/zcode-appserver-harness.ts +128 -0
  168. package/src/execution/engine/__tests__/engine-discovery.test.ts +15 -3
  169. package/src/execution/engine/__tests__/model-prompt.test.ts +15 -3
  170. package/src/execution/engine/__tests__/registry.test.ts +94 -3
  171. package/src/execution/engine/__tests__/routing.test.ts +115 -6
  172. package/src/execution/engine/common/capability-gate.ts +92 -0
  173. package/src/execution/engine/common/journal-wiring.ts +97 -0
  174. package/src/execution/engine/common/kill-chain.ts +42 -10
  175. package/src/execution/engine/common/nesting-guard.ts +66 -0
  176. package/src/execution/engine/common/persona-router.ts +18 -8
  177. package/src/execution/engine/common/pool-manager.ts +332 -78
  178. package/src/execution/engine/common/session-view-service.ts +506 -0
  179. package/src/execution/engine/common/session-view-types.ts +125 -0
  180. package/src/execution/engine/engine-discovery.ts +12 -16
  181. package/src/execution/engine/engines/pi/__tests__/pi-engine.test.ts +174 -28
  182. package/src/execution/engine/engines/pi/__tests__/spawn-opts-direct.test.ts +257 -0
  183. package/src/execution/{argv-mirror.ts → engine/engines/pi/argv-mirror.ts} +43 -21
  184. package/src/execution/{get-state-handshake.ts → engine/engines/pi/get-state-handshake.ts} +86 -11
  185. package/src/execution/{output-collector.ts → engine/engines/pi/output-collector.ts} +108 -19
  186. package/src/execution/engine/engines/pi/pi-engine.ts +434 -38
  187. package/src/execution/{pi-invocation.ts → engine/engines/pi/pi-invocation.ts} +2 -2
  188. package/src/execution/engine/engines/pi/reader.ts +11 -0
  189. package/src/execution/engine/engines/pi/registration.ts +4 -2
  190. package/src/execution/engine/engines/pi/session-runner.ts +2840 -0
  191. package/src/execution/{spawn-event-adapter.ts → engine/engines/pi/spawn-event-adapter.ts} +75 -34
  192. package/src/execution/{stdin-writer.ts → engine/engines/pi/stdin-writer.ts} +10 -9
  193. package/src/execution/{temp-prompt.ts → engine/engines/pi/temp-prompt.ts} +2 -2
  194. package/src/execution/{turn-limiter.ts → engine/engines/pi/turn-limiter.ts} +1 -1
  195. package/src/execution/engine/engines/zcode/__tests__/__fixtures__/fake-appserver.mjs +301 -0
  196. package/src/execution/engine/engines/zcode/__tests__/__fixtures__/zcode-golden-appserver.json +36 -0
  197. package/src/execution/engine/engines/zcode/__tests__/appserver-launcher.test.ts +321 -0
  198. package/src/execution/engine/engines/zcode/__tests__/connection.test.ts +473 -0
  199. package/src/execution/engine/engines/zcode/__tests__/parser.test.ts +7 -210
  200. package/src/execution/engine/engines/zcode/__tests__/preparer.test.ts +6 -110
  201. package/src/execution/engine/engines/zcode/__tests__/session-channel-dispose-harvest.test.ts +247 -0
  202. package/src/execution/engine/engines/zcode/__tests__/session-channel-turn-timers.test.ts +451 -0
  203. package/src/execution/engine/engines/zcode/__tests__/session-channel.test.ts +792 -0
  204. package/src/execution/engine/engines/zcode/__tests__/zcode-engine-appserver.test.ts +782 -0
  205. package/src/execution/engine/engines/zcode/__tests__/zcode-engine-degrade.test.ts +168 -0
  206. package/src/execution/engine/engines/zcode/__tests__/zcode-engine-dispose.test.ts +210 -0
  207. package/src/execution/engine/engines/zcode/__tests__/zcode-engine-retry.test.ts +353 -0
  208. package/src/execution/engine/engines/zcode/__tests__/zcode-engine-status.test.ts +282 -0
  209. package/src/execution/engine/engines/zcode/__tests__/zcode-engine-timeout.test.ts +372 -0
  210. package/src/execution/engine/engines/zcode/__tests__/zcode-engine.live.test.ts +65 -47
  211. package/src/execution/engine/engines/zcode/appserver-launcher.ts +167 -0
  212. package/src/execution/engine/engines/zcode/connection.ts +598 -0
  213. package/src/execution/engine/engines/zcode/constants.ts +147 -14
  214. package/src/execution/engine/engines/zcode/golden-sample.ts +42 -34
  215. package/src/execution/engine/engines/zcode/parser.ts +12 -346
  216. package/src/execution/engine/engines/zcode/preparer.ts +22 -150
  217. package/src/execution/engine/engines/zcode/session-channel.ts +968 -0
  218. package/src/execution/engine/engines/zcode/zcode-engine.ts +1023 -250
  219. package/src/execution/engine/host-task-spec.ts +32 -36
  220. package/src/execution/engine/model-validation.ts +177 -0
  221. package/src/execution/engine/port.ts +65 -7
  222. package/src/execution/engine/registry.ts +53 -0
  223. package/src/execution/engine/routing.ts +75 -1
  224. package/src/execution/engine/types.ts +30 -103
  225. package/src/execution/execution-record.ts +222 -144
  226. package/src/execution/finalize-record.ts +181 -85
  227. package/src/execution/idle-gc.ts +28 -0
  228. package/src/execution/lifecycle-manager.ts +27 -3
  229. package/src/execution/lifecycle-predicates.ts +1 -1
  230. package/src/execution/manifest-store.ts +53 -62
  231. package/src/execution/notifier.ts +10 -10
  232. package/src/execution/notify-host.ts +212 -0
  233. package/src/execution/notify-ledger.ts +117 -16
  234. package/src/execution/record-entry.ts +8 -2
  235. package/src/execution/record-store.ts +148 -88
  236. package/src/execution/round-settlement.ts +93 -0
  237. package/src/execution/session-file-gc.ts +96 -62
  238. package/src/execution/session-pending.ts +213 -62
  239. package/src/execution/session-reconstructor.ts +256 -143
  240. package/src/execution/sessions-index.ts +127 -102
  241. package/src/execution/settled-watchdog.ts +311 -0
  242. package/src/execution/subagent-actions-core.ts +686 -0
  243. package/src/execution/subagent-service.ts +1106 -907
  244. package/src/execution/subprocess-agent-runner.ts +144 -97
  245. package/src/execution/types.ts +9 -0
  246. package/src/execution/ui-request-handler-factory.ts +126 -75
  247. package/src/execution/ui-request-queue.ts +50 -19
  248. package/src/execution/worktree-git-ops.ts +397 -0
  249. package/src/execution/worktree-manager.ts +72 -10
  250. package/src/execution/worktree-registry.ts +6 -12
  251. package/src/index.ts +542 -9
  252. package/src/orchestration/__tests__/__fixtures__/worker-template.snapshot.txt +42 -3
  253. package/src/orchestration/__tests__/agent-call-catch-fallback.test.ts +3 -5
  254. package/src/orchestration/__tests__/agent-call-stream.test.ts +2 -2
  255. package/src/orchestration/__tests__/args-meta.test.ts +358 -0
  256. package/src/orchestration/__tests__/args-validator.test.ts +14 -0
  257. package/src/orchestration/__tests__/error-recovery-rebuild-failure.test.ts +312 -0
  258. package/src/orchestration/__tests__/error-recovery-terminal-hardening.test.ts +384 -0
  259. package/src/orchestration/__tests__/execute-agent-call.test.ts +151 -11
  260. package/src/orchestration/__tests__/file-run-store-prune.test.ts +168 -0
  261. package/src/orchestration/__tests__/file-run-store-throttle.test.ts +168 -0
  262. package/src/orchestration/__tests__/file-run-store.test.ts +390 -0
  263. package/src/orchestration/__tests__/helpers/flush-microtasks.ts +13 -0
  264. package/src/orchestration/__tests__/launcher-nested-workflow.test.ts +34 -0
  265. package/src/orchestration/__tests__/lifecycle-abort-broadcast-signal.test.ts +357 -0
  266. package/src/orchestration/__tests__/lifecycle-recover-crashed.test.ts +289 -0
  267. package/src/orchestration/__tests__/lifecycle-runid-injection.test.ts +3 -3
  268. package/src/orchestration/__tests__/lifecycle.test.ts +84 -5
  269. package/src/orchestration/__tests__/non-cloneable-return-e2e.test.ts +1 -1
  270. package/src/orchestration/__tests__/run-snapshot.test.ts +356 -0
  271. package/src/orchestration/__tests__/script-generate.test.ts +314 -0
  272. package/src/orchestration/__tests__/script-lint.test.ts +17 -0
  273. package/src/orchestration/__tests__/test-mocks.ts +2 -0
  274. package/src/orchestration/__tests__/worker-exit-without-result.test.ts +7 -6
  275. package/src/orchestration/__tests__/worker-message-pump-finalize-run.test.ts +229 -0
  276. package/src/orchestration/__tests__/{error-recovery-handlers.test.ts → worker-message-pump-handlers.test.ts} +28 -27
  277. package/src/orchestration/__tests__/{error-recovery-postmessage-defense.test.ts → worker-message-pump-postmessage-defense.test.ts} +8 -8
  278. package/src/orchestration/__tests__/{error-recovery-serialize-failed-result.test.ts → worker-message-pump-serialize-failed-result.test.ts} +2 -2
  279. package/src/orchestration/__tests__/{error-recovery-workflow-call.test.ts → worker-message-pump-workflow-call.test.ts} +1 -1
  280. package/src/orchestration/__tests__/worker-pending-timeout-abort.test.ts +275 -0
  281. package/src/orchestration/__tests__/worker-script-builder-runtime.test.ts +78 -1
  282. package/src/orchestration/__tests__/worker-script-builder.test.ts +1 -1
  283. package/src/orchestration/__tests__/workflow-files.test.ts +186 -0
  284. package/src/orchestration/__tests__/workflow-nesting-e2e.test.ts +1 -1
  285. package/src/orchestration/__tests__/workflow-run-summary.test.ts +119 -0
  286. package/src/orchestration/__tests__/workflow-script-registry-impl.test.ts +124 -0
  287. package/src/orchestration/agent-opts-resolver.ts +8 -8
  288. package/src/orchestration/args-meta.ts +198 -0
  289. package/src/orchestration/args-validator.ts +53 -31
  290. package/src/orchestration/execute-agent-call.ts +25 -79
  291. package/src/orchestration/file-run-store.ts +327 -0
  292. package/src/orchestration/launcher.ts +160 -104
  293. package/src/orchestration/lifecycle.ts +371 -97
  294. package/src/orchestration/models/agent-call.ts +7 -7
  295. package/src/orchestration/models/budget.ts +5 -5
  296. package/src/orchestration/models/ports.ts +4 -4
  297. package/src/orchestration/models/run-runtime.ts +15 -15
  298. package/src/orchestration/models/trace.ts +9 -9
  299. package/src/orchestration/models/types.ts +111 -22
  300. package/src/orchestration/models/workflow-run.ts +28 -28
  301. package/src/orchestration/models/workflow-script.ts +4 -4
  302. package/src/orchestration/run-snapshot.ts +266 -0
  303. package/src/orchestration/script-generate.ts +154 -0
  304. package/src/orchestration/script-lint.ts +123 -90
  305. package/src/orchestration/worker-handle.ts +10 -10
  306. package/src/orchestration/worker-host.ts +10 -10
  307. package/src/orchestration/{error-recovery.ts → worker-message-pump.ts} +498 -221
  308. package/src/orchestration/worker-script-builder.ts +401 -346
  309. package/src/orchestration/workflow-files.ts +37 -11
  310. package/src/orchestration/workflow-run-summary.ts +69 -0
  311. package/src/orchestration/workflow-script-registry-impl.ts +31 -9
  312. package/src/shared/__tests__/agent-ref.test.ts +209 -2
  313. package/src/shared/__tests__/atomic-write.test.ts +267 -0
  314. package/src/shared/__tests__/bounded-serialize.test.ts +236 -0
  315. package/src/shared/__tests__/injection-render.test.ts +518 -0
  316. package/src/shared/__tests__/meta-parser.test.ts +151 -0
  317. package/src/shared/__tests__/resource-discovery-host-roots.test.ts +474 -0
  318. package/src/shared/__tests__/resource-discovery.test.ts +261 -217
  319. package/src/shared/agent-ref.ts +143 -2
  320. package/src/shared/atomic-write.ts +323 -0
  321. package/src/shared/bounded-serialize.ts +154 -0
  322. package/src/shared/injection-render.ts +279 -0
  323. package/src/shared/meta-parser.ts +182 -68
  324. package/src/shared/model-ref.ts +15 -2
  325. package/src/shared/resource-discovery.ts +97 -204
  326. package/src/shared/resource-meta.ts +14 -0
  327. package/src/shared/schema-jsonify.ts +1 -1
  328. package/src/shared/xml-injection.ts +9 -9
  329. package/workflows/README.md +9 -9
  330. package/workflows/chain.js +4 -2
  331. package/workflows/map-reduce.js +5 -3
  332. package/workflows/parallel.js +5 -3
  333. package/workflows/review-fix-loop-utils.cjs +165 -92
  334. package/workflows/review-fix-loop-utils.d.cts +287 -0
  335. package/workflows/review-fix-loop.js +18 -7
  336. package/workflows/scatter-gather.js +4 -2
  337. package/dist/chunk-3VOERJPJ.js +0 -22
  338. package/src/execution/__tests__/execute-options-mapper.test.ts +0 -200
  339. package/src/execution/engine/engines/pi/__tests__/task-spec-mapper.test.ts +0 -164
  340. package/src/execution/engine/engines/pi/task-spec-mapper.ts +0 -100
  341. package/src/execution/engine/engines/zcode/__tests__/__fixtures__/zcode-golden-spawn.json +0 -39
  342. package/src/execution/engine/engines/zcode/__tests__/launcher.test.ts +0 -150
  343. package/src/execution/engine/engines/zcode/__tests__/zcode-engine.test.ts +0 -580
  344. package/src/execution/engine/engines/zcode/launcher.ts +0 -161
  345. package/src/execution/execute-options-mapper.ts +0 -115
  346. package/src/execution/session-runner.ts +0 -1789
  347. package/src/shared/__tests__/resource-discovery-manifest-cache.test.ts +0 -288
@@ -88,6 +88,8 @@ function _safePost(msg, context) {
88
88
  const pending = _pendingCalls.get(msg.callId);
89
89
  if (pending) {
90
90
  _pendingCalls.delete(msg.callId);
91
+ // [OR-3] 结果已到,清除 per-call 超时 timer(有 timer 才清,防御旧 pending 无该字段)
92
+ if (pending.timer) { clearTimeout(pending.timer); }
91
93
  if (typeof msg.result !== "undefined") {
92
94
  _callCache.set(msg.callId, msg.result);
93
95
  }
@@ -124,11 +126,16 @@ function _safePost(msg, context) {
124
126
  const pending = _pendingCalls.get(msg.callId);
125
127
  if (pending) {
126
128
  _pendingCalls.delete(msg.callId);
129
+ // [OR-3] 结果已到,清除 per-call 超时 timer(workflow() 当前不挂 timer,防御性对称清理)
130
+ if (pending.timer) { clearTimeout(pending.timer); }
127
131
  pending.resolve(msg.result);
128
132
  }
129
133
  } else if (msg.type === "abort") {
134
+ // [OR-3] 主线程 abortRun/terminateRunningRuns 的优雅解阻广播:全部 pending
135
+ // reject(脚本 catch WorkflowAbortedError 可感知取消)+ 清理 timer + 清 Map。
136
+ // 后续迟到结果命中空 Map(no-op),不产主线程 unhandledRejection。
130
137
  const err = new WorkflowAbortedError(msg.reason);
131
- _pendingCalls.forEach((p) => { p.reject(err); });
138
+ _pendingCalls.forEach((p) => { if (p.timer) { clearTimeout(p.timer); } p.reject(err); });
132
139
  _pendingCalls.clear();
133
140
  } else if (msg.type === "budget-update" && msg.budget) {
134
141
  _budgetData._spentTokens = msg.budget.usedTokens ?? _budgetData._spentTokens;
@@ -141,7 +148,11 @@ function _safePost(msg, context) {
141
148
  function phase(name) { _currentPhase = String(name); }
142
149
 
143
150
  function log(msg) {
144
- try { parentPort.postMessage({ type: "log", phase: _currentPhase, message: String(msg) }); } catch(e) { /* swallow */ }
151
+ var _logText = String(msg);
152
+ // [B-3] 单通路:只发独立 {type:"log"} 消息,主线程 handleWorkerLog 即时消费
153
+ //(push 进 errorLogs)。不再同时记入 _workerLogs——旧双通路让同一日志随
154
+ // return/error 的 workerLogs 再带回一份,errorLogs 占两格、TUI 双份。
155
+ try { parentPort.postMessage({ type: "log", phase: _currentPhase, message: _logText }); } catch(e) { /* swallow */ }
145
156
  }
146
157
 
147
158
  async function agent(firstArg, secondArg) {
@@ -158,6 +169,9 @@ function _safePost(msg, context) {
158
169
  // step 级 turn 上限(turn limiter;显式 0/负 = 显式不限,压过 spawn watchdog env 兑底,SP-6)
159
170
  // ?? 语义保真:仅 null/undefined 归 undefined(走 env 兑底),显式 0 保留(U5 参数 > env)
160
171
  maxTurns: (secondArg && typeof secondArg === "object" ? secondArg.maxTurns : undefined) ?? undefined,
172
+ // [OR-3] per-call timeoutMs 透传(string 分支此前丢弃该字段——对象分支已有;
173
+ // worker pending 消息层超时依赖它流到 agent-call 消息)
174
+ timeoutMs: (secondArg && typeof secondArg === "object" ? secondArg.timeoutMs : undefined),
161
175
  // P4 D9③:step 级 engine 显式指定(仅限必须某引擎独有能力的场景)
162
176
  engine: (secondArg && typeof secondArg === "object" && secondArg.engine) || undefined,
163
177
  };
@@ -233,7 +247,32 @@ function _safePost(msg, context) {
233
247
  return Promise.reject(new Error("postMessage failed for agent-call (callId=" + callId + "): see workerLogs"));
234
248
  }
235
249
  return new Promise((resolve, reject) => {
236
- _pendingCalls.set(callId, { resolve, reject, returnMeta: opts.returnMeta === true });
250
+ const _pending = { resolve: resolve, reject: reject, returnMeta: opts.returnMeta === true, timer: undefined };
251
+ // [OR-3] per-call timeoutMs:消息层自己的超时——主线程对 agent-call 永不回话
252
+ //(畸形消息丢弃 / postMessage 双重失败 / runner 不 settle)时 pending 不再永挂
253
+ //(旧实现零超时 → agent() 永挂 → worker 不退出 → run 永久 running)。
254
+ // 超时以错误 resolve(对齐 agent-result 失败容错策略:不 reject、不把单点超时
255
+ // 放大成脚本 error → rebuild),并记入 _workerLogs 留诊断。仅显式 timeoutMs > 0
256
+ // 启用(缺省不限,与 runner 侧 per-call timeout 语义一致)。timer unref:脚本
257
+ // 已 return 时残留 timer 不延迟 worker 自然退出。
258
+ if (typeof opts.timeoutMs === "number" && opts.timeoutMs > 0) {
259
+ _pending.timer = setTimeout(() => {
260
+ if (!_pendingCalls.delete(callId)) { return; }
261
+ const _timeoutMsg = "agent call timed out after " + opts.timeoutMs + "ms (callId=" + callId + ") — no agent-result received from main thread";
262
+ _pushWorkerLog("warn", ["[workflow] " + _timeoutMsg]);
263
+ if (opts.returnMeta === true) {
264
+ _pending.resolve({ value: "", error: _timeoutMsg });
265
+ } else {
266
+ // [B-5] 非 returnMeta 超时 resolve 单值错误消息字符串(对齐 agent-result
267
+ // 失败路径 resolve 单值 _value = parsedOutput ?? content 的形态)——
268
+ // 旧实现 resolve {content:"", error} 对象,字符串消费型脚本(r.trim() 类)
269
+ // 在超时路径 TypeError。
270
+ _pending.resolve(_timeoutMsg);
271
+ }
272
+ }, opts.timeoutMs);
273
+ if (_pending.timer && typeof _pending.timer.unref === "function") { _pending.timer.unref(); }
274
+ }
275
+ _pendingCalls.set(callId, _pending);
237
276
  });
238
277
  }
239
278
 
@@ -12,11 +12,12 @@
12
12
 
13
13
  import { describe, expect, it, vi } from "vitest";
14
14
 
15
- import { handleWorkerMessage } from "../error-recovery.ts";
15
+ import { handleWorkerMessage } from "../worker-message-pump.ts";
16
16
  import type { LifecycleDeps, WorkerHandlers } from "../models/ports.ts";
17
17
  import { Trace } from "../models/trace.ts";
18
18
  import type { WorkflowRun } from "../models/workflow-run.ts";
19
19
  import type { AgentResult } from "../models/types.ts";
20
+ import { flushMicrotasks } from "./helpers/flush-microtasks.ts";
20
21
 
21
22
  // ── helpers ──────────────────────────────────────────────────
22
23
 
@@ -189,10 +190,7 @@ describe("U7b: AbortError 不回发 agent-result", () => {
189
190
  // 先 flush 微任务队列(多次确保 withSlot promise 已 settle),
190
191
  // 再断言断言期内无 agent-result。vi.waitFor 用反向断言会一满足就 return,
191
192
  // 故采用:跑若干 microtask tick 后静态断言。
192
- for (let i = 0; i < 5; i++) {
193
- // eslint-disable-next-line no-await-in-loop -- 排空微任务队列的固定 tick 循环(vi.waitFor 反向断言会一满足即返回,不适用),非逐项等待
194
- await Promise.resolve();
195
- }
193
+ await flushMicrotasks(5);
196
194
  const posted = findAgentResultPost(postMessage, 3);
197
195
  expect(posted).toBeUndefined();
198
196
  } finally {
@@ -8,7 +8,7 @@
8
8
 
9
9
  import { describe, expect, it, vi } from "vitest";
10
10
 
11
- import { handleWorkerMessage } from "../error-recovery.ts";
11
+ import { handleWorkerMessage } from "../worker-message-pump.ts";
12
12
  import type { LifecycleDeps, WorkerHandlers } from "../models/ports.ts";
13
13
  import { Trace } from "../models/trace.ts";
14
14
  import type { WorkflowRun } from "../models/workflow-run.ts";
@@ -122,7 +122,7 @@ describe("U5: widgetKey 格式", () => {
122
122
  expect(setWidget.mock.calls.length).toBeGreaterThanOrEqual(1);
123
123
  });
124
124
 
125
- const widgetKey = setWidget.mock.calls[0][0] as string;
125
+ const widgetKey = setWidget.mock.calls[0]![0] as string;
126
126
  expect(widgetKey).toBe("subagent-stream-wf-test-123-2");
127
127
  });
128
128
  });
@@ -0,0 +1,358 @@
1
+ // src/orchestration/__tests__/args-meta.test.ts
2
+ //
3
+ // U9(args-meta)单元测试。fixture = 既有 workflow 资产 @pi-meta parameters
4
+ // (packages/subagent-core/workflows/,经 core 自有 parseResourceMeta 解析——与
5
+ // pi-sw detectors.test.ts 同源同解析器,防 fixture 漂移)。
6
+ //
7
+ // 等值对照:PI_TOOL_TOP_LEVEL 注入后复刻 pi-sw detectors.test.ts TC3a-TC3i 场景
8
+ // (u-core-args 验收条款②「pi-sw 既有 findFlattenedArgKeys 行为等值」的锚定面)。
9
+ // 对照锚常量抄自 extensions/universal/subagent-workflow/src/interface/tool-workflow.ts
10
+ // TOOL_TOP_LEVEL(m6 定稿值)——pi-sw 侧若演进,对照测试同步刷新即暴露语义分叉。
11
+
12
+ import { readFileSync } from "node:fs";
13
+ import { dirname, join } from "node:path";
14
+ import { fileURLToPath } from "node:url";
15
+ import { describe, expect, it } from "vitest";
16
+
17
+ import { parseResourceMeta } from "../../shared/meta-parser.ts";
18
+ import {
19
+ argKeysFromMeta,
20
+ findFlattenedArgKeys,
21
+ normalizeArgsByMeta,
22
+ } from "../args-meta.ts";
23
+
24
+ const here = dirname(fileURLToPath(import.meta.url));
25
+ const WORKFLOWS_DIR = join(here, "..", "..", "..", "workflows");
26
+
27
+ /** 解析既有 workflow 资产的 @pi-meta parameters(真实资产 fixture,禁合成替代)。 */
28
+ function loadWorkflowParameters(name: string): Record<string, unknown> {
29
+ const src = readFileSync(join(WORKFLOWS_DIR, `${name}.js`), "utf8");
30
+ const meta = parseResourceMeta(src, "workflow");
31
+ if (!meta || meta.kind !== "workflow" || !meta.parameters) {
32
+ throw new Error(`fixture 损坏:${name} 无 workflow meta 或无 parameters`);
33
+ }
34
+ return meta.parameters;
35
+ }
36
+
37
+ const CHAIN_PARAMS = loadWorkflowParameters("chain");
38
+ const RFL_PARAMS = loadWorkflowParameters("review-fix-loop");
39
+ const MAP_REDUCE_PARAMS = loadWorkflowParameters("map-reduce");
40
+
41
+ /**
42
+ * pi workflow tool 顶层键集(对照锚,抄自 pi-sw tool-workflow.ts TOOL_TOP_LEVEL,
43
+ * 含 model/thinkingLevel 升格注释)——reservedKeys 宿主注入形态的等值对照基准。
44
+ */
45
+ const PI_TOOL_TOP_LEVEL = new Set([
46
+ "action",
47
+ "name",
48
+ "slug",
49
+ "runId",
50
+ "args",
51
+ "tokens",
52
+ "time",
53
+ "error",
54
+ "model",
55
+ "thinkingLevel",
56
+ ]);
57
+
58
+ /** pi 等值形态:reservedKeys 注入 pi TOOL_TOP_LEVEL 后的平铺检测(宿主注入消费位)。 */
59
+ function piFindFlattened(params: unknown, meta: Record<string, unknown>): string[] {
60
+ return findFlattenedArgKeys(params, meta, { reservedKeys: PI_TOOL_TOP_LEVEL });
61
+ }
62
+
63
+ describe("argKeysFromMeta(schema → 已知参数键集)", () => {
64
+ it("真实资产 chain.js:exact = properties 键集(task/agents)", () => {
65
+ const { exact, patterns } = argKeysFromMeta(CHAIN_PARAMS);
66
+ expect([...exact].sort()).toEqual(["agents", "task"]);
67
+ expect(patterns).toEqual([]);
68
+ });
69
+
70
+ it("真实资产 review-fix-loop.js:exact 16 键 + patternProperties 正则(^batch\\d+$)", () => {
71
+ const { exact, patterns } = argKeysFromMeta(RFL_PARAMS);
72
+ // properties 键(schema 定义序)
73
+ expect([...exact]).toEqual([
74
+ "targetType",
75
+ "target",
76
+ "autoCommit",
77
+ "maxRounds",
78
+ "stuckThreshold",
79
+ "skipCleanAgents",
80
+ "recheckAfterFix",
81
+ "fixAgent",
82
+ "maxFixAttempts",
83
+ "convergeNewIssues",
84
+ "convergeRounds",
85
+ "aggregatorModel",
86
+ "reviewPrompt",
87
+ "fixPrompt",
88
+ "fallowScan",
89
+ "agents",
90
+ "batchNames",
91
+ ]);
92
+ expect(patterns).toHaveLength(1);
93
+ expect(patterns[0].source).toBe("^batch\\d+$");
94
+ // pattern 语义:数字后缀命中、拼错不命中(m6 评审 C-1)
95
+ expect(patterns[0].test("batch1")).toBe(true);
96
+ expect(patterns[0].test("batch12")).toBe(true);
97
+ expect(patterns[0].test("batchl")).toBe(false);
98
+ });
99
+
100
+ it("空 meta(undefined / null / 非对象)→ 空键集(legacy const-meta 检测跳过)", () => {
101
+ expect(argKeysFromMeta(undefined)).toEqual({ exact: new Set(), patterns: [] });
102
+ expect(argKeysFromMeta(null)).toEqual({ exact: new Set(), patterns: [] });
103
+ expect(argKeysFromMeta("not-an-object" as unknown as Record<string, unknown>)).toEqual({
104
+ exact: new Set(),
105
+ patterns: [],
106
+ });
107
+ });
108
+
109
+ it("meta 无 properties/patternProperties(如仅 type:object)→ 空键集", () => {
110
+ const { exact, patterns } = argKeysFromMeta({ type: "object" });
111
+ expect(exact.size).toBe(0);
112
+ expect(patterns).toEqual([]);
113
+ });
114
+
115
+ it("reservedKeys 注入:撞名参数键从 exact 排除(m6 评审 M-3 撞名保护)", () => {
116
+ const synthetic = {
117
+ type: "object",
118
+ properties: { name: { type: "string" }, task: { type: "string" } },
119
+ required: ["name"],
120
+ };
121
+ const keys = argKeysFromMeta(synthetic, { reservedKeys: PI_TOOL_TOP_LEVEL });
122
+ expect(keys.exact.has("task")).toBe(true);
123
+ expect(keys.exact.has("name")).toBe(false); // pi TOOL_TOP_LEVEL 排除(M-3 真回归锁定)
124
+ // 缺省(无 reservedKeys)不排除——core 中性形态
125
+ const bare = argKeysFromMeta(synthetic);
126
+ expect(bare.exact.has("name")).toBe(true);
127
+ });
128
+
129
+ it("能命中 reservedKeys 的 pattern 整条跳过(m6 exec-review S1:^run.*$ 不误伤 runId)", () => {
130
+ const synthetic = {
131
+ type: "object",
132
+ properties: { task: { type: "string" } },
133
+ patternProperties: { "^run.*$": { type: "string" }, "^batch\\d+$": { type: "string" } },
134
+ };
135
+ const keys = argKeysFromMeta(synthetic, { reservedKeys: PI_TOOL_TOP_LEVEL });
136
+ expect(keys.patterns).toHaveLength(1);
137
+ expect(keys.patterns[0].source).toBe("^batch\\d+$");
138
+ });
139
+
140
+ it("非法 pattern 跳过不抛(双保险通路,与 pi logger.warn+跳过等值)", () => {
141
+ const synthetic = {
142
+ type: "object",
143
+ patternProperties: { "([": { type: "string" }, "^ok\\d+$": { type: "string" } },
144
+ };
145
+ const keys = argKeysFromMeta(synthetic);
146
+ expect(keys.patterns).toHaveLength(1);
147
+ expect(keys.patterns[0].source).toBe("^ok\\d+$");
148
+ });
149
+ });
150
+
151
+ describe("findFlattenedArgKeys(args 平铺检测——pi-sw 行为等值对照)", () => {
152
+ it("TC3a: trigger——chain 参数集平铺 task 被识别;跨 workflow 区分(items 非 chain 参数不触发)", () => {
153
+ expect(piFindFlattened({ action: "run", name: "chain", task: "x" }, CHAIN_PARAMS)).toEqual(["task"]);
154
+ expect(piFindFlattened({ action: "run", name: "chain", items: ["a"] }, CHAIN_PARAMS)).toEqual([]);
155
+ });
156
+
157
+ it("TC3b: 嵌套 args 不触发", () => {
158
+ expect(
159
+ piFindFlattened({ action: "run", name: "x", args: { task: "x", items: ["a"] } }, CHAIN_PARAMS),
160
+ ).toEqual([]);
161
+ });
162
+
163
+ it("TC3c: 顶层 + args 内共存不触发(args-排除保留)", () => {
164
+ expect(
165
+ piFindFlattened({ action: "run", name: "x", args: { task: "x" }, task: "y" }, CHAIN_PARAMS),
166
+ ).toEqual([]);
167
+ });
168
+
169
+ it("TC3d: 无已知键不触发;空集不触发", () => {
170
+ expect(piFindFlattened({ action: "run", name: "x", args: {} }, CHAIN_PARAMS)).toEqual([]);
171
+ expect(findFlattenedArgKeys({ action: "status" }, undefined)).toEqual([]);
172
+ });
173
+
174
+ it("TC3e: review-fix-loop fixAgent 平铺被识别(动态集 exact)", () => {
175
+ expect(
176
+ piFindFlattened({ action: "run", name: "review-fix-loop", fixAgent: "worker" }, RFL_PARAMS),
177
+ ).toEqual(["fixAgent"]);
178
+ expect(
179
+ piFindFlattened({ action: "run", name: "review-fix-loop", args: { fixAgent: "worker" } }, RFL_PARAMS),
180
+ ).toEqual([]);
181
+ });
182
+
183
+ it("TC3f: batchN 经 pattern 触发 + batchl 拼错不触发(数字后缀语义)", () => {
184
+ expect(
185
+ piFindFlattened({ action: "run", name: "review-fix-loop", targetType: "git-diff", target: "main" }, RFL_PARAMS),
186
+ ).toEqual(["targetType", "target"]);
187
+ expect(
188
+ piFindFlattened({ action: "run", name: "review-fix-loop", batch1: "reviewer", autoCommit: true }, RFL_PARAMS),
189
+ ).toEqual(["batch1", "autoCommit"]);
190
+ expect(
191
+ piFindFlattened({ action: "run", name: "review-fix-loop", args: { targetType: "git-diff", target: "main" } }, RFL_PARAMS),
192
+ ).toEqual([]);
193
+ expect(piFindFlattened({ action: "run", name: "x", batchl: "reviewer" }, RFL_PARAMS)).toEqual([]);
194
+ });
195
+
196
+ it("TC3g: 收敛参数平铺被识别(model 升格 TOOL_TOP_LEVEL 后排除)", () => {
197
+ expect(
198
+ piFindFlattened(
199
+ { action: "run", name: "review-fix-loop", model: "ds-flash", maxFixAttempts: 3, convergeNewIssues: 2, convergeRounds: 3 },
200
+ RFL_PARAMS,
201
+ ),
202
+ ).toEqual(["maxFixAttempts", "convergeNewIssues", "convergeRounds"]);
203
+ expect(
204
+ piFindFlattened(
205
+ { action: "run", name: "review-fix-loop", args: { model: "ds-flash", maxFixAttempts: 3 }, convergeRounds: 3 },
206
+ RFL_PARAMS,
207
+ ),
208
+ ).toEqual(["convergeRounds"]);
209
+ });
210
+
211
+ it("TC3h: non-object 输入返回 []", () => {
212
+ expect(findFlattenedArgKeys(null, undefined)).toEqual([]);
213
+ expect(findFlattenedArgKeys(undefined, undefined)).toEqual([]);
214
+ });
215
+
216
+ it("TC3i: tool 顶层键不误报(M-3 回归——合成 parameters 直测 reservedKeys 排除)", () => {
217
+ const synthetic = {
218
+ type: "object",
219
+ properties: { name: { type: "string" }, task: { type: "string" } },
220
+ required: ["name"],
221
+ };
222
+ expect(
223
+ piFindFlattened({ action: "run", name: "mywf", args: { name: "x" } }, synthetic),
224
+ ).toEqual([]);
225
+ // 真实 meta 不含 name(探针口径)——附加验证
226
+ expect(argKeysFromMeta(RFL_PARAMS, { reservedKeys: PI_TOOL_TOP_LEVEL }).exact.has("name")).toBe(false);
227
+ });
228
+
229
+ it("args 为非对象标量时 args-排除失效(顶层已知键全报——与 pi 谓词等值)", () => {
230
+ expect(
231
+ piFindFlattened({ action: "run", name: "chain", args: "oops", task: "x" }, CHAIN_PARAMS),
232
+ ).toEqual(["task"]);
233
+ });
234
+ });
235
+
236
+ describe("normalizeArgsByMeta(归一组装 + 警告收集)", () => {
237
+ it("正常嵌套:args 原样归一、无警告(chain 真实契约)", () => {
238
+ const r = normalizeArgsByMeta(
239
+ { action: "run", name: "chain", args: { task: "x", agents: "/a.md" } },
240
+ CHAIN_PARAMS,
241
+ { reservedKeys: PI_TOOL_TOP_LEVEL },
242
+ );
243
+ expect(r.args).toEqual({ task: "x", agents: "/a.md" });
244
+ expect(r.warnings).toEqual([]);
245
+ });
246
+
247
+ it("args 缺省归一为空对象(params.args ?? {}——pi actionRun 等值)", () => {
248
+ const r = normalizeArgsByMeta(
249
+ { action: "run", name: "chain" },
250
+ CHAIN_PARAMS,
251
+ { reservedKeys: PI_TOOL_TOP_LEVEL },
252
+ );
253
+ expect(r.args).toEqual({});
254
+ expect(r.warnings).toEqual([]);
255
+ });
256
+
257
+ it("平铺误用:flattened_args 警告含键名列表(机读 keys + 中立 message)", () => {
258
+ const r = normalizeArgsByMeta(
259
+ { action: "run", name: "chain", task: "x" },
260
+ CHAIN_PARAMS,
261
+ { reservedKeys: PI_TOOL_TOP_LEVEL },
262
+ );
263
+ expect(r.args).toEqual({}); // 平铺键不静默收编(pi 现行为是 throw,修复动作留宿主)
264
+ expect(r.warnings).toEqual([
265
+ {
266
+ code: "flattened_args",
267
+ message: "Detected task at top level — they belong inside 'args'.",
268
+ keys: ["task"],
269
+ },
270
+ ]);
271
+ });
272
+
273
+ it("平铺 + args 共存:args-排除生效,无警告(与 pi TC3c 对齐)", () => {
274
+ const r = normalizeArgsByMeta(
275
+ { action: "run", name: "x", args: { task: "x" }, task: "y" },
276
+ CHAIN_PARAMS,
277
+ { reservedKeys: PI_TOOL_TOP_LEVEL },
278
+ );
279
+ expect(r.args).toEqual({ task: "x" });
280
+ expect(r.warnings).toEqual([]);
281
+ });
282
+
283
+ it("未知键:不产生警告(检测只对已知键——类型错误归 args-validator)", () => {
284
+ const r = normalizeArgsByMeta(
285
+ { action: "run", name: "chain", unknownKey: "v" },
286
+ CHAIN_PARAMS,
287
+ { reservedKeys: PI_TOOL_TOP_LEVEL },
288
+ );
289
+ expect(r.args).toEqual({});
290
+ expect(r.warnings).toEqual([]);
291
+ });
292
+
293
+ it("空 meta:no_parameter_contract 警告 + args 仍归一(M-2 显式信号)", () => {
294
+ const r = normalizeArgsByMeta({ action: "run", name: "legacy", args: { task: "x" } }, undefined);
295
+ expect(r.args).toEqual({ task: "x" });
296
+ expect(r.warnings).toEqual([
297
+ { code: "no_parameter_contract", message: "未声明参数契约(或解析为空)——平铺检测跳过,args 不校验" },
298
+ ]);
299
+ });
300
+
301
+ it("pattern 键平铺:batch1 经 pattern 命中进警告(review-fix-loop 真实契约)", () => {
302
+ const r = normalizeArgsByMeta(
303
+ { action: "run", name: "review-fix-loop", batch1: "reviewer" },
304
+ RFL_PARAMS,
305
+ { reservedKeys: PI_TOOL_TOP_LEVEL },
306
+ );
307
+ expect(r.warnings).toEqual([
308
+ {
309
+ code: "flattened_args",
310
+ message: "Detected batch1 at top level — they belong inside 'args'.",
311
+ keys: ["batch1"],
312
+ },
313
+ ]);
314
+ });
315
+
316
+ it("reservedKeys 撞名:workflow 声明 model 参数时顶层 model 不误报(宿主键集注入生效)", () => {
317
+ const synthetic = {
318
+ type: "object",
319
+ properties: { model: { type: "string" }, task: { type: "string" } },
320
+ };
321
+ const withReserved = normalizeArgsByMeta(
322
+ { action: "run", model: "ds-flash", task: "x" },
323
+ synthetic,
324
+ { reservedKeys: PI_TOOL_TOP_LEVEL },
325
+ );
326
+ expect(withReserved.warnings[0]).toMatchObject({ code: "flattened_args", keys: ["task"] });
327
+ // 不注入 reservedKeys(core 中性形态):model 是已知参数 → 平铺报 model+task
328
+ const bare = normalizeArgsByMeta({ action: "run", model: "ds-flash", task: "x" }, synthetic);
329
+ expect(bare.warnings[0]).toMatchObject({ code: "flattened_args", keys: ["model", "task"] });
330
+ });
331
+
332
+ it("多键平铺按 Object.keys 序产出(与 pi throw 文案键序一致)", () => {
333
+ const r = normalizeArgsByMeta(
334
+ { action: "run", name: "review-fix-loop", targetType: "git-diff", target: "main" },
335
+ RFL_PARAMS,
336
+ { reservedKeys: PI_TOOL_TOP_LEVEL },
337
+ );
338
+ expect(r.warnings[0]).toMatchObject({ code: "flattened_args", keys: ["targetType", "target"] });
339
+ expect((r.warnings[0] as { message: string }).message).toBe(
340
+ "Detected targetType, target at top level — they belong inside 'args'.",
341
+ );
342
+ });
343
+
344
+ it("params 非对象:args = {},无警告(防御分支,不抛)", () => {
345
+ expect(normalizeArgsByMeta(null, CHAIN_PARAMS)).toEqual({ args: {}, warnings: [] });
346
+ expect(normalizeArgsByMeta(undefined, CHAIN_PARAMS)).toEqual({ args: {}, warnings: [] });
347
+ });
348
+
349
+ it("args 为非对象标量:原样透传(类型校验责任在 args-validator,不发明约束)", () => {
350
+ const r = normalizeArgsByMeta({ action: "run", name: "chain", args: "oops" }, CHAIN_PARAMS);
351
+ expect(r.args).toBe("oops");
352
+ });
353
+
354
+ it("map-reduce oneOf 契约:oneOf 不参与键集(properties 才是键集来源——与 pi 等值)", () => {
355
+ const { exact } = argKeysFromMeta(MAP_REDUCE_PARAMS);
356
+ expect([...exact].sort()).toEqual(["agents", "items", "itemsJson", "operation"]);
357
+ });
358
+ });
@@ -123,6 +123,20 @@ describe("validateRunArgs — 校验语义", () => {
123
123
  }
124
124
  });
125
125
 
126
+ it("TC6c: parameters 非 object(数组)→ ArgsValidationError「expected object」", () => {
127
+ // 锚定 schema 形状守卫分支:数组 parameters 在 null-scan/compile 之前 fail-fast
128
+ const spec = makeSpec([{ target: "string" }] as unknown as Record<string, unknown>, { a: 1 });
129
+ try {
130
+ validateRunArgs(spec);
131
+ expect.unreachable("should throw");
132
+ } catch (err) {
133
+ expect(err).toBeInstanceOf(ArgsValidationError);
134
+ expect((err as ArgsValidationError).message).toContain(
135
+ "invalid parameter schema (expected object)",
136
+ );
137
+ }
138
+ });
139
+
126
140
  it("TC6b: strictSchema:false 容忍自定义关键字与 format(design-review major-3 回归)", () => {
127
141
  const spec = makeSpec(
128
142
  { type: "object", properties: { target: { type: "string", format: "uri" } }, required: ["target"], "x-custom": true },