@zhushanwen/pi-subagent-workflow 8.5.0 → 8.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (336) hide show
  1. package/package.json +13 -16
  2. package/relay/relay.mjs +1 -1
  3. package/src/host/__tests__/pi-host.test.ts +214 -0
  4. package/src/host/pi-host.ts +109 -0
  5. package/src/index.ts +142 -32
  6. package/src/injectors/__tests__/engine-awareness.test.ts +292 -0
  7. package/src/injectors/__tests__/engine-section-stability.test.ts +341 -0
  8. package/src/injectors/__tests__/subagent-list-injector.test.ts +5 -5
  9. package/src/injectors/__tests__/workflow-list-injector.test.ts +5 -5
  10. package/src/injectors/engine-awareness.ts +123 -0
  11. package/src/injectors/model-list-injector.ts +1 -1
  12. package/src/injectors/subagent-list-injector.ts +8 -8
  13. package/src/injectors/workflow-list-injector.ts +8 -8
  14. package/src/interface/__tests__/detectors.test.ts +3 -3
  15. package/src/interface/__tests__/subagent-tool-path-guard.test.ts +157 -0
  16. package/src/interface/__tests__/subagent-tool-prompt.test.ts +12 -0
  17. package/src/interface/__tests__/tool-workflow-script-generate.test.ts +2 -2
  18. package/src/interface/__tests__/tool-workflow-throw-paths.test.ts +1 -1
  19. package/src/interface/bg-notify-render.ts +34 -13
  20. package/src/interface/commands.ts +3 -3
  21. package/src/interface/format.ts +2 -2
  22. package/src/interface/helpers.ts +3 -3
  23. package/src/interface/list-component.ts +4 -4
  24. package/src/interface/list-shared.ts +2 -2
  25. package/src/interface/list-view.ts +2 -2
  26. package/src/interface/subagent-actions.ts +288 -19
  27. package/src/interface/subagent-tool-schema.ts +163 -0
  28. package/src/interface/subagent-tool.ts +65 -129
  29. package/src/interface/subagents.ts +5 -5
  30. package/src/interface/tool-render.ts +34 -2
  31. package/src/interface/tool-workflow-script.ts +4 -4
  32. package/src/interface/tool-workflow.ts +6 -6
  33. package/src/interface/views/WorkflowsView.ts +5 -5
  34. package/src/interface/views/__tests__/WorkflowsView-signature.test.ts +3 -3
  35. package/src/interface/views/__tests__/detail-content-session-file.test.ts +2 -2
  36. package/src/interface/views/detail-content.ts +4 -4
  37. package/src/interface/views/format.ts +4 -4
  38. package/src/{orchestration/jsonl-run-store.ts → jsonl-run-store.ts} +132 -18
  39. package/src/execution/__tests__/__fixtures__/notifier-golden-snapshots.json +0 -53
  40. package/src/execution/__tests__/__fixtures__/truncline.snapshot.json +0 -1
  41. package/src/execution/__tests__/agent-registry.test.ts +0 -363
  42. package/src/execution/__tests__/agent-result-mapper.test.ts +0 -150
  43. package/src/execution/__tests__/alive-store.test.ts +0 -147
  44. package/src/execution/__tests__/ask-user-transit-e2e.test.ts +0 -490
  45. package/src/execution/__tests__/before-agent-start-injection.test.ts +0 -132
  46. package/src/execution/__tests__/bg-notify-render.test.ts +0 -256
  47. package/src/execution/__tests__/channel-registry-handshake.test.ts +0 -243
  48. package/src/execution/__tests__/chat-engine-routing.test.ts +0 -597
  49. package/src/execution/__tests__/chatmode-first-round-closure-service.test.ts +0 -365
  50. package/src/execution/__tests__/chatmode-first-round-closure-spawn.test.ts +0 -190
  51. package/src/execution/__tests__/chatmode-round-notify-real-chain.test.ts +0 -215
  52. package/src/execution/__tests__/concurrency-pool.test.ts +0 -250
  53. package/src/execution/__tests__/config.test.ts +0 -110
  54. package/src/execution/__tests__/conversation-wiring.test.ts +0 -198
  55. package/src/execution/__tests__/crash-recovery.test.ts +0 -320
  56. package/src/execution/__tests__/delivery-methods.test.ts +0 -385
  57. package/src/execution/__tests__/dialog-queue.test.ts +0 -299
  58. package/src/execution/__tests__/epipe-fallback.test.ts +0 -241
  59. package/src/execution/__tests__/execute-and-await-worktree.test.ts +0 -243
  60. package/src/execution/__tests__/execute-nesting.test.ts +0 -323
  61. package/src/execution/__tests__/execute-options-mapper.test.ts +0 -189
  62. package/src/execution/__tests__/execution-record.test.ts +0 -1284
  63. package/src/execution/__tests__/finalize-record.test.ts +0 -368
  64. package/src/execution/__tests__/finalized-marker.test.ts +0 -82
  65. package/src/execution/__tests__/format-schema-instruction.test.ts +0 -138
  66. package/src/execution/__tests__/format.test.ts +0 -458
  67. package/src/execution/__tests__/gc-timer.test.ts +0 -184
  68. package/src/execution/__tests__/get-record-for-action-restart.test.ts +0 -254
  69. package/src/execution/__tests__/gui-mode-dispatch.test.ts +0 -59
  70. package/src/execution/__tests__/helpers/mock-extension-api.ts +0 -30
  71. package/src/execution/__tests__/helpers/spawn-mock.ts +0 -238
  72. package/src/execution/__tests__/host-mode.test.ts +0 -87
  73. package/src/execution/__tests__/index-session-start-identity.test.ts +0 -371
  74. package/src/execution/__tests__/index-session-start.test.ts +0 -593
  75. package/src/execution/__tests__/lifecycle-manager-lock.test.ts +0 -211
  76. package/src/execution/__tests__/lifecycle-manager.test.ts +0 -337
  77. package/src/execution/__tests__/lifecycle-predicates.test.ts +0 -116
  78. package/src/execution/__tests__/list-component.test.ts +0 -401
  79. package/src/execution/__tests__/list-fields.test.ts +0 -109
  80. package/src/execution/__tests__/manifest-parentid.test.ts +0 -117
  81. package/src/execution/__tests__/model-resolver.test.ts +0 -413
  82. package/src/execution/__tests__/nested-visibility-env-propagation.test.ts +0 -287
  83. package/src/execution/__tests__/nested-visibility.test.ts +0 -325
  84. package/src/execution/__tests__/notifier-flush.test.ts +0 -574
  85. package/src/execution/__tests__/notifier-golden-snapshot.test.ts +0 -261
  86. package/src/execution/__tests__/one-shot-upgrade.test.ts +0 -205
  87. package/src/execution/__tests__/output-collector.test.ts +0 -61
  88. package/src/execution/__tests__/parent-child-matrix.test.ts +0 -336
  89. package/src/execution/__tests__/path-encoding.test.ts +0 -104
  90. package/src/execution/__tests__/pi-invocation.test.ts +0 -134
  91. package/src/execution/__tests__/record-store.test.ts +0 -1057
  92. package/src/execution/__tests__/records-cwd-isolation.test.ts +0 -91
  93. package/src/execution/__tests__/recursive-visibility-baseline.test.ts +0 -339
  94. package/src/execution/__tests__/recursive-visibility-env.test.ts +0 -273
  95. package/src/execution/__tests__/relay-agent.test.ts +0 -448
  96. package/src/execution/__tests__/relay-env.test.ts +0 -42
  97. package/src/execution/__tests__/resource-policy.test.ts +0 -109
  98. package/src/execution/__tests__/rpc-mode.test.ts +0 -89
  99. package/src/execution/__tests__/run-and-finalize-chatmode.test.ts +0 -267
  100. package/src/execution/__tests__/run-spawn-chatmode-settled.test.ts +0 -253
  101. package/src/execution/__tests__/run-spawn-edges.test.ts +0 -644
  102. package/src/execution/__tests__/run-spawn-integration.test.ts +0 -933
  103. package/src/execution/__tests__/run-spawn-resume.test.ts +0 -322
  104. package/src/execution/__tests__/run-spawn-rpc-mode.test.ts +0 -196
  105. package/src/execution/__tests__/sdk-contract.test.ts +0 -273
  106. package/src/execution/__tests__/session-context-resolver.test.ts +0 -167
  107. package/src/execution/__tests__/session-file-gc.test.ts +0 -293
  108. package/src/execution/__tests__/session-pending.test.ts +0 -197
  109. package/src/execution/__tests__/session-reconstructor.test.ts +0 -379
  110. package/src/execution/__tests__/session-runner-epipe.test.ts +0 -178
  111. package/src/execution/__tests__/session-runner-schema-env.test.ts +0 -311
  112. package/src/execution/__tests__/session-start-reaper.test.ts +0 -233
  113. package/src/execution/__tests__/spawn-args.test.ts +0 -434
  114. package/src/execution/__tests__/spawn-event-adapter-rpc.test.ts +0 -189
  115. package/src/execution/__tests__/spawn-event-adapter.test.ts +0 -167
  116. package/src/execution/__tests__/spawn-worktree-guidance.test.ts +0 -207
  117. package/src/execution/__tests__/spawned-children.test.ts +0 -92
  118. package/src/execution/__tests__/startup-config-declaration.test.ts +0 -35
  119. package/src/execution/__tests__/status-refactor.test.ts +0 -345
  120. package/src/execution/__tests__/stdin-writer.test.ts +0 -463
  121. package/src/execution/__tests__/stream-sink-retirement.test.ts +0 -261
  122. package/src/execution/__tests__/subagent-service-abort.test.ts +0 -60
  123. package/src/execution/__tests__/subagent-service-message-close.test.ts +0 -629
  124. package/src/execution/__tests__/subagent-service-parent-guard.test.ts +0 -180
  125. package/src/execution/__tests__/subagent-service.test.ts +0 -788
  126. package/src/execution/__tests__/subprocess-agent-runner-routing.test.ts +0 -310
  127. package/src/execution/__tests__/subprocess-agent-runner.test.ts +0 -506
  128. package/src/execution/__tests__/temp-prompt.test.ts +0 -53
  129. package/src/execution/__tests__/timeout-integration.test.ts +0 -397
  130. package/src/execution/__tests__/tombstone-store.test.ts +0 -73
  131. package/src/execution/__tests__/tool-action.test.ts +0 -385
  132. package/src/execution/__tests__/truncline-snapshot.test.ts +0 -81
  133. package/src/execution/__tests__/turn-limiter-semantics.test.ts +0 -194
  134. package/src/execution/__tests__/turn-limiter.test.ts +0 -65
  135. package/src/execution/__tests__/ui-channels.test.ts +0 -187
  136. package/src/execution/__tests__/ui-interaction-model.test.ts +0 -67
  137. package/src/execution/__tests__/ui-request-handler-factory.test.ts +0 -370
  138. package/src/execution/__tests__/ui-request-handler.test.ts +0 -204
  139. package/src/execution/__tests__/ui-request-observability.test.ts +0 -114
  140. package/src/execution/__tests__/ui-request-queue.test.ts +0 -133
  141. package/src/execution/__tests__/worktree-manager.test.ts +0 -633
  142. package/src/execution/__tests__/worktree-pid-registration.integration.test.ts +0 -229
  143. package/src/execution/__tests__/worktree-reconcile.integration.test.ts +0 -181
  144. package/src/execution/__tests__/worktree-registry.test.ts +0 -199
  145. package/src/execution/agent-registry.ts +0 -193
  146. package/src/execution/agent-result-mapper.ts +0 -90
  147. package/src/execution/alive-store.ts +0 -92
  148. package/src/execution/argv-mirror.ts +0 -109
  149. package/src/execution/best-effort.ts +0 -38
  150. package/src/execution/channel-registry-access.ts +0 -144
  151. package/src/execution/concurrency-pool.ts +0 -116
  152. package/src/execution/config.ts +0 -96
  153. package/src/execution/dialog-queue.ts +0 -330
  154. package/src/execution/engine/__tests__/common/data-dir.test.ts +0 -53
  155. package/src/execution/engine/__tests__/common/errors.test.ts +0 -132
  156. package/src/execution/engine/__tests__/common/event-journal.test.ts +0 -177
  157. package/src/execution/engine/__tests__/common/kill-chain.test.ts +0 -192
  158. package/src/execution/engine/__tests__/common/nesting-guard.test.ts +0 -81
  159. package/src/execution/engine/__tests__/common/persona-router.test.ts +0 -123
  160. package/src/execution/engine/__tests__/common/pool-manager.test.ts +0 -154
  161. package/src/execution/engine/__tests__/common/schema-emulation.test.ts +0 -128
  162. package/src/execution/engine/__tests__/conformance/__fixtures__/pi-golden-events.json +0 -28
  163. package/src/execution/engine/__tests__/conformance/agent-event-invariants.ts +0 -141
  164. package/src/execution/engine/__tests__/conformance/contract.abort.test.ts +0 -109
  165. package/src/execution/engine/__tests__/conformance/contract.agent-events.test.ts +0 -101
  166. package/src/execution/engine/__tests__/conformance/contract.probe.test.ts +0 -77
  167. package/src/execution/engine/__tests__/conformance/contract.read-degradation.test.ts +0 -104
  168. package/src/execution/engine/__tests__/conformance/contract.relay.test.ts +0 -342
  169. package/src/execution/engine/__tests__/conformance/engine-conformance.live.test.ts +0 -201
  170. package/src/execution/engine/__tests__/conformance/golden-replay.pi.test.ts +0 -76
  171. package/src/execution/engine/__tests__/conformance/golden-replay.zcode.test.ts +0 -79
  172. package/src/execution/engine/__tests__/engine-discovery.test.ts +0 -87
  173. package/src/execution/engine/__tests__/engines-declaration.test.ts +0 -36
  174. package/src/execution/engine/__tests__/model-prompt.test.ts +0 -85
  175. package/src/execution/engine/__tests__/paths.test.ts +0 -39
  176. package/src/execution/engine/__tests__/registry.test.ts +0 -120
  177. package/src/execution/engine/__tests__/routing.test.ts +0 -231
  178. package/src/execution/engine/common/data-dir.ts +0 -62
  179. package/src/execution/engine/common/errors.ts +0 -183
  180. package/src/execution/engine/common/event-journal.ts +0 -254
  181. package/src/execution/engine/common/journal-replay.ts +0 -62
  182. package/src/execution/engine/common/kill-chain.ts +0 -221
  183. package/src/execution/engine/common/nesting-guard.ts +0 -50
  184. package/src/execution/engine/common/persona-router.ts +0 -108
  185. package/src/execution/engine/common/pool-manager.ts +0 -226
  186. package/src/execution/engine/common/schema-emulation.ts +0 -189
  187. package/src/execution/engine/common/session-view-projection.ts +0 -51
  188. package/src/execution/engine/engine-discovery.ts +0 -65
  189. package/src/execution/engine/engines/pi/__tests__/pi-engine.test.ts +0 -469
  190. package/src/execution/engine/engines/pi/__tests__/reader.test.ts +0 -155
  191. package/src/execution/engine/engines/pi/__tests__/task-spec-mapper.test.ts +0 -164
  192. package/src/execution/engine/engines/pi/pi-engine.ts +0 -415
  193. package/src/execution/engine/engines/pi/reader.ts +0 -48
  194. package/src/execution/engine/engines/pi/registration.ts +0 -35
  195. package/src/execution/engine/engines/pi/task-spec-mapper.ts +0 -100
  196. package/src/execution/engine/engines/zcode/__tests__/__fixtures__/zcode-golden-spawn.json +0 -39
  197. package/src/execution/engine/engines/zcode/__tests__/launcher.test.ts +0 -150
  198. package/src/execution/engine/engines/zcode/__tests__/parser.test.ts +0 -246
  199. package/src/execution/engine/engines/zcode/__tests__/preparer.test.ts +0 -228
  200. package/src/execution/engine/engines/zcode/__tests__/reader.test.ts +0 -210
  201. package/src/execution/engine/engines/zcode/__tests__/registration.test.ts +0 -64
  202. package/src/execution/engine/engines/zcode/__tests__/zcode-engine.live.test.ts +0 -127
  203. package/src/execution/engine/engines/zcode/__tests__/zcode-engine.test.ts +0 -567
  204. package/src/execution/engine/engines/zcode/constants.ts +0 -43
  205. package/src/execution/engine/engines/zcode/golden-sample.ts +0 -39
  206. package/src/execution/engine/engines/zcode/launcher.ts +0 -161
  207. package/src/execution/engine/engines/zcode/parser.ts +0 -436
  208. package/src/execution/engine/engines/zcode/preparer.ts +0 -363
  209. package/src/execution/engine/engines/zcode/reader.ts +0 -381
  210. package/src/execution/engine/engines/zcode/registration.ts +0 -37
  211. package/src/execution/engine/engines/zcode/zcode-engine.ts +0 -648
  212. package/src/execution/engine/host-task-spec.ts +0 -47
  213. package/src/execution/engine/model-prompt.ts +0 -59
  214. package/src/execution/engine/paths.ts +0 -42
  215. package/src/execution/engine/port.ts +0 -153
  216. package/src/execution/engine/registry.ts +0 -123
  217. package/src/execution/engine/routing.ts +0 -218
  218. package/src/execution/engine/types.ts +0 -304
  219. package/src/execution/execute-options-mapper.ts +0 -111
  220. package/src/execution/execution-record.ts +0 -906
  221. package/src/execution/finalize-record.ts +0 -251
  222. package/src/execution/finalized-marker.ts +0 -51
  223. package/src/execution/get-state-handshake.ts +0 -104
  224. package/src/execution/host-mode.ts +0 -56
  225. package/src/execution/idle-gc.ts +0 -47
  226. package/src/execution/lifecycle-manager.ts +0 -491
  227. package/src/execution/lifecycle-predicates.ts +0 -65
  228. package/src/execution/manifest-store.ts +0 -256
  229. package/src/execution/model-config-service.ts +0 -204
  230. package/src/execution/model-resolver.ts +0 -276
  231. package/src/execution/notifier.ts +0 -260
  232. package/src/execution/output-collector.ts +0 -88
  233. package/src/execution/path-encoding.ts +0 -60
  234. package/src/execution/pi-invocation.ts +0 -120
  235. package/src/execution/record-entry.ts +0 -132
  236. package/src/execution/record-store.ts +0 -1234
  237. package/src/execution/relay-env.ts +0 -37
  238. package/src/execution/session-context-resolver.ts +0 -64
  239. package/src/execution/session-file-gc.ts +0 -120
  240. package/src/execution/session-pending.ts +0 -164
  241. package/src/execution/session-reconstructor.ts +0 -678
  242. package/src/execution/session-runner.ts +0 -1548
  243. package/src/execution/sessions-index.ts +0 -304
  244. package/src/execution/spawn-event-adapter.ts +0 -363
  245. package/src/execution/stdin-writer.ts +0 -198
  246. package/src/execution/stream-sink.ts +0 -126
  247. package/src/execution/subagent-service.ts +0 -2119
  248. package/src/execution/subprocess-agent-runner.ts +0 -303
  249. package/src/execution/temp-prompt.ts +0 -62
  250. package/src/execution/tombstone-store.ts +0 -72
  251. package/src/execution/turn-limiter.ts +0 -102
  252. package/src/execution/types.ts +0 -910
  253. package/src/execution/ui-channels.ts +0 -216
  254. package/src/execution/ui-interaction-model.ts +0 -48
  255. package/src/execution/ui-request-handler-factory.ts +0 -212
  256. package/src/execution/ui-request-observability.ts +0 -81
  257. package/src/execution/ui-request-queue.ts +0 -174
  258. package/src/execution/worktree-manager.ts +0 -688
  259. package/src/execution/worktree-registry.ts +0 -222
  260. package/src/orchestration/__tests__/__fixtures__/worker-template.snapshot.txt +0 -336
  261. package/src/orchestration/__tests__/agent-call-catch-fallback.test.ts +0 -208
  262. package/src/orchestration/__tests__/agent-call-stream.test.ts +0 -157
  263. package/src/orchestration/__tests__/args-validator.test.ts +0 -143
  264. package/src/orchestration/__tests__/concurrency-gate.test.ts +0 -125
  265. package/src/orchestration/__tests__/config-loader.test.ts +0 -503
  266. package/src/orchestration/__tests__/error-recovery-handlers.test.ts +0 -724
  267. package/src/orchestration/__tests__/error-recovery-postmessage-defense.test.ts +0 -405
  268. package/src/orchestration/__tests__/error-recovery-serialize-failed-result.test.ts +0 -56
  269. package/src/orchestration/__tests__/error-recovery-workflow-call.test.ts +0 -166
  270. package/src/orchestration/__tests__/execute-agent-call.test.ts +0 -314
  271. package/src/orchestration/__tests__/jsonl-run-store-loadall-sources.test.ts +0 -171
  272. package/src/orchestration/__tests__/jsonl-run-store-session-file.test.ts +0 -1030
  273. package/src/orchestration/__tests__/launcher-nested-workflow.test.ts +0 -277
  274. package/src/orchestration/__tests__/lifecycle-runid-injection.test.ts +0 -96
  275. package/src/orchestration/__tests__/lifecycle.test.ts +0 -625
  276. package/src/orchestration/__tests__/review-fix-loop-e2e.test.ts +0 -1971
  277. package/src/orchestration/__tests__/script-lint.test.ts +0 -831
  278. package/src/orchestration/__tests__/skill-discovery.test.ts +0 -157
  279. package/src/orchestration/__tests__/test-mocks.ts +0 -197
  280. package/src/orchestration/__tests__/worker-host.test.ts +0 -120
  281. package/src/orchestration/__tests__/worker-returnmeta-passthrough.test.ts +0 -164
  282. package/src/orchestration/__tests__/worker-script-builder-runtime.test.ts +0 -520
  283. package/src/orchestration/__tests__/worker-script-builder.test.ts +0 -309
  284. package/src/orchestration/__tests__/worker-script-template-snapshot.test.ts +0 -110
  285. package/src/orchestration/__tests__/workflow-nesting-e2e.test.ts +0 -317
  286. package/src/orchestration/__tests__/workflow-script-lint-memo.test.ts +0 -110
  287. package/src/orchestration/__tests__/workflows-e2e.test.ts +0 -537
  288. package/src/orchestration/agent-opts-resolver.ts +0 -93
  289. package/src/orchestration/args-validator.ts +0 -127
  290. package/src/orchestration/concurrency-gate.ts +0 -69
  291. package/src/orchestration/config-loader.ts +0 -315
  292. package/src/orchestration/error-recovery.ts +0 -862
  293. package/src/orchestration/execute-agent-call.ts +0 -217
  294. package/src/orchestration/launcher.ts +0 -410
  295. package/src/orchestration/lifecycle.ts +0 -396
  296. package/src/orchestration/models/__tests__/budget.test.ts +0 -367
  297. package/src/orchestration/models/__tests__/trace.test.ts +0 -408
  298. package/src/orchestration/models/agent-call.ts +0 -83
  299. package/src/orchestration/models/budget.ts +0 -148
  300. package/src/orchestration/models/ports.ts +0 -164
  301. package/src/orchestration/models/run-runtime.ts +0 -89
  302. package/src/orchestration/models/run-spec.ts +0 -83
  303. package/src/orchestration/models/run-state.ts +0 -44
  304. package/src/orchestration/models/trace.ts +0 -183
  305. package/src/orchestration/models/types.ts +0 -292
  306. package/src/orchestration/models/workflow-run.ts +0 -254
  307. package/src/orchestration/models/workflow-script-registry.ts +0 -35
  308. package/src/orchestration/models/workflow-script.ts +0 -117
  309. package/src/orchestration/script-lint.ts +0 -766
  310. package/src/orchestration/skill-discovery.ts +0 -92
  311. package/src/orchestration/worker-handle.ts +0 -115
  312. package/src/orchestration/worker-host.ts +0 -98
  313. package/src/orchestration/worker-script-builder.ts +0 -408
  314. package/src/orchestration/workflow-files.ts +0 -85
  315. package/src/orchestration/workflow-script-registry-impl.ts +0 -133
  316. package/src/shared/__tests__/agent-ref.test.ts +0 -34
  317. package/src/shared/__tests__/meta-parser.test.ts +0 -304
  318. package/src/shared/__tests__/resource-discovery-manifest-cache.test.ts +0 -280
  319. package/src/shared/__tests__/resource-discovery.test.ts +0 -736
  320. package/src/shared/__tests__/resource-meta.test.ts +0 -51
  321. package/src/shared/__tests__/schema-jsonify.test.ts +0 -81
  322. package/src/shared/agent-event.ts +0 -13
  323. package/src/shared/agent-ref.ts +0 -57
  324. package/src/shared/meta-parser.ts +0 -261
  325. package/src/shared/resource-discovery.ts +0 -811
  326. package/src/shared/resource-meta.ts +0 -65
  327. package/src/shared/schema-jsonify.ts +0 -56
  328. package/src/shared/xml-injection.ts +0 -35
  329. package/workflows/README.md +0 -81
  330. package/workflows/_shared/agent-refs.cjs +0 -40
  331. package/workflows/chain.js +0 -131
  332. package/workflows/map-reduce.js +0 -174
  333. package/workflows/parallel.js +0 -148
  334. package/workflows/review-fix-loop-utils.cjs +0 -1307
  335. package/workflows/review-fix-loop.js +0 -1396
  336. package/workflows/scatter-gather.js +0 -178
