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
@@ -1,198 +0,0 @@
1
- /**
2
- * ============================================================
3
- * test/stateLifecycle.test.ts —— .minimal-agent-workflow/ 生命周期契约
4
- * ------------------------------------------------------------
5
- * 钉死 Fix ③ 的设计决策(曾经缺失这条测试,导致"失败即删"自废武功无人察觉):
6
- * - 干净跑完 → cleanup(),状态目录被删(不留垃圾)
7
- * - halt / 失败 → **保留** 状态目录 + progress.md(含失败那步的 ✗ 行)供事后排查
8
- *
9
- * 这类"失败时的行为契约"靠想清楚 + 测试钉死,不会被 happy-path 测试自动覆盖到。
10
- * 用 assert step 构造成功/失败两条路径,无需 mock LLM。
11
- * ============================================================
12
- */
13
-
14
- import { afterEach, beforeEach, describe, expect, it, mock } from 'bun:test';
15
- import { existsSync, mkdtempSync, readFileSync, readdirSync, rmSync } from 'node:fs';
16
- import { tmpdir } from 'node:os';
17
- import { join } from 'node:path';
18
-
19
- import { _resetWorkingDir } from '../../../src/bootstrap/workingDir.js';
20
- import { createAgentRuntimeContext } from '../../../src/plugin-sdk.js';
21
- import { ALL_TOOLS } from '../../../src/tools/index.js';
22
- import { runWorkflow } from '../src/runner.js';
23
- import type { LoopEvent, Provider } from '../../../src/types.js';
24
- import type { RunContext, WorkflowDef, WorkflowEvent } from '../src/types.js';
25
-
26
- type WfOrLoop = WorkflowEvent | LoopEvent;
27
-
28
- const fakeProvider: Provider = {
29
- name: 'test',
30
- baseURL: '',
31
- apiKey: '',
32
- model: 'm',
33
- contextWindow: 128000,
34
- };
35
-
36
- async function collect(
37
- gen: AsyncGenerator<WfOrLoop, void, void>,
38
- ): Promise<WfOrLoop[]> {
39
- const out: WfOrLoop[] = [];
40
- for await (const ev of gen) out.push(ev);
41
- return out;
42
- }
43
-
44
- const STATE_DIR = '.minimal-agent-workflow';
45
- // 集成测试统一用显式 sessionId(引擎对未指定 session 自动命名,见 runner 的
46
- // generateDefaultSessionId;测目录生命周期需要可预测的 session 名)
47
- const SESSION = 'test-session';
48
-
49
- function findInstanceDir(base: string, wfName: string, suffix = ''): string | null {
50
- const wfDir = join(base, STATE_DIR, wfName);
51
- if (!existsSync(wfDir)) {
52
- // 旧布局兼容:wfDir 本身就是实例目录(wfName.done 形式)
53
- const legacyDir = suffix
54
- ? join(base, STATE_DIR, wfName + suffix)
55
- : wfDir;
56
- if (existsSync(legacyDir)) return legacyDir;
57
- return null;
58
- }
59
-
60
- // Phase 3:session 化——实例目录名 = sessionId
61
- const sessionDir = suffix
62
- ? join(wfDir, SESSION + suffix)
63
- : join(wfDir, SESSION);
64
- if (existsSync(sessionDir)) return sessionDir;
65
-
66
- // 旧布局兼容:wfDir 本身有 current.json(无子实例目录时判定为 legacy)
67
- if (!suffix && existsSync(join(wfDir, 'current.json'))) {
68
- try {
69
- const entries = readdirSync(wfDir, { withFileTypes: true });
70
- const hasInstanceSubdir = entries.some(
71
- (e) => e.isDirectory() && /^[0-9a-f]{16}$/.test(e.name),
72
- );
73
- if (!hasInstanceSubdir) return wfDir;
74
- } catch {
75
- return wfDir;
76
- }
77
- }
78
- return null;
79
- }
80
-
81
- function hasInstance(base: string, wfName: string, suffix = ''): boolean {
82
- return findInstanceDir(base, wfName, suffix) !== null;
83
- }
84
-
85
- describe('workflow state-dir 生命周期', () => {
86
- let tmp: string;
87
- let ctx: RunContext;
88
-
89
- beforeEach(() => {
90
- tmp = mkdtempSync(join(tmpdir(), 'wf-state-'));
91
- process.env.MINIMAL_AGENT_CWD = tmp;
92
- _resetWorkingDir();
93
- mock.module('../../../src/bootstrap/workingDir.js', () => ({
94
- getWorkingDir: () => tmp,
95
- initWorkingDir: () => tmp,
96
- _resetWorkingDir: () => {},
97
- }));
98
- ctx = {
99
- provider: fakeProvider,
100
- history: [],
101
- cwd: tmp,
102
- tools: ALL_TOOLS,
103
- // 显式 sessionId:目录名可预测,resume/归档断言稳定
104
- runtimeContext: createAgentRuntimeContext({ surface: 'workflow', profile: 'main', cwd: tmp, sessionId: SESSION, owner: 'local-owner', interactiveAsk: false }),
105
- };
106
- });
107
-
108
- afterEach(() => {
109
- delete process.env.MINIMAL_AGENT_CWD;
110
- _resetWorkingDir();
111
- rmSync(tmp, { recursive: true, force: true });
112
- });
113
-
114
- it('干净跑完 → 活动状态目录归档为 <name>.done(E6 后按名隔离)', async () => {
115
- const def: WorkflowDef = {
116
- name: 'ok',
117
- description: 'd',
118
- steps: [{ id: 's1', type: 'assert', condition: '1 == 1' }],
119
- };
120
- const events = await collect(runWorkflow(def, {}, ctx));
121
- expect(events.some((e) => e.type === 'workflow_done')).toBe(true);
122
- // 成功路径:活动目录被归档改名(completed 检测靠它),不留活动现场
123
- expect(hasInstance(tmp, 'ok')).toBe(false);
124
- expect(hasInstance(tmp, 'ok', '.done')).toBe(true);
125
- });
126
-
127
- it('halt → 保留状态目录 + progress.md 含失败那步', async () => {
128
- const def: WorkflowDef = {
129
- name: 'boom',
130
- description: 'd',
131
- steps: [
132
- { id: 'will_fail', type: 'assert', condition: '1 == 2', onFail: 'nope' },
133
- ],
134
- };
135
- const events = await collect(runWorkflow(def, {}, ctx));
136
- // 确实 halt 了(有 error 事件、没有 workflow_done)
137
- expect(events.some((e) => e.type === 'error')).toBe(true);
138
- expect(events.some((e) => e.type === 'workflow_done')).toBe(false);
139
-
140
- // 失败现场必须保留,供事后排查
141
- const stateDir = findInstanceDir(tmp, 'boom');
142
- expect(stateDir).not.toBeNull();
143
- expect(stateDir).toBeDefined();
144
- const progress = readFileSync(join(stateDir as string, 'progress.md'), 'utf8');
145
- // 失败那步的 ✗ 行 + onFail 文案都应在 progress 里
146
- expect(progress).toContain('will_fail');
147
- expect(progress).toContain('nope');
148
- });
149
-
150
- it('再次运行同名 workflow(同 goal)→ resume 后成功 → 归档不堆积', async () => {
151
- const failDef: WorkflowDef = {
152
- name: 'rerun',
153
- description: 'd',
154
- steps: [{ id: 'f', type: 'assert', condition: 'false', onFail: 'x' }],
155
- };
156
- await collect(runWorkflow(failDef, {}, ctx));
157
- expect(hasInstance(tmp, 'rerun')).toBe(true); // 失败留下了
158
-
159
- // 第二次成功跑(同名同 inputs = 同任务 resume):成功后归档 → 活动目录干净
160
- const okDef: WorkflowDef = {
161
- name: 'rerun',
162
- description: 'd',
163
- steps: [{ id: 'ok', type: 'assert', condition: 'true' }],
164
- };
165
- await collect(runWorkflow(okDef, {}, ctx));
166
- expect(hasInstance(tmp, 'rerun')).toBe(false); // 不堆积
167
- expect(hasInstance(tmp, 'rerun', '.done')).toBe(true);
168
- });
169
-
170
- it('E6:A 工作流失败现场不被不同名工作流 B 的运行破坏(按名隔离)', async () => {
171
- // A 失败留现场 + 留声明产物
172
- const aDef: WorkflowDef = {
173
- name: 'wf-a',
174
- description: 'd',
175
- artifacts: ['A-PLAN.md'],
176
- steps: [
177
- { id: 'w', tool: 'Write', args: { file_path: 'A-PLAN.md', content: 'A 的半成品' } },
178
- { id: 'f', type: 'assert', condition: 'false', onFail: 'A 中断' },
179
- ],
180
- };
181
- await collect(runWorkflow(aDef, {}, ctx));
182
- expect(hasInstance(tmp, 'wf-a')).toBe(true);
183
- expect(existsSync(join(tmp, 'A-PLAN.md'))).toBe(true);
184
-
185
- // 跑不同名工作流 B(曾经会清掉 A 的状态目录和产物——E6 事故)
186
- const bDef: WorkflowDef = {
187
- name: 'wf-b',
188
- description: 'd',
189
- steps: [{ id: 'ok', type: 'assert', condition: 'true' }],
190
- };
191
- const evB = await collect(runWorkflow(bDef, {}, ctx));
192
- expect(evB.some((e) => e.type === 'workflow_done')).toBe(true);
193
-
194
- // A 的现场与产物完好,resume 能力保留
195
- expect(hasInstance(tmp, 'wf-a')).toBe(true);
196
- expect(existsSync(join(tmp, 'A-PLAN.md'))).toBe(true);
197
- });
198
- });
@@ -1,114 +0,0 @@
1
- /**
2
- * ============================================================
3
- * stepPipeline —— 「提交一步」的单一实现 + 顺带修正的静默失效
4
- * ------------------------------------------------------------
5
- * 重构前这套动作(绑 capture → 写 progress → 落 vars → 记 checkpoint → 发 step_end)
6
- * 被 `runSteps` 的各内联块各抄一份:recordCheckpoint ×7、appendProgress ×17、
7
- * step_end ×19。抄多份的直接后果是漂移 —— 其中 `echo` / `set` 干脆**从不调
8
- * `bindCapture`**:yaml 写了 `capture:` 既不报错也不生效,resume 也回放不出来。
9
- *
10
- * 搬进执行器体系后它们走统一流水线,capture 自然生效。这里钉住这个修正,
11
- * 以及「不许再抄第二份流水线」这条结构约束。
12
- * ============================================================
13
- */
14
-
15
- import { describe, expect, mock, test } from 'bun:test';
16
- import { mkdtempSync, readFileSync } from 'node:fs';
17
- import { tmpdir } from 'node:os';
18
- import { join } from 'node:path';
19
- import { runWorkflow } from '../src/runner.js';
20
- import type { Provider } from '../../../src/types.js';
21
- import type { RunContext, WorkflowDef, WorkflowEvent } from '../src/types.js';
22
-
23
- mock.module('../../../src/llm/client.js', () => ({
24
- chat: async function* () {
25
- yield { type: 'text_delta', delta: 'ok' };
26
- yield { type: 'done', stopReason: 'stop' };
27
- },
28
- }));
29
-
30
- const provider: Provider = {
31
- name: 't', baseURL: '', apiKey: '', model: 'm', contextWindow: 128000,
32
- };
33
-
34
- /**
35
- * ⚠️ **工作流名必须每次运行唯一。**
36
- *
37
- * 工作流状态目录锚的是 `getWorkingDir()`(进程级),**不是** `RunContext.cwd` ——
38
- * 见 `runner.ts` 里 `new WorkflowState(getWorkingDir(), def.name)`。
39
- * 所以给 ctx 传一个 mkdtemp 目录**隔离不了状态**:同名工作流第二次跑会命中
40
- * 「同名任务此前已完成」而**整条短路**,`workflow_done` 照发、步骤一个不跑 ——
41
- * 于是断言全部空过,测试在两种实现下都绿,什么都证明不了。
42
- *
43
- * 实测踩到:本文件的两条 capture 断言曾在「注入旧行为」时依然全绿,
44
- * 查下去才发现根本没执行到被测代码。用唯一名根治。
45
- */
46
- function uniqueName(base: string): string {
47
- return `${base}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
48
- }
49
-
50
- async function run(def: WorkflowDef): Promise<WorkflowEvent[]> {
51
- const ctx = {
52
- provider,
53
- history: [{ role: 'system', content: 'sys' }],
54
- cwd: mkdtempSync(join(tmpdir(), 'wf-pipeline-')),
55
- } as unknown as RunContext;
56
- const out: WorkflowEvent[] = [];
57
- for await (const e of runWorkflow(def, {}, ctx)) out.push(e as WorkflowEvent);
58
- return out;
59
- }
60
-
61
- describe('★ 修正:echo / set 的 capture 从静默失效变为生效', () => {
62
- test('echo 带 capture → 变量真的被绑上,下游 assert 能用', async () => {
63
- const events = await run({
64
- name: uniqueName('echo-capture'),
65
- steps: [
66
- { id: 'say', type: 'echo', message: 'HELLO', capture: { text: 'said' } },
67
- { id: 'check', type: 'assert', condition: "said == 'HELLO'" },
68
- ],
69
- } as never);
70
- // 重构前:echo 从不调 bindCapture → said 为 undefined → assert 判假 → halted
71
- expect(events.some((e) => e.type === 'workflow_done')).toBe(true);
72
- });
73
-
74
- test('set 带 capture → 别名变量也被绑上', async () => {
75
- const events = await run({
76
- name: uniqueName('set-capture'),
77
- steps: [
78
- { id: 'init', type: 'set', set: { phase: 'PLAN' }, capture: { phase: 'alias' } },
79
- { id: 'check', type: 'assert', condition: "alias == 'PLAN'" },
80
- ],
81
- } as never);
82
- expect(events.some((e) => e.type === 'workflow_done')).toBe(true);
83
- });
84
- });
85
-
86
- describe('★ 结构约束:流水线只许有一份实现', () => {
87
- const runnerSrc = readFileSync(
88
- join(import.meta.dir, '..', 'src', 'runner.ts'),
89
- 'utf8',
90
- );
91
-
92
- test('runner.ts 不再自己记 checkpoint / 发 step_end(都走 stepPipeline)', () => {
93
- // 重构前:recordCheckpoint ×7、appendProgress ×17、step_end ×19
94
- expect((runnerSrc.match(/state\.recordCheckpoint\(/g) ?? []).length).toBe(0);
95
- expect((runnerSrc.match(/type: 'workflow_step_end'/g) ?? []).length).toBeLessThanOrEqual(2);
96
- });
97
-
98
- test('runSteps 保持精简(控制流已搬进 controlFlow/)', () => {
99
- const lines = runnerSrc.split('\n');
100
- const s = lines.findIndex((l) => l.startsWith('async function* runSteps('));
101
- const e = lines.findIndex((l, i) => i > s && l === '}');
102
- expect(e - s + 1).toBeLessThanOrEqual(200); // 重构前 780
103
- });
104
-
105
- test('★ controlFlow/ 不反向 import runner —— 靠注入的 runBlock', () => {
106
- for (const f of ['loop', 'branch', 'pause', 'signal', 'types']) {
107
- const src = readFileSync(
108
- join(import.meta.dir, '..', 'src', 'controlFlow', `${f}.ts`),
109
- 'utf8',
110
- );
111
- expect(src).not.toContain("from '../runner.js'");
112
- }
113
- });
114
- });
@@ -1,354 +0,0 @@
1
- /**
2
- * ============================================================
3
- * plugins/workflow-runner/test/taskArbitration.test.ts
4
- * ------------------------------------------------------------
5
- * 任务身份语义仲裁层(hash 不等 + 旧任务未完成时的二审)契约:
6
- *
7
- * hash 仍是同任务的权威信号(相等 → resume/completed)。hash 不等且旧任务
8
- * 未完成时不再一票否决 new-task,仲裁链逐级降级:
9
- * a. 延续短语快路径(继续/接着/continue…)→ resume,不花 LLM
10
- * b. LLM 单轮仲裁(JSON verdict)→ resume / new 决断
11
- * c. unsure / LLM 不可用 → AskUserQuestion(非交互自动选推荐项=全新开始)
12
- * d. 提问也失败 → 兜底 new-task(= 旧红线行为)
13
- *
14
- * 仲裁判定 resume 时:旧任务状态/产物保留、从断点续跑,goalAnchor 锚旧目标
15
- * + 新输入作补充指令(不能让每步 LLM 把「继续」当成目标)。
16
- *
17
- * 约定:DI 注入(chat / executeTool stub),禁 mock.module(client/loop/plugin-sdk)。
18
- * ============================================================
19
- */
20
-
21
- import { afterEach, beforeEach, describe, expect, it, mock } from 'bun:test';
22
- import { existsSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs';
23
- import { tmpdir } from 'node:os';
24
- import { join } from 'node:path';
25
-
26
- import { _resetWorkingDir } from '../../../src/bootstrap/workingDir.js';
27
- import { createAgentRuntimeContext } from '../../../src/plugin-sdk.js';
28
- import type { LoopEvent, Provider } from '../../../src/types.js';
29
- import {
30
- buildTaskArbitrator,
31
- isContinuationPhrase,
32
- type TaskArbitrationContext,
33
- } from '../src/taskIdentity.js';
34
- import { WorkflowState } from '../src/workflowState.js';
35
- import { runWorkflow } from '../src/runner.js';
36
- import type { RunContext, StepDef, WorkflowDef, WorkflowEvent } from '../src/types.js';
37
-
38
- type WfOrLoop = WorkflowEvent | LoopEvent;
39
-
40
- const fakeProvider: Provider = {
41
- name: 'test',
42
- baseURL: '',
43
- apiKey: '',
44
- model: 'm',
45
- contextWindow: 128000,
46
- };
47
-
48
- async function collect(
49
- gen: AsyncGenerator<WfOrLoop, void, void>,
50
- ): Promise<WfOrLoop[]> {
51
- const out: WfOrLoop[] = [];
52
- for await (const ev of gen) out.push(ev);
53
- return out;
54
- }
55
-
56
- const ACTX: TaskArbitrationContext = {
57
- workflowName: 'plan',
58
- prevGoal: '写一份产品架构说明 PPT',
59
- newGoal: '继续',
60
- completedStepIds: ['gathering', 'research'],
61
- totalSteps: 5,
62
- artifacts: ['PLAN.md'],
63
- };
64
-
65
- // ---------------- 1. 单元:短语快路径 ----------------
66
-
67
- describe('taskIdentity::isContinuationPhrase(延续短语快路径)', () => {
68
- it('中英文延续短语命中', () => {
69
- for (const g of ['继续', '继续吧', '接着做', '继续执行', 'continue', 'resume', 'go on', '继续!']) {
70
- expect(isContinuationPhrase(g)).toBe(true);
71
- }
72
- });
73
-
74
- it('实质性新目标不命中', () => {
75
- for (const g of ['写一份产品架构说明 PPT', '帮我重构 runner.ts', '继续深入研究量子计算的产业落地并写一份长篇报告']) {
76
- expect(isContinuationPhrase(g)).toBe(false);
77
- }
78
- });
79
- });
80
-
81
- // ---------------- 2. 单元:复合仲裁器逐级降级 ----------------
82
-
83
- describe('taskIdentity::buildTaskArbitrator(仲裁链逐级降级)', () => {
84
- it('a. 延续短语 → resume,不调 LLM 也不提问', async () => {
85
- let chatCalled = 0;
86
- let askCalled = 0;
87
- const notes: string[] = [];
88
- const arb = buildTaskArbitrator({
89
- provider: fakeProvider,
90
- chat: async function* () {
91
- chatCalled++;
92
- yield { type: 'done', stopReason: 'stop' };
93
- },
94
- executeTool: async () => {
95
- askCalled++;
96
- return { ok: true, content: '' };
97
- },
98
- notes,
99
- });
100
-
101
- expect(await arb(ACTX)).toBe('resume');
102
- expect(chatCalled).toBe(0);
103
- expect(askCalled).toBe(0);
104
- expect(notes.some((n) => n.includes('延续短语'))).toBe(true);
105
- });
106
-
107
- it('b. LLM 判 resume → resume(决断,不提问)', async () => {
108
- let askCalled = 0;
109
- const arb = buildTaskArbitrator({
110
- provider: fakeProvider,
111
- chat: async function* () {
112
- yield { type: 'text_delta', delta: '{"verdict":"resume","reason":"是延续"}' };
113
- yield { type: 'done', stopReason: 'stop' };
114
- },
115
- executeTool: async () => {
116
- askCalled++;
117
- return { ok: true, content: '' };
118
- },
119
- });
120
-
121
- expect(await arb({ ...ACTX, newGoal: '把刚才那个 PPT 做完' })).toBe('resume');
122
- expect(askCalled).toBe(0);
123
- });
124
-
125
- it('b. LLM 判 new → new(决断)', async () => {
126
- const arb = buildTaskArbitrator({
127
- provider: fakeProvider,
128
- chat: async function* () {
129
- yield { type: 'text_delta', delta: '{"verdict":"new","reason":"无关新需求"}' };
130
- yield { type: 'done', stopReason: 'stop' };
131
- },
132
- });
133
-
134
- expect(await arb({ ...ACTX, newGoal: '帮我修一个登录 bug' })).toBe('new');
135
- });
136
-
137
- it('c. LLM unsure → AskUserQuestion,按用户选择走(选"继续上次任务"→ resume)', async () => {
138
- const arb = buildTaskArbitrator({
139
- provider: fakeProvider,
140
- chat: async function* () {
141
- yield { type: 'text_delta', delta: '{"verdict":"unsure","reason":"说不准"}' };
142
- yield { type: 'done', stopReason: 'stop' };
143
- },
144
- executeTool: async (name: string) => {
145
- expect(name).toBe('AskUserQuestion');
146
- return { ok: true, content: '用户已回答 AskUserQuestion:\nA1: 继续上次任务' };
147
- },
148
- });
149
-
150
- expect(await arb({ ...ACTX, newGoal: '模糊输入' })).toBe('resume');
151
- });
152
-
153
- it('d. LLM 抛错 + 提问也失败 → 兜底 new(= 旧红线行为)', async () => {
154
- const notes: string[] = [];
155
- const arb = buildTaskArbitrator({
156
- provider: fakeProvider,
157
- chat: async function* () {
158
- throw new Error('LLM down');
159
- // biome-ignore lint/correctness/noUnreachable: stub
160
- yield { type: 'done', stopReason: 'stop' };
161
- },
162
- executeTool: async () => ({ ok: false, error: '提问被取消' }),
163
- notes,
164
- });
165
-
166
- expect(await arb({ ...ACTX, newGoal: '模糊输入' })).toBe('new');
167
- expect(notes.some((n) => n.includes('全新任务'))).toBe(true);
168
- });
169
- });
170
-
171
- // ---------------- 3. 状态层:init 接仲裁结果 ----------------
172
-
173
- const STEPS: StepDef[] = [
174
- { id: 's1', type: 'assert', condition: 'true' },
175
- { id: 's2', type: 'assert', condition: 'true' },
176
- ];
177
-
178
- describe('workflowState::init × 仲裁(resume 保留现场 / new 清理重开)', () => {
179
- let tmp: string;
180
-
181
- beforeEach(() => {
182
- tmp = mkdtempSync(join(tmpdir(), 'wf-arb-'));
183
- });
184
- afterEach(() => {
185
- rmSync(tmp, { recursive: true, force: true });
186
- });
187
-
188
- it('仲裁判 resume → 保留旧状态与产物、回放 checkpoint、带 prevGoal/supplementaryGoal', async () => {
189
- // 旧任务:fresh + 完成一步 + 留产物 + 留补充要求(不 markDone = 未完成)
190
- const s1 = new WorkflowState(tmp, 'plan');
191
- await s1.init('plan', { goal: '写产品架构 PPT' }, STEPS, 1, ['PLAN.md']);
192
- await s1.recordCheckpoint('s1', 'ok', { out: 'v1' }, {});
193
- await s1.appendSupplement('用户要求加一章');
194
- writeFileSync(join(tmp, 'PLAN.md'), '# 半成品');
195
- const oldDir = s1.dir;
196
-
197
- // 新输入「继续」(hash 必然不等)+ 仲裁器判 resume
198
- const s2 = new WorkflowState(tmp, 'plan');
199
- const r = await s2.init('plan', { goal: '继续' }, STEPS, 1, ['PLAN.md'], async () => 'resume');
200
-
201
- expect(r.mode).toBe('resume');
202
- expect(r.completedStepIds).toEqual(['s1']);
203
- expect(r.capturedVars?.s1).toEqual({ out: 'v1' });
204
- expect(r.prevGoal).toBe('写产品架构 PPT');
205
- expect(r.supplementaryGoal).toBe('继续');
206
- // 现场未被清理
207
- expect(existsSync(join(tmp, 'PLAN.md'))).toBe(true);
208
- // 回归:resume 后实例目录必须仍指向旧任务(否则 supplements / checkpoint 全写丢,
209
- // 断点续跑静默失效——曾因 finally 误把 this.dir 改回新 hash 目录)
210
- expect(s2.dir).toBe(oldDir);
211
- expect(await s2.readSupplements()).toEqual(['用户要求加一章']);
212
- });
213
-
214
- it('仲裁判 new → 走 new-task 清理重开', async () => {
215
- const s1 = new WorkflowState(tmp, 'plan');
216
- await s1.init('plan', { goal: '写产品架构 PPT' }, STEPS, 1, ['PLAN.md']);
217
- writeFileSync(join(tmp, 'PLAN.md'), '# 旧产物');
218
-
219
- const s2 = new WorkflowState(tmp, 'plan');
220
- const r = await s2.init('plan', { goal: '修登录 bug' }, STEPS, 1, ['PLAN.md'], async () => 'new');
221
-
222
- expect(r.mode).toBe('new-task');
223
- expect(existsSync(join(tmp, 'PLAN.md'))).toBe(false);
224
- });
225
-
226
- it('仲裁器抛错 → 兜底 new-task(不让仲裁 halt 工作流)', async () => {
227
- const s1 = new WorkflowState(tmp, 'plan');
228
- await s1.init('plan', { goal: '写产品架构 PPT' }, STEPS, 1, []);
229
-
230
- const s2 = new WorkflowState(tmp, 'plan');
231
- const r = await s2.init('plan', { goal: '继续' }, STEPS, 1, [], async () => {
232
- throw new Error('仲裁崩了');
233
- });
234
- expect(r.mode).toBe('new-task');
235
- });
236
-
237
- it('旧任务已完成(status=done)→ 不仲裁,直接 new-task', async () => {
238
- const s1 = new WorkflowState(tmp, 'plan');
239
- await s1.init('plan', { goal: '写产品架构 PPT' }, STEPS, 1, []);
240
- await s1.markDone(); // 活动目录 done(未归档的边角情形)
241
-
242
- let arbCalled = 0;
243
- const s2 = new WorkflowState(tmp, 'plan');
244
- const r = await s2.init('plan', { goal: '继续' }, STEPS, 1, [], async () => {
245
- arbCalled++;
246
- return 'resume';
247
- });
248
- expect(r.mode).toBe('new-task');
249
- expect(arbCalled).toBe(0);
250
- });
251
-
252
- it('不同名 workflow 状态按名隔离(E6)→ B 判 fresh、不仲裁、不碰 A 的现场', async () => {
253
- // A(other-wf)未完成现场
254
- const sA = new WorkflowState(tmp, 'other-wf');
255
- await sA.init('other-wf', { goal: '别的事' }, STEPS, 1, []);
256
-
257
- // 跑 B(plan):A 的状态对 B 不可见 → fresh(不会误判 resume/new-task,也不仲裁)
258
- let arbCalled = 0;
259
- const sB = new WorkflowState(tmp, 'plan');
260
- const r = await sB.init('plan', { goal: '继续' }, STEPS, 1, [], async () => {
261
- arbCalled++;
262
- return 'resume';
263
- });
264
- expect(r.mode).toBe('fresh');
265
- expect(arbCalled).toBe(0);
266
- // A 的现场完好(曾经会被 B 的 new-task 清掉——E6 事故)
267
- // 新布局:current.json 在实例子目录里;旧布局兼容时直接在 wfDir 下
268
- const aDir = sA.dir;
269
- expect(existsSync(join(aDir, 'current.json'))).toBe(true);
270
- });
271
- });
272
-
273
- // ---------------- 4. 端到端:runWorkflow「继续」续跑 + goalAnchor 锚旧目标 ----------------
274
-
275
- describe('runWorkflow × 仲裁延续(端到端)', () => {
276
- let tmp: string;
277
- let ctx: RunContext;
278
-
279
- beforeEach(() => {
280
- tmp = mkdtempSync(join(tmpdir(), 'wf-arb-e2e-'));
281
- process.env.MINIMAL_AGENT_CWD = tmp;
282
- _resetWorkingDir();
283
- mock.module('../../../src/bootstrap/workingDir.js', () => ({
284
- getWorkingDir: () => tmp,
285
- initWorkingDir: () => tmp,
286
- _resetWorkingDir: () => {},
287
- }));
288
- ctx = {
289
- provider: fakeProvider,
290
- history: [],
291
- cwd: tmp,
292
- tools: [],
293
- // 显式 sessionId:两次 runWorkflow 同 session,仲裁「继续」才能 resume 旧任务
294
- runtimeContext: createAgentRuntimeContext({ surface: 'workflow', profile: 'main', cwd: tmp, sessionId: 'test-session', owner: 'local-owner', interactiveAsk: false }),
295
- };
296
- });
297
- afterEach(() => {
298
- delete process.env.MINIMAL_AGENT_CWD;
299
- _resetWorkingDir();
300
- rmSync(tmp, { recursive: true, force: true });
301
- });
302
-
303
- it('输入「继续」→ 短语快路径 resume;goalAnchor 锚旧目标而非「继续」', async () => {
304
- const def: WorkflowDef = {
305
- name: 'cont-wf',
306
- description: 'd',
307
- inputs: [{ name: 'goal', type: 'string', required: true }],
308
- steps: [
309
- // prompt 正文内插 ${inputs.goal}(模拟 plan.yaml 的业务上下文写法):
310
- // 仲裁 resume 时必须渲染旧任务真实目标,不能是「继续」
311
- { id: 'g1', llm: '目标:${inputs.goal},做第一步', capture: { text: 'a' } },
312
- { id: 'g2', llm: '目标:${inputs.goal},做第二步', capture: { text: 'b' } },
313
- ],
314
- };
315
-
316
- // 第一次:真实目标,g1 成功后 g2 失败(中断现场)
317
- let calls = 0;
318
- ctx.chat = async function* () {
319
- calls++;
320
- if (calls === 2) throw new Error('boom'); // g2 失败 → halt,保留现场
321
- yield { type: 'text_delta', delta: `out-${calls}` };
322
- yield { type: 'done', stopReason: 'stop' };
323
- };
324
- const ev1 = await collect(runWorkflow(def, { goal: '写产品架构说明文档' }, ctx));
325
- expect(ev1.some((e) => e.type === 'workflow_done')).toBe(false);
326
-
327
- // 第二次:输入「继续」→ 仲裁快路径 resume(不该花 LLM 做仲裁)。
328
- // 记录每次 chat 收到的 user prompt,验证 goalAnchor 锚的是旧目标。
329
- const prompts: string[] = [];
330
- ctx.history = [];
331
- ctx.chat = async function* (args) {
332
- const user = [...args.messages].reverse().find((m) => m.role === 'user');
333
- prompts.push(typeof user?.content === 'string' ? user.content : '');
334
- yield { type: 'text_delta', delta: 'resumed-out' };
335
- yield { type: 'done', stopReason: 'stop' };
336
- };
337
- const ev2 = await collect(runWorkflow(def, { goal: '继续' }, ctx));
338
-
339
- expect(ev2.some((e) => e.type === 'workflow_done')).toBe(true);
340
- // 仲裁可见化:延续短语 warning
341
- expect(
342
- ev2.some((e) => e.type === 'warning' && e.message.includes('延续短语')),
343
- ).toBe(true);
344
- // goalAnchor 锚旧目标 + 补充指令,而不是把「继续」当目标
345
- expect(prompts.length).toBeGreaterThan(0);
346
- for (const p of prompts) {
347
- expect(p).toContain('写产品架构说明文档');
348
- expect(p).not.toContain('用户原始目标:继续');
349
- // prompt 正文的 ${inputs.goal} 内插也必须是旧目标(VarStack inputs 已替换)
350
- expect(p).toContain('目标:写产品架构说明文档');
351
- expect(p).not.toContain('目标:继续');
352
- }
353
- });
354
- });