minimal-agent 0.13.0 → 0.14.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 (470) hide show
  1. package/README.md +6 -5
  2. package/package.json +60 -4
  3. package/plugins/computer_use/.claude-plugin/plugin.json +1 -1
  4. package/plugins/cron-runner/src/commands.js +4 -4
  5. package/plugins/cron-runner/src/commands.ts +4 -4
  6. package/plugins/workflow-runner/.claude-plugin/plugin.json +2 -2
  7. package/plugins/workflow-runner/commands/workflow.md +57 -90
  8. package/plugins/workflow-runner/commands/workflows.md +4 -4
  9. package/plugins/workflow-runner/plugin.js +24 -13
  10. package/plugins/workflow-runner/plugin.ts +28 -13
  11. package/plugins/workflow-runner/src/discovery.js +160 -0
  12. package/plugins/workflow-runner/src/discovery.ts +194 -0
  13. package/plugins/workflow-runner/src/goalAnchor.js +53 -0
  14. package/plugins/workflow-runner/src/goalAnchor.ts +59 -0
  15. package/plugins/workflow-runner/src/index.js +197 -206
  16. package/plugins/workflow-runner/src/index.ts +248 -249
  17. package/plugins/workflow-runner/src/inputCoercion.js +40 -0
  18. package/plugins/workflow-runner/src/inputCoercion.ts +43 -0
  19. package/plugins/workflow-runner/src/journal.js +224 -0
  20. package/plugins/workflow-runner/src/journal.ts +274 -0
  21. package/plugins/workflow-runner/src/jsonSchema.js +132 -0
  22. package/plugins/workflow-runner/src/jsonSchema.ts +162 -0
  23. package/plugins/workflow-runner/src/moduleResolution.js +64 -0
  24. package/plugins/workflow-runner/src/moduleResolution.ts +71 -0
  25. package/plugins/workflow-runner/src/resourceInputs.js +220 -0
  26. package/plugins/workflow-runner/src/resourceInputs.ts +300 -0
  27. package/plugins/workflow-runner/src/runner.js +400 -489
  28. package/plugins/workflow-runner/src/runner.ts +488 -560
  29. package/plugins/workflow-runner/src/runtime/config.js +46 -0
  30. package/plugins/workflow-runner/src/runtime/config.ts +69 -0
  31. package/plugins/workflow-runner/src/runtime/context.js +1304 -0
  32. package/plugins/workflow-runner/src/runtime/context.ts +1786 -0
  33. package/plugins/workflow-runner/src/runtime/exec.js +34 -0
  34. package/plugins/workflow-runner/src/runtime/exec.ts +53 -0
  35. package/plugins/workflow-runner/src/runtime/flow.js +8 -0
  36. package/plugins/workflow-runner/src/runtime/flow.ts +12 -0
  37. package/plugins/workflow-runner/src/runtime/fs.js +27 -0
  38. package/plugins/workflow-runner/src/runtime/fs.ts +41 -0
  39. package/plugins/workflow-runner/src/runtime/model.js +6 -0
  40. package/plugins/workflow-runner/src/runtime/model.ts +8 -0
  41. package/plugins/workflow-runner/src/runtime/output.js +7 -0
  42. package/plugins/workflow-runner/src/runtime/output.ts +21 -0
  43. package/plugins/workflow-runner/src/runtime/scheduler.js +141 -0
  44. package/plugins/workflow-runner/src/runtime/scheduler.ts +166 -0
  45. package/plugins/workflow-runner/src/runtime/state.js +16 -0
  46. package/plugins/workflow-runner/src/runtime/state.ts +19 -0
  47. package/plugins/workflow-runner/src/runtime/tools.js +3 -0
  48. package/plugins/workflow-runner/src/runtime/tools.ts +6 -0
  49. package/plugins/workflow-runner/src/sandbox.js +141 -0
  50. package/plugins/workflow-runner/src/sandbox.ts +182 -0
  51. package/plugins/workflow-runner/src/scriptGuard.d.mts +38 -0
  52. package/plugins/workflow-runner/src/scriptGuard.mjs +449 -0
  53. package/plugins/workflow-runner/src/taskIdentity.js +151 -307
  54. package/plugins/workflow-runner/src/taskIdentity.ts +207 -355
  55. package/plugins/workflow-runner/src/toolRuntime.js +29 -8
  56. package/plugins/workflow-runner/src/toolRuntime.ts +36 -11
  57. package/plugins/workflow-runner/src/types.js +7 -211
  58. package/plugins/workflow-runner/src/types.ts +154 -623
  59. package/plugins/workflow-runner/src/util.js +47 -0
  60. package/plugins/workflow-runner/src/util.ts +51 -0
  61. package/plugins/workflow-runner/src/workflowContext.js +20 -0
  62. package/plugins/workflow-runner/src/workflowContext.ts +58 -0
  63. package/plugins/workflow-runner/src/workflowDependencies.js +36 -0
  64. package/plugins/workflow-runner/src/workflowDependencies.ts +59 -0
  65. package/plugins/workflow-runner/src/workflowFingerprint.js +118 -0
  66. package/plugins/workflow-runner/src/workflowFingerprint.ts +122 -0
  67. package/plugins/workflow-runner/src/workflowState.js +474 -802
  68. package/plugins/workflow-runner/src/workflowState.ts +575 -970
  69. package/plugins/workflow-runner/test/api.test.ts +223 -0
  70. package/plugins/workflow-runner/test/apiCapabilities.test.ts +614 -0
  71. package/plugins/workflow-runner/test/apiConcurrencyGate.test.ts +104 -0
  72. package/plugins/workflow-runner/test/apiEcho.test.ts +210 -0
  73. package/plugins/workflow-runner/test/apiReadText.test.ts +178 -0
  74. package/plugins/workflow-runner/test/apiResourceLedger.test.ts +184 -0
  75. package/plugins/workflow-runner/test/childWorkflow.test.ts +304 -0
  76. package/plugins/workflow-runner/test/discovery.test.ts +234 -0
  77. package/plugins/workflow-runner/test/goalAnchor.test.ts +106 -0
  78. package/plugins/workflow-runner/test/goalController.test.ts +317 -0
  79. package/plugins/workflow-runner/test/journalLanes.test.ts +198 -0
  80. package/plugins/workflow-runner/test/listResources.test.ts +106 -0
  81. package/plugins/workflow-runner/test/resourceInputs.test.ts +294 -0
  82. package/plugins/workflow-runner/test/sandbox.test.ts +126 -0
  83. package/plugins/workflow-runner/test/scheduler.test.ts +70 -0
  84. package/plugins/workflow-runner/test/scriptGuard.test.ts +276 -0
  85. package/plugins/workflow-runner/test/skillDocSync.test.ts +78 -0
  86. package/plugins/workflow-runner/test/taskIdentity.test.ts +288 -179
  87. package/plugins/workflow-runner/test/workflowFingerprint.test.ts +106 -0
  88. package/plugins/workflow-runner/test/workflowState.test.ts +429 -0
  89. package/skills/pptx/examples/assets/cover-bg.svg +23 -0
  90. package/skills/pptx/examples/assets/icon-spark.svg +5 -0
  91. package/skills/ui-design/SKILL.md +5 -2
  92. package/skills/ui-design/references/layout-composition.md +1 -1
  93. package/skills/workflow-creator/SKILL.md +344 -0
  94. package/skills/workflow-creator/assets/template/workflow.mjs +48 -0
  95. package/skills/workflow-creator/references/api.md +216 -0
  96. package/skills/workflow-creator/references/migration.md +103 -0
  97. package/skills/workflow-creator/references/patterns.md +779 -0
  98. package/skills/workflow-creator/scripts/list-resources.mjs +205 -0
  99. package/skills/workflow-creator/scripts/scaffold.mjs +148 -0
  100. package/skills/workflow-creator/scripts/validate.mjs +275 -0
  101. package/skills/workflow-creator/scripts/workflow-creator.test.mjs +82 -0
  102. package/src/advisor/advisorCoordinator.js +503 -0
  103. package/src/advisor/advisorCoordinator.ts +657 -0
  104. package/src/cli/configMenu.tsx +0 -1
  105. package/src/cli/print.js +27 -6
  106. package/src/cli/print.ts +48 -4
  107. package/src/cli/streamJson.js +5 -6
  108. package/src/cli/streamJson.ts +8 -4
  109. package/src/cli/wizards/advisorWizard.js +4 -2
  110. package/src/cli/wizards/advisorWizard.tsx +8 -2
  111. package/src/cli/wizards/contextAdvisorWizard.js +5 -4
  112. package/src/cli/wizards/contextAdvisorWizard.tsx +7 -6
  113. package/src/collaboration/taskFlow.js +115 -0
  114. package/src/collaboration/taskFlow.ts +169 -0
  115. package/src/config/configDefaults.js +41 -1
  116. package/src/config/configDefaults.ts +43 -1
  117. package/src/config/configDiagnostics.ts +2 -2
  118. package/src/config/configFile.js +40 -9
  119. package/src/config/configFile.ts +60 -11
  120. package/src/config/configService.js +419 -74
  121. package/src/config/configService.ts +703 -145
  122. package/src/config/numericLimits.js +22 -0
  123. package/src/config/numericLimits.ts +24 -0
  124. package/src/config.js +1 -0
  125. package/src/config.ts +3 -2
  126. package/src/context/budgetPlan.js +1 -1
  127. package/src/context/budgetPlan.ts +1 -1
  128. package/src/context/compact.js +7 -2
  129. package/src/context/compact.ts +9 -2
  130. package/src/context/microCompactLite.js +212 -13
  131. package/src/context/microCompactLite.ts +261 -12
  132. package/src/context/sessionRegistry.js +8 -1
  133. package/src/context/sessionRegistry.ts +18 -5
  134. package/src/context/toolResultArchive.js +72 -0
  135. package/src/context/toolResultArchive.ts +95 -0
  136. package/src/goal/errors.js +8 -0
  137. package/src/goal/errors.ts +18 -0
  138. package/src/goal/goalRuntime.js +344 -0
  139. package/src/goal/goalRuntime.ts +466 -0
  140. package/src/goal/goalStore.js +200 -0
  141. package/src/goal/goalStore.ts +219 -0
  142. package/src/goal/index.js +4 -0
  143. package/src/goal/index.ts +4 -0
  144. package/src/goal/statusMapping.js +71 -0
  145. package/src/goal/statusMapping.ts +95 -0
  146. package/src/goal/types.js +7 -0
  147. package/src/goal/types.ts +179 -0
  148. package/src/goal/workflowController.js +306 -0
  149. package/src/goal/workflowController.ts +360 -0
  150. package/src/harness/evidenceLedger.js +2 -0
  151. package/src/harness/evidenceLedger.ts +1 -0
  152. package/src/harness/stallGuard.js +9 -3
  153. package/src/harness/stallGuard.ts +7 -3
  154. package/src/images/imageResolver.js +161 -0
  155. package/src/images/imageResolver.ts +189 -0
  156. package/src/infra/verification.js +34 -5
  157. package/src/infra/verification.ts +39 -4
  158. package/src/llm/client.js +79 -21
  159. package/src/llm/client.ts +182 -35
  160. package/src/llm/gatewayClient.js +201 -0
  161. package/src/llm/gatewayClient.ts +299 -0
  162. package/src/llm/messageProvenance.js +46 -0
  163. package/src/llm/messageProvenance.ts +58 -0
  164. package/src/llm/modelProvider.js +1 -0
  165. package/src/llm/modelProvider.ts +22 -0
  166. package/src/llm/providers/anthropicFormat.js +108 -1
  167. package/src/llm/providers/anthropicFormat.ts +115 -4
  168. package/src/llm/providers/openaiFormat.js +9 -0
  169. package/src/llm/providers/openaiFormat.ts +12 -3
  170. package/src/loop.js +319 -54
  171. package/src/loop.ts +373 -57
  172. package/src/main.js +4 -1
  173. package/src/main.tsx +4 -1
  174. package/src/managedExecution/index.js +2 -0
  175. package/src/managedExecution/index.ts +2 -0
  176. package/src/managedExecution/registry.js +15 -0
  177. package/src/managedExecution/registry.ts +20 -0
  178. package/src/managedExecution/types.js +1 -0
  179. package/src/managedExecution/types.ts +86 -0
  180. package/src/modelRef.js +9 -0
  181. package/src/modelRef.ts +31 -0
  182. package/src/multimodal.js +14 -28
  183. package/src/multimodal.ts +17 -33
  184. package/src/net/lanDiscovery.js +65 -0
  185. package/src/net/lanDiscovery.ts +82 -0
  186. package/src/plugin-sdk.js +28 -3
  187. package/src/plugin-sdk.ts +39 -6
  188. package/src/plugins/pluginApi.ts +16 -1
  189. package/src/plugins/pluginLoader.js +4 -0
  190. package/src/plugins/pluginLoader.ts +6 -0
  191. package/src/plugins/pluginRunner.js +139 -7
  192. package/src/plugins/pluginRunner.ts +173 -9
  193. package/src/plugins/slashFallback.js +113 -0
  194. package/src/plugins/slashFallback.ts +137 -0
  195. package/src/prompts/policyRegistry.js +12 -32
  196. package/src/prompts/policyRegistry.ts +15 -32
  197. package/src/prompts/promptAssembly.js +103 -12
  198. package/src/prompts/promptAssembly.ts +114 -12
  199. package/src/prompts/skillList.js +21 -3
  200. package/src/prompts/skillList.ts +27 -2
  201. package/src/prompts/stableSystemPrompt.js +18 -0
  202. package/src/prompts/stableSystemPrompt.ts +33 -0
  203. package/src/runtime/agentRuntimeContext.js +8 -2
  204. package/src/runtime/agentRuntimeContext.ts +17 -4
  205. package/src/session/runAgentTurn.js +78 -12
  206. package/src/session/runAgentTurn.ts +105 -23
  207. package/src/session/runTurn.js +10 -9
  208. package/src/session/runTurn.ts +17 -8
  209. package/src/session/sessionState.ts +2 -0
  210. package/src/team/auth.js +186 -0
  211. package/src/team/auth.ts +298 -0
  212. package/src/team/cli.js +105 -0
  213. package/src/team/cli.ts +122 -0
  214. package/src/team/onlineToolContract.js +64 -0
  215. package/src/team/onlineToolContract.ts +109 -0
  216. package/src/team/toolPolicy.js +36 -0
  217. package/src/team/toolPolicy.ts +75 -0
  218. package/src/tools/advisor/advisor.js +90 -46
  219. package/src/tools/advisor/advisor.ts +100 -47
  220. package/src/tools/advisor/advisorDecision.js +71 -0
  221. package/src/tools/advisor/advisorDecision.ts +85 -0
  222. package/src/tools/agent/agent.js +18 -5
  223. package/src/tools/agent/agent.ts +21 -7
  224. package/src/tools/agent/agentControl.js +246 -0
  225. package/src/tools/agent/agentControl.ts +284 -0
  226. package/src/tools/agent/agentTaskService.js +40 -1
  227. package/src/tools/agent/agentTaskService.ts +44 -1
  228. package/src/tools/agent/agentTypes.js +14 -2
  229. package/src/tools/agent/agentTypes.ts +15 -2
  230. package/src/tools/applypatch/applyPatch.js +362 -0
  231. package/src/tools/applypatch/applyPatch.ts +428 -0
  232. package/src/tools/bash/bash.js +22 -15
  233. package/src/tools/bash/bash.ts +23 -17
  234. package/src/tools/capabilities.js +10 -5
  235. package/src/tools/capabilities.ts +10 -5
  236. package/src/tools/computeruse/computerIntent.js +32 -0
  237. package/src/tools/computeruse/computerIntent.ts +46 -0
  238. package/src/tools/computeruse/telemetryLog.js +12 -0
  239. package/src/tools/computeruse/telemetryLog.ts +16 -0
  240. package/src/tools/computeruse/vision/telemetry.js +1 -0
  241. package/src/tools/computeruse/vision/telemetry.ts +1 -0
  242. package/src/tools/effectiveToolSet.js +4 -0
  243. package/src/tools/effectiveToolSet.ts +4 -0
  244. package/src/tools/index.js +21 -2
  245. package/src/tools/index.ts +33 -2
  246. package/src/tools/presentresult/presentresult.js +6 -15
  247. package/src/tools/presentresult/presentresult.ts +6 -15
  248. package/src/tools/read/read.js +62 -9
  249. package/src/tools/read/read.ts +63 -9
  250. package/src/tools/shared/fileUtils.js +1 -2
  251. package/src/tools/shared/fileUtils.ts +1 -2
  252. package/src/tools/shared/toolOutputStore.js +23 -1
  253. package/src/tools/shared/toolOutputStore.ts +22 -1
  254. package/src/tools/skill/skill.js +9 -3
  255. package/src/tools/skill/skill.ts +8 -4
  256. package/src/tools/toolPromptGuides.js +117 -0
  257. package/src/tools/toolPromptGuides.ts +137 -0
  258. package/src/tools/webbrowser/agentBrowserPath.js +19 -9
  259. package/src/tools/webbrowser/agentBrowserPath.ts +70 -28
  260. package/src/tools/webbrowser/browser.js +27 -11
  261. package/src/tools/webbrowser/browser.ts +68 -24
  262. package/src/tools/webbrowser/browserAutomation.js +329 -0
  263. package/src/tools/webbrowser/browserAutomation.ts +467 -0
  264. package/src/tools/webbrowser/browserAxState.js +97 -0
  265. package/src/tools/webbrowser/browserAxState.ts +142 -0
  266. package/src/tools/webbrowser/browserBatch.js +1578 -0
  267. package/src/tools/webbrowser/browserBatch.ts +2382 -0
  268. package/src/tools/webbrowser/browserCdp.js +208 -0
  269. package/src/tools/webbrowser/browserCdp.ts +255 -0
  270. package/src/tools/webbrowser/browserConditions.js +24 -0
  271. package/src/tools/webbrowser/browserConditions.ts +45 -0
  272. package/src/tools/webbrowser/browserContent.js +281 -0
  273. package/src/tools/webbrowser/browserContent.ts +438 -0
  274. package/src/tools/webbrowser/browserControl.js +131 -34
  275. package/src/tools/webbrowser/browserControl.ts +210 -56
  276. package/src/tools/webbrowser/browserDaemon.js +22 -14
  277. package/src/tools/webbrowser/browserDaemon.ts +54 -21
  278. package/src/tools/webbrowser/browserFrames.js +47 -0
  279. package/src/tools/webbrowser/browserFrames.ts +54 -0
  280. package/src/tools/webbrowser/browserInteractionResolver.js +17 -0
  281. package/src/tools/webbrowser/browserInteractionResolver.ts +40 -0
  282. package/src/tools/webbrowser/browserLocator.js +140 -0
  283. package/src/tools/webbrowser/browserLocator.ts +194 -0
  284. package/src/tools/webbrowser/browserOutcome.js +10 -17
  285. package/src/tools/webbrowser/browserOutcome.ts +48 -31
  286. package/src/tools/webbrowser/browserRecovery.js +58 -0
  287. package/src/tools/webbrowser/browserRecovery.ts +83 -0
  288. package/src/tools/webbrowser/browserRunRegistry.js +35 -3
  289. package/src/tools/webbrowser/browserRunRegistry.ts +91 -15
  290. package/src/tools/webbrowser/browserService.js +28 -2
  291. package/src/tools/webbrowser/browserService.ts +49 -9
  292. package/src/tools/webbrowser/browserStream.js +132 -33
  293. package/src/tools/webbrowser/browserStream.ts +224 -50
  294. package/src/tools/webbrowser/browserTabs.js +48 -15
  295. package/src/tools/webbrowser/browserTabs.ts +178 -96
  296. package/src/tools/webbrowser/webbrowser.js +679 -144
  297. package/src/tools/webbrowser/webbrowser.ts +975 -236
  298. package/src/tools/webfetch/preapproved.js +0 -2
  299. package/src/tools/webfetch/preapproved.ts +0 -2
  300. package/src/tools/websearch/websearch.js +129 -8
  301. package/src/tools/websearch/websearch.ts +138 -6
  302. package/src/types.ts +440 -38
  303. package/src/ui/App.js +377 -58
  304. package/src/ui/App.tsx +576 -111
  305. package/src/ui/LiveArea.js +14 -8
  306. package/src/ui/LiveArea.tsx +14 -10
  307. package/src/ui/MessageList.js +63 -20
  308. package/src/ui/MessageList.tsx +101 -16
  309. package/src/ui/ModelPicker.js +7 -4
  310. package/src/ui/ModelPicker.tsx +55 -8
  311. package/src/ui/hooks/useChat.js +152 -163
  312. package/src/ui/hooks/useChat.ts +396 -334
  313. package/src/ui/loginCommand.js +56 -0
  314. package/src/ui/loginCommand.ts +63 -0
  315. package/src/ui/toolProgress.js +41 -0
  316. package/src/ui/toolProgress.ts +44 -0
  317. package/src/web/browserBridge.js +20 -2
  318. package/src/web/browserBridge.ts +20 -2
  319. package/src/web/protocol.ts +1897 -194
  320. package/workflows/adaptive-research/workflow.mjs +86 -0
  321. package/workflows/e2e-capabilities/workflow.mjs +102 -0
  322. package/workflows/e2e-llm/workflow.mjs +52 -0
  323. package/workflows/e2e-smoke/workflow.mjs +59 -0
  324. package/workflows/e2e-vision-schema/README.md +48 -0
  325. package/workflows/e2e-vision-schema/workflow.mjs +149 -0
  326. package/workflows/e2e-write-greet/workflow.mjs +37 -0
  327. package/workflows/file-census/README.md +14 -0
  328. package/workflows/file-census/scripts/.gitkeep +2 -0
  329. package/workflows/file-census/workflow.mjs +110 -0
  330. package/workflows/goal/README.md +166 -0
  331. package/workflows/goal/prompts/audit.md +57 -0
  332. package/workflows/goal/prompts/contract.md +56 -0
  333. package/workflows/goal/prompts/iterate.md +37 -0
  334. package/workflows/goal/workflow.mjs +501 -0
  335. package/workflows/illustrate/README.md +90 -0
  336. package/workflows/illustrate/assets/engines.json +20 -0
  337. package/workflows/illustrate/assets/extract-baidu.js +21 -0
  338. package/workflows/illustrate/assets/extract-bing.js +33 -0
  339. package/workflows/illustrate/assets/extract-google.js +31 -0
  340. package/workflows/illustrate/workflow.mjs +301 -0
  341. package/workflows/plan/workflow.mjs +389 -0
  342. package/workflows/question-crops/README.md +35 -0
  343. package/workflows/question-crops/prompts/inventory.md +23 -0
  344. package/workflows/question-crops/scripts/image-ops.mjs +305 -0
  345. package/workflows/question-crops/scripts/image-ops.test.mjs +95 -0
  346. package/workflows/question-crops/workflow.mjs +172 -0
  347. package/workflows/ralph-loop/workflow.mjs +162 -0
  348. package/workflows/research/README.md +74 -0
  349. package/workflows/research/prompts/s0-goal.md +43 -0
  350. package/workflows/research/prompts/s1-early-exit.md +22 -0
  351. package/workflows/research/prompts/s1-map.md +58 -0
  352. package/workflows/research/prompts/s2-options.md +53 -0
  353. package/workflows/research/prompts/s3-deep-dive.md +56 -0
  354. package/workflows/research/prompts/s4-verify.md +74 -0
  355. package/workflows/research/prompts/s5-synthesis.md +81 -0
  356. package/workflows/research/prompts/s5b-fill-gaps.md +32 -0
  357. package/workflows/research/prompts/s6-package.md +32 -0
  358. package/workflows/research/scripts/build-report.mjs +98 -0
  359. package/workflows/research/workflow.mjs +288 -0
  360. package/workflows/wechat-reading-shots/prompts/collect-params.md +16 -0
  361. package/workflows/wechat-reading-shots/prompts/evaluate.md +20 -0
  362. package/workflows/wechat-reading-shots/prompts/open-book.md +28 -0
  363. package/workflows/wechat-reading-shots/prompts/read-page.md +13 -0
  364. package/workflows/wechat-reading-shots/prompts/turn-batch.md +24 -0
  365. package/workflows/wechat-reading-shots/test/control-flow.test.ts +329 -0
  366. package/workflows/wechat-reading-shots/workflow.mjs +266 -0
  367. package/workflows/wrong-question-docx/README.md +55 -0
  368. package/workflows/wrong-question-docx/prompts/figure-qa.md +8 -0
  369. package/workflows/wrong-question-docx/prompts/transcribe.md +25 -0
  370. package/workflows/wrong-question-docx/scripts/workbook.mjs +857 -0
  371. package/workflows/wrong-question-docx/test/control-flow.test.ts +165 -0
  372. package/workflows/wrong-question-docx/test/workbook.test.mjs +303 -0
  373. package/workflows/wrong-question-docx/workflow.mjs +485 -0
  374. package/plugins/workflow-runner/src/agentCommon.js +0 -354
  375. package/plugins/workflow-runner/src/agentCommon.ts +0 -435
  376. package/plugins/workflow-runner/src/controlFlow/branch.js +0 -40
  377. package/plugins/workflow-runner/src/controlFlow/branch.ts +0 -46
  378. package/plugins/workflow-runner/src/controlFlow/loop.js +0 -211
  379. package/plugins/workflow-runner/src/controlFlow/loop.ts +0 -248
  380. package/plugins/workflow-runner/src/controlFlow/pause.js +0 -129
  381. package/plugins/workflow-runner/src/controlFlow/pause.ts +0 -141
  382. package/plugins/workflow-runner/src/controlFlow/signal.js +0 -21
  383. package/plugins/workflow-runner/src/controlFlow/signal.ts +0 -29
  384. package/plugins/workflow-runner/src/controlFlow/types.js +0 -18
  385. package/plugins/workflow-runner/src/controlFlow/types.ts +0 -63
  386. package/plugins/workflow-runner/src/dslManifest.js +0 -72
  387. package/plugins/workflow-runner/src/dslManifest.ts +0 -127
  388. package/plugins/workflow-runner/src/expressions.js +0 -639
  389. package/plugins/workflow-runner/src/expressions.ts +0 -640
  390. package/plugins/workflow-runner/src/loader.js +0 -573
  391. package/plugins/workflow-runner/src/loader.ts +0 -721
  392. package/plugins/workflow-runner/src/multimodal.js +0 -38
  393. package/plugins/workflow-runner/src/multimodal.ts +0 -64
  394. package/plugins/workflow-runner/src/recovery.js +0 -126
  395. package/plugins/workflow-runner/src/recovery.ts +0 -156
  396. package/plugins/workflow-runner/src/stepExecutors/agent.js +0 -173
  397. package/plugins/workflow-runner/src/stepExecutors/agent.ts +0 -234
  398. package/plugins/workflow-runner/src/stepExecutors/assert.js +0 -42
  399. package/plugins/workflow-runner/src/stepExecutors/assert.ts +0 -52
  400. package/plugins/workflow-runner/src/stepExecutors/echo.js +0 -31
  401. package/plugins/workflow-runner/src/stepExecutors/echo.ts +0 -38
  402. package/plugins/workflow-runner/src/stepExecutors/llm.js +0 -99
  403. package/plugins/workflow-runner/src/stepExecutors/llm.ts +0 -133
  404. package/plugins/workflow-runner/src/stepExecutors/set.js +0 -49
  405. package/plugins/workflow-runner/src/stepExecutors/set.ts +0 -56
  406. package/plugins/workflow-runner/src/stepExecutors/skill.js +0 -111
  407. package/plugins/workflow-runner/src/stepExecutors/skill.ts +0 -140
  408. package/plugins/workflow-runner/src/stepExecutors/tool.js +0 -227
  409. package/plugins/workflow-runner/src/stepExecutors/tool.ts +0 -274
  410. package/plugins/workflow-runner/src/stepExecutors/verify.js +0 -61
  411. package/plugins/workflow-runner/src/stepExecutors/verify.ts +0 -72
  412. package/plugins/workflow-runner/src/stepPipeline.js +0 -277
  413. package/plugins/workflow-runner/src/stepPipeline.ts +0 -366
  414. package/plugins/workflow-runner/test/agent.test.ts +0 -587
  415. package/plugins/workflow-runner/test/agentLlmError.test.ts +0 -373
  416. package/plugins/workflow-runner/test/bashBlacklist.sweep.test.ts +0 -65
  417. package/plugins/workflow-runner/test/captureMisbind.test.ts +0 -99
  418. package/plugins/workflow-runner/test/cli.e2e.test.ts +0 -167
  419. package/plugins/workflow-runner/test/context-files.test.ts +0 -320
  420. package/plugins/workflow-runner/test/e2e.test.ts +0 -370
  421. package/plugins/workflow-runner/test/e2eNewLayout.test.ts +0 -314
  422. package/plugins/workflow-runner/test/engineRobustness.test.ts +0 -234
  423. package/plugins/workflow-runner/test/expressions.test.ts +0 -439
  424. package/plugins/workflow-runner/test/foundation.test.ts +0 -126
  425. package/plugins/workflow-runner/test/graceful.test.ts +0 -139
  426. package/plugins/workflow-runner/test/greedyInputs.load.test.ts +0 -47
  427. package/plugins/workflow-runner/test/hostCapability.test.ts +0 -108
  428. package/plugins/workflow-runner/test/illustrate.load.test.ts +0 -183
  429. package/plugins/workflow-runner/test/jsonPayload.test.ts +0 -83
  430. package/plugins/workflow-runner/test/loader.test.ts +0 -617
  431. package/plugins/workflow-runner/test/maxTurnsRetryDelay.test.ts +0 -33
  432. package/plugins/workflow-runner/test/multimodal.test.ts +0 -342
  433. package/plugins/workflow-runner/test/output-schema.test.ts +0 -213
  434. package/plugins/workflow-runner/test/parseWorkflowArgs.test.ts +0 -106
  435. package/plugins/workflow-runner/test/pluginRunner.isolation.test.ts +0 -269
  436. package/plugins/workflow-runner/test/ppt.load.test.ts +0 -1220
  437. package/plugins/workflow-runner/test/ralphLoop.e2e.test.ts +0 -221
  438. package/plugins/workflow-runner/test/recovery.test.ts +0 -265
  439. package/plugins/workflow-runner/test/research.load.test.ts +0 -328
  440. package/plugins/workflow-runner/test/runner.test.ts +0 -911
  441. package/plugins/workflow-runner/test/runnerPreflight.test.ts +0 -436
  442. package/plugins/workflow-runner/test/skillTurnDone.test.ts +0 -71
  443. package/plugins/workflow-runner/test/stateLifecycle.test.ts +0 -198
  444. package/plugins/workflow-runner/test/stepPipeline.test.ts +0 -114
  445. package/plugins/workflow-runner/test/taskArbitration.test.ts +0 -354
  446. package/plugins/workflow-runner/test/taskLifecycle.test.ts +0 -324
  447. package/plugins/workflow-runner/test/toolArtifactOverwrite.test.ts +0 -221
  448. package/plugins/workflow-runner/test/toolCapabilityBoundary.test.ts +0 -29
  449. package/plugins/workflow-runner/test/toolRuntimeIntegration.test.ts +0 -168
  450. package/plugins/workflow-runner/test/webppt.load.test.ts +0 -250
  451. package/plugins/workflow-runner/test/word.load.test.ts +0 -208
  452. package/plugins/workflow-runner/test/workflows.smoke.test.ts +0 -76
  453. package/plugins/workflow-runner/test/yamlGuardConditions.test.ts +0 -207
  454. package/src/advisorState.js +0 -22
  455. package/src/cli/configWizard.js +0 -293
  456. package/src/tools/toolProgressBridge.js +0 -48
  457. package/workflows/adaptive-research.yaml +0 -50
  458. package/workflows/book-review-short.yaml +0 -125
  459. package/workflows/e2e-write-greet.yaml +0 -31
  460. package/workflows/illustrate.yaml +0 -227
  461. package/workflows/plan.yaml +0 -470
  462. package/workflows/ppt.yaml +0 -1670
  463. package/workflows/ralph-loop.yaml +0 -201
  464. package/workflows/research.yaml +0 -873
  465. package/workflows/schema.json +0 -176
  466. package/workflows/webppt.yaml +0 -496
  467. package/workflows/wechat-reading-shots.yaml +0 -315
  468. package/workflows/word.yaml +0 -720
  469. package/workflows/youtube-shorts.yaml +0 -202
  470. /package/workflows/{research.md → research/DESIGN.md} +0 -0
