minimal-agent 0.9.0 → 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 (467) hide show
  1. package/README.md +11 -8
  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 +2 -1
  20. package/plugins/workflow-runner/src/agentCommon.js +172 -9
  21. package/plugins/workflow-runner/src/dslManifest.js +72 -0
  22. package/plugins/workflow-runner/src/expressions.js +150 -13
  23. package/plugins/workflow-runner/src/index.js +39 -8
  24. package/plugins/workflow-runner/src/loader.js +216 -64
  25. package/plugins/workflow-runner/src/multimodal.js +37 -0
  26. package/plugins/workflow-runner/src/recovery.js +26 -24
  27. package/plugins/workflow-runner/src/runner.js +693 -287
  28. package/plugins/workflow-runner/src/stepExecutors/agent.js +126 -37
  29. package/plugins/workflow-runner/src/stepExecutors/assert.js +3 -2
  30. package/plugins/workflow-runner/src/stepExecutors/llm.js +38 -30
  31. package/plugins/workflow-runner/src/stepExecutors/skill.js +27 -38
  32. package/plugins/workflow-runner/src/stepExecutors/tool.js +56 -3
  33. package/plugins/workflow-runner/src/stepExecutors/verify.js +19 -10
  34. package/plugins/workflow-runner/src/taskIdentity.js +185 -30
  35. package/plugins/workflow-runner/src/types.js +18 -7
  36. package/plugins/workflow-runner/src/workflowState.js +355 -55
  37. package/skills/ai-image/SKILL.md +89 -0
  38. package/skills/ai-image/scripts/generate.mjs +497 -0
  39. package/skills/ai-image/scripts/generate_from_plan.mjs +469 -0
  40. package/skills/ai-image/scripts/generate_from_plan_test.mjs +223 -0
  41. package/skills/ai-image/scripts/generate_test.mjs +74 -0
  42. package/skills/browser-automation/SKILL.md +47 -0
  43. package/skills/docx/SKILL.md +25 -3
  44. package/skills/docx/scripts/office/helpers/__pycache__/__init__.cpython-314.pyc +0 -0
  45. package/skills/docx/scripts/office/helpers/__pycache__/merge_runs.cpython-314.pyc +0 -0
  46. package/skills/docx/scripts/office/helpers/__pycache__/simplify_redlines.cpython-314.pyc +0 -0
  47. package/skills/docx/scripts/office/validators/__pycache__/__init__.cpython-314.pyc +0 -0
  48. package/skills/docx/scripts/office/validators/__pycache__/base.cpython-314.pyc +0 -0
  49. package/skills/docx/scripts/office/validators/__pycache__/docx.cpython-314.pyc +0 -0
  50. package/skills/docx/scripts/office/validators/__pycache__/pptx.cpython-314.pyc +0 -0
  51. package/skills/docx/scripts/office/validators/__pycache__/redlining.cpython-314.pyc +0 -0
  52. package/skills/docx/scripts/opc_check.py +253 -0
  53. package/skills/image-gen-openrouter/SKILL.md +10 -2
  54. package/skills/image-stats/SKILL.md +65 -0
  55. package/skills/image-stats/scripts/__pycache__/image_stats.cpython-314.pyc +0 -0
  56. package/skills/image-stats/scripts/__pycache__/image_stats_test.cpython-314.pyc +0 -0
  57. package/skills/image-stats/scripts/image_stats.py +368 -0
  58. package/skills/image-stats/scripts/image_stats_test.py +181 -0
  59. package/skills/pptx/SKILL.md +123 -95
  60. package/skills/pptx/design.md +82 -0
  61. package/skills/pptx/examples/README.md +29 -0
  62. package/skills/pptx/examples/hybrid_example.mjs +107 -0
  63. package/skills/pptx/examples/hybrid_example.pptx +0 -0
  64. package/skills/pptx/pptxgenjs.md +41 -0
  65. package/skills/pptx/scripts/deck_helpers.mjs +224 -0
  66. package/skills/pptx/scripts/layout_lint.py +293 -0
  67. package/skills/pptx/scripts/layout_lint_test.py +160 -0
  68. package/skills/pptx/scripts/opc_check.py +339 -0
  69. package/skills/pptx/scripts/svg_fallback.mjs +145 -0
  70. package/skills/pptx/svg.md +67 -0
  71. package/skills/tts-zh/SKILL.md +19 -2
  72. package/skills/ui-design/SKILL.md +158 -0
  73. package/skills/ui-design/claude_design.md +309 -0
  74. package/skills/ui-design/evals/evals.json +254 -0
  75. package/skills/ui-design/references/aesthetic-direction.md +83 -0
  76. package/skills/ui-design/references/claude-design-protocol.md +36 -0
  77. package/skills/ui-design/references/design-context.md +53 -0
  78. package/skills/ui-design/references/design-execution-playbook.md +82 -0
  79. package/skills/ui-design/references/export-pptx.md +124 -0
  80. package/skills/ui-design/references/layout-composition.md +654 -0
  81. package/skills/ui-design/references/preview-feedback.md +37 -0
  82. package/skills/ui-design/references/source-image-layout.md +189 -0
  83. package/skills/ui-design/references/starter-components.md +78 -0
  84. package/skills/ui-design/references/vi-aegis-vector.md +144 -0
  85. package/skills/ui-design/references/vi-amber-prism.md +133 -0
  86. package/skills/ui-design/references/vi-aqua-glass.md +200 -0
  87. package/skills/ui-design/references/vi-comfort-vision.md +147 -0
  88. package/skills/ui-design/references/vi-ember-prism.md +140 -0
  89. package/skills/ui-design/references/vi-nebula-console.md +151 -0
  90. package/skills/ui-design/references/vi-obsidian-optics.md +139 -0
  91. package/skills/ui-design/references/vi-obsidian-xui.md +143 -0
  92. package/skills/ui-design/references/vi-precision-horizon-dark.md +117 -0
  93. package/skills/ui-design/references/vi-precision-horizon.md +215 -0
  94. package/skills/ui-design/references/vi-solar-relay.md +130 -0
  95. package/skills/ui-design/references/vi-template-workflow.md +48 -0
  96. package/skills/ui-design/references/vi-verdant-grid.md +141 -0
  97. package/skills/ui-design/references/vi-violet-meridian.md +172 -0
  98. package/skills/ui-design/scripts/__pycache__/assemble_material.cpython-314.pyc +0 -0
  99. package/skills/ui-design/scripts/__pycache__/assemble_material_test.cpython-314.pyc +0 -0
  100. package/skills/ui-design/scripts/__pycache__/chart_sanity_check.cpython-314.pyc +0 -0
  101. package/skills/ui-design/scripts/__pycache__/check_language_policy.cpython-314.pyc +0 -0
  102. package/skills/ui-design/scripts/__pycache__/check_ui_artifact.cpython-314.pyc +0 -0
  103. package/skills/ui-design/scripts/__pycache__/check_ui_artifact_pptx_test.cpython-314.pyc +0 -0
  104. package/skills/ui-design/scripts/__pycache__/deck_pages_check.cpython-314.pyc +0 -0
  105. package/skills/ui-design/scripts/__pycache__/deck_pages_check_test.cpython-314.pyc +0 -0
  106. package/skills/ui-design/scripts/__pycache__/deck_visual_style_check.cpython-314.pyc +0 -0
  107. package/skills/ui-design/scripts/__pycache__/deck_visual_style_check_test.cpython-314.pyc +0 -0
  108. package/skills/ui-design/scripts/__pycache__/design_budget_check.cpython-314.pyc +0 -0
  109. package/skills/ui-design/scripts/__pycache__/design_budget_check_test.cpython-314.pyc +0 -0
  110. package/skills/ui-design/scripts/__pycache__/design_repair_hints.cpython-314.pyc +0 -0
  111. package/skills/ui-design/scripts/__pycache__/design_repair_hints_test.cpython-314.pyc +0 -0
  112. package/skills/ui-design/scripts/__pycache__/design_style_check.cpython-314.pyc +0 -0
  113. package/skills/ui-design/scripts/__pycache__/design_style_check_test.cpython-314.pyc +0 -0
  114. package/skills/ui-design/scripts/__pycache__/draft_coverage_check.cpython-314.pyc +0 -0
  115. package/skills/ui-design/scripts/__pycache__/extract_source_assets.cpython-314.pyc +0 -0
  116. package/skills/ui-design/scripts/__pycache__/extract_source_assets_test.cpython-314.pyc +0 -0
  117. package/skills/ui-design/scripts/__pycache__/generated_image_review_collect.cpython-314.pyc +0 -0
  118. package/skills/ui-design/scripts/__pycache__/generated_image_review_collect_test.cpython-314.pyc +0 -0
  119. package/skills/ui-design/scripts/__pycache__/image_plan_lint.cpython-314.pyc +0 -0
  120. package/skills/ui-design/scripts/__pycache__/image_plan_lint_test.cpython-314.pyc +0 -0
  121. package/skills/ui-design/scripts/__pycache__/image_prompt_rewrite.cpython-314.pyc +0 -0
  122. package/skills/ui-design/scripts/__pycache__/image_prompt_rewrite_test.cpython-314.pyc +0 -0
  123. package/skills/ui-design/scripts/__pycache__/layout_balance.cpython-314.pyc +0 -0
  124. package/skills/ui-design/scripts/__pycache__/layout_balance_test.cpython-314.pyc +0 -0
  125. package/skills/ui-design/scripts/__pycache__/normalize_design_json.cpython-314.pyc +0 -0
  126. package/skills/ui-design/scripts/__pycache__/normalize_design_json_test.cpython-314.pyc +0 -0
  127. package/skills/ui-design/scripts/__pycache__/plan_source_layout.cpython-314.pyc +0 -0
  128. package/skills/ui-design/scripts/__pycache__/plan_source_layout_test.cpython-314.pyc +0 -0
  129. package/skills/ui-design/scripts/__pycache__/resolve_source_hint.cpython-314.pyc +0 -0
  130. package/skills/ui-design/scripts/__pycache__/resolve_source_hint_test.cpython-314.pyc +0 -0
  131. package/skills/ui-design/scripts/__pycache__/resolve_source_input.cpython-314.pyc +0 -0
  132. package/skills/ui-design/scripts/__pycache__/resolve_source_input_test.cpython-314.pyc +0 -0
  133. package/skills/ui-design/scripts/__pycache__/scaffold_ui_artifact.cpython-314.pyc +0 -0
  134. package/skills/ui-design/scripts/__pycache__/source_usage_check.cpython-314.pyc +0 -0
  135. package/skills/ui-design/scripts/__pycache__/source_usage_check_test.cpython-314.pyc +0 -0
  136. package/skills/ui-design/scripts/__pycache__/text_readability_pixel.cpython-314.pyc +0 -0
  137. package/skills/ui-design/scripts/__pycache__/visual_quality_gate.cpython-314.pyc +0 -0
  138. package/skills/ui-design/scripts/__pycache__/visual_quality_gate_test.cpython-314.pyc +0 -0
  139. package/skills/ui-design/scripts/assemble_material.py +217 -0
  140. package/skills/ui-design/scripts/assemble_material_test.py +149 -0
  141. package/skills/ui-design/scripts/browser_check_ui_artifact.mjs +221 -0
  142. package/skills/ui-design/scripts/chart_sanity_check.py +261 -0
  143. package/skills/ui-design/scripts/chart_sanity_check_test.py +165 -0
  144. package/skills/ui-design/scripts/check_language_policy.py +351 -0
  145. package/skills/ui-design/scripts/check_ui_artifact.py +435 -0
  146. package/skills/ui-design/scripts/check_ui_artifact_pptx_test.py +251 -0
  147. package/skills/ui-design/scripts/deck_pages_check.py +158 -0
  148. package/skills/ui-design/scripts/deck_pages_check_test.py +134 -0
  149. package/skills/ui-design/scripts/deck_visual_style_check.py +767 -0
  150. package/skills/ui-design/scripts/deck_visual_style_check_test.py +713 -0
  151. package/skills/ui-design/scripts/design_budget_check.py +271 -0
  152. package/skills/ui-design/scripts/design_budget_check_test.py +183 -0
  153. package/skills/ui-design/scripts/design_repair_hints.py +162 -0
  154. package/skills/ui-design/scripts/design_repair_hints_test.py +54 -0
  155. package/skills/ui-design/scripts/design_style_check.py +397 -0
  156. package/skills/ui-design/scripts/design_style_check_test.py +233 -0
  157. package/skills/ui-design/scripts/draft_coverage_check.py +184 -0
  158. package/skills/ui-design/scripts/draft_coverage_check_test.py +131 -0
  159. package/skills/ui-design/scripts/export_pptx_from_html.mjs +2367 -0
  160. package/skills/ui-design/scripts/extract_source_assets.py +607 -0
  161. package/skills/ui-design/scripts/extract_source_assets_test.py +256 -0
  162. package/skills/ui-design/scripts/generated_image_review_collect.py +206 -0
  163. package/skills/ui-design/scripts/generated_image_review_collect_test.py +91 -0
  164. package/skills/ui-design/scripts/image_plan_lint.py +571 -0
  165. package/skills/ui-design/scripts/image_plan_lint_test.py +515 -0
  166. package/skills/ui-design/scripts/image_prompt_rewrite.py +713 -0
  167. package/skills/ui-design/scripts/image_prompt_rewrite_test.py +966 -0
  168. package/skills/ui-design/scripts/layout_balance.py +881 -0
  169. package/skills/ui-design/scripts/layout_balance_test.py +927 -0
  170. package/skills/ui-design/scripts/normalize_design_json.py +272 -0
  171. package/skills/ui-design/scripts/normalize_design_json_test.py +119 -0
  172. package/skills/ui-design/scripts/plan_source_layout.py +1450 -0
  173. package/skills/ui-design/scripts/plan_source_layout_test.py +1136 -0
  174. package/skills/ui-design/scripts/resolve_source_input.py +270 -0
  175. package/skills/ui-design/scripts/resolve_source_input_test.py +98 -0
  176. package/skills/ui-design/scripts/scaffold_ui_artifact.py +377 -0
  177. package/skills/ui-design/scripts/source_usage_check.py +581 -0
  178. package/skills/ui-design/scripts/source_usage_check_test.py +427 -0
  179. package/skills/ui-design/scripts/text_readability_pixel.py +340 -0
  180. package/skills/ui-design/scripts/text_readability_pixel_test.py +219 -0
  181. package/skills/ui-design/scripts/visual_quality_gate.py +301 -0
  182. package/skills/ui-design/scripts/visual_quality_gate_test.py +233 -0
  183. package/skills/ui-design/tests/e2e/prompts/deck.md +19 -0
  184. package/skills/ui-design/tests/e2e/prompts/webpage.md +17 -0
  185. package/skills/ui-design/tests/e2e/render_check.mjs +101 -0
  186. package/skills/ui-design/tests/e2e/rubric.json +54 -0
  187. package/skills/ui-design/tests/e2e/runs/aegis-vector-preview-1/checks/browser-check.json +49 -0
  188. package/skills/ui-design/tests/e2e/runs/aegis-vector-preview-1/checks/language-check.txt +12 -0
  189. package/skills/ui-design/tests/e2e/runs/aegis-vector-preview-1/checks/static-check.txt +15 -0
  190. package/skills/ui-design/tests/e2e/runs/aegis-vector-preview-1/outputs/index.html +837 -0
  191. package/skills/ui-design/tests/e2e/runs/aegis-vector-preview-1/prompt.md +12 -0
  192. package/skills/ui-design/tests/e2e/runs/aegis-vector-preview-1/review.md +21 -0
  193. package/skills/ui-design/tests/e2e/runs/aegis-vector-preview-1/screenshots/desktop.png +0 -0
  194. package/skills/ui-design/tests/e2e/runs/aegis-vector-preview-1/screenshots/mobile.png +0 -0
  195. package/skills/ui-design/tests/e2e/runs/amber-prism-screenshot-check-1/outputs/homepage.html +373 -0
  196. package/skills/ui-design/tests/e2e/runs/amber-prism-screenshot-check-1/outputs/platform.html +378 -0
  197. package/skills/ui-design/tests/e2e/runs/amber-prism-screenshot-check-1/prompt.md +13 -0
  198. package/skills/ui-design/tests/e2e/runs/amber-prism-screenshot-check-1/review.md +26 -0
  199. package/skills/ui-design/tests/e2e/runs/amber-prism-screenshot-check-1/screenshots/homepage/browser-check.json +49 -0
  200. package/skills/ui-design/tests/e2e/runs/amber-prism-screenshot-check-1/screenshots/homepage/desktop.png +0 -0
  201. package/skills/ui-design/tests/e2e/runs/amber-prism-screenshot-check-1/screenshots/homepage/mobile.png +0 -0
  202. package/skills/ui-design/tests/e2e/runs/amber-prism-screenshot-check-1/screenshots/platform/browser-check.json +49 -0
  203. package/skills/ui-design/tests/e2e/runs/amber-prism-screenshot-check-1/screenshots/platform/desktop.png +0 -0
  204. package/skills/ui-design/tests/e2e/runs/amber-prism-screenshot-check-1/screenshots/platform/mobile.png +0 -0
  205. package/skills/ui-design/tests/e2e/runs/aqua-glass-iteration-1/checks/static-check.txt +2 -0
  206. package/skills/ui-design/tests/e2e/runs/aqua-glass-iteration-1/outputs/index.html +656 -0
  207. package/skills/ui-design/tests/e2e/runs/aqua-glass-iteration-1/prompt.md +12 -0
  208. package/skills/ui-design/tests/e2e/runs/aqua-glass-iteration-1/render_check.mjs +67 -0
  209. package/skills/ui-design/tests/e2e/runs/aqua-glass-iteration-2/checks/static-check.txt +2 -0
  210. package/skills/ui-design/tests/e2e/runs/aqua-glass-iteration-2/outputs/index.html +587 -0
  211. package/skills/ui-design/tests/e2e/runs/aqua-glass-iteration-2/prompt.md +12 -0
  212. package/skills/ui-design/tests/e2e/runs/aqua-glass-iteration-2/review.md +36 -0
  213. package/skills/ui-design/tests/e2e/runs/aqua-glass-volcano-refresh-1/checks/browser-check.json +49 -0
  214. package/skills/ui-design/tests/e2e/runs/aqua-glass-volcano-refresh-1/checks/language-check.txt +8 -0
  215. package/skills/ui-design/tests/e2e/runs/aqua-glass-volcano-refresh-1/checks/static-check.txt +16 -0
  216. package/skills/ui-design/tests/e2e/runs/aqua-glass-volcano-refresh-1/outputs/index.html +739 -0
  217. package/skills/ui-design/tests/e2e/runs/aqua-glass-volcano-refresh-1/prompt.md +11 -0
  218. package/skills/ui-design/tests/e2e/runs/aqua-glass-volcano-refresh-1/review.md +22 -0
  219. package/skills/ui-design/tests/e2e/runs/aqua-glass-volcano-refresh-1/screenshots/desktop.png +0 -0
  220. package/skills/ui-design/tests/e2e/runs/aqua-glass-volcano-refresh-1/screenshots/mobile.png +0 -0
  221. package/skills/ui-design/tests/e2e/runs/comfort-vision-preview-1/checks/browser-check.json +52 -0
  222. package/skills/ui-design/tests/e2e/runs/comfort-vision-preview-1/outputs/index.html +641 -0
  223. package/skills/ui-design/tests/e2e/runs/comfort-vision-preview-1/screenshots/desktop.png +0 -0
  224. package/skills/ui-design/tests/e2e/runs/comfort-vision-preview-1/screenshots/mobile.png +0 -0
  225. package/skills/ui-design/tests/e2e/runs/ember-prism-red-preview-1/outputs/index.html +467 -0
  226. package/skills/ui-design/tests/e2e/runs/ember-prism-red-preview-1/screenshots/browser-check.json +49 -0
  227. package/skills/ui-design/tests/e2e/runs/ember-prism-red-preview-1/screenshots/desktop.png +0 -0
  228. package/skills/ui-design/tests/e2e/runs/ember-prism-red-preview-1/screenshots/mobile.png +0 -0
  229. package/skills/ui-design/tests/e2e/runs/ember-prism-screenshot-check-1/outputs/homepage.html +373 -0
  230. package/skills/ui-design/tests/e2e/runs/ember-prism-screenshot-check-1/outputs/platform.html +378 -0
  231. package/skills/ui-design/tests/e2e/runs/ember-prism-screenshot-check-1/prompt.md +13 -0
  232. package/skills/ui-design/tests/e2e/runs/ember-prism-screenshot-check-1/review.md +26 -0
  233. package/skills/ui-design/tests/e2e/runs/ember-prism-screenshot-check-1/screenshots/homepage/browser-check.json +49 -0
  234. package/skills/ui-design/tests/e2e/runs/ember-prism-screenshot-check-1/screenshots/homepage/desktop.png +0 -0
  235. package/skills/ui-design/tests/e2e/runs/ember-prism-screenshot-check-1/screenshots/homepage/mobile.png +0 -0
  236. package/skills/ui-design/tests/e2e/runs/ember-prism-screenshot-check-1/screenshots/platform/browser-check.json +49 -0
  237. package/skills/ui-design/tests/e2e/runs/ember-prism-screenshot-check-1/screenshots/platform/desktop.png +0 -0
  238. package/skills/ui-design/tests/e2e/runs/ember-prism-screenshot-check-1/screenshots/platform/mobile.png +0 -0
  239. package/skills/ui-design/tests/e2e/runs/empty-agent-protocol-check/checks/browser-check.json +49 -0
  240. package/skills/ui-design/tests/e2e/runs/empty-agent-protocol-check/checks/language-check.txt +4 -0
  241. package/skills/ui-design/tests/e2e/runs/empty-agent-protocol-check/checks/static-check.txt +2 -0
  242. package/skills/ui-design/tests/e2e/runs/empty-agent-protocol-check/execution_notes.md +10 -0
  243. package/skills/ui-design/tests/e2e/runs/empty-agent-protocol-check/outputs/index.html +587 -0
  244. package/skills/ui-design/tests/e2e/runs/empty-agent-protocol-check/prompt.md +14 -0
  245. package/skills/ui-design/tests/e2e/runs/empty-agent-protocol-check/review.md +13 -0
  246. package/skills/ui-design/tests/e2e/runs/empty-agent-protocol-check/screenshots/desktop.png +0 -0
  247. package/skills/ui-design/tests/e2e/runs/empty-agent-protocol-check/screenshots/mobile.png +0 -0
  248. package/skills/ui-design/tests/e2e/runs/iteration-1/deck/checks/browser-check.json +24 -0
  249. package/skills/ui-design/tests/e2e/runs/iteration-1/deck/checks/static-check.txt +2 -0
  250. package/skills/ui-design/tests/e2e/runs/iteration-1/deck/execution_notes.md +21 -0
  251. package/skills/ui-design/tests/e2e/runs/iteration-1/deck/outputs/deck.html +451 -0
  252. package/skills/ui-design/tests/e2e/runs/iteration-1/deck/screenshots/slide-01.png +0 -0
  253. package/skills/ui-design/tests/e2e/runs/iteration-1/deck/screenshots/slide-02.png +0 -0
  254. package/skills/ui-design/tests/e2e/runs/iteration-1/review.md +79 -0
  255. package/skills/ui-design/tests/e2e/runs/iteration-1/webpage/checks/browser-check.json +26 -0
  256. package/skills/ui-design/tests/e2e/runs/iteration-1/webpage/checks/static-check.txt +2 -0
  257. package/skills/ui-design/tests/e2e/runs/iteration-1/webpage/execution_notes.md +20 -0
  258. package/skills/ui-design/tests/e2e/runs/iteration-1/webpage/outputs/index.html +534 -0
  259. package/skills/ui-design/tests/e2e/runs/iteration-1/webpage/screenshots/desktop.png +0 -0
  260. package/skills/ui-design/tests/e2e/runs/iteration-1/webpage/screenshots/mobile.png +0 -0
  261. package/skills/ui-design/tests/e2e/runs/nebula-console-iteration-1/checks/browser-check.json +64 -0
  262. package/skills/ui-design/tests/e2e/runs/nebula-console-iteration-1/checks/language-check.txt +7 -0
  263. package/skills/ui-design/tests/e2e/runs/nebula-console-iteration-1/checks/quick-validate.txt +1 -0
  264. package/skills/ui-design/tests/e2e/runs/nebula-console-iteration-1/checks/static-console.txt +2 -0
  265. package/skills/ui-design/tests/e2e/runs/nebula-console-iteration-1/checks/static-homepage.txt +2 -0
  266. package/skills/ui-design/tests/e2e/runs/nebula-console-iteration-1/outputs/console.html +600 -0
  267. package/skills/ui-design/tests/e2e/runs/nebula-console-iteration-1/outputs/homepage.html +556 -0
  268. package/skills/ui-design/tests/e2e/runs/nebula-console-iteration-1/prompt.md +29 -0
  269. package/skills/ui-design/tests/e2e/runs/nebula-console-iteration-1/review.md +57 -0
  270. package/skills/ui-design/tests/e2e/runs/nebula-console-iteration-1/screenshots/console-desktop.png +0 -0
  271. package/skills/ui-design/tests/e2e/runs/nebula-console-iteration-1/screenshots/console-mobile.png +0 -0
  272. package/skills/ui-design/tests/e2e/runs/nebula-console-iteration-1/screenshots/homepage-desktop.png +0 -0
  273. package/skills/ui-design/tests/e2e/runs/nebula-console-iteration-1/screenshots/homepage-mobile.png +0 -0
  274. package/skills/ui-design/tests/e2e/runs/obsidian-optics-iteration-1/checks/browser-check.json +33 -0
  275. package/skills/ui-design/tests/e2e/runs/obsidian-optics-iteration-1/checks/language-check.txt +6 -0
  276. package/skills/ui-design/tests/e2e/runs/obsidian-optics-iteration-1/checks/quick-validate.txt +1 -0
  277. package/skills/ui-design/tests/e2e/runs/obsidian-optics-iteration-1/checks/static-check.txt +2 -0
  278. package/skills/ui-design/tests/e2e/runs/obsidian-optics-iteration-1/outputs/index.html +682 -0
  279. package/skills/ui-design/tests/e2e/runs/obsidian-optics-iteration-1/prompt.md +13 -0
  280. package/skills/ui-design/tests/e2e/runs/obsidian-optics-iteration-1/review.md +59 -0
  281. package/skills/ui-design/tests/e2e/runs/obsidian-optics-iteration-1/screenshots/desktop.png +0 -0
  282. package/skills/ui-design/tests/e2e/runs/obsidian-optics-iteration-1/screenshots/mobile.png +0 -0
  283. package/skills/ui-design/tests/e2e/runs/obsidian-xui-screenshot-check-1/outputs/page-01.html +466 -0
  284. package/skills/ui-design/tests/e2e/runs/obsidian-xui-screenshot-check-1/outputs/page-02.html +433 -0
  285. package/skills/ui-design/tests/e2e/runs/obsidian-xui-screenshot-check-1/prompt.md +13 -0
  286. package/skills/ui-design/tests/e2e/runs/obsidian-xui-screenshot-check-1/screenshots/page-01/browser-check.json +49 -0
  287. package/skills/ui-design/tests/e2e/runs/obsidian-xui-screenshot-check-1/screenshots/page-01/desktop.png +0 -0
  288. package/skills/ui-design/tests/e2e/runs/obsidian-xui-screenshot-check-1/screenshots/page-01/mobile.png +0 -0
  289. package/skills/ui-design/tests/e2e/runs/obsidian-xui-screenshot-check-1/screenshots/page-02/browser-check.json +49 -0
  290. package/skills/ui-design/tests/e2e/runs/obsidian-xui-screenshot-check-1/screenshots/page-02/desktop.png +0 -0
  291. package/skills/ui-design/tests/e2e/runs/obsidian-xui-screenshot-check-1/screenshots/page-02/mobile.png +0 -0
  292. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-1/checks/browser-check.json +49 -0
  293. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-1/checks/language-check.txt +3 -0
  294. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-1/checks/static-check.txt +2 -0
  295. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-1/execution_notes.md +24 -0
  296. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-1/outputs/index.html +723 -0
  297. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-1/prompt.md +15 -0
  298. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-1/review.md +26 -0
  299. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-1/screenshots/desktop.png +0 -0
  300. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-1/screenshots/mobile.png +0 -0
  301. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-2/checks/browser-dark.json +49 -0
  302. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-2/checks/browser-light.json +49 -0
  303. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-2/checks/language-check.txt +11 -0
  304. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-2/checks/static-dark.txt +2 -0
  305. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-2/checks/static-light.txt +3 -0
  306. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-2/execution_notes.md +23 -0
  307. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-2/outputs/dark.html +392 -0
  308. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-2/outputs/light.html +476 -0
  309. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-2/prompt.md +23 -0
  310. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-2/review.md +41 -0
  311. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-2/screenshots/dark/desktop.png +0 -0
  312. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-2/screenshots/dark/mobile.png +0 -0
  313. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-2/screenshots/light/desktop.png +0 -0
  314. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-2/screenshots/light/mobile.png +0 -0
  315. package/skills/ui-design/tests/e2e/runs/solar-relay-iteration-1/checks/browser-check.json +54 -0
  316. package/skills/ui-design/tests/e2e/runs/solar-relay-iteration-1/checks/language-check.txt +5 -0
  317. package/skills/ui-design/tests/e2e/runs/solar-relay-iteration-1/checks/quick-validate.txt +1 -0
  318. package/skills/ui-design/tests/e2e/runs/solar-relay-iteration-1/checks/static-check.txt +2 -0
  319. package/skills/ui-design/tests/e2e/runs/solar-relay-iteration-1/outputs/index.html +765 -0
  320. package/skills/ui-design/tests/e2e/runs/solar-relay-iteration-1/prompt.md +15 -0
  321. package/skills/ui-design/tests/e2e/runs/solar-relay-iteration-1/review.md +59 -0
  322. package/skills/ui-design/tests/e2e/runs/solar-relay-iteration-1/screenshots/desktop.png +0 -0
  323. package/skills/ui-design/tests/e2e/runs/solar-relay-iteration-1/screenshots/mobile.png +0 -0
  324. package/skills/ui-design/tests/e2e/runs/verdant-grid-preview-1/outputs/index.html +508 -0
  325. package/skills/ui-design/tests/e2e/runs/verdant-grid-preview-1/screenshots/browser-check.json +49 -0
  326. package/skills/ui-design/tests/e2e/runs/verdant-grid-preview-1/screenshots/desktop.png +0 -0
  327. package/skills/ui-design/tests/e2e/runs/verdant-grid-preview-1/screenshots/mobile.png +0 -0
  328. package/skills/ui-design/tests/e2e/runs/violet-meridian-iteration-1/checks/browser-check.json +54 -0
  329. package/skills/ui-design/tests/e2e/runs/violet-meridian-iteration-1/checks/language-check.txt +6 -0
  330. package/skills/ui-design/tests/e2e/runs/violet-meridian-iteration-1/checks/quick-validate.txt +1 -0
  331. package/skills/ui-design/tests/e2e/runs/violet-meridian-iteration-1/checks/static-check.txt +2 -0
  332. package/skills/ui-design/tests/e2e/runs/violet-meridian-iteration-1/outputs/index.html +788 -0
  333. package/skills/ui-design/tests/e2e/runs/violet-meridian-iteration-1/prompt.md +14 -0
  334. package/skills/ui-design/tests/e2e/runs/violet-meridian-iteration-1/review.md +60 -0
  335. package/skills/ui-design/tests/e2e/runs/violet-meridian-iteration-1/screenshots/desktop.png +0 -0
  336. package/skills/ui-design/tests/e2e/runs/violet-meridian-iteration-1/screenshots/mobile.png +0 -0
  337. package/skills/video-compose/SKILL.md +8 -1
  338. package/skills/web-crawler/SKILL.md +70 -0
  339. package/skills/web-crawler/scripts/crawl.mjs +538 -0
  340. package/skills/web-crawler/scripts/crawl.test.mjs +204 -0
  341. package/skills/xlsx/SKILL.md +12 -0
  342. package/skills/xlsx/scripts/opc_check.py +253 -0
  343. package/src/advisorState.js +22 -0
  344. package/src/askBridge.js +87 -3
  345. package/src/bootstrap/workingDir.js +12 -1
  346. package/src/cli/args.js +17 -1
  347. package/src/cli/configMenu.js +236 -0
  348. package/src/cli/configWizard.js +7 -5
  349. package/src/cli/print.js +37 -4
  350. package/src/cli/streamJson.js +77 -24
  351. package/src/cli/wizards/advisorWizard.js +147 -0
  352. package/src/cli/wizards/fieldForm.js +149 -0
  353. package/src/cli/wizards/imageWizard.js +60 -0
  354. package/src/cli/wizards/openRouterWizard.js +23 -0
  355. package/src/cli/wizards/providerPresets.js +95 -0
  356. package/src/cli/wizards/providerWizard.js +219 -0
  357. package/src/cli/wizards/searchWizard.js +23 -0
  358. package/src/cli/wizards/ttsWizard.js +34 -0
  359. package/src/config/configFile.js +441 -83
  360. package/src/config/configService.js +446 -0
  361. package/src/config.js +24 -64
  362. package/src/context/agentHome.js +9 -0
  363. package/src/context/archive.js +3 -2
  364. package/src/context/compact.js +8 -5
  365. package/src/context/contextLock.js +81 -0
  366. package/src/context/persistContext.js +30 -11
  367. package/src/context/reactiveCompact.js +4 -2
  368. package/src/context/recentDirs.js +21 -61
  369. package/src/context/sessionPath.js +97 -23
  370. package/src/context/sessionRegistry.js +303 -24
  371. package/src/context/tokenCounter.js +18 -0
  372. package/src/context/workspaceIndex.js +731 -0
  373. package/src/infra/verification.js +3 -2
  374. package/src/llm/client.js +293 -206
  375. package/src/llm/providers/anthropicFormat.js +241 -0
  376. package/src/llm/providers/openaiFormat.js +190 -0
  377. package/src/loop.js +211 -27
  378. package/src/main.js +61 -11
  379. package/src/messageQueue.js +97 -0
  380. package/src/multimodal.js +203 -0
  381. package/src/plugin-sdk.js +15 -0
  382. package/src/plugins/commandRouter.js +3 -0
  383. package/src/plugins/hookEngine.js +5 -1
  384. package/src/plugins/pluginLifecycle.js +28 -0
  385. package/src/plugins/pluginRunner.js +40 -0
  386. package/src/plugins/transcript.js +4 -3
  387. package/src/prompts/pluginList.js +2 -2
  388. package/src/prompts/projectInstructions.js +3 -3
  389. package/src/prompts/promptAssembly.js +133 -109
  390. package/src/prompts/skillList.js +12 -22
  391. package/src/session/runAgentTurn.js +70 -0
  392. package/src/session/runTurn.js +41 -6
  393. package/src/toolGate.js +17 -0
  394. package/src/tools/advisor/advisor.js +379 -0
  395. package/src/tools/agent/agent.js +555 -0
  396. package/src/tools/agent/agentTypes.js +59 -0
  397. package/src/tools/agent/coordinator.js +93 -0
  398. package/src/tools/agent/worktree.js +270 -0
  399. package/src/tools/ask/ask.js +7 -2
  400. package/src/tools/bash/bash.js +112 -18
  401. package/src/tools/download/download.js +144 -0
  402. package/src/tools/edit/edit.js +1 -0
  403. package/src/tools/edit/multi-edit.js +1 -0
  404. package/src/tools/genimage/genimage.js +242 -0
  405. package/src/tools/genspeech/genspeech.js +67 -0
  406. package/src/tools/glob/glob.js +1 -0
  407. package/src/tools/grep/grep.js +3 -0
  408. package/src/tools/grep/rgPath.js +2 -0
  409. package/src/tools/index.js +25 -2
  410. package/src/tools/read/read.js +1 -0
  411. package/src/tools/shared/killTree.js +52 -0
  412. package/src/tools/toolProgressBridge.js +48 -0
  413. package/src/tools/webbrowser/agentBrowserPath.js +258 -0
  414. package/src/tools/webbrowser/browser.js +59 -42
  415. package/src/tools/webbrowser/browserControl.js +118 -0
  416. package/src/tools/webbrowser/browserDaemon.js +97 -0
  417. package/src/tools/webbrowser/browserService.js +27 -0
  418. package/src/tools/webbrowser/browserStream.js +119 -0
  419. package/src/tools/webbrowser/webbrowser.js +699 -130
  420. package/src/tools/webfetch/webfetch.js +1 -0
  421. package/src/tools/websearch/websearch.js +134 -51
  422. package/src/tools/write/write.js +1 -0
  423. package/src/ui/App.js +249 -22
  424. package/src/ui/AskQuestion.js +19 -17
  425. package/src/ui/InputBox.js +34 -9
  426. package/src/ui/LiveArea.js +112 -19
  427. package/src/ui/MessageList.js +100 -14
  428. package/src/ui/ModelPicker.js +56 -0
  429. package/src/ui/QueuedMessagesBanner.js +14 -0
  430. package/src/ui/Root.js +18 -10
  431. package/src/ui/SessionPicker.js +102 -0
  432. package/src/ui/StatusLine.js +13 -4
  433. package/src/ui/WorkspacePicker.js +55 -0
  434. package/src/ui/figures.js +44 -0
  435. package/src/ui/hooks/askSlot.js +82 -0
  436. package/src/ui/hooks/useChat.js +345 -64
  437. package/src/ui/hooks/usePasteHandler.js +11 -1
  438. package/src/ui/hooks/useTextBuffer.js +1 -3
  439. package/src/ui/liveViewport.js +5 -1
  440. package/src/ui/markdown.js +206 -0
  441. package/src/ui/textBuffer.js +0 -7
  442. package/src/ui/theme.js +32 -0
  443. package/src/utils/debugLog.js +0 -4
  444. package/src/utils/resourcePaths.js +2 -2
  445. package/src/utils/stripThink.js +50 -0
  446. package/src/web/browserBridge.js +447 -0
  447. package/src/web/protocol.js +19 -0
  448. package/workflows/adaptive-research.yaml +9 -2
  449. package/workflows/book-review-short.yaml +31 -7
  450. package/workflows/e2e-write-greet.yaml +3 -0
  451. package/workflows/illustrate.yaml +227 -0
  452. package/workflows/plan.yaml +437 -207
  453. package/workflows/ppt.yaml +1659 -0
  454. package/workflows/ralph-loop.yaml +142 -112
  455. package/workflows/schema.json +81 -23
  456. package/workflows/webppt.yaml +496 -0
  457. package/workflows/word.yaml +710 -0
  458. package/workflows/youtube-shorts.yaml +38 -10
  459. package/MINIMAL-TRACE.md +0 -77
  460. package/plugins/workflow-runner/src/voteHelpers.js +0 -110
  461. package/skills/config/SKILL.md +0 -123
  462. package/skills/frontend-design/LICENSE.txt +0 -177
  463. package/skills/frontend-design/SKILL.md +0 -42
  464. package/skills/pptx/scripts/office/__pycache__/soffice.cpython-314.pyc +0 -0
  465. package/src/ui/ProgressPanel.js +0 -54
  466. package/src/ui/StreamingBlock.js +0 -11
  467. package/src/ui/ToolStatus.js +0 -11
