minimal-agent 0.12.0 → 0.14.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 +6 -5
- package/package.json +62 -5
- package/plugins/computer_use/.claude-plugin/plugin.json +1 -1
- package/plugins/cron-runner/src/commands.js +4 -4
- package/plugins/cron-runner/src/commands.ts +4 -4
- package/plugins/workflow-runner/.claude-plugin/plugin.json +2 -2
- package/plugins/workflow-runner/commands/workflow.md +57 -90
- package/plugins/workflow-runner/commands/workflows.md +4 -4
- package/plugins/workflow-runner/plugin.js +24 -13
- package/plugins/workflow-runner/plugin.ts +28 -13
- package/plugins/workflow-runner/src/discovery.js +160 -0
- package/plugins/workflow-runner/src/discovery.ts +194 -0
- package/plugins/workflow-runner/src/goalAnchor.js +53 -0
- package/plugins/workflow-runner/src/goalAnchor.ts +59 -0
- package/plugins/workflow-runner/src/index.js +204 -185
- package/plugins/workflow-runner/src/index.ts +249 -224
- package/plugins/workflow-runner/src/inputCoercion.js +40 -0
- package/plugins/workflow-runner/src/inputCoercion.ts +43 -0
- package/plugins/workflow-runner/src/journal.js +224 -0
- package/plugins/workflow-runner/src/journal.ts +274 -0
- package/plugins/workflow-runner/src/jsonSchema.js +132 -0
- package/plugins/workflow-runner/src/jsonSchema.ts +162 -0
- package/plugins/workflow-runner/src/moduleResolution.js +64 -0
- package/plugins/workflow-runner/src/moduleResolution.ts +71 -0
- package/plugins/workflow-runner/src/resourceInputs.js +220 -0
- package/plugins/workflow-runner/src/resourceInputs.ts +300 -0
- package/plugins/workflow-runner/src/runner.js +404 -467
- package/plugins/workflow-runner/src/runner.ts +491 -535
- package/plugins/workflow-runner/src/runtime/config.js +46 -0
- package/plugins/workflow-runner/src/runtime/config.ts +69 -0
- package/plugins/workflow-runner/src/runtime/context.js +1304 -0
- package/plugins/workflow-runner/src/runtime/context.ts +1786 -0
- package/plugins/workflow-runner/src/runtime/exec.js +34 -0
- package/plugins/workflow-runner/src/runtime/exec.ts +53 -0
- package/plugins/workflow-runner/src/runtime/flow.js +8 -0
- package/plugins/workflow-runner/src/runtime/flow.ts +12 -0
- package/plugins/workflow-runner/src/runtime/fs.js +27 -0
- package/plugins/workflow-runner/src/runtime/fs.ts +41 -0
- package/plugins/workflow-runner/src/runtime/model.js +6 -0
- package/plugins/workflow-runner/src/runtime/model.ts +8 -0
- package/plugins/workflow-runner/src/runtime/output.js +7 -0
- package/plugins/workflow-runner/src/runtime/output.ts +21 -0
- package/plugins/workflow-runner/src/runtime/scheduler.js +141 -0
- package/plugins/workflow-runner/src/runtime/scheduler.ts +166 -0
- package/plugins/workflow-runner/src/runtime/state.js +16 -0
- package/plugins/workflow-runner/src/runtime/state.ts +19 -0
- package/plugins/workflow-runner/src/runtime/tools.js +3 -0
- package/plugins/workflow-runner/src/runtime/tools.ts +6 -0
- package/plugins/workflow-runner/src/sandbox.js +141 -0
- package/plugins/workflow-runner/src/sandbox.ts +182 -0
- package/plugins/workflow-runner/src/scriptGuard.d.mts +38 -0
- package/plugins/workflow-runner/src/scriptGuard.mjs +449 -0
- package/plugins/workflow-runner/src/taskIdentity.js +172 -152
- package/plugins/workflow-runner/src/taskIdentity.ts +233 -200
- package/plugins/workflow-runner/src/toolRuntime.js +29 -8
- package/plugins/workflow-runner/src/toolRuntime.ts +36 -11
- package/plugins/workflow-runner/src/types.js +7 -105
- package/plugins/workflow-runner/src/types.ts +155 -520
- package/plugins/workflow-runner/src/util.js +47 -0
- package/plugins/workflow-runner/src/util.ts +51 -0
- package/plugins/workflow-runner/src/workflowContext.js +20 -0
- package/plugins/workflow-runner/src/workflowContext.ts +58 -0
- package/plugins/workflow-runner/src/workflowDependencies.js +36 -0
- package/plugins/workflow-runner/src/workflowDependencies.ts +59 -0
- package/plugins/workflow-runner/src/workflowFingerprint.js +118 -0
- package/plugins/workflow-runner/src/workflowFingerprint.ts +122 -0
- package/plugins/workflow-runner/src/workflowState.js +542 -469
- package/plugins/workflow-runner/src/workflowState.ts +639 -589
- package/plugins/workflow-runner/test/api.test.ts +223 -0
- package/plugins/workflow-runner/test/apiCapabilities.test.ts +614 -0
- package/plugins/workflow-runner/test/apiConcurrencyGate.test.ts +104 -0
- package/plugins/workflow-runner/test/apiEcho.test.ts +210 -0
- package/plugins/workflow-runner/test/apiReadText.test.ts +178 -0
- package/plugins/workflow-runner/test/apiResourceLedger.test.ts +184 -0
- package/plugins/workflow-runner/test/childWorkflow.test.ts +304 -0
- package/plugins/workflow-runner/test/discovery.test.ts +234 -0
- package/plugins/workflow-runner/test/goalAnchor.test.ts +106 -0
- package/plugins/workflow-runner/test/goalController.test.ts +317 -0
- package/plugins/workflow-runner/test/journalLanes.test.ts +198 -0
- package/plugins/workflow-runner/test/listResources.test.ts +106 -0
- package/plugins/workflow-runner/test/resourceInputs.test.ts +294 -0
- package/plugins/workflow-runner/test/sandbox.test.ts +126 -0
- package/plugins/workflow-runner/test/scheduler.test.ts +70 -0
- package/plugins/workflow-runner/test/scriptGuard.test.ts +276 -0
- package/plugins/workflow-runner/test/skillDocSync.test.ts +78 -0
- package/plugins/workflow-runner/test/taskIdentity.test.ts +288 -179
- package/plugins/workflow-runner/test/workflowFingerprint.test.ts +106 -0
- package/plugins/workflow-runner/test/workflowState.test.ts +429 -0
- package/skills/pptx/examples/assets/cover-bg.svg +23 -0
- package/skills/pptx/examples/assets/icon-spark.svg +5 -0
- package/skills/skill-creator/scripts/run_eval.py +3 -2
- package/skills/ui-design/SKILL.md +5 -2
- package/skills/ui-design/references/layout-composition.md +1 -1
- package/skills/workflow-creator/SKILL.md +344 -0
- package/skills/workflow-creator/assets/template/workflow.mjs +48 -0
- package/skills/workflow-creator/references/api.md +216 -0
- package/skills/workflow-creator/references/migration.md +103 -0
- package/skills/workflow-creator/references/patterns.md +779 -0
- package/skills/workflow-creator/scripts/list-resources.mjs +205 -0
- package/skills/workflow-creator/scripts/scaffold.mjs +148 -0
- package/skills/workflow-creator/scripts/validate.mjs +275 -0
- package/skills/workflow-creator/scripts/workflow-creator.test.mjs +82 -0
- package/src/advisor/advisorCoordinator.js +503 -0
- package/src/advisor/advisorCoordinator.ts +657 -0
- package/src/cli/configMenu.tsx +0 -1
- package/src/cli/print.js +44 -6
- package/src/cli/print.ts +73 -4
- package/src/cli/streamJson.js +5 -6
- package/src/cli/streamJson.ts +8 -4
- package/src/cli/wizards/advisorWizard.js +4 -2
- package/src/cli/wizards/advisorWizard.tsx +8 -2
- package/src/cli/wizards/contextAdvisorWizard.js +5 -4
- package/src/cli/wizards/contextAdvisorWizard.tsx +7 -6
- package/src/collaboration/taskFlow.js +115 -0
- package/src/collaboration/taskFlow.ts +169 -0
- package/src/config/configDefaults.js +41 -1
- package/src/config/configDefaults.ts +43 -1
- package/src/config/configDiagnostics.ts +2 -2
- package/src/config/configFile.js +40 -9
- package/src/config/configFile.ts +60 -11
- package/src/config/configService.js +443 -74
- package/src/config/configService.ts +723 -142
- package/src/config/numericLimits.js +22 -0
- package/src/config/numericLimits.ts +24 -0
- package/src/config.js +1 -0
- package/src/config.ts +3 -2
- package/src/context/budgetPlan.js +1 -1
- package/src/context/budgetPlan.ts +1 -1
- package/src/context/compact.js +7 -2
- package/src/context/compact.ts +9 -2
- package/src/context/microCompactLite.js +212 -13
- package/src/context/microCompactLite.ts +261 -12
- package/src/context/sessionRegistry.js +8 -1
- package/src/context/sessionRegistry.ts +18 -5
- package/src/context/toolResultArchive.js +72 -0
- package/src/context/toolResultArchive.ts +95 -0
- package/src/goal/errors.js +8 -0
- package/src/goal/errors.ts +18 -0
- package/src/goal/goalRuntime.js +344 -0
- package/src/goal/goalRuntime.ts +466 -0
- package/src/goal/goalStore.js +200 -0
- package/src/goal/goalStore.ts +219 -0
- package/src/goal/index.js +4 -0
- package/src/goal/index.ts +4 -0
- package/src/goal/statusMapping.js +71 -0
- package/src/goal/statusMapping.ts +95 -0
- package/src/goal/types.js +7 -0
- package/src/goal/types.ts +179 -0
- package/src/goal/workflowController.js +306 -0
- package/src/goal/workflowController.ts +360 -0
- package/src/harness/evidenceLedger.js +2 -0
- package/src/harness/evidenceLedger.ts +1 -0
- package/src/harness/stallGuard.js +9 -3
- package/src/harness/stallGuard.ts +7 -3
- package/src/images/imageResolver.js +161 -0
- package/src/images/imageResolver.ts +189 -0
- package/src/infra/verification.js +56 -20
- package/src/infra/verification.ts +87 -20
- package/src/llm/client.js +79 -21
- package/src/llm/client.ts +182 -35
- package/src/llm/gatewayClient.js +201 -0
- package/src/llm/gatewayClient.ts +299 -0
- package/src/llm/messageProvenance.js +46 -0
- package/src/llm/messageProvenance.ts +58 -0
- package/src/llm/modelProvider.js +1 -0
- package/src/llm/modelProvider.ts +22 -0
- package/src/llm/providers/anthropicFormat.js +108 -1
- package/src/llm/providers/anthropicFormat.ts +115 -4
- package/src/llm/providers/openaiFormat.js +9 -0
- package/src/llm/providers/openaiFormat.ts +12 -3
- package/src/loop.js +319 -54
- package/src/loop.ts +373 -57
- package/src/main.js +4 -1
- package/src/main.tsx +4 -1
- package/src/managedExecution/index.js +2 -0
- package/src/managedExecution/index.ts +2 -0
- package/src/managedExecution/registry.js +15 -0
- package/src/managedExecution/registry.ts +20 -0
- package/src/managedExecution/types.js +1 -0
- package/src/managedExecution/types.ts +86 -0
- package/src/modelRef.js +9 -0
- package/src/modelRef.ts +31 -0
- package/src/multimodal.js +14 -28
- package/src/multimodal.ts +17 -33
- package/src/net/lanDiscovery.js +65 -0
- package/src/net/lanDiscovery.ts +82 -0
- package/src/plugin-sdk.js +28 -3
- package/src/plugin-sdk.ts +39 -6
- package/src/plugins/pluginApi.ts +16 -1
- package/src/plugins/pluginLoader.js +4 -0
- package/src/plugins/pluginLoader.ts +6 -0
- package/src/plugins/pluginRunner.js +152 -9
- package/src/plugins/pluginRunner.ts +186 -8
- package/src/plugins/slashFallback.js +113 -0
- package/src/plugins/slashFallback.ts +137 -0
- package/src/prompts/policyRegistry.js +15 -8
- package/src/prompts/policyRegistry.ts +18 -8
- package/src/prompts/promptAssembly.js +103 -12
- package/src/prompts/promptAssembly.ts +114 -12
- package/src/prompts/skillList.js +21 -3
- package/src/prompts/skillList.ts +27 -2
- package/src/prompts/stableSystemPrompt.js +18 -0
- package/src/prompts/stableSystemPrompt.ts +33 -0
- package/src/runtime/agentRuntimeContext.js +8 -2
- package/src/runtime/agentRuntimeContext.ts +17 -4
- package/src/session/runAgentTurn.js +78 -12
- package/src/session/runAgentTurn.ts +105 -23
- package/src/session/runTurn.js +10 -9
- package/src/session/runTurn.ts +17 -8
- package/src/session/sessionState.ts +2 -0
- package/src/team/auth.js +186 -0
- package/src/team/auth.ts +298 -0
- package/src/team/cli.js +105 -0
- package/src/team/cli.ts +122 -0
- package/src/team/onlineToolContract.js +64 -0
- package/src/team/onlineToolContract.ts +109 -0
- package/src/team/toolPolicy.js +36 -0
- package/src/team/toolPolicy.ts +75 -0
- package/src/tools/advisor/advisor.js +148 -37
- package/src/tools/advisor/advisor.ts +161 -38
- package/src/tools/advisor/advisorDecision.js +71 -0
- package/src/tools/advisor/advisorDecision.ts +85 -0
- package/src/tools/agent/agent.js +18 -5
- package/src/tools/agent/agent.ts +21 -7
- package/src/tools/agent/agentControl.js +246 -0
- package/src/tools/agent/agentControl.ts +284 -0
- package/src/tools/agent/agentTaskService.js +40 -1
- package/src/tools/agent/agentTaskService.ts +44 -1
- package/src/tools/agent/agentTypes.js +14 -2
- package/src/tools/agent/agentTypes.ts +15 -2
- package/src/tools/applypatch/applyPatch.js +362 -0
- package/src/tools/applypatch/applyPatch.ts +428 -0
- package/src/tools/bash/bash.js +70 -18
- package/src/tools/bash/bash.ts +75 -20
- package/src/tools/capabilities.js +10 -5
- package/src/tools/capabilities.ts +10 -5
- package/src/tools/computeruse/computerIntent.js +32 -0
- package/src/tools/computeruse/computerIntent.ts +46 -0
- package/src/tools/computeruse/telemetryLog.js +12 -0
- package/src/tools/computeruse/telemetryLog.ts +16 -0
- package/src/tools/computeruse/vision/telemetry.js +1 -0
- package/src/tools/computeruse/vision/telemetry.ts +1 -0
- package/src/tools/effectiveToolSet.js +4 -0
- package/src/tools/effectiveToolSet.ts +4 -0
- package/src/tools/index.js +21 -2
- package/src/tools/index.ts +33 -2
- package/src/tools/powershell/powershell.js +18 -1
- package/src/tools/powershell/powershell.ts +18 -2
- package/src/tools/presentresult/presentresult.js +6 -15
- package/src/tools/presentresult/presentresult.ts +6 -15
- package/src/tools/read/read.js +69 -11
- package/src/tools/read/read.ts +70 -11
- package/src/tools/shared/fileUtils.js +1 -2
- package/src/tools/shared/fileUtils.ts +1 -2
- package/src/tools/shared/permanentDeleteGuard.js +34 -3
- package/src/tools/shared/permanentDeleteGuard.ts +43 -6
- package/src/tools/shared/toolOutputStore.js +23 -1
- package/src/tools/shared/toolOutputStore.ts +22 -1
- package/src/tools/skill/skill.js +9 -3
- package/src/tools/skill/skill.ts +8 -4
- package/src/tools/toolPromptGuides.js +117 -0
- package/src/tools/toolPromptGuides.ts +137 -0
- package/src/tools/webbrowser/agentBrowserPath.js +19 -9
- package/src/tools/webbrowser/agentBrowserPath.ts +70 -28
- package/src/tools/webbrowser/browser.js +27 -11
- package/src/tools/webbrowser/browser.ts +68 -24
- package/src/tools/webbrowser/browserAutomation.js +329 -0
- package/src/tools/webbrowser/browserAutomation.ts +467 -0
- package/src/tools/webbrowser/browserAxState.js +97 -0
- package/src/tools/webbrowser/browserAxState.ts +142 -0
- package/src/tools/webbrowser/browserBatch.js +1578 -0
- package/src/tools/webbrowser/browserBatch.ts +2382 -0
- package/src/tools/webbrowser/browserCdp.js +208 -0
- package/src/tools/webbrowser/browserCdp.ts +255 -0
- package/src/tools/webbrowser/browserConditions.js +24 -0
- package/src/tools/webbrowser/browserConditions.ts +45 -0
- package/src/tools/webbrowser/browserContent.js +281 -0
- package/src/tools/webbrowser/browserContent.ts +438 -0
- package/src/tools/webbrowser/browserControl.js +131 -30
- package/src/tools/webbrowser/browserControl.ts +210 -50
- package/src/tools/webbrowser/browserDaemon.js +22 -14
- package/src/tools/webbrowser/browserDaemon.ts +54 -21
- package/src/tools/webbrowser/browserFrames.js +47 -0
- package/src/tools/webbrowser/browserFrames.ts +54 -0
- package/src/tools/webbrowser/browserInteractionResolver.js +17 -0
- package/src/tools/webbrowser/browserInteractionResolver.ts +40 -0
- package/src/tools/webbrowser/browserLocator.js +140 -0
- package/src/tools/webbrowser/browserLocator.ts +194 -0
- package/src/tools/webbrowser/browserOutcome.js +10 -17
- package/src/tools/webbrowser/browserOutcome.ts +48 -31
- package/src/tools/webbrowser/browserRecovery.js +58 -0
- package/src/tools/webbrowser/browserRecovery.ts +83 -0
- package/src/tools/webbrowser/browserRunRegistry.js +35 -3
- package/src/tools/webbrowser/browserRunRegistry.ts +91 -15
- package/src/tools/webbrowser/browserService.js +28 -2
- package/src/tools/webbrowser/browserService.ts +49 -9
- package/src/tools/webbrowser/browserStream.js +132 -33
- package/src/tools/webbrowser/browserStream.ts +224 -50
- package/src/tools/webbrowser/browserTabs.js +48 -15
- package/src/tools/webbrowser/browserTabs.ts +178 -96
- package/src/tools/webbrowser/webbrowser.js +683 -144
- package/src/tools/webbrowser/webbrowser.ts +979 -236
- package/src/tools/webfetch/preapproved.js +0 -2
- package/src/tools/webfetch/preapproved.ts +0 -2
- package/src/tools/websearch/websearch.js +129 -8
- package/src/tools/websearch/websearch.ts +138 -6
- package/src/types.ts +450 -42
- package/src/ui/App.js +377 -58
- package/src/ui/App.tsx +576 -111
- package/src/ui/LiveArea.js +14 -8
- package/src/ui/LiveArea.tsx +14 -10
- package/src/ui/MessageList.js +63 -20
- package/src/ui/MessageList.tsx +101 -16
- package/src/ui/ModelPicker.js +7 -4
- package/src/ui/ModelPicker.tsx +55 -8
- package/src/ui/hooks/useChat.js +152 -163
- package/src/ui/hooks/useChat.ts +396 -334
- package/src/ui/loginCommand.js +56 -0
- package/src/ui/loginCommand.ts +63 -0
- package/src/ui/toolProgress.js +41 -0
- package/src/ui/toolProgress.ts +44 -0
- package/src/web/browserBridge.js +20 -2
- package/src/web/browserBridge.ts +20 -2
- package/src/web/protocol.ts +1897 -194
- package/workflows/adaptive-research/workflow.mjs +86 -0
- package/workflows/e2e-capabilities/workflow.mjs +102 -0
- package/workflows/e2e-llm/workflow.mjs +52 -0
- package/workflows/e2e-smoke/workflow.mjs +59 -0
- package/workflows/e2e-vision-schema/README.md +48 -0
- package/workflows/e2e-vision-schema/workflow.mjs +149 -0
- package/workflows/e2e-write-greet/workflow.mjs +37 -0
- package/workflows/file-census/README.md +14 -0
- package/workflows/file-census/scripts/.gitkeep +2 -0
- package/workflows/file-census/workflow.mjs +110 -0
- package/workflows/goal/README.md +166 -0
- package/workflows/goal/prompts/audit.md +57 -0
- package/workflows/goal/prompts/contract.md +56 -0
- package/workflows/goal/prompts/iterate.md +37 -0
- package/workflows/goal/workflow.mjs +501 -0
- package/workflows/illustrate/README.md +90 -0
- package/workflows/illustrate/assets/engines.json +20 -0
- package/workflows/illustrate/assets/extract-baidu.js +21 -0
- package/workflows/illustrate/assets/extract-bing.js +33 -0
- package/workflows/illustrate/assets/extract-google.js +31 -0
- package/workflows/illustrate/workflow.mjs +301 -0
- package/workflows/plan/workflow.mjs +389 -0
- package/workflows/question-crops/README.md +35 -0
- package/workflows/question-crops/prompts/inventory.md +23 -0
- package/workflows/question-crops/scripts/image-ops.mjs +305 -0
- package/workflows/question-crops/scripts/image-ops.test.mjs +95 -0
- package/workflows/question-crops/workflow.mjs +172 -0
- package/workflows/ralph-loop/workflow.mjs +162 -0
- package/workflows/research/DESIGN.md +449 -0
- package/workflows/research/README.md +74 -0
- package/workflows/research/prompts/s0-goal.md +43 -0
- package/workflows/research/prompts/s1-early-exit.md +22 -0
- package/workflows/research/prompts/s1-map.md +58 -0
- package/workflows/research/prompts/s2-options.md +53 -0
- package/workflows/research/prompts/s3-deep-dive.md +56 -0
- package/workflows/research/prompts/s4-verify.md +74 -0
- package/workflows/research/prompts/s5-synthesis.md +81 -0
- package/workflows/research/prompts/s5b-fill-gaps.md +32 -0
- package/workflows/research/prompts/s6-package.md +32 -0
- package/workflows/research/scripts/build-report.mjs +98 -0
- package/workflows/research/workflow.mjs +288 -0
- package/workflows/wechat-reading-shots/prompts/collect-params.md +16 -0
- package/workflows/wechat-reading-shots/prompts/evaluate.md +20 -0
- package/workflows/wechat-reading-shots/prompts/open-book.md +28 -0
- package/workflows/wechat-reading-shots/prompts/read-page.md +13 -0
- package/workflows/wechat-reading-shots/prompts/turn-batch.md +24 -0
- package/workflows/wechat-reading-shots/test/control-flow.test.ts +329 -0
- package/workflows/wechat-reading-shots/workflow.mjs +266 -0
- package/workflows/wrong-question-docx/README.md +55 -0
- package/workflows/wrong-question-docx/prompts/figure-qa.md +8 -0
- package/workflows/wrong-question-docx/prompts/transcribe.md +25 -0
- package/workflows/wrong-question-docx/scripts/workbook.mjs +857 -0
- package/workflows/wrong-question-docx/test/control-flow.test.ts +165 -0
- package/workflows/wrong-question-docx/test/workbook.test.mjs +303 -0
- package/workflows/wrong-question-docx/workflow.mjs +485 -0
- package/plugins/workflow-runner/src/agentCommon.js +0 -354
- package/plugins/workflow-runner/src/agentCommon.ts +0 -435
- package/plugins/workflow-runner/src/controlFlow/branch.js +0 -40
- package/plugins/workflow-runner/src/controlFlow/branch.ts +0 -46
- package/plugins/workflow-runner/src/controlFlow/loop.js +0 -211
- package/plugins/workflow-runner/src/controlFlow/loop.ts +0 -248
- package/plugins/workflow-runner/src/controlFlow/pause.js +0 -129
- package/plugins/workflow-runner/src/controlFlow/pause.ts +0 -141
- package/plugins/workflow-runner/src/controlFlow/signal.js +0 -21
- package/plugins/workflow-runner/src/controlFlow/signal.ts +0 -29
- package/plugins/workflow-runner/src/controlFlow/types.js +0 -18
- package/plugins/workflow-runner/src/controlFlow/types.ts +0 -63
- package/plugins/workflow-runner/src/dslManifest.js +0 -72
- package/plugins/workflow-runner/src/dslManifest.ts +0 -127
- package/plugins/workflow-runner/src/expressions.js +0 -618
- package/plugins/workflow-runner/src/expressions.ts +0 -622
- package/plugins/workflow-runner/src/loader.js +0 -573
- package/plugins/workflow-runner/src/loader.ts +0 -721
- package/plugins/workflow-runner/src/multimodal.js +0 -38
- package/plugins/workflow-runner/src/multimodal.ts +0 -64
- package/plugins/workflow-runner/src/recovery.js +0 -119
- package/plugins/workflow-runner/src/recovery.ts +0 -149
- package/plugins/workflow-runner/src/stepExecutors/agent.js +0 -173
- package/plugins/workflow-runner/src/stepExecutors/agent.ts +0 -234
- package/plugins/workflow-runner/src/stepExecutors/assert.js +0 -41
- package/plugins/workflow-runner/src/stepExecutors/assert.ts +0 -50
- package/plugins/workflow-runner/src/stepExecutors/echo.js +0 -31
- package/plugins/workflow-runner/src/stepExecutors/echo.ts +0 -38
- package/plugins/workflow-runner/src/stepExecutors/llm.js +0 -99
- package/plugins/workflow-runner/src/stepExecutors/llm.ts +0 -133
- package/plugins/workflow-runner/src/stepExecutors/set.js +0 -49
- package/plugins/workflow-runner/src/stepExecutors/set.ts +0 -56
- package/plugins/workflow-runner/src/stepExecutors/skill.js +0 -111
- package/plugins/workflow-runner/src/stepExecutors/skill.ts +0 -140
- package/plugins/workflow-runner/src/stepExecutors/tool.js +0 -208
- package/plugins/workflow-runner/src/stepExecutors/tool.ts +0 -256
- package/plugins/workflow-runner/src/stepExecutors/verify.js +0 -58
- package/plugins/workflow-runner/src/stepExecutors/verify.ts +0 -68
- package/plugins/workflow-runner/src/stepPipeline.js +0 -277
- package/plugins/workflow-runner/src/stepPipeline.ts +0 -366
- package/plugins/workflow-runner/test/agent.test.ts +0 -587
- package/plugins/workflow-runner/test/agentLlmError.test.ts +0 -373
- package/plugins/workflow-runner/test/bashBlacklist.sweep.test.ts +0 -65
- package/plugins/workflow-runner/test/captureMisbind.test.ts +0 -99
- package/plugins/workflow-runner/test/cli.e2e.test.ts +0 -147
- package/plugins/workflow-runner/test/context-files.test.ts +0 -320
- package/plugins/workflow-runner/test/e2e.test.ts +0 -370
- package/plugins/workflow-runner/test/engineRobustness.test.ts +0 -226
- package/plugins/workflow-runner/test/expressions.test.ts +0 -397
- package/plugins/workflow-runner/test/foundation.test.ts +0 -126
- package/plugins/workflow-runner/test/graceful.test.ts +0 -139
- package/plugins/workflow-runner/test/greedyInputs.load.test.ts +0 -47
- package/plugins/workflow-runner/test/hostCapability.test.ts +0 -108
- package/plugins/workflow-runner/test/illustrate.load.test.ts +0 -183
- package/plugins/workflow-runner/test/jsonPayload.test.ts +0 -83
- package/plugins/workflow-runner/test/loader.test.ts +0 -617
- package/plugins/workflow-runner/test/maxTurnsRetryDelay.test.ts +0 -33
- package/plugins/workflow-runner/test/multimodal.test.ts +0 -342
- package/plugins/workflow-runner/test/output-schema.test.ts +0 -213
- package/plugins/workflow-runner/test/parseWorkflowArgs.test.ts +0 -87
- package/plugins/workflow-runner/test/pluginRunner.isolation.test.ts +0 -269
- package/plugins/workflow-runner/test/ppt.load.test.ts +0 -1220
- package/plugins/workflow-runner/test/ralphLoop.e2e.test.ts +0 -220
- package/plugins/workflow-runner/test/recovery.test.ts +0 -265
- package/plugins/workflow-runner/test/runner.test.ts +0 -911
- package/plugins/workflow-runner/test/runnerPreflight.test.ts +0 -379
- package/plugins/workflow-runner/test/skillTurnDone.test.ts +0 -71
- package/plugins/workflow-runner/test/stateLifecycle.test.ts +0 -150
- package/plugins/workflow-runner/test/stepPipeline.test.ts +0 -114
- package/plugins/workflow-runner/test/taskArbitration.test.ts +0 -338
- package/plugins/workflow-runner/test/taskLifecycle.test.ts +0 -270
- package/plugins/workflow-runner/test/toolArtifactOverwrite.test.ts +0 -221
- package/plugins/workflow-runner/test/toolCapabilityBoundary.test.ts +0 -29
- package/plugins/workflow-runner/test/toolRuntimeIntegration.test.ts +0 -168
- package/plugins/workflow-runner/test/webppt.load.test.ts +0 -250
- package/plugins/workflow-runner/test/word.load.test.ts +0 -208
- package/plugins/workflow-runner/test/workflows.smoke.test.ts +0 -76
- package/plugins/workflow-runner/test/yamlGuardConditions.test.ts +0 -207
- package/src/advisorState.js +0 -22
- package/src/cli/configWizard.js +0 -293
- package/src/tools/toolProgressBridge.js +0 -48
- package/workflows/adaptive-research.yaml +0 -50
- package/workflows/book-review-short.yaml +0 -125
- package/workflows/e2e-write-greet.yaml +0 -31
- package/workflows/illustrate.yaml +0 -227
- package/workflows/plan.yaml +0 -470
- package/workflows/ppt.yaml +0 -1670
- package/workflows/ralph-loop.yaml +0 -199
- package/workflows/schema.json +0 -176
- package/workflows/webppt.yaml +0 -496
- package/workflows/wechat-reading-shots.yaml +0 -315
- package/workflows/word.yaml +0 -720
- package/workflows/youtube-shorts.yaml +0 -202
package/src/ui/App.tsx
CHANGED
|
@@ -7,47 +7,67 @@
|
|
|
7
7
|
* ============================================================
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
import React, { useDeferredValue } from 'react';
|
|
11
10
|
import { Box, Text, useApp } from 'ink';
|
|
11
|
+
import React, { useDeferredValue } from 'react';
|
|
12
12
|
|
|
13
13
|
import { getWorkingDir } from '../bootstrap/workingDir.js';
|
|
14
|
+
import {
|
|
15
|
+
createCliComputerRun,
|
|
16
|
+
readRecentComputerUseAudit,
|
|
17
|
+
} from '../cli/computerUse.js';
|
|
14
18
|
import { ConfigMenu } from '../cli/configMenu.js';
|
|
19
|
+
import type { ModelConfig } from '../config/configFile.js';
|
|
15
20
|
import { configService } from '../config/configService.js';
|
|
16
21
|
import { listArchives, restoreArchive } from '../context/archive.js';
|
|
17
22
|
import { saveContext } from '../context/persistContext.js';
|
|
18
23
|
import { sessionFileFor } from '../context/sessionPath.js';
|
|
19
|
-
import { workspaceIndexService, type WorkspaceRecord } from '../context/workspaceIndex.js';
|
|
20
|
-
import { memoryService } from '../memory/memoryService.js';
|
|
21
24
|
import {
|
|
25
|
+
type SessionSummary,
|
|
22
26
|
createSession,
|
|
23
27
|
listSessionSummaries,
|
|
24
28
|
updateSessionMetadata,
|
|
25
|
-
type SessionSummary,
|
|
26
29
|
} from '../context/sessionRegistry.js';
|
|
27
|
-
import
|
|
28
|
-
|
|
30
|
+
import {
|
|
31
|
+
type WorkspaceRecord,
|
|
32
|
+
workspaceIndexService,
|
|
33
|
+
} from '../context/workspaceIndex.js';
|
|
34
|
+
import { memoryService } from '../memory/memoryService.js';
|
|
35
|
+
import { legacyModelId, modelRefFromLegacy } from '../modelRef.js';
|
|
29
36
|
import { activatePlugins } from '../plugins/pluginLifecycle.js';
|
|
30
37
|
import { runAgentTurn } from '../session/runAgentTurn.js';
|
|
31
|
-
import { writeClipboardText } from '../utils/clipboardImage.js';
|
|
32
38
|
import {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
39
|
+
type OnlineModelCatalogItem,
|
|
40
|
+
beginTeamLogin,
|
|
41
|
+
clearTeamCredentials,
|
|
42
|
+
fetchOnlineModels,
|
|
43
|
+
fetchTeamSubscription,
|
|
44
|
+
fetchTeamUsage,
|
|
45
|
+
loadTeamCredentials,
|
|
46
|
+
revokeTeamCredentials,
|
|
47
|
+
} from '../team/auth.js';
|
|
36
48
|
import { computerUseService } from '../tools/computeruse/computerUseService.js';
|
|
37
|
-
import {
|
|
49
|
+
import type { Message, Provider } from '../types.js';
|
|
50
|
+
import { writeClipboardText } from '../utils/clipboardImage.js';
|
|
38
51
|
import { AskQuestion } from './AskQuestion.js';
|
|
39
52
|
import { InputBox } from './InputBox.js';
|
|
40
53
|
import { LiveArea } from './LiveArea.js';
|
|
41
|
-
import { QueuedMessagesBanner } from './QueuedMessagesBanner.js';
|
|
42
54
|
import { MessageList } from './MessageList.js';
|
|
43
55
|
import { ModelPicker } from './ModelPicker.js';
|
|
56
|
+
import type { PickerModel } from './ModelPicker.js';
|
|
57
|
+
import { QueuedMessagesBanner } from './QueuedMessagesBanner.js';
|
|
44
58
|
import { SessionPicker } from './SessionPicker.js';
|
|
45
|
-
import { WorkspacePicker } from './WorkspacePicker.js';
|
|
46
59
|
import { StatusLine } from './StatusLine.js';
|
|
47
|
-
import {
|
|
60
|
+
import { WorkspacePicker } from './WorkspacePicker.js';
|
|
61
|
+
import { advisorHelpText, parseAdvisorCommand } from './advisorCommand.js';
|
|
48
62
|
import { parseComputerCommand } from './computerCommand.js';
|
|
49
63
|
import { useChat } from './hooks/useChat.js';
|
|
50
64
|
import { useTerminalSize } from './hooks/useTerminalSize.js';
|
|
65
|
+
import {
|
|
66
|
+
isLogoutCommand,
|
|
67
|
+
loginHelpText,
|
|
68
|
+
parseLoginCommand,
|
|
69
|
+
} from './loginCommand.js';
|
|
70
|
+
import { theme } from './theme.js';
|
|
51
71
|
|
|
52
72
|
/**
|
|
53
73
|
* ANSI escape: 清屏 + 光标归位。
|
|
@@ -68,6 +88,40 @@ function formatProviderName(apiFormat: string): string {
|
|
|
68
88
|
return apiFormat;
|
|
69
89
|
}
|
|
70
90
|
|
|
91
|
+
function toPickerModel(model: OnlineModelCatalogItem): PickerModel {
|
|
92
|
+
return {
|
|
93
|
+
id: legacyModelId({
|
|
94
|
+
source: 'gateway',
|
|
95
|
+
serverId: 'gateway',
|
|
96
|
+
modelId: model.id,
|
|
97
|
+
}),
|
|
98
|
+
apiFormat: model.apiFormat,
|
|
99
|
+
displayName: model.displayName,
|
|
100
|
+
model: model.model,
|
|
101
|
+
contextWindow: model.contextWindow,
|
|
102
|
+
maxTokens: model.maxTokens,
|
|
103
|
+
supportsVision: model.supportsVision,
|
|
104
|
+
enabled: true,
|
|
105
|
+
source: 'gateway',
|
|
106
|
+
isDefault: model.isDefault,
|
|
107
|
+
availability: model.availability,
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
async function openTeamBrowser(url: string): Promise<void> {
|
|
112
|
+
const command =
|
|
113
|
+
process.platform === 'darwin'
|
|
114
|
+
? ['open', url]
|
|
115
|
+
: process.platform === 'win32'
|
|
116
|
+
? ['cmd', '/c', 'start', '', url]
|
|
117
|
+
: ['xdg-open', url];
|
|
118
|
+
try {
|
|
119
|
+
Bun.spawn({ cmd: command, stdout: 'ignore', stderr: 'ignore' });
|
|
120
|
+
} catch {
|
|
121
|
+
/* TUI prints the URL below */
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
71
125
|
interface Props {
|
|
72
126
|
provider: Provider;
|
|
73
127
|
/** 启动时的历史(含 system prompt);可能是从 last-context.json 加载来的旧对话。 */
|
|
@@ -94,19 +148,21 @@ export function App({ provider, initialHistory }: Props): JSX.Element {
|
|
|
94
148
|
provider,
|
|
95
149
|
signal: controller.signal,
|
|
96
150
|
runAgentTurn,
|
|
97
|
-
})
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
151
|
+
})
|
|
152
|
+
.then((fn) => {
|
|
153
|
+
if (controller.signal.aborted) {
|
|
154
|
+
fn();
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
cleanup = fn;
|
|
158
|
+
})
|
|
159
|
+
.catch((err) => {
|
|
160
|
+
console.warn(
|
|
161
|
+
`[minimal-agent] plugin activation failed: ${
|
|
162
|
+
err instanceof Error ? err.message : String(err)
|
|
163
|
+
}`,
|
|
164
|
+
);
|
|
165
|
+
});
|
|
110
166
|
return () => {
|
|
111
167
|
controller.abort();
|
|
112
168
|
cleanup?.();
|
|
@@ -148,15 +204,25 @@ export function App({ provider, initialHistory }: Props): JSX.Element {
|
|
|
148
204
|
// /session /sessions /resume(裸命令)打开的选择器数据;非 null 时顶替 InputBox。
|
|
149
205
|
// 纯本地 UI 态,不需要塞进 useChat——不像 pendingAsk 那样由 askBridge 从外部注入,
|
|
150
206
|
// 这里完全由 handleSubmit 自己的分支触发/清空。
|
|
151
|
-
const [sessionPicker, setSessionPicker] = React.useState<
|
|
152
|
-
|
|
207
|
+
const [sessionPicker, setSessionPicker] = React.useState<
|
|
208
|
+
SessionSummary[] | null
|
|
209
|
+
>(null);
|
|
210
|
+
const [workspacePicker, setWorkspacePicker] = React.useState<
|
|
211
|
+
WorkspaceRecord[] | null
|
|
212
|
+
>(null);
|
|
153
213
|
|
|
154
214
|
// /config —— 硬编码菜单覆盖层,纯本地 UI 态(与 sessionPicker 同构)。
|
|
155
215
|
const [configMenuOpen, setConfigMenuOpen] = React.useState(false);
|
|
156
216
|
|
|
157
217
|
// /model —— 基座模型选择器;/advisor select —— 顾问模型选择器
|
|
158
|
-
const [baseModelPicker, setBaseModelPicker] = React.useState<{
|
|
159
|
-
|
|
218
|
+
const [baseModelPicker, setBaseModelPicker] = React.useState<{
|
|
219
|
+
models: PickerModel[];
|
|
220
|
+
currentId: string | null;
|
|
221
|
+
} | null>(null);
|
|
222
|
+
const [advisorModelPicker, setAdvisorModelPicker] = React.useState<{
|
|
223
|
+
models: PickerModel[];
|
|
224
|
+
currentId: string | null;
|
|
225
|
+
} | null>(null);
|
|
160
226
|
|
|
161
227
|
// 强制刷新计数,用于切换模型后触发 StatusLine 重渲染
|
|
162
228
|
const [, forceRerender] = React.useState(0);
|
|
@@ -173,9 +239,14 @@ export function App({ provider, initialHistory }: Props): JSX.Element {
|
|
|
173
239
|
const handleSessionCreate = React.useCallback(
|
|
174
240
|
async (title: string) => {
|
|
175
241
|
setSessionPicker(null);
|
|
176
|
-
const summary = await createSession(
|
|
242
|
+
const summary = await createSession(
|
|
243
|
+
getWorkingDir(),
|
|
244
|
+
title ? { title } : {},
|
|
245
|
+
);
|
|
177
246
|
await chat.switchSession(summary.id);
|
|
178
|
-
chat.ephemeralLine(
|
|
247
|
+
chat.ephemeralLine(
|
|
248
|
+
`[/session] 已新建并切换到 session "${summary.id}"(${summary.title})`,
|
|
249
|
+
);
|
|
179
250
|
},
|
|
180
251
|
[chat.switchSession, chat.ephemeralLine],
|
|
181
252
|
);
|
|
@@ -187,13 +258,16 @@ export function App({ provider, initialHistory }: Props): JSX.Element {
|
|
|
187
258
|
setWorkspacePicker(await workspaceIndexService.list());
|
|
188
259
|
}, [chat.isLoading]);
|
|
189
260
|
|
|
190
|
-
const handleWorkspaceSelect = React.useCallback(
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
261
|
+
const handleWorkspaceSelect = React.useCallback(
|
|
262
|
+
async (cwd: string, sessionId: string) => {
|
|
263
|
+
const switched = await chat.switchWorkspace(cwd, sessionId);
|
|
264
|
+
setWorkspacePicker(null);
|
|
265
|
+
if (switched) {
|
|
266
|
+
chat.ephemeralLine(`[/workspaces] 已切换到 ${cwd} · ${sessionId}`);
|
|
267
|
+
}
|
|
268
|
+
},
|
|
269
|
+
[chat.switchWorkspace, chat.ephemeralLine],
|
|
270
|
+
);
|
|
197
271
|
|
|
198
272
|
const handleBaseModelSelect = React.useCallback(
|
|
199
273
|
async (modelId: string | null) => {
|
|
@@ -201,16 +275,54 @@ export function App({ provider, initialHistory }: Props): JSX.Element {
|
|
|
201
275
|
if (!modelId) return;
|
|
202
276
|
const snap = await configService.getSnapshot();
|
|
203
277
|
const model = snap.config?.models.find((m) => m.id === modelId);
|
|
278
|
+
const selectedRef = modelRefFromLegacy(modelId);
|
|
279
|
+
const isGateway = selectedRef.source === 'gateway';
|
|
280
|
+
if (isGateway) {
|
|
281
|
+
const provider = await configService.providerForRef(selectedRef);
|
|
282
|
+
if (provider) {
|
|
283
|
+
const gatewayProvider =
|
|
284
|
+
provider.source === 'gateway' ? provider : null;
|
|
285
|
+
if (!gatewayProvider) return;
|
|
286
|
+
await updateSessionMetadata(getWorkingDir(), chat.activeSessionId, {
|
|
287
|
+
modelId: null,
|
|
288
|
+
preferredModel: {
|
|
289
|
+
source: 'gateway',
|
|
290
|
+
serverId: gatewayProvider.gatewayUrl,
|
|
291
|
+
modelId: selectedRef.modelId,
|
|
292
|
+
},
|
|
293
|
+
effectiveModel: {
|
|
294
|
+
source: 'gateway',
|
|
295
|
+
serverId: gatewayProvider.gatewayUrl,
|
|
296
|
+
modelId: selectedRef.modelId,
|
|
297
|
+
},
|
|
298
|
+
restoreWhenAvailable: true,
|
|
299
|
+
});
|
|
300
|
+
chat.setProvider(provider);
|
|
301
|
+
forceRerender((v) => v + 1);
|
|
302
|
+
chat.ephemeralLine(
|
|
303
|
+
`[/model] 当前 Session 已切换为在线模型 ${provider.name},下一轮对话生效。`,
|
|
304
|
+
);
|
|
305
|
+
}
|
|
306
|
+
return;
|
|
307
|
+
}
|
|
204
308
|
if (!model) {
|
|
205
309
|
chat.ephemeralLine(`[/model] 未找到模型 "${modelId}"`);
|
|
206
310
|
return;
|
|
207
311
|
}
|
|
208
312
|
const newProvider = await configService.providerForModel(modelId);
|
|
209
313
|
if (newProvider) {
|
|
210
|
-
await updateSessionMetadata(getWorkingDir(), chat.activeSessionId, {
|
|
314
|
+
await updateSessionMetadata(getWorkingDir(), chat.activeSessionId, {
|
|
315
|
+
modelId: null,
|
|
316
|
+
preferredModel: { source: 'local', modelId },
|
|
317
|
+
effectiveModel: { source: 'local', modelId },
|
|
318
|
+
fallbackReason: undefined,
|
|
319
|
+
restoreWhenAvailable: false,
|
|
320
|
+
});
|
|
211
321
|
chat.setProvider(newProvider);
|
|
212
322
|
forceRerender((v) => v + 1);
|
|
213
|
-
chat.ephemeralLine(
|
|
323
|
+
chat.ephemeralLine(
|
|
324
|
+
`[/model] 当前 Session 已切换为 ${model.displayName || model.model}(${formatProviderName(model.apiFormat)}),下一轮对话生效。`,
|
|
325
|
+
);
|
|
214
326
|
}
|
|
215
327
|
},
|
|
216
328
|
[chat.activeSessionId, chat.setProvider, chat.ephemeralLine],
|
|
@@ -218,23 +330,120 @@ export function App({ provider, initialHistory }: Props): JSX.Element {
|
|
|
218
330
|
|
|
219
331
|
React.useEffect(() => {
|
|
220
332
|
let cancelled = false;
|
|
221
|
-
void listSessionSummaries(getWorkingDir())
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
333
|
+
void listSessionSummaries(getWorkingDir())
|
|
334
|
+
.then(async (sessions) => {
|
|
335
|
+
const currentSession = sessions.find(
|
|
336
|
+
(session) => session.id === chat.activeSessionId,
|
|
337
|
+
);
|
|
338
|
+
const preferredModel =
|
|
339
|
+
currentSession?.preferredModel ??
|
|
340
|
+
(currentSession?.modelId
|
|
341
|
+
? modelRefFromLegacy(currentSession.modelId)
|
|
342
|
+
: null);
|
|
343
|
+
let provider: Provider | null = null;
|
|
344
|
+
let fallbackReason: import('../modelRef.js').ModelFallbackReason = null;
|
|
345
|
+
if (preferredModel) {
|
|
346
|
+
const resolved = await configService.resolveModelRef(preferredModel);
|
|
347
|
+
provider = resolved.provider;
|
|
348
|
+
fallbackReason = resolved.fallbackReason;
|
|
349
|
+
if (!provider && preferredModel.source === 'gateway')
|
|
350
|
+
provider = await configService.providerForModel(null);
|
|
351
|
+
} else {
|
|
352
|
+
provider = await configService.onlineDefaultProvider();
|
|
353
|
+
if (!provider) provider = await configService.providerForModel(null);
|
|
354
|
+
}
|
|
355
|
+
if (cancelled) return;
|
|
356
|
+
if (provider) {
|
|
357
|
+
const localModelId =
|
|
358
|
+
currentSession?.preferredModel?.source === 'local'
|
|
359
|
+
? currentSession.preferredModel.modelId
|
|
360
|
+
: currentSession?.modelId &&
|
|
361
|
+
!currentSession.modelId.startsWith('online:')
|
|
362
|
+
? currentSession.modelId
|
|
363
|
+
: ((await configService.getSnapshot()).config?.baseModelId ??
|
|
364
|
+
provider.model);
|
|
365
|
+
const effectiveModel =
|
|
366
|
+
provider.source === 'gateway'
|
|
367
|
+
? {
|
|
368
|
+
source: 'gateway' as const,
|
|
369
|
+
serverId: provider.gatewayUrl ?? 'gateway',
|
|
370
|
+
modelId: provider.remoteModelId ?? provider.model,
|
|
371
|
+
}
|
|
372
|
+
: { source: 'local' as const, modelId: localModelId };
|
|
373
|
+
const isGatewayPreference = preferredModel?.source === 'gateway';
|
|
374
|
+
const shouldPersist =
|
|
375
|
+
(provider.source === 'gateway' && !preferredModel) ||
|
|
376
|
+
(isGatewayPreference &&
|
|
377
|
+
(currentSession?.effectiveModel?.source !==
|
|
378
|
+
effectiveModel.source ||
|
|
379
|
+
currentSession?.effectiveModel?.modelId !==
|
|
380
|
+
effectiveModel.modelId ||
|
|
381
|
+
currentSession.fallbackReason !== fallbackReason));
|
|
382
|
+
chat.setProvider(provider);
|
|
383
|
+
if (
|
|
384
|
+
shouldPersist ||
|
|
385
|
+
(isGatewayPreference && provider.source === 'local')
|
|
386
|
+
) {
|
|
387
|
+
await updateSessionMetadata(getWorkingDir(), chat.activeSessionId, {
|
|
388
|
+
...(provider.source === 'gateway' && !preferredModel
|
|
389
|
+
? { preferredModel: effectiveModel }
|
|
390
|
+
: {}),
|
|
391
|
+
effectiveModel,
|
|
392
|
+
fallbackReason:
|
|
393
|
+
provider.source === 'local' ? fallbackReason : null,
|
|
394
|
+
restoreWhenAvailable:
|
|
395
|
+
isGatewayPreference || provider.source === 'gateway',
|
|
396
|
+
}).catch(() => {});
|
|
397
|
+
if (isGatewayPreference && provider.source === 'local')
|
|
398
|
+
chat.ephemeralLine(
|
|
399
|
+
`[/model] 在线模型暂不可用,已回退本地模型(${fallbackReason ?? 'offline'});服务恢复后将自动恢复。`,
|
|
400
|
+
);
|
|
401
|
+
else if (
|
|
402
|
+
isGatewayPreference &&
|
|
403
|
+
provider.source === 'gateway' &&
|
|
404
|
+
currentSession?.fallbackReason
|
|
405
|
+
)
|
|
406
|
+
chat.ephemeralLine(
|
|
407
|
+
`[/model] 在线模型已恢复,当前 Session 已恢复在线模型 ${provider.name}。`,
|
|
408
|
+
);
|
|
409
|
+
}
|
|
410
|
+
} else if (preferredModel?.source === 'gateway') {
|
|
411
|
+
chat.ephemeralLine(
|
|
412
|
+
'[/model] 在线模型不可用,且未配置本地模型;请连接团队服务器或配置本地模型。',
|
|
413
|
+
);
|
|
414
|
+
}
|
|
415
|
+
})
|
|
416
|
+
.catch(() => {});
|
|
417
|
+
return () => {
|
|
418
|
+
cancelled = true;
|
|
419
|
+
};
|
|
420
|
+
}, [chat.activeSessionId, chat.setProvider, chat.ephemeralLine]);
|
|
229
421
|
|
|
230
422
|
const handleAdvisorModelSelect = React.useCallback(
|
|
231
423
|
async (modelId: string | null) => {
|
|
232
424
|
setAdvisorModelPicker(null);
|
|
233
425
|
if (modelId === null) {
|
|
234
|
-
await configService.savePatch({
|
|
426
|
+
await configService.savePatch({
|
|
427
|
+
advisor: { modelId: null, enabled: false },
|
|
428
|
+
});
|
|
235
429
|
chat.ephemeralLine('[/advisor] 已关闭顾问模型。');
|
|
236
430
|
return;
|
|
237
431
|
}
|
|
432
|
+
const selectedRef = modelRefFromLegacy(modelId);
|
|
433
|
+
if (selectedRef.source === 'gateway') {
|
|
434
|
+
const provider = await configService.providerForRef(selectedRef);
|
|
435
|
+
if (!provider) {
|
|
436
|
+
chat.ephemeralLine(
|
|
437
|
+
'[/advisor] 在线模型不可用、未授权或设备凭据已过期。',
|
|
438
|
+
);
|
|
439
|
+
return;
|
|
440
|
+
}
|
|
441
|
+
await configService.savePatch({ advisor: { modelId, enabled: true } });
|
|
442
|
+
chat.ephemeralLine(
|
|
443
|
+
`[/advisor] 已设置顾问模型为 ${provider.name}(在线)。`,
|
|
444
|
+
);
|
|
445
|
+
return;
|
|
446
|
+
}
|
|
238
447
|
const snap = await configService.getSnapshot();
|
|
239
448
|
const model = snap.config?.models.find((m) => m.id === modelId);
|
|
240
449
|
if (!model) {
|
|
@@ -242,7 +451,9 @@ export function App({ provider, initialHistory }: Props): JSX.Element {
|
|
|
242
451
|
return;
|
|
243
452
|
}
|
|
244
453
|
await configService.savePatch({ advisor: { modelId, enabled: true } });
|
|
245
|
-
chat.ephemeralLine(
|
|
454
|
+
chat.ephemeralLine(
|
|
455
|
+
`[/advisor] 已设置顾问模型为 ${model.displayName || model.model}(${formatProviderName(model.apiFormat)})。`,
|
|
456
|
+
);
|
|
246
457
|
},
|
|
247
458
|
[chat.ephemeralLine],
|
|
248
459
|
);
|
|
@@ -270,7 +481,9 @@ export function App({ provider, initialHistory }: Props): JSX.Element {
|
|
|
270
481
|
async (text: string) => {
|
|
271
482
|
const trimmed = text.trim();
|
|
272
483
|
const isBareSessionCmd =
|
|
273
|
-
trimmed === '/session' ||
|
|
484
|
+
trimmed === '/session' ||
|
|
485
|
+
trimmed === '/sessions' ||
|
|
486
|
+
trimmed === '/resume';
|
|
274
487
|
const sessionArgMatch = /^\/(?:session|resume)\s+(.+)$/.exec(trimmed);
|
|
275
488
|
|
|
276
489
|
if (trimmed === '/workspaces') {
|
|
@@ -279,10 +492,19 @@ export function App({ provider, initialHistory }: Props): JSX.Element {
|
|
|
279
492
|
}
|
|
280
493
|
|
|
281
494
|
if (trimmed === '/copy') {
|
|
282
|
-
const latest = [...historyRef.current]
|
|
283
|
-
(
|
|
284
|
-
|
|
285
|
-
|
|
495
|
+
const latest = [...historyRef.current]
|
|
496
|
+
.reverse()
|
|
497
|
+
.find(
|
|
498
|
+
(message) =>
|
|
499
|
+
message.role === 'assistant' &&
|
|
500
|
+
typeof message.content === 'string' &&
|
|
501
|
+
message.content.length > 0,
|
|
502
|
+
);
|
|
503
|
+
if (
|
|
504
|
+
!latest ||
|
|
505
|
+
latest.role !== 'assistant' ||
|
|
506
|
+
typeof latest.content !== 'string'
|
|
507
|
+
) {
|
|
286
508
|
chat.ephemeralLine('[/copy] 当前会话还没有可复制的 AI 最终回复。');
|
|
287
509
|
return;
|
|
288
510
|
}
|
|
@@ -290,7 +512,9 @@ export function App({ provider, initialHistory }: Props): JSX.Element {
|
|
|
290
512
|
await writeClipboardText(latest.content);
|
|
291
513
|
chat.ephemeralLine('[/copy] 已复制最近一条 AI 最终回复。');
|
|
292
514
|
} catch (error) {
|
|
293
|
-
chat.ephemeralLine(
|
|
515
|
+
chat.ephemeralLine(
|
|
516
|
+
`[/copy] ${error instanceof Error ? error.message : String(error)}`,
|
|
517
|
+
);
|
|
294
518
|
}
|
|
295
519
|
return;
|
|
296
520
|
}
|
|
@@ -302,7 +526,7 @@ export function App({ provider, initialHistory }: Props): JSX.Element {
|
|
|
302
526
|
if (sub === 'help') {
|
|
303
527
|
const helperAvailable = computerUseService.available();
|
|
304
528
|
let helperBackend = '未知';
|
|
305
|
-
|
|
529
|
+
const modeLabel = chat.computerUseMode;
|
|
306
530
|
try {
|
|
307
531
|
const workspace = await workspaceIndexService
|
|
308
532
|
.findByPath(getWorkingDir())
|
|
@@ -315,7 +539,9 @@ export function App({ provider, initialHistory }: Props): JSX.Element {
|
|
|
315
539
|
allowedFileRoots: [getWorkingDir()],
|
|
316
540
|
supervised: true,
|
|
317
541
|
});
|
|
318
|
-
const resp = await computerUseService.request(context, {
|
|
542
|
+
const resp = await computerUseService.request(context, {
|
|
543
|
+
action: 'status',
|
|
544
|
+
});
|
|
319
545
|
if (resp.ok && resp.state?.backend) {
|
|
320
546
|
helperBackend = resp.state.backend;
|
|
321
547
|
}
|
|
@@ -432,7 +658,7 @@ export function App({ provider, initialHistory }: Props): JSX.Element {
|
|
|
432
658
|
.map((window) => window.appId)
|
|
433
659
|
.filter((id, index, all) => all.indexOf(id) === index);
|
|
434
660
|
chat.ephemeralLine(
|
|
435
|
-
`[/computer] 可见应用:${apps?.length ? apps.join('、') : response.content ?? '当前没有可见应用'}`,
|
|
661
|
+
`[/computer] 可见应用:${apps?.length ? apps.join('、') : (response.content ?? '当前没有可见应用')}`,
|
|
436
662
|
);
|
|
437
663
|
return;
|
|
438
664
|
}
|
|
@@ -444,11 +670,7 @@ export function App({ provider, initialHistory }: Props): JSX.Element {
|
|
|
444
670
|
}
|
|
445
671
|
// status
|
|
446
672
|
chat.ephemeralLine(
|
|
447
|
-
`[/computer] 状态:\n
|
|
448
|
-
` 模式:${chat.computerUseMode}\n` +
|
|
449
|
-
` Helper:${computerUseService.available() ? '✅ 可用' : '❌ 不可用'}\n` +
|
|
450
|
-
` Backend:${response.state?.backend ?? '未知'}\n` +
|
|
451
|
-
` 提示:输入 /computer help 查看所有可用命令`,
|
|
673
|
+
`[/computer] 状态:\n 模式:${chat.computerUseMode}\n Helper:${computerUseService.available() ? '✅ 可用' : '❌ 不可用'}\n Backend:${response.state?.backend ?? '未知'}\n 提示:输入 /computer help 查看所有可用命令`,
|
|
452
674
|
);
|
|
453
675
|
return;
|
|
454
676
|
}
|
|
@@ -461,20 +683,30 @@ export function App({ provider, initialHistory }: Props): JSX.Element {
|
|
|
461
683
|
}
|
|
462
684
|
|
|
463
685
|
if (trimmed === '/memories') {
|
|
464
|
-
const workspace = await workspaceIndexService
|
|
686
|
+
const workspace = await workspaceIndexService
|
|
687
|
+
.findByPath(getWorkingDir())
|
|
688
|
+
.catch(() => null);
|
|
465
689
|
const records = await memoryService.recall('', workspace?.id, 100);
|
|
466
690
|
if (!records.length) {
|
|
467
691
|
chat.ephemeralLine('[/memories] 当前没有本机长期记忆。');
|
|
468
692
|
return;
|
|
469
693
|
}
|
|
470
|
-
chat.ephemeralLine(
|
|
471
|
-
`
|
|
694
|
+
chat.ephemeralLine(
|
|
695
|
+
`[/memories] 本机长期记忆:\n${records
|
|
696
|
+
.map(
|
|
697
|
+
(record) =>
|
|
698
|
+
` ${record.id} · ${record.scope === 'personal' ? '个人' : '工作区'} · ${record.subject}: ${record.statement}`,
|
|
699
|
+
)
|
|
700
|
+
.join('\n')}\n使用 /forget-memory <id> 删除一条记忆。`,
|
|
701
|
+
);
|
|
472
702
|
return;
|
|
473
703
|
}
|
|
474
704
|
|
|
475
705
|
if (trimmed.startsWith('/forget-memory ')) {
|
|
476
706
|
const id = trimmed.slice('/forget-memory '.length).trim();
|
|
477
|
-
const workspace = await workspaceIndexService
|
|
707
|
+
const workspace = await workspaceIndexService
|
|
708
|
+
.findByPath(getWorkingDir())
|
|
709
|
+
.catch(() => null);
|
|
478
710
|
const records = await memoryService.recall('', workspace?.id, 100);
|
|
479
711
|
const record = records.find((item) => item.id === id);
|
|
480
712
|
if (!record) {
|
|
@@ -520,7 +752,9 @@ export function App({ provider, initialHistory }: Props): JSX.Element {
|
|
|
520
752
|
)
|
|
521
753
|
.join('\n');
|
|
522
754
|
const tail =
|
|
523
|
-
entries.length > 30
|
|
755
|
+
entries.length > 30
|
|
756
|
+
? `\n ...(共 ${entries.length} 条,仅显示前 30)`
|
|
757
|
+
: '';
|
|
524
758
|
chat.ephemeralLine(
|
|
525
759
|
`[/list-archive] 归档列表(最新优先):\n${lines}${tail}`,
|
|
526
760
|
);
|
|
@@ -540,7 +774,10 @@ export function App({ provider, initialHistory }: Props): JSX.Element {
|
|
|
540
774
|
return;
|
|
541
775
|
}
|
|
542
776
|
// 覆盖当前 active session 文件;强制 reload 让 history 替换
|
|
543
|
-
const targetFile = sessionFileFor(
|
|
777
|
+
const targetFile = sessionFileFor(
|
|
778
|
+
getWorkingDir(),
|
|
779
|
+
chat.activeSessionId,
|
|
780
|
+
);
|
|
544
781
|
await saveContext(messages, targetFile);
|
|
545
782
|
await chat.switchSession(chat.activeSessionId, { force: true });
|
|
546
783
|
chat.ephemeralLine(
|
|
@@ -571,45 +808,226 @@ export function App({ provider, initialHistory }: Props): JSX.Element {
|
|
|
571
808
|
return;
|
|
572
809
|
}
|
|
573
810
|
|
|
574
|
-
//
|
|
575
|
-
//
|
|
576
|
-
//
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
811
|
+
// ⚠️ 这里曾有一个 `/plan` 的硬编码特例(改写成 `/workflow plan`)。
|
|
812
|
+
// 已删 —— `src/plugins/slashFallback.ts` 把它通用化了:`/xxx` 没命中插件
|
|
813
|
+
// 命令时会按「工具 → skill → 工作流目录」继续找,所以 `/plan`、`/research`、
|
|
814
|
+
// `/pptx` 全都自动可用,加一条工作流不用再回来改 TUI。
|
|
815
|
+
|
|
816
|
+
// /model —— 列出已配置的基座模型并切换选择器;/model <id> 直接切换
|
|
817
|
+
// /login /logout —— 团队在线模型登录;不登录不影响本机模型(原 /team,改名理由见 loginCommand.ts)。
|
|
818
|
+
if (isLogoutCommand(trimmed)) {
|
|
819
|
+
// A standalone session can retain an old auth file from a previous
|
|
820
|
+
// team run. Only revoke over the network when the credential matches
|
|
821
|
+
// the currently configured server; otherwise just clear it locally.
|
|
822
|
+
const configuredServer = await configService.resolveTeamServerUrl();
|
|
823
|
+
if (configuredServer.value) {
|
|
824
|
+
const credentials = await loadTeamCredentials(configuredServer.value);
|
|
825
|
+
if (credentials) await revokeTeamCredentials(credentials);
|
|
826
|
+
else await clearTeamCredentials();
|
|
827
|
+
} else {
|
|
828
|
+
// No configured server is the standalone boundary. Do not let
|
|
829
|
+
// revokeTeamCredentials() discover and contact a stale auth file.
|
|
830
|
+
await clearTeamCredentials();
|
|
831
|
+
}
|
|
832
|
+
chat.ephemeralLine(
|
|
833
|
+
'[/logout] 已登出并撤销本机设备授权;本地模型配置和 Session 未受影响。',
|
|
834
|
+
);
|
|
835
|
+
return;
|
|
836
|
+
}
|
|
837
|
+
// 发起一次登录尝试。普通用户只使用发行版/配置中的服务端地址。
|
|
838
|
+
const attemptTeamLogin = async (server?: string, sourceNote?: string) => {
|
|
839
|
+
if (sourceNote) chat.ephemeralLine(`[/login] ${sourceNote}`);
|
|
840
|
+
try {
|
|
841
|
+
const resolved = server
|
|
842
|
+
? { value: server, source: 'explicit' }
|
|
843
|
+
: await configService.resolveTeamServerUrl();
|
|
844
|
+
if (!resolved.value)
|
|
845
|
+
throw new Error('KakadeAI 登录服务暂时不可用,请稍后重试');
|
|
846
|
+
const login = await beginTeamLogin(
|
|
847
|
+
resolved.value,
|
|
848
|
+
'minimal-agent TUI',
|
|
849
|
+
);
|
|
580
850
|
chat.ephemeralLine(
|
|
581
|
-
|
|
851
|
+
`[/login] 已打开浏览器授权:${login.authorizeUrl}`,
|
|
582
852
|
);
|
|
853
|
+
await openTeamBrowser(login.authorizeUrl);
|
|
854
|
+
await login.complete();
|
|
855
|
+
chat.ephemeralLine(
|
|
856
|
+
'[/login] 登录完成;使用 /login models 查看在线模型。',
|
|
857
|
+
);
|
|
858
|
+
} catch (error) {
|
|
859
|
+
chat.ephemeralLine(
|
|
860
|
+
`[/login] ${error instanceof Error ? error.message : String(error)}`,
|
|
861
|
+
);
|
|
862
|
+
}
|
|
863
|
+
};
|
|
864
|
+
const loginCommand = parseLoginCommand(trimmed);
|
|
865
|
+
if (loginCommand.matched) {
|
|
866
|
+
if (loginCommand.command === 'help') {
|
|
867
|
+
chat.ephemeralLine(loginHelpText());
|
|
868
|
+
return;
|
|
869
|
+
}
|
|
870
|
+
if (loginCommand.command === 'login') {
|
|
871
|
+
await attemptTeamLogin(loginCommand.server);
|
|
872
|
+
return;
|
|
873
|
+
}
|
|
874
|
+
const configuredServer = await configService.resolveTeamServerUrl();
|
|
875
|
+
const credentials = configuredServer.value
|
|
876
|
+
? await loadTeamCredentials(configuredServer.value)
|
|
877
|
+
: null;
|
|
878
|
+
if (loginCommand.command === 'models') {
|
|
879
|
+
if (!credentials) {
|
|
880
|
+
chat.ephemeralLine(
|
|
881
|
+
'[/login models] 未登录;当前仅使用本机模型。输入 /login 登录。',
|
|
882
|
+
);
|
|
883
|
+
return;
|
|
884
|
+
}
|
|
885
|
+
try {
|
|
886
|
+
const models = await fetchOnlineModels(credentials);
|
|
887
|
+
chat.ephemeralLine(
|
|
888
|
+
models.length
|
|
889
|
+
? models
|
|
890
|
+
.map(
|
|
891
|
+
(model) =>
|
|
892
|
+
`${model.displayName} · ${model.model}${model.isDefault ? ' · 默认' : ''}${model.availability?.reason ? ` · ${model.availability.reason}` : ''}`,
|
|
893
|
+
)
|
|
894
|
+
.join('\n')
|
|
895
|
+
: '没有可用在线模型。',
|
|
896
|
+
);
|
|
897
|
+
} catch (error) {
|
|
898
|
+
chat.ephemeralLine(
|
|
899
|
+
`[/login models] ${error instanceof Error ? error.message : String(error)}`,
|
|
900
|
+
);
|
|
901
|
+
}
|
|
902
|
+
return;
|
|
903
|
+
}
|
|
904
|
+
if (loginCommand.command === 'usage') {
|
|
905
|
+
if (!credentials) {
|
|
906
|
+
chat.ephemeralLine('[/login usage] 未登录。');
|
|
907
|
+
return;
|
|
908
|
+
}
|
|
909
|
+
try {
|
|
910
|
+
const subscription = await fetchTeamSubscription(credentials);
|
|
911
|
+
const usage = await fetchTeamUsage(credentials);
|
|
912
|
+
chat.ephemeralLine(
|
|
913
|
+
subscription
|
|
914
|
+
? `[/login usage] ${subscription.planName} · ${subscription.status}\n请求 ${subscription.requestCount}/${subscription.requestLimit} · 输入 ${subscription.inputTokens}/${subscription.inputTokenLimit} · 输出 ${subscription.outputTokens}/${subscription.outputTokenLimit}\n模型记录 ${usage.length} 条`
|
|
915
|
+
: '[/login usage] 当前没有有效订阅。',
|
|
916
|
+
);
|
|
917
|
+
} catch (error) {
|
|
918
|
+
chat.ephemeralLine(
|
|
919
|
+
`[/login usage] ${error instanceof Error ? error.message : String(error)}`,
|
|
920
|
+
);
|
|
921
|
+
}
|
|
922
|
+
return;
|
|
923
|
+
}
|
|
924
|
+
if (loginCommand.command === 'status') {
|
|
925
|
+
if (!credentials) {
|
|
926
|
+
chat.ephemeralLine(
|
|
927
|
+
'[/login status] 未登录;当前仅使用本机模型。输入 /login 登录。',
|
|
928
|
+
);
|
|
929
|
+
} else {
|
|
930
|
+
try {
|
|
931
|
+
const online = await fetchOnlineModels(credentials);
|
|
932
|
+
chat.ephemeralLine(
|
|
933
|
+
`[/login status] 已连接 ${credentials.serverUrl};可用在线模型 ${online.length} 个。/logout 登出。`,
|
|
934
|
+
);
|
|
935
|
+
} catch (error) {
|
|
936
|
+
chat.ephemeralLine(
|
|
937
|
+
`[/login status] 设备凭据存在,但在线服务不可用:${error instanceof Error ? error.message : String(error)};当前使用本机模型。`,
|
|
938
|
+
);
|
|
939
|
+
}
|
|
940
|
+
}
|
|
941
|
+
return;
|
|
942
|
+
}
|
|
943
|
+
if (loginCommand.command === 'tools') {
|
|
944
|
+
const current = (await listSessionSummaries(getWorkingDir())).find(
|
|
945
|
+
(session) => session.id === chat.activeSessionId,
|
|
946
|
+
);
|
|
947
|
+
const value = loginCommand.value;
|
|
948
|
+
if (!value || value === 'list') {
|
|
949
|
+
chat.ephemeralLine(
|
|
950
|
+
`[/login tools] 当前 Session 类别:${current?.onlineToolCategories?.join(', ') || '未设置(使用服务器模型预设)'}\n可选:workspace web computer subagents integrations\n设置:/login tools workspace,web`,
|
|
951
|
+
);
|
|
952
|
+
} else {
|
|
953
|
+
const categories = [
|
|
954
|
+
...new Set(value.split(/[ ,]+/).filter(Boolean)),
|
|
955
|
+
];
|
|
956
|
+
const allowed = new Set([
|
|
957
|
+
'workspace',
|
|
958
|
+
'web',
|
|
959
|
+
'computer',
|
|
960
|
+
'subagents',
|
|
961
|
+
'integrations',
|
|
962
|
+
]);
|
|
963
|
+
if (categories.some((category) => !allowed.has(category))) {
|
|
964
|
+
chat.ephemeralLine(
|
|
965
|
+
'[/login tools] 类别无效,可用:workspace web computer subagents integrations',
|
|
966
|
+
);
|
|
967
|
+
return;
|
|
968
|
+
}
|
|
969
|
+
await updateSessionMetadata(getWorkingDir(), chat.activeSessionId, {
|
|
970
|
+
onlineToolCategories: categories,
|
|
971
|
+
});
|
|
972
|
+
chat.ephemeralLine(
|
|
973
|
+
`[/login tools] 已设置当前 Session 工具类别:${categories.join(', ') || '无'}`,
|
|
974
|
+
);
|
|
975
|
+
}
|
|
583
976
|
return;
|
|
584
977
|
}
|
|
585
|
-
void chat.submit(`/workflow plan "${goal}"`);
|
|
586
|
-
return;
|
|
587
978
|
}
|
|
588
979
|
|
|
589
|
-
// /model —— 列出已配置的基座模型并切换选择器;/model <id> 直接切换
|
|
590
980
|
const modelArgMatch = /^\/model(?:\s+(.+))?$/.exec(trimmed);
|
|
591
981
|
if (modelArgMatch) {
|
|
592
982
|
const arg = modelArgMatch[1]?.trim();
|
|
593
983
|
const models = await configService.models();
|
|
594
|
-
const enabledModels = models.filter(
|
|
595
|
-
|
|
596
|
-
|
|
984
|
+
const enabledModels: PickerModel[] = models.filter(
|
|
985
|
+
(m) => m.enabled && m.apiKey,
|
|
986
|
+
);
|
|
987
|
+
const configuredServer = await configService.resolveTeamServerUrl();
|
|
988
|
+
const credentials = configuredServer.value
|
|
989
|
+
? await loadTeamCredentials(configuredServer.value)
|
|
990
|
+
: null;
|
|
991
|
+
const online = credentials
|
|
992
|
+
? await fetchOnlineModels(credentials).catch((error) => {
|
|
993
|
+
chat.ephemeralLine(
|
|
994
|
+
`[/model] 在线模型暂时不可用:${error instanceof Error ? error.message : String(error)};仍可选择本机模型。`,
|
|
995
|
+
);
|
|
996
|
+
return [];
|
|
997
|
+
})
|
|
998
|
+
: [];
|
|
999
|
+
const onlinePickerModels: PickerModel[] = online.map(toPickerModel);
|
|
1000
|
+
const pickerModels = [...enabledModels, ...onlinePickerModels];
|
|
1001
|
+
if (pickerModels.length === 0) {
|
|
1002
|
+
chat.ephemeralLine(
|
|
1003
|
+
'[/model] 尚未配置任何模型,请先通过 Desktop 设置或 /config 添加模型。',
|
|
1004
|
+
);
|
|
597
1005
|
return;
|
|
598
1006
|
}
|
|
599
1007
|
if (arg) {
|
|
600
1008
|
// 直接切换到指定模型(支持精确匹配 id 或模糊匹配 displayName/model)
|
|
601
|
-
const target =
|
|
1009
|
+
const target = pickerModels.find(
|
|
1010
|
+
(m) => m.id === arg || m.model === arg || m.displayName === arg,
|
|
1011
|
+
);
|
|
602
1012
|
if (!target) {
|
|
603
|
-
chat.ephemeralLine(
|
|
1013
|
+
chat.ephemeralLine(
|
|
1014
|
+
`[/model] 未找到模型 "${arg}",可用模型:\n${pickerModels.map((m) => ` ${m.id} · ${m.displayName || m.model}`).join('\n')}`,
|
|
1015
|
+
);
|
|
604
1016
|
return;
|
|
605
1017
|
}
|
|
606
1018
|
await handleBaseModelSelect(target.id);
|
|
607
1019
|
return;
|
|
608
1020
|
}
|
|
609
1021
|
// 无参数:打开交互选择器
|
|
610
|
-
const currentSession = (
|
|
1022
|
+
const currentSession = (
|
|
1023
|
+
await listSessionSummaries(getWorkingDir())
|
|
1024
|
+
).find((session) => session.id === chat.activeSessionId);
|
|
611
1025
|
const snap = await configService.getSnapshot();
|
|
612
|
-
|
|
1026
|
+
const currentRef = currentSession?.preferredModel;
|
|
1027
|
+
const currentId = currentRef
|
|
1028
|
+
? legacyModelId(currentRef)
|
|
1029
|
+
: (currentSession?.modelId ?? snap.config?.baseModelId ?? null);
|
|
1030
|
+
setBaseModelPicker({ models: pickerModels, currentId });
|
|
613
1031
|
return;
|
|
614
1032
|
}
|
|
615
1033
|
|
|
@@ -645,42 +1063,70 @@ export function App({ provider, initialHistory }: Props): JSX.Element {
|
|
|
645
1063
|
|
|
646
1064
|
if (sub === 'on') {
|
|
647
1065
|
if (!advisor) {
|
|
648
|
-
chat.ephemeralLine(
|
|
649
|
-
'[/advisor] 未配置顾问模型,无法开启。',
|
|
650
|
-
);
|
|
1066
|
+
chat.ephemeralLine('[/advisor] 未配置顾问模型,无法开启。');
|
|
651
1067
|
} else {
|
|
652
1068
|
await configService.savePatch({ advisor: { enabled: true } });
|
|
653
|
-
chat.ephemeralLine(
|
|
1069
|
+
chat.ephemeralLine(
|
|
1070
|
+
`[/advisor] 顾问模型已开启(${advisor.provider.name}/${advisor.provider.model})。`,
|
|
1071
|
+
);
|
|
654
1072
|
}
|
|
655
1073
|
return;
|
|
656
1074
|
}
|
|
657
1075
|
|
|
658
1076
|
if (sub === 'off') {
|
|
659
|
-
if (advisor)
|
|
1077
|
+
if (advisor)
|
|
1078
|
+
await configService.savePatch({ advisor: { enabled: false } });
|
|
660
1079
|
chat.ephemeralLine('[/advisor] 顾问模型已关闭。');
|
|
661
1080
|
return;
|
|
662
1081
|
}
|
|
663
1082
|
|
|
664
1083
|
if (sub === 'select') {
|
|
665
1084
|
const models = await configService.models();
|
|
666
|
-
const enabledModels = models.filter(
|
|
667
|
-
|
|
1085
|
+
const enabledModels: PickerModel[] = models.filter(
|
|
1086
|
+
(m) => m.enabled && m.apiKey,
|
|
1087
|
+
);
|
|
1088
|
+
const configuredServer = await configService.resolveTeamServerUrl();
|
|
1089
|
+
const credentials = configuredServer.value
|
|
1090
|
+
? await loadTeamCredentials(configuredServer.value)
|
|
1091
|
+
: null;
|
|
1092
|
+
const online = credentials
|
|
1093
|
+
? await fetchOnlineModels(credentials).catch((error) => {
|
|
1094
|
+
chat.ephemeralLine(
|
|
1095
|
+
`[/advisor] 在线顾问模型暂时不可用:${error instanceof Error ? error.message : String(error)};仍可选择本机顾问模型。`,
|
|
1096
|
+
);
|
|
1097
|
+
return [];
|
|
1098
|
+
})
|
|
1099
|
+
: [];
|
|
1100
|
+
const pickerModels = [...enabledModels, ...online.map(toPickerModel)];
|
|
1101
|
+
if (pickerModels.length === 0) {
|
|
668
1102
|
chat.ephemeralLine('[/advisor] 尚未配置任何模型,请先添加模型。');
|
|
669
1103
|
return;
|
|
670
1104
|
}
|
|
671
1105
|
if (note) {
|
|
672
1106
|
// /advisor select <id> 直接指定模型
|
|
673
|
-
const target =
|
|
1107
|
+
const target = pickerModels.find(
|
|
1108
|
+
(m) =>
|
|
1109
|
+
m.id === note || m.model === note || m.displayName === note,
|
|
1110
|
+
);
|
|
674
1111
|
if (!target) {
|
|
675
|
-
chat.ephemeralLine(
|
|
1112
|
+
chat.ephemeralLine(
|
|
1113
|
+
`[/advisor] 未找到模型 "${note}",可用模型:\n${pickerModels.map((m) => ` ${m.id} · ${m.displayName || m.model}`).join('\n')}`,
|
|
1114
|
+
);
|
|
676
1115
|
return;
|
|
677
1116
|
}
|
|
678
|
-
await configService.savePatch({
|
|
679
|
-
|
|
1117
|
+
await configService.savePatch({
|
|
1118
|
+
advisor: { modelId: target.id, enabled: true },
|
|
1119
|
+
});
|
|
1120
|
+
chat.ephemeralLine(
|
|
1121
|
+
`[/advisor] 已设置顾问模型为 ${target.displayName || target.model}(${formatProviderName(target.apiFormat)})。`,
|
|
1122
|
+
);
|
|
680
1123
|
} else {
|
|
681
1124
|
// 无参数:打开交互选择器(含"关闭"选项)
|
|
682
1125
|
const snap = await configService.getSnapshot();
|
|
683
|
-
setAdvisorModelPicker({
|
|
1126
|
+
setAdvisorModelPicker({
|
|
1127
|
+
models: pickerModels,
|
|
1128
|
+
currentId: snap.config?.advisor?.modelId ?? null,
|
|
1129
|
+
});
|
|
684
1130
|
}
|
|
685
1131
|
return;
|
|
686
1132
|
}
|
|
@@ -688,11 +1134,15 @@ export function App({ provider, initialHistory }: Props): JSX.Element {
|
|
|
688
1134
|
// sub === 'ask':立即请求顾问意见。走 chat.runManualAdvisor 而不是裸 runAdvisor——
|
|
689
1135
|
// 它负责过程显示(LiveArea 进度行)与 ESC 中断,否则最长 180 秒 TUI 全静默。
|
|
690
1136
|
if (!advisor) {
|
|
691
|
-
chat.ephemeralLine(
|
|
1137
|
+
chat.ephemeralLine(
|
|
1138
|
+
'[/advisor] 未配置顾问模型:先用 /advisor select 选一个,或在 Desktop 设置 → 模型中配置。',
|
|
1139
|
+
);
|
|
692
1140
|
return;
|
|
693
1141
|
}
|
|
694
1142
|
if (!advisor.enabled) {
|
|
695
|
-
chat.ephemeralLine(
|
|
1143
|
+
chat.ephemeralLine(
|
|
1144
|
+
'[/advisor] 顾问模型当前已关闭;/advisor on 开启后再咨询。',
|
|
1145
|
+
);
|
|
696
1146
|
return;
|
|
697
1147
|
}
|
|
698
1148
|
const result = await chat.runManualAdvisor(note);
|
|
@@ -725,7 +1175,6 @@ export function App({ provider, initialHistory }: Props): JSX.Element {
|
|
|
725
1175
|
chat.activeSessionId,
|
|
726
1176
|
chat.submit,
|
|
727
1177
|
chat.runManualAdvisor,
|
|
728
|
-
chat.setProvider,
|
|
729
1178
|
chat.computerUseMode,
|
|
730
1179
|
chat.setComputerUseMode,
|
|
731
1180
|
chat.stopComputerUse,
|
|
@@ -735,7 +1184,6 @@ export function App({ provider, initialHistory }: Props): JSX.Element {
|
|
|
735
1184
|
handleSessionCreate,
|
|
736
1185
|
openWorkspacePicker,
|
|
737
1186
|
handleBaseModelSelect,
|
|
738
|
-
handleAdvisorModelSelect,
|
|
739
1187
|
],
|
|
740
1188
|
);
|
|
741
1189
|
|
|
@@ -784,10 +1232,19 @@ export function App({ provider, initialHistory }: Props): JSX.Element {
|
|
|
784
1232
|
<QueuedMessagesBanner items={chat.queuedItems} />
|
|
785
1233
|
|
|
786
1234
|
{chat.pendingComputerIntervention && (
|
|
787
|
-
<Box
|
|
788
|
-
|
|
1235
|
+
<Box
|
|
1236
|
+
flexDirection="column"
|
|
1237
|
+
borderStyle="round"
|
|
1238
|
+
borderColor={theme.warning}
|
|
1239
|
+
paddingX={1}
|
|
1240
|
+
marginBottom={1}
|
|
1241
|
+
>
|
|
1242
|
+
<Text color={theme.warning}>
|
|
1243
|
+
需要人工操作:{chat.pendingComputerIntervention.sanitizedSummary}
|
|
1244
|
+
</Text>
|
|
789
1245
|
<Text dimColor>
|
|
790
|
-
请直接在目标应用中处理;敏感内容不会进入终端或模型。完成后输入
|
|
1246
|
+
请直接在目标应用中处理;敏感内容不会进入终端或模型。完成后输入
|
|
1247
|
+
/computer resume,取消输入 /computer cancel。
|
|
791
1248
|
</Text>
|
|
792
1249
|
</Box>
|
|
793
1250
|
)}
|
|
@@ -802,8 +1259,13 @@ export function App({ provider, initialHistory }: Props): JSX.Element {
|
|
|
802
1259
|
{/* 中断提示 */}
|
|
803
1260
|
{chat.interrupted && (
|
|
804
1261
|
<Box marginBottom={1}>
|
|
805
|
-
<Text color={theme.warning}
|
|
806
|
-
|
|
1262
|
+
<Text color={theme.warning}>
|
|
1263
|
+
⚡ 操作被用户中断,等待新的任务输入...
|
|
1264
|
+
</Text>
|
|
1265
|
+
<Text dimColor>
|
|
1266
|
+
{' '}
|
|
1267
|
+
调整上一次计划:直接说调整内容 / 重新跑 /plan 目标 / 新任务直接说
|
|
1268
|
+
</Text>
|
|
807
1269
|
</Box>
|
|
808
1270
|
)}
|
|
809
1271
|
|
|
@@ -812,7 +1274,10 @@ export function App({ provider, initialHistory }: Props): JSX.Element {
|
|
|
812
1274
|
或普通 InputBox——都用 useInput,同时挂会抢键 → 必须互斥渲染。
|
|
813
1275
|
选完/取消/关闭后各自状态归位,InputBox 自动回归。 */}
|
|
814
1276
|
{configMenuOpen ? (
|
|
815
|
-
<ConfigMenu
|
|
1277
|
+
<ConfigMenu
|
|
1278
|
+
mode="reconfigure"
|
|
1279
|
+
onDone={() => setConfigMenuOpen(false)}
|
|
1280
|
+
/>
|
|
816
1281
|
) : baseModelPicker ? (
|
|
817
1282
|
<ModelPicker
|
|
818
1283
|
models={baseModelPicker.models}
|