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
@@ -0,0 +1,447 @@
1
+ import { randomUUID } from 'node:crypto';
2
+ import { rm } from 'node:fs/promises';
3
+ import { configService } from '../config/configService.js';
4
+ import { resolveAgentBrowserPath } from '../tools/webbrowser/agentBrowserPath.js';
5
+ import { browserProfilePath, sessionArgs, spawnRunner } from '../tools/webbrowser/browser.js';
6
+ import { ensureDaemon, stopDaemon } from '../tools/webbrowser/browserDaemon.js';
7
+ import { browserService } from '../tools/webbrowser/browserService.js';
8
+ import { getBrowserStream, } from '../tools/webbrowser/browserStream.js';
9
+ function normalizeUrl(value) {
10
+ const input = value.trim();
11
+ if (/^[a-z][a-z0-9+.-]*:/i.test(input))
12
+ return input;
13
+ return `https://${input}`;
14
+ }
15
+ function requireAllowedDeveloperOrigin(url, allowedOrigins) {
16
+ let origin;
17
+ try {
18
+ origin = new URL(url).origin;
19
+ }
20
+ catch {
21
+ throw new Error('当前页面没有可授权的 HTTP(S) 站点');
22
+ }
23
+ if (!/^https?:\/\//i.test(origin) || !allowedOrigins.includes(origin)) {
24
+ throw new Error(`Developer Mode 尚未授权当前站点:${origin}。请在“设置 → 浏览器”添加该站点。`);
25
+ }
26
+ return origin;
27
+ }
28
+ function parseJsonOutput(text) {
29
+ const trimmed = text.trim();
30
+ if (!trimmed)
31
+ return undefined;
32
+ try {
33
+ const value = JSON.parse(trimmed);
34
+ if (typeof value === 'string') {
35
+ try {
36
+ return JSON.parse(value);
37
+ }
38
+ catch {
39
+ return value;
40
+ }
41
+ }
42
+ return value;
43
+ }
44
+ catch {
45
+ return trimmed;
46
+ }
47
+ }
48
+ function pickScript(x, y) {
49
+ return `(() => {
50
+ const el = document.elementFromPoint(${Math.round(x)}, ${Math.round(y)});
51
+ if (!el) return null;
52
+ const r = el.getBoundingClientRect();
53
+ const attrs = {};
54
+ for (const a of Array.from(el.attributes || []).slice(0, 16)) attrs[a.name] = String(a.value).slice(0, 300);
55
+ const escape = (v) => CSS.escape(String(v));
56
+ let selector = el.id ? '#' + escape(el.id) : el.tagName.toLowerCase();
57
+ if (!el.id && el.classList && el.classList.length) selector += '.' + Array.from(el.classList).slice(0, 2).map(escape).join('.');
58
+ if (!el.id) {
59
+ const same = Array.from(document.querySelectorAll(selector));
60
+ if (same.length > 1) selector += ':nth-of-type(' + (Array.from(el.parentElement?.children || []).filter((n) => n.tagName === el.tagName).indexOf(el) + 1) + ')';
61
+ }
62
+ const role = el.getAttribute('role') || ({A:'link',BUTTON:'button',INPUT:'textbox',SELECT:'combobox',TEXTAREA:'textbox',IMG:'img'}[el.tagName] || undefined);
63
+ const accessibleName = el.getAttribute('aria-label') || el.getAttribute('alt') || el.getAttribute('title') || String(el.innerText || el.textContent || '').trim().slice(0, 160);
64
+ return { ref: selector, tag: el.tagName.toLowerCase(), role, accessibleName, text: String(el.innerText || el.textContent || '').trim().slice(0, 1000), boundingBox: { x: r.x, y: r.y, width: r.width, height: r.height }, attributes: attrs };
65
+ })()`;
66
+ }
67
+ function mapStreamEvent(event) {
68
+ switch (event.type) {
69
+ case 'frame':
70
+ return { t: 'browser/frame', data: String(event.data ?? ''), metadata: event.metadata,
71
+ frameWidth: Number(event.frameWidth ?? event.width ?? event.metadata?.width ?? 0) || undefined,
72
+ frameHeight: Number(event.frameHeight ?? event.height ?? event.metadata?.height ?? 0) || undefined,
73
+ viewportWidth: Number(event.viewportWidth ?? 0) || undefined,
74
+ viewportHeight: Number(event.viewportHeight ?? 0) || undefined,
75
+ deviceScaleFactor: Number(event.deviceScaleFactor ?? 1) || 1 };
76
+ case 'status':
77
+ return {
78
+ t: 'browser/status',
79
+ connected: Boolean(event.connected),
80
+ screencasting: Boolean(event.screencasting ?? event.screencast),
81
+ viewportWidth: Number(event.viewportWidth ?? event.width ?? 1280),
82
+ viewportHeight: Number(event.viewportHeight ?? event.height ?? 720),
83
+ engine: 'agent-browser',
84
+ };
85
+ case 'tabs': {
86
+ const tabs = Array.isArray(event.tabs) ? event.tabs : [];
87
+ return { t: 'browser/tabs', tabs: tabs.map((tab) => {
88
+ const item = tab;
89
+ return { id: String(item.tabId ?? item.id ?? ''), url: String(item.url ?? ''), title: typeof item.title === 'string' ? item.title : undefined, active: Boolean(item.active), favicon: typeof item.favicon === 'string' ? item.favicon : undefined };
90
+ }) };
91
+ }
92
+ case 'url': return { t: 'browser/url', url: String(event.url ?? ''), tabId: typeof event.tabId === 'string' ? event.tabId : undefined };
93
+ case 'title': return { t: 'browser/title', title: String(event.title ?? ''), tabId: typeof event.tabId === 'string' ? event.tabId : undefined };
94
+ case 'navigation': return { t: 'browser/navigation', url: String(event.url ?? ''), state: (event.state === 'start' || event.state === 'commit' || event.state === 'domcontentloaded') ? event.state : 'load' };
95
+ case 'console': return { t: 'browser/console', level: typeof event.level === 'string' ? event.level : undefined, text: String(event.text ?? '') };
96
+ case 'page_error': return { t: 'browser/page_error', text: String(event.text ?? event.message ?? '') };
97
+ case 'dialog': return { t: 'browser/dialog', type: (event.dialogType ?? event.kind ?? 'alert'), message: String(event.message ?? ''), defaultValue: typeof event.defaultValue === 'string' ? event.defaultValue : undefined };
98
+ case 'error': return { t: 'browser/error', message: String(event.message ?? '浏览器流错误') };
99
+ case 'mode': return { t: 'browser/mode', mode: event.mode };
100
+ case 'marked_elements': return { t: 'browser/marked/elements', elements: (event.elements ?? []) };
101
+ case 'agent_acting': return { t: 'browser/agent/acting', acting: Boolean(event.acting), action: typeof event.action === 'string' ? event.action : undefined, target: typeof event.target === 'string' ? event.target : undefined };
102
+ case 'control_state': return { t: 'browser/control/changed', state: event.state };
103
+ default: return null;
104
+ }
105
+ }
106
+ export function createBrowserBridge(send, sessionName = 'minimal-agent') {
107
+ let disposed = false;
108
+ let activeTabId = '';
109
+ let pageRevision = randomUUID();
110
+ const stream = getBrowserStream(sessionName);
111
+ const unsubscribe = stream.subscribe((event) => {
112
+ if (disposed)
113
+ return;
114
+ if (event.type === 'url' || event.type === 'navigation')
115
+ pageRevision = randomUUID();
116
+ if (event.type === 'tabs' && Array.isArray(event.tabs)) {
117
+ const active = event.tabs.find((tab) => Boolean(tab.active));
118
+ activeTabId = String(active?.tabId ?? active?.id ?? activeTabId);
119
+ }
120
+ const mapped = mapStreamEvent(event);
121
+ if (mapped?.t === 'browser/frame')
122
+ send({ ...mapped, tabId: activeTabId || mapped.tabId, pageRevision });
123
+ else if (mapped)
124
+ send(mapped);
125
+ if (event.type === 'control_state') {
126
+ const state = event.state;
127
+ if (state?.status === 'waiting_human' && state.reason !== 'manual') {
128
+ const summary = state.reason === 'captcha' ? '页面需要人工完成安全验证'
129
+ : state.reason === 'otp' ? '页面需要人工输入一次性验证码'
130
+ : state.reason === 'login' ? '页面需要人工登录'
131
+ : state.reason === 'permission' ? '页面需要人工确认权限'
132
+ : '网站已暂停或拦截当前访问';
133
+ send({
134
+ t: 'browser/challenge/detected',
135
+ reason: state.reason,
136
+ summary,
137
+ resumeToken: state.resumeToken,
138
+ runId: state.runId,
139
+ ...(state.conversationKey ? { conversationKey: state.conversationKey } : {}),
140
+ });
141
+ }
142
+ }
143
+ });
144
+ const run = async (args) => {
145
+ const bin = await resolveAgentBrowserPath();
146
+ if (!bin)
147
+ throw new Error('未找到内置 agent-browser');
148
+ await ensureDaemon(sessionName);
149
+ const result = await browserService.run({ sessionName, bin, args: [...sessionArgs(sessionName), ...args], runner: spawnRunner });
150
+ if (result.code !== 0)
151
+ throw new Error((result.stderr || result.stdout || `exit ${result.code}`).trim());
152
+ return result.stdout.trim();
153
+ };
154
+ const ensureStarted = async () => {
155
+ const config = await configService.browser();
156
+ if (!config.enabled)
157
+ throw new Error('内置浏览器已在设置中关闭');
158
+ const info = await ensureDaemon(sessionName);
159
+ await stream.connect();
160
+ return info;
161
+ };
162
+ const applyViewport = async (widthValue, heightValue) => {
163
+ const width = Math.max(320, Math.round(Number(widthValue)));
164
+ const height = Math.max(240, Math.round(Number(heightValue)));
165
+ if (!Number.isFinite(width) || !Number.isFinite(height))
166
+ throw new Error('无效的浏览器视口尺寸');
167
+ await run(['set', 'viewport', String(width), String(height)]);
168
+ // agent-browser starts its CDP screencast with the dimensions that were
169
+ // active when the viewer connected. Reconnect only the viewer so the next
170
+ // frame is encoded at the new viewport without touching tabs or profile.
171
+ if (stream.isConnected()) {
172
+ stream.disconnect();
173
+ await stream.connect();
174
+ }
175
+ };
176
+ const pick = async (x, y) => {
177
+ const value = parseJsonOutput(await run(['eval', pickScript(x, y)]));
178
+ if (!value || typeof value !== 'object')
179
+ return null;
180
+ return value;
181
+ };
182
+ stream.setPicker(pick);
183
+ const result = (frame, ok, data, error) => send({
184
+ t: 'browser/result', requestId: frame.requestId, action: frame.t, ok,
185
+ ...(typeof data === 'string' ? { content: data } : data === undefined ? {} : { data }),
186
+ ...(error ? { error } : {}),
187
+ });
188
+ return {
189
+ async handle(frame) {
190
+ if (disposed)
191
+ return;
192
+ try {
193
+ switch (frame.t) {
194
+ case 'browser/start': {
195
+ const info = await ensureStarted();
196
+ const viewport = frame.viewport;
197
+ if (viewport?.width && viewport?.height)
198
+ await applyViewport(viewport.width, viewport.height);
199
+ send({ t: 'browser/started', requestId: frame.requestId, streamPort: info.streamPort, pid: info.pid, sessionId: sessionName });
200
+ result(frame, true, info);
201
+ return;
202
+ }
203
+ case 'browser/stop':
204
+ stream.disconnect();
205
+ await stopDaemon(sessionName);
206
+ send({ t: 'browser/stopped', requestId: frame.requestId });
207
+ return;
208
+ case 'browser/profile/clear':
209
+ stream.disconnect();
210
+ await stopDaemon(sessionName);
211
+ await rm(browserProfilePath('default'), { recursive: true, force: true });
212
+ send({ t: 'browser/stopped', requestId: frame.requestId });
213
+ result(frame, true);
214
+ return;
215
+ case 'browser/mode':
216
+ stream.setMode(frame.mode);
217
+ result(frame, true);
218
+ return;
219
+ case 'browser/control/get':
220
+ send({ t: 'browser/control/changed', state: stream.getControlState() });
221
+ return;
222
+ case 'browser/control/takeover':
223
+ stream.requestHuman('manual');
224
+ result(frame, true, stream.getControlState());
225
+ return;
226
+ case 'browser/control/resume': {
227
+ const ok = stream.resumeControl(typeof frame.resumeToken === 'string' ? frame.resumeToken : undefined);
228
+ result(frame, ok, stream.getControlState(), ok ? undefined : '浏览器接管请求已失效');
229
+ return;
230
+ }
231
+ case 'browser/control/stop':
232
+ stream.stopControl(typeof frame.resumeToken === 'string' ? frame.resumeToken : undefined);
233
+ result(frame, true, stream.getControlState());
234
+ return;
235
+ case 'browser/user/input': {
236
+ const type = String(frame.eventType ?? '');
237
+ if (type.startsWith('mouse'))
238
+ stream.send({ type: 'input_mouse', ...frame, t: undefined });
239
+ else
240
+ stream.send({ type: 'input_keyboard', ...frame, t: undefined });
241
+ return;
242
+ }
243
+ case 'browser/navigate': {
244
+ await ensureStarted();
245
+ const url = normalizeUrl(String(frame.url ?? ''));
246
+ await run(frame.newTab ? ['tab', 'new', url] : ['open', url]);
247
+ stream.setCurrentUrl(url);
248
+ result(frame, true);
249
+ return;
250
+ }
251
+ case 'browser/back':
252
+ await run(['back']);
253
+ result(frame, true);
254
+ return;
255
+ case 'browser/forward':
256
+ await run(['forward']);
257
+ result(frame, true);
258
+ return;
259
+ case 'browser/reload':
260
+ await run(['reload']);
261
+ result(frame, true);
262
+ return;
263
+ case 'browser/tab/new':
264
+ await run(frame.url ? ['tab', 'new', normalizeUrl(String(frame.url))] : ['tab', 'new']);
265
+ result(frame, true);
266
+ return;
267
+ case 'browser/tab/close':
268
+ await run(frame.tabId ? ['tab', 'close', String(frame.tabId)] : ['tab', 'close']);
269
+ result(frame, true);
270
+ return;
271
+ case 'browser/tab/switch':
272
+ await run(['tab', 'switch', String(frame.tabId)]);
273
+ result(frame, true);
274
+ return;
275
+ case 'browser/tabs':
276
+ result(frame, true, parseJsonOutput(await run(['tab', 'list'])));
277
+ return;
278
+ case 'browser/pick': {
279
+ const element = await pick(Number(frame.x), Number(frame.y));
280
+ if (!element) {
281
+ send({ t: 'browser/picked', requestId: frame.requestId, error: '该位置没有可选元素' });
282
+ return;
283
+ }
284
+ stream.markElement(element);
285
+ const item = element;
286
+ send({ t: 'browser/picked', requestId: frame.requestId, reference: {
287
+ id: randomUUID(), url: stream.getCurrentUrl(), tabId: activeTabId, selector: item.ref,
288
+ tag: item.tag, role: item.role, accessibleName: item.accessibleName, text: item.text,
289
+ attributes: item.attributes ?? {}, boundingBox: item.boundingBox, pageRevision, capturedAt: Date.now(),
290
+ }, ...element });
291
+ return;
292
+ }
293
+ case 'browser/mark/element': {
294
+ const raw = parseJsonOutput(await run(['eval', `(() => { const el=document.querySelector(${JSON.stringify(String(frame.selector))}); if(!el)return null; const r=el.getBoundingClientRect(); return {ref:${JSON.stringify(String(frame.selector))},tag:el.tagName.toLowerCase(),text:String(el.innerText||el.textContent||'').trim().slice(0,1000),boundingBox:{x:r.x,y:r.y,width:r.width,height:r.height}} })()`]));
295
+ if (!raw || typeof raw !== 'object')
296
+ throw new Error('未找到该元素');
297
+ stream.markElement({ ...raw, label: typeof frame.label === 'string' ? frame.label : undefined, color: typeof frame.color === 'string' ? frame.color : undefined });
298
+ result(frame, true);
299
+ return;
300
+ }
301
+ case 'browser/mark/clear':
302
+ stream.clearMarkedElements();
303
+ result(frame, true);
304
+ return;
305
+ case 'browser/viewport':
306
+ await ensureStarted();
307
+ await applyViewport(frame.width, frame.height);
308
+ result(frame, true);
309
+ return;
310
+ case 'browser/dialog/accept':
311
+ await run(frame.text ? ['dialog', 'accept', '--text', String(frame.text)] : ['dialog', 'accept']);
312
+ result(frame, true);
313
+ return;
314
+ case 'browser/dialog/dismiss':
315
+ await run(['dialog', 'dismiss']);
316
+ result(frame, true);
317
+ return;
318
+ case 'browser/snapshot': {
319
+ const tree = await run(['snapshot']);
320
+ send({ t: 'browser/snapshot', requestId: frame.requestId, tree });
321
+ return;
322
+ }
323
+ case 'browser/getContent': {
324
+ const text = await run(['get', 'text', String(frame.selector ?? 'body')]);
325
+ send({ t: 'browser/content', requestId: frame.requestId, text });
326
+ return;
327
+ }
328
+ case 'browser/click':
329
+ await run(['click', String(frame.selector)]);
330
+ result(frame, true);
331
+ return;
332
+ case 'browser/fill':
333
+ await run(['fill', String(frame.selector), String(frame.value ?? '')]);
334
+ result(frame, true);
335
+ return;
336
+ case 'browser/type':
337
+ await run(['type', String(frame.selector), String(frame.value ?? '')]);
338
+ result(frame, true);
339
+ return;
340
+ case 'browser/hover':
341
+ await run(['hover', String(frame.selector)]);
342
+ result(frame, true);
343
+ return;
344
+ case 'browser/scroll': {
345
+ const args = frame.selector ? ['scrollintoview', String(frame.selector)] : ['scroll', String(frame.direction ?? 'down'), String(Math.abs(Number(frame.y ?? 600)))];
346
+ await run(args);
347
+ result(frame, true);
348
+ return;
349
+ }
350
+ case 'browser/select':
351
+ await run(['select', String(frame.selector), ...(Array.isArray(frame.value) ? frame.value.map(String) : [String(frame.value ?? '')])]);
352
+ result(frame, true);
353
+ return;
354
+ case 'browser/press':
355
+ await run(['press', String(frame.key)]);
356
+ result(frame, true);
357
+ return;
358
+ case 'browser/wait':
359
+ await run(frame.selector ? ['wait', String(frame.selector)] : frame.text ? ['wait', '--text', String(frame.text)] : ['wait', String(frame.ms ?? 500)]);
360
+ result(frame, true);
361
+ return;
362
+ case 'browser/eval':
363
+ result(frame, true, parseJsonOutput(await run(['eval', String(frame.js ?? '')])));
364
+ return;
365
+ case 'browser/upload':
366
+ await run(['upload', String(frame.selector), ...(Array.isArray(frame.files) ? frame.files.map(String) : [])]);
367
+ result(frame, true);
368
+ return;
369
+ case 'browser/drag':
370
+ await run(['drag', String(frame.fromSelector), String(frame.toSelector)]);
371
+ result(frame, true);
372
+ return;
373
+ case 'browser/screenshot': {
374
+ const args = ['screenshot', ...(frame.path ? [String(frame.path)] : []), ...(frame.annotate ? ['--annotate'] : []), ...(frame.fullPage ? ['--full'] : [])];
375
+ const output = await run(args);
376
+ send({ t: 'browser/screenshot', requestId: frame.requestId, path: typeof frame.path === 'string' ? frame.path : undefined, data: output });
377
+ return;
378
+ }
379
+ case 'browser/pdf': {
380
+ const output = await run(['pdf', String(frame.path ?? '')]);
381
+ result(frame, true, output);
382
+ return;
383
+ }
384
+ case 'browser/cookies/get':
385
+ send({ t: 'browser/cookies', requestId: frame.requestId, cookies: parseJsonOutput(await run(['cookies', 'get'])) ?? [] });
386
+ return;
387
+ case 'browser/cookies/clear':
388
+ await run(['cookies', 'clear']);
389
+ result(frame, true);
390
+ return;
391
+ case 'browser/device':
392
+ await run(frame.preset ? ['set', 'device', String(frame.preset)] : ['set', 'viewport', String(frame.width), String(frame.height)]);
393
+ result(frame, true);
394
+ return;
395
+ case 'browser/media':
396
+ await run(['set', 'media', String(frame.scheme ?? 'no-preference')]);
397
+ result(frame, true);
398
+ return;
399
+ case 'browser/record/start':
400
+ await run(['record', 'start', String(frame.path ?? '')]);
401
+ result(frame, true);
402
+ return;
403
+ case 'browser/record/stop':
404
+ result(frame, true, await run(['record', 'stop']));
405
+ return;
406
+ case 'browser/debug/console':
407
+ case 'browser/debug/errors':
408
+ case 'browser/debug/network':
409
+ case 'browser/debug/styles':
410
+ case 'browser/debug/vitals':
411
+ case 'browser/debug/trace/start':
412
+ case 'browser/debug/trace/stop': {
413
+ const browserConfig = await configService.browser();
414
+ if (!browserConfig.developerMode)
415
+ throw new Error('请先在“设置 → 浏览器”开启完整 CDP Developer Mode');
416
+ requireAllowedDeveloperOrigin(stream.getCurrentUrl(), browserConfig.allowedCdpOrigins);
417
+ if (frame.t === 'browser/debug/console')
418
+ result(frame, true, await run(['console']));
419
+ else if (frame.t === 'browser/debug/errors')
420
+ result(frame, true, await run(['errors']));
421
+ else if (frame.t === 'browser/debug/network')
422
+ result(frame, true, parseJsonOutput(await run(['network', 'requests', '--json'])));
423
+ else if (frame.t === 'browser/debug/styles')
424
+ result(frame, true, parseJsonOutput(await run(['get', 'styles', String(frame.selector ?? 'body'), '--json'])));
425
+ else if (frame.t === 'browser/debug/vitals')
426
+ result(frame, true, parseJsonOutput(await run(['vitals', ...(frame.url ? [String(frame.url)] : []), '--json'])));
427
+ else if (frame.t === 'browser/debug/trace/start') {
428
+ await run(['trace', 'start']);
429
+ result(frame, true);
430
+ }
431
+ else
432
+ result(frame, true, await run(['trace', 'stop', ...(frame.path ? [String(frame.path)] : [])]));
433
+ return;
434
+ }
435
+ default:
436
+ result(frame, false, undefined, `暂不支持浏览器操作:${frame.t}`);
437
+ }
438
+ }
439
+ catch (error) {
440
+ const message = error instanceof Error ? error.message : String(error);
441
+ result(frame, false, undefined, message);
442
+ send({ t: 'browser/error', message });
443
+ }
444
+ },
445
+ dispose() { disposed = true; unsubscribe(); },
446
+ };
447
+ }
@@ -0,0 +1,19 @@
1
+ /**
2
+ * ============================================================
3
+ * src/web/protocol.ts —— Web/Gateway WebSocket 线路协议(ClientFrame/ServerFrame)
4
+ * ------------------------------------------------------------
5
+ * ⚠️ 不变量:本文件必须保持**纯 type-only**(只有 `import type` / `export type` /
6
+ * `export interface`,零运行时 import/export)。webchat(浏览器)靠
7
+ * `export type … from '../../src/web/protocol.js'` **单一真源**引用这里,
8
+ * 而它能安全 import 后端目录的唯一原因是:整条链全是 type,打包器(Vite/esbuild)
9
+ * 在编译期把它擦除干净,绝不把 `node:*` 依赖拉进浏览器 bundle。
10
+ *
11
+ * 一旦这里出现**任何运行时** import/export(哪怕一个 helper 函数),
12
+ * webchat bundle 就会顺着链把 `src/types.ts`→`plugins/**` 的 node 代码拖进浏览器 →
13
+ * **静默**打断前端构建。`test/webProtocolTypeOnly.test.ts` 守这条线(改动会报红)。
14
+ *
15
+ * 同理它 `import type` 了具体插件 `cron-runner` 的类型:仅因 type-only 才不构成
16
+ * 内核对插件的运行时耦合;下游若要新增插件资源帧,务必同样只引 type。
17
+ * ============================================================
18
+ */
19
+ export {};
@@ -12,21 +12,27 @@ inputs:
12
12
  - name: topic