@@ -0,0 +1,304 @@
1
+ import { expect, test } from 'bun:test';
2
+ import {
3
+ mkdir,
4
+ mkdtemp,
5
+ readFile,
6
+ readdir,
7
+ unlink,
8
+ writeFile,
9
+ } from 'node:fs/promises';
10
+ import { tmpdir } from 'node:os';
11
+ import { join } from 'node:path';
12
+ import { loadWorkflowDir } from '../src/discovery.js';
13
+ import { coerceInputs } from '../src/inputCoercion.js';
14
+ import { runWorkflowScript } from '../src/runner.js';
15
+ import type { RunContext } from '../src/types.js';
16
+
17
+ async function fixture(parent: string, child: string) {
18
+ const cwd = await mkdtemp(join(tmpdir(), 'wf-child-'));
19
+ async function put(name: string, source: string) {
20
+ const dir = join(cwd, 'workflows', name);
21
+ await mkdir(dir, { recursive: true });
22
+ await writeFile(join(dir, 'workflow.mjs'), source);
23
+ }
24
+ await put('parent', parent);
25
+ await put('child', child);
26
+ let calls = 0;
27
+ let peak = 0;
28
+ let active = 0;
29
+ const ctx = {
30
+ cwd,
31
+ provider: { name: 'stub' },
32
+ history: [{ role: 'system', content: 'sys' }],
33
+ tools: [],
34
+ chat: async function* () {
35
+ calls++;
36
+ active++;
37
+ peak = Math.max(peak, active);
38
+ await new Promise((r) => setTimeout(r, 5));
39
+ active--;
40
+ yield { type: 'text_delta', delta: 'child-value' };
41
+ yield {
42
+ type: 'done',
43
+ stopReason: 'stop',
44
+ promptTokens: 10,
45
+ outputTokens: 2,
46
+ };
47
+ },
48
+ } as unknown as RunContext;
49
+ async function run(name = 'parent') {
50
+ const script = await loadWorkflowDir(join(cwd, 'workflows', name));
51
+ if (!script.ok) throw Error(script.errors.join(';'));
52
+ const events = [];
53
+ for await (const e of runWorkflowScript(script.workflow, {
54
+ ctx,
55
+ inputs: {},
56
+ sessionId: 'test',
57
+ }))
58
+ events.push(e);
59
+ return events;
60
+ }
61
+ async function done() {
62
+ const base = join(cwd, '.minimal-agent-workflow/parent/test');
63
+ return join(base, (await readdir(base)).find((n) => n.endsWith('.done'))!);
64
+ }
65
+ return { cwd, ctx, put, run, done, calls: () => calls, peak: () => peak };
66
+ }
67
+ const parent = `export const meta={description:'parent',dependencies:['child']};
68
+ export default async function run(wf) { const r=await wf.flow.child('child', {}, {label:'child-label'}); wf.output.message(r.result); return r; }`.replace('{description:', '{apiVersion:2,description:');
69
+ const child = `export const meta={apiVersion:2,description:'child'}; export default async function run(wf) { return await wf.model.generate('child', {isolated:true}); }`;
70
+
71
+ test('root and child use strict typed input coercion', () => {
72
+ const defs = [{ name: 'n', type: 'number' as const }];
73
+ expect(coerceInputs({ n: '900' }, defs)).toEqual({ n: 900 });
74
+ expect(coerceInputs({ n: 900 }, defs)).toEqual({ n: 900 });
75
+ for (const n of ['3abc', '', Number.POSITIVE_INFINITY, {}])
76
+ expect(() => coerceInputs({ n }, defs)).toThrow();
77
+ });
78
+
79
+ test('child JSON return persists; only root completes; completed replay; missing return restarts', async () => {
80
+ const h = await fixture(parent, child);
81
+ const events = await h.run();
82
+ expect(events.filter((e) => e.type === 'error')).toEqual([]);
83
+ expect(events.filter((e) => e.type === 'turn_done')).toHaveLength(1);
84
+ expect(h.ctx.resourceLedger?.inputTokens).toBe(10);
85
+ const dir = await h.done();
86
+ expect(
87
+ JSON.parse(await readFile(join(dir, 'return.json'), 'utf8')).result,
88
+ ).toBe('child-value');
89
+ await h.run();
90
+ expect(h.calls()).toBe(1);
91
+ await unlink(join(dir, 'return.json'));
92
+ await h.run();
93
+ expect(h.calls()).toBe(2);
94
+ });
95
+
96
+ test('parent resume reuses completed child and child bundle change invalidates parent', async () => {
97
+ const h = await fixture(
98
+ parent.replace(
99
+ 'wf.output.message(r.result);',
100
+ `if (!(await wf.fs.exists('continue'))) throw new Error('pause'); wf.output.message(r.result);`,
101
+ ),
102
+ child,
103
+ );
104
+ expect((await h.run()).some((e) => e.type === 'error')).toBe(true);
105
+ await writeFile(join(h.cwd, 'continue'), 'go');
106
+ expect((await h.run()).some((e) => e.type === 'error')).toBe(false);
107
+ expect(h.calls()).toBe(1);
108
+ await h.put('child', `${child}\n// changed contract`);
109
+ await h.run();
110
+ expect(h.calls()).toBe(2);
111
+ await h.run('child');
112
+ expect(h.calls()).toBe(3); // standalone identity differs
113
+ });
114
+
115
+ test('caught child failure does not send top-level error, undeclared/managed/cyclic dependencies fail', async () => {
116
+ const h = await fixture(
117
+ parent.replace(
118
+ "const r=await wf.flow.child('child', {}, {label:'child-label'}); wf.output.message(r.result); return r;",
119
+ `try {await wf.flow.child('child');} catch(e) {wf.output.message('caught');} return null;`,
120
+ ),
121
+ `export const meta={apiVersion:2,description:'child'};export default async function run(wf){throw new Error('broken');}`,
122
+ );
123
+ expect((await h.run()).some((e) => e.type === 'error')).toBe(false);
124
+ await h.put(
125
+ 'child',
126
+ `export const meta={apiVersion:2,description:'child',dependencies:['parent']};export default async function run(wf){}`,
127
+ );
128
+ expect(
129
+ (await h.run()).some(
130
+ (e) => e.type === 'error' && e.error.includes('依赖环'),
131
+ ),
132
+ ).toBe(true);
133
+ await h.put(
134
+ 'child',
135
+ `export const meta={apiVersion:2,description:'child',managedRuntime:'goal'};export default async function run(wf){}`,
136
+ );
137
+ expect(
138
+ (await h.run()).some(
139
+ (e) => e.type === 'error' && e.error.includes('managedRuntime'),
140
+ ),
141
+ ).toBe(true);
142
+ await h.put(
143
+ 'parent',
144
+ `export const meta={apiVersion:2,description:'parent'};export default async function run(wf){await wf.flow.child('child');}`,
145
+ );
146
+ const undeclaredEvents = await h.run();
147
+ expect(
148
+ undeclaredEvents.some(
149
+ (e) => e.type === 'error' && e.error.includes('未声明'),
150
+ ),
151
+ ).toBe(true);
152
+ });
153
+
154
+ test('pipeline child instances are isolated, share gate and usage; missing child output invalidates tree', async () => {
155
+ const h = await fixture(
156
+ `export const meta={apiVersion:2,description:'parent',dependencies:['child']}; export default async function run(wf){const batch=await wf.flow.pipeline(Array.from({length:12},(_,i)=>i),{label:'children',concurrency:8,errorMode:'fail-fast',stages:[async(i)=>wf.flow.child('child',{i})]});return batch.results.map(r=>r.value);}`,
157
+ child,
158
+ );
159
+ const events = await h.run();
160
+ expect(events.filter((e) => e.type === 'error')).toEqual([]);
161
+ expect(h.calls()).toBe(12);
162
+ expect(h.peak()).toBeLessThanOrEqual(8);
163
+ expect(h.ctx.resourceLedger?.inputTokens).toBe(120);
164
+ const results = JSON.parse(
165
+ await readFile(join(await h.done(), 'return.json'), 'utf8'),
166
+ );
167
+ expect(new Set(results.map((r: { runId: string }) => r.runId)).size).toBe(12);
168
+ await h.run();
169
+ expect(h.calls()).toBe(12);
170
+ const childRoot = join(await h.done(), 'children/child');
171
+ await unlink(join(childRoot, (await readdir(childRoot))[0], 'return.json'));
172
+ await h.run();
173
+ expect(h.calls()).toBe(24);
174
+ // 36 次真实子工作流实例化(各自 mkdir/journal/progress.md/return.json)在较慢的
175
+ // 文件系统(尤其 Windows)上实测约 5s,超过 bun 默认 5000ms 测试超时——这是真实
176
+ // 磁盘 IO 耗时,不是卡死或回归,给足预算避免偶发超时。
177
+ }, 15000);
178
+
179
+ test('parent cancellation remains interrupted even if child exception is caught', async () => {
180
+ const h = await fixture(parent, child);
181
+ const controller = new AbortController();
182
+ h.ctx.signal = controller.signal;
183
+ h.ctx.chat = async function* () {
184
+ controller.abort();
185
+ yield* [];
186
+ throw Object.assign(new Error('stop'), { name: 'AbortError' });
187
+ };
188
+ const events = await h.run();
189
+ expect(events.some((e) => e.type === 'interrupted')).toBe(true);
190
+ expect(events.some((e) => e.type === 'turn_done')).toBe(false);
191
+ });
192
+
193
+ test('explicit child resources use workflow cwd and do not inherit parent attachments/history', async () => {
194
+ const h = await fixture(
195
+ parent.replace(
196
+ "wf.flow.child('child', {}",
197
+ "wf.flow.child('child', {source:'input.png'}",
198
+ ),
199
+ `export const meta={apiVersion:2,description:'child',inputs:[{name:'source',type:'resource',required:true,accepts:['image/*'],attachmentFallback:true}]}; export default async function run(wf){return {source:wf.input.values.source};}`,
200
+ );
201
+ await writeFile(
202
+ join(h.cwd, 'input.png'),
203
+ Buffer.from(
204
+ 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/x8AAwMCAO+a5lsAAAAASUVORK5CYII=',
205
+ 'base64',
206
+ ),
207
+ );
208
+ h.ctx.history.push({ role: 'user', content: 'private-parent-context' });
209
+ const events = await h.run();
210
+ expect(events.filter((e) => e.type === 'error')).toEqual([]);
211
+ const value = JSON.parse(
212
+ await readFile(join(await h.done(), 'return.json'), 'utf8'),
213
+ );
214
+ expect(value.result.source).toBe(join(h.cwd, 'input.png'));
215
+ await h.put('parent', parent); // No explicit source: must not absorb any parent attachment.
216
+ const missingResourceEvents = await h.run();
217
+ expect(
218
+ missingResourceEvents.some(
219
+ (e) => e.type === 'error' && /source|输入/.test(e.error),
220
+ ),
221
+ ).toBe(true);
222
+ });
223
+
224
+ test('history isolation preserves system instructions and child uses the same tool authorization ceiling', async () => {
225
+ const h = await fixture(parent, child);
226
+ h.ctx.history.push({ role: 'user', content: 'private-parent-context' });
227
+ h.ctx.chat = async function* (options) {
228
+ expect(JSON.stringify(options)).not.toContain('private-parent-context');
229
+ yield { type: 'text_delta', delta: 'ok' };
230
+ yield { type: 'done', stopReason: 'stop' };
231
+ } as RunContext['chat'];
232
+ expect((await h.run()).filter((e) => e.type === 'error')).toEqual([]);
233
+ await h.put(
234
+ 'child',
235
+ `export const meta={apiVersion:2,description:'child'}; export default async function run(wf){await wf.tools.call('NotAuthorized',{});}`,
236
+ );
237
+ const unauthorizedEvents = await h.run();
238
+ expect(unauthorizedEvents.some((e) => e.type === 'error')).toBe(true);
239
+ });
240
+
241
+ test('artifact ownership tree archives once without touching standalone child outputs', async () => {
242
+ const h = await fixture(
243
+ parent,
244
+ `export const meta={apiVersion:2,description:'child'}; export default async function run(wf){const path='artifact-'+wf.runtime.taskId+'.txt';wf.output.artifact(path);await wf.tools.call('Write',{file_path:path,content:'owned'});return {path};}`,
245
+ );
246
+ const toolCallIds: string[] = [];
247
+ h.ctx.executeTool = (async (
248
+ _name: string,
249
+ raw: string,
250
+ _signal: unknown,
251
+ context: { toolCallId: string },
252
+ ) => {
253
+ toolCallIds.push(context.toolCallId);
254
+ const input = JSON.parse(raw);
255
+ await writeFile(join(h.cwd, input.file_path), input.content);
256
+ return { ok: true, content: 'written' };
257
+ }) as unknown as RunContext['executeTool'];
258
+ // Match the host's real Write dispatch shape; the tool is deliberately restricted to this fixture.
259
+ h.ctx.tools = [
260
+ {
261
+ name: 'Write',
262
+ description: 'write fixture',
263
+ parameters: { type: 'object', properties: {} },
264
+ },
265
+ ] as unknown as RunContext['tools'];
266
+ const first = await h.run();
267
+ expect(first.filter((e) => e.type === 'error')).toEqual([]);
268
+ const result = JSON.parse(
269
+ await readFile(join(await h.done(), 'return.json'), 'utf8'),
270
+ );
271
+ expect(result.artifacts).toContain(join(h.cwd, result.result.path));
272
+ await h.run('child');
273
+ const before = (await readdir(h.cwd)).filter((n) =>
274
+ n.startsWith('artifact-'),
275
+ );
276
+ expect(before).toHaveLength(2);
277
+ await unlink(join(h.cwd, result.result.path));
278
+ expect((await h.run()).some((e) => e.type === 'error')).toBe(false);
279
+ expect(
280
+ await readFile(
281
+ join(h.cwd, before.find((n) => n !== result.result.path)!),
282
+ 'utf8',
283
+ ),
284
+ ).toBe('owned');
285
+ expect(new Set(toolCallIds).size).toBe(toolCallIds.length);
286
+ });
287
+
288
+ test('dependency depth is bounded and JSON-only return is enforced', async () => {
289
+ const h = await fixture(parent, child);
290
+ for (let i = 0; i < 8; i++)
291
+ await h.put(
292
+ i === 0 ? 'child' : `level${i}`,
293
+ `export const meta={apiVersion:2,description:'level',dependencies:['level${i + 1}']};export default async function run(wf){return wf.flow.child('level${i + 1}',{});}`,
294
+ );
295
+ await h.put('level8', `export const meta={apiVersion:2,description:'last'};export default async function run(wf){return null;}`);
296
+ expect(
297
+ (await h.run()).some((e) => e.type === 'error' && e.error.includes('8层')),
298
+ ).toBe(true);
299
+ await h.put(
300
+ 'child',
301
+ `export const meta={apiVersion:2,description:'child'};export default async function run(wf){return {bad:Infinity};}`,
302
+ );
303
+ expect((await h.run()).some((e) => e.type === 'error')).toBe(true);
304
+ });
@@ -0,0 +1,234 @@
1
+ /**
2
+ * plugins/workflow-runner/test/discovery.test.ts —— 工作流目录发现
3
+ *
4
+ * 这里守的重点是**坏工作流的可见性**。
5
+ *
6
+ * legacy loader 的做法是「坏 yaml → console.warn 后从列表里消失」,于是用户看到的
7
+ * 现象是「我的工作流不见了」而不是「我的工作流写错了」——两者的排查成本差一个数量级。
8
+ * 本项目在别处已经为同一类静默降级付过学费(插件漏注册悄悄退化成 markdown prompt、
9
+ * 资产目录漏拷导致「未找到说明书」),所以新引擎把失败原因显式带出来。
10
+ *
11
+ * 用 discoverWorkflowsIn(roots) 而不是 mock 掉 getResourceSearchPaths:项目惯例是
12
+ * 依赖注入,`mock.module` 会全局泄漏污染同批其它测试文件。
13
+ */
14
+
15
+ import { afterEach, describe, expect, test } from 'bun:test';
16
+ import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs';
17
+ import { tmpdir } from 'node:os';
18
+ import { join } from 'node:path';
19
+
20
+ import {
21
+ discoverWorkflowsIn,
22
+ findWorkflowByName,
23
+ loadWorkflowDir,
24
+ } from '../src/discovery.js';
25
+
26
+ const created: string[] = [];
27
+
28
+ afterEach(() => {
29
+ for (const dir of created.splice(0)) {
30
+ // Windows 上偶发 EBUSY(杀毒/索引器短暂持有句柄),重试 + 吞掉,
31
+ // 否则会把上一条**已通过**的测试标成失败,误导排查方向。
32
+ try {
33
+ rmSync(dir, { recursive: true, force: true, maxRetries: 3 });
34
+ } catch {
35
+ /* 清理失败不影响断言结论 */
36
+ }
37
+ }
38
+ });
39
+
40
+ /** 建一个临时的 workflows 根,返回该根路径。 */
41
+ function makeRoot(): string {
42
+ const base = mkdtempSync(join(tmpdir(), 'wf-discovery-'));
43
+ created.push(base);
44
+ const root = join(base, 'workflows');
45
+ mkdirSync(root, { recursive: true });
46
+ return root;
47
+ }
48
+
49
+ /** 在根下建一个工作流目录,写入给定脚本内容。 */
50
+ function makeWorkflow(root: string, name: string, source: string): string {
51
+ const dir = join(root, name);
52
+ mkdirSync(dir, { recursive: true });
53
+ writeFileSync(join(dir, 'workflow.mjs'), source, 'utf8');
54
+ return dir;
55
+ }
56
+
57
+ const GOOD = `export const meta = { apiVersion: 2, description: '一个合法工作流' }
58
+ export default async function run(wf) { wf.output.message('hi') }
59
+ `;
60
+
61
+ describe('工作流目录发现', () => {
62
+ test('发现合法工作流,name 取目录名而非任何脚本内字段', async () => {
63
+ const root = makeRoot();
64
+ makeWorkflow(root, 'my-flow', GOOD);
65
+
66
+ const { workflows, failures } = await discoverWorkflowsIn([root]);
67
+
68
+ expect(failures).toEqual([]);
69
+ expect(workflows.map((w) => w.name)).toEqual(['my-flow']);
70
+ expect(workflows[0].meta.description).toBe('一个合法工作流');
71
+ expect(workflows[0].scriptPath).toBe(join(root, 'my-flow', 'workflow.mjs'));
72
+ });
73
+
74
+ test('meta 的 inputs / artifacts 被解析出来', async () => {
75
+ const root = makeRoot();
76
+ makeWorkflow(
77
+ root,
78
+ 'with-meta',
79
+ `export const meta = {
80
+ apiVersion: 2,
81
+ description: 'x',
82
+ inputs: [{ name: 'topic', greedy: true, required: true }],
83
+ artifacts: ['out/a.md', 'out/b.md'],
84
+ }
85
+ export default async function run(wf) { wf.output.message('ok') }
86
+ `,
87
+ );
88
+
89
+ const { workflows } = await discoverWorkflowsIn([root]);
90
+ expect(workflows[0].meta.inputs?.[0]).toMatchObject({
91
+ name: 'topic',
92
+ greedy: true,
93
+ });
94
+ expect(workflows[0].meta.artifacts).toEqual(['out/a.md', 'out/b.md']);
95
+ });
96
+
97
+ test('managedRuntime 只是外层控制器声明,不改变普通工作流脚本形态', async () => {
98
+ const root = makeRoot();
99
+ makeWorkflow(
100
+ root,
101
+ 'managed',
102
+ `export const meta = { apiVersion: 2, description: 'x', managedRuntime: 'goal' }
103
+ export default async function run(wf) { wf.output.message('ok') }
104
+ `,
105
+ );
106
+ const { workflows } = await discoverWorkflowsIn([root]);
107
+ expect(workflows[0]?.meta.managedRuntime).toBe('goal');
108
+ });
109
+
110
+ test('★ 坏脚本进 failures 并带上原因,不是静默消失', async () => {
111
+ const root = makeRoot();
112
+ makeWorkflow(
113
+ root,
114
+ 'broken',
115
+ `export const meta = { apiVersion: 2, description: 'x' }\nexport default async function run() { const t = Date.now() }\n`,
116
+ );
117
+
118
+ const { workflows, failures } = await discoverWorkflowsIn([root]);
119
+
120
+ expect(workflows).toEqual([]);
121
+ expect(failures).toHaveLength(1);
122
+ expect(failures[0].name).toBe('broken');
123
+ expect(failures[0].errors.join('\n')).toContain('Date.now()');
124
+ });
125
+
126
+ test('★ 一个坏工作流不拖累同目录下的好工作流', async () => {
127
+ const root = makeRoot();
128
+ makeWorkflow(
129
+ root,
130
+ 'broken',
131
+ `export const meta = { apiVersion: 2, description: 'x' }\nexport default async function run() { Date.now() }\n`,
132
+ );
133
+ makeWorkflow(root, 'fine', GOOD);
134
+
135
+ const { workflows, failures } = await discoverWorkflowsIn([root]);
136
+
137
+ expect(workflows.map((w) => w.name)).toEqual(['fine']);
138
+ expect(failures.map((f) => f.name)).toEqual(['broken']);
139
+ });
140
+
141
+ test('★ 没有 workflow.mjs 的普通目录既不算工作流也不算失败', async () => {
142
+ // assets/、模板目录、编辑器的临时目录都会出现在这里。把它们报成
143
+ // 「加载失败」等于每次 /workflows 都刷一堆假告警,用户很快就不看告警了。
144
+ const root = makeRoot();
145
+ mkdirSync(join(root, 'just-a-folder'), { recursive: true });
146
+ makeWorkflow(root, 'real', GOOD);
147
+
148
+ const { workflows, failures } = await discoverWorkflowsIn([root]);
149
+
150
+ expect(workflows.map((w) => w.name)).toEqual(['real']);
151
+ expect(failures).toEqual([]);
152
+ });
153
+
154
+ test('目录里的 *.yaml 不被代码引擎认领(legacy 引擎的地盘)', async () => {
155
+ const root = makeRoot();
156
+ writeFileSync(
157
+ join(root, 'old-style.yaml'),
158
+ 'name: old\nsteps: []\n',
159
+ 'utf8',
160
+ );
161
+
162
+ const { workflows, failures } = await discoverWorkflowsIn([root]);
163
+ expect(workflows).toEqual([]);
164
+ expect(failures).toEqual([]);
165
+ });
166
+
167
+ test('搜索根不存在 → 空结果,不抛', async () => {
168
+ const r = await discoverWorkflowsIn([
169
+ join(tmpdir(), 'definitely-not-here-xyz'),
170
+ ]);
171
+ expect(r).toEqual({ workflows: [], failures: [] });
172
+ });
173
+
174
+ describe('多根优先级', () => {
175
+ test('★ 前面的根覆盖后面的同名工作流(cwd 覆盖包内自带)', async () => {
176
+ const high = makeRoot();
177
+ const low = makeRoot();
178
+ makeWorkflow(
179
+ high,
180
+ 'dup',
181
+ `export const meta = { apiVersion: 2, description: '高优先级' }\nexport default async function run(wf){wf.output.message('a')}\n`,
182
+ );
183
+ makeWorkflow(
184
+ low,
185
+ 'dup',
186
+ `export const meta = { apiVersion: 2, description: '低优先级' }\nexport default async function run(wf){wf.output.message('b')}\n`,
187
+ );
188
+
189
+ const { workflows } = await discoverWorkflowsIn([high, low]);
190
+
191
+ expect(workflows).toHaveLength(1);
192
+ expect(workflows[0].meta.description).toBe('高优先级');
193
+ });
194
+
195
+ test('★ 高优先级里写错的工作流**占住**这个名字,不被低优先级同名顶替', async () => {
196
+ // 否则现象是「我改坏了 cwd 里的工作流,跑起来却是包内那个旧版本」——
197
+ // 静默换了实现体,比直接报错难查得多。
198
+ const high = makeRoot();
199
+ const low = makeRoot();
200
+ makeWorkflow(
201
+ high,
202
+ 'dup',
203
+ `export const meta = { apiVersion: 2, description: 'x' }\nexport default async function run(){Date.now()}\n`,
204
+ );
205
+ makeWorkflow(
206
+ low,
207
+ 'dup',
208
+ `export const meta = { apiVersion: 2, description: '包内版本' }\nexport default async function run(wf){wf.output.message('b')}\n`,
209
+ );
210
+
211
+ const { workflows, failures } = await discoverWorkflowsIn([high, low]);
212
+
213
+ expect(workflows).toEqual([]);
214
+ expect(failures.map((f) => f.name)).toEqual(['dup']);
215
+ });
216
+ });
217
+ });
218
+
219
+ describe('loadWorkflowDir / findWorkflowByName', () => {
220
+ test('loadWorkflowDir 对缺失脚本给出可读原因', async () => {
221
+ const root = makeRoot();
222
+ mkdirSync(join(root, 'empty'), { recursive: true });
223
+ const r = await loadWorkflowDir(join(root, 'empty'));
224
+ expect(r.ok).toBe(false);
225
+ if (!r.ok) {
226
+ expect(r.reason).toBe('no-script');
227
+ expect(r.errors[0]).toContain('workflow.mjs');
228
+ }
229
+ });
230
+
231
+ test('findWorkflowByName 找不到时返回 null(可能是 legacy yaml,由调用方决定)', async () => {
232
+ expect(await findWorkflowByName('绝不存在的工作流名-xyz')).toBeNull();
233
+ });
234
+ });
@@ -0,0 +1,106 @@
1
+ /**
2
+ * plugins/workflow-runner/test/goalAnchor.test.ts —— 目标锚定与补充要求
3
+ *
4
+ * 守两条:
5
+ * ① 锚由引擎注入,**对脚本作者透明** —— 作者不写 `${goal}`,每次 agent/llm/skill
6
+ * 调用都自动带上。多步长跑(尤其压缩之后)模型会忘记原始目标而偏航。
7
+ * ② 补充要求与原始目标**同等贯穿**,且在**没有**原始锚时也必须注入 ——
8
+ * 那种场景下它们就是仅有的约束,漏掉等于用户白说。
9
+ */
10
+
11
+ import { describe, expect, test } from 'bun:test';
12
+
13
+ import {
14
+ buildGoalAnchor,
15
+ effectiveGoalAnchor,
16
+ withGoalAnchor,
17
+ } from '../src/goalAnchor.js';
18
+ import type { RunContext } from '../src/types.js';
19
+
20
+ const ctx = (over: Partial<RunContext>): RunContext => over as RunContext;
21
+
22
+ describe('buildGoalAnchor', () => {
23
+ test('优先取名为 goal 的输入', () => {
24
+ const a = buildGoalAnchor({ topic: '别的', goal: '真目标' });
25
+ expect(a).toContain('真目标');
26
+ expect(a).not.toContain('别的');
27
+ });
28
+
29
+ test('无 goal 时取第一个 required 字符串输入', () => {
30
+ const a = buildGoalAnchor({ n: 3, topic: '调研主题' }, [
31
+ { name: 'n' },
32
+ { name: 'topic', required: true },
33
+ ]);
34
+ expect(a).toContain('调研主题');
35
+ });
36
+
37
+ test('没有 required 声明时退到第一个非空字符串', () => {
38
+ expect(buildGoalAnchor({ y: '内容' })).toContain('内容');
39
+ });
40
+
41
+ test('★ 两个字的中文目标必须取到(曾被 >2 的长度阈值跳过)', () => {
42
+ // 那条阈值是照拉丁文本想的(跳过 ab、语言代码),对中文直接失效。
43
+ expect(buildGoalAnchor({ name: '世界' })).toContain('世界');
44
+ });
45
+
46
+ test('★ 取不到真实文本时不拿 JSON 兜底值当锚', () => {
47
+ // extractGoal 最后一档会回退成整体 JSON(为了让 taskId 恒有确定值),
48
+ // 但那种东西绝不能当目标注入给模型 —— 它会朝着一坨 JSON 努力。
49
+ expect(buildGoalAnchor({ pages: 12 })).toBe('');
50
+ });
51
+
52
+ test('★ 一个字符串输入都没有 → 空串(无锚),不硬凑', () => {
53
+ // 硬凑一个假目标比没有目标更糟:模型会朝着那个假目标努力。
54
+ expect(buildGoalAnchor({ pages: 12 })).toBe('');
55
+ expect(buildGoalAnchor({ goal: ' ' })).toBe('');
56
+ });
57
+ });
58
+
59
+ describe('effectiveGoalAnchor', () => {
60
+ test('只有锚 → 原样返回', () => {
61
+ expect(effectiveGoalAnchor(ctx({ goalAnchor: '> 🎯 目标:A' }))).toBe(
62
+ '> 🎯 目标:A',
63
+ );
64
+ });
65
+
66
+ test('锚 + 补充要求 → 补充逐条跟在锚后面', () => {
67
+ const out = effectiveGoalAnchor(
68
+ ctx({
69
+ goalAnchor: '> 🎯 目标:A',
70
+ goalSupplements: ['改成 12 页', '要中文'],
71
+ }),
72
+ );
73
+ expect(out).toContain('目标:A');
74
+ expect(out).toContain('改成 12 页');
75
+ expect(out).toContain('要中文');
76
+ });
77
+
78
+ test('★ 没有原始锚时补充要求仍要注入,并说明其效力等级', () => {
79
+ const out = effectiveGoalAnchor(ctx({ goalSupplements: ['只用中文'] }));
80
+ expect(out).toContain('只用中文');
81
+ expect(out).toContain('同等约束');
82
+ });
83
+
84
+ test('空白补充要求被过滤', () => {
85
+ expect(
86
+ effectiveGoalAnchor(
87
+ ctx({ goalAnchor: 'A', goalSupplements: [' ', ''] }),
88
+ ),
89
+ ).toBe('A');
90
+ });
91
+
92
+ test('全无 → 空串(调用方据此跳过注入)', () => {
93
+ expect(effectiveGoalAnchor(ctx({}))).toBe('');
94
+ });
95
+ });
96
+
97
+ describe('withGoalAnchor', () => {
98
+ test('有锚时前置,中间空一行', () => {
99
+ const out = withGoalAnchor(ctx({ goalAnchor: '锚' }), '干活');
100
+ expect(out).toBe('锚\n\n干活');
101
+ });
102
+
103
+ test('★ 无锚时原样返回 —— 不留多余空行污染 prompt 开头', () => {
104
+ expect(withGoalAnchor(ctx({}), '干活')).toBe('干活');
105
+ });
106
+ });