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
package/src/loop.js
CHANGED
|
@@ -27,19 +27,31 @@
|
|
|
27
27
|
* ============================================================
|
|
28
28
|
*/
|
|
29
29
|
import crypto from 'node:crypto';
|
|
30
|
-
import { autoCompactIfNeeded } from './context/compact.js';
|
|
30
|
+
import { autoCompactIfNeeded, getCompactThreshold } from './context/compact.js';
|
|
31
31
|
import { microCompact, incrementTurn, expireOldEntries } from './context/microCompactLite.js';
|
|
32
32
|
import { isPromptTooLongError, reactiveCompactIfApplicable, } from './context/reactiveCompact.js';
|
|
33
33
|
import { snipCompactIfNeeded } from './context/snipCompact.js';
|
|
34
34
|
import { countMessagesTokens, estimateToolsTokens } from './context/tokenCounter.js';
|
|
35
35
|
import { chat as defaultChat } from './llm/client.js';
|
|
36
|
+
import { buildUserContent } from './multimodal.js';
|
|
36
37
|
import { ALL_TOOLS, executeTool as defaultExecuteTool, getToolByName } from './tools/index.js';
|
|
38
|
+
import { configService } from './config/configService.js';
|
|
37
39
|
/**
|
|
38
|
-
* 工具 schema 的估算 token 数(ALL_TOOLS
|
|
40
|
+
* 工具 schema 的估算 token 数(ALL_TOOLS 固定,首次使用时算一次并缓存)。
|
|
39
41
|
* 每轮 LLM 请求都会带上整套工具 schema,但 countMessagesTokens 只数历史消息、
|
|
40
42
|
* 漏了这部分 → 系统性低估。autoCompact 判定阈值时加上它才准。
|
|
43
|
+
*
|
|
44
|
+
* ⚠️ 必须懒求值而非模块顶层求值:Agent 工具引入了 tools/index → agent → loop →
|
|
45
|
+
* tools/index 的模块环,若从 tools/index 一侧先进入,顶层求值 ALL_TOOLS 时它还在
|
|
46
|
+
* TDZ(临时死区),启动即 ReferenceError。函数体内访问时模块图早已初始化完毕。
|
|
41
47
|
*/
|
|
42
|
-
|
|
48
|
+
let toolsSchemaTokensCache = null;
|
|
49
|
+
function getToolsSchemaTokens() {
|
|
50
|
+
if (toolsSchemaTokensCache === null) {
|
|
51
|
+
toolsSchemaTokensCache = estimateToolsTokens(ALL_TOOLS);
|
|
52
|
+
}
|
|
53
|
+
return toolsSchemaTokensCache;
|
|
54
|
+
}
|
|
43
55
|
/**
|
|
44
56
|
* 执行一次"用户输入 → 模型回答完成"的完整流程。
|
|
45
57
|
*
|
|
@@ -51,11 +63,50 @@ export async function* runQuery(userInput, options) {
|
|
|
51
63
|
// DI fallback:测试可传 stub,生产走真实模块单例
|
|
52
64
|
const chatFn = options.chat ?? defaultChat;
|
|
53
65
|
const executeToolFn = options.executeTool ?? defaultExecuteTool;
|
|
66
|
+
const subagents = await configService.subagents();
|
|
67
|
+
const toolCallContext = {
|
|
68
|
+
history,
|
|
69
|
+
advisorCalls: { used: 0, max: 1 },
|
|
70
|
+
advisorInvocation: 'agent',
|
|
71
|
+
// Agent 等派生工具起嵌套子查询所需的运行态:与主循环同一 provider、
|
|
72
|
+
// 同一审批闸门(子 agent 不绕过宿主策略)。agentCalls 与 advisorCalls 同构,
|
|
73
|
+
// 限同一回合内的子任务派生总数,防失控 fan-out。
|
|
74
|
+
provider,
|
|
75
|
+
toolGate: options.toolGate,
|
|
76
|
+
agentCalls: { used: 0, max: subagents.enabled ? subagents.maxCallsPerTurn : 0 },
|
|
77
|
+
subagentConfig: subagents,
|
|
78
|
+
browserRun: options.browserRun,
|
|
79
|
+
};
|
|
80
|
+
// 实际下发给模型的工具集:allowedTools 未传时给全量;
|
|
81
|
+
// 传入数组时严格按数组过滤,[] 表示本轮不下发任何工具。
|
|
82
|
+
// 仅影响呈现给 LLM 的工具清单,不影响 executeTool 的内部调度能力。
|
|
83
|
+
const activeTools = options.allowedTools !== undefined
|
|
84
|
+
? ALL_TOOLS.filter((t) => options.allowedTools.includes(t.name))
|
|
85
|
+
: ALL_TOOLS.filter((tool) => tool.name !== 'Agent' || subagents.enabled);
|
|
86
|
+
let userContent;
|
|
87
|
+
try {
|
|
88
|
+
userContent = await buildUserContent({
|
|
89
|
+
text: userInput,
|
|
90
|
+
images: options.imageInputs,
|
|
91
|
+
parseInlineImages: options.parseInlineImages,
|
|
92
|
+
detail: options.imageDetail,
|
|
93
|
+
maxLongSidePixel: options.imageMaxLongSidePixel,
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
catch (e) {
|
|
97
|
+
yield {
|
|
98
|
+
type: 'error',
|
|
99
|
+
error: `图片输入处理失败:${e.message}`,
|
|
100
|
+
code: 'llm_error',
|
|
101
|
+
};
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
54
104
|
// 1. 用户消息入栈(v2:填 timestamp,让 MessageList 的 turn 边界横线
|
|
55
105
|
// 能拿到首条 user 的真实时间,否则 fallback Date.now() 会丢失"提交时刻"语义)
|
|
56
106
|
history.push({
|
|
57
107
|
role: 'user',
|
|
58
|
-
content:
|
|
108
|
+
content: userContent,
|
|
109
|
+
...(options.userAttachments?.length ? { attachments: options.userAttachments } : {}),
|
|
59
110
|
timestamp: Date.now(),
|
|
60
111
|
id: crypto.randomUUID(),
|
|
61
112
|
});
|
|
@@ -70,15 +121,29 @@ export async function* runQuery(userInput, options) {
|
|
|
70
121
|
// 作为 autoCompact 的权威判据,校正本地估算的系统性低估;压缩后清空(历史已变,旧值失真)。
|
|
71
122
|
let lastPromptTokens;
|
|
72
123
|
let turn = 0;
|
|
124
|
+
let workStarted = false;
|
|
73
125
|
while (turn < maxTurns) {
|
|
74
126
|
turn++;
|
|
75
127
|
incrementTurn(sessionState?.microCompact);
|
|
76
128
|
expireOldEntries(sessionState?.microCompact);
|
|
77
129
|
if (signal?.aborted) {
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
130
|
+
pushManualInterruptMarker(history);
|
|
131
|
+
if (!workStarted) {
|
|
132
|
+
yield { type: 'error', error: '用户中断了任务', code: 'aborted' };
|
|
133
|
+
}
|
|
134
|
+
else {
|
|
135
|
+
yield { type: 'interrupted', reason: 'manual' };
|
|
136
|
+
}
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
if (options.hasQueuedInput?.()) {
|
|
140
|
+
pushQueuedInputMarker(history);
|
|
141
|
+
if (!workStarted) {
|
|
142
|
+
yield { type: 'error', error: '收到新输入,取消当前任务', code: 'aborted' };
|
|
143
|
+
}
|
|
144
|
+
else {
|
|
145
|
+
yield { type: 'interrupted', reason: 'queued_input' };
|
|
146
|
+
}
|
|
82
147
|
return;
|
|
83
148
|
}
|
|
84
149
|
// 2. 自动压缩(判据 = max(上一轮真实 usage, 历史估算 + 工具 schema),
|
|
@@ -86,7 +151,8 @@ export async function* runQuery(userInput, options) {
|
|
|
86
151
|
try {
|
|
87
152
|
const compact = await autoCompactIfNeeded(history, provider, {
|
|
88
153
|
actualPromptTokens: lastPromptTokens,
|
|
89
|
-
toolsTokens:
|
|
154
|
+
toolsTokens: getToolsSchemaTokens(),
|
|
155
|
+
signal,
|
|
90
156
|
});
|
|
91
157
|
if (compact.compacted) {
|
|
92
158
|
yield { type: 'compact_start' };
|
|
@@ -144,11 +210,18 @@ export async function* runQuery(userInput, options) {
|
|
|
144
210
|
let lastFinishReason = 'unknown';
|
|
145
211
|
// 进入 LLM 流前:等首 token / reasoning,本质是 thinking
|
|
146
212
|
yield { type: 'stage_change', stage: 'thinking' };
|
|
213
|
+
workStarted = true;
|
|
147
214
|
try {
|
|
215
|
+
const modelMessages = options.transientContext?.trim()
|
|
216
|
+
? [
|
|
217
|
+
{ role: 'system', content: options.transientContext.trim() },
|
|
218
|
+
...history,
|
|
219
|
+
]
|
|
220
|
+
: history;
|
|
148
221
|
for await (const ev of chatFn({
|
|
149
222
|
provider,
|
|
150
|
-
messages:
|
|
151
|
-
tools:
|
|
223
|
+
messages: modelMessages,
|
|
224
|
+
tools: activeTools,
|
|
152
225
|
signal,
|
|
153
226
|
})) {
|
|
154
227
|
if (ev.type === 'text_delta') {
|
|
@@ -187,9 +260,14 @@ export async function* runQuery(userInput, options) {
|
|
|
187
260
|
}
|
|
188
261
|
catch (e) {
|
|
189
262
|
if (e.name === 'AbortError') {
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
263
|
+
if (options.hasQueuedInput?.()) {
|
|
264
|
+
pushQueuedInputMarker(history);
|
|
265
|
+
yield { type: 'interrupted', reason: 'queued_input' };
|
|
266
|
+
}
|
|
267
|
+
else {
|
|
268
|
+
pushManualInterruptMarker(history);
|
|
269
|
+
yield { type: 'interrupted', reason: 'manual' };
|
|
270
|
+
}
|
|
193
271
|
return;
|
|
194
272
|
}
|
|
195
273
|
// prompt_too_long 反应式自救:压缩历史 → 同 turn 重发 LLM
|
|
@@ -199,7 +277,7 @@ export async function* runQuery(userInput, options) {
|
|
|
199
277
|
reactiveCount++;
|
|
200
278
|
yield { type: 'compact_start' };
|
|
201
279
|
yield { type: 'stage_change', stage: 'compacting' };
|
|
202
|
-
const result = await reactiveCompactIfApplicable(history, provider, e, sessionState?.reactive);
|
|
280
|
+
const result = await reactiveCompactIfApplicable(history, provider, e, sessionState?.reactive, signal);
|
|
203
281
|
// 仅当确实压下去了(after < before)才重试;否则(没压动 / 未恢复)退回错误路径,杜绝死循环。
|
|
204
282
|
const shrank = (result.after ?? 0) < (result.before ?? 0);
|
|
205
283
|
if (result.recovered && shrank) {
|
|
@@ -226,6 +304,17 @@ export async function* runQuery(userInput, options) {
|
|
|
226
304
|
};
|
|
227
305
|
return;
|
|
228
306
|
}
|
|
307
|
+
if (lastFinishReason === 'aborted' || signal?.aborted || options.hasQueuedInput?.()) {
|
|
308
|
+
if (options.hasQueuedInput?.() && !signal?.aborted) {
|
|
309
|
+
pushQueuedInputMarker(history);
|
|
310
|
+
yield { type: 'interrupted', reason: 'queued_input' };
|
|
311
|
+
}
|
|
312
|
+
else {
|
|
313
|
+
pushManualInterruptMarker(history);
|
|
314
|
+
yield { type: 'interrupted', reason: 'manual' };
|
|
315
|
+
}
|
|
316
|
+
return;
|
|
317
|
+
}
|
|
229
318
|
// S2 防御:OpenAI 协议保证 tool_call 的 index 连续;万一某 provider 跳号产生稀疏数组,
|
|
230
319
|
// 这里压实掉空洞,避免后续遍历 / 回填遇到 undefined(assistantMsg.tool_calls 与执行都用它)。
|
|
231
320
|
{
|
|
@@ -259,6 +348,16 @@ export async function* runQuery(userInput, options) {
|
|
|
259
348
|
'可调大模型的 max_tokens 上限,或让模型分段输出。',
|
|
260
349
|
};
|
|
261
350
|
}
|
|
351
|
+
const estimatedTokens = countMessagesTokens(history) + getToolsSchemaTokens();
|
|
352
|
+
const usedTokens = Math.min(provider.contextWindow, Math.max(lastPromptTokens ?? 0, estimatedTokens));
|
|
353
|
+
yield {
|
|
354
|
+
type: 'context_usage',
|
|
355
|
+
usedTokens,
|
|
356
|
+
contextWindow: provider.contextWindow,
|
|
357
|
+
remainingTokens: Math.max(0, provider.contextWindow - usedTokens),
|
|
358
|
+
compactThreshold: getCompactThreshold(provider),
|
|
359
|
+
source: lastPromptTokens === undefined ? 'estimated' : 'actual',
|
|
360
|
+
};
|
|
262
361
|
yield { type: 'turn_done' };
|
|
263
362
|
return;
|
|
264
363
|
}
|
|
@@ -274,13 +373,34 @@ export async function* runQuery(userInput, options) {
|
|
|
274
373
|
// (OpenAI 协议要求 tool 消息与上一条 assistant 的 tool_calls 一一对应)。
|
|
275
374
|
yield { type: 'stage_change', stage: 'tool_executing' };
|
|
276
375
|
// 执行单个 tool call → WorkerOutcome(内部吞掉 abort/异常,永不 reject)。
|
|
277
|
-
const runOne = async (tc) => {
|
|
376
|
+
const runOne = async (tc, reportProgress) => {
|
|
278
377
|
try {
|
|
279
|
-
|
|
378
|
+
// 工具执行闸门:宿主注入后覆盖所有工具,包括只读工具。deny → 把 reason 当
|
|
379
|
+
// tool_result 回填,模型据此改道/如实报告,不抛异常不中断整轮。宿主异常一律 deny。
|
|
380
|
+
const isReadOnly = getToolByName(tc.function.name)?.isReadOnly === true;
|
|
381
|
+
if (options.toolGate) {
|
|
382
|
+
let decision;
|
|
383
|
+
try {
|
|
384
|
+
decision = await options.toolGate({ name: tc.function.name, argsJson: tc.function.arguments, isReadOnly, toolCallId: tc.id }, signal);
|
|
385
|
+
}
|
|
386
|
+
catch {
|
|
387
|
+
decision = { allow: false, reason: '工具执行审批失败(宿主异常)' };
|
|
388
|
+
}
|
|
389
|
+
if (!decision.allow) {
|
|
390
|
+
return { tc, result: { ok: false, error: decision.reason } };
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
options.onToolStart?.(tc.function.name, tc.id);
|
|
394
|
+
// 浅拷贝填入本次调用的 toolCallId:进度按调用实例路由(并行同名工具不串扰)。
|
|
395
|
+
// advisorCalls/agentCalls 是共享对象引用,计数仍全轮共享,不受拷贝影响。
|
|
396
|
+
const result = await executeToolFn(tc.function.name, tc.function.arguments, signal, { ...toolCallContext, toolCallId: tc.id, reportProgress });
|
|
397
|
+
options.onToolEnd?.(tc.id, false);
|
|
280
398
|
return { tc, result };
|
|
281
399
|
}
|
|
282
400
|
catch (e) {
|
|
283
|
-
|
|
401
|
+
const wasAborted = e.name === 'AbortError';
|
|
402
|
+
options.onToolEnd?.(tc.id, wasAborted);
|
|
403
|
+
if (wasAborted) {
|
|
284
404
|
return { tc, result: { ok: false, error: 'aborted' } };
|
|
285
405
|
}
|
|
286
406
|
return { tc, result: { ok: false, error: e.message } };
|
|
@@ -303,6 +423,7 @@ export async function* runQuery(userInput, options) {
|
|
|
303
423
|
: o.result.error === 'aborted'
|
|
304
424
|
? '(已中断)'
|
|
305
425
|
: `Error: ${o.result.error}`,
|
|
426
|
+
metadata: o.result.metadata,
|
|
306
427
|
});
|
|
307
428
|
const allConcurrencySafe = toolCallsByIndex.every((tc) => {
|
|
308
429
|
const tool = getToolByName(tc.function.name);
|
|
@@ -320,7 +441,13 @@ export async function* runQuery(userInput, options) {
|
|
|
320
441
|
};
|
|
321
442
|
const workers = toolCallsByIndex.map(async (tc) => {
|
|
322
443
|
enqueue(startEvent(tc));
|
|
323
|
-
const o = await runOne(tc)
|
|
444
|
+
const o = await runOne(tc, (message, detail) => enqueue({
|
|
445
|
+
type: 'tool_progress',
|
|
446
|
+
toolCallId: tc.id,
|
|
447
|
+
toolName: tc.function.name,
|
|
448
|
+
message,
|
|
449
|
+
detail,
|
|
450
|
+
}));
|
|
324
451
|
enqueue(endEvent(o));
|
|
325
452
|
return o;
|
|
326
453
|
});
|
|
@@ -347,7 +474,32 @@ export async function* runQuery(userInput, options) {
|
|
|
347
474
|
settled = [];
|
|
348
475
|
for (const tc of toolCallsByIndex) {
|
|
349
476
|
yield startEvent(tc);
|
|
350
|
-
const
|
|
477
|
+
const queue = [];
|
|
478
|
+
let wake = null;
|
|
479
|
+
let finished = false;
|
|
480
|
+
const worker = runOne(tc, (message, detail) => {
|
|
481
|
+
queue.push({
|
|
482
|
+
type: 'tool_progress',
|
|
483
|
+
toolCallId: tc.id,
|
|
484
|
+
toolName: tc.function.name,
|
|
485
|
+
message,
|
|
486
|
+
detail,
|
|
487
|
+
});
|
|
488
|
+
wake?.();
|
|
489
|
+
wake = null;
|
|
490
|
+
}).finally(() => {
|
|
491
|
+
finished = true;
|
|
492
|
+
wake?.();
|
|
493
|
+
wake = null;
|
|
494
|
+
});
|
|
495
|
+
while (!finished || queue.length > 0) {
|
|
496
|
+
while (queue.length > 0)
|
|
497
|
+
yield queue.shift();
|
|
498
|
+
if (finished)
|
|
499
|
+
break;
|
|
500
|
+
await new Promise((resolve) => { wake = resolve; });
|
|
501
|
+
}
|
|
502
|
+
const o = await worker;
|
|
351
503
|
yield endEvent(o);
|
|
352
504
|
settled.push(o);
|
|
353
505
|
}
|
|
@@ -366,13 +518,25 @@ export async function* runQuery(userInput, options) {
|
|
|
366
518
|
role: 'tool',
|
|
367
519
|
content,
|
|
368
520
|
tool_call_id: tc.id,
|
|
521
|
+
...(result.metadata ? { display_metadata: result.metadata } : {}),
|
|
369
522
|
timestamp: Date.now(),
|
|
370
523
|
id: crypto.randomUUID(),
|
|
371
524
|
});
|
|
372
525
|
}
|
|
373
526
|
if (anyAborted || signal?.aborted) {
|
|
374
|
-
|
|
375
|
-
|
|
527
|
+
if (options.hasQueuedInput?.()) {
|
|
528
|
+
pushQueuedInputMarker(history);
|
|
529
|
+
yield { type: 'interrupted', reason: 'queued_input' };
|
|
530
|
+
}
|
|
531
|
+
else {
|
|
532
|
+
pushManualInterruptMarker(history);
|
|
533
|
+
yield { type: 'interrupted', reason: 'manual' };
|
|
534
|
+
}
|
|
535
|
+
return;
|
|
536
|
+
}
|
|
537
|
+
if (options.hasQueuedInput?.()) {
|
|
538
|
+
pushQueuedInputMarker(history);
|
|
539
|
+
yield { type: 'interrupted', reason: 'queued_input' };
|
|
376
540
|
return;
|
|
377
541
|
}
|
|
378
542
|
// 6. 继续 while 让模型看到 tool_result 后继续推理
|
|
@@ -386,14 +550,30 @@ export async function* runQuery(userInput, options) {
|
|
|
386
550
|
}
|
|
387
551
|
// ---------------- 辅助函数 ----------------
|
|
388
552
|
/**
|
|
389
|
-
*
|
|
390
|
-
|
|
391
|
-
|
|
553
|
+
* 用户按ESC手动中断
|
|
554
|
+
*/
|
|
555
|
+
function pushManualInterruptMarker(history) {
|
|
556
|
+
history.push({
|
|
557
|
+
role: 'user',
|
|
558
|
+
content: '(用户按下了 ESC/Ctrl+C 中断了任务。等待用户新指令。)',
|
|
559
|
+
timestamp: Date.now(),
|
|
560
|
+
id: crypto.randomUUID(),
|
|
561
|
+
});
|
|
562
|
+
}
|
|
563
|
+
/**
|
|
564
|
+
* 有新的用户输入在队列中,当前安全停靠
|
|
392
565
|
*/
|
|
393
|
-
function
|
|
566
|
+
function pushQueuedInputMarker(history) {
|
|
394
567
|
history.push({
|
|
395
568
|
role: 'user',
|
|
396
|
-
content:
|
|
569
|
+
content: [
|
|
570
|
+
'(系统提示:用户在工作过程中发送了新的消息。',
|
|
571
|
+
'你现在看到的是上一轮工作在被打断前已完成的部分。',
|
|
572
|
+
'请阅读用户的新消息,判断:',
|
|
573
|
+
'1. 如果是不相关的新任务:简要确认已收到,转向新任务,可在新任务完成后询问是否回到之前工作',
|
|
574
|
+
'2. 如果是补充信息或前置依赖:把新信息纳入考虑,判断是否需要调整已做的工作,然后继续',
|
|
575
|
+
'3. 如果是改变方向或取消:评估已完成的部分,说明你对变更的理解,按新方向推进)',
|
|
576
|
+
].join('\n'),
|
|
397
577
|
timestamp: Date.now(),
|
|
398
578
|
id: crypto.randomUUID(),
|
|
399
579
|
});
|
|
@@ -466,6 +646,10 @@ function friendlyToolDescription(toolName, rawArgsJson) {
|
|
|
466
646
|
}
|
|
467
647
|
}
|
|
468
648
|
case 'WebSearch': return `Searching \`${truncate(args.query, 50)}\``;
|
|
649
|
+
case 'Agent': {
|
|
650
|
+
const t = typeof args.agent_type === 'string' ? args.agent_type : 'explore';
|
|
651
|
+
return `子任务[${t}] ${truncate(args.description, 50)}`;
|
|
652
|
+
}
|
|
469
653
|
default: return `${toolName}(...)`;
|
|
470
654
|
}
|
|
471
655
|
}
|
package/src/main.js
CHANGED
|
@@ -19,9 +19,12 @@ const pkg = (() => {
|
|
|
19
19
|
})();
|
|
20
20
|
import { extractCwdArg } from './bootstrap/cwdArg.js';
|
|
21
21
|
import { initWorkingDir, getWorkingDir } from './bootstrap/workingDir.js';
|
|
22
|
+
import { ensureAgentBrowserOnPath } from './tools/webbrowser/agentBrowserPath.js';
|
|
22
23
|
import { applyToolKeysToEnv, loadProviderLayered } from './config.js';
|
|
23
24
|
import { clearContext, loadContext } from './context/persistContext.js';
|
|
24
|
-
import {
|
|
25
|
+
import { validateSessionId } from './context/sessionRegistry.js';
|
|
26
|
+
import { workspaceIndexService } from './context/workspaceIndex.js';
|
|
27
|
+
import { migrateLegacyContext, sessionFileFor } from './context/sessionPath.js';
|
|
25
28
|
import { buildFullSystemPrompt } from './prompts/system.js';
|
|
26
29
|
import { ALL_TOOLS } from './tools/index.js';
|
|
27
30
|
import { Root } from './ui/Root.js';
|
|
@@ -60,12 +63,25 @@ async function main() {
|
|
|
60
63
|
console.log(`minimal-agent v${pkg.version}`);
|
|
61
64
|
return;
|
|
62
65
|
}
|
|
66
|
+
if (args.includes('--workspace-doctor')) {
|
|
67
|
+
const snapshot = await workspaceIndexService.snapshot();
|
|
68
|
+
console.log(`Workspace Index v${snapshot.version} · revision ${snapshot.revision}`);
|
|
69
|
+
for (const workspace of snapshot.workspaces) {
|
|
70
|
+
console.log(`${workspace.available ? '✓' : '○'} ${workspace.displayPath} · ${workspace.sessions.length} sessions`);
|
|
71
|
+
}
|
|
72
|
+
console.log(`可恢复/已过滤历史:${snapshot.recoverable.length}`);
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
63
75
|
// --clear:清空当前工作目录的会话(归档 + 清空)后退出。供 webchat 的 /new 调用。
|
|
64
76
|
// 无需 provider;上面 -d/--cwd 已 chdir + initWorkingDir,clearContext() 作用于该目录。
|
|
65
77
|
if (args.includes('--clear')) {
|
|
66
78
|
await clearContext();
|
|
67
79
|
return;
|
|
68
80
|
}
|
|
81
|
+
// 内置 agent 浏览器:把已 vendored/绿色版同级的 agent-browser 目录前置进 PATH,
|
|
82
|
+
// 让 Bash 工具 / skill 脚本 / 工作流 bash 步也能直接调(tools/skills/工作流共用底座)。
|
|
83
|
+
// dev/npm 无 vendored 二进制时 no-op;不污染 stdout,-p json 契约安全。
|
|
84
|
+
await ensureAgentBrowserOnPath();
|
|
69
85
|
const isPrintMode = args.includes('-p') || args.includes('--print');
|
|
70
86
|
if (isPrintMode) {
|
|
71
87
|
// 先把输出格式解析出来——provider 缺失分支也要按 json 契约给信号。
|
|
@@ -81,6 +97,20 @@ async function main() {
|
|
|
81
97
|
const rawMaxTurns = extractFlagValue(args, ['--max-turns']);
|
|
82
98
|
const parsedMaxTurns = rawMaxTurns !== undefined ? Number.parseInt(rawMaxTurns, 10) : Number.NaN;
|
|
83
99
|
const maxTurns = Number.isNaN(parsedMaxTurns) ? undefined : parsedMaxTurns;
|
|
100
|
+
// -s/--session <id>:选定同 cwd 下的哪个 session;缺省 'default'(向后兼容,
|
|
101
|
+
// 逐字节保持旧行为)。读、写必须用同一份解析结果——不能只让加载感知这个 flag,
|
|
102
|
+
// 落盘时各自重新计算,否则出现"从 session A 读、写回 default"的读写分裂。
|
|
103
|
+
let sessionId;
|
|
104
|
+
try {
|
|
105
|
+
sessionId = validateSessionId(extractFlagValue(args, ['-s', '--session']) ?? 'default');
|
|
106
|
+
}
|
|
107
|
+
catch (error) {
|
|
108
|
+
console.error(`无效的 --session:${error.message}`);
|
|
109
|
+
process.exitCode = 2;
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
await workspaceIndexService.register(getWorkingDir()).catch(() => { });
|
|
113
|
+
const sessionFile = sessionFileFor(getWorkingDir(), sessionId);
|
|
84
114
|
// CLI 非交互模式:先 env,再 ~/.minimal-agent/config.json fallback;都没就退出
|
|
85
115
|
// (宿主 spawn 子进程时通常没 export env,靠 TUI 向导写出的 config.json 持久化)
|
|
86
116
|
const provider = await loadProviderLayered();
|
|
@@ -104,24 +134,29 @@ async function main() {
|
|
|
104
134
|
process.stderr.write(`\n未找到 provider 配置。\n\n` +
|
|
105
135
|
`请二选一:\n` +
|
|
106
136
|
` 1. 设置环境变量 MINIMAL_AGENT_BASE_URL / MINIMAL_AGENT_API_KEY / MINIMAL_AGENT_MODEL\n` +
|
|
107
|
-
` 2. 先直接运行 \`minimal-agent\`(TUI
|
|
137
|
+
` 2. 先直接运行 \`minimal-agent\`(TUI)在首次配置菜单里配好基座模型;菜单会写出 ~/.minimal-agent/config.json,之后 -p 模式自动复用\n\n`);
|
|
108
138
|
process.exit(1);
|
|
109
139
|
}
|
|
110
|
-
const initialHistory = await buildInitialHistory();
|
|
140
|
+
const initialHistory = await buildInitialHistory(sessionFile);
|
|
111
141
|
const verbose = args.includes('--verbose') || args.includes('-v');
|
|
112
142
|
// stream-json 走独立流式模式(webchat broker / 网关用);text/json 仍走原 runPrintMode(契约不变)。
|
|
113
143
|
if (outputFormat === 'stream-json') {
|
|
114
144
|
await runStreamJsonMode(provider, args, initialHistory, {
|
|
115
145
|
maxTurns,
|
|
116
146
|
compact: args.includes('--compact'),
|
|
147
|
+
sessionFile,
|
|
148
|
+
workspace: { cwd: getWorkingDir(), sessionId },
|
|
117
149
|
});
|
|
118
150
|
return;
|
|
119
151
|
}
|
|
120
|
-
await runPrintMode(provider, args, initialHistory, {
|
|
152
|
+
await runPrintMode(provider, args, initialHistory, {
|
|
153
|
+
verbose, outputFormat, maxTurns, sessionFile,
|
|
154
|
+
workspace: { cwd: getWorkingDir(), sessionId },
|
|
155
|
+
});
|
|
121
156
|
return;
|
|
122
157
|
}
|
|
123
|
-
// TUI
|
|
124
|
-
// 都没有则 Root
|
|
158
|
+
// TUI 交互模式:优先读 JSON 配置(home → exe 同级),没有再用 env 兜底;
|
|
159
|
+
// 都没有则 Root 渲染首次配置菜单。
|
|
125
160
|
const initialProvider = await loadProviderLayered();
|
|
126
161
|
const { waitUntilExit } = render(_jsx(Root, { initialProvider: initialProvider }));
|
|
127
162
|
await waitUntilExit();
|
|
@@ -133,10 +168,14 @@ async function main() {
|
|
|
133
168
|
* 总是用 fresh system prompt(即便 last-context.json 里也存了一份),
|
|
134
169
|
* 这样换 cwd 或工具后也不会沿用错的环境上下文。
|
|
135
170
|
*/
|
|
136
|
-
|
|
171
|
+
/**
|
|
172
|
+
* @param sessionFile 显式会话文件路径(`-p`/stream-json 模式下由 `--session` 解析而来);
|
|
173
|
+
* 缺省时走 `loadContext()` 的默认路径(= default session,向后兼容)。
|
|
174
|
+
*/
|
|
175
|
+
async function buildInitialHistory(sessionFile) {
|
|
137
176
|
const content = await buildFullSystemPrompt(getWorkingDir(), ALL_TOOLS);
|
|
138
177
|
const fresh = { role: 'system', content };
|
|
139
|
-
const persisted = await loadContext();
|
|
178
|
+
const persisted = await loadContext(sessionFile);
|
|
140
179
|
if (!persisted || persisted.length === 0)
|
|
141
180
|
return [fresh];
|
|
142
181
|
const rest = persisted[0]?.role === 'system' ? persisted.slice(1) : persisted;
|
|
@@ -155,24 +194,35 @@ minimal-agent - 轻量级 AI 编程助手
|
|
|
155
194
|
-v, --verbose 显示详细输出(工具调用、压缩信息)
|
|
156
195
|
-d, --cwd <dir> 指定工作目录(不存在自动创建);启动时 chdir 到这里,
|
|
157
196
|
上下文文件、工具相对路径、.env 加载都以此为基准
|
|
197
|
+
-s, --session <id> 选定同一工作目录下的哪个会话(缺省 'default');配合 TUI 的
|
|
198
|
+
/session /sessions /resume 是同一套内核会话,互不冲突
|
|
158
199
|
--output-format <fmt> text(默认)= 纯文本答案;json = 一行结局契约
|
|
159
200
|
(ok / result / stop_reason / num_turns / error);
|
|
160
201
|
stream-json = 逐行 LoopEvent(webchat / 网关 broker 用)
|
|
161
202
|
--max-turns <n> 最大工具循环轮数(防失控;缺省 50)
|
|
203
|
+
--image <path|url> 附加图片(可重复;JPEG/PNG/GIF/WEBP,本地单图≤10MB)
|
|
204
|
+
--image-detail <d> 图片细节级别:low / default / high(默认 default)
|
|
205
|
+
--image-max-long-side-pixel <n>
|
|
206
|
+
OpenAI 兼容图片参数:限制最长边像素
|
|
162
207
|
--clear 清空当前工作目录的会话(归档 + 清空)后退出
|
|
163
208
|
--compact 与 -p --output-format stream-json 连用:压缩当前会话
|
|
209
|
+
--workspace-doctor 只读诊断全局工作区索引、Session 数量和孤立历史
|
|
164
210
|
-h, --help 显示帮助信息
|
|
165
211
|
|
|
166
212
|
会话记忆:
|
|
167
|
-
|
|
168
|
-
~/.minimal-agent/sessions
|
|
169
|
-
TUI 与 -p
|
|
213
|
+
按当前工作目录隔离,同一工作目录下可以有多个会话(session),
|
|
214
|
+
自动加载 / 保存到 ~/.minimal-agent/sessions/<目录哈希>--<sessionId>.json
|
|
215
|
+
(不传 --session 时用 'default')。TUI 与 -p 共享同一套会话;
|
|
216
|
+
TUI 里用 /session /sessions /resume 列出并切换会话、/session new [标题] 新建;
|
|
217
|
+
-p 用 -s/--session <id> 选定;TUI 中输入 /new 仅清当前会话(不影响其它会话)
|
|
170
218
|
|
|
171
219
|
示例:
|
|
172
220
|
minimal-agent -p "帮我写一个 hello world"
|
|
173
221
|
echo "解释代码" | minimal-agent -p
|
|
174
222
|
minimal-agent -p --verbose "运行测试并报告结果"
|
|
175
223
|
minimal-agent -p "处理资料" -d /tmp/job-123 # 工作目录隔离
|
|
224
|
+
minimal-agent -p --image ./ui.png "评价这个界面"
|
|
225
|
+
minimal-agent -p "@/tmp/a.png 请识别图片内容" # TUI / -p 均支持 @图片路径
|
|
176
226
|
minimal-agent -p --output-format json "做点事" # 宿主程序 spawn:一行 JSON 结局
|
|
177
227
|
`);
|
|
178
228
|
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ============================================================
|
|
3
|
+
* src/messageQueue.ts —— 用户输入队列(支持优先级)
|
|
4
|
+
* ------------------------------------------------------------
|
|
5
|
+
* 实现对话进行中用户插话的排队管理。
|
|
6
|
+
*
|
|
7
|
+
* 设计原则:
|
|
8
|
+
* - 极简:只保留 minimal-agent 需要的核心功能
|
|
9
|
+
* - 发布订阅:subscribe() 订阅队列变化,用于UI更新
|
|
10
|
+
* - 工厂函数:createInputQueue() 创建独立实例,支持多session/测试
|
|
11
|
+
* - 两级优先级:'now'(立即处理)> 'next'(排队等待)
|
|
12
|
+
* ============================================================
|
|
13
|
+
*/
|
|
14
|
+
function randomUUID() {
|
|
15
|
+
if (typeof crypto !== 'undefined' && 'randomUUID' in crypto) {
|
|
16
|
+
return crypto.randomUUID();
|
|
17
|
+
}
|
|
18
|
+
return `q_${Date.now()}_${Math.random().toString(36).slice(2)}`;
|
|
19
|
+
}
|
|
20
|
+
export function createInputQueue() {
|
|
21
|
+
const queue = [];
|
|
22
|
+
const subscribers = new Set();
|
|
23
|
+
function notify() {
|
|
24
|
+
for (const cb of subscribers) {
|
|
25
|
+
cb();
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
function findBestIndex() {
|
|
29
|
+
let bestIdx = 0;
|
|
30
|
+
let bestPrio = 99;
|
|
31
|
+
for (let i = 0; i < queue.length; i++) {
|
|
32
|
+
const p = queue[i].priority === 'now' ? 0 : 1;
|
|
33
|
+
if (p < bestPrio) {
|
|
34
|
+
bestPrio = p;
|
|
35
|
+
bestIdx = i;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return bestIdx;
|
|
39
|
+
}
|
|
40
|
+
return {
|
|
41
|
+
enqueue(value, priority = 'next') {
|
|
42
|
+
queue.push({
|
|
43
|
+
id: randomUUID(),
|
|
44
|
+
value,
|
|
45
|
+
priority,
|
|
46
|
+
timestamp: Date.now(),
|
|
47
|
+
});
|
|
48
|
+
notify();
|
|
49
|
+
},
|
|
50
|
+
dequeue() {
|
|
51
|
+
if (queue.length === 0)
|
|
52
|
+
return undefined;
|
|
53
|
+
const bestIdx = findBestIndex();
|
|
54
|
+
const item = queue.splice(bestIdx, 1)[0];
|
|
55
|
+
if (item)
|
|
56
|
+
notify();
|
|
57
|
+
return item;
|
|
58
|
+
},
|
|
59
|
+
dequeueAll() {
|
|
60
|
+
if (queue.length === 0)
|
|
61
|
+
return [];
|
|
62
|
+
const sorted = [...queue].sort((a, b) => {
|
|
63
|
+
const pa = a.priority === 'now' ? 0 : 1;
|
|
64
|
+
const pb = b.priority === 'now' ? 0 : 1;
|
|
65
|
+
if (pa !== pb)
|
|
66
|
+
return pa - pb;
|
|
67
|
+
return a.timestamp - b.timestamp;
|
|
68
|
+
});
|
|
69
|
+
queue.length = 0;
|
|
70
|
+
notify();
|
|
71
|
+
return sorted;
|
|
72
|
+
},
|
|
73
|
+
peek() {
|
|
74
|
+
if (queue.length === 0)
|
|
75
|
+
return undefined;
|
|
76
|
+
return queue[findBestIndex()];
|
|
77
|
+
},
|
|
78
|
+
clear() {
|
|
79
|
+
if (queue.length === 0)
|
|
80
|
+
return;
|
|
81
|
+
queue.length = 0;
|
|
82
|
+
notify();
|
|
83
|
+
},
|
|
84
|
+
size() {
|
|
85
|
+
return queue.length;
|
|
86
|
+
},
|
|
87
|
+
subscribe(callback) {
|
|
88
|
+
subscribers.add(callback);
|
|
89
|
+
return () => {
|
|
90
|
+
subscribers.delete(callback);
|
|
91
|
+
};
|
|
92
|
+
},
|
|
93
|
+
getSnapshot() {
|
|
94
|
+
return Object.freeze([...queue]);
|
|
95
|
+
},
|
|
96
|
+
};
|
|
97
|
+
}
|