13
13
  type: string
14
14
  required: true
15
- description: "调研主题"
15
+ greedy: true
16
+ description: "调研主题(可直接一句话自然语言;命令行整句进 topic,不被空格拆碎)"
16
17
 
17
18
  steps:
18
19
  # 1. agent 节点:开放式调研,自主决定怎么做,产出笔记并自我验证
19
20
  - id: research
21
+ title: "1/3·自主调研写笔记"
20
22
  agent: |
21
23
  调研主题「${inputs.topic}」:用你手上的工具搜集 3-5 个关键要点,
22
24
  整理成简洁的中文 markdown,写到 ./out/notes.md(目录不存在就先创建)。
23
25
  写完后用 Read 自检文件确实写好了再收尾。
24
26
  allowed_tools: [Read, Write, Edit, Bash, Grep, Glob]
27
+ # 确定性完成条件:模型「宣告要写却没写」时引擎自动提醒续跑(announce-not-do 兜底)
28
+ done_when: "fileExists('./out/notes.md')"
25
29
  capture:
26
- content: notes
30
+ # agent 步 raw 只有 text/result(无 content)——曾绑 content 导致 notes 恒 undefined
31
+ text: notes
27
32
 
28
33
  # 2. 确定性工具,但带 onError: recover —— 出意外就地自愈,而非崩停
