minimal-agent 0.10.0 → 0.12.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 (573) hide show
  1. package/README.md +11 -3
  2. package/package.json +52 -5
  3. package/plugins/computer_use/.claude-plugin/plugin.json +6 -0
  4. package/plugins/computer_use/README.md +94 -0
  5. package/plugins/computer_use/app/_template.md +142 -0
  6. package/plugins/computer_use/app/chrome.md +165 -0
  7. package/plugins/computer_use/app/finder.md +76 -0
  8. package/plugins/computer_use/app/macos-system-settings.md +81 -0
  9. package/plugins/computer_use/app/microsoft-excel.md +80 -0
  10. package/plugins/computer_use/app/microsoft-powerpoint.md +80 -0
  11. package/plugins/computer_use/app/microsoft-word.md +79 -0
  12. package/plugins/computer_use/app/wechat.md +413 -0
  13. package/plugins/computer_use/app/weread.md +319 -0
  14. package/plugins/computer_use/commands/computeruse.md +25 -0
  15. package/plugins/computer_use/commands/cu.md +9 -0
  16. package/plugins/computer_use/plugin.js +26 -0
  17. package/plugins/computer_use/plugin.ts +30 -0
  18. package/plugins/computer_use/src/capture.js +90 -0
  19. package/plugins/computer_use/src/capture.ts +104 -0
  20. package/plugins/computer_use/src/commands.js +91 -0
  21. package/plugins/computer_use/src/commands.ts +103 -0
  22. package/plugins/computer_use/src/daemon.js +134 -0
  23. package/plugins/computer_use/src/daemon.ts +161 -0
  24. package/plugins/computer_use/src/manuals.js +135 -0
  25. package/plugins/computer_use/src/manuals.ts +159 -0
  26. package/plugins/computer_use/src/verify.js +168 -0
  27. package/plugins/computer_use/src/verify.ts +219 -0
  28. package/plugins/computer_use/test/commands.test.ts +73 -0
  29. package/plugins/cron-runner/plugin.ts +38 -0
  30. package/plugins/cron-runner/src/commands.ts +373 -0
  31. package/plugins/cron-runner/src/cronTime.ts +155 -0
  32. package/plugins/cron-runner/src/lock.ts +79 -0
  33. package/plugins/cron-runner/src/runs.ts +99 -0
  34. package/plugins/cron-runner/src/scheduleEngine.ts +76 -0
  35. package/plugins/cron-runner/src/scheduler.ts +376 -0
  36. package/plugins/cron-runner/src/schema.ts +139 -0
  37. package/plugins/cron-runner/src/service.ts +193 -0
  38. package/plugins/cron-runner/src/store.ts +333 -0
  39. package/plugins/cron-runner/src/types.ts +141 -0
  40. package/plugins/cron-runner/test/cronTime.test.ts +41 -0
  41. package/plugins/cron-runner/test/plugin-integration.test.ts +47 -0
  42. package/plugins/cron-runner/test/scheduleEngine.test.ts +41 -0
  43. package/plugins/cron-runner/test/service.test.ts +76 -0
  44. package/plugins/cron-runner/test/store-and-scheduler.test.ts +501 -0
  45. package/plugins/workflow-runner/plugin.js +56 -2
  46. package/plugins/workflow-runner/plugin.ts +113 -0
  47. package/plugins/workflow-runner/src/agentCommon.js +43 -4
  48. package/plugins/workflow-runner/src/agentCommon.ts +435 -0
  49. package/plugins/workflow-runner/src/controlFlow/branch.js +40 -0
  50. package/plugins/workflow-runner/src/controlFlow/branch.ts +46 -0
  51. package/plugins/workflow-runner/src/controlFlow/loop.js +211 -0
  52. package/plugins/workflow-runner/src/controlFlow/loop.ts +248 -0
  53. package/plugins/workflow-runner/src/controlFlow/pause.js +129 -0
  54. package/plugins/workflow-runner/src/controlFlow/pause.ts +141 -0
  55. package/plugins/workflow-runner/src/controlFlow/signal.js +21 -0
  56. package/plugins/workflow-runner/src/controlFlow/signal.ts +29 -0
  57. package/plugins/workflow-runner/src/controlFlow/types.js +18 -0
  58. package/plugins/workflow-runner/src/controlFlow/types.ts +63 -0
  59. package/plugins/workflow-runner/src/dslManifest.ts +127 -0
  60. package/plugins/workflow-runner/src/expressions.ts +622 -0
  61. package/plugins/workflow-runner/src/index.js +13 -4
  62. package/plugins/workflow-runner/src/index.ts +338 -0
  63. package/plugins/workflow-runner/src/jsonPayload.ts +84 -0
  64. package/plugins/workflow-runner/src/loader.js +23 -0
  65. package/plugins/workflow-runner/src/loader.ts +721 -0
  66. package/plugins/workflow-runner/src/multimodal.js +2 -1
  67. package/plugins/workflow-runner/src/multimodal.ts +64 -0
  68. package/plugins/workflow-runner/src/recovery.ts +149 -0
  69. package/plugins/workflow-runner/src/runner.js +75 -659
  70. package/plugins/workflow-runner/src/runner.ts +604 -0
  71. package/plugins/workflow-runner/src/stepExecutors/agent.ts +234 -0
  72. package/plugins/workflow-runner/src/stepExecutors/assert.ts +50 -0
  73. package/plugins/workflow-runner/src/stepExecutors/echo.js +31 -0
  74. package/plugins/workflow-runner/src/stepExecutors/echo.ts +38 -0
  75. package/plugins/workflow-runner/src/stepExecutors/llm.ts +133 -0
  76. package/plugins/workflow-runner/src/stepExecutors/set.js +49 -0
  77. package/plugins/workflow-runner/src/stepExecutors/set.ts +56 -0
  78. package/plugins/workflow-runner/src/stepExecutors/skill.js +5 -6
  79. package/plugins/workflow-runner/src/stepExecutors/skill.ts +140 -0
  80. package/plugins/workflow-runner/src/stepExecutors/tool.js +80 -8
  81. package/plugins/workflow-runner/src/stepExecutors/tool.ts +256 -0
  82. package/plugins/workflow-runner/src/stepExecutors/verify.ts +68 -0
  83. package/plugins/workflow-runner/src/stepPipeline.js +277 -0
  84. package/plugins/workflow-runner/src/stepPipeline.ts +366 -0
  85. package/plugins/workflow-runner/src/taskIdentity.ts +405 -0
  86. package/plugins/workflow-runner/src/toolRuntime.js +48 -0
  87. package/plugins/workflow-runner/src/toolRuntime.ts +64 -0
  88. package/plugins/workflow-runner/src/types.js +37 -1
  89. package/plugins/workflow-runner/src/types.ts +602 -0
  90. package/plugins/workflow-runner/src/workflowState.ts +733 -0
  91. package/plugins/workflow-runner/test/agent.test.ts +587 -0
  92. package/plugins/workflow-runner/test/agentLlmError.test.ts +373 -0
  93. package/plugins/workflow-runner/test/bashBlacklist.sweep.test.ts +65 -0
  94. package/plugins/workflow-runner/test/captureMisbind.test.ts +99 -0
  95. package/plugins/workflow-runner/test/cli.e2e.test.ts +147 -0
  96. package/plugins/workflow-runner/test/context-files.test.ts +320 -0
  97. package/plugins/workflow-runner/test/e2e.test.ts +370 -0
  98. package/plugins/workflow-runner/test/engineRobustness.test.ts +226 -0
  99. package/plugins/workflow-runner/test/expressions.test.ts +397 -0
  100. package/plugins/workflow-runner/test/foundation.test.ts +126 -0
  101. package/plugins/workflow-runner/test/graceful.test.ts +139 -0
  102. package/plugins/workflow-runner/test/greedyInputs.load.test.ts +47 -0
  103. package/plugins/workflow-runner/test/hostCapability.test.ts +108 -0
  104. package/plugins/workflow-runner/test/illustrate.load.test.ts +183 -0
  105. package/plugins/workflow-runner/test/jsonPayload.test.ts +83 -0
  106. package/plugins/workflow-runner/test/loader.test.ts +617 -0
  107. package/plugins/workflow-runner/test/maxTurnsRetryDelay.test.ts +33 -0
  108. package/plugins/workflow-runner/test/multimodal.test.ts +342 -0
  109. package/plugins/workflow-runner/test/output-schema.test.ts +213 -0
  110. package/plugins/workflow-runner/test/parseWorkflowArgs.test.ts +87 -0
  111. package/plugins/workflow-runner/test/pluginRunner.isolation.test.ts +269 -0
  112. package/plugins/workflow-runner/test/ppt.load.test.ts +1220 -0
  113. package/plugins/workflow-runner/test/ralphLoop.e2e.test.ts +220 -0
  114. package/plugins/workflow-runner/test/recovery.test.ts +265 -0
  115. package/plugins/workflow-runner/test/runner.test.ts +911 -0
  116. package/plugins/workflow-runner/test/runnerPreflight.test.ts +379 -0
  117. package/plugins/workflow-runner/test/skillTurnDone.test.ts +71 -0
  118. package/plugins/workflow-runner/test/stateLifecycle.test.ts +150 -0
  119. package/plugins/workflow-runner/test/stepPipeline.test.ts +114 -0
  120. package/plugins/workflow-runner/test/taskArbitration.test.ts +338 -0
  121. package/plugins/workflow-runner/test/taskIdentity.test.ts +237 -0
  122. package/plugins/workflow-runner/test/taskLifecycle.test.ts +270 -0
  123. package/plugins/workflow-runner/test/toolArtifactOverwrite.test.ts +221 -0
  124. package/plugins/workflow-runner/test/toolCapabilityBoundary.test.ts +29 -0
  125. package/plugins/workflow-runner/test/toolRuntimeIntegration.test.ts +168 -0
  126. package/plugins/workflow-runner/test/webppt.load.test.ts +250 -0
  127. package/plugins/workflow-runner/test/word.load.test.ts +208 -0
  128. package/plugins/workflow-runner/test/workflows.smoke.test.ts +76 -0
  129. package/plugins/workflow-runner/test/yamlGuardConditions.test.ts +207 -0
  130. package/skills/browser-automation/SKILL.md +9 -19
  131. package/skills/web-crawler/SKILL.md +9 -11
  132. package/src/askBridge.ts +249 -0
  133. package/src/bootstrap/cwdArg.js +10 -3
  134. package/src/bootstrap/cwdArg.ts +31 -0
  135. package/src/bootstrap/workingDir.ts +48 -0
  136. package/src/cli/args.js +70 -7
  137. package/src/cli/args.ts +146 -0
  138. package/src/cli/computerUse.js +185 -0
  139. package/src/cli/computerUse.ts +221 -0
  140. package/src/cli/configMenu.js +274 -202
  141. package/src/cli/configMenu.tsx +521 -0
  142. package/src/cli/memoryAccess.js +45 -0
  143. package/src/cli/memoryAccess.ts +53 -0
  144. package/src/cli/print.js +119 -5
  145. package/src/cli/print.ts +597 -0
  146. package/src/cli/quickModelSetup.js +60 -0
  147. package/src/cli/quickModelSetup.tsx +96 -0
  148. package/src/cli/streamJson.js +80 -4
  149. package/src/cli/streamJson.ts +433 -0
  150. package/src/cli/wizards/advisorWizard.js +1 -0
  151. package/src/cli/wizards/advisorWizard.tsx +195 -0
  152. package/src/cli/wizards/agentRuntimeWizard.js +84 -0
  153. package/src/cli/wizards/agentRuntimeWizard.tsx +134 -0
  154. package/src/cli/wizards/browserWizard.js +55 -0
  155. package/src/cli/wizards/browserWizard.tsx +77 -0
  156. package/src/cli/wizards/contextAdvisorWizard.js +84 -0
  157. package/src/cli/wizards/contextAdvisorWizard.tsx +124 -0
  158. package/src/cli/wizards/fieldForm.js +7 -4
  159. package/src/cli/wizards/fieldForm.tsx +250 -0
  160. package/src/cli/wizards/imageWizard.js +4 -5
  161. package/src/cli/wizards/imageWizard.tsx +96 -0
  162. package/src/cli/wizards/openRouterWizard.tsx +48 -0
  163. package/src/cli/wizards/providerPresets.js +1 -8
  164. package/src/cli/wizards/providerPresets.ts +106 -0
  165. package/src/cli/wizards/providerWizard.js +152 -205
  166. package/src/cli/wizards/providerWizard.tsx +266 -0
  167. package/src/cli/wizards/searchWizard.tsx +47 -0
  168. package/src/cli/wizards/ttsWizard.js +4 -3
  169. package/src/cli/wizards/ttsWizard.tsx +61 -0
  170. package/src/config/configDefaults.js +25 -0
  171. package/src/config/configDefaults.ts +33 -0
  172. package/src/config/configDiagnostics.js +117 -0
  173. package/src/config/configDiagnostics.ts +166 -0
  174. package/src/config/configFile.js +52 -32
  175. package/src/config/configFile.ts +633 -0
  176. package/src/config/configManagement.js +126 -0
  177. package/src/config/configManagement.ts +159 -0
  178. package/src/config/configService.js +66 -11
  179. package/src/config/configService.ts +554 -0
  180. package/src/config/numericLimits.js +61 -0
  181. package/src/config/numericLimits.ts +80 -0
  182. package/src/config.ts +109 -0
  183. package/src/context/agentHome.ts +11 -0
  184. package/src/context/archive.ts +144 -0
  185. package/src/context/budgetPlan.js +54 -0
  186. package/src/context/budgetPlan.ts +88 -0
  187. package/src/context/compact.js +179 -106
  188. package/src/context/compact.ts +584 -0
  189. package/src/context/contextBundle.js +119 -0
  190. package/src/context/contextBundle.ts +176 -0
  191. package/src/context/contextLock.ts +84 -0
  192. package/src/context/microCompactLite.ts +218 -0
  193. package/src/context/persistContext.ts +177 -0
  194. package/src/context/reactiveCompact.js +19 -4
  195. package/src/context/reactiveCompact.ts +240 -0
  196. package/src/context/recentDirs.ts +42 -0
  197. package/src/context/sessionPath.ts +206 -0
  198. package/src/context/sessionRegistry.js +8 -2
  199. package/src/context/sessionRegistry.ts +414 -0
  200. package/src/context/snipCompact.ts +163 -0
  201. package/src/context/tokenCounter.ts +106 -0
  202. package/src/context/workspaceIndex.js +2 -2
  203. package/src/context/workspaceIndex.ts +751 -0
  204. package/src/entry.compiled.tsx +20 -0
  205. package/src/harness/claimValidation.js +9 -0
  206. package/src/harness/claimValidation.ts +22 -0
  207. package/src/harness/evidenceLedger.js +92 -0
  208. package/src/harness/evidenceLedger.ts +96 -0
  209. package/src/harness/stallGuard.js +60 -0
  210. package/src/harness/stallGuard.ts +81 -0
  211. package/src/humanIntervention.js +129 -0
  212. package/src/humanIntervention.ts +216 -0
  213. package/src/infra/verification.ts +295 -0
  214. package/src/irreversible/rules.js +216 -0
  215. package/src/irreversible/rules.ts +261 -0
  216. package/src/llm/client.js +40 -3
  217. package/src/llm/client.ts +621 -0
  218. package/src/llm/promptCache.js +27 -0
  219. package/src/llm/promptCache.ts +41 -0
  220. package/src/llm/providers/anthropicFormat.js +41 -5
  221. package/src/llm/providers/anthropicFormat.ts +290 -0
  222. package/src/llm/providers/openaiFormat.js +186 -8
  223. package/src/llm/providers/openaiFormat.ts +428 -0
  224. package/src/loop.js +614 -63
  225. package/src/loop.ts +1454 -0
  226. package/src/main.js +67 -10
  227. package/src/main.tsx +344 -0
  228. package/src/memory/codec.js +204 -0
  229. package/src/memory/codec.ts +249 -0
  230. package/src/memory/constants.js +34 -0
  231. package/src/memory/constants.ts +50 -0
  232. package/src/memory/episodicMemory.js +684 -0
  233. package/src/memory/episodicMemory.ts +815 -0
  234. package/src/memory/fileStore.js +148 -0
  235. package/src/memory/fileStore.ts +184 -0
  236. package/src/memory/memoryService.js +570 -0
  237. package/src/memory/memoryService.ts +859 -0
  238. package/src/memory/migration.js +128 -0
  239. package/src/memory/migration.ts +160 -0
  240. package/src/memory/repository.js +789 -0
  241. package/src/memory/repository.ts +893 -0
  242. package/src/memory/retrieval.js +108 -0
  243. package/src/memory/retrieval.ts +145 -0
  244. package/src/memory/searchIndex.js +107 -0
  245. package/src/memory/searchIndex.ts +153 -0
  246. package/src/memory/topicView.js +82 -0
  247. package/src/memory/topicView.ts +95 -0
  248. package/src/memory/turnJournal.js +95 -0
  249. package/src/memory/turnJournal.ts +120 -0
  250. package/src/memory/types.js +2 -0
  251. package/src/memory/types.ts +186 -0
  252. package/src/messageQueue.ts +123 -0
  253. package/src/multimodal.js +128 -46
  254. package/src/multimodal.ts +349 -0
  255. package/src/plugin-sdk.js +21 -1
  256. package/src/plugin-sdk.ts +134 -0
  257. package/src/plugins/builtinPluginIds.js +47 -0
  258. package/src/plugins/builtinPluginIds.ts +51 -0
  259. package/src/plugins/builtinRegistry.ts +43 -0
  260. package/src/plugins/commandRouter.js +40 -26
  261. package/src/plugins/commandRouter.ts +261 -0
  262. package/src/plugins/hookEngine.ts +341 -0
  263. package/src/plugins/pluginApi.ts +133 -0
  264. package/src/plugins/pluginLifecycle.ts +34 -0
  265. package/src/plugins/pluginLoader.ts +147 -0
  266. package/src/plugins/pluginManifest.js +127 -0
  267. package/src/plugins/pluginManifest.ts +152 -0
  268. package/src/plugins/pluginRunner.js +47 -2
  269. package/src/plugins/pluginRunner.ts +207 -0
  270. package/src/plugins/transcript.ts +208 -0
  271. package/src/prompts/lint.js +169 -0
  272. package/src/prompts/lint.ts +220 -0
  273. package/src/prompts/pluginList.js +32 -16
  274. package/src/prompts/pluginList.ts +86 -0
  275. package/src/prompts/policyRegistry.js +318 -0
  276. package/src/prompts/policyRegistry.ts +358 -0
  277. package/src/prompts/projectInstructions.js +24 -0
  278. package/src/prompts/projectInstructions.ts +81 -0
  279. package/src/prompts/promptAssembly.js +311 -202
  280. package/src/prompts/promptAssembly.ts +510 -0
  281. package/src/prompts/skillList.js +106 -45
  282. package/src/prompts/skillList.ts +198 -0
  283. package/src/prompts/system.js +31 -6
  284. package/src/prompts/system.ts +81 -0
  285. package/src/runtime/agentRuntimeContext.js +53 -0
  286. package/src/runtime/agentRuntimeContext.ts +112 -0
  287. package/src/session/runAgentTurn.js +71 -12
  288. package/src/session/runAgentTurn.ts +181 -0
  289. package/src/session/runTurn.js +29 -0
  290. package/src/session/runTurn.ts +160 -0
  291. package/src/session/runtimeStore.js +149 -0
  292. package/src/session/runtimeStore.ts +197 -0
  293. package/src/session/sessionState.js +79 -3
  294. package/src/session/sessionState.ts +134 -0
  295. package/src/toolGate.ts +50 -0
  296. package/src/tools/advisor/advisor.js +76 -15
  297. package/src/tools/advisor/advisor.ts +512 -0
  298. package/src/tools/agent/agent.js +245 -29
  299. package/src/tools/agent/agent.ts +837 -0
  300. package/src/tools/agent/agentTaskService.js +60 -0
  301. package/src/tools/agent/agentTaskService.ts +113 -0
  302. package/src/tools/agent/agentTypes.js +20 -8
  303. package/src/tools/agent/agentTypes.ts +94 -0
  304. package/src/tools/agent/coordinator.ts +111 -0
  305. package/src/tools/agent/worktree.ts +296 -0
  306. package/src/tools/ask/ask.js +17 -0
  307. package/src/tools/ask/ask.ts +122 -0
  308. package/src/tools/bash/attachImage.js +223 -0
  309. package/src/tools/bash/attachImage.ts +281 -0
  310. package/src/tools/bash/bash.js +132 -14
  311. package/src/tools/bash/bash.ts +642 -0
  312. package/src/tools/bash/semantics.ts +103 -0
  313. package/src/tools/bash/warnings.js +17 -11
  314. package/src/tools/bash/warnings.ts +117 -0
  315. package/src/tools/capabilities.js +42 -0
  316. package/src/tools/capabilities.ts +69 -0
  317. package/src/tools/computeruse/accessibilityPermission.js +261 -0
  318. package/src/tools/computeruse/accessibilityPermission.ts +294 -0
  319. package/src/tools/computeruse/axSearch.js +96 -0
  320. package/src/tools/computeruse/axSearch.ts +120 -0
  321. package/src/tools/computeruse/axSnap.js +272 -0
  322. package/src/tools/computeruse/axSnap.ts +349 -0
  323. package/src/tools/computeruse/capabilityLabels.js +95 -0
  324. package/src/tools/computeruse/capabilityLabels.ts +115 -0
  325. package/src/tools/computeruse/computerUseService.js +2235 -0
  326. package/src/tools/computeruse/computerUseService.ts +2686 -0
  327. package/src/tools/computeruse/computeruse.js +2742 -0
  328. package/src/tools/computeruse/computeruse.ts +3265 -0
  329. package/src/tools/computeruse/diagnosticTrace.js +154 -0
  330. package/src/tools/computeruse/diagnosticTrace.ts +195 -0
  331. package/src/tools/computeruse/diagnostics.js +369 -0
  332. package/src/tools/computeruse/diagnostics.ts +453 -0
  333. package/src/tools/computeruse/grants.js +53 -0
  334. package/src/tools/computeruse/grants.ts +70 -0
  335. package/src/tools/computeruse/humanBoundary.js +159 -0
  336. package/src/tools/computeruse/humanBoundary.ts +199 -0
  337. package/src/tools/computeruse/intervention.js +77 -0
  338. package/src/tools/computeruse/intervention.ts +110 -0
  339. package/src/tools/computeruse/manualSchema.js +709 -0
  340. package/src/tools/computeruse/manualSchema.ts +916 -0
  341. package/src/tools/computeruse/observationImages.js +705 -0
  342. package/src/tools/computeruse/observationImages.ts +889 -0
  343. package/src/tools/computeruse/protocol.js +60 -0
  344. package/src/tools/computeruse/protocol.ts +515 -0
  345. package/src/tools/computeruse/telemetryLog.js +57 -0
  346. package/src/tools/computeruse/telemetryLog.ts +67 -0
  347. package/src/tools/computeruse/vision/appGuide.js +325 -0
  348. package/src/tools/computeruse/vision/appGuide.ts +482 -0
  349. package/src/tools/computeruse/vision/focus.js +291 -0
  350. package/src/tools/computeruse/vision/focus.ts +382 -0
  351. package/src/tools/computeruse/vision/frames.js +248 -0
  352. package/src/tools/computeruse/vision/frames.ts +329 -0
  353. package/src/tools/computeruse/vision/geometry.js +131 -0
  354. package/src/tools/computeruse/vision/geometry.ts +163 -0
  355. package/src/tools/computeruse/vision/imaging.js +201 -0
  356. package/src/tools/computeruse/vision/imaging.ts +281 -0
  357. package/src/tools/computeruse/vision/png.js +274 -0
  358. package/src/tools/computeruse/vision/png.ts +317 -0
  359. package/src/tools/computeruse/vision/raster.js +320 -0
  360. package/src/tools/computeruse/vision/raster.ts +408 -0
  361. package/src/tools/computeruse/vision/settle.js +131 -0
  362. package/src/tools/computeruse/vision/settle.ts +220 -0
  363. package/src/tools/computeruse/vision/telemetry.js +196 -0
  364. package/src/tools/computeruse/vision/telemetry.ts +241 -0
  365. package/src/tools/computeruse/visionOcr.js +115 -0
  366. package/src/tools/computeruse/visionOcr.ts +128 -0
  367. package/src/tools/computeruse/waitFor.js +237 -0
  368. package/src/tools/computeruse/waitFor.ts +291 -0
  369. package/src/tools/download/download.js +10 -6
  370. package/src/tools/download/download.ts +187 -0
  371. package/src/tools/edit/edit.js +30 -18
  372. package/src/tools/edit/edit.ts +349 -0
  373. package/src/tools/edit/multi-edit.js +25 -14
  374. package/src/tools/edit/multi-edit.ts +223 -0
  375. package/src/tools/effectiveToolSet.js +73 -0
  376. package/src/tools/effectiveToolSet.ts +91 -0
  377. package/src/tools/genimage/genimage.ts +257 -0
  378. package/src/tools/genspeech/genspeech.ts +83 -0
  379. package/src/tools/glob/glob.js +176 -29
  380. package/src/tools/glob/glob.ts +283 -0
  381. package/src/tools/grep/grep.js +61 -19
  382. package/src/tools/grep/grep.ts +239 -0
  383. package/src/tools/grep/rgPath.ts +206 -0
  384. package/src/tools/index.js +99 -5
  385. package/src/tools/index.ts +293 -0
  386. package/src/tools/lsp/config.js +113 -0
  387. package/src/tools/lsp/config.ts +113 -0
  388. package/src/tools/lsp/lsp.js +256 -0
  389. package/src/tools/lsp/lsp.ts +274 -0
  390. package/src/tools/lsp/service.js +303 -0
  391. package/src/tools/lsp/service.ts +296 -0
  392. package/src/tools/memory/memory.js +520 -0
  393. package/src/tools/memory/memory.ts +625 -0
  394. package/src/tools/memory/policy.js +53 -0
  395. package/src/tools/memory/policy.ts +72 -0
  396. package/src/tools/notebook/notebookEdit.js +114 -0
  397. package/src/tools/notebook/notebookEdit.ts +126 -0
  398. package/src/tools/powershell/powershell.js +210 -0
  399. package/src/tools/powershell/powershell.ts +239 -0
  400. package/src/tools/presentresult/presentresult.js +145 -0
  401. package/src/tools/presentresult/presentresult.ts +156 -0
  402. package/src/tools/read/read.js +371 -163
  403. package/src/tools/read/read.ts +558 -0
  404. package/src/tools/shared/diffSummary.js +44 -0
  405. package/src/tools/shared/diffSummary.ts +63 -0
  406. package/src/tools/shared/fileActivity.js +51 -0
  407. package/src/tools/shared/fileActivity.ts +66 -0
  408. package/src/tools/shared/fileMutationBus.js +13 -0
  409. package/src/tools/shared/fileMutationBus.ts +14 -0
  410. package/src/tools/shared/fileState.js +73 -65
  411. package/src/tools/shared/fileState.ts +145 -0
  412. package/src/tools/shared/fileUtils.js +81 -4
  413. package/src/tools/shared/fileUtils.ts +504 -0
  414. package/src/tools/shared/killTree.ts +50 -0
  415. package/src/tools/shared/permanentDeleteGuard.js +26 -0
  416. package/src/tools/shared/permanentDeleteGuard.ts +34 -0
  417. package/src/tools/shared/schemas.ts +20 -0
  418. package/src/tools/shared/toolOutputStore.js +97 -0
  419. package/src/tools/shared/toolOutputStore.ts +110 -0
  420. package/src/tools/skill/skill.js +86 -0
  421. package/src/tools/skill/skill.ts +94 -0
  422. package/src/tools/snip/snip.js +67 -0
  423. package/src/tools/snip/snip.ts +80 -0
  424. package/src/tools/tasks/backgroundTaskService.js +275 -0
  425. package/src/tools/tasks/backgroundTaskService.ts +369 -0
  426. package/src/tools/tasks/processEvidence.js +24 -0
  427. package/src/tools/tasks/processEvidence.ts +34 -0
  428. package/src/tools/tasks/taskManagement.js +163 -0
  429. package/src/tools/tasks/taskManagement.ts +180 -0
  430. package/src/tools/tasks/taskOutput.js +113 -0
  431. package/src/tools/tasks/taskOutput.ts +145 -0
  432. package/src/tools/tasks/taskStop.js +74 -0
  433. package/src/tools/tasks/taskStop.ts +99 -0
  434. package/src/tools/tasks/taskStore.js +46 -0
  435. package/src/tools/tasks/taskStore.ts +77 -0
  436. package/src/tools/terminal/terminalCapture.js +167 -0
  437. package/src/tools/terminal/terminalCapture.ts +179 -0
  438. package/src/tools/trash/trash.js +233 -0
  439. package/src/tools/trash/trash.ts +283 -0
  440. package/src/tools/types.js +1 -1
  441. package/src/tools/types.ts +24 -0
  442. package/src/tools/webbrowser/agentBrowserPath.ts +286 -0
  443. package/src/tools/webbrowser/browser.js +214 -11
  444. package/src/tools/webbrowser/browser.ts +293 -0
  445. package/src/tools/webbrowser/browserControl.js +153 -48
  446. package/src/tools/webbrowser/browserControl.ts +262 -0
  447. package/src/tools/webbrowser/browserDaemon.js +147 -39
  448. package/src/tools/webbrowser/browserDaemon.ts +270 -0
  449. package/src/tools/webbrowser/browserOutcome.js +273 -0
  450. package/src/tools/webbrowser/browserOutcome.ts +306 -0
  451. package/src/tools/webbrowser/browserRunRegistry.js +150 -0
  452. package/src/tools/webbrowser/browserRunRegistry.ts +179 -0
  453. package/src/tools/webbrowser/browserService.js +20 -6
  454. package/src/tools/webbrowser/browserService.ts +56 -0
  455. package/src/tools/webbrowser/browserStream.js +30 -1
  456. package/src/tools/webbrowser/browserStream.ts +216 -0
  457. package/src/tools/webbrowser/browserTabs.js +143 -0
  458. package/src/tools/webbrowser/browserTabs.ts +210 -0
  459. package/src/tools/webbrowser/webbrowser.js +831 -150
  460. package/src/tools/webbrowser/webbrowser.ts +1537 -0
  461. package/src/tools/webfetch/preapproved.ts +274 -0
  462. package/src/tools/webfetch/webfetch.js +24 -23
  463. package/src/tools/webfetch/webfetch.ts +383 -0
  464. package/src/tools/websearch/websearch.js +12 -11
  465. package/src/tools/websearch/websearch.ts +292 -0
  466. package/src/tools/worktree/sessionWorktree.js +128 -0
  467. package/src/tools/worktree/sessionWorktree.ts +158 -0
  468. package/src/tools/write/write.js +27 -44
  469. package/src/tools/write/write.ts +136 -0
  470. package/src/types.ts +960 -0
  471. package/src/ui/App.js +216 -12
  472. package/src/ui/App.tsx +879 -0
  473. package/src/ui/AskQuestion.tsx +300 -0
  474. package/src/ui/InputBox.js +77 -1
  475. package/src/ui/InputBox.tsx +567 -0
  476. package/src/ui/LiveArea.tsx +344 -0
  477. package/src/ui/MessageList.js +27 -6
  478. package/src/ui/MessageList.tsx +559 -0
  479. package/src/ui/ModelPicker.tsx +107 -0
  480. package/src/ui/QueuedMessagesBanner.tsx +51 -0
  481. package/src/ui/Root.js +6 -8
  482. package/src/ui/Root.tsx +100 -0
  483. package/src/ui/SessionPicker.tsx +169 -0
  484. package/src/ui/StatusLine.js +22 -1
  485. package/src/ui/StatusLine.tsx +145 -0
  486. package/src/ui/WorkspacePicker.tsx +84 -0
  487. package/src/ui/advisorCommand.js +54 -0
  488. package/src/ui/advisorCommand.ts +63 -0
  489. package/src/ui/computerCommand.js +19 -0
  490. package/src/ui/computerCommand.ts +35 -0
  491. package/src/ui/figures.ts +45 -0
  492. package/src/ui/hooks/askSlot.ts +112 -0
  493. package/src/ui/hooks/useChat.js +271 -7
  494. package/src/ui/hooks/useChat.ts +1578 -0
  495. package/src/ui/hooks/useInputHistory.ts +235 -0
  496. package/src/ui/hooks/usePasteHandler.ts +220 -0
  497. package/src/ui/hooks/useTerminalSize.ts +41 -0
  498. package/src/ui/hooks/useTextBuffer.ts +108 -0
  499. package/src/ui/hooks/useTokenUsage.ts +44 -0
  500. package/src/ui/liveViewport.ts +189 -0
  501. package/src/ui/markdown.ts +230 -0
  502. package/src/ui/textBuffer.ts +239 -0
  503. package/src/ui/theme.ts +35 -0
  504. package/src/utils/clipboardImage.js +358 -0
  505. package/src/utils/clipboardImage.ts +420 -0
  506. package/src/utils/debugLog.ts +50 -0
  507. package/src/utils/greenRoot.ts +35 -0
  508. package/src/utils/packageRoot.ts +40 -0
  509. package/src/utils/pathContains.js +28 -0
  510. package/src/utils/pathContains.ts +31 -0
  511. package/src/utils/resourcePaths.ts +71 -0
  512. package/src/utils/stripThink.ts +49 -0
  513. package/src/utils/zodToJson.ts +73 -0
  514. package/src/web/browserBridge.js +365 -66
  515. package/src/web/browserBridge.ts +672 -0
  516. package/src/web/protocol.ts +1804 -0
  517. package/vendor/computer-use/arm64-darwin/cu-helperd +0 -0
  518. package/workflows/ppt.yaml +13 -2
  519. package/workflows/wechat-reading-shots.yaml +315 -0
  520. package/workflows/word.yaml +11 -1
  521. package/skills/docx/scripts/office/helpers/__pycache__/__init__.cpython-314.pyc +0 -0
  522. package/skills/docx/scripts/office/helpers/__pycache__/merge_runs.cpython-314.pyc +0 -0
  523. package/skills/docx/scripts/office/helpers/__pycache__/simplify_redlines.cpython-314.pyc +0 -0
  524. package/skills/docx/scripts/office/validators/__pycache__/__init__.cpython-314.pyc +0 -0
  525. package/skills/docx/scripts/office/validators/__pycache__/base.cpython-314.pyc +0 -0
  526. package/skills/docx/scripts/office/validators/__pycache__/docx.cpython-314.pyc +0 -0
  527. package/skills/docx/scripts/office/validators/__pycache__/pptx.cpython-314.pyc +0 -0
  528. package/skills/docx/scripts/office/validators/__pycache__/redlining.cpython-314.pyc +0 -0
  529. package/skills/image-stats/scripts/__pycache__/image_stats.cpython-314.pyc +0 -0
  530. package/skills/image-stats/scripts/__pycache__/image_stats_test.cpython-314.pyc +0 -0
  531. package/skills/ui-design/scripts/__pycache__/assemble_material.cpython-314.pyc +0 -0
  532. package/skills/ui-design/scripts/__pycache__/assemble_material_test.cpython-314.pyc +0 -0
  533. package/skills/ui-design/scripts/__pycache__/chart_sanity_check.cpython-314.pyc +0 -0
  534. package/skills/ui-design/scripts/__pycache__/check_language_policy.cpython-314.pyc +0 -0
  535. package/skills/ui-design/scripts/__pycache__/check_ui_artifact.cpython-314.pyc +0 -0
  536. package/skills/ui-design/scripts/__pycache__/check_ui_artifact_pptx_test.cpython-314.pyc +0 -0
  537. package/skills/ui-design/scripts/__pycache__/deck_pages_check.cpython-314.pyc +0 -0
  538. package/skills/ui-design/scripts/__pycache__/deck_pages_check_test.cpython-314.pyc +0 -0
  539. package/skills/ui-design/scripts/__pycache__/deck_visual_style_check.cpython-314.pyc +0 -0
  540. package/skills/ui-design/scripts/__pycache__/deck_visual_style_check_test.cpython-314.pyc +0 -0
  541. package/skills/ui-design/scripts/__pycache__/design_budget_check.cpython-314.pyc +0 -0
  542. package/skills/ui-design/scripts/__pycache__/design_budget_check_test.cpython-314.pyc +0 -0
  543. package/skills/ui-design/scripts/__pycache__/design_repair_hints.cpython-314.pyc +0 -0
  544. package/skills/ui-design/scripts/__pycache__/design_repair_hints_test.cpython-314.pyc +0 -0
  545. package/skills/ui-design/scripts/__pycache__/design_style_check.cpython-314.pyc +0 -0
  546. package/skills/ui-design/scripts/__pycache__/design_style_check_test.cpython-314.pyc +0 -0
  547. package/skills/ui-design/scripts/__pycache__/draft_coverage_check.cpython-314.pyc +0 -0
  548. package/skills/ui-design/scripts/__pycache__/extract_source_assets.cpython-314.pyc +0 -0
  549. package/skills/ui-design/scripts/__pycache__/extract_source_assets_test.cpython-314.pyc +0 -0
  550. package/skills/ui-design/scripts/__pycache__/generated_image_review_collect.cpython-314.pyc +0 -0
  551. package/skills/ui-design/scripts/__pycache__/generated_image_review_collect_test.cpython-314.pyc +0 -0
  552. package/skills/ui-design/scripts/__pycache__/image_plan_lint.cpython-314.pyc +0 -0
  553. package/skills/ui-design/scripts/__pycache__/image_plan_lint_test.cpython-314.pyc +0 -0
  554. package/skills/ui-design/scripts/__pycache__/image_prompt_rewrite.cpython-314.pyc +0 -0
  555. package/skills/ui-design/scripts/__pycache__/image_prompt_rewrite_test.cpython-314.pyc +0 -0
  556. package/skills/ui-design/scripts/__pycache__/layout_balance.cpython-314.pyc +0 -0
  557. package/skills/ui-design/scripts/__pycache__/layout_balance_test.cpython-314.pyc +0 -0
  558. package/skills/ui-design/scripts/__pycache__/normalize_design_json.cpython-314.pyc +0 -0
  559. package/skills/ui-design/scripts/__pycache__/normalize_design_json_test.cpython-314.pyc +0 -0
  560. package/skills/ui-design/scripts/__pycache__/plan_source_layout.cpython-314.pyc +0 -0
  561. package/skills/ui-design/scripts/__pycache__/plan_source_layout_test.cpython-314.pyc +0 -0
  562. package/skills/ui-design/scripts/__pycache__/resolve_source_hint.cpython-314.pyc +0 -0
  563. package/skills/ui-design/scripts/__pycache__/resolve_source_hint_test.cpython-314.pyc +0 -0
  564. package/skills/ui-design/scripts/__pycache__/resolve_source_input.cpython-314.pyc +0 -0
  565. package/skills/ui-design/scripts/__pycache__/resolve_source_input_test.cpython-314.pyc +0 -0
  566. package/skills/ui-design/scripts/__pycache__/scaffold_ui_artifact.cpython-314.pyc +0 -0
  567. package/skills/ui-design/scripts/__pycache__/source_usage_check.cpython-314.pyc +0 -0
  568. package/skills/ui-design/scripts/__pycache__/source_usage_check_test.cpython-314.pyc +0 -0
  569. package/skills/ui-design/scripts/__pycache__/text_readability_pixel.cpython-314.pyc +0 -0
  570. package/skills/ui-design/scripts/__pycache__/visual_quality_gate.cpython-314.pyc +0 -0
  571. package/skills/ui-design/scripts/__pycache__/visual_quality_gate_test.cpython-314.pyc +0 -0
  572. /package/skills/ai-image/scripts/{generate_test.mjs → generate.selftest.mjs} +0 -0
  573. /package/skills/ai-image/scripts/{generate_from_plan_test.mjs → generate_from_plan.selftest.mjs} +0 -0
