minimal-agent 0.8.2 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -12
- 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 +3 -1
- package/plugins/workflow-runner/src/agentCommon.js +249 -51
- package/plugins/workflow-runner/src/dslManifest.js +72 -0
- package/plugins/workflow-runner/src/expressions.js +198 -15
- package/plugins/workflow-runner/src/index.js +40 -8
- package/plugins/workflow-runner/src/jsonPayload.js +1 -1
- package/plugins/workflow-runner/src/loader.js +286 -72
- package/plugins/workflow-runner/src/multimodal.js +37 -0
- package/plugins/workflow-runner/src/recovery.js +49 -39
- package/plugins/workflow-runner/src/runner.js +802 -287
- package/plugins/workflow-runner/src/stepExecutors/agent.js +157 -39
- package/plugins/workflow-runner/src/stepExecutors/assert.js +16 -3
- package/plugins/workflow-runner/src/stepExecutors/llm.js +60 -111
- package/plugins/workflow-runner/src/stepExecutors/skill.js +47 -47
- package/plugins/workflow-runner/src/stepExecutors/tool.js +76 -10
- package/plugins/workflow-runner/src/stepExecutors/verify.js +27 -10
- package/plugins/workflow-runner/src/taskIdentity.js +296 -0
- package/plugins/workflow-runner/src/types.js +19 -9
- package/plugins/workflow-runner/src/workflowState.js +516 -33
- package/skills/adversarial-review/SKILL.md +0 -1
- 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 +97 -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 +7 -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 +28 -15
- package/src/llm/client.js +394 -159
- package/src/llm/providers/anthropicFormat.js +241 -0
- package/src/llm/providers/openaiFormat.js +190 -0
- package/src/loop.js +219 -32
- 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/pluginLoader.js +2 -1
- package/src/plugins/pluginRunner.js +49 -21
- package/src/plugins/transcript.js +4 -3
- package/src/prompts/pluginList.js +5 -5
- package/src/prompts/projectInstructions.js +3 -3
- package/src/prompts/promptAssembly.js +133 -144
- package/src/prompts/skillList.js +12 -22
- package/src/prompts/system.js +0 -1
- 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 +29 -10
- package/src/ui/InputBox.js +104 -12
- 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 +358 -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 +13 -5
- package/workflows/book-review-short.yaml +36 -10
- package/workflows/e2e-write-greet.yaml +6 -2
- package/workflows/illustrate.yaml +227 -0
- package/workflows/plan.yaml +460 -179
- package/workflows/ppt.yaml +1659 -0
- package/workflows/ralph-loop.yaml +199 -0
- package/workflows/schema.json +81 -23
- package/workflows/webppt.yaml +496 -0
- package/workflows/word.yaml +710 -0
- package/workflows/youtube-shorts.yaml +46 -15
- package/MINIMAL-TRACE.md +0 -77
- package/plugins/HOW-TO-WRITE-A-PLUGIN.md +0 -186
- package/plugins/ralph-wiggum/.claude-plugin/plugin.json +0 -9
- package/plugins/ralph-wiggum/README.md +0 -197
- package/plugins/ralph-wiggum/commands/cancel-ralph.md +0 -18
- package/plugins/ralph-wiggum/commands/help.md +0 -137
- package/plugins/ralph-wiggum/commands/ralph-loop.md +0 -85
- package/plugins/ralph-wiggum/hooks/hooks.json +0 -15
- package/plugins/ralph-wiggum/hooks/stop-hook.sh +0 -191
- package/plugins/ralph-wiggum/plugin.js +0 -280
- package/plugins/ralph-wiggum/src/goalState.js +0 -258
- package/plugins/ralph-wiggum/src/sentinels.js +0 -76
- package/plugins/ralph-wiggum/src/stopHookRunner.js +0 -104
- package/plugins/ralph-wiggum/src/verificationGate.js +0 -202
- package/plugins/workflow-runner/src/miniReAct.js +0 -223
- 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/src/ui/ProgressPanel.js +0 -54
- package/src/ui/StreamingBlock.js +0 -11
- package/src/ui/ToolStatus.js +0 -11
|
@@ -0,0 +1,446 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import { readMinimalAgentConfig, updateMinimalAgentConfig, defaultMaxTokens, DEFAULT_SUBAGENTS_CONFIG, DEFAULT_BROWSER_CONFIG, } from './configFile.js';
|
|
3
|
+
const DEFAULT_CONTEXT_WINDOW = 128_000;
|
|
4
|
+
function positiveInt(value, fallback) {
|
|
5
|
+
const parsed = Number.parseInt(value ?? '', 10);
|
|
6
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* 纯 env 自举路径(无 JSON 配置文件时的兜底)。只产出 OpenAI 格式的模型——Anthropic 格式
|
|
10
|
+
* 只能通过 JSON 配置(Desktop/CLI)新增,这是刻意的范围限制,不是遗漏。
|
|
11
|
+
*
|
|
12
|
+
* 顾问不再有独立的 env 自举路径(ADVISOR_AGENT_* 家族整体移除)——顾问配置以后只能通过
|
|
13
|
+
* JSON 配置的 `advisor.modelId` 引用模型列表条目,没有 JSON 时顾问就是未配置。
|
|
14
|
+
*
|
|
15
|
+
* ⚠️ 这里派生的模型 id/createdAt/updatedAt 必须是固定字面量,不能用 randomUUID()/Date.now()——
|
|
16
|
+
* configFromEnv() 在每次 reload() 都会重新构造,reload() 靠 JSON.stringify 差异判断 revision
|
|
17
|
+
* 是否变化;任何一次性值都会让 env 模式下每次 reload 的 revision 都在跳,进而每轮对话都触发
|
|
18
|
+
* 桌面端 resetProvider()。
|
|
19
|
+
*/
|
|
20
|
+
function configFromEnv() {
|
|
21
|
+
const baseURL = process.env.MINIMAL_AGENT_BASE_URL;
|
|
22
|
+
const apiKey = process.env.MINIMAL_AGENT_API_KEY;
|
|
23
|
+
const model = process.env.MINIMAL_AGENT_MODEL;
|
|
24
|
+
if (!baseURL || !apiKey || !model)
|
|
25
|
+
return null;
|
|
26
|
+
const tools = {};
|
|
27
|
+
if (process.env.TAVILY_API_KEY)
|
|
28
|
+
tools.webSearch = { provider: 'tavily', apiKey: process.env.TAVILY_API_KEY };
|
|
29
|
+
if (process.env.OPENAI_IMAGE_BASE_URL && process.env.OPENAI_IMAGE_API_KEY && process.env.OPENAI_IMAGE_MODEL) {
|
|
30
|
+
tools.imageGeneration = {
|
|
31
|
+
provider: process.env.OPENAI_IMAGE_PROVIDER ?? 'openai',
|
|
32
|
+
baseURL: process.env.OPENAI_IMAGE_BASE_URL,
|
|
33
|
+
apiKey: process.env.OPENAI_IMAGE_API_KEY,
|
|
34
|
+
model: process.env.OPENAI_IMAGE_MODEL,
|
|
35
|
+
protocol: process.env.OPENAI_IMAGE_PROTOCOL === 'chat' || process.env.OPENAI_IMAGE_PROTOCOL === 'images'
|
|
36
|
+
? process.env.OPENAI_IMAGE_PROTOCOL
|
|
37
|
+
: 'auto',
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
if (process.env.OPENROUTER_API_KEY)
|
|
41
|
+
tools.openRouter = { apiKey: process.env.OPENROUTER_API_KEY };
|
|
42
|
+
if (process.env.MINIMAX_API_KEY && process.env.MINIMAX_GROUP_ID) {
|
|
43
|
+
tools.textToSpeech = { provider: 'minimax', apiKey: process.env.MINIMAX_API_KEY, groupId: process.env.MINIMAX_GROUP_ID };
|
|
44
|
+
}
|
|
45
|
+
const compactRatio = Number(process.env.MINIMAL_AGENT_COMPACT_RATIO);
|
|
46
|
+
const name = process.env.MINIMAL_AGENT_PROVIDER ?? 'env';
|
|
47
|
+
const contextWindow = positiveInt(process.env.MINIMAL_AGENT_CONTEXT_WINDOW, DEFAULT_CONTEXT_WINDOW);
|
|
48
|
+
const envModel = {
|
|
49
|
+
id: 'env-base',
|
|
50
|
+
apiFormat: 'openai',
|
|
51
|
+
...(name !== 'env' ? { displayName: name } : {}),
|
|
52
|
+
baseURL,
|
|
53
|
+
apiKey,
|
|
54
|
+
model,
|
|
55
|
+
contextWindow,
|
|
56
|
+
maxTokens: positiveInt(process.env.MINIMAL_AGENT_MAX_TOKENS, defaultMaxTokens(contextWindow)),
|
|
57
|
+
enabled: true,
|
|
58
|
+
createdAt: 0,
|
|
59
|
+
updatedAt: 0,
|
|
60
|
+
};
|
|
61
|
+
return {
|
|
62
|
+
version: 4,
|
|
63
|
+
models: [envModel],
|
|
64
|
+
baseModelId: envModel.id,
|
|
65
|
+
...(compactRatio > 0 && compactRatio <= 1 ? { compactRatio } : {}),
|
|
66
|
+
subagents: {
|
|
67
|
+
...DEFAULT_SUBAGENTS_CONFIG,
|
|
68
|
+
maxCallsPerTurn: positiveInt(process.env.AGENT_TOOL_MAX_CALLS_PER_TURN, 6),
|
|
69
|
+
maxConcurrent: positiveInt(process.env.AGENT_TOOL_MAX_CONCURRENT, 3),
|
|
70
|
+
timeoutMs: positiveInt(process.env.AGENT_TOOL_TIMEOUT_MS, 1_800_000),
|
|
71
|
+
exploreMaxTurns: positiveInt(process.env.AGENT_TOOL_EXPLORE_MAX_TURNS, 50),
|
|
72
|
+
generalMaxTurns: positiveInt(process.env.AGENT_TOOL_GENERAL_MAX_TURNS, 100),
|
|
73
|
+
},
|
|
74
|
+
browser: DEFAULT_BROWSER_CONFIG,
|
|
75
|
+
tools,
|
|
76
|
+
savedAt: 0,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
function resolveProvider(entry, compactRatio) {
|
|
80
|
+
return {
|
|
81
|
+
name: entry.displayName ?? entry.model,
|
|
82
|
+
baseURL: entry.baseURL,
|
|
83
|
+
apiKey: entry.apiKey,
|
|
84
|
+
model: entry.model,
|
|
85
|
+
contextWindow: entry.contextWindow,
|
|
86
|
+
maxTokens: entry.maxTokens ?? defaultMaxTokens(entry.contextWindow),
|
|
87
|
+
apiFormat: entry.apiFormat,
|
|
88
|
+
...(compactRatio !== undefined ? { compactRatio } : {}),
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
function mergeRecord(base, patch) {
|
|
92
|
+
const out = { ...(base ?? {}) };
|
|
93
|
+
for (const [key, value] of Object.entries(patch)) {
|
|
94
|
+
if (value && typeof value === 'object' && !Array.isArray(value)) {
|
|
95
|
+
out[key] = mergeRecord(out[key] && typeof out[key] === 'object' && !Array.isArray(out[key]) ? out[key] : undefined, value);
|
|
96
|
+
}
|
|
97
|
+
else if (value !== undefined)
|
|
98
|
+
out[key] = value;
|
|
99
|
+
}
|
|
100
|
+
return out;
|
|
101
|
+
}
|
|
102
|
+
function hasEntries(value) {
|
|
103
|
+
return !!value && Object.keys(value).length > 0;
|
|
104
|
+
}
|
|
105
|
+
function applyModelsPatch(models, ops) {
|
|
106
|
+
let out = [...models];
|
|
107
|
+
const now = Date.now();
|
|
108
|
+
for (const item of ops) {
|
|
109
|
+
if (item.op === 'add') {
|
|
110
|
+
const m = item.model;
|
|
111
|
+
const apiFormat = m.apiFormat === 'anthropic' ? 'anthropic' : 'openai';
|
|
112
|
+
const baseURL = m.baseURL?.trim();
|
|
113
|
+
const modelId = m.model?.trim();
|
|
114
|
+
const apiKey = m.apiKey?.trim();
|
|
115
|
+
if (!baseURL || !modelId || !apiKey)
|
|
116
|
+
throw new Error('模型缺少 Base URL、模型 ID 或 API Key');
|
|
117
|
+
out.push({
|
|
118
|
+
id: randomUUID(),
|
|
119
|
+
apiFormat,
|
|
120
|
+
...(m.displayName?.trim() ? { displayName: m.displayName.trim() } : {}),
|
|
121
|
+
baseURL,
|
|
122
|
+
apiKey,
|
|
123
|
+
model: modelId,
|
|
124
|
+
contextWindow: m.contextWindow && m.contextWindow > 0 ? m.contextWindow : DEFAULT_CONTEXT_WINDOW,
|
|
125
|
+
...(m.maxTokens ? { maxTokens: m.maxTokens } : {}),
|
|
126
|
+
supportsVision: m.supportsVision !== false,
|
|
127
|
+
enabled: m.enabled ?? true,
|
|
128
|
+
createdAt: now,
|
|
129
|
+
updatedAt: now,
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
else if (item.op === 'update') {
|
|
133
|
+
const index = out.findIndex((entry) => entry.id === item.id);
|
|
134
|
+
if (index === -1)
|
|
135
|
+
throw new Error('要修改的模型不存在(可能已被删除)');
|
|
136
|
+
const existing = out[index];
|
|
137
|
+
const patch = item.patch;
|
|
138
|
+
const next = {
|
|
139
|
+
...existing,
|
|
140
|
+
...(patch.apiFormat ? { apiFormat: patch.apiFormat === 'anthropic' ? 'anthropic' : 'openai' } : {}),
|
|
141
|
+
...(patch.displayName !== undefined ? { displayName: patch.displayName.trim() || undefined } : {}),
|
|
142
|
+
...(patch.baseURL?.trim() ? { baseURL: patch.baseURL.trim() } : {}),
|
|
143
|
+
...(patch.model?.trim() ? { model: patch.model.trim() } : {}),
|
|
144
|
+
...(patch.contextWindow ? { contextWindow: patch.contextWindow } : {}),
|
|
145
|
+
...(patch.maxTokens !== undefined ? { maxTokens: patch.maxTokens || undefined } : {}),
|
|
146
|
+
...(patch.supportsVision !== undefined ? { supportsVision: patch.supportsVision !== false } : {}),
|
|
147
|
+
...(patch.enabled !== undefined ? { enabled: patch.enabled } : {}),
|
|
148
|
+
updatedAt: now,
|
|
149
|
+
};
|
|
150
|
+
if (patch.apiKey?.trim())
|
|
151
|
+
next.apiKey = patch.apiKey.trim();
|
|
152
|
+
if (patch.clearApiKey)
|
|
153
|
+
next.apiKey = '';
|
|
154
|
+
out[index] = next;
|
|
155
|
+
}
|
|
156
|
+
else {
|
|
157
|
+
out = out.filter((entry) => entry.id !== item.id);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
return out;
|
|
161
|
+
}
|
|
162
|
+
function applyConfigPatch(base, patch) {
|
|
163
|
+
const wasEmpty = (base?.models.length ?? 0) === 0 && (base?.baseModelId ?? null) === null;
|
|
164
|
+
let models = base?.models ?? [];
|
|
165
|
+
let baseModelId = base?.baseModelId ?? null;
|
|
166
|
+
if (patch.models?.length)
|
|
167
|
+
models = applyModelsPatch(models, patch.models);
|
|
168
|
+
// 全新安装加第一个模型时自动设为基座——否则用户加完模型还是没法聊天。
|
|
169
|
+
if (wasEmpty && baseModelId === null && models.length > 0)
|
|
170
|
+
baseModelId = models[0].id;
|
|
171
|
+
if (patch.baseModelId !== undefined) {
|
|
172
|
+
if (patch.baseModelId !== null && !models.some((entry) => entry.id === patch.baseModelId)) {
|
|
173
|
+
throw new Error('要设为基座的模型不存在');
|
|
174
|
+
}
|
|
175
|
+
baseModelId = patch.baseModelId;
|
|
176
|
+
}
|
|
177
|
+
// 不变量:禁止删除/禁用当前基座模型指向的条目(先设别的为基座)。
|
|
178
|
+
if (baseModelId !== null) {
|
|
179
|
+
const baseEntry = models.find((entry) => entry.id === baseModelId);
|
|
180
|
+
if (!baseEntry)
|
|
181
|
+
throw new Error('当前基座模型已被删除,请先设置其他模型为基座');
|
|
182
|
+
if (baseEntry.enabled === false)
|
|
183
|
+
throw new Error('不能禁用当前基座模型,请先设置其他模型为基座');
|
|
184
|
+
}
|
|
185
|
+
let advisor = base?.advisor;
|
|
186
|
+
if (patch.advisor) {
|
|
187
|
+
if (patch.advisor.clear) {
|
|
188
|
+
advisor = undefined;
|
|
189
|
+
}
|
|
190
|
+
else {
|
|
191
|
+
advisor = {
|
|
192
|
+
enabled: patch.advisor.enabled ?? advisor?.enabled ?? true,
|
|
193
|
+
mode: patch.advisor.mode ?? advisor?.mode ?? 'auto',
|
|
194
|
+
modelId: patch.advisor.modelId !== undefined ? patch.advisor.modelId : (advisor?.modelId ?? null),
|
|
195
|
+
contextPolicy: 'full-relevant',
|
|
196
|
+
shareSensitive: patch.advisor.shareSensitive ?? advisor?.shareSensitive ?? false,
|
|
197
|
+
inputTokenBudget: Math.min(128_000, Math.max(8_000, patch.advisor.inputTokenBudget ?? advisor?.inputTokenBudget ?? 128_000)),
|
|
198
|
+
timeoutMs: patch.advisor.timeoutMs ?? advisor?.timeoutMs ?? 180_000,
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
// 顾问引用的模型若已被删除或禁用,级联置空(软失联——解析层已把 null/悬空 id 当
|
|
203
|
+
// 「未配置」优雅处理,不留死链接;这与基座模型的硬阻止是刻意的不对称:顾问本来就是
|
|
204
|
+
// 可降级的第二意见,不像基座那样「没有就整个应用跑不起来」)。
|
|
205
|
+
if (advisor?.modelId) {
|
|
206
|
+
const referenced = models.find((entry) => entry.id === advisor.modelId);
|
|
207
|
+
if (!referenced || referenced.enabled === false)
|
|
208
|
+
advisor = { ...advisor, modelId: null };
|
|
209
|
+
}
|
|
210
|
+
const compactRatio = patch.compactRatio !== undefined
|
|
211
|
+
? (patch.compactRatio > 0 && patch.compactRatio <= 1 ? patch.compactRatio : undefined)
|
|
212
|
+
: base?.compactRatio;
|
|
213
|
+
const tools = { ...(base?.tools ?? {}) };
|
|
214
|
+
const toolPatch = patch.tools;
|
|
215
|
+
if (toolPatch?.webSearch) {
|
|
216
|
+
if (toolPatch.webSearch.clear)
|
|
217
|
+
delete tools.webSearch;
|
|
218
|
+
else
|
|
219
|
+
tools.webSearch = mergeRecord(tools.webSearch, { provider: 'tavily', ...toolPatch.webSearch });
|
|
220
|
+
}
|
|
221
|
+
if (toolPatch?.imageGeneration) {
|
|
222
|
+
if (toolPatch.imageGeneration.clear)
|
|
223
|
+
delete tools.imageGeneration;
|
|
224
|
+
else {
|
|
225
|
+
const next = mergeRecord(tools.imageGeneration, toolPatch.imageGeneration);
|
|
226
|
+
if (toolPatch.imageGeneration.clearApiKey)
|
|
227
|
+
next.apiKey = '';
|
|
228
|
+
delete next.clear;
|
|
229
|
+
delete next.clearApiKey;
|
|
230
|
+
if (!next.baseURL || !next.apiKey || !next.model)
|
|
231
|
+
throw new Error('图片生成缺少 Base URL、API Key 或模型名称');
|
|
232
|
+
tools.imageGeneration = next;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
if (toolPatch?.openRouter) {
|
|
236
|
+
if (toolPatch.openRouter.clear)
|
|
237
|
+
delete tools.openRouter;
|
|
238
|
+
else
|
|
239
|
+
tools.openRouter = mergeRecord(tools.openRouter, toolPatch.openRouter);
|
|
240
|
+
}
|
|
241
|
+
if (toolPatch?.textToSpeech) {
|
|
242
|
+
if (toolPatch.textToSpeech.clear)
|
|
243
|
+
delete tools.textToSpeech;
|
|
244
|
+
else {
|
|
245
|
+
const next = mergeRecord(tools.textToSpeech, { provider: 'minimax', ...toolPatch.textToSpeech });
|
|
246
|
+
if (toolPatch.textToSpeech.clearApiKey)
|
|
247
|
+
next.apiKey = '';
|
|
248
|
+
delete next.clear;
|
|
249
|
+
delete next.clearApiKey;
|
|
250
|
+
if (!next.apiKey || !next.groupId)
|
|
251
|
+
throw new Error('MiniMax 语音缺少 API Key 或 Group ID');
|
|
252
|
+
tools.textToSpeech = next;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
const subagents = { ...(base?.subagents ?? DEFAULT_SUBAGENTS_CONFIG), ...(patch.subagents ?? {}) };
|
|
256
|
+
const browser = { ...(base?.browser ?? DEFAULT_BROWSER_CONFIG), ...(patch.browser ?? {}), profileId: 'default' };
|
|
257
|
+
return { models, baseModelId, ...(compactRatio !== undefined ? { compactRatio } : {}), advisor, subagents, browser, tools, drafts: base?.drafts };
|
|
258
|
+
}
|
|
259
|
+
export class ConfigService {
|
|
260
|
+
snapshot = { source: 'none', readOnly: false, config: null, revision: 0 };
|
|
261
|
+
get revision() { return this.snapshot.revision; }
|
|
262
|
+
async reload() {
|
|
263
|
+
const envOnly = process.env.MINIMAL_AGENT_CONFIG_MODE === 'env';
|
|
264
|
+
const json = envOnly ? null : await readMinimalAgentConfig();
|
|
265
|
+
const env = configFromEnv();
|
|
266
|
+
const next = {
|
|
267
|
+
source: json ? 'json' : env ? 'environment' : 'none',
|
|
268
|
+
readOnly: envOnly,
|
|
269
|
+
config: json ?? env,
|
|
270
|
+
};
|
|
271
|
+
const changed = this.snapshot.revision === 0
|
|
272
|
+
|| this.snapshot.source !== next.source
|
|
273
|
+
|| this.snapshot.readOnly !== next.readOnly
|
|
274
|
+
|| JSON.stringify(this.snapshot.config) !== JSON.stringify(next.config);
|
|
275
|
+
this.snapshot = { ...next, revision: changed ? this.snapshot.revision + 1 : this.snapshot.revision };
|
|
276
|
+
return this.getSnapshot();
|
|
277
|
+
}
|
|
278
|
+
async getSnapshot() {
|
|
279
|
+
if (this.snapshot.revision === 0)
|
|
280
|
+
await this.reload();
|
|
281
|
+
return structuredClone(this.snapshot);
|
|
282
|
+
}
|
|
283
|
+
async provider() {
|
|
284
|
+
const { config } = await this.getSnapshot();
|
|
285
|
+
const entry = config?.models.find((m) => m.id === config.baseModelId);
|
|
286
|
+
if (!entry?.apiKey)
|
|
287
|
+
return null;
|
|
288
|
+
return resolveProvider(entry, config?.compactRatio);
|
|
289
|
+
}
|
|
290
|
+
async providerForModel(modelId) {
|
|
291
|
+
if (!modelId)
|
|
292
|
+
return this.provider();
|
|
293
|
+
const { config } = await this.getSnapshot();
|
|
294
|
+
const entry = config?.models.find((m) => m.id === modelId);
|
|
295
|
+
if (!entry?.apiKey)
|
|
296
|
+
return null;
|
|
297
|
+
return resolveProvider(entry, config?.compactRatio);
|
|
298
|
+
}
|
|
299
|
+
/** 带 apiKey 的原始列表——仅服务端内部消费(桌面端 store.ts 的 readConfig() 会脱敏成 ModelView)。 */
|
|
300
|
+
async models() {
|
|
301
|
+
return (await this.getSnapshot()).config?.models ?? [];
|
|
302
|
+
}
|
|
303
|
+
async model(id) {
|
|
304
|
+
return (await this.getSnapshot()).config?.models.find((entry) => entry.id === id) ?? null;
|
|
305
|
+
}
|
|
306
|
+
/**
|
|
307
|
+
* 保留「未启用但已配置」和「完全未配置」两种返回值的区分——advisor.ts 的两条不同报错
|
|
308
|
+
* 文案依赖这个区分,所以只按 modelId+对应条目 apiKey 是否非空判定可解析性,不掺 .enabled。
|
|
309
|
+
*
|
|
310
|
+
* `supportsVision` 现在是模型条目的属性(不再是顾问自己的行为字段),这里从所引用的
|
|
311
|
+
* ModelConfig 镜像到返回对象顶层,让 advisor.ts 的 `advisor.supportsVision` 读取方式
|
|
312
|
+
* 保持不变——它是解析后的便利视图,不是存储形状本身。
|
|
313
|
+
*/
|
|
314
|
+
async advisor() {
|
|
315
|
+
// fresh reload(不用 getSnapshot 的缓存)——CLI/TUI/Desktop/Cron 共享同一份 config.json,
|
|
316
|
+
// 跨进程时缓存会读到过期状态(如 A 进程 /advisor off 后 B 进程仍判定已启用)。这条不变量
|
|
317
|
+
// 由 test/configService.test.ts 的 "advisor() always does a fresh reload" 用例锁定。
|
|
318
|
+
const { config } = await this.reload();
|
|
319
|
+
const advisor = config?.advisor;
|
|
320
|
+
if (!advisor?.modelId)
|
|
321
|
+
return null;
|
|
322
|
+
const entry = config.models.find((m) => m.id === advisor.modelId);
|
|
323
|
+
if (!entry?.apiKey)
|
|
324
|
+
return null;
|
|
325
|
+
return { ...advisor, provider: resolveProvider(entry), supportsVision: entry.supportsVision !== false };
|
|
326
|
+
}
|
|
327
|
+
async subagents() {
|
|
328
|
+
return (await this.reload()).config?.subagents ?? DEFAULT_SUBAGENTS_CONFIG;
|
|
329
|
+
}
|
|
330
|
+
async browser() {
|
|
331
|
+
return (await this.reload()).config?.browser ?? DEFAULT_BROWSER_CONFIG;
|
|
332
|
+
}
|
|
333
|
+
async tool(key) {
|
|
334
|
+
return (await this.reload()).config?.tools[key];
|
|
335
|
+
}
|
|
336
|
+
async toolStatus() {
|
|
337
|
+
const tools = (await this.reload()).config?.tools ?? {};
|
|
338
|
+
return {
|
|
339
|
+
webSearch: { state: tools.webSearch ? 'configured' : 'unconfigured', configured: !!tools.webSearch },
|
|
340
|
+
imageGeneration: { state: tools.imageGeneration ? 'configured' : 'unconfigured', configured: !!tools.imageGeneration },
|
|
341
|
+
openRouter: { state: tools.openRouter ? 'configured' : 'unconfigured', configured: !!tools.openRouter },
|
|
342
|
+
textToSpeech: { state: tools.textToSpeech ? 'configured' : 'unconfigured', configured: !!tools.textToSpeech },
|
|
343
|
+
};
|
|
344
|
+
}
|
|
345
|
+
async savePatch(patch) {
|
|
346
|
+
const current = await this.getSnapshot();
|
|
347
|
+
if (current.readOnly)
|
|
348
|
+
throw new Error('当前使用环境变量只读配置,不能从设置页修改');
|
|
349
|
+
await updateMinimalAgentConfig((latest) => applyConfigPatch(latest ?? current.config, patch));
|
|
350
|
+
return this.reload();
|
|
351
|
+
}
|
|
352
|
+
/** 现在只服务 tools 分区的实时录入表单(imageGeneration/textToSpeech 多字段、可能中途非法)。 */
|
|
353
|
+
async autosave(section, patch) {
|
|
354
|
+
void section;
|
|
355
|
+
const current = await this.getSnapshot();
|
|
356
|
+
if (current.readOnly)
|
|
357
|
+
throw new Error('当前使用环境变量只读配置,不能从设置页修改');
|
|
358
|
+
if (!current.config)
|
|
359
|
+
throw new Error('请先添加至少一个模型');
|
|
360
|
+
let state = 'applied';
|
|
361
|
+
const draftFields = [];
|
|
362
|
+
await updateMinimalAgentConfig((latest) => {
|
|
363
|
+
let active = latest ?? current.config;
|
|
364
|
+
if (patch.subagents)
|
|
365
|
+
active = { version: 4, ...applyConfigPatch(active, { subagents: patch.subagents }), savedAt: active.savedAt };
|
|
366
|
+
const drafts = structuredClone(active.drafts ?? {});
|
|
367
|
+
for (const key of Object.keys(patch.tools ?? {})) {
|
|
368
|
+
const rawPiece = patch.tools[key];
|
|
369
|
+
if (!rawPiece)
|
|
370
|
+
continue;
|
|
371
|
+
const previous = (drafts.tools ?? {})[key];
|
|
372
|
+
const merged = mergeRecord(previous, rawPiece);
|
|
373
|
+
try {
|
|
374
|
+
active = { version: 4, ...applyConfigPatch(active, { tools: { [key]: merged } }), savedAt: active.savedAt };
|
|
375
|
+
delete drafts.tools?.[key];
|
|
376
|
+
}
|
|
377
|
+
catch {
|
|
378
|
+
state = 'draft';
|
|
379
|
+
draftFields.push(key);
|
|
380
|
+
(drafts.tools ??= {})[key] = merged;
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
if (!hasEntries(drafts.tools))
|
|
384
|
+
delete drafts.tools;
|
|
385
|
+
return {
|
|
386
|
+
models: active.models, baseModelId: active.baseModelId,
|
|
387
|
+
...(active.compactRatio !== undefined ? { compactRatio: active.compactRatio } : {}),
|
|
388
|
+
advisor: active.advisor, subagents: active.subagents, browser: active.browser, tools: active.tools,
|
|
389
|
+
...(hasEntries(drafts) ? { drafts } : {}),
|
|
390
|
+
};
|
|
391
|
+
});
|
|
392
|
+
return { snapshot: await this.reload(), state, draftFields };
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
export const configService = new ConfigService();
|
|
396
|
+
export const SENSITIVE_ENV_KEYS = new Set([
|
|
397
|
+
'MINIMAL_AGENT_API_KEY', 'TAVILY_API_KEY', 'OPENAI_IMAGE_API_KEY',
|
|
398
|
+
'OPENROUTER_API_KEY', 'MINIMAX_API_KEY', 'MINIMAX_GROUP_ID',
|
|
399
|
+
]);
|
|
400
|
+
/** Child processes inherit normal runtime variables, never model or tool credentials. */
|
|
401
|
+
export function sanitizeCredentialEnv(source = process.env) {
|
|
402
|
+
const env = { ...source };
|
|
403
|
+
for (const key of SENSITIVE_ENV_KEYS)
|
|
404
|
+
delete env[key];
|
|
405
|
+
return env;
|
|
406
|
+
}
|
|
407
|
+
export async function trustedToolEnv(kind, service = configService) {
|
|
408
|
+
const env = sanitizeCredentialEnv();
|
|
409
|
+
if (kind === 'imageGeneration') {
|
|
410
|
+
let cfg = await service.tool('imageGeneration');
|
|
411
|
+
if (!cfg) {
|
|
412
|
+
const openRouter = await service.tool('openRouter');
|
|
413
|
+
if (openRouter)
|
|
414
|
+
cfg = {
|
|
415
|
+
provider: 'openrouter',
|
|
416
|
+
baseURL: 'https://openrouter.ai/api/v1',
|
|
417
|
+
apiKey: openRouter.apiKey,
|
|
418
|
+
model: 'google/gemini-3-pro-image-preview',
|
|
419
|
+
protocol: 'chat',
|
|
420
|
+
};
|
|
421
|
+
}
|
|
422
|
+
if (cfg) {
|
|
423
|
+
env.OPENAI_IMAGE_PROVIDER = cfg.provider;
|
|
424
|
+
env.OPENAI_IMAGE_BASE_URL = cfg.baseURL;
|
|
425
|
+
env.OPENAI_IMAGE_API_KEY = cfg.apiKey;
|
|
426
|
+
env.OPENAI_IMAGE_MODEL = cfg.model;
|
|
427
|
+
if (cfg.protocol && cfg.protocol !== 'auto')
|
|
428
|
+
env.OPENAI_IMAGE_PROTOCOL = cfg.protocol;
|
|
429
|
+
else
|
|
430
|
+
delete env.OPENAI_IMAGE_PROTOCOL;
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
else if (kind === 'openRouter') {
|
|
434
|
+
const cfg = await service.tool('openRouter');
|
|
435
|
+
if (cfg)
|
|
436
|
+
env.OPENROUTER_API_KEY = cfg.apiKey;
|
|
437
|
+
}
|
|
438
|
+
else {
|
|
439
|
+
const cfg = await service.tool('textToSpeech');
|
|
440
|
+
if (cfg) {
|
|
441
|
+
env.MINIMAX_API_KEY = cfg.apiKey;
|
|
442
|
+
env.MINIMAX_GROUP_ID = cfg.groupId;
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
return env;
|
|
446
|
+
}
|
package/src/config.js
CHANGED
|
@@ -5,17 +5,33 @@
|
|
|
5
5
|
* 只从环境变量读取,集中管理所有配置。
|
|
6
6
|
*
|
|
7
7
|
* 必需的环境变量:
|
|
8
|
-
* MINIMAL_AGENT_BASE_URL - API
|
|
8
|
+
* MINIMAL_AGENT_BASE_URL - API 完整端点地址,如 https://api.minimax.chat/v1/chat/completions
|
|
9
9
|
* MINIMAL_AGENT_API_KEY - API key
|
|
10
10
|
* MINIMAL_AGENT_MODEL - 模型名,如 MiniMax-M2.7
|
|
11
11
|
*
|
|
12
12
|
* 可选的环境变量:
|
|
13
13
|
* MINIMAL_AGENT_PROVIDER - 友好名称,默认 "env"
|
|
14
14
|
* MINIMAL_AGENT_CONTEXT_WINDOW - 上下文窗口,默认 128000
|
|
15
|
+
* MINIMAL_AGENT_MAX_TOKENS - 单次输出 token 上限,默认 16384(弱 provider 默认偏小会截断长输出)
|
|
15
16
|
* ============================================================
|
|
16
17
|
*/
|
|
17
|
-
import {
|
|
18
|
+
import { configService } from './config/configService.js';
|
|
18
19
|
const DEFAULT_CONTEXT_WINDOW = 128_000;
|
|
20
|
+
/**
|
|
21
|
+
* 单次输出 token 上限的默认值。
|
|
22
|
+
*
|
|
23
|
+
* 16384:实测火山豆包接受、足够一次性写完整篇 PLAN.md(实测 ~6.5k token),又远低于
|
|
24
|
+
* 豆包 131072 的宽容上限。不发 max_tokens 的话豆包默认仅 4096,长输出会被硬截断。
|
|
25
|
+
* 个别 provider 若拒绝该值,client 会自动去掉 max_tokens 重发降级。
|
|
26
|
+
*/
|
|
27
|
+
const DEFAULT_MAX_TOKENS = 16_384;
|
|
28
|
+
/** 解析正整数环境变量(NaN/≤0 → undefined,由调用方决定默认值)。 */
|
|
29
|
+
function parsePositiveInt(raw) {
|
|
30
|
+
if (!raw)
|
|
31
|
+
return undefined;
|
|
32
|
+
const n = parseInt(raw, 10);
|
|
33
|
+
return !Number.isNaN(n) && n > 0 ? n : undefined;
|
|
34
|
+
}
|
|
19
35
|
/**
|
|
20
36
|
* 解析压缩阈值比例(0~1 小数)。
|
|
21
37
|
* 非法值(NaN、0、负数、>1)返回 undefined,由调用方决定是否用默认值 0.85。
|
|
@@ -60,78 +76,22 @@ export async function loadProvider() {
|
|
|
60
76
|
}
|
|
61
77
|
}
|
|
62
78
|
const compactRatio = parseCompactRatio(process.env.MINIMAL_AGENT_COMPACT_RATIO);
|
|
79
|
+
const maxTokens = parsePositiveInt(process.env.MINIMAL_AGENT_MAX_TOKENS) ?? DEFAULT_MAX_TOKENS;
|
|
63
80
|
return {
|
|
64
81
|
name: process.env.MINIMAL_AGENT_PROVIDER ?? 'env',
|
|
65
82
|
baseURL,
|
|
66
83
|
apiKey,
|
|
67
84
|
model,
|
|
68
85
|
contextWindow,
|
|
86
|
+
maxTokens,
|
|
69
87
|
...(compactRatio !== undefined ? { compactRatio } : {}),
|
|
70
88
|
};
|
|
71
89
|
}
|
|
72
|
-
/**
|
|
73
|
-
* 层级加载:env 优先(模式 A),不全则回退到 ~/.minimal-agent/config.json(模式 B)。
|
|
74
|
-
* 都没有时返回 null —— 调用方决定是触发向导还是直接退出。
|
|
75
|
-
*
|
|
76
|
-
* 与 loadProvider() 的区别:
|
|
77
|
-
* - loadProvider() 只读 env,全缺则抛错(给 -p 模式用,明确告知"先做配置")
|
|
78
|
-
* - 本函数静默 fallback 到 JSON,全缺则返回 null(给 TUI 模式用,可触发向导)
|
|
79
|
-
*
|
|
80
|
-
* env 字段优先级是逐项的:只要 env 里有 BASE_URL 就用 env 的 BASE_URL,
|
|
81
|
-
* 其它字段从 JSON 补;这样用户可以临时用 env 覆盖单个字段做调试。
|
|
82
|
-
*/
|
|
90
|
+
/** Shared CLI/Desktop configuration: JSON first, environment only as bootstrap or env mode. */
|
|
83
91
|
export async function loadProviderLayered() {
|
|
84
|
-
|
|
85
|
-
const envApiKey = process.env.MINIMAL_AGENT_API_KEY;
|
|
86
|
-
const envModel = process.env.MINIMAL_AGENT_MODEL;
|
|
87
|
-
const envName = process.env.MINIMAL_AGENT_PROVIDER;
|
|
88
|
-
const envContextRaw = process.env.MINIMAL_AGENT_CONTEXT_WINDOW;
|
|
89
|
-
let saved = null;
|
|
90
|
-
if (!envBaseURL || !envApiKey || !envModel) {
|
|
91
|
-
saved = await readSavedConfig();
|
|
92
|
-
}
|
|
93
|
-
const baseURL = envBaseURL ?? saved?.baseURL;
|
|
94
|
-
const apiKey = envApiKey ?? saved?.apiKey;
|
|
95
|
-
const model = envModel ?? saved?.model;
|
|
96
|
-
if (!baseURL || !apiKey || !model)
|
|
97
|
-
return null;
|
|
98
|
-
let contextWindow = DEFAULT_CONTEXT_WINDOW;
|
|
99
|
-
if (envContextRaw) {
|
|
100
|
-
const n = parseInt(envContextRaw, 10);
|
|
101
|
-
if (!Number.isNaN(n) && n > 0)
|
|
102
|
-
contextWindow = n;
|
|
103
|
-
}
|
|
104
|
-
else if (saved?.contextWindow) {
|
|
105
|
-
contextWindow = saved.contextWindow;
|
|
106
|
-
}
|
|
107
|
-
// compactRatio:env 优先(测试时临时 MINIMAL_AGENT_COMPACT_RATIO=0.2 即可),否则取 saved。
|
|
108
|
-
const envRatio = parseCompactRatio(process.env.MINIMAL_AGENT_COMPACT_RATIO);
|
|
109
|
-
const compactRatio = envRatio ??
|
|
110
|
-
(typeof saved?.compactRatio === 'number' ? saved.compactRatio : undefined);
|
|
111
|
-
return {
|
|
112
|
-
name: envName ?? saved?.provider ?? 'env',
|
|
113
|
-
baseURL,
|
|
114
|
-
apiKey,
|
|
115
|
-
model,
|
|
116
|
-
contextWindow,
|
|
117
|
-
...(compactRatio !== undefined ? { compactRatio } : {}),
|
|
118
|
-
};
|
|
92
|
+
return configService.provider();
|
|
119
93
|
}
|
|
120
|
-
/**
|
|
121
|
-
* 把 saved config 里的工具 key 注入 process.env(env 已设则不覆盖)。
|
|
122
|
-
*
|
|
123
|
-
* 解决 npm 全局安装场景:`node dist/main.js` 不会自动加载 .env,
|
|
124
|
-
* Tavily 之类的工具 key 只能从 ~/.minimal-agent/config.json 取出再写回 env,
|
|
125
|
-
* websearch.ts 才能在 call() 里读到。
|
|
126
|
-
*
|
|
127
|
-
* 应在 main() 最早期调用(initWorkingDir 之后即可),保证 ALL_TOOLS
|
|
128
|
-
* 任何 call() 触发前 env 已就位。
|
|
129
|
-
*/
|
|
94
|
+
/** @deprecated Compatibility hook. Trusted tools now read ConfigService directly. */
|
|
130
95
|
export async function applyToolKeysToEnv() {
|
|
131
|
-
|
|
132
|
-
if (!saved)
|
|
133
|
-
return;
|
|
134
|
-
if (!process.env.TAVILY_API_KEY && saved.tavilyApiKey) {
|
|
135
|
-
process.env.TAVILY_API_KEY = saved.tavilyApiKey;
|
|
136
|
-
}
|
|
96
|
+
await configService.reload();
|
|
137
97
|
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { homedir } from 'node:os';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
/** 测试和嵌入宿主可覆盖 home 根,生产默认使用系统主目录。 */
|
|
4
|
+
export function agentHomeBase() {
|
|
5
|
+
return process.env.MINIMAL_AGENT_HOME || homedir();
|
|
6
|
+
}
|
|
7
|
+
export function agentStateDir() {
|
|
8
|
+
return join(agentHomeBase(), '.minimal-agent');
|
|
9
|
+
}
|
package/src/context/archive.js
CHANGED
|
@@ -26,6 +26,7 @@ import { mkdir, readFile, readdir, stat, writeFile } from 'node:fs/promises';
|
|
|
26
26
|
import { homedir } from 'node:os';
|
|
27
27
|
import { join } from 'node:path';
|
|
28
28
|
import { getWorkingDir } from '../bootstrap/workingDir.js';
|
|
29
|
+
import { contentToPlainText } from '../multimodal.js';
|
|
29
30
|
/** 解析 archive 根目录,支持 MINIMAL_AGENT_HOME 测试覆盖 */
|
|
30
31
|
function agentHome() {
|
|
31
32
|
return process.env.MINIMAL_AGENT_HOME ?? homedir();
|
|
@@ -51,6 +52,10 @@ export async function archiveCurrent(messages) {
|
|
|
51
52
|
await mkdir(dir, { recursive: true });
|
|
52
53
|
const id = new Date().toISOString().replace(/:/g, '-');
|
|
53
54
|
const path = join(dir, `${id}.json`);
|
|
55
|
+
// 写入完整 messages(含 system)—— restoreArchive 期待原样还原以做 roundtrip
|
|
56
|
+
// 跳过判定单独用 nonSystem(空对话不归档)—— 这是有意分层的:
|
|
57
|
+
// skip 判定 = "有没有用户/助手消息可归档"
|
|
58
|
+
// write 内容 = "保留完整上下文供 /restore 还原"
|
|
54
59
|
await writeFile(path, JSON.stringify({ updatedAt: Date.now(), messages }), 'utf8');
|
|
55
60
|
return id;
|
|
56
61
|
}
|
|
@@ -82,8 +87,8 @@ export async function listArchives() {
|
|
|
82
87
|
if (!Array.isArray(data.messages))
|
|
83
88
|
continue;
|
|
84
89
|
const firstUser = data.messages.find((m) => m.role === 'user');
|
|
85
|
-
const preview = firstUser
|
|
86
|
-
? firstUser.content.replace(/\s+/g, ' ').slice(0, 80)
|
|
90
|
+
const preview = firstUser
|
|
91
|
+
? contentToPlainText(firstUser.content).replace(/\s+/g, ' ').slice(0, 80)
|
|
87
92
|
: '';
|
|
88
93
|
entries.push({
|
|
89
94
|
id,
|
package/src/context/compact.js
CHANGED
|
@@ -299,7 +299,7 @@ export async function autoCompactIfNeeded(messages, provider, opts) {
|
|
|
299
299
|
return { messages, compacted: false, before, after: before };
|
|
300
300
|
}
|
|
301
301
|
// 真正要压缩了
|
|
302
|
-
const compactedMessages = await runCompaction(messages, provider);
|
|
302
|
+
const compactedMessages = await runCompaction(messages, provider, opts?.signal, opts?.chat ?? chat);
|
|
303
303
|
const after = countMessagesTokens(compactedMessages);
|
|
304
304
|
return { messages: compactedMessages, compacted: true, before, after };
|
|
305
305
|
}
|
|
@@ -309,9 +309,11 @@ export async function autoCompactIfNeeded(messages, provider, opts) {
|
|
|
309
309
|
* 与 autoCompactIfNeeded 共用同一段 runCompaction 逻辑;区别仅在于此函数
|
|
310
310
|
* 永远跑压缩、不返回 `compacted` 字段(由调用方决定如何反馈)。
|
|
311
311
|
*/
|
|
312
|
-
export async function forceCompact(messages, provider
|
|
312
|
+
export async function forceCompact(messages, provider, signal,
|
|
313
|
+
/** 注入 chat 实现(仅测试用;生产不传走默认实现)。 */
|
|
314
|
+
chatFn = chat) {
|
|
313
315
|
const before = countMessagesTokens(messages);
|
|
314
|
-
const compactedMessages = await runCompaction(messages, provider);
|
|
316
|
+
const compactedMessages = await runCompaction(messages, provider, signal, chatFn);
|
|
315
317
|
const after = countMessagesTokens(compactedMessages);
|
|
316
318
|
return { messages: compactedMessages, before, after };
|
|
317
319
|
}
|
|
@@ -324,7 +326,7 @@ export async function forceCompact(messages, provider) {
|
|
|
324
326
|
* ✅ 关键修复:使用 findTailWithCompleteToolChains() 替代简单的 slice(-N)
|
|
325
327
|
* 保证压缩后的消息结构符合 OpenAI 协议要求(tool 消息必须有对应的 tool_call 父消息)。
|
|
326
328
|
*/
|
|
327
|
-
async function runCompaction(messages, provider) {
|
|
329
|
+
async function runCompaction(messages, provider, signal, chatFn = chat) {
|
|
328
330
|
// 1. 找出原 system(保留它,避免 agent 失忆)
|
|
329
331
|
const systemMsg = messages.find((m) => m.role === 'system');
|
|
330
332
|
const nonSystem = messages.filter((m) => m.role !== 'system');
|
|
@@ -342,10 +344,11 @@ async function runCompaction(messages, provider) {
|
|
|
342
344
|
// 3. 调 LLM(用同一个 chat() 但不传工具,避免模型尝试调用工具)
|
|
343
345
|
let summary = '';
|
|
344
346
|
try {
|
|
345
|
-
for await (const ev of
|
|
347
|
+
for await (const ev of chatFn({
|
|
346
348
|
provider,
|
|
347
349
|
messages: compactRequest,
|
|
348
350
|
tools: [],
|
|
351
|
+
signal,
|
|
349
352
|
})) {
|
|
350
353
|
if (ev.type === 'text_delta')
|
|
351
354
|
summary += ev.delta;
|