29
34
  - id: count_lines
35
+ title: "2/3·统计笔记行数"
30
36
  tool: Bash
31
37
  args:
32
38
  command: "wc -l ./out/notes.md"
@@ -35,8 +41,10 @@ steps:
35
41
  capture:
36
42
  content: line_report
37
43
 
38
- # 3. 守门 assert
44
+ # 3. 守门 verify(升级自 assert,享受 verify 的 file_exists 检查器)
39
45
  - id: verify
40
- type: assert
41
- condition: 'fileExists("./out/notes.md")'
46
+ title: "3/3·守门校验"
47
+ type: verify
48
+ checks:
49
+ - 'file_exists:./out/notes.md'
42
50
  onFail: "调研笔记 ./out/notes.md 未生成,检查 research 步"
@@ -1,27 +1,31 @@
1
1
  name: book-review-short
2
2
  description: |
3
3
  从书名生成 YouTube Shorts 书评短视频(60 秒):
4
- 调研 → 60 秒口播脚本 → 缩略图 prompt → meta.json。
5
- P0 跑通 steps 1/2/3/5/6/7(step 4 loop/钩子标题 P1 启用)。
4
+ 调研 → 60 秒口播脚本 → 缩略图 prompt + 真生图(可降级)→ meta.json。
5
+ step 4(loop 生成钩子标题)P1 启用。
6
6
  version: "0.1"