@@ -1,766 +0,0 @@
1
- /**
2
- * Workflow Extension — 静态 lint
3
- *
4
- * 在执行前捕获常见的 workflow 脚本 API 误用。纯函数,零副作用,零 IO。
5
- *
6
- * 设计:
7
- * - lint 是编排层关注(非技术资源),归属 Engine 层。
8
- * - **entry-point 检查**:脚本必须含 agent/parallel/pipeline 之一,否则视为 error。
9
- * WorkflowScript.validate 直接委托 lintScript,故 entry-point 检查必须在此。
10
- * - LintFinding/LintResult 类型规范的 canonical 源在本文件。
11
- *
12
- * 检查项:
13
- * 1. 必须含 agent/parallel/pipeline 入口(error)
14
- * 2. agent 选项中 outputSchema 当 key 用 → 应为 schema(error)
15
- * 3. result.output / result.parsedOutput / result.content → agent 返回未包装值(error)
16
- * 4. readFileSync/writeFileSync 传状态 → 脆弱(warning)
17
- * 5. unlinkSync 清理状态 → 与 subprocess 文件读竞态(warning)
18
- * 6. 顶层未 await 的异步 IIFE + 内部调 agent/parallel/pipeline → 子进程被提前 kill(error)
19
- * 7. agent() 缺 description/label → TUI /workflows 显示 '(unnamed)'(warning)
20
- * 8. meta.phases 非字符串数组(对象数组等)→ 引擎忽略(warning)
21
- * 9. meta.phases 声明与 phase() 调用不一致 → 运行时分组与声明脱节(warning)
22
- *
23
- * 层归属:Engine。
24
- *
25
- * 参考:domain-models.md §7(validate 语义)。
26
- */
27
-
28
- /** m4 W2:meta 描述字段长度上限(§5.1 注入段预算约束)。 */
29
- const DESC_MAX_LENGTH = 200;
30
- /** m4 W4:agent examples 最少条数(正反各一需 ≥2)。 */
31
- const EXAMPLES_MIN_COUNT = 2;
32
- /** m5:examples 上限(注入是每 turn 常驻成本——防数据无界膨胀)。 */
33
- const EXAMPLES_MAX_COUNT = 4;
34
-
35
- /** Lint 检查发现项。 */
36
- export interface LintFinding {
37
- /** error = 会导致运行时崩溃; warning = 可能的错误 */
38
- severity: "error" | "warning";
39
- line: number;
40
- message: string;
41
- suggestion: string;
42
- }
43
-
44
- /** Lint 检查结果。 */
45
- export interface LintResult {
46
- valid: boolean;
47
- findings: LintFinding[];
48
- }
49
-
50
- /** 必须命中其一——workflow 脚本不调用任何编排函数等于空跑。 */
51
- import type { AgentMeta } from "../shared/resource-meta.ts";
52
- import { parseResourceMeta } from "../shared/meta-parser.ts";
53
-
54
- const ENTRY_POINT_PATTERNS = [/\bagent\s*\(/, /\bparallel\s*\(/, /\bpipeline\s*\(/] as const;
55
-
56
- /**
57
- * 检查脚本是否含至少一个编排入口(agent/parallel/pipeline)。
58
- * 无入口视为 error——空跑脚本无意义。
59
- */
60
- function checkEntryPoint(source: string): LintFinding[] {
61
- const hasEntryPoint = ENTRY_POINT_PATTERNS.some((p) => p.test(source));
62
- if (hasEntryPoint) return [];
63
- return [
64
- {
65
- severity: "error",
66
- line: 0,
67
- message: "Workflow script must call agent(), parallel(), or pipeline() at least once.",
68
- suggestion: "Add at least one agent(), parallel(), or pipeline() invocation.",
69
- },
70
- ];
71
- }
72
-
73
- /**
74
- * 检查单行 lint 问题,返回该行的发现项(可能为空)。
75
- */
76
- function checkLine(lineText: string, lineNum: number): LintFinding[] {
77
- const results: LintFinding[] = [];
78
-
79
- // 跳过注释行
80
- const trimmed = lineText.trim();
81
- if (trimmed.startsWith("//") || trimmed.startsWith("*") || trimmed.startsWith("/*")) {
82
- return results;
83
- }
84
-
85
- // result.output / result.parsedOutput / result.content
86
- const resultAccessPatterns: Array<{ regex: RegExp; field: string }> = [
87
- { regex: /\bresult\s*\.\s*output\b/, field: "output" },
88
- { regex: /\bresult\s*\.\s*parsedOutput\b/, field: "parsedOutput" },
89
- { regex: /\bresult\s*\.\s*content\b/, field: "content" },
90
- ];
91
- for (const p of resultAccessPatterns) {
92
- if (p.regex.test(lineText)) {
93
- results.push({
94
- severity: "error",
95
- line: lineNum,
96
- message: `\`result.${p.field}\` does not exist. agent() returns the unwrapped value directly.`,
97
- suggestion: "Use `const value = await agent(...)` and access `value` directly.",
98
- });
99
- }
100
- }
101
-
102
- // 文件传状态(readFileSync of STATE)
103
- if (/readFileSync\(.*STATE.*\)|readFileSync\(.*state.*\.json/i.test(lineText)) {
104
- results.push({
105
- severity: "warning",
106
- line: lineNum,
107
- message: "Reading a state file between agent calls is fragile (subprocess file access).",
108
- suggestion: "Use agent() with `schema` to get structured output directly, avoiding file I/O for state passing.",
109
- });
110
- }
111
-
112
- // unlinkSync 清理状态
113
- if (/unlinkSync.*state/i.test(lineText)) {
114
- results.push({
115
- severity: "warning",
116
- line: lineNum,
117
- message: "unlinkSync in finally may race with agent subprocess file reads.",
118
- suggestion: "Avoid file-based state passing; use agent() `schema` for structured output.",
119
- });
120
- }
121
-
122
- return results;
123
- }
124
-
125
- /**
126
- * 剔除字符串字面量与注释内容(MF-4)。逐行处理,不跨行。
127
- * 用途:`\bagent\s*\(` 不命中字符串里的 "agent(s)"(review-fix-loop L281 误报根因);
128
- * checkAgentDescription 的 `description\s*:` 不把 schema 内嵌 description 字符串当已提供。
129
- */
130
- function stripStringsAndComments(line: string): string {
131
- return line
132
- .replace(/"(?:\\.|[^"\\])*"/g, "")
133
- .replace(/'(?:\\.|[^'\\])*'/g, "")
134
- .replace(/`(?:\\.|[^`\\])*`/g, "")
135
- .replace(/\/\/.*$/, "")
136
- .replace(/\/\*[\s\S]*?\*\//g, "");
137
- }
138
-
139
- /**
140
- * 遍历 source 中所有 agent 调用的行范围,对每个调用执行 callback。
141
- *
142
- * agent 调用可能跨多行,通过括号配对定位起止行:
143
- * agent({
144
- * prompt: ...,
145
- * })
146
- *
147
- * 单行 agent 调用(如 `agent({ prompt: 'x' })`)的 startLine === endLine。
148
- * 与 checkAgentCalls / checkAgentDescription 共享同一套范围定义,确保
149
- * outputSchema 检查与 description 检查覆盖完全相同的调用集合(含 parallel/pipeline
150
- * 内嵌的 agent() 调用——它们同样被 `\bagent\s*\(` 匹配)。
151
- *
152
- * 匹配前逐行剔除字符串/注释内容(MF-4):字符串字面量里的 "agent(s)" 不再误触发。
153
- * 非字面量实参(agent(callVar) / agent(expr))跳过不回调:description 等选项在调用点
154
- * 静态不可见,checkAgentDescription 无法验证运行时构造的调用——继续报 warning 即误报
155
- * (review-fix-loop 的 agent(call) 三连误报根因)。
156
- *
157
- * @param callback (startLine, endLine) 0-based 行号
158
- */
159
- function forEachAgentCallRange(
160
- source: string,
161
- callback: (startLine: number, endLine: number) => void,
162
- ): void {
163
- const lines = source.split("\n");
164
- let inAgentCall = false;
165
- let depth = 0;
166
- let agentStartLine = -1;
167
-
168
- for (let i = 0; i < lines.length; i++) {
169
- const line = lines[i];
170
- const trimmed = line.trim();
171
-
172
- // 跳过注释
173
- if (trimmed.startsWith("//") || trimmed.startsWith("*") || trimmed.startsWith("/*")) {
174
- continue;
175
- }
176
-
177
- // 匹配/计括号都用剔除字符串与注释后的行,避免字面量内容干扰
178
- const codeLine = stripStringsAndComments(line);
179
-
180
- // 检测 agent 调用开始
181
- if (!inAgentCall && /\bagent\s*\(/.test(codeLine)) {
182
- inAgentCall = true;
183
- depth = 0;
184
- agentStartLine = i;
185
- // 从 agent( 开始计括号
186
- const afterAgent = codeLine.replace(/^.*?\bagent\s*\(/, "(");
187
- // 非字面量实参(agent(callVar) / agent(expr))→ 跳过(见函数头注释)。
188
- // 形如 `agent(` 换行 `{` 的多行字面量调用(argTail 为空)保留原追踪行为。
189
- const argTail = afterAgent.trimStart().slice(1).trimStart();
190
- if (argTail.length > 0 && !argTail.startsWith("{")) {
191
- inAgentCall = false;
192
- continue;
193
- }
194
- for (const ch of afterAgent) {
195
- if (ch === "(" || ch === "{" || ch === "[") depth++;
196
- if (ch === ")" || ch === "}" || ch === "]") depth--;
197
- }
198
- if (depth <= 0) {
199
- // 单行 agent 调用
200
- callback(agentStartLine, i);
201
- inAgentCall = false;
202
- }
203
- continue;
204
- }
205
-
206
- if (inAgentCall) {
207
- for (const ch of codeLine) {
208
- if (ch === "(" || ch === "{" || ch === "[") depth++;
209
- if (ch === ")" || ch === "}" || ch === "]") depth--;
210
- }
211
- if (depth <= 0) {
212
- callback(agentStartLine, i);
213
- inAgentCall = false;
214
- }
215
- }
216
- }
217
- }
218
-
219
- /**
220
- * 找出 source 中所有 agent 调用跨度,检查错误的选项 key(outputSchema)。
221
- * 范围遍历委托 forEachAgentCallRange。
222
- */
223
- function checkAgentCalls(source: string): LintFinding[] {
224
- const lines = source.split("\n");
225
- const findings: LintFinding[] = [];
226
- forEachAgentCallRange(source, (startLine, endLine) => {
227
- checkAgentCallOptions(lines, startLine, endLine, findings);
228
- });
229
- return findings;
230
- }
231
-
232
- /**
233
- * 检查 agent 调用内的错误选项 key。
234
- * 只标记 outputSchema 作为 KEY(属性名)使用的情况,不标记作为 VALUE。
235
- *
236
- * Error: { outputSchema } ← 简写属性(outputSchema 是 key)
237
- * Error: { outputSchema: ... } ← 显式 key
238
- * OK: { schema: outputSchema } ← outputSchema 是 value,`schema` 是 key
239
- * OK: const outputSchema = {} ← 变量声明(在 agent 调用外)
240
- */
241
- function checkAgentCallOptions(
242
- lines: string[],
243
- startLine: number,
244
- endLine: number,
245
- findings: LintFinding[],
246
- ): void {
247
- for (let i = startLine; i <= endLine; i++) {
248
- const line = lines[i];
249
-
250
- // 跳过变量声明(const/let/var outputSchema = ...)
251
- if (/\b(?:const|let|var)\s+outputSchema\b/.test(line)) {
252
- continue;
253
- }
254
-
255
- // 匹配:outputSchema 作为对象 key(简写或显式)
256
- if (/\boutputSchema\s*[,\}]/.test(line) || /\boutputSchema\s*:/.test(line)) {
257
- // 排除:outputSchema 作为 value(在另一个 key 的冒号后)
258
- // e.g. "schema: outputSchema," — outputSchema 前是冒号
259
- const beforeOutput = line.substring(0, line.indexOf("outputSchema"));
260
- if (/:\s*$/.test(beforeOutput)) {
261
- continue; // outputSchema 是 value,不是 key
262
- }
263
-
264
- findings.push({
265
- severity: "error",
266
- line: i + 1,
267
- message: "`outputSchema` is not a valid agent() option.",
268
- suggestion: "Use `schema` instead of `outputSchema`.",
269
- });
270
- }
271
- }
272
- }
273
-
274
- /**
275
- * 剔除 agent 选项对象里的 schema 块(`schema: {...}` 嵌套对象,括号配对)。
276
- * 用于 checkAgentDescription:JSON Schema 的 properties 里常见 `description:` 字段
277
- * (schema 文档字段,不是 agent 选项)——只剔字符串字面量时该 key 仍保留,会把
278
- * 内嵌 description 误判为「已提供」导致漏报(I-10 修正的剩余部分)。
279
- * 输入为已剔除字符串/注释的 range(无引号内容干扰,括号配对安全)。
280
- */
281
- function stripSchemaBlocks(text: string): string {
282
- let out = "";
283
- let i = 0;
284
- while (i < text.length) {
285
- const m = text.slice(i).match(/\bschema\s*:\s*\{/);
286
- if (!m || m.index === undefined) {
287
- out += text.slice(i);
288
- break;
289
- }
290
- const start = i + m.index;
291
- const braceIdx = start + m[0].lastIndexOf("{");
292
- out += text.slice(i, start);
293
- let depth = 0;
294
- let j = braceIdx;
295
- for (; j < text.length; j++) {
296
- if (text[j] === "{") depth++;
297
- else if (text[j] === "}") {
298
- depth--;
299
- if (depth === 0) {
300
- j++;
301
- break;
302
- }
303
- }
304
- }
305
- i = j;
306
- }
307
- return out;
308
- }
309
-
310
- /**
311
- * 检查 agent 调用是否提供 description(或其别名 label)。
312
- *
313
- * worker-script-builder 取 `firstArg.label || firstArg.description` 作 TUI 显示名,
314
- * 两者都缺 → node.agent 为空 → /workflows 视图显示 '(unnamed)'。
315
- *
316
- * 实现复用 forEachAgentCallRange 的范围定义,在范围内检测 `description:` 或 `label:`
317
- * 作为对象 key。两层剔除保证只认 agent 选项层的真正 key:① 字符串字面量(
318
- * stripStringsAndComments,MF-4);② schema 块(stripSchemaBlocks——schema 内嵌的
319
- * `description:` 是 JSON Schema 字段说明不是 agent 选项,不剔除会漏报)。
320
- */
321
- function checkAgentDescription(source: string): LintFinding[] {
322
- const lines = source.split("\n");
323
- const findings: LintFinding[] = [];
324
- forEachAgentCallRange(source, (startLine, endLine) => {
325
- // range 逐行剔除字符串字面量(MF-4)后再剔除 schema 块:schema 内嵌的
326
- // `description:` 对象 key(JSON Schema 字段说明)不再被误判为「已提供」——
327
- // 只有 agent 选项层的真正 description/label key 才算数(修正 I-10 漏报方向)。
328
- const range = stripSchemaBlocks(lines.slice(startLine, endLine + 1).map(stripStringsAndComments).join("\n"));
329
- // 对象以展开开头(agent({ ...call, ... })):description 来自运行时对象、调用点静态
330
- // 不可见——无法验证即不报(review-fix-loop 的 agent({ ...call, agent: ... }) 即此形态)。
331
- if (/\{\s*\.\.\./.test(range)) return;
332
- // 匹配 description 或 label 作为对象 key(后跟冒号)
333
- if (!/\b(description|label)\s*:/.test(range)) {
334
- findings.push({
335
- severity: "warning",
336
- line: startLine + 1,
337
- message:
338
- "agent() call without `description` (or `label`) will show as '(unnamed)' in TUI.",
339
- suggestion:
340
- "Add `description: 'kebab-case-name'` to agent() opts for readable /workflows display.",
341
- });
342
- }
343
- });
344
- return findings;
345
- }
346
-
347
- // ── 顶层未 await 的异步 IIFE 检测 ───────────────────────────
348
-
349
- /**
350
- * 匹配未 await 的 async IIFE 起点(粗筛)。
351
- *
352
- * 形式:`(async function`、`(async ()`、`(async (args)` 后跟 `=>`
353
- * 不匹配:`await (async ...`(lookbehind 排除)
354
- */
355
- const BARE_ASYNC_IIFE_PATTERN = /(^|[;\n\s{}(])(?<!await\s)\(async\s+(?:function\b|\(\)|\([^)]*\)\s*=>)/g;
356
-
357
- /**
358
- * 判断 IIFE 调用表达式是否被某个上下文「接住」(return/赋值/await 链等)。
359
- *
360
- * 返回 true 表示 IIFE 的 Promise 被接住(合法或可能合法);
361
- * false 表示 IIFE 是孤立语句表达式(fire-and-forget)。
362
- *
363
- * 判断方法:扫描 IIFE 起点 `(async` 前的非空白 token:
364
- * - 遇到 `=` `return` `await` `(` `[` `,` → 接住
365
- * - 遇到 `;` `{` `}` 或行首 → 孤立语句
366
- *
367
- * 例:
368
- * `const x = (async ...` → '=' 接住
369
- * `return (async ...` → 'return' 接住
370
- * `(async ...` 行首 → 孤立
371
- * `}; (async ...` → 孤立(前一个语句结束后新起一个)
372
- */
373
- function isIIFEAwaited(source: string, iifeStart: number): boolean {
374
- let i = iifeStart - 1;
375
- while (i >= 0) {
376
- const ch = source[i];
377
- if (/\s/.test(ch)) {
378
- i--;
379
- continue;
380
- }
381
- // 当前字符是标识符字符 → 向前扫完整标识符
382
- if (/[A-Za-z0-9_$]/.test(ch)) {
383
- // return / await / yield / 变量名(如 `foo(async ...`)→ 接住
384
- return true;
385
- }
386
- // 单字符操作符
387
- if (ch === "=" || ch === "(" || ch === "[" || ch === "," || ch === "?" || ch === ":") {
388
- return true;
389
- }
390
- if (ch === ";" || ch === "{" || ch === "}" || ch === ")") {
391
- return false;
392
- }
393
- // 其他字符(如 `.` `+`),保守视为接住(避免误报)
394
- return true;
395
- }
396
- return false;
397
- }
398
-
399
- /**
400
- * 检测未 await 的 async IIFE,且其内部调用了 agent/parallel/pipeline。
401
- *
402
- * 严重度分级:
403
- * - **error**:IIFE 是孤立语句表达式(fire-and-forget)+ 内部调 agent。
404
- * 这是 daily-news-impact 的 bug 模式——worker 外层 IIFE 不等内层就 post return,
405
- * 主线程 transition done → releaseRuntime → controller.abort() → SIGKILL 子进程。
406
- * - **warning**:IIFE 被 `=`/`return`/`(` 等接住(可能后续 await),但内部调 agent。
407
- * 提醒作者确认 Promise 真的被 await,不阻断运行。
408
- *
409
- * 误报规避(不报):
410
- * - await 前缀的 IIFE(lookbehind 排除)
411
- * - IIFE 内不含 agent/parallel/pipeline(stock-screening 这类纯 execSync 合法)
412
- *
413
- * 局限:纯正则 + 括号配对,无法做数据流分析。「赋值后稍后 await」「return 给外层 await」
414
- * 都识别为「接住」(warning 而非 error),避免阻断合法写法。
415
- */
416
- function checkBareAsyncIIFE(source: string): LintFinding[] {
417
- if (!ENTRY_POINT_PATTERNS.some((p) => p.test(source))) return [];
418
-
419
- // 用 matchAll 检查所有 IIFE(脚本可能有多个,每个都需独立判断)
420
- const findings: LintFinding[] = [];
421
- for (const match of source.matchAll(BARE_ASYNC_IIFE_PATTERN)) {
422
- const iifeStart = match.index ?? 0;
423
- const finding = analyzeIIFE(source, iifeStart);
424
- if (finding) findings.push(finding);
425
- }
426
- return findings;
427
- }
428
-
429
- /**
430
- * 分析单个 IIFE 起点是否触发 finding。
431
- *
432
- * 返回 LintFinding(error 或 warning)或 undefined(IIFE 内无 agent/无闭合)。
433
- * 详见 checkBareAsyncIIFE 的 [HISTORICAL] 教训记录。
434
- */
435
- function analyzeIIFE(source: string, iifeStart: number): LintFinding | undefined {
436
- const iifeLine = source.slice(0, iifeStart).split("\n").length;
437
-
438
- const firstBrace = source.indexOf("{", iifeStart);
439
- if (firstBrace === -1) return undefined;
440
-
441
- let depth = 0;
442
- let iifeEnd = -1;
443
- for (let i = firstBrace; i < source.length; i++) {
444
- const ch = source[i];
445
- if (ch === "{") depth++;
446
- else if (ch === "}") {
447
- depth--;
448
- if (depth === 0) {
449
- iifeEnd = i;
450
- break;
451
- }
452
- }
453
- }
454
- if (iifeEnd === -1) return undefined;
455
-
456
- const iifeBody = source.slice(firstBrace, iifeEnd);
457
- const hasAgentInside = ENTRY_POINT_PATTERNS.some((p) => p.test(iifeBody));
458
- if (!hasAgentInside) return undefined;
459
-
460
- const awaited = isIIFEAwaited(source, iifeStart);
461
- if (awaited) {
462
- return {
463
- severity: "warning",
464
- line: iifeLine,
465
- message:
466
- "Async IIFE wrapping agent() is assigned/returned but must be awaited. If the surrounding context does not await this Promise, the worker will post `return` early and kill in-flight agent() subprocesses.",
467
- suggestion:
468
- "Verify the surrounding code awaits this IIFE's Promise. When unsure, prefer top-level await directly (the worker already wraps your script in an async IIFE).",
469
- };
470
- }
471
-
472
- return {
473
- severity: "error",
474
- line: iifeLine,
475
- message:
476
- "Top-level async IIFE is a fire-and-forget statement. The worker's outer IIFE will post `return` before agent() resolves, killing the subprocess via runtime abort.",
477
- suggestion:
478
- "Remove the IIFE wrapper and use top-level await directly (the worker already wraps your script in an async IIFE). Or `await` the IIFE: `await (async function main() { ... })();`.",
479
- };
480
- }
481
-
482
- // ── 显示性检查(description / phase)───────────────────────────
483
-
484
- /**
485
- * 检查 meta.phases 是否字符串数组。
486
- *
487
- * 引擎 buildPhaseGroups 只按运行时 node.phase 分组,不读 meta.phases 声明。但 meta.phases
488
- * 仍用于文档/一致性检查(见 checkPhaseConsistency),且 SSOT 约定为字符串数组。对象数组
489
- * (如 [{title,detail}])是常见误写,提醒作者改为字符串数组。
490
- */
491
- function checkMetaPhases(source: string): LintFinding[] {
492
- const findings: LintFinding[] = [];
493
- // 跨行匹配(\s* 含换行):`phases: [` 与 `{` 换行分离的多行对象数组同样命中。
494
- // 原逐行匹配只命中「`[` 与 `{` 同行」,最常见的格式化写法(phases: [ 换行 { ... })零检出(MF-5)。
495
- // 行号从 match index 反推。字符串数组(phases: [\n "a")不匹配 \s*\{,不会误报。
496
- for (const m of source.matchAll(/phases\s*:\s*\[\s*\{/g)) {
497
- if (m.index === undefined) continue;
498
- const lineNum = source.slice(0, m.index).split("\n").length;
499
- findings.push({
500
- severity: "warning",
501
- line: lineNum,
502
- message:
503
- "`meta.phases` should be a string array like ['phase1','phase2']. Object arrays are ignored by the engine.",
504
- suggestion:
505
- "Use `phases: ['analyze','fix']`. Engine groups nodes by runtime `phase()` calls, not by `meta.phases` declarations.",
506
- });
507
- }
508
- return findings;
509
- }
510
-
511
- /**
512
- * 检查 meta.phases 声明与 phase() 调用的一致性。
513
- *
514
- * - 声明了但从未 phase() 调用 → warning(运行时分组用不上,声明形同虚设)
515
- * - phase() 调用了但未声明 → warning(声明遗漏,meta.phases 失去文档价值)
516
- *
517
- * 两者都为空时跳过(脚本不使用 phase 机制,不报)。
518
- */
519
- function checkPhaseConsistency(source: string): LintFinding[] {
520
- const findings: LintFinding[] = [];
521
-
522
- // 提取 meta.phases 声明的字符串 + 声明所在行号
523
- const declared = new Map<string, number>();
524
- const phasesArrayMatch = source.match(/phases\s*:\s*\[[^\]]*\]/);
525
- if (phasesArrayMatch && phasesArrayMatch.index !== undefined) {
526
- const inner = phasesArrayMatch[0];
527
- // 对象数组(如 [{title,detail}])由 checkMetaPhases 单独报,这里跳过提取,
528
- // 避免从对象字段里误抽出字符串作 declared。
529
- if (!/\[\s*\{/.test(inner)) {
530
- const phasesLine = source.slice(0, phasesArrayMatch.index).split("\n").length;
531
- for (const m of inner.matchAll(/['"]([^'"]+)['"]/g)) {
532
- if (!declared.has(m[1])) declared.set(m[1], phasesLine);
533
- }
534
- }
535
- }
536
-
537
- // 提取所有 phase() 调用实参 + 首次出现的行号
538
- const called = new Map<string, number>();
539
- for (const m of source.matchAll(/\bphase\s*\(\s*['"]([^'"]+)['"]/g)) {
540
- if (m.index === undefined) continue;
541
- const lineNum = source.slice(0, m.index).split("\n").length;
542
- if (!called.has(m[1])) called.set(m[1], lineNum);
543
- }
544
-
545
- // 两者都为空 → 跳过(脚本不使用 phase 机制)
546
- if (declared.size === 0 && called.size === 0) return [];
547
-
548
- // 声明了但从未 phase() 调用
549
- for (const [name, line] of declared) {
550
- if (!called.has(name)) {
551
- findings.push({
552
- severity: "warning",
553
- line,
554
- message: `declared phase '${name}' never set via phase().`,
555
- suggestion: `Add phase('${name}') before the agent() calls belonging to this phase, or remove it from meta.phases.`,
556
- });
557
- }
558
- }
559
-
560
- // 调用了但未声明
561
- for (const [name, line] of called) {
562
- if (!declared.has(name)) {
563
- findings.push({
564
- severity: "warning",
565
- line,
566
- message: `phase('${name}') called but not in meta.phases.`,
567
- suggestion: `Add '${name}' to meta.phases array, e.g. phases: [..., '${name}'].`,
568
- });
569
- }
570
- }
571
-
572
- return findings;
573
- }
574
-
575
- /**
576
- * 静态检查 workflow 脚本合法性。
577
- *
578
- * @param source 脚本源码(原始文件内容)
579
- * @returns LintResult(valid = 无 error 级 finding)
580
- */
581
- /**
582
- * W2/W3 共享 helper:meta 描述字段长度上限 + 单句判定(workflow lintScript 与
583
- * agent lintAgentMeta 共用同一规则——S-5 对称,避免 agent 长 description 常驻膨胀)。
584
- *
585
- * W2:>200 字符 → error。
586
- * W3:括号内容剥离((…)/(…)与 (...))后含换行/。;/'. ' 分句 → error。
587
- * 输出格式与 severity 约定与 checkMetaQuality 一致(error / line 1 / meta.<field> 前缀)。
588
- */
589
- function checkMetaFieldQuality(field: string, value: string): LintFinding[] {
590
- const findings: LintFinding[] = [];
591
- // W2
592
- if (value.length > DESC_MAX_LENGTH) {
593
- findings.push({
594
- severity: "error",
595
- line: 1,
596
- message: `meta.${field} 长度 ${value.length} 超过 ${DESC_MAX_LENGTH} 字符(§5.1 注入段预算约束)`,
597
- suggestion: "精简为单句路由描述,细节移入 usage",
598
- });
599
- }
600
- // W3:括号剥离后分句判定
601
- const stripped = value
602
- .replace(/([^)]*)/g, "")
603
- .replace(/\([^)]*\)/g, "")
604
- .replace(/\b(?:e\.g|i\.e)\.\s/g, ""); // 缩写剔除(exec-review F8:括号外 e.g. 不误报)
605
- if (/[\n。;]|\.\s/.test(stripped)) {
606
- findings.push({
607
- severity: "error",
608
- line: 1,
609
- message: `meta.${field} 非单句(含换行/分句标点)——§5.1 注入段要求一句话路由描述`,
610
- suggestion: "精简为单句,细节移入 when/notFor/usage",
611
- });
612
- }
613
- return findings;
614
- }
615
-
616
- /**
617
- * W1-W3:SSOT lint(m4)——保 §5.1 注入段 description 短单句、不含已声明参数名。
618
- * 仅对 parseResourceMeta 解析成功的 meta 执行(旧 const meta 格式不检查)。
619
- *
620
- * W1 参数名匹配(非形态匹配):'note:'/'Example:'/'a=b' 等 prose 不误报(design-review
621
- * 探针实测形态匹配误报面);参数名集合 = meta.parameters.properties keys + patternProperties
622
- * 的 word 前缀(^word\\d+$ 转义形态)。检查面 = description + when + notFor 三字段
623
- * (同进 §5.1 注入段)。W2/W3 抽到 checkMetaFieldQuality(lintAgentMeta 共用)。
624
- */
625
- function checkMetaQuality(meta: { description?: string; when?: string; notFor?: string; parameters?: Record<string, unknown> }): LintFinding[] {
626
- const findings: LintFinding[] = [];
627
- const description = meta.description ?? "";
628
-
629
- // W1 参数名集合
630
- const paramNames = new Set<string>();
631
- if (meta.parameters && typeof meta.parameters === "object") {
632
- const props = (meta.parameters as Record<string, unknown>).properties;
633
- if (props !== null && typeof props === "object") {
634
- for (const k of Object.keys(props as Record<string, unknown>)) paramNames.add(k);
635
- }
636
- const pp = (meta.parameters as Record<string, unknown>).patternProperties;
637
- if (pp !== null && typeof pp === "object") {
638
- for (const p of Object.keys(pp as Record<string, unknown>)) {
639
- const m = p.match(/^\^([a-zA-Z]+)\\d\+\$$/);
640
- if (m) paramNames.add(m[1] as string);
641
- }
642
- }
643
- }
644
-
645
- const fields: Array<[string, string]> = [
646
- ["description", description],
647
- ["when", meta.when ?? ""],
648
- ["notFor", meta.notFor ?? ""],
649
- ];
650
- for (const [field, value] of fields) {
651
- if (value.length === 0) continue;
652
- // W1:已声明参数名的 ':'/'=' 形态(参数名转义防 RegExp 注入崩溃——exec-review F2;
653
- // ':' 形态加 \\b 前缀防子串误报(subtask: 不命中 task——exec-review F6))
654
- for (const name of paramNames) {
655
- const nameEsc = name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
656
- if (new RegExp(`\\b${nameEsc}:\\s`).test(value) || new RegExp(`\\b${nameEsc}=\\S`).test(value)) {
657
- findings.push({
658
- severity: "error",
659
- line: 1,
660
- message: `meta.${field} 包含已声明参数名 '${name}'('${name}:' / '${name}=' 形态)——参数契约请 read 脚本文件的 @pi-meta parameters 查询,description/when/notFor 只放路由信息`,
661
- suggestion: `从 meta.${field} 移除 '${name}: ...' / '${name}=...',改在 parameters/usage 中声明`,
662
- });
663
- }
664
- }
665
- findings.push(...checkMetaFieldQuality(field, value));
666
- }
667
- return findings;
668
- }
669
-
670
- /**
671
- * W2/W3 + W4(m4/m5 挂 agent 加载路径):description/when/notFor 长度与单句检查
672
- * (与 workflow 同规则,S-5 对称)+ examples 正反各一。
673
- * RoutingExample.positive 判别(action 是 string 非 null);examples 缺失 → 无 finding
674
- * (未迁移 agent 不报错——m5 挂载安全根基)。
675
- */
676
- export function lintAgentMeta(meta: AgentMeta): LintFinding[] {
677
- // W2/W3(S-5 对称):agent 的 description/when/notFor 与 workflow 同规则
678
- // (长度上限 + 单句判定)——formatAgentList 原样注入 systemPrompt,长描述是每 turn 常驻成本。
679
- const findings: LintFinding[] = [];
680
- const fields: Array<[string, string]> = [
681
- ["description", meta.description],
682
- ["when", meta.when ?? ""],
683
- ["notFor", meta.notFor ?? ""],
684
- ];
685
- for (const [field, value] of fields) {
686
- if (value.length === 0) continue;
687
- findings.push(...checkMetaFieldQuality(field, value));
688
- }
689
-
690
- const examples = meta.examples;
691
- if (examples === undefined) return findings; // 未迁移 agent(无 examples 字段)不报错
692
- if (examples.length === 0) {
693
- findings.push({
694
- severity: "error",
695
- line: 1,
696
- message: `agent '${meta.name}' 声明了 examples 但为空——需 ≥2 条且正反各一`,
697
- suggestion: "补正向样本(何时调用)+ 反向样本(何时不调用)",
698
- });
699
- return findings;
700
- }
701
- const hasPositive = examples.some((e) => e.positive === true);
702
- const hasNegative = examples.some((e) => e.positive === false);
703
- if (examples.length < EXAMPLES_MIN_COUNT || !hasPositive || !hasNegative) {
704
- findings.push({
705
- severity: "error",
706
- line: 1,
707
- message: `agent '${meta.name}' 的 examples 需 ≥2 条且正反各一(positive:true 触发路由 + positive:false 反例)`,
708
- suggestion: "补正向样本(何时调用)+ 反向样本(何时不调用)",
709
- });
710
- return findings;
711
- }
712
- if (examples.length > EXAMPLES_MAX_COUNT) {
713
- findings.push({
714
- severity: "error",
715
- line: 1,
716
- message: `agent '${meta.name}' 的 examples ${examples.length} 条超过上限 ${EXAMPLES_MAX_COUNT}(注入段是每 turn 常驻成本)`,
717
- suggestion: "精简到 2-4 条:正反各一 + 最多 2 条冗余",
718
- });
719
- }
720
- return findings;
721
- }
722
-
723
- export function lintScript(source: string): LintResult {
724
- const lines = source.split("\n");
725
- const findings: LintFinding[] = [];
726
-
727
- // 入口检查(必须有 agent/parallel/pipeline 之一)
728
- findings.push(...checkEntryPoint(source));
729
-
730
- // 逐行检查(result.output、文件传状态等)
731
- for (let i = 0; i < lines.length; i++) {
732
- findings.push(...checkLine(lines[i], i + 1));
733
- }
734
-
735
- // agent 调用上下文检查(outputSchema 作为 key)
736
- findings.push(...checkAgentCalls(source));
737
-
738
- // [HISTORICAL] 顶层未 await 的异步 IIFE + 内部调 agent——子进程被提前 kill。
739
- // 教训来源:daily-news-impact.js 用 (async function main(){...})();() 包裹整个脚本,
740
- // worker 外层 IIFE 不等内层 IIFE 就 postMessage("return"),主线程 transition done
741
- // → release runtime → controller.abort() → spawn 后 2ms SIGKILL 子进程。
742
- // 诊断耗时 4 轮:先后误判为 model 故障 / 工具缺失 / turn-signal abort / ConcurrencyGate 异常,
743
- // 最终靠 worker-host → handleReturn → release → abort 的调用栈定位。
744
- findings.push(...checkBareAsyncIIFE(source));
745
-
746
- // m4 W1-W3:meta 质量(description/when/notFor 短单句 + 不含已声明参数名)。
747
- // 仅对 IF1 解析成功的 @pi-meta 执行——旧 const meta 格式(D1 无 adapter)不检查。
748
- const meta = parseResourceMeta(source, "workflow");
749
- if (meta && meta.kind === "workflow") {
750
- findings.push(...checkMetaQuality(meta));
751
- }
752
-
753
- // 显示性检查(warning):agent 缺 description / meta.phases 形式 / phase 一致性。
754
- // 目的:让 TUI /workflows 视图避免 unnamed agent 与 (unnamed) phase 分组。
755
- findings.push(...checkAgentDescription(source));
756
- findings.push(...checkMetaPhases(source));
757
- findings.push(...checkPhaseConsistency(source));
758
-
759
- // 按行号排序,稳定输出
760
- findings.sort((a, b) => a.line - b.line);
761
-
762
- return {
763
- valid: !findings.some((f) => f.severity === "error"),
764
- findings,
765
- };
766
- }