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
package/src/ui/hooks/useChat.js
CHANGED
|
@@ -15,25 +15,30 @@
|
|
|
15
15
|
* ============================================================
|
|
16
16
|
*/
|
|
17
17
|
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
18
|
-
import {
|
|
18
|
+
import { stat } from 'node:fs/promises';
|
|
19
|
+
import { getWorkingDir, setWorkingDir } from '../../bootstrap/workingDir.js';
|
|
19
20
|
import { forceCompact } from '../../context/compact.js';
|
|
20
21
|
import { clearContext, loadContext, saveContext, } from '../../context/persistContext.js';
|
|
21
22
|
import { resetReactiveCompactState } from '../../context/reactiveCompact.js';
|
|
22
|
-
import {
|
|
23
|
+
import { acquireContextLock } from '../../context/contextLock.js';
|
|
24
|
+
import { getActiveSession, setActiveSession, updateSessionMetadata, } from '../../context/sessionRegistry.js';
|
|
23
25
|
import { resolveSessionFile, sessionFileFor, } from '../../context/sessionPath.js';
|
|
24
26
|
import { buildFullSystemPrompt } from '../../prompts/system.js';
|
|
25
|
-
import { ALL_TOOLS } from '../../tools/index.js';
|
|
26
|
-
import {
|
|
27
|
+
import { ALL_TOOLS, getToolByName } from '../../tools/index.js';
|
|
28
|
+
import { createInputQueue } from '../../messageQueue.js';
|
|
29
|
+
import { createAskCoordinator, setAskHandler, } from '../../askBridge.js';
|
|
30
|
+
import { createAskSlot } from './askSlot.js';
|
|
31
|
+
import { figures } from '../figures.js';
|
|
27
32
|
import { clearFileState } from '../../tools/shared/fileState.js';
|
|
28
33
|
import { runWithPlugins } from '../../plugins/pluginRunner.js';
|
|
29
34
|
import { dlog } from '../../utils/debugLog.js';
|
|
30
35
|
/**
|
|
31
36
|
* reasoningPreview 环形 buffer 容量(字符)。
|
|
32
37
|
* LiveArea 现在多行显示思维链尾部(最近 N 行),需要喂饱足够文本;
|
|
33
|
-
*
|
|
38
|
+
* 2400 字符约 30~40 行中文,LiveArea wrapToDisplayLines 后再取尾部最近 N 行。
|
|
34
39
|
* 上界由 LiveArea 的 contentMaxLines 兜底,这里只管"留够素材"。
|
|
35
40
|
*/
|
|
36
|
-
const REASONING_PREVIEW_MAX_CHARS =
|
|
41
|
+
const REASONING_PREVIEW_MAX_CHARS = 2400;
|
|
37
42
|
/**
|
|
38
43
|
* streamingText 环形 buffer 容量(字符)—— 仅用于 live 区"答案流式预览"。
|
|
39
44
|
*
|
|
@@ -108,15 +113,37 @@ export function useChat(args) {
|
|
|
108
113
|
const historyRef = useRef(args.initialHistory.slice());
|
|
109
114
|
const [version, setVersion] = useState(0);
|
|
110
115
|
const bump = useCallback(() => setVersion((v) => v + 1), []);
|
|
116
|
+
// provider 用 ref 存储,支持运行时切换(/model 命令切换基座模型)
|
|
117
|
+
const providerRef = useRef(args.provider);
|
|
111
118
|
const [streamingText, setStreamingText] = useState('');
|
|
112
119
|
const [isLoading, setIsLoading] = useState(false);
|
|
113
120
|
const [error, setError] = useState(null);
|
|
121
|
+
const [transientError, setTransientError] = useState(null);
|
|
114
122
|
const [interrupted, setInterrupted] = useState(false);
|
|
123
|
+
/**
|
|
124
|
+
* 暂态错误状态机的跨事件可变上下文(用 ref,不需要为它单独重渲染)。
|
|
125
|
+
* - activeStepId:当前正在执行的 workflow step id(workflow_step_start 记下,
|
|
126
|
+
* 该 step 的 workflow_step_end 清除);null = 不在任何 step 内。
|
|
127
|
+
* - pendingLlmError:该 step 执行中捕获到的最近一次 llm_error 文案(暂存,
|
|
128
|
+
* 等 step 结束才决定丢弃 or 提升为永久红字)。
|
|
129
|
+
* handleEvent 通过 setters.workflowCtx 同步读写这个对象(pure 逻辑,测试可注入 stub)。
|
|
130
|
+
*/
|
|
131
|
+
const workflowCtxRef = useRef({ activeStepId: null, pendingLlmError: null });
|
|
132
|
+
// 工作流步骤 id → 人话进度名(title)映射:start 记入、end/skipped 回查,
|
|
133
|
+
// 让 scrollback 三种进度行(▶/✓/⊘)都显示同一人话名而非 id。跨事件持 ref。
|
|
134
|
+
const stepTitlesRef = useRef(new Map());
|
|
115
135
|
const [pluginProgress, setPluginProgress] = useState(null);
|
|
116
136
|
// ---- 工作过程显示相关 state ----
|
|
117
137
|
const [compactGeneration, setCompactGeneration] = useState(0);
|
|
118
138
|
const [progressStage, setProgressStage] = useState('idle');
|
|
119
139
|
const [reasoningPreview, setReasoningPreview] = useState('');
|
|
140
|
+
/**
|
|
141
|
+
* 内核每轮 yield 的真实上下文用量(含 tools-schema 开销 + 真实 provider usage,
|
|
142
|
+
* 见 loop.ts 的 context_usage 事件)。收到过至少一次前 StatusLine 用它渲染,
|
|
143
|
+
* 比 useTokenUsage 的本地估算更准;未收到过(如刚启动、纯只读问答走了只读快速
|
|
144
|
+
* 路径)时该值仍是 null,StatusLine 降级回 useTokenUsage 估算。
|
|
145
|
+
*/
|
|
146
|
+
const [contextUsage, setContextUsage] = useState(null);
|
|
120
147
|
/**
|
|
121
148
|
* 工具并行执行:以 callId 为 key 保存所有运行中工具。
|
|
122
149
|
* 用 Map 是为了 O(1) 增删;对外 memo 物化成稳定数组(Map 迭代顺序 = 插入顺序,
|
|
@@ -125,12 +152,28 @@ export function useChat(args) {
|
|
|
125
152
|
const [runningToolsMap, setRunningToolsMap] = useState(new Map());
|
|
126
153
|
const runningTools = useMemo(() => Array.from(runningToolsMap.values()), [runningToolsMap]);
|
|
127
154
|
const abortRef = useRef(null);
|
|
155
|
+
const [inputQueue] = useState(() => createInputQueue());
|
|
156
|
+
const [queuedCount, setQueuedCount] = useState(0);
|
|
157
|
+
const [queuedItems, setQueuedItems] = useState([]);
|
|
158
|
+
const runningToolsRef = useRef(new Map());
|
|
159
|
+
useEffect(() => {
|
|
160
|
+
return inputQueue.subscribe(() => {
|
|
161
|
+
setQueuedCount(inputQueue.size());
|
|
162
|
+
setQueuedItems(inputQueue.getSnapshot());
|
|
163
|
+
});
|
|
164
|
+
}, [inputQueue]);
|
|
128
165
|
// ---- AskUserQuestion:执行中向用户提问 ----
|
|
129
166
|
// pendingAsk 非 null 时 App 渲染 <AskQuestion> 独占输入(不渲染 InputBox)。
|
|
130
|
-
//
|
|
131
|
-
//
|
|
167
|
+
// 单槽状态机(resolve/reject 持有、abort 归属校验、悬挂防护)抽在
|
|
168
|
+
// askSlot.ts(纯函数可单测);这里只桥接 React 状态。
|
|
132
169
|
const [pendingAsk, setPendingAsk] = useState(null);
|
|
133
|
-
|
|
170
|
+
// 提问来源:子任务提问时 kind:'subagent' + description → AskQuestion 显示徽标。
|
|
171
|
+
const [pendingAskSource, setPendingAskSource] = useState(null);
|
|
172
|
+
const [pendingAskQueueCount, setPendingAskQueueCount] = useState(0);
|
|
173
|
+
const [askSlot] = useState(() => createAskSlot((questions, source) => {
|
|
174
|
+
setPendingAsk(questions);
|
|
175
|
+
setPendingAskSource(questions ? source ?? null : null);
|
|
176
|
+
}));
|
|
134
177
|
const streamingFlush = useThrottledFlush({
|
|
135
178
|
delayMs: STREAM_FLUSH_MS,
|
|
136
179
|
maxChars: STREAM_FLUSH_MAX_CHARS,
|
|
@@ -171,6 +214,9 @@ export function useChat(args) {
|
|
|
171
214
|
setProgressStage('idle');
|
|
172
215
|
setReasoningPreview('');
|
|
173
216
|
setRunningToolsMap(new Map());
|
|
217
|
+
// 暂态错误是"过程级"提示,turn 结束 / 切换时一并清掉,不残留到下一轮
|
|
218
|
+
setTransientError(null);
|
|
219
|
+
workflowCtxRef.current = { activeStepId: null, pendingLlmError: null };
|
|
174
220
|
}, []);
|
|
175
221
|
/** turn 结束 / 中断 / 切换时一并取消所有节流 buffer 的 pending timer。 */
|
|
176
222
|
const cancelAllThrottles = useCallback(() => {
|
|
@@ -195,48 +241,26 @@ export function useChat(args) {
|
|
|
195
241
|
};
|
|
196
242
|
}, []);
|
|
197
243
|
// mount 时把「弹选择界面」的处理器注册到 askBridge;unmount 注销。
|
|
198
|
-
//
|
|
199
|
-
//
|
|
244
|
+
// 两层组合:createAskCoordinator(FIFO 队列——主循环与并行 general 子 Agent 都可能
|
|
245
|
+
// 提问,用户一次只看一个问题,其余排队;实例按 TUI 挂载创建,不同进程互不阻塞)
|
|
246
|
+
// 包住 askSlot.handler(呈现层单槽 + abort 归属校验——同轮多次提问共享 turn signal,
|
|
247
|
+
// 归属校验保证已答完提问的 abort 监听器不会清掉后来提问的槽,否则后者 promise
|
|
248
|
+
// 永不落定 → 整轮死挂,见 askSlot.ts 头注)。
|
|
200
249
|
useEffect(() => {
|
|
201
|
-
setAskHandler((
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
askResolverRef.current = { resolve, reject };
|
|
205
|
-
dlog('ui', 'ask handler INVOKED (等待用户答题)', {
|
|
206
|
-
n: questions.length,
|
|
207
|
-
headers: questions.map((q) => q.header),
|
|
208
|
-
});
|
|
209
|
-
setPendingAsk(questions);
|
|
210
|
-
const onAbort = () => {
|
|
211
|
-
if (!askResolverRef.current)
|
|
212
|
-
return;
|
|
213
|
-
askResolverRef.current = null;
|
|
214
|
-
setPendingAsk(null);
|
|
215
|
-
reject(new Error('已被用户中断'));
|
|
216
|
-
};
|
|
217
|
-
if (signal?.aborted) {
|
|
218
|
-
onAbort();
|
|
219
|
-
return;
|
|
220
|
-
}
|
|
221
|
-
signal?.addEventListener('abort', onAbort, { once: true });
|
|
222
|
-
});
|
|
223
|
-
});
|
|
250
|
+
setAskHandler(createAskCoordinator(askSlot.handler, (status) => {
|
|
251
|
+
setPendingAskQueueCount(status.queuedCount ?? 0);
|
|
252
|
+
}));
|
|
224
253
|
return () => setAskHandler(null);
|
|
225
|
-
}, []);
|
|
254
|
+
}, [askSlot]);
|
|
226
255
|
/** 用户答完当前提问:清 pending + resolve 工具侧 Promise,执行继续。 */
|
|
227
|
-
const answerAsk = useCallback((answers) =>
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
}, []);
|
|
234
|
-
//
|
|
235
|
-
// 启动时初始 history 已经由 main.tsx/Root 走默认路径加载完了;
|
|
236
|
-
// 如果注册表说 active 不是 default,理论上 main.tsx 应该按 active 加载——
|
|
237
|
-
// 但当前 main.tsx 用的是 getContextPath() 走 default。为了兼容这个不一致,
|
|
238
|
-
// 此处把 ref/state 设为 active id 但不重新 loadContext —— 由用户手动 /session
|
|
239
|
-
// 切回正确 session(极少数 edge case,避免启动时异步加载导致界面闪烁)。
|
|
256
|
+
const answerAsk = useCallback((answers) => askSlot.answer(answers), [askSlot]);
|
|
257
|
+
const cancelAsk = useCallback(() => {
|
|
258
|
+
if (pendingAskSource?.kind === 'subagent')
|
|
259
|
+
askSlot.cancel();
|
|
260
|
+
else
|
|
261
|
+
abortRef.current?.abort();
|
|
262
|
+
}, [askSlot, pendingAskSource]);
|
|
263
|
+
// Root 已按 active session 加载初始历史;这里同步同一 active id 给交互状态。
|
|
240
264
|
useEffect(() => {
|
|
241
265
|
let cancelled = false;
|
|
242
266
|
void (async () => {
|
|
@@ -256,13 +280,22 @@ export function useChat(args) {
|
|
|
256
280
|
};
|
|
257
281
|
}, []);
|
|
258
282
|
const submit = useCallback(async (input) => {
|
|
259
|
-
if (isLoading)
|
|
260
|
-
return;
|
|
261
283
|
const trimmed = input.trim();
|
|
262
284
|
if (trimmed.length === 0)
|
|
263
285
|
return;
|
|
286
|
+
if (isLoading) {
|
|
287
|
+
inputQueue.enqueue(trimmed, 'next');
|
|
288
|
+
const running = Array.from(runningToolsRef.current.values());
|
|
289
|
+
const allCancelable = running.length > 0 && running.every(t => t.isCancelable);
|
|
290
|
+
if (allCancelable) {
|
|
291
|
+
abortRef.current?.abort('interrupt');
|
|
292
|
+
}
|
|
293
|
+
bump();
|
|
294
|
+
return;
|
|
295
|
+
}
|
|
264
296
|
setIsLoading(true);
|
|
265
297
|
setError(null);
|
|
298
|
+
setTransientError(null);
|
|
266
299
|
setInterrupted(false);
|
|
267
300
|
cancelAllThrottles();
|
|
268
301
|
setStreamingText('');
|
|
@@ -271,14 +304,48 @@ export function useChat(args) {
|
|
|
271
304
|
setProgressStage('idle');
|
|
272
305
|
setReasoningPreview('');
|
|
273
306
|
setRunningToolsMap(new Map());
|
|
307
|
+
// 重置暂态错误状态机(防上一轮工作流残留)
|
|
308
|
+
workflowCtxRef.current = { activeStepId: null, pendingLlmError: null };
|
|
274
309
|
const ac = new AbortController();
|
|
275
310
|
abortRef.current = ac;
|
|
311
|
+
let releaseContextLock = null;
|
|
276
312
|
dlog('ui', 'turn START', { input: trimmed.slice(0, 80) });
|
|
313
|
+
// 长时间运行时(如工作流 agent 步骤)的定时 token 刷新:
|
|
314
|
+
// runQuery 的 text/reasoning 事件不触发 bump,导致 StatusLine token 数
|
|
315
|
+
// 可能冻结数十秒。此定时器每 10s 强制刷新一次,确保用户看到上下文增长。
|
|
316
|
+
const tokenRefreshTimer = setInterval(() => {
|
|
317
|
+
bump();
|
|
318
|
+
}, 10_000);
|
|
277
319
|
try {
|
|
320
|
+
await updateSessionMetadata(getWorkingDir(), activeSessionIdRef.current, {
|
|
321
|
+
lastMessagePreview: trimmed,
|
|
322
|
+
}).catch(() => { });
|
|
323
|
+
const activeFile = sessionFileFor(getWorkingDir(), activeSessionIdRef.current);
|
|
324
|
+
releaseContextLock = await acquireContextLock(activeFile, ac.signal);
|
|
325
|
+
// Gateway/Cron may have written this cwd since the TUI rendered its last frame.
|
|
326
|
+
// Reload after acquiring the cross-process lock so this turn extends the newest history.
|
|
327
|
+
const latest = await loadContext(activeFile);
|
|
328
|
+
if (latest) {
|
|
329
|
+
const ephemeral = historyRef.current.filter((message) => message.role === 'system' && message._ephemeral);
|
|
330
|
+
historyRef.current.length = 0;
|
|
331
|
+
historyRef.current.push(...latest, ...ephemeral);
|
|
332
|
+
}
|
|
278
333
|
for await (const ev of runWithPlugins(trimmed, {
|
|
279
|
-
provider:
|
|
334
|
+
provider: providerRef.current,
|
|
280
335
|
history: historyRef.current,
|
|
281
336
|
signal: ac.signal,
|
|
337
|
+
parseInlineImages: true,
|
|
338
|
+
hasQueuedInput: () => inputQueue.size() > 0,
|
|
339
|
+
onToolStart: (name, id) => {
|
|
340
|
+
const tool = getToolByName(name);
|
|
341
|
+
runningToolsRef.current.set(id, {
|
|
342
|
+
toolName: name,
|
|
343
|
+
isCancelable: tool?.interruptBehavior?.() === 'cancel',
|
|
344
|
+
});
|
|
345
|
+
},
|
|
346
|
+
onToolEnd: (id) => {
|
|
347
|
+
runningToolsRef.current.delete(id);
|
|
348
|
+
},
|
|
282
349
|
})) {
|
|
283
350
|
handleEvent(ev, {
|
|
284
351
|
setStreamingText,
|
|
@@ -289,6 +356,12 @@ export function useChat(args) {
|
|
|
289
356
|
setProgressStage,
|
|
290
357
|
setReasoningPreview,
|
|
291
358
|
setRunningToolsMap,
|
|
359
|
+
setContextUsage,
|
|
360
|
+
// 暂态错误(工作流步内 llm_error / 重试进度)→ LiveArea 黄字
|
|
361
|
+
setTransientError,
|
|
362
|
+
workflowCtx: workflowCtxRef.current,
|
|
363
|
+
// 步骤 id→人话名映射:scrollback 进度行三态(▶/✓/⊘)统一显示 title
|
|
364
|
+
stepTitles: stepTitlesRef.current,
|
|
292
365
|
// 节流相关 —— buffer 累积 + 100ms timer flush
|
|
293
366
|
appendStreamingDelta: streamingFlush.append,
|
|
294
367
|
flushStreamingBuffer: streamingFlush.flush,
|
|
@@ -311,16 +384,31 @@ export function useChat(args) {
|
|
|
311
384
|
}
|
|
312
385
|
finally {
|
|
313
386
|
dlog('ui', 'turn END (finally)');
|
|
387
|
+
clearInterval(tokenRefreshTimer);
|
|
314
388
|
setIsLoading(false);
|
|
315
389
|
cancelAllThrottles();
|
|
316
390
|
resetTurnState();
|
|
317
391
|
setPluginProgress(null);
|
|
318
392
|
abortRef.current = null;
|
|
393
|
+
runningToolsRef.current.clear();
|
|
319
394
|
// v2 multi-session:持久化到当前 active session 文件。
|
|
320
|
-
|
|
395
|
+
try {
|
|
396
|
+
await saveContext(historyRef.current, sessionFileFor(getWorkingDir(), activeSessionIdRef.current));
|
|
397
|
+
}
|
|
398
|
+
finally {
|
|
399
|
+
await releaseContextLock?.();
|
|
400
|
+
}
|
|
401
|
+
const consumeQueued = () => {
|
|
402
|
+
const next = inputQueue.dequeue();
|
|
403
|
+
if (!next)
|
|
404
|
+
return;
|
|
405
|
+
queueMicrotask(() => {
|
|
406
|
+
void submit(next.value);
|
|
407
|
+
});
|
|
408
|
+
};
|
|
409
|
+
consumeQueued();
|
|
321
410
|
}
|
|
322
411
|
}, [
|
|
323
|
-
args.provider,
|
|
324
412
|
bump,
|
|
325
413
|
isLoading,
|
|
326
414
|
sessionFile,
|
|
@@ -329,10 +417,17 @@ export function useChat(args) {
|
|
|
329
417
|
reasoningFlush,
|
|
330
418
|
toolBumpFlush,
|
|
331
419
|
resetTurnState,
|
|
420
|
+
inputQueue,
|
|
332
421
|
]);
|
|
333
422
|
const abort = useCallback(() => {
|
|
423
|
+
inputQueue.clear();
|
|
424
|
+
runningToolsRef.current.clear();
|
|
334
425
|
abortRef.current?.abort();
|
|
426
|
+
}, [inputQueue]);
|
|
427
|
+
const setProvider = useCallback((next) => {
|
|
428
|
+
providerRef.current = next;
|
|
335
429
|
}, []);
|
|
430
|
+
const getCurrentProvider = useCallback(() => providerRef.current, []);
|
|
336
431
|
/**
|
|
337
432
|
* v2.x 新增:把一段临时提示文本作为 _ephemeral 的 system 消息插入 history。
|
|
338
433
|
*
|
|
@@ -496,6 +591,75 @@ export function useChat(args) {
|
|
|
496
591
|
setCompactGeneration((g) => g + 1);
|
|
497
592
|
bump();
|
|
498
593
|
}, [bump, isLoading, cancelAllThrottles, resetTurnState]);
|
|
594
|
+
const switchWorkspace = useCallback(async (cwd, sessionId) => {
|
|
595
|
+
if (isLoading)
|
|
596
|
+
return false;
|
|
597
|
+
const targetCwd = cwd.trim();
|
|
598
|
+
const targetSessionId = sessionId.trim();
|
|
599
|
+
if (!targetCwd || !targetSessionId)
|
|
600
|
+
return false;
|
|
601
|
+
let loaded = null;
|
|
602
|
+
let targetExists = false;
|
|
603
|
+
try {
|
|
604
|
+
const targetFile = await resolveSessionFile(targetCwd, targetSessionId);
|
|
605
|
+
try {
|
|
606
|
+
targetExists = (await stat(targetFile)).isFile();
|
|
607
|
+
}
|
|
608
|
+
catch {
|
|
609
|
+
targetExists = false;
|
|
610
|
+
}
|
|
611
|
+
loaded = await loadContext(targetFile);
|
|
612
|
+
if (targetExists && loaded === null)
|
|
613
|
+
throw new Error('目标会话历史损坏或无法读取');
|
|
614
|
+
}
|
|
615
|
+
catch (error) {
|
|
616
|
+
setError(error instanceof Error ? error.message : String(error));
|
|
617
|
+
bump();
|
|
618
|
+
return false;
|
|
619
|
+
}
|
|
620
|
+
let systemPrompt;
|
|
621
|
+
try {
|
|
622
|
+
systemPrompt = await buildFullSystemPrompt(targetCwd, ALL_TOOLS);
|
|
623
|
+
}
|
|
624
|
+
catch (error) {
|
|
625
|
+
setError(`无法打开工作区:${error instanceof Error ? error.message : String(error)}`);
|
|
626
|
+
bump();
|
|
627
|
+
return false;
|
|
628
|
+
}
|
|
629
|
+
const previousCwd = getWorkingDir();
|
|
630
|
+
try {
|
|
631
|
+
await saveContext(historyRef.current, sessionFileFor(previousCwd, activeSessionIdRef.current));
|
|
632
|
+
}
|
|
633
|
+
catch {
|
|
634
|
+
// A damaged old workspace must not trap the user in it.
|
|
635
|
+
}
|
|
636
|
+
try {
|
|
637
|
+
await setActiveSession(targetCwd, targetSessionId);
|
|
638
|
+
}
|
|
639
|
+
catch (error) {
|
|
640
|
+
setError(`无法切换 Session:${error instanceof Error ? error.message : String(error)}`);
|
|
641
|
+
bump();
|
|
642
|
+
return false;
|
|
643
|
+
}
|
|
644
|
+
setWorkingDir(targetCwd);
|
|
645
|
+
activeSessionIdRef.current = targetSessionId;
|
|
646
|
+
setActiveSessionId(targetSessionId);
|
|
647
|
+
historyRef.current.length = 0;
|
|
648
|
+
historyRef.current.push({ role: 'system', content: systemPrompt });
|
|
649
|
+
if (loaded?.length) {
|
|
650
|
+
historyRef.current.push(...(loaded[0]?.role === 'system' ? loaded.slice(1) : loaded));
|
|
651
|
+
}
|
|
652
|
+
clearFileState();
|
|
653
|
+
resetReactiveCompactState();
|
|
654
|
+
cancelAllThrottles();
|
|
655
|
+
setContextUsage(null);
|
|
656
|
+
setError(null);
|
|
657
|
+
setInterrupted(false);
|
|
658
|
+
resetTurnState();
|
|
659
|
+
setCompactGeneration((generation) => generation + 1);
|
|
660
|
+
bump();
|
|
661
|
+
return true;
|
|
662
|
+
}, [bump, isLoading, cancelAllThrottles, resetTurnState]);
|
|
499
663
|
// history 用 useMemo 托管:version 变化时返回 historyRef.current 的浅拷贝。
|
|
500
664
|
// 为什么必须 .slice():runQuery 内部 mutate 同一个数组(push 新消息),如果这里
|
|
501
665
|
// 返回同一引用,Ink <Static items={history}> 浅比较看不到 prop 变化 → 不 commit
|
|
@@ -507,21 +671,31 @@ export function useChat(args) {
|
|
|
507
671
|
streamingText,
|
|
508
672
|
isLoading,
|
|
509
673
|
error,
|
|
674
|
+
transientError,
|
|
510
675
|
interrupted,
|
|
511
676
|
pluginProgress,
|
|
512
677
|
compactGeneration,
|
|
513
678
|
progressStage,
|
|
514
679
|
reasoningPreview,
|
|
515
680
|
runningTools,
|
|
681
|
+
contextUsage,
|
|
516
682
|
activeSessionId,
|
|
683
|
+
queuedCount,
|
|
684
|
+
queuedItems,
|
|
517
685
|
submit,
|
|
518
686
|
abort,
|
|
519
687
|
clearHistory,
|
|
520
688
|
compactNow,
|
|
521
689
|
switchSession,
|
|
690
|
+
switchWorkspace,
|
|
522
691
|
ephemeralLine,
|
|
523
692
|
pendingAsk,
|
|
693
|
+
pendingAskSource,
|
|
694
|
+
pendingAskQueueCount,
|
|
524
695
|
answerAsk,
|
|
696
|
+
cancelAsk,
|
|
697
|
+
setProvider,
|
|
698
|
+
getCurrentProvider,
|
|
525
699
|
};
|
|
526
700
|
}
|
|
527
701
|
export function handleEvent(ev, setters) {
|
|
@@ -529,21 +703,73 @@ export function handleEvent(ev, setters) {
|
|
|
529
703
|
// 渲染成持久的暗色行落进 scrollback:跑到哪一步、成功/失败/跳过都看得见,
|
|
530
704
|
// 且出错后仍停留在屏幕上 —— 解决"工作流执行过程不可见、不知道停在哪"。
|
|
531
705
|
const wf = ev;
|
|
706
|
+
// 据 id 回查人话进度名:声明了 title 的步走 title,否则回落 id(end/skipped 用)。
|
|
707
|
+
const titleOrId = (id) => (id && setters.stepTitles?.get(id)) || id || '?';
|
|
532
708
|
switch (wf.type) {
|
|
533
709
|
case 'workflow_start':
|
|
534
|
-
setters.emitProgressLine?.(
|
|
710
|
+
setters.emitProgressLine?.(`${figures.step} 工作流 ${wf.name} 开始(共 ${wf.totalSteps} 步)`);
|
|
711
|
+
// 新工作流开始:清空上一条工作流残留的 title 映射,避免同名步串味。
|
|
712
|
+
setters.stepTitles?.clear();
|
|
713
|
+
// 工作流开始:立即刷新 token(可能已积累大量前置对话上下文)
|
|
714
|
+
setters.bump();
|
|
535
715
|
return;
|
|
536
716
|
case 'workflow_step_start':
|
|
537
|
-
|
|
717
|
+
// 声明了 title 的步显示人话名(与状态行一致、不显示块内相对序号——嵌套块
|
|
718
|
+
// 的 i/total 拼在一起会被误读成"跳步");未声明 title 的步保持 legacy 格式。
|
|
719
|
+
// ⎿ 前缀:step 行挂在「▶ 工作流 x 开始」下的层次连接符(figures.elbow 语汇,
|
|
720
|
+
// 与工具结果/蜂群树统一)。ephemeral 是尾部追加消息,不触任何既有前缀字节。
|
|
721
|
+
if (wf.title) {
|
|
722
|
+
if (wf.id)
|
|
723
|
+
setters.stepTitles?.set(wf.id, wf.title);
|
|
724
|
+
setters.emitProgressLine?.(` ${figures.elbow} ${figures.step} ${wf.title}`);
|
|
725
|
+
}
|
|
726
|
+
else {
|
|
727
|
+
setters.emitProgressLine?.(` ${figures.elbow} ${figures.step} ${(wf.index ?? 0) + 1}/${wf.total ?? '?'} ${wf.id} (${wf.kind})`);
|
|
728
|
+
}
|
|
729
|
+
// 暂态错误状态机:记下当前正在执行的 step id,进入"该步内的 llm_error 暂态化"窗口。
|
|
730
|
+
// 进新步先清掉上一步可能残留的暂态错误(上一步已 end、不应跨步显示)。
|
|
731
|
+
if (setters.workflowCtx) {
|
|
732
|
+
setters.workflowCtx.activeStepId = wf.id ?? null;
|
|
733
|
+
setters.workflowCtx.pendingLlmError = null;
|
|
734
|
+
}
|
|
735
|
+
setters.setTransientError?.(null);
|
|
538
736
|
return;
|
|
539
737
|
case 'workflow_step_end':
|
|
540
|
-
setters.emitProgressLine?.(` ${wf.ok ?
|
|
738
|
+
setters.emitProgressLine?.(` ${figures.elbow} ${wf.ok ? figures.ok : figures.fail} ${titleOrId(wf.id)}${wf.error ? `:${wf.error}` : ''}`);
|
|
739
|
+
// 暂态错误裁决(仅当 end 的是当前活跃步):
|
|
740
|
+
// ok:true → 重试/恢复成功 → 丢弃 pending llm_error,短暂显示"已恢复"后随刷新消失。
|
|
741
|
+
// ok:false → 该步彻底失败 → 把暂态错误提升为永久红字(chat.error,进 scrollback 上方)。
|
|
742
|
+
if (setters.workflowCtx && setters.workflowCtx.activeStepId === wf.id) {
|
|
743
|
+
const pending = setters.workflowCtx.pendingLlmError;
|
|
744
|
+
if (wf.ok) {
|
|
745
|
+
if (pending) {
|
|
746
|
+
// 成功收回:留一条灰底"已恢复"提示,下一次状态行刷新自然覆盖消失
|
|
747
|
+
setters.setTransientError?.(`${figures.ok} ${wf.id} 已恢复(此前 LLM 调用失败已重试成功)`);
|
|
748
|
+
}
|
|
749
|
+
else {
|
|
750
|
+
setters.setTransientError?.(null);
|
|
751
|
+
}
|
|
752
|
+
}
|
|
753
|
+
else {
|
|
754
|
+
// 彻底失败:暂态升永久红字。优先用 step.error,回落到 pending llm_error。
|
|
755
|
+
const finalMsg = wf.error ?? pending;
|
|
756
|
+
if (finalMsg)
|
|
757
|
+
setters.setError(finalMsg);
|
|
758
|
+
setters.setTransientError?.(null);
|
|
759
|
+
}
|
|
760
|
+
setters.workflowCtx.activeStepId = null;
|
|
761
|
+
setters.workflowCtx.pendingLlmError = null;
|
|
762
|
+
}
|
|
763
|
+
// 步骤完成:强制刷新 token(agent/llm 步骤可能写入大量上下文)
|
|
764
|
+
setters.bump();
|
|
541
765
|
return;
|
|
542
766
|
case 'workflow_step_skipped':
|
|
543
|
-
setters.emitProgressLine?.(`
|
|
767
|
+
setters.emitProgressLine?.(` ${figures.elbow} ${figures.skip} ${titleOrId(wf.id)}:${wf.reason ?? ''}`);
|
|
544
768
|
return;
|
|
545
769
|
case 'workflow_done':
|
|
546
|
-
setters.emitProgressLine?.(
|
|
770
|
+
setters.emitProgressLine?.(`${figures.ok} 工作流 ${wf.name} 完成`);
|
|
771
|
+
// 工作流结束:最终刷新(反映完整执行后的上下文量)
|
|
772
|
+
setters.bump();
|
|
547
773
|
return;
|
|
548
774
|
}
|
|
549
775
|
// 收口到 LoopEvent:未识别的 PluginEvent type 走 default no-op
|
|
@@ -592,6 +818,17 @@ export function handleEvent(ev, setters) {
|
|
|
592
818
|
// 压垮 Ink ANSI 重绘。
|
|
593
819
|
setters.scheduleToolBump();
|
|
594
820
|
break;
|
|
821
|
+
case 'tool_progress':
|
|
822
|
+
setters.setRunningToolsMap((prev) => {
|
|
823
|
+
const current = prev.get(e.toolCallId);
|
|
824
|
+
if (!current)
|
|
825
|
+
return prev;
|
|
826
|
+
const next = new Map(prev);
|
|
827
|
+
next.set(e.toolCallId, { ...current, progress: e.message, progressDetail: e.detail });
|
|
828
|
+
return next;
|
|
829
|
+
});
|
|
830
|
+
setters.scheduleToolBump();
|
|
831
|
+
break;
|
|
595
832
|
case 'tool_end':
|
|
596
833
|
// 仅删除对应 callId 的 entry;其余并行工具继续
|
|
597
834
|
setters.setRunningToolsMap((prev) => {
|
|
@@ -636,22 +873,68 @@ export function handleEvent(ev, setters) {
|
|
|
636
873
|
setters.setInterrupted(true);
|
|
637
874
|
setters.bump();
|
|
638
875
|
break;
|
|
639
|
-
case 'error':
|
|
876
|
+
case 'error': {
|
|
877
|
+
const code = e.code;
|
|
878
|
+
// compact_failed:非致命压缩失败(loop 会继续硬上)。不当红字,作为黄字暂态提示。
|
|
879
|
+
if (code === 'compact_failed') {
|
|
880
|
+
setters.setTransientError?.(`压缩失败(已忽略继续):${e.error}`);
|
|
881
|
+
setters.bump();
|
|
882
|
+
break;
|
|
883
|
+
}
|
|
884
|
+
// 工作流步执行中的 max_turns 是可恢复截断信号:runner 会在共享 history 上续跑。
|
|
885
|
+
// 这里不要落入永久红字错误区;顶层非工作流 max_turns 仍保持原有错误展示。
|
|
886
|
+
if (code === 'max_turns' &&
|
|
887
|
+
setters.workflowCtx &&
|
|
888
|
+
setters.workflowCtx.activeStepId !== null &&
|
|
889
|
+
setters.setTransientError) {
|
|
890
|
+
setters.setTransientError('↻ 当前步骤达到轮数预算,正在续跑…');
|
|
891
|
+
setters.bump();
|
|
892
|
+
break;
|
|
893
|
+
}
|
|
894
|
+
// 工作流步执行中的 llm_error:暂态而非永久红字 ——
|
|
895
|
+
// 该步通常会被重试,这里只暂存 + 喂 LiveArea 黄字,等 step_end 裁决去留。
|
|
896
|
+
// 非工作流场景(无活跃 step,或缺 setTransientError 降级)走原有永久红字路径。
|
|
897
|
+
if (code === 'llm_error' &&
|
|
898
|
+
setters.workflowCtx &&
|
|
899
|
+
setters.workflowCtx.activeStepId !== null &&
|
|
900
|
+
setters.setTransientError) {
|
|
901
|
+
setters.workflowCtx.pendingLlmError = e.error;
|
|
902
|
+
setters.setTransientError(`⚠ LLM 调用失败(重试中):${e.error}`);
|
|
903
|
+
setters.bump();
|
|
904
|
+
break;
|
|
905
|
+
}
|
|
906
|
+
// 其余:顶层 halt(无 code)、非工作流 llm_error、aborted 等 → 永久红字。
|
|
640
907
|
setters.setError(e.error);
|
|
641
908
|
setters.bump();
|
|
642
909
|
break;
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
//
|
|
646
|
-
//
|
|
910
|
+
}
|
|
911
|
+
case 'warning': {
|
|
912
|
+
// 通用非致命警告。workflow 这类插件已另发 plugin_progress 把同文案镜像到状态行。
|
|
913
|
+
// 这里额外把"重试 N/M"类警告聚合到 LiveArea 黄色暂态行(覆盖刷新,不 append),
|
|
914
|
+
// 让用户一眼看出"正在第 N/M 次重试"而非"卡住"。其余 warning 仍 no-op。
|
|
915
|
+
const msg = e.message;
|
|
916
|
+
if (setters.setTransientError &&
|
|
917
|
+
typeof msg === 'string' &&
|
|
918
|
+
msg.includes('重试')) {
|
|
919
|
+
setters.setTransientError(`↻ ${msg}`);
|
|
920
|
+
setters.bump();
|
|
921
|
+
}
|
|
647
922
|
break;
|
|
923
|
+
}
|
|
648
924
|
case 'plugin_progress':
|
|
649
925
|
setters.setPluginProgress({
|
|
650
926
|
pluginId: e.pluginId,
|
|
651
927
|
current: e.current,
|
|
652
|
-
max: e.max
|
|
928
|
+
max: e.max,
|
|
653
929
|
message: e.message,
|
|
654
930
|
});
|
|
931
|
+
// 重试进度也镜像到暂态行(覆盖刷新):runner 在重试时发 "↻ 重试 x N/M…"。
|
|
932
|
+
// 与 warning 分支互为补充(plugin_progress 文案更短,适合状态行下方一行)。
|
|
933
|
+
if (setters.setTransientError &&
|
|
934
|
+
typeof e.message === 'string' &&
|
|
935
|
+
e.message.includes('↻ 重试')) {
|
|
936
|
+
setters.setTransientError(e.message);
|
|
937
|
+
}
|
|
655
938
|
setters.bump();
|
|
656
939
|
break;
|
|
657
940
|
case 'reasoning':
|
|
@@ -666,6 +949,17 @@ export function handleEvent(ev, setters) {
|
|
|
666
949
|
// 工具集合的清空时机是各自 tool_end
|
|
667
950
|
setters.setProgressStage(e.stage);
|
|
668
951
|
break;
|
|
952
|
+
case 'context_usage':
|
|
953
|
+
// 内核每轮算出的真实用量(含 tools-schema 开销 + 真实 provider usage)——
|
|
954
|
+
// 比 StatusLine 本地 useTokenUsage 估算更准,落地后 StatusLine 优先用它。
|
|
955
|
+
setters.setContextUsage?.({
|
|
956
|
+
usedTokens: e.usedTokens,
|
|
957
|
+
contextWindow: e.contextWindow,
|
|
958
|
+
remainingTokens: e.remainingTokens,
|
|
959
|
+
compactThreshold: e.compactThreshold,
|
|
960
|
+
source: e.source,
|
|
961
|
+
});
|
|
962
|
+
break;
|
|
669
963
|
default:
|
|
670
964
|
// 落到这里 = 插件私有事件(PluginEvent,type 任意字符串),TUI 不识别即忽略。
|
|
671
965
|
// 这里**故意不**写 console.warn —— Ink 渲染期间写 stdout/stderr 会撕裂终端画面。
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
* 导致只有最后一个换行之前的内容被保留,或者直接触发了发送。
|
|
21
21
|
* ============================================================
|
|
22
22
|
*/
|
|
23
|
-
import { useCallback, useRef, useState } from 'react';
|
|
23
|
+
import { useCallback, useEffect, useRef, useState } from 'react';
|
|
24
24
|
// 粘贴检测阈值:超过此长度视为粘贴输入
|
|
25
25
|
const PASTE_THRESHOLD = 20;
|
|
26
26
|
// 多 Chunk 等待超时:100ms 内收到的内容视为同一批粘贴
|
|
@@ -58,6 +58,16 @@ export function usePasteHandler({ onPasteComplete, multiline = false, }) {
|
|
|
58
58
|
chunks: [],
|
|
59
59
|
timeoutId: null,
|
|
60
60
|
});
|
|
61
|
+
// 卸载清理:粘贴中途组件被切走(SessionPicker/ConfigMenu/AskQuestion 顶替)时
|
|
62
|
+
// 取消挂起的 flush 定时器,防止回调打到已卸载组件。
|
|
63
|
+
useEffect(() => {
|
|
64
|
+
return () => {
|
|
65
|
+
if (pasteStateRef.current.timeoutId) {
|
|
66
|
+
clearTimeout(pasteStateRef.current.timeoutId);
|
|
67
|
+
pasteStateRef.current.timeoutId = null;
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
}, []);
|
|
61
71
|
/**
|
|
62
72
|
* 刷新 UI 的引用(避免闭包问题)
|
|
63
73
|
*/
|
|
@@ -8,14 +8,13 @@
|
|
|
8
8
|
* ============================================================
|
|
9
9
|
*/
|
|
10
10
|
import { useCallback, useMemo, useState } from 'react';
|
|
11
|
-
import { bufClear,
|
|
11
|
+
import { bufClear, bufDeleteBefore, bufDeleteWordBefore, bufInsert, bufKillToLineEnd, bufKillToLineStart, bufMoveBufferEnd, bufMoveBufferStart, bufMoveDown, bufMoveLeft, bufMoveLineEnd, bufMoveLineStart, bufMoveRight, bufMoveUp, bufMoveWordLeft, bufMoveWordRight, bufSetText, EMPTY_BUFFER, layoutBuffer, } from '../textBuffer.js';
|
|
12
12
|
export function useTextBuffer() {
|
|
13
13
|
const [state, setState] = useState(EMPTY_BUFFER);
|
|
14
14
|
const insert = useCallback((s) => setState((p) => bufInsert(p, s)), []);
|
|
15
15
|
const setText = useCallback((s) => setState(bufSetText(s)), []);
|
|
16
16
|
const clear = useCallback(() => setState(bufClear()), []);
|
|
17
17
|
const deleteBefore = useCallback(() => setState(bufDeleteBefore), []);
|
|
18
|
-
const deleteAt = useCallback(() => setState(bufDeleteAt), []);
|
|
19
18
|
const deleteWordBefore = useCallback(() => setState(bufDeleteWordBefore), []);
|
|
20
19
|
const killToLineStart = useCallback(() => setState(bufKillToLineStart), []);
|
|
21
20
|
const killToLineEnd = useCallback(() => setState(bufKillToLineEnd), []);
|
|
@@ -37,7 +36,6 @@ export function useTextBuffer() {
|
|
|
37
36
|
setText,
|
|
38
37
|
clear,
|
|
39
38
|
deleteBefore,
|
|
40
|
-
deleteAt,
|
|
41
39
|
deleteWordBefore,
|
|
42
40
|
killToLineStart,
|
|
43
41
|
killToLineEnd,
|