7
7
 
8
8
  inputs:
9
9
  - name: book
10
10
  type: string
11
11
  required: true
12
- description: "书名(中英都行)"
12
+ greedy: true
13
+ description: "书名(中英都行,可一句话自然语言;命令行整句进 book,不被空格拆碎)"
13
14
  - name: angle
14
15
  type: enum
15
16
  values: [insight, contrarian, beginner]
16
17
  default: insight
18
+ description: "【高级·用 --input angle=...】切入角度"
17
19
  - name: lang
18
20
  type: enum
19
21
  values: [zh, en]
20
22
  default: zh
23
+ description: "【高级·用 --input lang=...】输出语言"
21
24
 
22
25
  steps:
23
26
  # 1. 调研(确定性工具)
24
27
  - id: research
28
+ title: "1/7·调研书评素材"
25
29
  tool: WebSearch
26
30
  args:
27
31
  query: "${inputs.book} 核心观点 书评 ${inputs.lang}"
@@ -30,6 +34,7 @@ steps:
30
34
 
31
35
  # 2. 60 秒短视频脚本(llm 单轮,不开工具)
32
36
  - id: script
37
+ title: "2/7·写 60 秒口播脚本"
33
38
  llm: |
34
39
  你是一名 YouTube Shorts 创作者。基于以下调研,为书 "${inputs.book}" 写一段
