@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
@@ -0,0 +1,204 @@
1
+ // worktree-reconcile-aging.test.ts —— 对账「歧义跳过」老化升级(PS-12 措施⑤)。
2
+ //
3
+ // 环境模式对齐 worktree-reconcile.integration.test.ts:真实 git repo + TMPDIR
4
+ // 重定向到测试私有目录(物理面扫描根 os.tmpdir()/pi-subagents 跟随 TMPDIR);
5
+ // 本文件额外 mock core logger 以断言升级 warn(消息含磁盘路径与人工清理指引)。
6
+ //
7
+ // 语义锁定:
8
+ // - 阈值前(连续 N-1 周期内):保持既有低信息聚合 warn,无升级、不清理(宁延迟勿误删);
9
+ // - 第 N 周期:升级 warn——消息含 checkout 磁盘路径 + git worktree list 查看、
10
+ // worktree remove --force / branch -D 手动清理、无主残留 rm -rf 指引;
11
+ // 结构化字段带 branch/checkout/repo/skippedCycles;升级是提醒不是动作(资源不动);
12
+ // - 重置语义:路径「出现对应」(自愈补写)后计数清零,重现歧义需重新连续 N 周期。
13
+
14
+ import { execFileSync, spawn } from "node:child_process";
15
+ import * as fs from "node:fs";
16
+ import * as os from "node:os";
17
+ import * as path from "node:path";
18
+
19
+ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
20
+
21
+ const { loggerMock } = vi.hoisted(() => ({
22
+ loggerMock: {
23
+ debug: vi.fn(),
24
+ warn: vi.fn(),
25
+ error: vi.fn(),
26
+ },
27
+ }));
28
+ vi.mock("../../core/logger.ts", () => ({ getLogger: () => loggerMock }));
29
+
30
+ import { encodeCwd } from "../path-encoding.ts";
31
+ import { WorktreeRegistry } from "../worktree-registry.ts";
32
+ import { RECONCILE_SKIP_ESCALATION_CYCLES, WorktreeManager } from "../worktree-manager.ts";
33
+
34
+ /** 原始 TMPDIR(beforeEach 重定向、afterEach 还原)。 */
35
+ const ORIG_TMPDIR = os.tmpdir();
36
+
37
+ /** git 辅助:repo 内执行(输出 trim)。 */
38
+ function git(repo: string, ...args: string[]): string {
39
+ return execFileSync("git", ["-C", repo, ...args], { encoding: "utf-8" }).trim();
40
+ }
41
+
42
+ /** 升级 warn 调用(按消息锚文本过滤)。 */
43
+ function escalationCalls(): Array<{ msg: string; data: Record<string, unknown> }> {
44
+ return loggerMock.warn.mock.calls
45
+ .filter((c) => typeof c[0] === "string" && c[0].includes("manual cleanup may be needed"))
46
+ .map((c) => ({ msg: c[0] as string, data: (c[1] ?? {}) as Record<string, unknown> }));
47
+ }
48
+
49
+ describe("WorktreeManager 对账歧义跳过老化(PS-12 措施⑤)", { timeout: 30_000 }, () => {
50
+ let outerDir: string;
51
+ let agentDir: string;
52
+ let repo: string;
53
+ let enc: string;
54
+ let registry: WorktreeRegistry;
55
+ let mgr: WorktreeManager;
56
+
57
+ beforeEach(() => {
58
+ outerDir = fs.mkdtempSync(path.join(ORIG_TMPDIR, "wt-reconcile-aging-"));
59
+ process.env.TMPDIR = outerDir;
60
+
61
+ agentDir = path.join(outerDir, "agent");
62
+ repo = path.join(outerDir, "repo");
63
+ fs.mkdirSync(repo, { recursive: true });
64
+ git(repo, "init", "-q");
65
+ git(repo, "config", "user.email", "test@test.local");
66
+ git(repo, "config", "user.name", "test");
67
+ fs.writeFileSync(path.join(repo, "a.txt"), "init\n", "utf-8");
68
+ git(repo, "add", "-A");
69
+ git(repo, "commit", "-q", "-m", "init");
70
+
71
+ enc = encodeCwd(repo);
72
+ registry = new WorktreeRegistry(agentDir);
73
+ mgr = new WorktreeManager(agentDir);
74
+ loggerMock.warn.mockClear();
75
+ });
76
+
77
+ afterEach(() => {
78
+ process.env.TMPDIR = ORIG_TMPDIR;
79
+ fs.rmSync(outerDir, { recursive: true, force: true });
80
+ });
81
+
82
+ /** 建一个物理 worktree(绕过 WorktreeManager.create 的脏树校验,路径布局同 create)。 */
83
+ function makePhysicalWorktree(branch: string): string {
84
+ const checkout = path.join(outerDir, "pi-subagents", enc, branch);
85
+ git(repo, "worktree", "add", "-q", "-b", branch, checkout, "HEAD");
86
+ return checkout;
87
+ }
88
+
89
+ /** 在 <agentDir>/subagents/<enc>/sessions/ 下写一个 .alive marker。 */
90
+ function writeAliveMarker(sessionId: string, pid: number): void {
91
+ const sessions = path.join(agentDir, "subagents", enc, "sessions");
92
+ fs.mkdirSync(sessions, { recursive: true });
93
+ fs.writeFileSync(
94
+ path.join(sessions, `${sessionId}.jsonl.alive`),
95
+ JSON.stringify({ pid, id: sessionId, startedAt: Date.now() }),
96
+ "utf-8",
97
+ );
98
+ }
99
+
100
+ /** 真实子进程等待:等 pid 死透(isProcessAlive 翻 false,SIGKILL 后的短暂窗口)。 */
101
+ async function waitPidDead(pid: number): Promise<void> {
102
+ for (let i = 0; i < 50; i++) {
103
+ try {
104
+ process.kill(pid, 0);
105
+ } catch {
106
+ return; // ESRCH = 已死
107
+ }
108
+ await new Promise((r) => setTimeout(r, 10));
109
+ }
110
+ }
111
+
112
+ it("阈值前保持普通 warn,第 N 周期升级含磁盘路径与清理指引(资源不动)", async () => {
113
+ const c1 = makePhysicalWorktree("pi-sub-a1");
114
+ const c2 = makePhysicalWorktree("pi-sub-a2");
115
+ const sleeper = spawn("sleep", ["60"]);
116
+ try {
117
+ expect(sleeper.pid).toBeTruthy();
118
+ writeAliveMarker("s-a1", process.pid);
119
+ writeAliveMarker("s-a2", sleeper.pid as number);
120
+
121
+ // 阈值前(N-1 周期):无升级消息,资源原样(宁延迟勿误删语义不变)
122
+ for (let i = 0; i < RECONCILE_SKIP_ESCALATION_CYCLES - 1; i++) {
123
+ await mgr.scan();
124
+ }
125
+ expect(escalationCalls()).toEqual([]);
126
+ expect(fs.existsSync(c1)).toBe(true);
127
+ expect(fs.existsSync(c2)).toBe(true);
128
+
129
+ // 第 N 周期:升级 warn——每个残留路径一条,含路径与人工清理指引
130
+ await mgr.scan();
131
+ const calls = escalationCalls();
132
+ expect(calls.length).toBe(2); // c1 / c2 各一条
133
+ for (const checkout of [c1, c2]) {
134
+ const hit = calls.find((c) => c.msg.includes(checkout));
135
+ expect(hit).toBeDefined();
136
+ expect(hit?.msg).toContain("worktree list");
137
+ expect(hit?.msg).toContain("worktree remove --force");
138
+ expect(hit?.msg).toContain("branch -D");
139
+ expect(hit?.msg).toContain("rm -rf");
140
+ expect(hit?.msg).toContain(`skipped for ${RECONCILE_SKIP_ESCALATION_CYCLES} consecutive cycles`);
141
+ }
142
+ // 结构化字段:branch/checkout/skippedCycles 可机读
143
+ // (repo 是 git .git 指针解析产物,macOS 上为 realpath 形态 /private/var/...)
144
+ const data1 = calls.find((c) => c.data.checkout === c1)?.data;
145
+ expect(data1?.branch).toBe("pi-sub-a1");
146
+ expect(data1?.repo).toBe(fs.realpathSync(repo));
147
+ expect(data1?.skippedCycles).toBe(RECONCILE_SKIP_ESCALATION_CYCLES);
148
+
149
+ // 升级 warn 是提醒不是清理动作:物理资源与注册表未动
150
+ expect(fs.existsSync(c1)).toBe(true);
151
+ expect(fs.existsSync(c2)).toBe(true);
152
+ expect(registry.load()).toEqual([]);
153
+ } finally {
154
+ sleeper.kill("SIGKILL");
155
+ }
156
+ });
157
+
158
+ it("重置语义:自愈补写(出现对应)后计数清零,重现歧义需重新连续 N 周期", async () => {
159
+ const c1 = makePhysicalWorktree("pi-sub-r1");
160
+ const c2 = makePhysicalWorktree("pi-sub-r2");
161
+ const sleeper = spawn("sleep", ["60"]);
162
+ try {
163
+ writeAliveMarker("s-r1", process.pid);
164
+ writeAliveMarker("s-r2", sleeper.pid as number);
165
+
166
+ // 累计 N-1 周期歧义(未达阈值)
167
+ for (let i = 0; i < RECONCILE_SKIP_ESCALATION_CYCLES - 1; i++) {
168
+ await mgr.scan();
169
+ }
170
+ expect(escalationCalls()).toEqual([]);
171
+ } finally {
172
+ sleeper.kill("SIGKILL");
173
+ }
174
+
175
+ // 出现对应:s-r2 pid 死 + c2 删除 → 1 活 pid ↔ 1 残留 → 自愈补写 c1
176
+ await waitPidDead(sleeper.pid as number);
177
+ fs.rmSync(c2, { recursive: true, force: true });
178
+ await mgr.scan();
179
+ expect(registry.load().map((e) => e.branch)).toEqual(["pi-sub-r1"]);
180
+ expect(escalationCalls()).toEqual([]);
181
+
182
+ // 重现歧义:c1 退出注册表 + 新残留 c3 + 新活 pid。
183
+ // c1 的历史歧义计数已被自愈轮清零——若未清零(陈账 N-1),下面第 1 轮就达
184
+ // 阈值升级;断言 N-1 轮内无升级即证明重置生效。
185
+ await registry.remove("pi-sub-r1");
186
+ const c3 = makePhysicalWorktree("pi-sub-r3");
187
+ const sleeper2 = spawn("sleep", ["60"]);
188
+ try {
189
+ writeAliveMarker("s-r3", sleeper2.pid as number);
190
+ for (let i = 0; i < RECONCILE_SKIP_ESCALATION_CYCLES - 1; i++) {
191
+ await mgr.scan();
192
+ }
193
+ expect(escalationCalls()).toEqual([]);
194
+
195
+ // 第 N 轮:升级(新计数从 1 起步走满阈值)
196
+ await mgr.scan();
197
+ const msgs = escalationCalls().map((c) => c.msg);
198
+ expect(msgs.some((m) => m.includes(c1))).toBe(true);
199
+ expect(msgs.some((m) => m.includes(c3))).toBe(true);
200
+ } finally {
201
+ sleeper2.kill("SIGKILL");
202
+ }
203
+ });
204
+ });
@@ -18,7 +18,7 @@ import {
18
18
  import { normalizeRef, AGENT_REF_EXT } from "../shared/agent-ref.ts";
19
19
  import { parseResourceMeta } from "../shared/meta-parser.ts";
20
20
  import { lintAgentMeta } from "../orchestration/script-lint.ts";
21
- import type { AgentMeta } from "../shared/resource-meta.ts";
21
+ import type { AgentMeta, RoutingExample } from "../shared/resource-meta.ts";
22
22
  import type { AgentConfig } from "./model-resolver.ts";
23
23
  import { hasEngine, listEngines, EngineNotFoundError } from "./engine/registry.ts";
24
24
 
@@ -56,41 +56,64 @@ export function parseAgentWithMeta(
56
56
  content: string,
57
57
  ): { config: AgentConfig; meta: AgentMeta | null } {
58
58
  const name = path.basename(filePath, ".md");
59
+ // 结构切分三态(与 parseAgentProfile 共用 splitAgentFrontmatter,切分逻辑逐字节一致)
60
+ const fm = splitAgentFrontmatter(content);
59
61
 
60
62
  // 无 frontmatter → 整个内容作为 systemPrompt
61
- if (!content.startsWith(FM_DELIM)) {
63
+ if (fm.kind === "none") {
62
64
  return { config: { name, systemPrompt: content.trim() }, meta: null };
63
65
  }
64
66
 
65
- const closeIdx = content.indexOf(FM_DELIM, FM_DELIM.length);
66
- if (closeIdx === -1) {
67
- // 未闭合 frontmatter:提取 name,其余作为 systemPrompt
68
- const yamlBlock = content.slice(FM_DELIM.length);
67
+ // 未闭合 frontmatter:提取 name,其余作为 systemPrompt
68
+ if (fm.kind === "unclosed") {
69
69
  return {
70
70
  config: {
71
- name: extractYamlField(yamlBlock, "name") ?? name,
71
+ name: extractYamlField(fm.yamlBlock, "name") ?? name,
72
72
  systemPrompt: content.trim(),
73
73
  },
74
74
  meta: null,
75
75
  };
76
76
  }
77
77
 
78
- const yamlBlock = content.slice(FM_DELIM.length, closeIdx);
79
- const body = content.slice(closeIdx + FM_DELIM.length).trim();
78
+ return parseClosedFrontmatterAgent(filePath, name, content, fm.yamlBlock, fm.body);
79
+ }
80
80
 
81
+ /** closed frontmatter 的主路径:IF1 严格层 + legacy fallback 字段解析 + config 投影。
82
+ * 字段解析/warn/throw 时序与提取前一致(parseResourceMeta → fallback+warn → engine+校验 → 投影)。 */
83
+ function parseClosedFrontmatterAgent(
84
+ filePath: string,
85
+ name: string,
86
+ content: string,
87
+ yamlBlock: string,
88
+ body: string,
89
+ ): { config: AgentConfig; meta: AgentMeta | null } {
81
90
  // m2:结构化路由字段(name/model/tools)经 IF1 parseResourceMeta(统一 parser),
82
91
  // 消灭本地 frontmatter parser 与 subagent-list-injector 的重复。thinkingLevel/defaultBackground
83
92
  // 是执行配置(非 AgentMeta 路由字段),仍用 extractYamlField 取。
84
93
  const meta = parseResourceMeta(content, "agent");
85
94
  const agentMeta = meta?.kind === "agent" ? meta : null;
95
+ const legacyFallbacks = resolveLegacyRoutingFallbacks(agentMeta, yamlBlock, filePath);
96
+ // engine 字段(D9):结构化优先(IF1),legacy fallback 与 model/tools 同判——
97
+ // agentMeta 未通过 IF1 时配置不丢
98
+ const engine = resolveAgentEngine(agentMeta, yamlBlock, filePath);
99
+
100
+ return {
101
+ config: projectAgentConfig(name, body, agentMeta, legacyFallbacks, engine, yamlBlock),
102
+ meta: agentMeta,
103
+ };
104
+ }
105
+
106
+ /** legacy fallback 路由字段(model/tools):IF1 未通过时不静默丢失(MF-3),并按需 warn。 */
107
+ function resolveLegacyRoutingFallbacks(
108
+ agentMeta: AgentMeta | null,
109
+ yamlBlock: string,
110
+ filePath: string,
111
+ ): { modelFallback: string | undefined; toolsFallback: string[] | undefined } {
86
112
  // MF-3 regression fix:agentMeta=null(IF1 要求 name/description 必填,缺则 parseResourceMeta
87
113
  // 返 null)时,model/tools 不能静默丢失——fallback 用 extractYamlField 取(保留重构前行为)。
88
114
  // 注入路由可见性由 discovery/injector 按 available 标志决定,与本处(direct-path loadByPath)无关。
89
115
  const modelFallback = extractYamlField(yamlBlock, "model");
90
- const toolsFallbackRaw = extractYamlField(yamlBlock, "tools");
91
- const toolsFallback = toolsFallbackRaw
92
- ? toolsFallbackRaw.split(",").map((s) => s.trim()).filter(Boolean)
93
- : undefined;
116
+ const toolsFallback = parseCommaListFallback(extractYamlField(yamlBlock, "tools"));
94
117
  if (!agentMeta && /^model:|^tools:/m.test(yamlBlock)) {
95
118
  // m2 exec-review MINOR-2 + MF-3:IF1 未通过(缺 name/description)→ model/tools 经 legacy
96
119
  // fallback 取(direct-path loadByPath 不丢配置,与重构前一致);但结构化路由注入不可见,
@@ -100,32 +123,53 @@ export function parseAgentWithMeta(
100
123
  "model/tools 经 legacy fallback 生效(直接路径不丢配置),但结构化路由不可见——请补充 description",
101
124
  );
102
125
  }
103
- const defaultBackgroundRaw = extractYamlField(yamlBlock, "defaultBackground");
104
- // engine 字段(D9):结构化优先(IF1),legacy fallback 与 model/tools 同判——
105
- // agentMeta 未通过 IF1 时配置不丢
126
+ return { modelFallback, toolsFallback };
127
+ }
128
+
129
+ /** engine 字段解析 + 解析期注册校验(D9:未注册 id 前置暴露,不留到运行时神秘失败)。
130
+ * 为什么在解析期而非路由期:配置错误的根源在 .md 文件,越早报错定位越准(错误含文件路径 + 注册清单)。 */
131
+ function resolveAgentEngine(
132
+ agentMeta: AgentMeta | null,
133
+ yamlBlock: string,
134
+ filePath: string,
135
+ ): string | undefined {
106
136
  const engine = agentMeta?.engine ?? extractYamlField(yamlBlock, "engine");
107
- // 解析期校验(D9:未注册 id 前置暴露,不留到运行时神秘失败)。为什么在解析期而非
108
- // 路由期:配置错误的根源在 .md 文件,越早报错定位越准(错误含文件路径 + 注册清单)
109
137
  if (engine !== undefined && !hasEngine(engine)) {
110
138
  throw new EngineNotFoundError(engine, listEngines(), filePath);
111
139
  }
140
+ return engine;
141
+ }
112
142
 
143
+ /** closed frontmatter 的 AgentConfig 投影(IF1 严格层优先,逐字段 legacy fallback)。 */
144
+ function projectAgentConfig(
145
+ name: string,
146
+ body: string,
147
+ agentMeta: AgentMeta | null,
148
+ fallbacks: { modelFallback: string | undefined; toolsFallback: string[] | undefined },
149
+ engine: string | undefined,
150
+ yamlBlock: string,
151
+ ): AgentConfig {
113
152
  return {
114
- config: {
115
- name: agentMeta?.name ?? name,
116
- systemPrompt: body,
117
- model: agentMeta?.model ?? modelFallback ?? undefined,
118
- thinkingLevel: extractYamlField(yamlBlock, "thinkingLevel") ?? undefined,
119
- ...(engine !== undefined ? { engine } : {}),
120
- tools: agentMeta?.tools && agentMeta.tools.length > 0
121
- ? agentMeta.tools
122
- : (toolsFallback && toolsFallback.length > 0 ? toolsFallback : undefined),
123
- defaultBackground: defaultBackgroundRaw === "true" ? true : undefined,
124
- },
125
- meta: agentMeta,
153
+ name: agentMeta?.name ?? name,
154
+ systemPrompt: body,
155
+ model: agentMeta?.model ?? fallbacks.modelFallback ?? undefined,
156
+ thinkingLevel: extractYamlField(yamlBlock, "thinkingLevel") ?? undefined,
157
+ ...(engine !== undefined ? { engine } : {}),
158
+ tools: resolveAgentTools(agentMeta, fallbacks.toolsFallback),
159
+ defaultBackground: extractYamlField(yamlBlock, "defaultBackground") === "true" ? true : undefined,
126
160
  };
127
161
  }
128
162
 
163
+ /** tools 投影:IF1 严格层非空清单优先,否则 legacy fallback 非空清单,均缺省 undefined。 */
164
+ function resolveAgentTools(
165
+ agentMeta: AgentMeta | null,
166
+ toolsFallback: string[] | undefined,
167
+ ): string[] | undefined {
168
+ return agentMeta?.tools && agentMeta.tools.length > 0
169
+ ? agentMeta.tools
170
+ : (toolsFallback && toolsFallback.length > 0 ? toolsFallback : undefined);
171
+ }
172
+
129
173
  /** 提取简单 `key: value` 字段,剥离引号。 */
130
174
  function extractYamlField(yaml: string, key: string): string | undefined {
131
175
  const regex = new RegExp(`^${key}:\\s*(.+)$`, "m");
@@ -138,6 +182,230 @@ function extractYamlField(yaml: string, key: string): string | undefined {
138
182
  return value || undefined;
139
183
  }
140
184
 
185
+ // ============================================================
186
+ // 宽容解析(sink 设计 D3 / U2)
187
+ // ============================================================
188
+
189
+ /**
190
+ * agent .md 的宽容解析结果(执行消费面全字段投影)。
191
+ *
192
+ * 双轨语义(D3 定稿):本结构是**执行可用性**面(可用即跑),严格路由面仍是
193
+ * AgentMeta(注入清单可见性:缺 name/description 不进清单,由 meta !== null 表达)。
194
+ * 两轨分离是两宿主既有设计,本类型只是把执行侧第三份手写实现(zsw mini parser)
195
+ * 收敛到 core 单点。
196
+ */
197
+ export interface AgentProfile {
198
+ /** 宽容 name:frontmatter name,缺省文件 stem(宽松语义不拒)。 */
199
+ name: string;
200
+ /** 宽容 description:frontmatter description,缺省空串(宽松语义不拒)。 */
201
+ description: string;
202
+ /** frontmatter 后正文(trim)——即 systemPrompt。 */
203
+ body: string;
204
+ /** 路由提示(严格层投影;IF1 未通过时经 legacy fallback 也不取——该资产不进清单)。 */
205
+ when?: string;
206
+ examples?: RoutingExample[];
207
+ // ── 执行字段全量(与 AgentConfig / AgentMeta 同名同语义)──
208
+ model?: string;
209
+ tools?: string[];
210
+ /** 执行引擎 id(D9 per-agent)。 */
211
+ engine?: string;
212
+ thinkingLevel?: string;
213
+ defaultBackground?: boolean;
214
+ /** turn 预算上限(D3 可选执行字段;消费优先级:显式参数 > 本字段 > 缺省)。 */
215
+ maxTurns?: number;
216
+ /** tool denylist(D3 可选执行字段,与 tools allowlist 正交)。 */
217
+ disallowedTools?: string[];
218
+ /** agent 声明依赖的 skill 名清单(D3 可选执行字段)。 */
219
+ skills?: string[];
220
+ /**
221
+ * IF1 严格层 meta:null = 无 frontmatter / 未闭合 / 严格校验未通过。
222
+ * 注入清单可见性判定归它(discoverAgents 只放行 meta 非 null 的条目——
223
+ * 与 pi 现装配循环口径等值)。
224
+ */
225
+ meta: AgentMeta | null;
226
+ /**
227
+ * 宽容解析降级说明(legacy fallback 生效等)。宽容语义不抛——资产异常时
228
+ * 返回尽力解析结果 + warnings,调用方决定呈现(错误规格表:`{ name: stem, body,
229
+ * warnings[] }` 宽容降级)。
230
+ */
231
+ warnings: string[];
232
+ }
233
+
234
+ /**
235
+ * agent .md 宽容解析(D3/U2 定稿):无 frontmatter 不拒、name 缺省 stem、
236
+ * description 缺省空串、返回 body 与执行字段全量,**永不抛**。
237
+ *
238
+ * 实现基础(D3 字段形态覆盖矩阵):
239
+ * - 主路径 = parseResourceMeta(eemeli/yaml 全量解析):原生支持单行 key:value、
240
+ * 行内数组、block-scalar、多行 `- item` 列表;
241
+ * - meta=null 时(无 frontmatter / 未闭合 / yaml 整体解析失败 / IF1 严格校验未通过)
242
+ * 经 extractYamlField legacy fallback(仅单行 key:value 形态)兜底取执行字段,
243
+ * 保证「可用即跑」不因资产格式瑕疵丢失配置(先例:parseAgentWithMeta 的 MF-3
244
+ * fallback)。fallback 触达时写入 warnings(warn 可见,调用方决定呈现)。
245
+ *
246
+ * 与 parseAgentWithMeta 的关系:本函数是新增导出面(执行消费面),既有
247
+ * parseAgentWithMeta/loadByPath 语义零改动(含 engine 未注册 throw 的解析期校验)。
248
+ * 本函数宽容语义不抛,故不做 engine 注册校验(执行校验归执行路径)。
249
+ */
250
+ export function parseAgentProfile(text: string, filePath: string): AgentProfile {
251
+ const stem = path.basename(filePath, ".md");
252
+ const warnings: string[] = [];
253
+
254
+ // 阶段 1:结构切分(无 frontmatter / 未闭合 / 闭合三态)
255
+ const fm = splitAgentFrontmatter(text);
256
+ // 无 frontmatter → 整个内容作为 body(宽容:README 等普通 .md 也是合法 profile)
257
+ if (fm.kind === "none") {
258
+ return {
259
+ name: stem,
260
+ description: "",
261
+ body: text.trim(),
262
+ meta: null,
263
+ warnings,
264
+ };
265
+ }
266
+ if (fm.kind === "unclosed") {
267
+ // 未闭合 frontmatter:name 经 legacy fallback,其余全文作 body(与 parseAgentWithMeta 同判)
268
+ warnings.push(
269
+ `[agent-registry] ${filePath}: frontmatter 未闭合——name 经 legacy fallback(仅单行 key:value),全文作 body`,
270
+ );
271
+ return {
272
+ name: extractYamlField(fm.yamlBlock, "name") ?? stem,
273
+ description: "",
274
+ body: text.trim(),
275
+ meta: null,
276
+ warnings,
277
+ };
278
+ }
279
+
280
+ // 阶段 2:主路径 = IF1 严格层(eemeli/yaml 全量解析)
281
+ const meta = parseResourceMeta(text, "agent");
282
+ const agentMeta = meta?.kind === "agent" ? meta : null;
283
+ if (agentMeta !== null) {
284
+ return profileFromStrictMeta(agentMeta, fm.yamlBlock, fm.body, warnings);
285
+ }
286
+
287
+ // 阶段 3:meta=null(yaml 解析失败或 IF1 未通过)→ legacy fallback 仅单行 key:value,
288
+ // 执行字段不静默丢失(MF-3 先例收敛)。宽松语义:name 缺省 stem、description 缺省空串。
289
+ warnings.push(
290
+ `[agent-registry] ${filePath}: agent frontmatter 未通过严格校验(IF1:yaml 解析失败或缺 name/description)` +
291
+ "——执行字段经 legacy fallback(仅单行 key:value 形态)生效,结构化路由不可见,建议补 name/description",
292
+ );
293
+ return profileFromLegacyFallback(fm.yamlBlock, fm.body, stem, filePath, warnings);
294
+ }
295
+
296
+ /** 宽容解析阶段 1 的切分产物(frontmatter 结构三态)。 */
297
+ type AgentFrontmatterSlice =
298
+ | { kind: "none" }
299
+ | { kind: "unclosed"; yamlBlock: string }
300
+ | { kind: "closed"; yamlBlock: string; body: string };
301
+
302
+ /** frontmatter 结构切分(产出三态供分派;parseAgentWithMeta 与 parseAgentProfile 共用单点)。 */
303
+ function splitAgentFrontmatter(text: string): AgentFrontmatterSlice {
304
+ if (!text.startsWith(FM_DELIM)) return { kind: "none" };
305
+ const closeIdx = text.indexOf(FM_DELIM, FM_DELIM.length);
306
+ if (closeIdx === -1) {
307
+ return { kind: "unclosed", yamlBlock: text.slice(FM_DELIM.length) };
308
+ }
309
+ return {
310
+ kind: "closed",
311
+ yamlBlock: text.slice(FM_DELIM.length, closeIdx),
312
+ body: text.slice(closeIdx + FM_DELIM.length).trim(),
313
+ };
314
+ }
315
+
316
+ /** 严格层(IF1 通过)的 profile 投影:AgentMeta 路由/执行字段 + 单行提取的执行配置。 */
317
+ function profileFromStrictMeta(
318
+ agentMeta: AgentMeta,
319
+ yamlBlock: string,
320
+ body: string,
321
+ warnings: string[],
322
+ ): AgentProfile {
323
+ // thinkingLevel/defaultBackground 是执行配置(非 AgentMeta 字段),与
324
+ // parseAgentWithMeta 同位经单行提取(严格层资产同样携带这两字段的现实先例)
325
+ const thinkingLevelRaw = extractYamlField(yamlBlock, "thinkingLevel");
326
+ const defaultBackgroundRaw = extractYamlField(yamlBlock, "defaultBackground");
327
+ return {
328
+ name: agentMeta.name,
329
+ description: agentMeta.description,
330
+ body,
331
+ ...(agentMeta.when !== undefined ? { when: agentMeta.when } : {}),
332
+ ...(agentMeta.examples !== undefined ? { examples: agentMeta.examples } : {}),
333
+ ...(agentMeta.model !== undefined ? { model: agentMeta.model } : {}),
334
+ ...nonEmptyArraySpread("tools", agentMeta.tools),
335
+ ...(agentMeta.engine !== undefined ? { engine: agentMeta.engine } : {}),
336
+ ...(thinkingLevelRaw !== undefined ? { thinkingLevel: thinkingLevelRaw } : {}),
337
+ ...(defaultBackgroundRaw === "true" ? { defaultBackground: true } : {}),
338
+ ...(agentMeta.maxTurns !== undefined ? { maxTurns: agentMeta.maxTurns } : {}),
339
+ ...nonEmptyArraySpread("disallowedTools", agentMeta.disallowedTools),
340
+ ...nonEmptyArraySpread("skills", agentMeta.skills),
341
+ meta: agentMeta,
342
+ warnings,
343
+ };
344
+ }
345
+
346
+ /** legacy fallback 的 profile 投影(仅单行 key:value 提取;warnings 同引用收集)。 */
347
+ function profileFromLegacyFallback(
348
+ yamlBlock: string,
349
+ body: string,
350
+ stem: string,
351
+ filePath: string,
352
+ warnings: string[],
353
+ ): AgentProfile {
354
+ const nameFallback = extractYamlField(yamlBlock, "name") ?? stem;
355
+ const modelFallback = extractYamlField(yamlBlock, "model");
356
+ const toolsFallback = parseCommaListFallback(extractYamlField(yamlBlock, "tools"));
357
+ const maxTurnsFallback = parseNumberFallback(extractYamlField(yamlBlock, "maxTurns"), filePath, "maxTurns", warnings);
358
+ const disallowedToolsFallback = parseCommaListFallback(extractYamlField(yamlBlock, "disallowedTools"));
359
+ const skillsFallback = parseCommaListFallback(extractYamlField(yamlBlock, "skills"));
360
+ const thinkingLevelFallback = extractYamlField(yamlBlock, "thinkingLevel");
361
+ const defaultBackgroundRaw = extractYamlField(yamlBlock, "defaultBackground");
362
+ const engineFallback = extractYamlField(yamlBlock, "engine");
363
+
364
+ return {
365
+ name: nameFallback,
366
+ description: "",
367
+ body,
368
+ ...(modelFallback !== undefined ? { model: modelFallback } : {}),
369
+ ...nonEmptyArraySpread("tools", toolsFallback),
370
+ ...(engineFallback !== undefined ? { engine: engineFallback } : {}),
371
+ ...(thinkingLevelFallback !== undefined ? { thinkingLevel: thinkingLevelFallback } : {}),
372
+ ...(defaultBackgroundRaw === "true" ? { defaultBackground: true } : {}),
373
+ ...(maxTurnsFallback !== undefined ? { maxTurns: maxTurnsFallback } : {}),
374
+ ...nonEmptyArraySpread("disallowedTools", disallowedToolsFallback),
375
+ ...nonEmptyArraySpread("skills", skillsFallback),
376
+ meta: null,
377
+ warnings,
378
+ };
379
+ }
380
+
381
+ /** 可选数组字段非空才带键(tools/disallowedTools/skills 的条件展开收敛,键序随展开位不变)。 */
382
+ function nonEmptyArraySpread<T>(key: string, v: T[] | undefined): Record<string, T[]> {
383
+ return v !== undefined && v.length > 0 ? { [key]: v } : {};
384
+ }
385
+
386
+ /** legacy fallback 的逗号分隔列表解析(`tools: read, bash` 约定,与 parseAgentWithMeta 同构)。 */
387
+ function parseCommaListFallback(raw: string | undefined): string[] | undefined {
388
+ return raw
389
+ ? raw.split(",").map((s) => s.trim()).filter(Boolean)
390
+ : undefined;
391
+ }
392
+
393
+ /** legacy fallback 的数字解析(extractYamlField 只出字符串,`maxTurns: 2` 需换算)。 */
394
+ function parseNumberFallback(
395
+ raw: string | undefined,
396
+ filePath: string,
397
+ field: string,
398
+ warnings: string[],
399
+ ): number | undefined {
400
+ if (raw === undefined) return undefined;
401
+ const n = Number(raw);
402
+ if (!Number.isFinite(n)) {
403
+ warnings.push(`[agent-registry] ${filePath}: ${field} 值 "${raw}" 不是有限数字,忽略`);
404
+ return undefined;
405
+ }
406
+ return n;
407
+ }
408
+
141
409
  // ============================================================
142
410
  // AgentRegistry
143
411
  // ============================================================
@@ -35,6 +35,9 @@ export function mapToWorkflowAgentResult(
35
35
  ): WorkflowAgentResult {
36
36
  return {
37
37
  content: r.text,
38
+ // [D5-③] 失败分诊标签透传(产出侧 output-collector 写入,消费侧
39
+ // executeAgentCall 读字段分诊)。成功路径 undefined 不落键。
40
+ ...(r.failureKind !== undefined ? { failureKind: r.failureKind } : {}),
38
41
  parsedOutput: r.parsedOutput,
39
42
  error: r.success ? undefined : (r.error || "Agent call failed (aborted or unknown error)"),
40
43
  durationMs: r.durationMs,