minimal-agent 0.8.2 → 0.10.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 (484) hide show
  1. package/README.md +14 -12
  2. package/package.json +34 -1
  3. package/plugins/cron-runner/.claude-plugin/plugin.json +5 -0
  4. package/plugins/cron-runner/commands/cron.md +26 -0
  5. package/plugins/cron-runner/commands/crons.md +7 -0
  6. package/plugins/cron-runner/plugin.js +34 -0
  7. package/plugins/cron-runner/src/commands.js +348 -0
  8. package/plugins/cron-runner/src/cronTime.js +148 -0
  9. package/plugins/cron-runner/src/lock.js +68 -0
  10. package/plugins/cron-runner/src/runs.js +89 -0
  11. package/plugins/cron-runner/src/scheduleEngine.js +72 -0
  12. package/plugins/cron-runner/src/scheduler.js +355 -0
  13. package/plugins/cron-runner/src/schema.js +140 -0
  14. package/plugins/cron-runner/src/service.js +167 -0
  15. package/plugins/cron-runner/src/store.js +298 -0
  16. package/plugins/cron-runner/src/types.js +1 -0
  17. package/plugins/workflow-runner/commands/workflow.md +4 -4
  18. package/plugins/workflow-runner/commands/workflows.md +1 -1
  19. package/plugins/workflow-runner/plugin.js +3 -1
  20. package/plugins/workflow-runner/src/agentCommon.js +249 -51
  21. package/plugins/workflow-runner/src/dslManifest.js +72 -0
  22. package/plugins/workflow-runner/src/expressions.js +198 -15
  23. package/plugins/workflow-runner/src/index.js +40 -8
  24. package/plugins/workflow-runner/src/jsonPayload.js +1 -1
  25. package/plugins/workflow-runner/src/loader.js +286 -72
  26. package/plugins/workflow-runner/src/multimodal.js +37 -0
  27. package/plugins/workflow-runner/src/recovery.js +49 -39
  28. package/plugins/workflow-runner/src/runner.js +802 -287
  29. package/plugins/workflow-runner/src/stepExecutors/agent.js +157 -39
  30. package/plugins/workflow-runner/src/stepExecutors/assert.js +16 -3
  31. package/plugins/workflow-runner/src/stepExecutors/llm.js +60 -111
  32. package/plugins/workflow-runner/src/stepExecutors/skill.js +47 -47
  33. package/plugins/workflow-runner/src/stepExecutors/tool.js +76 -10
  34. package/plugins/workflow-runner/src/stepExecutors/verify.js +27 -10
  35. package/plugins/workflow-runner/src/taskIdentity.js +296 -0
  36. package/plugins/workflow-runner/src/types.js +19 -9
  37. package/plugins/workflow-runner/src/workflowState.js +516 -33
  38. package/skills/adversarial-review/SKILL.md +0 -1
  39. package/skills/ai-image/SKILL.md +89 -0
  40. package/skills/ai-image/scripts/generate.mjs +497 -0
  41. package/skills/ai-image/scripts/generate_from_plan.mjs +469 -0
  42. package/skills/ai-image/scripts/generate_from_plan_test.mjs +223 -0
  43. package/skills/ai-image/scripts/generate_test.mjs +74 -0
  44. package/skills/browser-automation/SKILL.md +47 -0
  45. package/skills/docx/SKILL.md +25 -3
  46. package/skills/docx/scripts/office/helpers/__pycache__/__init__.cpython-314.pyc +0 -0
  47. package/skills/docx/scripts/office/helpers/__pycache__/merge_runs.cpython-314.pyc +0 -0
  48. package/skills/docx/scripts/office/helpers/__pycache__/simplify_redlines.cpython-314.pyc +0 -0
  49. package/skills/docx/scripts/office/validators/__pycache__/__init__.cpython-314.pyc +0 -0
  50. package/skills/docx/scripts/office/validators/__pycache__/base.cpython-314.pyc +0 -0
  51. package/skills/docx/scripts/office/validators/__pycache__/docx.cpython-314.pyc +0 -0
  52. package/skills/docx/scripts/office/validators/__pycache__/pptx.cpython-314.pyc +0 -0
  53. package/skills/docx/scripts/office/validators/__pycache__/redlining.cpython-314.pyc +0 -0
  54. package/skills/docx/scripts/opc_check.py +253 -0
  55. package/skills/image-gen-openrouter/SKILL.md +10 -2
  56. package/skills/image-stats/SKILL.md +65 -0
  57. package/skills/image-stats/scripts/__pycache__/image_stats.cpython-314.pyc +0 -0
  58. package/skills/image-stats/scripts/__pycache__/image_stats_test.cpython-314.pyc +0 -0
  59. package/skills/image-stats/scripts/image_stats.py +368 -0
  60. package/skills/image-stats/scripts/image_stats_test.py +181 -0
  61. package/skills/pptx/SKILL.md +123 -95
  62. package/skills/pptx/design.md +82 -0
  63. package/skills/pptx/examples/README.md +29 -0
  64. package/skills/pptx/examples/hybrid_example.mjs +107 -0
  65. package/skills/pptx/examples/hybrid_example.pptx +0 -0
  66. package/skills/pptx/pptxgenjs.md +41 -0
  67. package/skills/pptx/scripts/deck_helpers.mjs +224 -0
  68. package/skills/pptx/scripts/layout_lint.py +293 -0
  69. package/skills/pptx/scripts/layout_lint_test.py +160 -0
  70. package/skills/pptx/scripts/opc_check.py +339 -0
  71. package/skills/pptx/scripts/svg_fallback.mjs +145 -0
  72. package/skills/pptx/svg.md +67 -0
  73. package/skills/tts-zh/SKILL.md +19 -2
  74. package/skills/ui-design/SKILL.md +158 -0
  75. package/skills/ui-design/claude_design.md +309 -0
  76. package/skills/ui-design/evals/evals.json +254 -0
  77. package/skills/ui-design/references/aesthetic-direction.md +83 -0
  78. package/skills/ui-design/references/claude-design-protocol.md +36 -0
  79. package/skills/ui-design/references/design-context.md +53 -0
  80. package/skills/ui-design/references/design-execution-playbook.md +82 -0
  81. package/skills/ui-design/references/export-pptx.md +124 -0
  82. package/skills/ui-design/references/layout-composition.md +654 -0
  83. package/skills/ui-design/references/preview-feedback.md +37 -0
  84. package/skills/ui-design/references/source-image-layout.md +189 -0
  85. package/skills/ui-design/references/starter-components.md +78 -0
  86. package/skills/ui-design/references/vi-aegis-vector.md +144 -0
  87. package/skills/ui-design/references/vi-amber-prism.md +133 -0
  88. package/skills/ui-design/references/vi-aqua-glass.md +200 -0
  89. package/skills/ui-design/references/vi-comfort-vision.md +147 -0
  90. package/skills/ui-design/references/vi-ember-prism.md +140 -0
  91. package/skills/ui-design/references/vi-nebula-console.md +151 -0
  92. package/skills/ui-design/references/vi-obsidian-optics.md +139 -0
  93. package/skills/ui-design/references/vi-obsidian-xui.md +143 -0
  94. package/skills/ui-design/references/vi-precision-horizon-dark.md +117 -0
  95. package/skills/ui-design/references/vi-precision-horizon.md +215 -0
  96. package/skills/ui-design/references/vi-solar-relay.md +130 -0
  97. package/skills/ui-design/references/vi-template-workflow.md +48 -0
  98. package/skills/ui-design/references/vi-verdant-grid.md +141 -0
  99. package/skills/ui-design/references/vi-violet-meridian.md +172 -0
  100. package/skills/ui-design/scripts/__pycache__/assemble_material.cpython-314.pyc +0 -0
  101. package/skills/ui-design/scripts/__pycache__/assemble_material_test.cpython-314.pyc +0 -0
  102. package/skills/ui-design/scripts/__pycache__/chart_sanity_check.cpython-314.pyc +0 -0
  103. package/skills/ui-design/scripts/__pycache__/check_language_policy.cpython-314.pyc +0 -0
  104. package/skills/ui-design/scripts/__pycache__/check_ui_artifact.cpython-314.pyc +0 -0
  105. package/skills/ui-design/scripts/__pycache__/check_ui_artifact_pptx_test.cpython-314.pyc +0 -0
  106. package/skills/ui-design/scripts/__pycache__/deck_pages_check.cpython-314.pyc +0 -0
  107. package/skills/ui-design/scripts/__pycache__/deck_pages_check_test.cpython-314.pyc +0 -0
  108. package/skills/ui-design/scripts/__pycache__/deck_visual_style_check.cpython-314.pyc +0 -0
  109. package/skills/ui-design/scripts/__pycache__/deck_visual_style_check_test.cpython-314.pyc +0 -0
  110. package/skills/ui-design/scripts/__pycache__/design_budget_check.cpython-314.pyc +0 -0
  111. package/skills/ui-design/scripts/__pycache__/design_budget_check_test.cpython-314.pyc +0 -0
  112. package/skills/ui-design/scripts/__pycache__/design_repair_hints.cpython-314.pyc +0 -0
  113. package/skills/ui-design/scripts/__pycache__/design_repair_hints_test.cpython-314.pyc +0 -0
  114. package/skills/ui-design/scripts/__pycache__/design_style_check.cpython-314.pyc +0 -0
  115. package/skills/ui-design/scripts/__pycache__/design_style_check_test.cpython-314.pyc +0 -0
  116. package/skills/ui-design/scripts/__pycache__/draft_coverage_check.cpython-314.pyc +0 -0
  117. package/skills/ui-design/scripts/__pycache__/extract_source_assets.cpython-314.pyc +0 -0
  118. package/skills/ui-design/scripts/__pycache__/extract_source_assets_test.cpython-314.pyc +0 -0
  119. package/skills/ui-design/scripts/__pycache__/generated_image_review_collect.cpython-314.pyc +0 -0
  120. package/skills/ui-design/scripts/__pycache__/generated_image_review_collect_test.cpython-314.pyc +0 -0
  121. package/skills/ui-design/scripts/__pycache__/image_plan_lint.cpython-314.pyc +0 -0
  122. package/skills/ui-design/scripts/__pycache__/image_plan_lint_test.cpython-314.pyc +0 -0
  123. package/skills/ui-design/scripts/__pycache__/image_prompt_rewrite.cpython-314.pyc +0 -0
  124. package/skills/ui-design/scripts/__pycache__/image_prompt_rewrite_test.cpython-314.pyc +0 -0
  125. package/skills/ui-design/scripts/__pycache__/layout_balance.cpython-314.pyc +0 -0
  126. package/skills/ui-design/scripts/__pycache__/layout_balance_test.cpython-314.pyc +0 -0
  127. package/skills/ui-design/scripts/__pycache__/normalize_design_json.cpython-314.pyc +0 -0
  128. package/skills/ui-design/scripts/__pycache__/normalize_design_json_test.cpython-314.pyc +0 -0
  129. package/skills/ui-design/scripts/__pycache__/plan_source_layout.cpython-314.pyc +0 -0
  130. package/skills/ui-design/scripts/__pycache__/plan_source_layout_test.cpython-314.pyc +0 -0
  131. package/skills/ui-design/scripts/__pycache__/resolve_source_hint.cpython-314.pyc +0 -0
  132. package/skills/ui-design/scripts/__pycache__/resolve_source_hint_test.cpython-314.pyc +0 -0
  133. package/skills/ui-design/scripts/__pycache__/resolve_source_input.cpython-314.pyc +0 -0
  134. package/skills/ui-design/scripts/__pycache__/resolve_source_input_test.cpython-314.pyc +0 -0
  135. package/skills/ui-design/scripts/__pycache__/scaffold_ui_artifact.cpython-314.pyc +0 -0
  136. package/skills/ui-design/scripts/__pycache__/source_usage_check.cpython-314.pyc +0 -0
  137. package/skills/ui-design/scripts/__pycache__/source_usage_check_test.cpython-314.pyc +0 -0
  138. package/skills/ui-design/scripts/__pycache__/text_readability_pixel.cpython-314.pyc +0 -0
  139. package/skills/ui-design/scripts/__pycache__/visual_quality_gate.cpython-314.pyc +0 -0
  140. package/skills/ui-design/scripts/__pycache__/visual_quality_gate_test.cpython-314.pyc +0 -0
  141. package/skills/ui-design/scripts/assemble_material.py +217 -0
  142. package/skills/ui-design/scripts/assemble_material_test.py +149 -0
  143. package/skills/ui-design/scripts/browser_check_ui_artifact.mjs +221 -0
  144. package/skills/ui-design/scripts/chart_sanity_check.py +261 -0
  145. package/skills/ui-design/scripts/chart_sanity_check_test.py +165 -0
  146. package/skills/ui-design/scripts/check_language_policy.py +351 -0
  147. package/skills/ui-design/scripts/check_ui_artifact.py +435 -0
  148. package/skills/ui-design/scripts/check_ui_artifact_pptx_test.py +251 -0
  149. package/skills/ui-design/scripts/deck_pages_check.py +158 -0
  150. package/skills/ui-design/scripts/deck_pages_check_test.py +134 -0
  151. package/skills/ui-design/scripts/deck_visual_style_check.py +767 -0
  152. package/skills/ui-design/scripts/deck_visual_style_check_test.py +713 -0
  153. package/skills/ui-design/scripts/design_budget_check.py +271 -0
  154. package/skills/ui-design/scripts/design_budget_check_test.py +183 -0
  155. package/skills/ui-design/scripts/design_repair_hints.py +162 -0
  156. package/skills/ui-design/scripts/design_repair_hints_test.py +54 -0
  157. package/skills/ui-design/scripts/design_style_check.py +397 -0
  158. package/skills/ui-design/scripts/design_style_check_test.py +233 -0
  159. package/skills/ui-design/scripts/draft_coverage_check.py +184 -0
  160. package/skills/ui-design/scripts/draft_coverage_check_test.py +131 -0
  161. package/skills/ui-design/scripts/export_pptx_from_html.mjs +2367 -0
  162. package/skills/ui-design/scripts/extract_source_assets.py +607 -0
  163. package/skills/ui-design/scripts/extract_source_assets_test.py +256 -0
  164. package/skills/ui-design/scripts/generated_image_review_collect.py +206 -0
  165. package/skills/ui-design/scripts/generated_image_review_collect_test.py +91 -0
  166. package/skills/ui-design/scripts/image_plan_lint.py +571 -0
  167. package/skills/ui-design/scripts/image_plan_lint_test.py +515 -0
  168. package/skills/ui-design/scripts/image_prompt_rewrite.py +713 -0
  169. package/skills/ui-design/scripts/image_prompt_rewrite_test.py +966 -0
  170. package/skills/ui-design/scripts/layout_balance.py +881 -0
  171. package/skills/ui-design/scripts/layout_balance_test.py +927 -0
  172. package/skills/ui-design/scripts/normalize_design_json.py +272 -0
  173. package/skills/ui-design/scripts/normalize_design_json_test.py +119 -0
  174. package/skills/ui-design/scripts/plan_source_layout.py +1450 -0
  175. package/skills/ui-design/scripts/plan_source_layout_test.py +1136 -0
  176. package/skills/ui-design/scripts/resolve_source_input.py +270 -0
  177. package/skills/ui-design/scripts/resolve_source_input_test.py +98 -0
  178. package/skills/ui-design/scripts/scaffold_ui_artifact.py +377 -0
  179. package/skills/ui-design/scripts/source_usage_check.py +581 -0
  180. package/skills/ui-design/scripts/source_usage_check_test.py +427 -0
  181. package/skills/ui-design/scripts/text_readability_pixel.py +340 -0
  182. package/skills/ui-design/scripts/text_readability_pixel_test.py +219 -0
  183. package/skills/ui-design/scripts/visual_quality_gate.py +301 -0
  184. package/skills/ui-design/scripts/visual_quality_gate_test.py +233 -0
  185. package/skills/ui-design/tests/e2e/prompts/deck.md +19 -0
  186. package/skills/ui-design/tests/e2e/prompts/webpage.md +17 -0
  187. package/skills/ui-design/tests/e2e/render_check.mjs +101 -0
  188. package/skills/ui-design/tests/e2e/rubric.json +54 -0
  189. package/skills/ui-design/tests/e2e/runs/aegis-vector-preview-1/checks/browser-check.json +49 -0
  190. package/skills/ui-design/tests/e2e/runs/aegis-vector-preview-1/checks/language-check.txt +12 -0
  191. package/skills/ui-design/tests/e2e/runs/aegis-vector-preview-1/checks/static-check.txt +15 -0
  192. package/skills/ui-design/tests/e2e/runs/aegis-vector-preview-1/outputs/index.html +837 -0
  193. package/skills/ui-design/tests/e2e/runs/aegis-vector-preview-1/prompt.md +12 -0
  194. package/skills/ui-design/tests/e2e/runs/aegis-vector-preview-1/review.md +21 -0
  195. package/skills/ui-design/tests/e2e/runs/aegis-vector-preview-1/screenshots/desktop.png +0 -0
  196. package/skills/ui-design/tests/e2e/runs/aegis-vector-preview-1/screenshots/mobile.png +0 -0
  197. package/skills/ui-design/tests/e2e/runs/amber-prism-screenshot-check-1/outputs/homepage.html +373 -0
  198. package/skills/ui-design/tests/e2e/runs/amber-prism-screenshot-check-1/outputs/platform.html +378 -0
  199. package/skills/ui-design/tests/e2e/runs/amber-prism-screenshot-check-1/prompt.md +13 -0
  200. package/skills/ui-design/tests/e2e/runs/amber-prism-screenshot-check-1/review.md +26 -0
  201. package/skills/ui-design/tests/e2e/runs/amber-prism-screenshot-check-1/screenshots/homepage/browser-check.json +49 -0
  202. package/skills/ui-design/tests/e2e/runs/amber-prism-screenshot-check-1/screenshots/homepage/desktop.png +0 -0
  203. package/skills/ui-design/tests/e2e/runs/amber-prism-screenshot-check-1/screenshots/homepage/mobile.png +0 -0
  204. package/skills/ui-design/tests/e2e/runs/amber-prism-screenshot-check-1/screenshots/platform/browser-check.json +49 -0
  205. package/skills/ui-design/tests/e2e/runs/amber-prism-screenshot-check-1/screenshots/platform/desktop.png +0 -0
  206. package/skills/ui-design/tests/e2e/runs/amber-prism-screenshot-check-1/screenshots/platform/mobile.png +0 -0
  207. package/skills/ui-design/tests/e2e/runs/aqua-glass-iteration-1/checks/static-check.txt +2 -0
  208. package/skills/ui-design/tests/e2e/runs/aqua-glass-iteration-1/outputs/index.html +656 -0
  209. package/skills/ui-design/tests/e2e/runs/aqua-glass-iteration-1/prompt.md +12 -0
  210. package/skills/ui-design/tests/e2e/runs/aqua-glass-iteration-1/render_check.mjs +67 -0
  211. package/skills/ui-design/tests/e2e/runs/aqua-glass-iteration-2/checks/static-check.txt +2 -0
  212. package/skills/ui-design/tests/e2e/runs/aqua-glass-iteration-2/outputs/index.html +587 -0
  213. package/skills/ui-design/tests/e2e/runs/aqua-glass-iteration-2/prompt.md +12 -0
  214. package/skills/ui-design/tests/e2e/runs/aqua-glass-iteration-2/review.md +36 -0
  215. package/skills/ui-design/tests/e2e/runs/aqua-glass-volcano-refresh-1/checks/browser-check.json +49 -0
  216. package/skills/ui-design/tests/e2e/runs/aqua-glass-volcano-refresh-1/checks/language-check.txt +8 -0
  217. package/skills/ui-design/tests/e2e/runs/aqua-glass-volcano-refresh-1/checks/static-check.txt +16 -0
  218. package/skills/ui-design/tests/e2e/runs/aqua-glass-volcano-refresh-1/outputs/index.html +739 -0
  219. package/skills/ui-design/tests/e2e/runs/aqua-glass-volcano-refresh-1/prompt.md +11 -0
  220. package/skills/ui-design/tests/e2e/runs/aqua-glass-volcano-refresh-1/review.md +22 -0
  221. package/skills/ui-design/tests/e2e/runs/aqua-glass-volcano-refresh-1/screenshots/desktop.png +0 -0
  222. package/skills/ui-design/tests/e2e/runs/aqua-glass-volcano-refresh-1/screenshots/mobile.png +0 -0
  223. package/skills/ui-design/tests/e2e/runs/comfort-vision-preview-1/checks/browser-check.json +52 -0
  224. package/skills/ui-design/tests/e2e/runs/comfort-vision-preview-1/outputs/index.html +641 -0
  225. package/skills/ui-design/tests/e2e/runs/comfort-vision-preview-1/screenshots/desktop.png +0 -0
  226. package/skills/ui-design/tests/e2e/runs/comfort-vision-preview-1/screenshots/mobile.png +0 -0
  227. package/skills/ui-design/tests/e2e/runs/ember-prism-red-preview-1/outputs/index.html +467 -0
  228. package/skills/ui-design/tests/e2e/runs/ember-prism-red-preview-1/screenshots/browser-check.json +49 -0
  229. package/skills/ui-design/tests/e2e/runs/ember-prism-red-preview-1/screenshots/desktop.png +0 -0
  230. package/skills/ui-design/tests/e2e/runs/ember-prism-red-preview-1/screenshots/mobile.png +0 -0
  231. package/skills/ui-design/tests/e2e/runs/ember-prism-screenshot-check-1/outputs/homepage.html +373 -0
  232. package/skills/ui-design/tests/e2e/runs/ember-prism-screenshot-check-1/outputs/platform.html +378 -0
  233. package/skills/ui-design/tests/e2e/runs/ember-prism-screenshot-check-1/prompt.md +13 -0
  234. package/skills/ui-design/tests/e2e/runs/ember-prism-screenshot-check-1/review.md +26 -0
  235. package/skills/ui-design/tests/e2e/runs/ember-prism-screenshot-check-1/screenshots/homepage/browser-check.json +49 -0
  236. package/skills/ui-design/tests/e2e/runs/ember-prism-screenshot-check-1/screenshots/homepage/desktop.png +0 -0
  237. package/skills/ui-design/tests/e2e/runs/ember-prism-screenshot-check-1/screenshots/homepage/mobile.png +0 -0
  238. package/skills/ui-design/tests/e2e/runs/ember-prism-screenshot-check-1/screenshots/platform/browser-check.json +49 -0
  239. package/skills/ui-design/tests/e2e/runs/ember-prism-screenshot-check-1/screenshots/platform/desktop.png +0 -0
  240. package/skills/ui-design/tests/e2e/runs/ember-prism-screenshot-check-1/screenshots/platform/mobile.png +0 -0
  241. package/skills/ui-design/tests/e2e/runs/empty-agent-protocol-check/checks/browser-check.json +49 -0
  242. package/skills/ui-design/tests/e2e/runs/empty-agent-protocol-check/checks/language-check.txt +4 -0
  243. package/skills/ui-design/tests/e2e/runs/empty-agent-protocol-check/checks/static-check.txt +2 -0
  244. package/skills/ui-design/tests/e2e/runs/empty-agent-protocol-check/execution_notes.md +10 -0
  245. package/skills/ui-design/tests/e2e/runs/empty-agent-protocol-check/outputs/index.html +587 -0
  246. package/skills/ui-design/tests/e2e/runs/empty-agent-protocol-check/prompt.md +14 -0
  247. package/skills/ui-design/tests/e2e/runs/empty-agent-protocol-check/review.md +13 -0
  248. package/skills/ui-design/tests/e2e/runs/empty-agent-protocol-check/screenshots/desktop.png +0 -0
  249. package/skills/ui-design/tests/e2e/runs/empty-agent-protocol-check/screenshots/mobile.png +0 -0
  250. package/skills/ui-design/tests/e2e/runs/iteration-1/deck/checks/browser-check.json +24 -0
  251. package/skills/ui-design/tests/e2e/runs/iteration-1/deck/checks/static-check.txt +2 -0
  252. package/skills/ui-design/tests/e2e/runs/iteration-1/deck/execution_notes.md +21 -0
  253. package/skills/ui-design/tests/e2e/runs/iteration-1/deck/outputs/deck.html +451 -0
  254. package/skills/ui-design/tests/e2e/runs/iteration-1/deck/screenshots/slide-01.png +0 -0
  255. package/skills/ui-design/tests/e2e/runs/iteration-1/deck/screenshots/slide-02.png +0 -0
  256. package/skills/ui-design/tests/e2e/runs/iteration-1/review.md +79 -0
  257. package/skills/ui-design/tests/e2e/runs/iteration-1/webpage/checks/browser-check.json +26 -0
  258. package/skills/ui-design/tests/e2e/runs/iteration-1/webpage/checks/static-check.txt +2 -0
  259. package/skills/ui-design/tests/e2e/runs/iteration-1/webpage/execution_notes.md +20 -0
  260. package/skills/ui-design/tests/e2e/runs/iteration-1/webpage/outputs/index.html +534 -0
  261. package/skills/ui-design/tests/e2e/runs/iteration-1/webpage/screenshots/desktop.png +0 -0
  262. package/skills/ui-design/tests/e2e/runs/iteration-1/webpage/screenshots/mobile.png +0 -0
  263. package/skills/ui-design/tests/e2e/runs/nebula-console-iteration-1/checks/browser-check.json +64 -0
  264. package/skills/ui-design/tests/e2e/runs/nebula-console-iteration-1/checks/language-check.txt +7 -0
  265. package/skills/ui-design/tests/e2e/runs/nebula-console-iteration-1/checks/quick-validate.txt +1 -0
  266. package/skills/ui-design/tests/e2e/runs/nebula-console-iteration-1/checks/static-console.txt +2 -0
  267. package/skills/ui-design/tests/e2e/runs/nebula-console-iteration-1/checks/static-homepage.txt +2 -0
  268. package/skills/ui-design/tests/e2e/runs/nebula-console-iteration-1/outputs/console.html +600 -0
  269. package/skills/ui-design/tests/e2e/runs/nebula-console-iteration-1/outputs/homepage.html +556 -0
  270. package/skills/ui-design/tests/e2e/runs/nebula-console-iteration-1/prompt.md +29 -0
  271. package/skills/ui-design/tests/e2e/runs/nebula-console-iteration-1/review.md +57 -0
  272. package/skills/ui-design/tests/e2e/runs/nebula-console-iteration-1/screenshots/console-desktop.png +0 -0
  273. package/skills/ui-design/tests/e2e/runs/nebula-console-iteration-1/screenshots/console-mobile.png +0 -0
  274. package/skills/ui-design/tests/e2e/runs/nebula-console-iteration-1/screenshots/homepage-desktop.png +0 -0
  275. package/skills/ui-design/tests/e2e/runs/nebula-console-iteration-1/screenshots/homepage-mobile.png +0 -0
  276. package/skills/ui-design/tests/e2e/runs/obsidian-optics-iteration-1/checks/browser-check.json +33 -0
  277. package/skills/ui-design/tests/e2e/runs/obsidian-optics-iteration-1/checks/language-check.txt +6 -0
  278. package/skills/ui-design/tests/e2e/runs/obsidian-optics-iteration-1/checks/quick-validate.txt +1 -0
  279. package/skills/ui-design/tests/e2e/runs/obsidian-optics-iteration-1/checks/static-check.txt +2 -0
  280. package/skills/ui-design/tests/e2e/runs/obsidian-optics-iteration-1/outputs/index.html +682 -0
  281. package/skills/ui-design/tests/e2e/runs/obsidian-optics-iteration-1/prompt.md +13 -0
  282. package/skills/ui-design/tests/e2e/runs/obsidian-optics-iteration-1/review.md +59 -0
  283. package/skills/ui-design/tests/e2e/runs/obsidian-optics-iteration-1/screenshots/desktop.png +0 -0
  284. package/skills/ui-design/tests/e2e/runs/obsidian-optics-iteration-1/screenshots/mobile.png +0 -0
  285. package/skills/ui-design/tests/e2e/runs/obsidian-xui-screenshot-check-1/outputs/page-01.html +466 -0
  286. package/skills/ui-design/tests/e2e/runs/obsidian-xui-screenshot-check-1/outputs/page-02.html +433 -0
  287. package/skills/ui-design/tests/e2e/runs/obsidian-xui-screenshot-check-1/prompt.md +13 -0
  288. package/skills/ui-design/tests/e2e/runs/obsidian-xui-screenshot-check-1/screenshots/page-01/browser-check.json +49 -0
  289. package/skills/ui-design/tests/e2e/runs/obsidian-xui-screenshot-check-1/screenshots/page-01/desktop.png +0 -0
  290. package/skills/ui-design/tests/e2e/runs/obsidian-xui-screenshot-check-1/screenshots/page-01/mobile.png +0 -0
  291. package/skills/ui-design/tests/e2e/runs/obsidian-xui-screenshot-check-1/screenshots/page-02/browser-check.json +49 -0
  292. package/skills/ui-design/tests/e2e/runs/obsidian-xui-screenshot-check-1/screenshots/page-02/desktop.png +0 -0
  293. package/skills/ui-design/tests/e2e/runs/obsidian-xui-screenshot-check-1/screenshots/page-02/mobile.png +0 -0
  294. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-1/checks/browser-check.json +49 -0
  295. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-1/checks/language-check.txt +3 -0
  296. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-1/checks/static-check.txt +2 -0
  297. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-1/execution_notes.md +24 -0
  298. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-1/outputs/index.html +723 -0
  299. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-1/prompt.md +15 -0
  300. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-1/review.md +26 -0
  301. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-1/screenshots/desktop.png +0 -0
  302. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-1/screenshots/mobile.png +0 -0
  303. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-2/checks/browser-dark.json +49 -0
  304. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-2/checks/browser-light.json +49 -0
  305. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-2/checks/language-check.txt +11 -0
  306. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-2/checks/static-dark.txt +2 -0
  307. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-2/checks/static-light.txt +3 -0
  308. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-2/execution_notes.md +23 -0
  309. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-2/outputs/dark.html +392 -0
  310. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-2/outputs/light.html +476 -0
  311. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-2/prompt.md +23 -0
  312. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-2/review.md +41 -0
  313. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-2/screenshots/dark/desktop.png +0 -0
  314. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-2/screenshots/dark/mobile.png +0 -0
  315. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-2/screenshots/light/desktop.png +0 -0
  316. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-2/screenshots/light/mobile.png +0 -0
  317. package/skills/ui-design/tests/e2e/runs/solar-relay-iteration-1/checks/browser-check.json +54 -0
  318. package/skills/ui-design/tests/e2e/runs/solar-relay-iteration-1/checks/language-check.txt +5 -0
  319. package/skills/ui-design/tests/e2e/runs/solar-relay-iteration-1/checks/quick-validate.txt +1 -0
  320. package/skills/ui-design/tests/e2e/runs/solar-relay-iteration-1/checks/static-check.txt +2 -0
  321. package/skills/ui-design/tests/e2e/runs/solar-relay-iteration-1/outputs/index.html +765 -0
  322. package/skills/ui-design/tests/e2e/runs/solar-relay-iteration-1/prompt.md +15 -0
  323. package/skills/ui-design/tests/e2e/runs/solar-relay-iteration-1/review.md +59 -0
  324. package/skills/ui-design/tests/e2e/runs/solar-relay-iteration-1/screenshots/desktop.png +0 -0
  325. package/skills/ui-design/tests/e2e/runs/solar-relay-iteration-1/screenshots/mobile.png +0 -0
  326. package/skills/ui-design/tests/e2e/runs/verdant-grid-preview-1/outputs/index.html +508 -0
  327. package/skills/ui-design/tests/e2e/runs/verdant-grid-preview-1/screenshots/browser-check.json +49 -0
  328. package/skills/ui-design/tests/e2e/runs/verdant-grid-preview-1/screenshots/desktop.png +0 -0
  329. package/skills/ui-design/tests/e2e/runs/verdant-grid-preview-1/screenshots/mobile.png +0 -0
  330. package/skills/ui-design/tests/e2e/runs/violet-meridian-iteration-1/checks/browser-check.json +54 -0
  331. package/skills/ui-design/tests/e2e/runs/violet-meridian-iteration-1/checks/language-check.txt +6 -0
  332. package/skills/ui-design/tests/e2e/runs/violet-meridian-iteration-1/checks/quick-validate.txt +1 -0
  333. package/skills/ui-design/tests/e2e/runs/violet-meridian-iteration-1/checks/static-check.txt +2 -0
  334. package/skills/ui-design/tests/e2e/runs/violet-meridian-iteration-1/outputs/index.html +788 -0
  335. package/skills/ui-design/tests/e2e/runs/violet-meridian-iteration-1/prompt.md +14 -0
  336. package/skills/ui-design/tests/e2e/runs/violet-meridian-iteration-1/review.md +60 -0
  337. package/skills/ui-design/tests/e2e/runs/violet-meridian-iteration-1/screenshots/desktop.png +0 -0
  338. package/skills/ui-design/tests/e2e/runs/violet-meridian-iteration-1/screenshots/mobile.png +0 -0
  339. package/skills/video-compose/SKILL.md +8 -1
  340. package/skills/web-crawler/SKILL.md +70 -0
  341. package/skills/web-crawler/scripts/crawl.mjs +538 -0
  342. package/skills/web-crawler/scripts/crawl.test.mjs +204 -0
  343. package/skills/xlsx/SKILL.md +12 -0
  344. package/skills/xlsx/scripts/opc_check.py +253 -0
  345. package/src/advisorState.js +22 -0
  346. package/src/askBridge.js +97 -3
  347. package/src/bootstrap/workingDir.js +12 -1
  348. package/src/cli/args.js +17 -1
  349. package/src/cli/configMenu.js +236 -0
  350. package/src/cli/configWizard.js +7 -5
  351. package/src/cli/print.js +37 -4
  352. package/src/cli/streamJson.js +77 -24
  353. package/src/cli/wizards/advisorWizard.js +147 -0
  354. package/src/cli/wizards/fieldForm.js +149 -0
  355. package/src/cli/wizards/imageWizard.js +60 -0
  356. package/src/cli/wizards/openRouterWizard.js +23 -0
  357. package/src/cli/wizards/providerPresets.js +95 -0
  358. package/src/cli/wizards/providerWizard.js +219 -0
  359. package/src/cli/wizards/searchWizard.js +23 -0
  360. package/src/cli/wizards/ttsWizard.js +34 -0
  361. package/src/config/configFile.js +441 -83
  362. package/src/config/configService.js +446 -0
  363. package/src/config.js +24 -64
  364. package/src/context/agentHome.js +9 -0
  365. package/src/context/archive.js +7 -2
  366. package/src/context/compact.js +8 -5
  367. package/src/context/contextLock.js +81 -0
  368. package/src/context/persistContext.js +30 -11
  369. package/src/context/reactiveCompact.js +4 -2
  370. package/src/context/recentDirs.js +21 -61
  371. package/src/context/sessionPath.js +97 -23
  372. package/src/context/sessionRegistry.js +303 -24
  373. package/src/context/tokenCounter.js +18 -0
  374. package/src/context/workspaceIndex.js +731 -0
  375. package/src/infra/verification.js +28 -15
  376. package/src/llm/client.js +394 -159
  377. package/src/llm/providers/anthropicFormat.js +241 -0
  378. package/src/llm/providers/openaiFormat.js +190 -0
  379. package/src/loop.js +219 -32
  380. package/src/main.js +61 -11
  381. package/src/messageQueue.js +97 -0
  382. package/src/multimodal.js +203 -0
  383. package/src/plugin-sdk.js +15 -0
  384. package/src/plugins/commandRouter.js +3 -0
  385. package/src/plugins/hookEngine.js +5 -1
  386. package/src/plugins/pluginLifecycle.js +28 -0
  387. package/src/plugins/pluginLoader.js +2 -1
  388. package/src/plugins/pluginRunner.js +49 -21
  389. package/src/plugins/transcript.js +4 -3
  390. package/src/prompts/pluginList.js +5 -5
  391. package/src/prompts/projectInstructions.js +3 -3
  392. package/src/prompts/promptAssembly.js +133 -144
  393. package/src/prompts/skillList.js +12 -22
  394. package/src/prompts/system.js +0 -1
  395. package/src/session/runAgentTurn.js +70 -0
  396. package/src/session/runTurn.js +41 -6
  397. package/src/toolGate.js +17 -0
  398. package/src/tools/advisor/advisor.js +379 -0
  399. package/src/tools/agent/agent.js +555 -0
  400. package/src/tools/agent/agentTypes.js +59 -0
  401. package/src/tools/agent/coordinator.js +93 -0
  402. package/src/tools/agent/worktree.js +270 -0
  403. package/src/tools/ask/ask.js +7 -2
  404. package/src/tools/bash/bash.js +112 -18
  405. package/src/tools/download/download.js +144 -0
  406. package/src/tools/edit/edit.js +1 -0
  407. package/src/tools/edit/multi-edit.js +1 -0
  408. package/src/tools/genimage/genimage.js +242 -0
  409. package/src/tools/genspeech/genspeech.js +67 -0
  410. package/src/tools/glob/glob.js +1 -0
  411. package/src/tools/grep/grep.js +3 -0
  412. package/src/tools/grep/rgPath.js +2 -0
  413. package/src/tools/index.js +25 -2
  414. package/src/tools/read/read.js +1 -0
  415. package/src/tools/shared/killTree.js +52 -0
  416. package/src/tools/toolProgressBridge.js +48 -0
  417. package/src/tools/webbrowser/agentBrowserPath.js +258 -0
  418. package/src/tools/webbrowser/browser.js +59 -42
  419. package/src/tools/webbrowser/browserControl.js +118 -0
  420. package/src/tools/webbrowser/browserDaemon.js +97 -0
  421. package/src/tools/webbrowser/browserService.js +27 -0
  422. package/src/tools/webbrowser/browserStream.js +119 -0
  423. package/src/tools/webbrowser/webbrowser.js +699 -130
  424. package/src/tools/webfetch/webfetch.js +1 -0
  425. package/src/tools/websearch/websearch.js +134 -51
  426. package/src/tools/write/write.js +1 -0
  427. package/src/ui/App.js +249 -22
  428. package/src/ui/AskQuestion.js +29 -10
  429. package/src/ui/InputBox.js +104 -12
  430. package/src/ui/LiveArea.js +112 -19
  431. package/src/ui/MessageList.js +100 -14
  432. package/src/ui/ModelPicker.js +56 -0
  433. package/src/ui/QueuedMessagesBanner.js +14 -0
  434. package/src/ui/Root.js +18 -10
  435. package/src/ui/SessionPicker.js +102 -0
  436. package/src/ui/StatusLine.js +13 -4
  437. package/src/ui/WorkspacePicker.js +55 -0
  438. package/src/ui/figures.js +44 -0
  439. package/src/ui/hooks/askSlot.js +82 -0
  440. package/src/ui/hooks/useChat.js +358 -64
  441. package/src/ui/hooks/usePasteHandler.js +11 -1
  442. package/src/ui/hooks/useTextBuffer.js +1 -3
  443. package/src/ui/liveViewport.js +5 -1
  444. package/src/ui/markdown.js +206 -0
  445. package/src/ui/textBuffer.js +0 -7
  446. package/src/ui/theme.js +32 -0
  447. package/src/utils/debugLog.js +0 -4
  448. package/src/utils/resourcePaths.js +2 -2
  449. package/src/utils/stripThink.js +50 -0
  450. package/src/web/browserBridge.js +447 -0
  451. package/src/web/protocol.js +19 -0
  452. package/workflows/adaptive-research.yaml +13 -5
  453. package/workflows/book-review-short.yaml +36 -10
  454. package/workflows/e2e-write-greet.yaml +6 -2
  455. package/workflows/illustrate.yaml +227 -0
  456. package/workflows/plan.yaml +460 -179
  457. package/workflows/ppt.yaml +1659 -0
  458. package/workflows/ralph-loop.yaml +199 -0
  459. package/workflows/schema.json +81 -23
  460. package/workflows/webppt.yaml +496 -0
  461. package/workflows/word.yaml +710 -0
  462. package/workflows/youtube-shorts.yaml +46 -15
  463. package/MINIMAL-TRACE.md +0 -77
  464. package/plugins/HOW-TO-WRITE-A-PLUGIN.md +0 -186
  465. package/plugins/ralph-wiggum/.claude-plugin/plugin.json +0 -9
  466. package/plugins/ralph-wiggum/README.md +0 -197
  467. package/plugins/ralph-wiggum/commands/cancel-ralph.md +0 -18
  468. package/plugins/ralph-wiggum/commands/help.md +0 -137
  469. package/plugins/ralph-wiggum/commands/ralph-loop.md +0 -85
  470. package/plugins/ralph-wiggum/hooks/hooks.json +0 -15
  471. package/plugins/ralph-wiggum/hooks/stop-hook.sh +0 -191
  472. package/plugins/ralph-wiggum/plugin.js +0 -280
  473. package/plugins/ralph-wiggum/src/goalState.js +0 -258
  474. package/plugins/ralph-wiggum/src/sentinels.js +0 -76
  475. package/plugins/ralph-wiggum/src/stopHookRunner.js +0 -104
  476. package/plugins/ralph-wiggum/src/verificationGate.js +0 -202
  477. package/plugins/workflow-runner/src/miniReAct.js +0 -223
  478. package/plugins/workflow-runner/src/voteHelpers.js +0 -110
  479. package/skills/config/SKILL.md +0 -123
  480. package/skills/frontend-design/LICENSE.txt +0 -177
  481. package/skills/frontend-design/SKILL.md +0 -42
  482. package/src/ui/ProgressPanel.js +0 -54
  483. package/src/ui/StreamingBlock.js +0 -11
  484. package/src/ui/ToolStatus.js +0 -11