@@ -13,14 +13,14 @@
13
13
  * 5. 中断后上下文完整:已完成的步骤全在 history 里,可继续/调整
14
14
  *
15
15
  * 与 llm 步的区别:
16
- * - agent 有 maxTurns 上限(默认 12),允许多轮工具调用
16
+ * - agent 有 maxTurns 上限(默认 25),允许多轮工具调用
17
17
  * - agent 的产出经过验证闸门(interpretMiniReActResult)
18
18
  * - llm 只跑单轮 chat(maxTurns=1)
19
19
  * ============================================================
20
20
  */
21
- import { runQuery } from '../../../../src/loop.js';
22
- import { interpolate } from '../expressions.js';
23
- import { describeCaptureContract, interpretMiniReActResult, } from '../agentCommon.js';
21
+ import { describeCaptureContract, describeLlmFailure, describeMaxTurnsCutoff, effectiveGoalAnchor, interpretMiniReActResult, runStepQuery, } from '../agentCommon.js';
22
+ import { evalExpr, interpolate } from '../expressions.js';
23
+ import { resolveWorkflowMultimodalOptions } from '../multimodal.js';
24
24
  /**
25
25
  * 执行一个 agent 步骤:构造 step prompt → 委托 runQuery(TAOR) → 提取结果。
26
26
  *
@@ -31,54 +31,143 @@ import { describeCaptureContract, interpretMiniReActResult, } from '../agentComm
31
31
  * 4. yield UI 事件(text_delta / tool_start / tool_end / ...)
32
32
  */