35
40
  60 秒口播脚本,视角是 ${inputs.angle}(insight=洞察、contrarian=逆向、beginner=入门)。
@@ -48,27 +53,36 @@ steps:
48
53
 
49
54
  # 3. 写脚本到文件(确定性工具)
50
55
  - id: write_script
56
+ title: "3/7·脚本落盘"
51
57
  tool: Write
52
58
  args:
53
59
  file_path: "videos/${inputs.book}/script.txt"
54
60
  content: "${script_text}"
55
61
 
56
62
  # 4. 生成 3 个钩子标题(loop + llm)—— P1 启用
63
+ # ⚠️ 启用前注意:step id 必须是静态字符串(id: hook_${idx} 这种动态 id 会破坏
64
+ # checkpoint/resume 的步骤寻址)。正确写法:静态 id + 累积变量拼接,如下。
57
65
  # - id: gen_hooks
58
66
  # type: loop
59
67
  # over: "[1, 2, 3]"
60
68
  # as: idx
61
69
  # do:
62
- # - id: hook_${idx}
70
+ # - id: gen_one_hook
71
+ # title: "生成第 ${idx} 个钩子标题"
63
72
  # llm: |
64
73
  # 为 "${inputs.book}" 书评 Shorts 生成第 ${idx} 个标题(≤20 字 / ≤8 words)。