@@ -23,10 +23,8 @@
23
23
  */
24
24
  import { existsSync } from 'node:fs';
25
25
  import { resolve } from 'node:path';
26
- import { dlog, executeTool, getWorkingDir } from '../../../src/plugin-sdk.js';
27
- import { isMaxTurnsCutoff, truncate } from './agentCommon.js';
28
- import { evalExpr, interpolate, interpolateDeep, interpolateTyped } from './expressions.js';
29
- import { runRecovery } from './recovery.js';
26
+ import { dlog, getWorkingDir } from '../../../src/plugin-sdk.js';
27
+ import { evalExpr, interpolate } from './expressions.js';
30
28
  import { buildTaskArbitrator, isContinuationPhrase } from './taskIdentity.js';
31
29
  import { execAgentStep } from './stepExecutors/agent.js';
32
30
  import { execAssertStep } from './stepExecutors/assert.js';
@@ -34,8 +32,15 @@ import { execVerifyStep } from './stepExecutors/verify.js';
34
32
  import { execLlmStep } from './stepExecutors/llm.js';
35
33
  import { execSkillStep } from './stepExecutors/skill.js';
36
34
  import { execToolStep } from './stepExecutors/tool.js';
37
- import { normalizeCapture, VarStack } from './types.js';
38
- import { loopCheckpointName, WorkflowState, } from './workflowState.js';
35
+ import { execEchoStep } from './stepExecutors/echo.js';
36
+ import { execSetStep } from './stepExecutors/set.js';
37
+ import { execBranchStep } from './controlFlow/branch.js';
38
+ import { execLoopStep } from './controlFlow/loop.js';
39
+ import { execPauseStep } from './controlFlow/pause.js';
40
+ import { execSignalStep } from './controlFlow/signal.js';
41
+ import { runStepWithRetry } from './stepPipeline.js';
42
+ import { VarStack } from './types.js';
43
+ import { WorkflowState, } from './workflowState.js';
39
44
  /**
40
45
  * 插件 ID:把私有 workflow_* 事件镜像成框架通用 plugin_progress 时挂这个 id。
41
46
  * LiveArea 状态行会渲染成 "[plugin:workflow-runner] i/total <message>"
@@ -127,32 +132,6 @@ function stepKind(step) {
127
132
  return 'llm';
128
133
  return 'assert'; // 不会到这里(loader 已校验),兜底
129
134
  }
130
- function readPath(root, path) {
131
- const parts = path.split('.');
132
- let cur = root;
133
- for (const p of parts) {
134
- if (cur == null || typeof cur !== 'object')
135
- return undefined;
136
- cur = cur[p];
137
- }
138
- return cur;
139
- }
140
- function bindCapture(captureMap, result, vars) {
141
- for (const [resultField, varName] of Object.entries(normalizeCapture(captureMap))) {
142
- const value = resultField.includes('.')
143
- ? readPath(result.raw, resultField)
144
- : result.raw[resultField];
145
- vars.set(varName, value);
146
- }
147
- }
148
- /** 取本步 capture 出的 { varName: value },供 checkpoint 记录(resume 回放用)。 */
149
- function collectCaptured(captureMap, vars) {
150
- const out = {};
151
- for (const varName of Object.values(normalizeCapture(captureMap))) {
152
- out[varName] = vars.get(varName);
153
- }
154
- return out;
155
- }
156
135
  function resolvePositiveIterationLimit(raw, vars, defaultValue, label) {
157
136
  if (raw === undefined)
158
137
  return { ok: true, value: defaultValue };
@@ -175,6 +154,12 @@ async function* dispatchActionStep(step, vars, ctx) {
175
154
  return execAssertStep(step, vars, ctx);
176
155
  if (step.type === 'verify')
177
156
  return await execVerifyStep(step, vars);
157
+ // set / echo 从 runSteps 的内联块搬进执行器体系:它们不影响迭代、只产出 StepResult。
158
+ // 搬过来的直接收益是 `capture:` 自然生效(此前它们从不调 bindCapture —— 静默失效)。
159
+ if (step.type === 'set')
160
+ return yield* execSetStep(step, vars);
161
+ if (step.type === 'echo')
162
+ return yield* execEchoStep(step, vars);
178
163
  if (step.tool)
179
164
  return yield* execToolStep(step, vars, ctx);
180
165
  if (step.agent)
@@ -215,7 +200,9 @@ loopResume) {
215
200
  };
216
201
  continue;
217
202
  }
