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