65
74
  # 风格序号 1=金句、2=反问、3=数字承诺。
66
75
  # 只输出标题本体。
67
76
  # capture:
68
- # text: hook_${idx}
77
+ # text: hook_text
78
+ # - id: accumulate_hook
79
+ # type: set
80
+ # set:
81
+ # hooks_text: "${hooks_text}${idx}. ${hook_text}\n"
69
82
 
70
83
  # 5. 缩略图 prompt(llm 单轮)
71
84
  - id: thumbnail
85
+ title: "4/7·生成缩略图提示词"
72
86
  llm: |
73
87
  为书 "${inputs.book}" 的 YouTube Shorts 缩略图生成英文文生图 prompt:
74
88
  - 主体:书 + 一个隐喻物
@@ -79,21 +93,33 @@ steps:
79
93
  capture:
80
94
  text: thumb_prompt
81
95
 
96
+ # 5b. 真生成缩略图(可选:缺 OPENROUTER_API_KEY 时降级只留 prompt,不挡流水线)
97
+ - id: gen_thumbnail
98
+ title: "5/7·生成缩略图(可降级)"
99
+ skill: image-gen-openrouter
100
+ input: 'prompt=${thumb_prompt} output=videos/${inputs.book}/thumbnail.png aspect=9:16'
101
+ onError: continue
102
+
82
103
  # 6. 元数据汇总(确定性工具)