33
33
  export async function* execAgentStep(step, vars, ctx) {
34
- const maxTurns = step.maxTurns ?? 12;
34
+ // 默认 25(与 skill 步 skill.ts 对齐)。agent 步本就是「开放多工具任务」执行器,
35
+ // 弱模型常一轮只调一个工具(Read/Grep/Glob 各占一轮),旧默认 12 对探索类步骤
36
+ // (如 plan.yaml stage1_research)太紧——12 轮还没收集完就撞顶截断、误判失败。
37
+ // 这是安全阀不是性能阀;要更紧的步显式写小值即可。
38
+ const maxTurns = step.maxTurns ?? 25;
39
+ // 未定义变量插值可见化(裸 ${goal} 这类拼写/命名空间错的病根,静默空串会坑死下游)
40
+ const missing = [];
41
+ const iopts = { onMissing: (e) => missing.push(e) };
35
42
  // 构造用户提示:包含步骤指令 + 变量 + capture 约束 + 内部标记防护
36
- const userPrompt = interpolate(step.agent ?? '', vars) +
37
- describeCaptureContract(step) +
38
- (step.input ? `\n\n上下文变量:${interpolate(step.input, vars)}` : '') +
39
- '\n\n⚠️ 输出规范:直接输出你的工作成果即可,不要在输出中包含任何类似「放弃」、' +
40
- '「GIVEUP」、' +
41
- '「__AGENT__」或' +
42
- '「[wf-internal」的内部标记文本。';
43
+ const userPrompt = `${interpolate(step.agent ?? '', vars, iopts)}${describeCaptureContract(step)}${step.input ? `\n\n上下文变量:${interpolate(step.input, vars, iopts)}` : ''}\n\n⚠️ 输出规范:直接输出你的工作成果即可,不要在输出中包含任何类似「放弃」、「GIVEUP」、「__AGENT__」或「[wf-internal」的内部标记文本。`;
43
44
  // 如果有 output_schema,把 schema 约束嵌入 prompt(runQuery 不原生支持 responseFormat)
44
45
  const schemaHint = step.output_schema
45
46
  ? `\n\n【结构化输出约束】请严格按照以下 JSON Schema 输出结果(不要包裹在 markdown 代码块中):\n${JSON.stringify(step.output_schema, null, 2)}`
46
47
  : '';
47
48
  const fullPrompt = userPrompt + schemaHint;
48
- // 通用目标锚定:如果 runner 提供了 goalAnchor(从 inputs 自动提取),
49
- // 前置注入到 prompt。确保每步 LLM 都能看到用户原始目标,
49
+ // 通用目标锚定(原始目标 + 累积的用户补充要求),前置注入 prompt。
50
+ // 确保每步 LLM 都能看到用户原始目标与运行中的补充要求,
50
51
  // 避免长上下文 / fresh_context 导致目标被稀释或遗忘。
51
- const effectivePrompt = ctx.goalAnchor
52
- ? `${ctx.goalAnchor}\n\n${fullPrompt}`
53
- : fullPrompt;
54
- // 记录 runQuery 开始前的 history 长度,用于之后提取本步的 assistant 输出
55
- const historyLenBefore = ctx.history.length;
56
- // 委托 TAOR 引擎执行(携带 sessionState 以启用上下文压缩)
57
- yield* runQuery(effectivePrompt, {
58
- provider: ctx.provider,
59
- history: ctx.history,
60
- signal: ctx.signal,
52
+ const anchor = effectiveGoalAnchor(ctx);
53
+ const effectivePrompt = anchor ? `${anchor}\n\n${fullPrompt}` : fullPrompt;
54
+ const multimodal = resolveWorkflowMultimodalOptions(step, vars, ctx, (e) => missing.push(e));
55
+ if (missing.length > 0) {
56
+ yield {
57
+ type: 'warning',
58
+ message: `step "${step.id}": 模板变量未定义,已按空串插值:${[...new Set(missing)].map((m) => `\${${m}}`).join('、')}(裸名请用 inputs.<name> 或先 capture/set)`,
59
+ };
60
+ }
61
+ // 委托 TAOR 引擎执行(共享消费层 runStepQuery:携带 sessionState 以启用上下文压缩,
62
+ // 并统一侦测终结错误——runQuery 遇 LLM 失败是 yield error 后 return 不 throw)。
63
+ const { rawText, sawToolCall, toolNames, toolOkCount, toolFailCount, aborted, sawLlmError, sawMaxTurns, llmErrorDetail, } = yield* runStepQuery(effectivePrompt, step, ctx, {
61
64
  maxTurns,
62
- chat: ctx.chat,
63
- executeTool: ctx.executeTool,
64
- sessionState: ctx.sessionState,
65
+ ...multimodal,
65
66
  });
66
- // 从 history 中提取本步产生的最后一条 assistant message 内容(用于 capture 绑定)
67
- let rawText = '';
68
- for (let i = ctx.history.length - 1; i >= historyLenBefore; i--) {
69
- const msg = ctx.history[i];
70
- if (msg.role === 'assistant' && typeof msg.content === 'string' && msg.content.trim()) {
71
- rawText = msg.content;
72
- break;
73
- }
74
- }
75
- // 验证闸门:判断结果是"真交付"还是"放弃"
76
67
  const stepResult = {
77
68
  raw: { text: rawText, result: rawText },
78
69
  preview: rawText.length > 200 ? `${rawText.slice(0, 200)}...` : rawText,
79
70
  };
71
+ // 用户中断导致的空产出:不过验证闸门(否则会误抛「放弃: 没有产出任何结果」),
72
+ // 直接返回当前(可能为空的)结果,由 runner 据 signal.aborted 收口(不当作可重试失败)。
73
+ if (aborted)
74
+ return stepResult;
75
+ // P0-1(同 llm.ts / skill.ts):LLM 失败(超时/网络错)且本步无任何产出 →
76
+ // 抛「可重试失败」交给 runner 的 in-run retry。必须在验证闸门之前——
77
+ // 否则 provider 故障会被闸门误归因为「agent 步骤放弃: 没有产出任何结果」,
78
+ // 混淆「provider 失败(该重试)」与「模型真放弃/空答(诚实 halt)」。
79
+ if (!rawText.trim() && sawLlmError) {
80
+ // 抛错文案附带 provider 真实报错(经 runner 落 progress.md,刷屏后仍可排障)
81
+ throw new Error(describeLlmFailure(step.id, llmErrorDetail));
82
+ }
83
+ // 撞 maxTurns 截断:截断时刻的文本是半截工作叙述,不是可信交付——
84
+ // 即使非空也不能过闸门(曾被静默当成功:半成品 PPTX 被整条链当真交付)。
85
+ // 抛可重试失败 → in-run retry 在共享 history 上续作(模型看到已有进度接着干)。
86
+ if (sawMaxTurns) {
87
+ throw new Error(describeMaxTurnsCutoff(step.id, maxTurns));
88
+ }
89
+ // done_when 验收兜底(K3/A2):弱模型常「宣告要干活」却不调工具就 end_turn
90
+ //(announce-not-do,2026-06-12 plan 工作流实跑事故的执行步烂尾根因之一)。
91
+ // yaml 作者声明确定性完成条件(如 fileExists('./doc/x.pptx')),不满足时
92
+ // 引擎自动 nudge 同一 agent 在共享 history 上继续干(看得到自己刚才的半截工作),
93
+ // 把「对抗模型不可靠」从每个 yaml 的 prompt 工程收回引擎单点。
94
+ if (step.done_when) {
95
+ const NUDGES = 2; // 提醒上限:超过说明模型真干不动,交给 onError/max_retries
96
+ let result = resultIfTextLooksDeliverable(step, rawText);
97
+ let lastOutcome = {
98
+ rawText,
99
+ sawToolCall,
100
+ toolNames,
101
+ toolOkCount,
102
+ toolFailCount,
103
+ };
104
+ for (let n = 0;; n++) {
105
+ if (evalDoneWhen(step, vars)) {
106
+ return result ?? doneWhenSatisfiedResult(step);
107
+ }
108
+ if (n >= NUDGES) {
109
+ throw new Error(`agent 步骤 "${step.id}" 完成条件未满足(done_when: ${step.done_when}),已提醒 ${NUDGES} 次仍未达成。${describeAttemptForDoneWhen(lastOutcome)}`);
110
+ }
111
+ yield {
112
+ type: 'warning',
113
+ message: `⏩ step "${step.id}" 完成条件未满足(${step.done_when}),提醒 agent 继续执行(第 ${n + 1}/${NUDGES} 次)。${describeAttemptForDoneWhen(lastOutcome)}`,
114
+ };
115
+ const nudge = yield* runStepQuery(`检查发现这一步还没有真正完成:完成条件 \`${step.done_when}\` 仍不成立。不要解释、不要宣告计划——继续实际执行(调用工具落盘/操作),直到完成条件成立,然后给出简短完成报告。`, step, ctx, { maxTurns });
116
+ if (nudge.aborted)
117
+ return result ?? stepResult;
118
+ if (!nudge.rawText.trim() && nudge.sawLlmError) {
119
+ throw new Error(describeLlmFailure(step.id, nudge.llmErrorDetail));
120
+ }
121
+ if (nudge.sawMaxTurns) {
122
+ throw new Error(describeMaxTurnsCutoff(step.id, maxTurns));
123
+ }
124
+ lastOutcome = nudge;
125
+ if (nudge.rawText.trim()) {
126
+ result = resultIfTextLooksDeliverable(step, nudge.rawText) ?? result;
127
+ }
128
+ }
129
+ }
130
+ // 验证闸门:判断结果是"真交付"还是"放弃"。注意:带 done_when 的步骤已在上方
131
+ // 由确定性条件验收,避免"文件已落盘但最终空文本"或"还没落盘但空文本"绕过 nudge。
80
132
  const outcome = interpretMiniReActResult(stepResult, !!step.output_schema);
81
133
  if (!outcome.ok)
82
134
  throw new Error(`agent 步骤放弃: ${outcome.reason}`);
83
135
  return outcome.value;
84
136
  }
137
+ function evalDoneWhen(step, vars) {
138
+ try {
139
+ return Boolean(evalExpr(step.done_when ?? '', vars));
140
+ }
141
+ catch (e) {
142
+ // 表达式坏了是 yaml 作者错误:响亮失败(同 max_iterations 解析失败哲学),
143
+ // 不能静默当 false 把好结果 nudge 死、也不能静默当 true 放走烂尾。
144
+ throw new Error(`step "${step.id}" 的 done_when 表达式求值失败: ${e.message}`);
145
+ }
146
+ }
147
+ function doneWhenSatisfiedResult(step) {
148
+ const text = `完成条件已满足:${step.done_when}`;
149
+ return { raw: { text, result: text, content: text }, preview: text };
150
+ }
151
+ function resultIfTextLooksDeliverable(step, text) {
152
+ if (!text.trim())
153
+ return null;
154
+ const outcome = interpretMiniReActResult({
155
+ raw: { text, result: text },
156
+ preview: text.length > 200 ? `${text.slice(0, 200)}...` : text,
157
+ }, !!step.output_schema);
158
+ return outcome.ok ? outcome.value : null;
159
+ }
160
+ function describeAttemptForDoneWhen(outcome) {
161
+ const textState = outcome.rawText.trim()
162
+ ? '本轮有文本报告'
163
+ : '本轮没有文本报告';
164
+ if (!outcome.sawToolCall) {
165
+ return `${textState},且没有观察到工具调用;请不要只宣告计划,必须调用工具完成产物。`;
166
+ }
167
+ const names = outcome.toolNames.length ? outcome.toolNames.join('、') : '未知工具';
168
+ const wrote = outcome.toolNames.some((name) => ['Write', 'Edit', 'MultiEdit', 'Bash'].includes(name));
169
+ const writeHint = wrote
170
+ ? '已观察到写入类工具调用,但完成条件仍未满足,请检查路径或实际写入结果'
171
+ : '未观察到写入类工具调用,请立即调用 Write/Edit/Bash 落盘产物';
172
+ return `${textState};本轮工具调用:${names}(成功 ${outcome.toolOkCount},失败 ${outcome.toolFailCount})。${writeHint}。`;
173
+ }
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * ============================================================
3
- * src/workflows/stepExecutors/assert.ts —— assert step 执行
3
+ * plugins/workflow-runner/src/stepExecutors/assert.ts —— assert step 执行
4
4
  * ------------------------------------------------------------
5
5
  * evalExpr(condition) → 真值通过;假值抛错(带 onFail 自定义信息)。
6
6
  *
@@ -28,7 +28,8 @@ export function execAssertStep(step, vars, ctx) {
28
28
  }
29
29
  // ════ v0.9.3:上下文统一 —— assert 通过也留痕 ════
30
30
  // 让后续 LLM 步骤知道某个前置条件/不变量已确认满足。
31
- if (ctx?.history) {
31
+ // ctx 必填(签名强制,runner 唯一调用点 dispatchActionStep 始终传),直接访问 history。
32
+ if (ctx.history) {
32
33
  ctx.history.push({
33
34
  role: 'assistant',
34
35
  content: `[workflow:${step.id}] ✅ 断言通过:${step.condition}`,
@@ -6,8 +6,8 @@
6
6
  * (runQuery / src/loop.ts)执行。
7
7
  *
8
8
  * 与 agent 步的区别:
9
- * - llm 默认单轮(maxTurns=1),不期望工具调用(但 runQuery 允许模型自主决定)
10
- * - 有 context_files 时允许少量轮次读取文件后输出
9
+ * - llm 默认单轮(maxTurns=1),不下发工具
10
+ * - 有 context_files 或 allowed_tools 时允许少量轮次读取文件后输出
11
11
  * - output_schema 嵌入 prompt 作为结构化约束
12
12
  * - 不经过 interpretMiniReActResult 验证闸门(直接信任 LLM 输出)
13
13
  *
@@ -16,19 +16,23 @@
16
16
  * - `{ <field>: var_name }` 当 output_schema 存在时按 JSON 字段取值
17
17
  * ============================================================
18
18
  */
19
- import { runQuery } from '../../../../src/loop.js';
20
- import { extractJsonPayload } from '../jsonPayload.js';
19
+ import { describeLlmFailure, describeMaxTurnsCutoff, effectiveGoalAnchor, runStepQuery, } from '../agentCommon.js';
21
20
  import { interpolate } from '../expressions.js';
21
+ import { extractJsonPayload } from '../jsonPayload.js';
22
+ import { resolveWorkflowMultimodalOptions } from '../multimodal.js';
22
23
  export async function* execLlmStep(step, vars, ctx) {
23
24
  if (typeof step.llm !== 'string')
24
25
  throw new Error(`step ${step.id}: 缺少 llm 字段`);
25
- const prompt = interpolate(step.llm, vars);
26
+ // 未定义变量插值可见化(静默空串是下游 prompt 留白的病根)
27
+ const missing = [];
28
+ const iopts = { onMissing: (e) => missing.push(e) };
29
+ const prompt = interpolate(step.llm, vars, iopts);
26
30
  // 有 context_files → 在 prompt 中附上文件列表,runQuery 的 TAOR 循环中
27
31
  // 模型可自主用 Read 工具读取(ALL_TOOLS 全量可用)
28
32
  let fullPrompt = prompt;
29
33
  if (step.context_files && step.context_files.length > 0) {
30
34
  const fileList = step.context_files
31
- .map((f) => `- ${interpolate(f.path, vars)}${f.hint ? ` — ${f.hint}` : ''}`)
35
+ .map((f) => `- ${interpolate(f.path, vars, iopts)}${f.hint ? ` — ${f.hint}` : ''}`)
32
36
  .join('\n');
33
37
  fullPrompt = `${prompt}\n\n**以下上下文文件可用,请用 Read 工具按需读取**:\n${fileList}`;
34
38
  }
@@ -37,34 +41,38 @@ export async function* execLlmStep(step, vars, ctx) {
37
41
  ? `\n\n【结构化输出约束】请严格按照以下 JSON Schema 输出有效 JSON 对象本身(不要 markdown 代码块包裹):\n${JSON.stringify(step.output_schema, null, 2)}`
38
42
  : '';
39
43
  fullPrompt += schemaHint;
40
- // 通用目标锚定:同 agent.ts,确保每步 LLM 不偏离原始目标
41
- const effectivePrompt = ctx.goalAnchor
42
- ? `${ctx.goalAnchor}\n\n${fullPrompt}`
43
- : fullPrompt;
44
- // llm 步默认单轮;有 context_files 时允许多轮(读文件→输出)
44
+ // 通用目标锚定(原始目标 + 补充要求):同 agent.ts,确保每步 LLM 不偏离
45
+ const anchor = effectiveGoalAnchor(ctx);
46
+ const effectivePrompt = anchor ? `${anchor}\n\n${fullPrompt}` : fullPrompt;
47
+ const needsTools = !!step.context_files?.length || step.allowed_tools !== undefined;
48
+ const queryStep = needsTools ? step : { ...step, allowed_tools: [] };
49
+ // llm 步默认单轮;需要工具时允许多轮(读文件→输出)
45
50
  const maxTurns = step.maxTurns ?? (step.context_files?.length ? 10 : 1);
46
- // 记录 history 长度用于提取本步输出
47
- const historyLenBefore = ctx.history.length;
48
- // 委托 TAOR 引擎执行(携带 sessionState 以启用上下文压缩)
49
- yield* runQuery(effectivePrompt, {
50
- provider: ctx.provider,
51
- history: ctx.history,
52
- signal: ctx.signal,
51
+ const multimodal = resolveWorkflowMultimodalOptions(step, vars, ctx, (e) => missing.push(e));
52
+ if (missing.length > 0) {
53
+ yield {
54
+ type: 'warning',
55
+ message: `step "${step.id}": 模板变量未定义,已按空串插值:${[...new Set(missing)].map((m) => `\${${m}}`).join('、')}(裸名请用 inputs.<name> 或先 capture/set)`,
56
+ };
57
+ }
58
+ // 委托 TAOR 引擎执行(共享消费层 runStepQuery:携带 sessionState 以启用上下文压缩,
59
+ // 并统一侦测终结错误——runQuery 遇 LLM 失败是 yield error 后 return 不 throw,
60
+ // 直透会让本步静默返回空文本、runner 的 in-run retry 不触发 → 产出空如空 PLAN.md)。
61
+ const { rawText, aborted, sawLlmError, sawMaxTurns, llmErrorDetail } = yield* runStepQuery(effectivePrompt, queryStep, ctx, {
53
62
  maxTurns,
54
- chat: ctx.chat,
55
- executeTool: ctx.executeTool,
56
- sessionState: ctx.sessionState,
63
+ ...multimodal,
57
64
  });
58
- // 从 history 提取本步最后一条 assistant 消息
59
- let rawText = '';
60
- for (let i = ctx.history.length - 1; i >= historyLenBefore; i--) {
61
- const msg = ctx.history[i];
62
- if (msg.role === 'assistant' && typeof msg.content === 'string' && msg.content.trim()) {
63
- rawText = msg.content;
64
- break;
65
- }
66
- }
67
65
  const trimmed = rawText.trim();
66
+ // P0-1:LLM 失败(超时/网络错)且本步无任何产出 → 抛异常,触发 runner 的 max_retries 重跑。
67
+ // 必须放在 output_schema 解析之前(空文本本就无法解析),且只在 !aborted 时抛
68
+ // (用户中断不该被当成可重试失败;aborted 时按现状返回空结果即可)。
69
+ if (!trimmed && sawLlmError && !aborted) {
70
+ throw new Error(describeLlmFailure(step.id, llmErrorDetail));
71
+ }
72
+ // 撞 maxTurns 截断 → 截断时产出不可信,抛可重试失败(续作语义,同 agent.ts)
73
+ if (sawMaxTurns && !aborted) {
74
+ throw new Error(describeMaxTurnsCutoff(step.id, maxTurns));
75
+ }
68
76
  // 结构化输出模式:解析 JSON
69
77
  if (step.output_schema) {
70
78
  const payload = extractJsonPayload(trimmed);
@@ -17,8 +17,8 @@
17
17
  */
18
18
  import { readdir, readFile } from 'node:fs/promises';
19
19
  import { join } from 'node:path';
20
- import { runQuery } from '../../../../src/loop.js';
21
20
  import { getResourceSearchPaths } from '../../../../src/plugin-sdk.js';
21
+ import { describeLlmFailure, describeMaxTurnsCutoff, effectiveGoalAnchor, runStepQuery, } from '../agentCommon.js';
22
22
  import { interpolate } from '../expressions.js';
23
23
  function stripFrontmatter(content) {
24
24
  return content.replace(/^---\n[\s\S]*?\n---\n?/, '');
@@ -33,10 +33,7 @@ function parseFrontmatterName(content) {
33
33
  return nm[1].trim().replace(/^["']|["']$/g, '');
34
34
  }
35
35
  async function findSkillFile(skillName, cwd) {
36
- const roots = getResourceSearchPaths('skills', import.meta.url);
37
- // 优先搜索 cwd/skills(工作目录下的 skills 优先于包内 skills)
38
- const cwdSkills = join(cwd, 'skills');
39
- roots.unshift(cwdSkills);
36
+ const roots = getResourceSearchPaths('skills', import.meta.url, cwd);
40
37
  for (const root of roots) {
41
38
  let entries;
42
39
  try {
@@ -76,46 +73,38 @@ export async function* execSkillStep(step, vars, ctx) {
76
73
  // 构造用户提示:skill 指令 + 步骤 input
77
74
  // 系统提示词已在 history[0] 中(含完整 skill 列表和描述),
78
75
  // 这里不需要再拼 system message——把 SKILL.md body 作为操作指令即可
76
+ const missing = [];
79
77
  const userInput = step.input
80
- ? interpolate(step.input, vars)
78
+ ? interpolate(step.input, vars, { onMissing: (e) => missing.push(e) })
81
79
  : `请按以下 SKILL "${skillName}" 的指示完成任务。`;
82
80
  const fullPrompt = `【执行 Skill:${skillName}】\n\n${body}\n\n---\n\n当前任务:\n${userInput}`;
83
- // 通用目标锚定:确保 skill 执行时也不偏离原始目标
84
- const effectivePrompt = ctx.goalAnchor
85
- ? `${ctx.goalAnchor}\n\n${fullPrompt}`
86
- : fullPrompt;
81
+ // 通用目标锚定(原始目标 + 补充要求):确保 skill 执行时也不偏离
82
+ const anchor = effectiveGoalAnchor(ctx);
83
+ const effectivePrompt = anchor ? `${anchor}\n\n${fullPrompt}` : fullPrompt;
87
84
  const maxTurns = step.maxTurns ?? 25;
88
- // 记录 history 长度用于提取本步输出
89
- const historyLenBefore = ctx.history.length;
90
- // 委托 TAOR 引擎在共享 history 上执行
91
- // 注意:需要过滤掉 turn_done 事件(它是 runQuery 内部的终结标记,
92
- // 不应外泄为 workflow 的终结——workflow 的收口由 index.ts 统一处理)
93
- for await (const ev of runQuery(effectivePrompt, {
94
- provider: ctx.provider,
95
- history: ctx.history,
96
- signal: ctx.signal,
85
+ if (missing.length > 0) {
86
+ yield {
87
+ type: 'warning',
88
+ message: `step "${step.id}": 模板变量未定义,已按空串插值:${[...new Set(missing)].map((m) => `\${${m}}`).join('、')}(裸名请用 inputs.<name> 或先 capture/set)`,
89
+ };
90
+ }
91
+ // 委托 TAOR 引擎在共享 history 上执行(共享消费层 runStepQuery)。
92
+ // suppressTurnDone:turn_done 是 runQuery 单次调用的终结标记,不是 workflow 终结,
93
+ // 外泄会导致 gateway broker / -p json 模式误判 workflow 中途结束。
94
+ const { rawText, aborted, sawLlmError, sawMaxTurns, llmErrorDetail } = yield* runStepQuery(effectivePrompt, step, ctx, {
97
95
  maxTurns,
98
- chat: ctx.chat,
99
- executeTool: ctx.executeTool,
100
- sessionState: ctx.sessionState,
101
- })) {
102
- // turn_done 是 runQuery 单次调用的终结标记,不是 workflow 终结。
103
- // 外泄会导致 gateway broker / -p json 模式误判 workflow 中途结束。
104
- if (ev.type === 'turn_done')
105
- continue;
106
- // error / interrupted 应该冒泡让 runner 处理
107
- yield ev;
96
+ suppressTurnDone: true,
97
+ });
98
+ const trimmed = rawText.trim();
99
+ // P0-1:LLM 失败(超时/网络错)且本步无任何产出 → 抛异常,触发 runner 的 max_retries 重跑。
100
+ // 用户中断(aborted)不该被当成可重试失败,按现状返回空结果即可。
101
+ if (!trimmed && sawLlmError && !aborted) {
102
+ throw new Error(describeLlmFailure(step.id, llmErrorDetail));
108
103
  }
109
- // 从 history 提取本步最后一条 assistant 消息
110
- let rawText = '';
111
- for (let i = ctx.history.length - 1; i >= historyLenBefore; i--) {
112
- const msg = ctx.history[i];
113
- if (msg.role === 'assistant' && typeof msg.content === 'string' && msg.content.trim()) {
114
- rawText = msg.content;
115
- break;
116
- }
104
+ // 撞 maxTurns 截断 → 截断时产出不可信,抛可重试失败(续作语义,同 agent.ts)
105
+ if (sawMaxTurns && !aborted) {
106
+ throw new Error(describeMaxTurnsCutoff(step.id, maxTurns));
117
107
  }
118
- const trimmed = rawText.trim();
119
108
  return {
120
109
  raw: { text: trimmed, result: trimmed },
121
110
  preview: trimmed.length > 200 ? `${trimmed.slice(0, 200)}...` : trimmed,
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * ============================================================
3
- * src/workflows/stepExecutors/tool.ts —— tool step 执行
3
+ * plugins/workflow-runner/src/stepExecutors/tool.ts —— tool step 执行
4
4
  * ------------------------------------------------------------
5
5
  * 把 step.args 递归 ${var} 插值后 JSON.stringify,转发到 executeTool。
6
6
  * 失败(ok=false)抛 Error,让 runner 走 onError 处理。
@@ -21,7 +21,9 @@
21
21
  * - `{ args.<任意字段>: var_name }` 绑入参的具名字段
22
22
  * ============================================================
23
23
  */
24
- import { executeTool, getToolByName, } from '../../../../src/plugin-sdk.js';
24
+ import { existsSync } from 'node:fs';
25
+ import { resolve } from 'node:path';
26
+ import { executeTool, getToolByName, recordRead, } from '../../../../src/plugin-sdk.js';
25
27
  import { interpolateDeep } from '../expressions.js';
26
28
  export async function* execToolStep(step, vars, ctx) {
27
29
  if (!step.tool)
@@ -29,7 +31,17 @@ export async function* execToolStep(step, vars, ctx) {
29
31
  const tool = getToolByName(step.tool);
30
32
  if (!tool)
31
33
  throw new Error(`step ${step.id}: 未知工具 "${step.tool}"`);
32
- const args = interpolateDeep(step.args ?? {}, vars);
34
+ // 未定义变量插值可见化(args 里静默空串会让工具拿到空路径/空内容)
35
+ const missing = [];
36
+ const args = interpolateDeep(step.args ?? {}, vars, {
37
+ onMissing: (e) => missing.push(e),
38
+ });
39
+ if (missing.length > 0) {
40
+ yield {
41
+ type: 'warning',
42
+ message: `step "${step.id}": 模板变量未定义,已按空串插值:${[...new Set(missing)].map((m) => `\${${m}}`).join('、')}(裸名请用 inputs.<name> 或先 capture/set)`,
43
+ };
44
+ }
33
45
  const argsJson = JSON.stringify(args);
34
46
  const toolCallId = `wf_${step.id}`;
35
47
  const argsPreview = argsJson.length > 80 ? `${argsJson.slice(0, 80)}…` : argsJson;
@@ -40,6 +52,14 @@ export async function* execToolStep(step, vars, ctx) {
40
52
  argsPreview,
41
53
  argsFriendly: friendlyToolDesc(tool.name, args),
42
54
  };
55
+ // ════ 声明产物覆盖判定(解 Write/Edit 的 pre-read 守卫) ════
56
+ // 内核写类工具有「文件已存在必须先 Read」守卫(fileState 进程级 Map,
57
+ // 跨进程 / resume / 残留旧产物时必空)。确定性 tool 步的 args 是 yaml 作者
58
+ // 写死的,模型没有机会先 Read —— 目标文件若是工作流**声明的产物**
59
+ // (def 级 + step 级 artifacts:,= 工作流自己拥有、本就要覆盖的文件),
60
+ // 这里自动 pre-Read 一次解除守卫(管线动作,不 yield 事件、结果丢弃)。
61
+ // 非声明产物的盲写保护原样生效;pre-Read 失败也不拦,让写工具自己报错。
62
+ await preReadDeclaredArtifact(tool, args, step, ctx);
43
63
  const result = await executeTool(tool.name, argsJson, ctx.signal);
44
64
  yield {
45
65
  type: 'tool_end',
@@ -70,6 +90,39 @@ export async function* execToolStep(step, vars, ctx) {
70
90
  preview: content.length > 200 ? `${content.slice(0, 200)}...` : content,
71
91
  };
72
92
  }
93
+ /**
94
+ * 写类 tool 步的目标文件若命中声明产物且已存在 → pre-Read 解除内核守卫。
95
+ *
96
+ * 路径归一化:artifacts 声明相对 cwd;args.file_path 可能是 ./相对 或 绝对,
97
+ * 双方都 resolve(cwd, …) 后比较(win32 文件系统大小写不敏感 → lowercase 比较;
98
+ * 其余平台保持大小写敏感,避免 Plan.md / plan.md 误判同文件)。
99
+ */
100
+ async function preReadDeclaredArtifact(tool, args, step, ctx) {
101
+ if (tool.isReadOnly !== false)
102
+ return; // 只对写类工具生效
103
+ if (typeof args.file_path !== 'string')
104
+ return;
105
+ const norm = (p) => {
106
+ const abs = resolve(ctx.cwd, p);
107
+ return process.platform === 'win32' ? abs.toLowerCase() : abs;
108
+ };
109
+ const target = norm(args.file_path);
110
+ const declared = [...(ctx.declaredArtifacts ?? []), ...(step.artifacts ?? [])];
111
+ if (!declared.some((a) => norm(a) === target))
112
+ return;
113
+ const abs = resolve(ctx.cwd, args.file_path);
114
+ if (!existsSync(abs))
115
+ return; // 新建文件不需要解守卫
116
+ // 直接登记一次「完整已读」状态解除内核 pre-read 守卫——不真正 Read 内容:
117
+ // 1. 声明产物本就要被本步整体覆盖,旧内容无需进上下文/烧 token;
118
+ // 2. 真 Read 大文件会触发字符上限截断(read.ts → recordRead({truncated:true})),
119
+ // 反而踩中 Write 的「截断禁写」硬守卫(write.ts: existsSync && wasTruncated → 拒绝),
120
+ // 正是本步要解除的同一道墙——曾导致 plan.yaml 回写变长的 PLAN.md 时 halt
121
+ // (首轮新建 PLAN.md 不存在直接放行,审批反馈后回写已存在的大 PLAN.md 被挡死)。
122
+ // 两道守卫(never-read + truncated)必须一并解除,声明产物的整体覆盖才算真正授权。
123
+ // recordRead 内部自带 statSync 失败兜底(返回 1、不抛),无需再包 try/catch。
124
+ recordRead(abs, { truncated: false });
125
+ }
73
126
  /**
74
127
  * 给 tool_start 生成人话描述(LiveArea spinner 行用)。
75
128
  */
@@ -1,8 +1,8 @@
1
1
  /**
2
2
  * ============================================================
3
- * stepExecutors/verify.ts —— verify step 执行
3
+ * plugins/workflow-runner/src/stepExecutors/verify.ts —— verify step 执行
4
4
  * ------------------------------------------------------------
5
- * 复用 src/infra/verification.ts 共享验证基础设施,
5
+ * 复用内核共享验证基础设施(经 plugin-sdk 门面取 runVerification/parseCheckString),
6
6
  * 支持 shell / file_exists / file_contains / test_count 四种检查。
7
7
  *
8
8
  * 与 assert 的区别:
@@ -10,27 +10,36 @@
10
10
  * - verify: 完整检查器(重量,支持命令执行/正则匹配/测试计数)
11
11
  * ============================================================
12
12
  */
13
- import { parseCheckString, runVerification } from '../../../../src/infra/verification.js';
13
+ import { parseCheckString, runVerification } from '../../../../src/plugin-sdk.js';
14
14
  import { interpolate } from '../expressions.js';
15
15
  export async function execVerifyStep(step, vars) {
16
- // 兼容两种格式:YAML 数组 或逗号分隔字符串
16
+ // 兼容两种格式:YAML 数组 或逗号分隔字符串。
17
+ // 关键:【先插值再按逗号分割】。checks 可能是 "${verify_checks}" 这类模板,插值后才
18
+ // 展开成逗号分隔的多项(如 "shell:bun test,file_exists:x")。若先分割再插值,整段模板
19
+ // 会被当成一项,解析时 value 混入逗号、多项检查失效(ralph-loop 的 run_verify 即如此)。
17
20
  let rawChecks;
18
21
  if (Array.isArray(step.checks) && step.checks.length > 0) {
19
- rawChecks = step.checks;
22
+ rawChecks = step.checks
23
+ .flatMap((s) => interpolate(s, vars).split(','))
24
+ .map((s) => s.trim())
25
+ .filter(Boolean);
20
26
  }
21
27
  else if (typeof step.checks === 'string' && step.checks.trim() !== '') {
22
- rawChecks = step.checks.split(',').map((s) => s.trim()).filter(Boolean);
28
+ rawChecks = interpolate(step.checks, vars)
29
+ .split(',')
30
+ .map((s) => s.trim())
31
+ .filter(Boolean);
23
32
  }
24
33
  else {
25
34
  throw new Error(`step ${step.id}: verify 缺少 checks(需要数组或逗号分隔字符串)`);
26
35
  }
27
- // 解析并插值每条 check 字符串
36
+ // 解析每条已插值的 check 字符串(插值后若整体为空——如 ${verify_checks} 取到空串——
37
+ // 则 checks 为空,runVerification 视为"无检查项"通过;ralph-loop 另有 when 守卫不会走到这)
28
38
  const checks = [];
29
39
  for (const raw of rawChecks) {
30
- const interpolated = interpolate(raw, vars);
31
- const check = parseCheckString(interpolated);
40
+ const check = parseCheckString(raw);
32
41
  if (!check) {
33
- throw new Error(`step ${step.id}: 无法解析 check "${interpolated}",格式应为 "type:value"`);
42
+ throw new Error(`step ${step.id}: 无法解析 check "${raw}",格式应为 "type:value"`);
34
43
  }
35
44
  checks.push(check);
36
45
  }