218
- // when 条件
203
+ // when 条件:**所有** step 类型统一在这里判。控制流处理器里不要再判一次 ——
204
+ // 重构前 break/continue 块自带一份,而它恒为真(false 时这里已经 continue 掉了),
205
+ // 连它的 catch 分支都不可达。那是纯死代码,随本次重构删除。
219
206
  if (step.when) {
220
207
  let pass = false;
221
208
  try {
@@ -223,12 +210,7 @@ loopResume) {
223
210
  }
224
211
  catch (e) {
225
212
  const errMsg = `when 表达式错误: ${e.message}`;
226
- yield {
227
- type: 'workflow_step_end',
228
- id: step.id,
229
- ok: false,
230
- error: errMsg,
231
- };
213
+ yield { type: 'workflow_step_end', id: step.id, ok: false, error: errMsg };
232
214
  yield {
233
215
  type: 'error',
234
216
  error: `Workflow halted at step "${step.id}": ${errMsg}`,
@@ -236,11 +218,7 @@ loopResume) {
236
218
  return 'halted';
237
219
  }
238
220
  if (!pass) {
239
- yield {
240
- type: 'workflow_step_skipped',
241
- id: step.id,
242
- reason: 'when=false',
243
- };
221
+ yield { type: 'workflow_step_skipped', id: step.id, reason: 'when=false' };
244
222
  await state.appendProgress(`- ${step.id} skipped (when=false)`);
245
223
  continue;
246
224
  }
@@ -249,9 +227,7 @@ loopResume) {
249
227
  dlog('wf', `step ${i + 1}/${steps.length} START ${step.id} (${kind})`);
250
228
  // 已插值的人话进度名随 start 事件带给 UI:scrollback 进度行显示它而非块内相对
251
229
  // 序号,与状态行(下方 plugin_progress)保持一致。未声明 title 的步省略此字段。
252
- const interpolatedTitle = step.title
253
- ? interpolate(step.title, vars)
254
- : undefined;
230
+ const interpolatedTitle = step.title ? interpolate(step.title, vars) : undefined;
255
231
  yield {
256
232
  type: 'workflow_step_start',
257
233
  id: step.id,
@@ -264,627 +240,67 @@ loopResume) {
264
240
  // 有 title 的步显示人话阶段名(如「阶段2/6·起草实现计划」)且不带块内序号——
265
241
  // 嵌套块的 i/total(顶层 5、循环体 12…)会被用户误读成"跳步/结构乱";
266
242
  // 未声明 title 的步保持 legacy 格式 "i/total step <id> (<kind>)"。
267
- if (interpolatedTitle) {
268
- yield {
269
- type: 'plugin_progress',
270
- pluginId: WF_PLUGIN_ID,
271
- message: interpolatedTitle,
272
- };
273
- }
274
- else {
275
- yield {
276
- type: 'plugin_progress',
277
- pluginId: WF_PLUGIN_ID,
278
- current: i + 1,
279
- max: steps.length,
280
- message: `step ${step.id} (${kind})`,
281
- };
282
- }
283
- // ---------- 控制流:branch ----------
284
- if (step.type === 'branch') {
285
- let cond = false;
286
- try {
287
- cond = Boolean(evalExpr(step.condition ?? 'false', vars));
288
- }
289
- catch (e) {
290
- const errMsg = `branch 条件错误: ${e.message}`;
291
- await state.appendProgress(`✗ ${step.id}: ${errMsg}`);
292
- yield { type: 'workflow_step_end', id: step.id, ok: false, error: errMsg };
293
- yield { type: 'error', error: `Workflow halted at step "${step.id}": ${errMsg}` };
294
- return 'halted';
295
- }
296
- const branch = cond ? step.then : step.else;
297
- if (Array.isArray(branch) && branch.length > 0) {
298
- // branch 对 break 透明:子块 break/halt 都原样上抛给最近的循环 / 顶层。
299
- const r = yield* runSteps(branch, vars, ctx, state);
300
- if (r !== 'completed')
301
- return r;
302
- }
303
- await state.appendProgress(`✓ ${step.id} (branch=${cond})`);
304
- await state.writeVars(vars.snapshot());
305
- yield {
306
- type: 'workflow_step_end',
307
- id: step.id,
308
- ok: true,
309
- output: `branch=${cond}`,
310
- };
311
- continue;
312
- }
313
- // ---------- 控制流:set(确定性变量赋值) ----------
243
+ yield {
244
+ type: 'plugin_progress',
245
+ pluginId: WF_PLUGIN_ID,
246
+ current: i + 1,
247
+ max: steps.length,
248
+ message: interpolatedTitle ?? `step ${step.id} (${kind})`,
249
+ };
250
+ // ---------- 控制流:改变的是「循环怎么走」,不是产出一个 StepResult ----------
314
251
  //
315
- // 状态机类工作流(如 ralph-loop 的 phase)必须用 set——曾有 yaml 用
316
- // `llm: "PLAN"` 当赋值语句:真把 "PLAN" 发给模型、capture 模型自由回复,
317
- // 变量值非确定,下游 phase == 'PLAN' 比较全部不可靠。
318
- if (step.type === 'set') {
319
- const missing = [];
320
- // 顶层值若是单一 "${EXPR}" 模板走 interpolateTyped 保留类型(布尔/数字/数组
321
- // 不再字符串化成 "true",下游条件不必写 == true || == 'true' 防御);
322
- // 多片段模板与嵌套结构行为不变(interpolateDeep)。
323
- const resolved = {};
324
- for (const [k, v] of Object.entries(step.set ?? {})) {
325
- resolved[k] =
326
- typeof v === 'string'
327
- ? interpolateTyped(v, vars, { onMissing: (e) => missing.push(e) })
328
- : interpolateDeep(v, vars, { onMissing: (e) => missing.push(e) });
329
- }
330
- if (missing.length > 0) {
331
- yield {
332
- type: 'warning',
333
- message: `step "${step.id}": 模板变量未定义:${[...new Set(missing)].map((m) => `\${${m}}`).join('、')}(裸名请用 inputs.<name> 或先 capture/set)`,
334
- };
335
- }
336
- for (const [k, v] of Object.entries(resolved)) {
337
- vars.setGlobal(k, v);
338
- }
339
- const summary = Object.entries(resolved)
340
- .map(([k, v]) => `${k}=${String(v).slice(0, 40)}`)
341
- .join(', ');
342
- await state.appendProgress(`✓ ${step.id} (set ${summary})`);
343
- await state.writeVars(vars.snapshot());
344
- // set 记 checkpoint(值入 captures):resume 跳过它时变量仍能回放
345
- if (cp) {
346
- await state.recordCheckpoint(cp.name(step.id), `set: ${summary}`, resolved, vars.snapshot());
347
- }
348
- yield {
349
- type: 'workflow_step_end',
350
- id: step.id,
351
- ok: true,
352
- output: `set: ${summary}`,
353
- };
354
- continue;
252
+ // 所以它们不塞进 stepExecutors 那套 (step,vars,ctx)→StepResult 的形状里,
253
+ // 各自住在 controlFlow/ 并共用出口 ControlOutcome —— 这里只需一个分派。
254
+ const controlDeps = {
255
+ ctx,
256
+ state,
257
+ ...(cp ? { cp } : {}),
258
+ runBlock: (blockSteps, blockVars, blockCp, blockStart, blockResume) => runSteps(blockSteps, blockVars, ctx, state, blockCp, blockStart, blockResume),
259
+ index: i,
260
+ total: steps.length,
261
+ addGoalSupplement: (text) => addGoalSupplement(ctx, state, text),
262
+ };
263
+ let outcome = null;
264
+ if (step.type === 'branch') {
265
+ outcome = yield* execBranchStep(step, vars, controlDeps);
355
266
  }
356
- // ---------- 控制流:loop(for-each 或 while) ----------
357
- if (step.type === 'loop') {
358
- const isWhileMode = step.over === undefined;
359
- // max_iterations 支持 ${} 模板(如 "${inputs.max_iterations}"):插值 + coerce;
360
- // 失败响亮 halt——曾有模板字符串不插值直接参与 < 比较 → NaN → 静默 0 次迭代。
361
- let maxIter = 200; // while 模式安全上限(缺省)
362
- if (step.max_iterations !== undefined) {
363
- const raw = step.max_iterations;
364
- const n = typeof raw === 'number'
365
- ? raw
366
- : Number(interpolate(String(raw), vars).trim());
367
- if (!Number.isFinite(n) || n <= 0) {
368
- const errMsg = `loop "${step.id}" 的 max_iterations 解析失败:${JSON.stringify(raw)} → "${typeof raw === 'string' ? interpolate(String(raw), vars) : raw}" 不是正数(检查变量是否存在,裸名请用 inputs.<name>)`;
369
- yield { type: 'workflow_step_end', id: step.id, ok: false, error: errMsg };
370
- yield { type: 'error', error: `Workflow halted at step "${step.id}": ${errMsg}` };
371
- return 'halted';
372
- }
373
- maxIter = Math.floor(n);
374
- }
375
- const useFreshContext = step.fresh_context === true;
376
- // fresh context:循环开始时 snapshot 当前 history
377
- let baseHistorySnapshot = null;
378
- if (useFreshContext) {
379
- baseHistorySnapshot = ctx.history.slice();
380
- }
381
- // loop resume:恢复信息匹配本循环时消费一次——从中断时的迭代号起跑,
382
- // 该迭代内已完成的步经 innerStartFrom 跳过(变量已由 runWorkflow 回放快照)。
383
- let resumeIterStart = 0;
384
- let pendingInnerStart = 0;
385
- if (loopResume && loopResume.loopId === step.id) {
386
- resumeIterStart = loopResume.iteration;
387
- pendingInnerStart = loopResume.innerStartFrom;
267
+ else if (step.type === 'loop') {
268
+ outcome = yield* execLoopStep(step, vars, controlDeps, loopResume);
269
+ // 恢复信息只消费一次:匹配到的那个 loop 用掉之后,后续 loop 不再套用。
270
+ if (loopResume && loopResume.loopId === step.id)
388
271
  loopResume = undefined;
389
- yield {
390
- type: 'warning',
391
- message: `♻ 循环 ${step.id} 从第 ${resumeIterStart + 1} 轮迭代第 ${pendingInnerStart + 1} 步续跑。`,
392
- };
393
- }
394
- // 顶层 loop 的 do[] 步记复合 checkpoint(loopId@@迭代号@@stepId),
395
- // resume 可恢复到迭代中间;更深嵌套不再下钻(descendIntoLoop=false)。
396
- const innerCpFor = (iter) => cp?.descendIntoLoop
397
- ? {
398
- name: (id) => loopCheckpointName(step.id, iter, id),
399
- descendIntoLoop: false,
400
- }
401
- : undefined;
402
- if (isWhileMode) {
403
- // ===== WHILE 循环模式 =====
404
- const asName = step.as ?? '_iter';
405
- let iterCount = resumeIterStart;
406
- let shouldBreak = false;
407
- while (iterCount < maxIter) {
408
- if (ctx.signal?.aborted) {
409
- yield { type: 'interrupted' };
410
- return 'halted';
411
- }
412
- // 注入迭代变量
413
- vars.push();
414
- vars.set(asName, iterCount);
415
- vars.set(`${asName}_idx`, iterCount);
416
- // fresh context:每轮重置 history 到循环入口快照
417
- if (useFreshContext && baseHistorySnapshot) {
418
- ctx.history.length = 0;
419
- ctx.history.push(...baseHistorySnapshot);
420
- }
421
- // 迭代序号对用户有真实意义(第几轮重做),title 步也保留,只换人话名。
422
- yield {
423
- type: 'plugin_progress',
424
- pluginId: WF_PLUGIN_ID,
425
- current: iterCount + 1,
426
- max: maxIter,
427
- message: step.title
428
- ? `${interpolate(step.title, vars)}·第 ${iterCount + 1}/${maxIter} 轮`
429
- : `while-loop ${step.id} 迭代 ${iterCount + 1}/${maxIter}`,
430
- };
431
- // 第 2+ 轮额外发持久 warning:上面的状态行 progress 会被首步 title 立即
432
- // 覆盖(驻留一瞬),用户感知不到「循环重来了」——曾有 plan 工作流验收
433
- // 打回静默重启,被用户误读成死循环。while 模式的多轮 = 重做/重试,
434
- // 值得进 MessageList 留痕;for-each 的逐元素迭代是正常推进,不发。
435
- if (iterCount > 0) {
436
- yield {
437
- type: 'warning',
438
- message: `♻ 循环「${step.title ? interpolate(step.title, vars) : step.id}」进入第 ${iterCount + 1}/${maxIter} 轮`,
439
- };
440
- }
441
- const innerStart = pendingInnerStart;
442
- pendingInnerStart = 0; // 仅 resume 后的首个迭代生效
443
- const r = yield* runSteps(step.do ?? [], vars, ctx, state, innerCpFor(iterCount), innerStart);
444
- vars.pop();
445
- iterCount++;
446
- // halt(error/interrupt/onError:halt)→ 真正向上中止整个工作流。
447
- if (r === 'halted')
448
- return 'halted';
449
- if (typeof r === 'object') {
450
- // 带 target 且不是本循环 → 穿透上抛给外层循环
451
- if (r.target !== undefined && r.target !== step.id)
452
- return r;
453
- if (r.signal === 'break') {
454
- shouldBreak = true;
455
- break;
456
- }
457
- // continue → 跳到下一轮迭代(什么都不做,自然进入下一轮)
458
- }
459
- }
460
- const whyEnd = shouldBreak ? 'break 退出' : `达到上限 ${maxIter}`;
461
- await state.appendProgress(`✓ ${step.id} (while ×${iterCount}, ${whyEnd})`);
462
- await state.writeVars(vars.snapshot());
463
- // 循环完成记自身 checkpoint:resume 据此判定「循环已整体完成」(否则
464
- // 中断在循环之后的步骤时,resume 会误把整条循环从头重跑)。
465
- if (cp) {
466
- await state.recordCheckpoint(cp.name(step.id), `while loop ${iterCount} iterations (${whyEnd})`, collectCaptured(step.capture, vars), vars.snapshot());
467
- }
468
- yield {
469
- type: 'workflow_step_end',
470
- id: step.id,
471
- ok: true,
472
- output: `while loop ${iterCount} iterations (${whyEnd})`,
473
- };
474
- continue;
475
- }
476
- // ===== FOR-EACH 循环模式(原有逻辑) =====
477
- let arr;
478
- try {
479
- arr = evalExpr(step.over ?? '[]', vars);
480
- }
481
- catch (e) {
482
- const errMsg = `loop over 表达式错误: ${e.message}`;
483
- await state.appendProgress(`✗ ${step.id}: ${errMsg}`);
484
- yield { type: 'workflow_step_end', id: step.id, ok: false, error: errMsg };
485
- yield { type: 'error', error: `Workflow halted at step "${step.id}": ${errMsg}` };
486
- return 'halted';
487
- }
488
- if (!Array.isArray(arr)) {
489
- const errMsg = `loop over 必须求值为数组,得到 ${typeof arr}`;
490
- await state.appendProgress(`✗ ${step.id}: ${errMsg}`);
491
- yield { type: 'workflow_step_end', id: step.id, ok: false, error: errMsg };
492
- yield { type: 'error', error: `Workflow halted at step "${step.id}": ${errMsg}` };
493
- return 'halted';
494
- }
495
- const asName = step.as ?? 'item';
496
- let iterCount = 0;
497
- for (let idx = resumeIterStart; idx < arr.length; idx++) {
498
- if (ctx.signal?.aborted) {
499
- yield { type: 'interrupted' };
500
- return 'halted';
501
- }
502
- vars.push();
503
- vars.set(asName, arr[idx]);
504
- vars.set(`${asName}_idx`, idx);
505
- // fresh context 也支持 for-each 模式
506
- if (useFreshContext && baseHistorySnapshot) {
507
- ctx.history.length = 0;
508
- ctx.history.push(...baseHistorySnapshot);
509
- }
510
- const innerStart = pendingInnerStart;
511
- pendingInnerStart = 0;
512
- const r = yield* runSteps(step.do ?? [], vars, ctx, state, innerCpFor(idx), innerStart);
513
- vars.pop();
514
- iterCount++;
515
- if (r === 'halted')
516
- return 'halted';
517
- if (typeof r === 'object') {
518
- if (r.target !== undefined && r.target !== step.id)
519
- return r; // 穿透上抛
520
- if (r.signal === 'break')
521
- break; // 跳出 for-each,循环后兄弟步继续
522
- // continue → 下一个元素
523
- }
524
- }
525
- await state.appendProgress(`✓ ${step.id} (loop ×${iterCount})`);
526
- await state.writeVars(vars.snapshot());
527
- if (cp) {
528
- await state.recordCheckpoint(cp.name(step.id), `loop ${iterCount} 次`, collectCaptured(step.capture, vars), vars.snapshot());
529
- }
530
- yield {
531
- type: 'workflow_step_end',
532
- id: step.id,
533
- ok: true,
534
- output: `loop ${iterCount} 次`,
535
- };
536
- continue;
537
272
  }
538
- // ---------- 控制流:pause(真 HITL 审批闸门) ----------
539
- //
540
- // 经 AskUserQuestion/askBridge 实现真停等:TUI 弹选择界面、用户拍板后继续;
541
- // 非交互宿主(-p/gateway/测试)askBridge 自动选推荐项(继续执行)且 stderr
542
- // 注记,绝不挂起——与 -p 单行 JSON 契约兼容。「等用户」骑在既有
543
- // tool_call→tool_result 回路上(同 AskUserQuestion 工具),引擎零新通道。
544
- if (step.type === 'pause') {
545
- const msg = step.prompt
546
- ? interpolate(step.prompt, vars)
547
- : '工作流暂停点:请确认是否继续执行后续步骤。';
548
- await state.appendProgress(`- ${step.id} pause: 等待用户确认`);
549
- // pause 会驻留状态行很久(等用户),title 步同样不带块内序号防误读。
550
- if (step.title) {
551
- yield {
552
- type: 'plugin_progress',
553
- pluginId: WF_PLUGIN_ID,
554
- message: `⏸ ${interpolate(step.title, vars)}:等待用户确认…`,
555
- };
556
- }
557
- else {
558
- yield {
559
- type: 'plugin_progress',
560
- pluginId: WF_PLUGIN_ID,
561
- current: i + 1,
562
- max: steps.length,
563
- message: `⏸ 审批闸门 ${step.id}:等待用户确认…`,
564
- };
565
- }
566
- const exec = ctx.executeTool ?? executeTool;
567
- // 审批语义:明确选「继续」才放行;选「停止」、取消提问(ESC)、或自由输入
568
- // 其它内容(既非继续也非停止 = 用户想先改点什么)都不放行(审批门上的
569
- // 含糊 = 不批准,保守处理);桥本身异常才兜底放行(保持可用性)。
570
- let approved = true;
571
- let askFailed = false;
572
- let freeText = '';
573
- const freeTextMode = step.free_text ?? 'halt';
574
- try {
575
- const r = await exec('AskUserQuestion', JSON.stringify({
576
- questions: [
577
- {
578
- question: truncate(msg, 400),
579
- header: '审批闸门',
580
- options: [
581
- { label: '继续执行 (推荐)', description: '放行后续步骤' },
582
- {
583
- label: '停止(保留现场)',
584
- description: '中止本次运行;同任务重跑可从断点 resume',
585
- },
586
- ],
587
- },
588
- ],
589
- }), ctx.signal);
590
- // ⚠️ 只解析【答案行】("A1: …")——回执里 Q 行带着问题原文,
591
- // 工作流的 pause 提示语若含「停止」字样(如 plan.yaml 的说明文字),
592
- // 看整个 content 会把用户选的「继续」误判成停止(曾是真事故)。
593
- const answerText = r.ok
594
- ? r.content
595
- .split('\n')
596
- .map((l) => l.trim())
597
- .filter((l) => /^A\d+[::]/.test(l))
598
- .join('\n')
599
- : '';
600
- if (!r.ok || answerText.includes('停止')) {
601
- approved = false;
602
- }
603
- else if (answerText.includes('继续')) {
604
- approved = true;
605
- }
606
- else {
607
- // 自由输入(「其他」逃生项):用户想先调整 → 不放行,把原话带进 halt 文案
608
- freeText = answerText.replace(/^A\d+[::]\s*/, '');
609
- approved = freeTextMode === 'approve';
610
- }
611
- }
612
- catch {
613
- askFailed = true; // 桥不可用 → 放行 + 响亮警告(保持旧"不挂起"行为)
614
- }
615
- if (askFailed) {
616
- yield {
617
- type: 'warning',
618
- message: `⚠ pause "${step.id}" 审批闸门不可用(提问桥异常),已自动放行。提示:${msg}`,
619
- };
620
- }
621
- if (!approved) {
622
- const why = freeText
623
- ? `用户在审批闸门 "${step.id}" 给出指示「${truncate(freeText, 120)}」——未放行(现场已保留,按指示调整后重跑同目标可从断点续跑)`
624
- : `用户在审批闸门 "${step.id}" 选择停止(现场已保留,重跑同目标可从断点续跑)`;
625
- await state.appendProgress(`■ ${step.id}: ${why}`);
626
- if (cp)
627
- await state.recordFailure(step.id, why);
628
- yield { type: 'workflow_step_end', id: step.id, ok: false, error: why };
629
- yield { type: 'error', error: `Workflow halted at step "${step.id}": ${why}` };
630
- return 'halted';
631
- }
632
- // 带补充要求的放行:自由输入并入贯穿目标(后续每步都看到,不止 capture 变量)
633
- if (freeText)
634
- await addGoalSupplement(ctx, state, freeText);
635
- const pauseResult = {
636
- raw: {
637
- result: freeText,
638
- text: freeText,
639
- approved: true,
640
- free_text: freeText,
641
- },
642
- preview: freeText
643
- ? `审批通过:继续执行;补充要求:${truncate(freeText, 120)}`
644
- : '审批通过:继续执行',
645
- };
646
- if (step.capture)
647
- bindCapture(step.capture, pauseResult, vars);
648
- await state.writeVars(vars.snapshot());
649
- if (cp) {
650
- await state.recordCheckpoint(cp.name(step.id), pauseResult.preview, collectCaptured(step.capture, vars), vars.snapshot());
651
- }
652
- await state.appendProgress(`✓ ${step.id} (pause: 已确认继续)`);
653
- yield {
654
- type: 'workflow_step_end',
655
- id: step.id,
656
- ok: true,
657
- output: pauseResult.preview,
658
- };
659
- continue;
273
+ else if (step.type === 'pause') {
274
+ outcome = yield* execPauseStep(step, vars, controlDeps);
660
275
  }
661
- // ---------- 控制流:break / continue(循环信号) ----------
662
- //
663
- // 仅在 loop 的 do[] 内有意义。break = 跳出循环;continue = 跳到下一轮迭代。
664
- // 缺省作用于最近一层循环;带 target 时穿透中间层循环直达指定 id 的 loop
665
- //(如审批反馈是方向性变化时,从审批小循环 continue 到外层 plan_loop 重启规划)。
666
- // 通常配合 when 条件使用;游离信号(无外层循环 / target 不存在)冒泡到顶层
667
- // 按正常完成处理,绝不当 halt。
668
- if (step.type === 'break' || step.type === 'continue') {
669
- let shouldFire = true;
670
- if (step.when) {
671
- try {
672
- shouldFire = Boolean(evalExpr(step.when, vars));
673
- }
674
- catch (e) {
675
- // when 表达式错误 → 不触发,当 skipped 处理
676
- shouldFire = false;
677
- }
678
- }
679
- if (shouldFire) {
680
- const label = `${step.type}${step.target ? ` → ${step.target}` : ''}`;
681
- await state.appendProgress(`↵ ${step.id}: ${label}`);
682
- yield { type: 'workflow_step_end', id: step.id, ok: true, output: label };
683
- return { signal: step.type, target: step.target };
684
- }
685
- // 条件不满足 → 当 skip 处理,继续下一步
686
- yield {
687
- type: 'workflow_step_skipped',
688
- id: step.id,
689
- reason: `${step.type} 条件不满足`,
690
- };
691
- continue;
276
+ else if (step.type === 'break' || step.type === 'continue') {
277
+ outcome = yield* execSignalStep(step, controlDeps);
692
278
  }
693
- // ---------- 控制流:echo(确定性输出,不烧 LLM) ----------
694
- //
695
- // 把 message 插值后作为 text 事件输出给用户。适合工作流末尾的完成报告:
696
- // 产物/裁决已在变量里时直接插值,省一次 LLM 调用且零幻觉。
697
- if (step.type === 'echo') {
698
- const missing = [];
699
- const text = interpolate(step.message ?? '', vars, {
700
- onMissing: (e) => missing.push(e),
701
- });
702
- if (missing.length > 0) {
703
- yield {
704
- type: 'warning',
705
- message: `step "${step.id}": 模板变量未定义,已按空串插值:${[...new Set(missing)].map((m) => `\${${m}}`).join('、')}`,
706
- };
707
- }
708
- yield { type: 'text', delta: text.endsWith('\n') ? text : `${text}\n` };
709
- await state.appendProgress(`✓ ${step.id} (echo)`);
710
- if (cp) {
711
- await state.recordCheckpoint(cp.name(step.id), truncate(text, 200), {}, vars.snapshot());
712
- }
713
- yield {
714
- type: 'workflow_step_end',
715
- id: step.id,
716
- ok: true,
717
- output: truncate(text, 200),
718
- };
279
+ if (outcome) {
280
+ if (outcome.kind === 'halted')
281
+ return 'halted';
282
+ if (outcome.kind === 'flow')
283
+ return outcome.signal;
719
284
  continue;
720
285
  }
721
- // ---------- 普通动作 step:tool / llm / skill / agent / assert / verify ----------
286
+ // ---------- 动作步:tool / llm / skill / agent / assert / verify / set / echo ----------
722
287
  //
723
- // in-run retry(self_heal):失败先确定性重跑同一步,至多 maxRetries 次(retry 先于
724
- // onError:recover)。maxRetries = step.max_retries ?? def.self_heal.max_retries(runWorkflow
725
- // 注入到 ctx.selfHealMaxRetries)?? 0。语义:max_retries 是「额外重试次数」,总尝试 = 1 + maxRetries。
726
- const maxRetries = step.max_retries ?? ctx.selfHealMaxRetries ?? 0;
727
- let attempt = 0;
728
- while (true) {
729
- try {
730
- const result = yield* dispatchActionStep(step, vars, ctx);
731
- if (step.capture)
732
- bindCapture(step.capture, result, vars);
733
- // goal_supplement:本步产出作为用户补充要求贯穿后续每一步(如 stage0 澄清结论)
734
- if (step.goal_supplement) {
735
- const t = typeof result.raw.text === 'string' && result.raw.text.trim()
736
- ? result.raw.text
737
- : String(result.raw.result ?? result.preview ?? '');
738
- await addGoalSupplement(ctx, state, t);
739
- }
740
- await state.appendProgress(`✓ ${step.id} (${kind})`);
741
- await state.writeVars(vars.snapshot());
742
- // checkpoint:记录范围见 CpScope(顶层步 + 顶层 loop 的 do[] 一层)。
743
- if (cp) {
744
- await state.recordCheckpoint(cp.name(step.id), result.preview, collectCaptured(step.capture, vars), vars.snapshot());
745
- }
746
- yield {
747
- type: 'workflow_step_end',
748
- id: step.id,
749
- ok: true,
750
- output: result.preview,
751
- };
752
- break; // 成功 → 退出 retry 循环,进入下一步
753
- }
754
- catch (e) {
755
- const errMsg = e.message ?? String(e);
756
- // in-run retry:还有重试额度且未中断 → 重跑同一步(不动 onError)。
757
- if (attempt < maxRetries && !ctx.signal?.aborted) {
758
- attempt++;
759
- // 退避等待(线性):普通 LLM/网络类错误用 retry_delay_ms。
760
- // maxTurns 截断默认保持旧行为立即续跑;长文档/PPT 类工作流可显式配置
761
- // max_turns_retry_delay_ms,在模型端高峰期给下一次续作留出恢复窗口。
762
- const isMaxTurns = isMaxTurnsCutoff(errMsg);
763
- const delayBase = isMaxTurns
764
- ? (ctx.maxTurnsRetryDelayMs ?? 0)
765
- : (ctx.retryDelayMs ?? 0);
766
- const delayMs = delayBase * attempt;
767
- const retryKind = isMaxTurns ? '轮数预算用尽,可续跑截断' : '失败';
768
- const retryProgress = isMaxTurns
769
- ? `↻ ${step.id}: 阶段轮数预算用尽,正在续跑 ${attempt}/${maxRetries}${delayMs > 0 ? `(${delayMs}ms 后)` : ''}`
770
- : `↻ ${step.id}: ${retryKind},重试 ${attempt}/${maxRetries}${delayMs > 0 ? `(${delayMs}ms 后)` : ''}(${errMsg})`;
771
- await state.appendProgress(retryProgress);
772
- yield {
773
- type: 'warning',
774
- message: isMaxTurns
775
- ? `step "${step.id}" 阶段轮数预算用尽,正在续跑 ${attempt}/${maxRetries}${delayMs > 0 ? `(${delayMs}ms 后)` : ''}…`
776
- : `step "${step.id}" 失败:${errMsg};${delayMs > 0 ? `${delayMs}ms 后` : ''}重试 ${attempt}/${maxRetries}…`,
777
- };
778
- yield {
779
- type: 'plugin_progress',
780
- pluginId: WF_PLUGIN_ID,
781
- current: i + 1,
782
- max: steps.length,
783
- message: isMaxTurns
784
- ? `↻ ${step.id} 阶段轮数预算用尽,正在续跑 ${attempt}/${maxRetries}…`
785
- : `↻ 重试 ${step.id} ${attempt}/${maxRetries}…`,
786
- };
787
- if (delayMs > 0)
788
- await sleepInterruptible(delayMs, ctx.signal);
789
- continue; // 重跑同一步
790
- }
791
- await state.appendProgress(`✗ ${step.id}: ${errMsg}`);
792
- // onError:'recover' —— retry 用尽仍失败,交给有界恢复 agent 随机应变把这一步救回来。
793
- // 「确定性工作流 × TAOR」的结合点:happy path 走刚性 DAG,撞上预料之外的就地
794
- // 降级为 agent 自适应;救回则继续 happy path,救不回才 honest halt。
795
- if (step.onError === 'recover') {
796
- yield {
797
- type: 'warning',
798
- message: `step "${step.id}" 失败:${errMsg};尝试自适应恢复…`,
799
- };
800
- yield {
801
- type: 'plugin_progress',
802
- pluginId: WF_PLUGIN_ID,
803
- current: i + 1,
804
- max: steps.length,
805
- message: `↻ 恢复 ${step.id}…`,
806
- };
807
- const rec = yield* runRecovery(step, vars, ctx, errMsg);
808
- if (ctx.signal?.aborted) {
809
- yield { type: 'interrupted' };
810
- return 'halted';
811
- }
812
- // 验证闸门:只有恢复 agent 真正交付(rec.ok)才绑 capture 走下去;
813
- // 显式放弃 / 产出不可信(rec.ok=false)则带原因 honest halt,绝不盲绑脏值。
814
- if (rec.ok) {
815
- if (step.capture)
816
- bindCapture(step.capture, rec.value, vars);
817
- await state.appendProgress(`⚠→✓ ${step.id} recovered`);
818
- await state.writeVars(vars.snapshot());
819
- if (cp) {
820
- await state.recordCheckpoint(cp.name(step.id), rec.value.preview, collectCaptured(step.capture, vars), vars.snapshot());
821
- }
822
- yield {
823
- type: 'workflow_step_end',
824
- id: step.id,
825
- ok: true,
826
- output: rec.value.preview,
827
- };
828
- break; // 救回 → 进入下一步
829
- }
830
- const combined = `${errMsg}(自适应恢复未成功:${rec.reason})`;
831
- if (cp)
832
- await state.recordFailure(step.id, combined);
833
- yield {
834
- type: 'workflow_step_end',
835
- id: step.id,
836
- ok: false,
837
- error: combined,
838
- };
839
- yield {
840
- type: 'error',
841
- error: `Workflow halted at step "${step.id}": ${combined}`,
842
- };
843
- return 'halted';
844
- }
845
- if (step.onError === 'continue') {
846
- // 降级时给 capture 变量绑【明确占位文本】而非留空:空串静默内插进下游
847
- // prompt(如「已知需求分析:\n\n」)是更大的最小惊讶违例——模型看到
848
- // 空段会困惑。占位前缀「(上游步骤 」机器可识别;continue 本就是作者
849
- // 显式选择「无此内容也继续」,需要严格闸门的步骤应配 onError:halt 或 assert。
850
- if (step.capture) {
851
- const placeholder = `(上游步骤 ${step.id} 失败已降级(onError:continue),无内容——` +
852
- `请基于用户原始目标自行推断。错误:${errMsg.length > 120 ? `${errMsg.slice(0, 120)}…` : errMsg})`;
853
- for (const varName of Object.values(normalizeCapture(step.capture))) {
854
- vars.setGlobal(varName, placeholder);
855
- }
856
- }
857
- // 降级此前完全静默(仅 progress.md 有 ✗ 行)——补一条 warning 可见化
858
- yield {
859
- type: 'warning',
860
- message: `step "${step.id}" 失败已降级(onError:continue):${errMsg}`,
861
- };
862
- yield {
863
- type: 'workflow_step_end',
864
- id: step.id,
865
- ok: false,
866
- error: errMsg,
867
- };
868
- break; // 跳过本步 → 进入下一步
869
- }
870
- const finalErrMsg = isMaxTurnsCutoff(errMsg)
871
- ? `${errMsg};重试额度已用尽,请拆分该步骤或提高 maxTurns。`
872
- : errMsg;
873
- if (cp)
874
- await state.recordFailure(step.id, finalErrMsg);
875
- yield {
876
- type: 'workflow_step_end',
877
- id: step.id,
878
- ok: false,
879
- error: finalErrMsg,
880
- };
881
- yield {
882
- type: 'error',
883
- error: `Workflow halted at step "${step.id}": ${finalErrMsg}`,
884
- };
885
- return 'halted';
886
- }
887
- }
288
+ // 统一走 runStepWithRetry:in-run 重试 → onError 三态 → commitStep 提交。
289
+ // 「提交一步」只有 stepPipeline 那一份实现,这里不再自己写(见 stepPipeline 头注)。
290
+ const actionOutcome = yield* runStepWithRetry({
291
+ step,
292
+ kind,
293
+ vars,
294
+ ctx,
295
+ state,
296
+ ...(cp ? { cp } : {}),
297
+ index: i,
298
+ total: steps.length,
299
+ addGoalSupplement: (text) => addGoalSupplement(ctx, state, text),
300
+ execute: () => dispatchActionStep(step, vars, ctx),
301
+ });
302
+ if (actionOutcome === 'halted')
303
+ return 'halted';
888
304
  }
889
305
  return 'completed';
890
306
  }