104
+ # json() 生成含外层引号的合法 JSON 字符串——裸 "${thumb_prompt}" 在值含引号/换行时产坏 JSON
83
105
  - id: meta
106
+ title: "6/7·汇总 meta.json"
84
107
  tool: Write
85
108
  args:
86
109
  file_path: "videos/${inputs.book}/meta.json"
87
110
  content: |
88
111
  {
89
- "book": "${inputs.book}",
112
+ "book": ${json(inputs.book)},
90
113
  "angle": "${inputs.angle}",
91
114
  "lang": "${inputs.lang}",
92
- "thumbnail_prompt": "${thumb_prompt}"
115
+ "thumbnail_prompt": ${json(thumb_prompt)}
93
116
  }
94
117
 
95
- # 7. 守门 assert
118
+ # 7. 守门 verify(升级自 assert;多条 file_exists 拆成 checks 数组)
96
119
  - id: verify
97
- type: assert
98
- condition: 'fileExists("videos/${inputs.book}/script.txt") && fileExists("videos/${inputs.book}/meta.json")'
120
+ title: "7/7·守门校验"
121
+ type: verify
122
+ checks:
123
+ - 'file_exists:videos/${inputs.book}/script.txt'
124
+ - 'file_exists:videos/${inputs.book}/meta.json'
99
125
  onFail: "脚本或 meta 未写盘,请检查 Write 工具"
@@ -9,6 +9,7 @@ inputs:
9
9
 
10
10
  steps:
11
11
  - id: write_note
12
+ title: "1/3·写入便签"
12
13
  tool: Write
13
14
  args:
14
15
  file_path: "out/${inputs.topic}/note.txt"
@@ -17,11 +18,14 @@ steps:
17
18
  args.content: note_body
18
19
 
19
20
  - id: verify_file
20
- type: assert
21
- condition: 'fileExists("out/${inputs.topic}/note.txt")'
21
+ title: "2/3·校验落盘"
22
+ type: verify
23
+ checks:
24
+ - 'file_exists:out/${inputs.topic}/note.txt'
22
25
  onFail: "note.txt 未落盘"
23
26
 
24
27
  - id: verify_capture
28
+ title: "3/3·校验 capture"
25
29
  type: assert
26
30
  condition: 'length(note_body) > 0'
27
31
  onFail: "capture 没绑到 note_body"