@@ -1,22 +1,23 @@
1
1
  /**
2
2
  * ============================================================
3
- * src/workflows/loader.ts —— workflow yaml 双源加载 + 最小校验
3
+ * plugins/workflow-runner/src/loader.ts —— workflow yaml 双源加载 + 校验
4
4
  * ------------------------------------------------------------
5
5
  * - 扫描 cwd/workflows/ 与 packageRoot/workflows/(cwd 优先,复用
6
6
  * getResourceSearchPaths 契约)
7
7
  * - 一个坏 yaml 不应拖累其它工作流(catch 单文件错误,继续扫描)
8
8
  * - 目录不存在 / 为空 → 静默返回 [](默认 T-A-O-R 不受影响)
9
9
  *
10
- * P0 不引 AJV;下面手写 ≤80 行的最小校验器,只挡死硬性错误:
11
- * - name / description / steps 必填且类型正确
12
- * - steps 非空
13
- * - 每个 step 必须有且只有一个动作字段(tool / skill / llm / type)
14
- *
15
- * P1 切 AJV 做完整 schema。
10
+ * 不引 AJV:下面手写校验器(validate/validateSteps),覆盖结构性硬错误并对
11
+ * 每条 step 字段做类型/一致性校验——
12
+ * - name / description / steps 必填且类型正确;steps 非空
13
+ * - 每个 step 必须有且只有一个动作字段(tool / skill / llm / agent / type)
14
+ * - 控制流字段一致性 + ADR-05 的 llm-only 字段(output_schema/context_files/
15
+ * allowed_tools)准入 + 断点续跑字段(artifacts/max_retries)类型
16
+ * 手写而非 schema 库:依赖少、错误信息可中文化、按减法不为「完整 schema」引依赖。
16
17
  * ============================================================
17
18
  */
18
19
  import { readFile, readdir } from 'node:fs/promises';
19
- import { join } from 'node:path';
20
+ import { basename, join } from 'node:path';
20
21
  import { parse as parseYamlLib } from 'yaml';
21
22
  import { getResourceSearchPaths } from '../../../src/plugin-sdk.js';
22
23
  export class WorkflowLoadError extends Error {
@@ -31,10 +32,10 @@ export class WorkflowLoadError extends Error {
31
32
  * 扫描双源目录,返回所有 *.yaml / *.yml 解析后的 workflow。
32
33
  * 永远不抛;坏文件 console.warn 并跳过。
33
34
  */
34
- export async function listWorkflows() {
35
+ export async function listWorkflows(workingDir) {
35
36
  const out = [];
36
37
  const seen = new Set();
37
- const roots = getResourceSearchPaths('workflows', import.meta.url);
38
+ const roots = getResourceSearchPaths('workflows', import.meta.url, workingDir);
38
39
  for (const root of roots) {
39
40
  let entries;
40
41
  try {
@@ -63,9 +64,21 @@ export async function listWorkflows() {
63
64
  return out;
64
65
  }
65
66
  /** 按 name 查找。找不到返回 null —— 调用方决定如何提示用户。 */
66
- export async function findWorkflowByName(name) {
67
- const all = await listWorkflows();
68
- return all.find((w) => w.name === name) ?? null;
67
+ export async function findWorkflowByName(name, workingDir) {
68
+ const all = await listWorkflows(workingDir);
69
+ const normalized = normalizeWorkflowLookupName(name);
70
+ return all.find((w) => w.name === normalized || workflowFileBaseName(w) === normalized) ?? null;
71
+ }
72
+ function stripYamlExt(name) {
73
+ return name.replace(/\.ya?ml$/i, '');
74
+ }
75
+ function normalizeWorkflowLookupName(name) {
76
+ return stripYamlExt(basename(name));
77
+ }
78
+ function workflowFileBaseName(workflow) {
79
+ if (!workflow.__source)
80
+ return null;
81
+ return stripYamlExt(basename(workflow.__source));
69
82
  }
70
83
  /** 读单个 yaml 文件 → 解析 → 校验。失败抛 WorkflowLoadError。 */
71
84
  export async function parseWorkflowFile(file) {
@@ -103,6 +116,39 @@ function validate(obj, file) {
103
116
  throw new WorkflowLoadError("缺少必填字段 'steps' 或为空数组", file);
104
117
  }
105
118
  validateSteps(obj.steps, file, '');
119
+ // self_heal 校验(可选)
120
+ if (obj.self_heal !== undefined) {
121
+ const sh = obj.self_heal;
122
+ if (typeof sh !== 'object' || sh === null || Array.isArray(sh)) {
123
+ throw new WorkflowLoadError("'self_heal' 必须是对象", file);
124
+ }
125
+ if (sh.backoff !== undefined) {
126
+ if (typeof sh.backoff !== 'number' || !Number.isInteger(sh.backoff) || sh.backoff < 0) {
127
+ throw new WorkflowLoadError("self_heal.backoff 必须是 >= 0 的整数", file);
128
+ }
129
+ }
130
+ if (sh.max_retries !== undefined) {
131
+ if (typeof sh.max_retries !== 'number' || !Number.isInteger(sh.max_retries) || sh.max_retries < 1) {
132
+ throw new WorkflowLoadError("self_heal.max_retries 必须是 >= 1 的整数", file);
133
+ }
134
+ }
135
+ if (sh.retry_delay_ms !== undefined) {
136
+ if (typeof sh.retry_delay_ms !== 'number' || !Number.isFinite(sh.retry_delay_ms) || sh.retry_delay_ms < 0) {
137
+ throw new WorkflowLoadError("self_heal.retry_delay_ms 必须是 >= 0 的数字(毫秒)", file);
138
+ }
139
+ }
140
+ if (sh.max_turns_retry_delay_ms !== undefined) {
141
+ if (typeof sh.max_turns_retry_delay_ms !== 'number' || !Number.isFinite(sh.max_turns_retry_delay_ms) || sh.max_turns_retry_delay_ms < 0) {
142
+ throw new WorkflowLoadError("self_heal.max_turns_retry_delay_ms 必须是 >= 0 的数字(毫秒)", file);
143
+ }
144
+ }
145
+ }
146
+ // artifacts 校验(WorkflowDef 级别,可选)
147
+ if (obj.artifacts !== undefined) {
148
+ if (!Array.isArray(obj.artifacts) || !obj.artifacts.every((x) => typeof x === 'string' && x.length > 0)) {
149
+ throw new WorkflowLoadError("'artifacts' 必须是非空字符串数组", file);
150
+ }
151
+ }
106
152
  if (obj.inputs !== undefined) {
107
153
  if (!Array.isArray(obj.inputs)) {
108
154
  throw new WorkflowLoadError("'inputs' 必须是数组", file);
@@ -121,37 +167,71 @@ function validate(obj, file) {
121
167
  if (r.type === 'enum' && (!Array.isArray(r.values) || r.values.length === 0)) {
122
168
  throw new WorkflowLoadError(`inputs[${i}].values 必填(type=enum 时)`, file);
123
169
  }
170
+ // greedy 首参:只许 boolean,且只能标在第一个 input(语义=吸收全部位置参数,
171
+ // 标在非首位无意义且会误导,响亮拒绝)。
172
+ if (r.greedy !== undefined && typeof r.greedy !== 'boolean') {
173
+ throw new WorkflowLoadError(`inputs[${i}].greedy 必须是 boolean`, file);
174
+ }
175
+ if (r.greedy === true && i !== 0) {
176
+ throw new WorkflowLoadError(`inputs[${i}].greedy 只能标在第一个 input 上`, file);
177
+ }
124
178
  }
125
179
  }
126
180
  }
127
181
  // ADR-05: 控制流 type 集合——这些节点的"动作"由 type 自身定义,
128
182
  // 因此不能附带 tool/skill/llm 也不能配 output_schema / context_files / allowed_tools。
129
- // 导出供「editor ↔ 后端」schema 一致性测试比对(test/editorSchemaSync.test.ts)。
183
+ // 导出供「editor ↔ 后端」schema 一致性测试比对(test/workflowEditorSchemaSync.test.ts)。
130
184
  // editor 因 D2 红线禁止 import 后端、手抄了同一份;后端在此演进时该测试会红,提示同步 editor。
131
185
  export const CONTROL_FLOW_TYPES = new Set([
132
186
  'assert',
133
187
  'verify',
188
+ 'set',
134
189
  'branch',
135
190
  'loop',
191
+ 'break',
192
+ 'continue',
193
+ 'echo',
136
194
  'pause',
137
- 'parallel',
138
- 'vote',
139
195
  ]);
140
196
  export const VALID_STEP_TYPES = new Set([
141
197
  'assert',
142
198
  'verify',
199
+ 'set',
143
200
  'branch',
144
201
  'loop',
202
+ 'break',
203
+ 'continue',
204
+ 'echo',
145
205
  'pause',
146
- 'parallel',
147
- 'vote',
148
206
  ]);
149
207
  // 动作字段里「自带 kind 含义」的那几个(不含 type —— type 的 kind 由其值决定)。
150
- // 单一来源:validateSteps 的 actionFields + test/editorSchemaSync.test.ts 都从这里取,
208
+ // 单一来源:validateSteps 的 actionFields + test/workflowEditorSchemaSync.test.ts 都从这里取,
151
209
  // 新增动作 kind 只改这一处(并同步 editor 手抄镜像 + 该测试快照)。
152
210
  export const ACTION_KINDS = ['tool', 'skill', 'agent', 'llm'];
153
211
  // onError 合法值(单一来源:onError 校验 + editor schema 同步测试)。
154
212
  export const ON_ERROR_VALUES = ['halt', 'continue', 'recover'];
213
+ function isValidMaxIterations(value) {
214
+ return ((typeof value === 'number' && Number.isInteger(value) && value > 0) ||
215
+ (typeof value === 'string' && (value.includes('${') || /^\d+$/.test(value.trim()))));
216
+ }
217
+ function isValidImageDetail(value) {
218
+ return value === 'low' || value === 'default' || value === 'high';
219
+ }
220
+ function assertLlmOrAgentMultimodalField(s, key, p, file) {
221
+ if (s.llm === undefined && s.agent === undefined) {
222
+ throw new WorkflowLoadError(`${p}.${key} 仅支持 llm / agent 节点`, file);
223
+ }
224
+ }
225
+ function assertHistoryModeField(s, p, file) {
226
+ if (s.llm === undefined && s.agent === undefined && s.skill === undefined) {
227
+ throw new WorkflowLoadError(`${p}.history_mode 只能用在 llm / agent / skill 节点上`, file);
228
+ }
229
+ }
230
+ function requireNonEmptyString(s, key, p, file, typeName) {
231
+ if (typeof s[key] !== 'string' || String(s[key]).length === 0) {
232
+ throw new WorkflowLoadError(`${p} type=${typeName} requires non-empty '${key}'`, file);
233
+ }
234
+ }
155
235
  function validateSteps(steps, file, pathPrefix) {
156
236
  const seenIds = new Set();
157
237
  for (const [i, item] of steps.entries()) {
@@ -191,30 +271,194 @@ function validateSteps(steps, file, pathPrefix) {
191
271
  if (s.recover_hint !== undefined && typeof s.recover_hint !== 'string') {
192
272
  throw new WorkflowLoadError(`${p}.recover_hint 必须是 string`, file);
193
273
  }
274
+ if (s.free_text !== undefined) {
275
+ if (s.type !== 'pause') {
276
+ throw new WorkflowLoadError(`${p}.free_text 只能用在 type=pause 节点上`, file);
277
+ }
278
+ if (s.free_text !== 'halt' && s.free_text !== 'approve') {
279
+ throw new WorkflowLoadError(`${p}.free_text "${String(s.free_text)}" 非法;合法值: halt / approve`, file);
280
+ }
281
+ }
282
+ // 数字字段类型校验:yaml 给字符串会静默进入运行时算术(NaN footgun),加载期拦掉
283
+ if (s.maxTurns !== undefined &&
284
+ (typeof s.maxTurns !== 'number' || !Number.isInteger(s.maxTurns) || s.maxTurns < 1)) {
285
+ throw new WorkflowLoadError(`${p}.maxTurns 必须是 >= 1 的整数`, file);
286
+ }
287
+ if (s.max_retries !== undefined &&
288
+ (typeof s.max_retries !== 'number' || !Number.isInteger(s.max_retries) || s.max_retries < 0)) {
289
+ throw new WorkflowLoadError(`${p}.max_retries 必须是 >= 0 的整数`, file);
290
+ }
291
+ // 多模态图片输入:仅 llm / agent 节点支持;skill/控制流/确定性 tool 节点
292
+ // 不接收图片字段,避免 yaml 作者以为图片已传给模型但实际被静默忽略。
293
+ const multimodalFields = [
294
+ 'images',
295
+ 'parse_inline_images',
296
+ 'image_detail',
297
+ 'image_max_long_side_pixel',
298
+ ];
299
+ for (const key of multimodalFields) {
300
+ if (s[key] !== undefined)
301
+ assertLlmOrAgentMultimodalField(s, key, p, file);
302
+ }
303
+ if (s.images !== undefined) {
304
+ if (!Array.isArray(s.images)) {
305
+ throw new WorkflowLoadError(`${p}.images 必须是数组`, file);
306
+ }
307
+ for (const [j, item] of s.images.entries()) {
308
+ if (typeof item === 'string') {
309
+ if (item.length === 0) {
310
+ throw new WorkflowLoadError(`${p}.images[${j}] 必须是非空 string`, file);
311
+ }
312
+ continue;
313
+ }
314
+ if (!item || typeof item !== 'object' || Array.isArray(item)) {
315
+ throw new WorkflowLoadError(`${p}.images[${j}] 必须是 string 或 { source, detail?, max_long_side_pixel? }`, file);
316
+ }
317
+ const r = item;
318
+ if (typeof r.source !== 'string' || r.source.length === 0) {
319
+ throw new WorkflowLoadError(`${p}.images[${j}].source 必须是非空 string`, file);
320
+ }
321
+ if (r.detail !== undefined && !isValidImageDetail(r.detail)) {
322
+ throw new WorkflowLoadError(`${p}.images[${j}].detail 必须是 low/default/high`, file);
323
+ }
324
+ if (r.max_long_side_pixel !== undefined &&
325
+ (typeof r.max_long_side_pixel !== 'number' ||
326
+ !Number.isInteger(r.max_long_side_pixel) ||
327
+ r.max_long_side_pixel < 1)) {
328
+ throw new WorkflowLoadError(`${p}.images[${j}].max_long_side_pixel 必须是正整数`, file);
329
+ }
330
+ }
331
+ }
332
+ if (s.parse_inline_images !== undefined &&
333
+ typeof s.parse_inline_images !== 'boolean') {
334
+ throw new WorkflowLoadError(`${p}.parse_inline_images 必须是 boolean`, file);
335
+ }
336
+ if (s.image_detail !== undefined && !isValidImageDetail(s.image_detail)) {
337
+ throw new WorkflowLoadError(`${p}.image_detail 必须是 low/default/high`, file);
338
+ }
339
+ if (s.image_max_long_side_pixel !== undefined &&
340
+ (typeof s.image_max_long_side_pixel !== 'number' ||
341
+ !Number.isInteger(s.image_max_long_side_pixel) ||
342
+ s.image_max_long_side_pixel < 1)) {
343
+ throw new WorkflowLoadError(`${p}.image_max_long_side_pixel 必须是正整数`, file);
344
+ }
345
+ if (s.history_mode !== undefined) {
346
+ assertHistoryModeField(s, p, file);
347
+ if (s.history_mode !== 'shared' && s.history_mode !== 'isolated') {
348
+ throw new WorkflowLoadError(`${p}.history_mode "${String(s.history_mode)}" 非法;合法值: shared / isolated`, file);
349
+ }
350
+ }
194
351
  // 控制流字段一致性
195
352
  if (s.type === 'branch' && s.condition === undefined) {
196
353
  throw new WorkflowLoadError(`${p} type=branch 必须带 'condition'`, file);
197
354
  }
198
355
  if (s.type === 'loop') {
199
- if (s.over === undefined)
200
- throw new WorkflowLoadError(`${p} type=loop 必须带 'over'`, file);
201
- if (typeof s.as !== 'string' || s.as.length === 0) {
202
- throw new WorkflowLoadError(`${p} type=loop 必须带 'as'`, file);
356
+ const hasOver = s.over !== undefined;
357
+ const hasMaxIter = s.max_iterations !== undefined;
358
+ // 两种模式互斥:for-each(over 必填)或 while(max_iterations 必填)
359
+ if (!hasOver && !hasMaxIter) {
360
+ throw new WorkflowLoadError(`${p} type=loop: for-each 模式需 'over',while 模式需 'max_iterations',二者必选其一`, file);
361
+ }
362
+ if (hasOver) {
363
+ // for-each 模式
364
+ if (typeof s.as !== 'string' || s.as.length === 0) {
365
+ throw new WorkflowLoadError(`${p} type=loop for-each 模式必须带 'as'`, file);
366
+ }
367
+ }
368
+ else {
369
+ // while 模式:as 可选(不设则不注入迭代变量)
370
+ if (s.as !== undefined && (typeof s.as !== 'string' || s.as.length === 0)) {
371
+ throw new WorkflowLoadError(`${p} type=loop while 模式的 'as' 必须是非空字符串`, file);
372
+ }
373
+ // max_iterations:正整数 或 含 ${} 的模板字符串(运行时插值 + coerce,
374
+ // 失败响亮报错)。裸字符串数字("50")也容忍。其它一律加载期拦掉——
375
+ // 曾有 "${max_iterations}" 不被插值直接参与 < 比较 → NaN → 静默 0 次迭代。
376
+ const mi = s.max_iterations;
377
+ if (!isValidMaxIterations(mi)) {
378
+ throw new WorkflowLoadError(`${p}.max_iterations 必须是正整数或含 \${} 的模板字符串,得到: ${JSON.stringify(mi)}`, file);
379
+ }
203
380
  }
204
381
  if (!Array.isArray(s.do)) {
205
382
  throw new WorkflowLoadError(`${p} type=loop 必须带 'do' 数组`, file);
206
383
  }
207
384
  validateSteps(s.do, file, `${p}.do`);
208
385
  }
386
+ if (s.type === 'break') {
387
+ // break 是循环退出信号,通常配合 when 条件使用
388
+ // 无额外必填字段;when 缺失时等价于无条件跳出(极少用)
389
+ }
390
+ // continue:跳到(目标)循环的下一轮迭代;与 break 同族(when 可选)
391
+ // echo:确定性输出 message(支持 ${} 插值),不烧 LLM
392
+ if (s.type === 'echo') {
393
+ requireNonEmptyString(s, 'message', p, file, 'echo');
394
+ }
395
+ else if (s.message !== undefined) {
396
+ throw new WorkflowLoadError(`${p}.message 只能用在 type=echo 节点上`, file);
397
+ }
398
+ // target:仅 break/continue 的标签语义(指定要穿透到哪一层循环)
399
+ if (s.target !== undefined) {
400
+ if (s.type !== 'break' && s.type !== 'continue') {
401
+ throw new WorkflowLoadError(`${p}.target 只能用在 type=break / type=continue 节点上`, file);
402
+ }
403
+ if (typeof s.target !== 'string' || s.target.length === 0) {
404
+ throw new WorkflowLoadError(`${p}.target 必须是非空 string(目标循环的 id)`, file);
405
+ }
406
+ }
407
+ // title:任意步可选的人话进度显示名(声明后状态行显示它、不显示块内相对序号)
408
+ if (s.title !== undefined && (typeof s.title !== 'string' || s.title.length === 0)) {
409
+ throw new WorkflowLoadError(`${p}.title 必须是非空 string(进度显示名)`, file);
410
+ }
411
+ // goal_supplement:动作步与 pause 可声明(产出作为用户补充要求贯穿后续步骤)
412
+ if (s.goal_supplement !== undefined) {
413
+ if (typeof s.goal_supplement !== 'boolean') {
414
+ throw new WorkflowLoadError(`${p}.goal_supplement 必须是 boolean`, file);
415
+ }
416
+ const isActionStep = ACTION_KINDS.some((k) => s[k] !== undefined);
417
+ if (!isActionStep && s.type !== 'pause') {
418
+ throw new WorkflowLoadError(`${p}.goal_supplement 只能用在动作步(${ACTION_KINDS.join('/')})或 type=pause 节点上`, file);
419
+ }
420
+ }
421
+ // done_when:仅 agent 步可选(确定性完成条件,引擎 nudge 兜底 announce-not-do 烂尾)
422
+ if (s.done_when !== undefined) {
423
+ if (typeof s.done_when !== 'string' || s.done_when.trim().length === 0) {
424
+ throw new WorkflowLoadError(`${p}.done_when 必须是非空 string(白名单表达式)`, file);
425
+ }
426
+ if (s.agent === undefined) {
427
+ throw new WorkflowLoadError(`${p}.done_when 仅支持 agent 节点`, file);
428
+ }
429
+ }
209
430
  if (s.type === 'assert' && s.condition === undefined) {
210
431
  throw new WorkflowLoadError(`${p} type=assert 必须带 'condition'`, file);
211
432
  }
433
+ // set:确定性变量赋值(曾有 yaml 用 llm:"PLAN" 当赋值——模型自由回复非确定,set 是根治)
434
+ if (s.type === 'set') {
435
+ if (!s.set ||
436
+ typeof s.set !== 'object' ||
437
+ Array.isArray(s.set) ||
438
+ Object.keys(s.set).length === 0) {
439
+ throw new WorkflowLoadError(`${p} type=set 必须带非空对象 'set'({ 变量名: 值 },值支持 \${} 插值)`, file);
440
+ }
441
+ }
212
442
  if (s.type === 'verify') {
213
- if (!Array.isArray(s.checks) || s.checks.length === 0) {
214
- throw new WorkflowLoadError(`${p} type=verify 必须带非空 'checks' 数组`, file);
443
+ // 兼容两种格式:YAML 数组 或逗号分隔字符串
444
+ const rawChecks = Array.isArray(s.checks)
445
+ ? s.checks
446
+ : typeof s.checks === 'string'
447
+ ? s.checks.split(',').map((x) => x.trim()).filter(Boolean)
448
+ : [];
449
+ if (rawChecks.length === 0) {
450
+ throw new WorkflowLoadError(`${p} type=verify 必须带 checks(数组或逗号分隔字符串)`, file);
215
451
  }
216
- for (const [ci, c] of s.checks.entries()) {
217
- if (typeof c !== 'string' || !c.includes(':')) {
452
+ for (const [ci, c] of rawChecks.entries()) {
453
+ if (typeof c !== 'string') {
454
+ throw new WorkflowLoadError(`${p}.checks[${ci}] 必须是字符串`, file);
455
+ }
456
+ // 含 ${...} 模板的 check(如 checks: "${verify_checks}")在运行时插值后才知道真实值,
457
+ // 加载期跳过格式校验,交给 verify.ts 在插值后解析。否则这类合法的动态配置会被
458
+ // 误判为"损坏 yaml"而整个工作流加载失败(ralph-loop 的 run_verify 即如此)。
459
+ if (c.includes('${'))
460
+ continue;
461
+ if (!c.includes(':')) {
218
462
  throw new WorkflowLoadError(`${p}.checks[${ci}] 格式错误,应为 "type:value"(如 "file_exists:./x")`, file);
219
463
  }
220
464
  }
@@ -232,50 +476,6 @@ function validateSteps(steps, file, pathPrefix) {
232
476
  validateSteps(s.else, file, `${p}.else`);
233
477
  }
234
478
  // ---------------- ADR-05 新增校验 ----------------
235
- // parallel:必须有非空 fork[],递归校验子步骤
236
- if (s.type === 'parallel') {
237
- if (!Array.isArray(s.fork) || s.fork.length === 0) {
238
- throw new WorkflowLoadError(`${p} type=parallel 必须有 fork: [...](至少 1 个子步骤)`, file);
239
- }
240
- validateSteps(s.fork, file, `${p}.fork`);
241
- }
242
- else if (s.fork !== undefined) {
243
- throw new WorkflowLoadError(`${p}.fork 只能用在 type=parallel 节点上`, file);
244
- }
245
- // vote:voters_count (2~10) / rules / input 必填;threshold 可选但越界要拦
246
- if (s.type === 'vote') {
247
- const vc = s.voters_count;
248
- const rules = s.rules;
249
- const input = s.input;
250
- const missing = typeof vc !== 'number' ||
251
- !Number.isInteger(vc) ||
252
- vc < 2 ||
253
- vc > 10 ||
254
- typeof rules !== 'string' ||
255
- rules.length === 0 ||
256
- typeof input !== 'string' ||
257
- input.length === 0;
258
- if (missing) {
259
- throw new WorkflowLoadError(`${p} type=vote 要求 voters_count (2~10), rules, input 三个字段`, file);
260
- }
261
- if (s.threshold !== undefined) {
262
- const t = s.threshold;
263
- if (typeof t !== 'number' ||
264
- !Number.isInteger(t) ||
265
- t < 1 ||
266
- t > vc) {
267
- throw new WorkflowLoadError(`${p} threshold=${String(t)} 超出范围;必须是 1 <= threshold <= voters_count(${vc})`, file);
268
- }
269
- }
270
- }
271
- else {
272
- // 非 vote 节点不应携带 vote-only 字段
273
- for (const k of ['voters_count', 'rules', 'threshold']) {
274
- if (s[k] !== undefined) {
275
- throw new WorkflowLoadError(`${p}.${k} 只能用在 type=vote 节点上`, file);
276
- }
277
- }
278
- }
279
479
  // output_schema:必须是 object;不能用在控制流节点上
280
480
  if (s.output_schema !== undefined) {
281
481
  if (typeof s.output_schema !== 'object' ||
@@ -313,7 +513,7 @@ function validateSteps(steps, file, pathPrefix) {
313
513
  throw new WorkflowLoadError(`${p}.context_files 不能用在 type=${s.type} 控制流节点上`, file);
314
514
  }
315
515
  // P3:context_files 同样是 skill 节点的静默 no-op(execSkillStep 无视它),
316
- // 仅 llm 节点经 miniReAct 生效——loader 直接拒绝,避免悄悄丢弃。
516
+ // 仅 llm 节点经 runQuery 生效——loader 直接拒绝,避免悄悄丢弃。
317
517
  if (s.skill !== undefined) {
318
518
  throw new WorkflowLoadError(`${p}.context_files 暂不支持 skill 节点(仅 llm 节点生效)`, file);
319
519
  }
@@ -332,5 +532,19 @@ function validateSteps(steps, file, pathPrefix) {
332
532
  throw new WorkflowLoadError(`${p}.allowed_tools 暂不支持 skill 节点(仅 llm 节点生效)`, file);
333
533
  }
334
534
  }
535
+ // ---------------- Phase 1: 断点续执行 / 自愈字段校验 ----------------
536
+ // step.artifacts: string[](可选)
537
+ if (s.artifacts !== undefined) {
538
+ if (!Array.isArray(s.artifacts) || !s.artifacts.every((x) => typeof x === 'string' && x.length > 0)) {
539
+ throw new WorkflowLoadError(`${p}.artifacts 必须是非空字符串数组`, file);
540
+ }
541
+ }
542
+ // step.max_retries: number(可选,>= 0 的整数)
543
+ if (s.max_retries !== undefined) {
544
+ const mr = s.max_retries;
545
+ if (typeof mr !== 'number' || !Number.isInteger(mr) || mr < 0) {
546
+ throw new WorkflowLoadError(`${p}.max_retries 必须是 >= 0 的整数`, file);
547
+ }
548
+ }
335
549
  }
336
550
  }
@@ -0,0 +1,37 @@
1
+ import { isAbsolute, resolve } from 'node:path';
2
+ import { interpolate } from './expressions.js';
3
+ export function resolveWorkflowMultimodalOptions(step, vars, ctx, onMissing) {
4
+ const imageDetail = step.image_detail;
5
+ const imageMaxLongSidePixel = step.image_max_long_side_pixel;
6
+ const imageInputs = (step.images ?? []).map((item) => {
7
+ if (typeof item === 'string') {
8
+ return {
9
+ source: resolveWorkflowImageSource(interpolate(item, vars, { onMissing }), ctx),
10
+ detail: imageDetail,
11
+ maxLongSidePixel: imageMaxLongSidePixel,
12
+ };
13
+ }
14
+ return {
15
+ source: resolveWorkflowImageSource(interpolate(item.source, vars, { onMissing }), ctx),
16
+ detail: item.detail ?? imageDetail,
17
+ maxLongSidePixel: item.max_long_side_pixel ?? imageMaxLongSidePixel,
18
+ };
19
+ });
20
+ return {
21
+ imageInputs: imageInputs.length > 0 ? imageInputs : undefined,
22
+ parseInlineImages: step.parse_inline_images,
23
+ imageDetail,
24
+ imageMaxLongSidePixel,
25
+ };
26
+ }
27
+ function resolveWorkflowImageSource(source, ctx) {
28
+ const clean = source.trim();
29
+ if (/^https?:\/\//i.test(clean) ||
30
+ /^data:image\//i.test(clean) ||
31
+ clean.startsWith('file://') ||
32
+ clean.startsWith('~/') ||
33
+ isAbsolute(clean)) {
34
+ return clean;
35
+ }
36
+ return resolve(ctx.cwd, clean);
37
+ }
@@ -6,30 +6,25 @@
6
6
  *
7
7
  * 传统 DAG 的脆点:某节点抛错 → 整条 halt(或 onError:'continue' 盲跳,下游
8
8
  * capture 拿 undefined 静默级联错)。当节点声明 onError:'recover' 时,runner
9
- * 不立刻 halt,而是调本模块起一个**有界恢复 agent**(复用 miniReAct),喂给它
10
- * 「工作流总目标 + 失败节点的意图 + 错误信息 + 当前已知变量 + 下游 capture 契约」,
9
+ * 不立刻 halt,而是调本模块起一个**有界恢复 agent**(委托 runQuery/TAOR),
10
+ * 喂给它「工作流总目标 + 失败节点的意图 + 错误信息 + 当前已知变量 + 下游 capture 契约」,
11
11
  * 让它**随机应变去达成该节点本来要的结果**(换路径 / 换命令 / 先读后写…)。
12
12
  *
13
+ * v0.9 重构:不再使用独立 miniReAct 子循环,而是委托主 TAOR 引擎(runQuery)
14
+ * 在共享 ctx.history 上执行。恢复过程也写入共享上下文——后续对话能看到"某步失败后
15
+ * 如何被自动修复",增强可解释性。
16
+ *
13
17
  * —— 共享大脑 ——
14
- * 恢复 agent 与 agent: 节点(stepExecutors/agent.ts)本质同构,故系统提示词纪律与
15
- * 验证闸门都收敛在 agentCommon.ts:本模块只贴一句「救火」角色开场白(RECOVER_OPENING),
16
- * 跑完后交给 interpretMiniReActResult 判定「真救回 / 放弃」——绝不把没验证过的产出当成功
17
- * 绑给下游。返回 AgentOutcome:
18
+ * 恢复 agent 与正常 agent/llm/skill 步本质同构:都通过 runQuery(TAOR) 执行。
19
+ * 本模块只构造「救火」角色提示词,跑完后交给 interpretMiniReActResult 判定
20
+ * 「真救回 / 放弃」。返回 AgentOutcome:
18
21
  * - { ok:true, value } —— runner 用同一个 step.capture 绑定后继续 happy path
19
22
  * - { ok:false, reason } —— runner 带原因 honest halt
20
- *
21
- * 设计取舍:复用 miniReAct 而非 runQuery —— 恢复要的正是「有界、白名单、不污染主
22
- * history、直接返回 StepResult」;默认全量 ALL_TOOLS(与 skill/agent 节点同等授权、
23
- * 同一信任边界),step.allowed_tools 可收窄。本函数是 AsyncGenerator,yield*
24
- * runMiniReAct 把恢复 agent 的内层思考透传给 UI;warning/plugin_progress/halt 仍由
25
- * runner 在调用前后发(事件卫生集中在 runner)。
26
23
  * ============================================================
27
24
  */
28
- import { ALL_TOOLS } from '../../../src/plugin-sdk.js';
29
- import { buildWorkflowAgentSystem, describeCaptureContract, interpretMiniReActResult, truncate, } from './agentCommon.js';
25
+ import { describeCaptureContract, effectiveGoalAnchor, interpretMiniReActResult, runStepQuery, truncate, } from './agentCommon.js';
30
26
  import { interpolate, interpolateDeep } from './expressions.js';
31
- import { runMiniReAct } from './miniReAct.js';
32
- // RECOVERY_SYSTEM 现在是 async(需要扫描 skill/plugin),在 runRecovery 内部构建
27
+ // RECOVER_OPENING 作为用户消息的一部分(系统提示词已在 history[0] 中)
33
28
  const RECOVER_OPENING = `你是一名资深的自动化工程师 / SRE,被一个确定性工作流临时调来「救火」。
34
29
  工作流里一个原本写死的步骤刚刚执行失败了,现在交给你随机应变——先看错误信息诊断根因,再想办法真正达成这个步骤**本来要的结果**(关注它要的结果,别拘泥它原先怎么写)。`;
35
30
  /** 描述失败节点「本来要干什么」,喂给恢复 agent 作为意图说明。 */
@@ -57,7 +52,6 @@ function describeStepIntent(step, vars) {
57
52
  * 尝试自适应恢复一个失败的动作 step。
58
53
  * - 救回 → { ok:true, value }(value.raw 已 normalize,可被原 step.capture 绑定)
59
54
  * - 放弃 / 不可信 / 子循环抛错(含用户中断)→ { ok:false, reason }
60
- * (中断的最终语义由调用方在拿到结果后另行检测 ctx.signal 决定)
61
55
  */
62
56
  export async function* runRecovery(step, vars, ctx, errorMsg) {
63
57
  const intent = describeStepIntent(step, vars);
@@ -67,33 +61,49 @@ export async function* runRecovery(step, vars, ctx, errorMsg) {
67
61
  const hintLine = step.recover_hint
68
62
  ? `\n\n作者给恢复的提示:${interpolate(step.recover_hint, vars)}`
69
63
  : '';
70
- const userMsg = `${goalLine}` +
64
+ // 构造恢复 prompt(作为 user message push 进共享 history)
65
+ const recoverPrompt = `${RECOVER_OPENING}\n\n` +
66
+ `${goalLine}` +
71
67
  `失败的步骤(id=${step.id})本来要做的事:\n${intent}\n\n` +
72
68
  `它失败时的错误:\n${truncate(errorMsg, 800)}\n\n` +
73
69
  `当前已知的变量(JSON):\n${knownVars}` +
74
70
  `${captureLine}${hintLine}\n\n` +
75
71
  `请随机应变,把这一步要的结果做出来;做不成就按约定输出放弃标记。`;
76
- // 工具集:默认全量(与 skill/agent 节点同等 agency);step.allowed_tools 收窄。
77
- const allowed = step.allowed_tools;
78
- const tools = allowed && allowed.length > 0
79
- ? ALL_TOOLS.filter((t) => allowed.includes(t.name))
80
- : ALL_TOOLS;
81
- // ✅ Phase 2: 异步构建完整 system prompt(含 skill/plugin 列表)
82
- const systemContent = await buildWorkflowAgentSystem(ctx.cwd, RECOVER_OPENING, tools);
83
- const messages = [
84
- { role: 'system', content: systemContent },
85
- { role: 'user', content: userMsg },
86
- ];
72
+ // output_schema 约束嵌入 prompt
73
+ const schemaHint = step.output_schema
74
+ ? `\n\n【结构化输出约束】请严格按照以下 JSON Schema 输出结果:\n${JSON.stringify(step.output_schema, null, 2)}`
75
+ : '';
76
+ // 通用目标锚定(同 agent/llm/skill 步):恢复 agent 也要看到用户原始目标
77
+ // 与补充要求,不能只有 def.description 的"工作流总目标"(E10)。
78
+ const anchor = effectiveGoalAnchor(ctx);
79
+ const fullPrompt = anchor
80
+ ? `${anchor}\n\n${recoverPrompt}${schemaHint}`
81
+ : recoverPrompt + schemaHint;
87
82
  try {
88
- const result = yield* runMiniReAct({
89
- messages,
90
- tools,
91
- output_schema: step.output_schema,
92
- provider: ctx.provider,
93
- signal: ctx.signal,
94
- maxTurns: step.maxTurns ?? 12,
95
- });
96
- const outcome = interpretMiniReActResult(result, step.output_schema !== undefined);
83
+ // 委托 TAOR 引擎执行(共享消费层 runStepQuery)——事件透传给 UI(显示恢复进度)。
84
+ // 注意剥掉 step 的 allowed_tools 白名单:原步收敛工具是为弱模型聚焦,
85
+ // 恢复 agent 的职责正是「随机应变换路径」,要全工具箱(保持 v0.9 行为)。
86
+ const { rawText, sawLlmError, sawMaxTurns, llmErrorDetail } = yield* runStepQuery(fullPrompt, { ...step, allowed_tools: undefined }, ctx, { maxTurns: step.maxTurns ?? 25 });
87
+ // 恢复是兜底路径:无论 provider 失败还是模型空答,都按「没救回」返回
88
+ // ok:false(不抛重试——retry 在进入 recover 前已用尽);只是归因更诚实。
89
+ if (!rawText.trim() && sawLlmError) {
90
+ return {
91
+ ok: false,
92
+ reason: `LLM 调用失败且无产出${llmErrorDetail ? `—— ${truncate(llmErrorDetail, 300)}` : ''}`,
93
+ };
94
+ }
95
+ // 恢复 agent 也撞 maxTurns 截断 → 截断产出不可信,诚实「未救回」
96
+ if (sawMaxTurns) {
97
+ return {
98
+ ok: false,
99
+ reason: `恢复 agent 达到最大轮数 ${step.maxTurns ?? 25} 被截断,产出不可信(未救回)`,
100
+ };
101
+ }
102
+ const stepResult = {
103
+ raw: { text: rawText, result: rawText },
104
+ preview: rawText.length > 200 ? `${rawText.slice(0, 200)}...` : rawText,
105
+ };
106
+ const outcome = interpretMiniReActResult(stepResult, !!step.output_schema);
97
107
  if (!outcome.ok)
98
108
  return outcome;
99
109
  // 救回:preview 加 recovered: 前缀(runner 的 step_end output 用),raw 原样。
@@ -103,7 +113,7 @@ export async function* runRecovery(step, vars, ctx, errorMsg) {
103
113
  };
104
114
  }
105
115
  catch (e) {
106
- // miniReAct 抛错(用尽轮数 / 重复工具调用 / schema 解析失败 / 中断)→ 恢复失败。
116
+ // runStepQuery 抛错(编程性异常 / 中断竞态)→ 恢复失败。
107
117
  return { ok: false, reason: e.message ?? String(e) };
108
118
  }
109
119
  }