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
|
@@ -1,21 +1,35 @@
|
|
|
1
1
|
import { randomUUID } from 'node:crypto';
|
|
2
2
|
|
|
3
|
-
import type { Provider } from '../types.js';
|
|
4
3
|
import {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
4
|
+
type ModelFallbackReason,
|
|
5
|
+
type ModelRef,
|
|
6
|
+
modelRefFromLegacy,
|
|
7
|
+
} from '../modelRef.js';
|
|
8
|
+
import {
|
|
9
|
+
type OnlineModelCatalogItem,
|
|
10
|
+
type TeamCredentials,
|
|
11
|
+
fetchOnlineModels,
|
|
12
|
+
loadTeamCredentials,
|
|
13
|
+
resolveOnlineModel,
|
|
14
|
+
} from '../team/auth.js';
|
|
15
|
+
import type { GatewayProvider, LocalProvider, Provider } from '../types.js';
|
|
16
|
+
import {
|
|
9
17
|
type AdvisorConfig,
|
|
10
|
-
type ModelConfig,
|
|
11
18
|
type ApiFormat,
|
|
12
|
-
type ToolConfigs,
|
|
13
|
-
type SubagentsConfig,
|
|
14
19
|
type BrowserConfig,
|
|
15
20
|
type ComputerUseConfig,
|
|
16
|
-
|
|
21
|
+
DEFAULT_ADVISOR_CONFIG,
|
|
17
22
|
DEFAULT_BROWSER_CONFIG,
|
|
18
23
|
DEFAULT_COMPUTER_USE_CONFIG,
|
|
24
|
+
DEFAULT_SUBAGENTS_CONFIG,
|
|
25
|
+
type MinimalAgentConfig,
|
|
26
|
+
type ModelConfig,
|
|
27
|
+
type SubagentsConfig,
|
|
28
|
+
type ToolConfigs,
|
|
29
|
+
type WorkflowModelRoles,
|
|
30
|
+
defaultMaxTokens,
|
|
31
|
+
readMinimalAgentConfig,
|
|
32
|
+
updateMinimalAgentConfig,
|
|
19
33
|
} from './configFile.js';
|
|
20
34
|
import { CONFIG_LIMITS, normalizeLimited } from './numericLimits.js';
|
|
21
35
|
|
|
@@ -42,28 +56,103 @@ export interface ConfigSnapshot {
|
|
|
42
56
|
* 而不是「类型说必填、实现却防着 undefined」的分裂状态。
|
|
43
57
|
*/
|
|
44
58
|
export type ModelOp =
|
|
45
|
-
| {
|
|
46
|
-
|
|
59
|
+
| {
|
|
60
|
+
op: 'add';
|
|
61
|
+
model: Partial<Omit<ModelConfig, 'id' | 'createdAt' | 'updatedAt'>>;
|
|
62
|
+
}
|
|
63
|
+
| {
|
|
64
|
+
op: 'update';
|
|
65
|
+
id: string;
|
|
66
|
+
patch: Partial<Omit<ModelConfig, 'id' | 'createdAt' | 'updatedAt'>> & {
|
|
67
|
+
clearApiKey?: boolean;
|
|
68
|
+
};
|
|
69
|
+
}
|
|
47
70
|
| { op: 'remove'; id: string };
|
|
48
71
|
|
|
49
72
|
export interface ConfigPatch {
|
|
50
73
|
models?: ModelOp[];
|
|
51
74
|
baseModelId?: string | null;
|
|
75
|
+
/** 私有部署/开发覆盖用的团队服务器地址;普通用户使用内置 KakadeAI 服务。 */
|
|
76
|
+
teamServerUrl?: string | null;
|
|
52
77
|
compactRatio?: number;
|
|
53
|
-
advisor?:
|
|
78
|
+
advisor?: Partial<Omit<AdvisorConfig, 'contextPolicy'>> & { clear?: boolean };
|
|
79
|
+
/** Set a role to null/empty to remove its mapping. */
|
|
80
|
+
workflowModels?: Partial<Record<string, string | null>>;
|
|
54
81
|
subagents?: Partial<SubagentsConfig>;
|
|
55
82
|
browser?: Partial<BrowserConfig>;
|
|
56
83
|
computerUse?: Partial<ComputerUseConfig>;
|
|
57
84
|
tools?: {
|
|
58
|
-
webSearch?: Partial<NonNullable<ToolConfigs['webSearch']>> & {
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
85
|
+
webSearch?: Partial<NonNullable<ToolConfigs['webSearch']>> & {
|
|
86
|
+
clear?: boolean;
|
|
87
|
+
};
|
|
88
|
+
imageGeneration?: Partial<NonNullable<ToolConfigs['imageGeneration']>> & {
|
|
89
|
+
clear?: boolean;
|
|
90
|
+
clearApiKey?: boolean;
|
|
91
|
+
};
|
|
92
|
+
openRouter?: Partial<NonNullable<ToolConfigs['openRouter']>> & {
|
|
93
|
+
clear?: boolean;
|
|
94
|
+
};
|
|
95
|
+
textToSpeech?: Partial<NonNullable<ToolConfigs['textToSpeech']>> & {
|
|
96
|
+
clear?: boolean;
|
|
97
|
+
clearApiKey?: boolean;
|
|
98
|
+
};
|
|
62
99
|
};
|
|
63
100
|
}
|
|
64
101
|
|
|
65
102
|
const DEFAULT_CONTEXT_WINDOW = 128_000;
|
|
66
103
|
|
|
104
|
+
export interface ModelResolution {
|
|
105
|
+
provider: Provider | null;
|
|
106
|
+
fallbackReason: ModelFallbackReason;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function gatewayFallbackReason(
|
|
110
|
+
model?: OnlineModelCatalogItem,
|
|
111
|
+
error?: unknown,
|
|
112
|
+
): ModelFallbackReason {
|
|
113
|
+
const state = model?.availability?.state;
|
|
114
|
+
if (state === 'quota_exhausted' || state === 'concurrency_exceeded')
|
|
115
|
+
return 'quota_exhausted';
|
|
116
|
+
if (state === 'model_disabled' || state === 'disabled')
|
|
117
|
+
return 'model_disabled';
|
|
118
|
+
if (state === 'device_expired') return 'credential_expired';
|
|
119
|
+
if (state === 'not_authorized') return 'grant_revoked';
|
|
120
|
+
if (state === 'server_offline' || state === 'offline') return 'offline';
|
|
121
|
+
const message = error instanceof Error ? error.message : '';
|
|
122
|
+
if (message.includes('DEVICE_REVOKED') || message.includes('401'))
|
|
123
|
+
return 'credential_expired';
|
|
124
|
+
if (message.includes('MODEL_UNAVAILABLE') || message.includes('403'))
|
|
125
|
+
return 'grant_revoked';
|
|
126
|
+
return 'offline';
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function gatewayProviderFromModel(
|
|
130
|
+
credentials: TeamCredentials,
|
|
131
|
+
model: OnlineModelCatalogItem,
|
|
132
|
+
): GatewayProvider {
|
|
133
|
+
return {
|
|
134
|
+
name: model.displayName || model.model,
|
|
135
|
+
source: 'gateway',
|
|
136
|
+
remoteModelId: model.id,
|
|
137
|
+
gatewayUrl: credentials.serverUrl,
|
|
138
|
+
gatewayToken: credentials.deviceToken,
|
|
139
|
+
gatewayMaxToolCategories: model.maxToolCategories,
|
|
140
|
+
gatewayDefaultToolCategories: model.defaultToolCategories,
|
|
141
|
+
// Deprecated compatibility field: before the explicit max/default/session
|
|
142
|
+
// split this field was used as the provider boundary. Keep its safe
|
|
143
|
+
// default as the server maximum; the current Session selection is applied
|
|
144
|
+
// per turn by runAgentTurn/useChat.
|
|
145
|
+
gatewayToolCategories: model.maxToolCategories,
|
|
146
|
+
model: model.model,
|
|
147
|
+
contextWindow: model.contextWindow,
|
|
148
|
+
maxTokens: model.maxTokens,
|
|
149
|
+
apiFormat: model.apiFormat,
|
|
150
|
+
supportsVision: model.supportsVision,
|
|
151
|
+
gatewayCapabilities: model.capabilities,
|
|
152
|
+
promptCache: { mode: 'disabled' },
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
|
|
67
156
|
function positiveInt(value: string | undefined, fallback: number): number {
|
|
68
157
|
const parsed = Number.parseInt(value ?? '', 10);
|
|
69
158
|
return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
|
|
@@ -87,25 +176,43 @@ function configFromEnv(): MinimalAgentConfig | null {
|
|
|
87
176
|
const model = process.env.MINIMAL_AGENT_MODEL;
|
|
88
177
|
if (!baseURL || !apiKey || !model) return null;
|
|
89
178
|
const tools: ToolConfigs = {};
|
|
90
|
-
if (process.env.TAVILY_API_KEY)
|
|
91
|
-
|
|
179
|
+
if (process.env.TAVILY_API_KEY)
|
|
180
|
+
tools.webSearch = {
|
|
181
|
+
provider: 'tavily',
|
|
182
|
+
apiKey: process.env.TAVILY_API_KEY,
|
|
183
|
+
};
|
|
184
|
+
if (
|
|
185
|
+
process.env.OPENAI_IMAGE_BASE_URL &&
|
|
186
|
+
process.env.OPENAI_IMAGE_API_KEY &&
|
|
187
|
+
process.env.OPENAI_IMAGE_MODEL
|
|
188
|
+
) {
|
|
92
189
|
tools.imageGeneration = {
|
|
93
190
|
provider: process.env.OPENAI_IMAGE_PROVIDER ?? 'openai',
|
|
94
191
|
baseURL: process.env.OPENAI_IMAGE_BASE_URL,
|
|
95
192
|
apiKey: process.env.OPENAI_IMAGE_API_KEY,
|
|
96
193
|
model: process.env.OPENAI_IMAGE_MODEL,
|
|
97
|
-
protocol:
|
|
98
|
-
|
|
99
|
-
|
|
194
|
+
protocol:
|
|
195
|
+
process.env.OPENAI_IMAGE_PROTOCOL === 'chat' ||
|
|
196
|
+
process.env.OPENAI_IMAGE_PROTOCOL === 'images'
|
|
197
|
+
? process.env.OPENAI_IMAGE_PROTOCOL
|
|
198
|
+
: 'auto',
|
|
100
199
|
};
|
|
101
200
|
}
|
|
102
|
-
if (process.env.OPENROUTER_API_KEY)
|
|
201
|
+
if (process.env.OPENROUTER_API_KEY)
|
|
202
|
+
tools.openRouter = { apiKey: process.env.OPENROUTER_API_KEY };
|
|
103
203
|
if (process.env.MINIMAX_API_KEY && process.env.MINIMAX_GROUP_ID) {
|
|
104
|
-
tools.textToSpeech = {
|
|
204
|
+
tools.textToSpeech = {
|
|
205
|
+
provider: 'minimax',
|
|
206
|
+
apiKey: process.env.MINIMAX_API_KEY,
|
|
207
|
+
groupId: process.env.MINIMAX_GROUP_ID,
|
|
208
|
+
};
|
|
105
209
|
}
|
|
106
210
|
const compactRatio = Number(process.env.MINIMAL_AGENT_COMPACT_RATIO);
|
|
107
211
|
const name = process.env.MINIMAL_AGENT_PROVIDER ?? 'env';
|
|
108
|
-
const contextWindow = positiveInt(
|
|
212
|
+
const contextWindow = positiveInt(
|
|
213
|
+
process.env.MINIMAL_AGENT_CONTEXT_WINDOW,
|
|
214
|
+
DEFAULT_CONTEXT_WINDOW,
|
|
215
|
+
);
|
|
109
216
|
const envModel: ModelConfig = {
|
|
110
217
|
id: 'env-base',
|
|
111
218
|
apiFormat: 'openai',
|
|
@@ -114,13 +221,17 @@ function configFromEnv(): MinimalAgentConfig | null {
|
|
|
114
221
|
apiKey,
|
|
115
222
|
model,
|
|
116
223
|
contextWindow,
|
|
117
|
-
maxTokens: positiveInt(
|
|
224
|
+
maxTokens: positiveInt(
|
|
225
|
+
process.env.MINIMAL_AGENT_MAX_TOKENS,
|
|
226
|
+
defaultMaxTokens(contextWindow),
|
|
227
|
+
),
|
|
118
228
|
promptCache: {
|
|
119
|
-
mode:
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
229
|
+
mode:
|
|
230
|
+
process.env.MINIMAL_AGENT_PROMPT_CACHE_MODE === 'session-key'
|
|
231
|
+
? 'session-key'
|
|
232
|
+
: process.env.MINIMAL_AGENT_PROMPT_CACHE_MODE === 'disabled'
|
|
233
|
+
? 'disabled'
|
|
234
|
+
: 'auto',
|
|
124
235
|
},
|
|
125
236
|
enabled: true,
|
|
126
237
|
createdAt: 0,
|
|
@@ -130,23 +241,39 @@ function configFromEnv(): MinimalAgentConfig | null {
|
|
|
130
241
|
version: 4,
|
|
131
242
|
models: [envModel],
|
|
132
243
|
baseModelId: envModel.id,
|
|
244
|
+
teamServerUrl: process.env.KAKADEAI_SOCIAL_SERVER_URL?.trim() || null,
|
|
133
245
|
...(compactRatio > 0 && compactRatio <= 1 ? { compactRatio } : {}),
|
|
134
246
|
subagents: {
|
|
135
247
|
...DEFAULT_SUBAGENTS_CONFIG,
|
|
136
|
-
maxCallsPerTurn: positiveInt(
|
|
248
|
+
maxCallsPerTurn: positiveInt(
|
|
249
|
+
process.env.AGENT_TOOL_MAX_CALLS_PER_TURN,
|
|
250
|
+
6,
|
|
251
|
+
),
|
|
137
252
|
maxConcurrent: positiveInt(process.env.AGENT_TOOL_MAX_CONCURRENT, 3),
|
|
138
253
|
timeoutMs: positiveInt(process.env.AGENT_TOOL_TIMEOUT_MS, 1_800_000),
|
|
139
|
-
exploreMaxTurns: positiveInt(
|
|
140
|
-
|
|
254
|
+
exploreMaxTurns: positiveInt(
|
|
255
|
+
process.env.AGENT_TOOL_EXPLORE_MAX_TURNS,
|
|
256
|
+
200,
|
|
257
|
+
),
|
|
258
|
+
generalMaxTurns: positiveInt(
|
|
259
|
+
process.env.AGENT_TOOL_GENERAL_MAX_TURNS,
|
|
260
|
+
100,
|
|
261
|
+
),
|
|
141
262
|
},
|
|
142
263
|
browser: DEFAULT_BROWSER_CONFIG,
|
|
143
264
|
computerUse: {
|
|
144
265
|
enabled: process.env.MINIMAL_AGENT_COMPUTER_USE_ENABLED !== 'false',
|
|
145
266
|
imagePolicy: 'single-adaptive',
|
|
146
|
-
maxImagePixels: Math.min(
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
267
|
+
maxImagePixels: Math.min(
|
|
268
|
+
4_000_000,
|
|
269
|
+
Math.max(
|
|
270
|
+
250_000,
|
|
271
|
+
positiveInt(
|
|
272
|
+
process.env.MINIMAL_AGENT_COMPUTER_USE_MAX_IMAGE_PIXELS,
|
|
273
|
+
DEFAULT_COMPUTER_USE_CONFIG.maxImagePixels,
|
|
274
|
+
),
|
|
275
|
+
),
|
|
276
|
+
),
|
|
150
277
|
observationFrames: 1,
|
|
151
278
|
},
|
|
152
279
|
tools,
|
|
@@ -154,8 +281,12 @@ function configFromEnv(): MinimalAgentConfig | null {
|
|
|
154
281
|
};
|
|
155
282
|
}
|
|
156
283
|
|
|
157
|
-
function resolveProvider(
|
|
284
|
+
function resolveProvider(
|
|
285
|
+
entry: ModelConfig,
|
|
286
|
+
compactRatio?: number,
|
|
287
|
+
): LocalProvider {
|
|
158
288
|
return {
|
|
289
|
+
source: 'local',
|
|
159
290
|
name: entry.displayName ?? entry.model,
|
|
160
291
|
baseURL: entry.baseURL,
|
|
161
292
|
apiKey: entry.apiKey,
|
|
@@ -164,17 +295,31 @@ function resolveProvider(entry: ModelConfig, compactRatio?: number): Provider {
|
|
|
164
295
|
maxTokens: entry.maxTokens ?? defaultMaxTokens(entry.contextWindow),
|
|
165
296
|
apiFormat: entry.apiFormat,
|
|
166
297
|
supportsVision: entry.supportsVision !== false,
|
|
298
|
+
...(entry.supportsJsonSchema !== undefined
|
|
299
|
+
? { supportsJsonSchema: entry.supportsJsonSchema }
|
|
300
|
+
: {}),
|
|
167
301
|
promptCache: entry.promptCache ?? { mode: 'auto' },
|
|
168
302
|
...(compactRatio !== undefined ? { compactRatio } : {}),
|
|
303
|
+
// cache_edits 式回收:仅 Anthropic 格式 + 显式 env 开启时生效(非公开协议,
|
|
304
|
+
// 依赖支持该扩展的网关端点;语义与默认值说明见 types.ts Provider.cacheEdits)
|
|
305
|
+
...(entry.apiFormat === 'anthropic' &&
|
|
306
|
+
process.env.MINIMAL_AGENT_CACHE_EDITS === '1'
|
|
307
|
+
? { cacheEdits: true }
|
|
308
|
+
: {}),
|
|
169
309
|
};
|
|
170
310
|
}
|
|
171
311
|
|
|
172
|
-
function mergeRecord(
|
|
312
|
+
function mergeRecord(
|
|
313
|
+
base: Record<string, unknown> | undefined,
|
|
314
|
+
patch: Record<string, unknown>,
|
|
315
|
+
): Record<string, unknown> {
|
|
173
316
|
const out: Record<string, unknown> = { ...(base ?? {}) };
|
|
174
317
|
for (const [key, value] of Object.entries(patch)) {
|
|
175
318
|
if (value && typeof value === 'object' && !Array.isArray(value)) {
|
|
176
319
|
out[key] = mergeRecord(
|
|
177
|
-
out[key] && typeof out[key] === 'object' && !Array.isArray(out[key])
|
|
320
|
+
out[key] && typeof out[key] === 'object' && !Array.isArray(out[key])
|
|
321
|
+
? (out[key] as Record<string, unknown>)
|
|
322
|
+
: undefined,
|
|
178
323
|
value as Record<string, unknown>,
|
|
179
324
|
);
|
|
180
325
|
} else if (value !== undefined) out[key] = value;
|
|
@@ -186,17 +331,33 @@ function hasEntries(value: object | undefined): boolean {
|
|
|
186
331
|
return !!value && Object.keys(value).length > 0;
|
|
187
332
|
}
|
|
188
333
|
|
|
189
|
-
|
|
334
|
+
/** Remove an optional config key while keeping the persisted object sparse. */
|
|
335
|
+
function omitKey<T extends object>(
|
|
336
|
+
value: T | undefined,
|
|
337
|
+
key: PropertyKey,
|
|
338
|
+
): T | undefined {
|
|
339
|
+
if (!value) return value;
|
|
340
|
+
const { [key]: _removed, ...rest } = value as T &
|
|
341
|
+
Record<PropertyKey, unknown>;
|
|
342
|
+
return rest as T;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
function applyModelsPatch(
|
|
346
|
+
models: ModelConfig[],
|
|
347
|
+
ops: ModelOp[],
|
|
348
|
+
): ModelConfig[] {
|
|
190
349
|
let out = [...models];
|
|
191
350
|
const now = Date.now();
|
|
192
351
|
for (const item of ops) {
|
|
193
352
|
if (item.op === 'add') {
|
|
194
353
|
const m = item.model;
|
|
195
|
-
const apiFormat: ApiFormat =
|
|
354
|
+
const apiFormat: ApiFormat =
|
|
355
|
+
m.apiFormat === 'anthropic' ? 'anthropic' : 'openai';
|
|
196
356
|
const baseURL = m.baseURL?.trim();
|
|
197
357
|
const modelId = m.model?.trim();
|
|
198
358
|
const apiKey = m.apiKey?.trim();
|
|
199
|
-
if (!baseURL || !modelId || !apiKey)
|
|
359
|
+
if (!baseURL || !modelId || !apiKey)
|
|
360
|
+
throw new Error('模型缺少 Base URL、模型 ID 或 API Key');
|
|
200
361
|
out.push({
|
|
201
362
|
id: randomUUID(),
|
|
202
363
|
apiFormat,
|
|
@@ -204,9 +365,15 @@ function applyModelsPatch(models: ModelConfig[], ops: ModelOp[]): ModelConfig[]
|
|
|
204
365
|
baseURL,
|
|
205
366
|
apiKey,
|
|
206
367
|
model: modelId,
|
|
207
|
-
contextWindow:
|
|
368
|
+
contextWindow:
|
|
369
|
+
m.contextWindow && m.contextWindow > 0
|
|
370
|
+
? m.contextWindow
|
|
371
|
+
: DEFAULT_CONTEXT_WINDOW,
|
|
208
372
|
...(m.maxTokens ? { maxTokens: m.maxTokens } : {}),
|
|
209
373
|
supportsVision: m.supportsVision !== false,
|
|
374
|
+
...(m.supportsJsonSchema !== undefined
|
|
375
|
+
? { supportsJsonSchema: m.supportsJsonSchema }
|
|
376
|
+
: {}),
|
|
210
377
|
promptCache: m.promptCache ?? { mode: 'auto' },
|
|
211
378
|
enabled: m.enabled ?? true,
|
|
212
379
|
createdAt: now,
|
|
@@ -219,13 +386,27 @@ function applyModelsPatch(models: ModelConfig[], ops: ModelOp[]): ModelConfig[]
|
|
|
219
386
|
const patch = item.patch;
|
|
220
387
|
const next: ModelConfig = {
|
|
221
388
|
...existing,
|
|
222
|
-
...(patch.apiFormat
|
|
223
|
-
|
|
389
|
+
...(patch.apiFormat
|
|
390
|
+
? {
|
|
391
|
+
apiFormat:
|
|
392
|
+
patch.apiFormat === 'anthropic' ? 'anthropic' : 'openai',
|
|
393
|
+
}
|
|
394
|
+
: {}),
|
|
395
|
+
...(patch.displayName !== undefined
|
|
396
|
+
? { displayName: patch.displayName.trim() || undefined }
|
|
397
|
+
: {}),
|
|
224
398
|
...(patch.baseURL?.trim() ? { baseURL: patch.baseURL.trim() } : {}),
|
|
225
399
|
...(patch.model?.trim() ? { model: patch.model.trim() } : {}),
|
|
226
400
|
...(patch.contextWindow ? { contextWindow: patch.contextWindow } : {}),
|
|
227
|
-
...(patch.maxTokens !== undefined
|
|
228
|
-
|
|
401
|
+
...(patch.maxTokens !== undefined
|
|
402
|
+
? { maxTokens: patch.maxTokens || undefined }
|
|
403
|
+
: {}),
|
|
404
|
+
...(patch.supportsVision !== undefined
|
|
405
|
+
? { supportsVision: patch.supportsVision !== false }
|
|
406
|
+
: {}),
|
|
407
|
+
...(patch.supportsJsonSchema !== undefined
|
|
408
|
+
? { supportsJsonSchema: patch.supportsJsonSchema }
|
|
409
|
+
: {}),
|
|
229
410
|
...(patch.promptCache ? { promptCache: patch.promptCache } : {}),
|
|
230
411
|
...(patch.enabled !== undefined ? { enabled: patch.enabled } : {}),
|
|
231
412
|
updatedAt: now,
|
|
@@ -240,18 +421,26 @@ function applyModelsPatch(models: ModelConfig[], ops: ModelOp[]): ModelConfig[]
|
|
|
240
421
|
return out;
|
|
241
422
|
}
|
|
242
423
|
|
|
243
|
-
function applyConfigPatch(
|
|
244
|
-
|
|
424
|
+
function applyConfigPatch(
|
|
425
|
+
base: MinimalAgentConfig | null,
|
|
426
|
+
patch: ConfigPatch,
|
|
427
|
+
): Omit<MinimalAgentConfig, 'version' | 'savedAt'> {
|
|
428
|
+
const wasEmpty =
|
|
429
|
+
(base?.models.length ?? 0) === 0 && (base?.baseModelId ?? null) === null;
|
|
245
430
|
let models = base?.models ?? [];
|
|
246
431
|
let baseModelId = base?.baseModelId ?? null;
|
|
247
432
|
|
|
248
433
|
if (patch.models?.length) models = applyModelsPatch(models, patch.models);
|
|
249
434
|
|
|
250
435
|
// 全新安装加第一个模型时自动设为基座——否则用户加完模型还是没法聊天。
|
|
251
|
-
if (wasEmpty && baseModelId === null && models.length > 0)
|
|
436
|
+
if (wasEmpty && baseModelId === null && models.length > 0)
|
|
437
|
+
baseModelId = models[0]!.id;
|
|
252
438
|
|
|
253
439
|
if (patch.baseModelId !== undefined) {
|
|
254
|
-
if (
|
|
440
|
+
if (
|
|
441
|
+
patch.baseModelId !== null &&
|
|
442
|
+
!models.some((entry) => entry.id === patch.baseModelId)
|
|
443
|
+
) {
|
|
255
444
|
throw new Error('要设为基座的模型不存在');
|
|
256
445
|
}
|
|
257
446
|
baseModelId = patch.baseModelId;
|
|
@@ -260,8 +449,16 @@ function applyConfigPatch(base: MinimalAgentConfig | null, patch: ConfigPatch):
|
|
|
260
449
|
// 不变量:禁止删除/禁用当前基座模型指向的条目(先设别的为基座)。
|
|
261
450
|
if (baseModelId !== null) {
|
|
262
451
|
const baseEntry = models.find((entry) => entry.id === baseModelId);
|
|
263
|
-
if (!baseEntry)
|
|
264
|
-
|
|
452
|
+
if (!baseEntry)
|
|
453
|
+
throw new Error('当前基座模型已被删除,请先设置其他模型为基座');
|
|
454
|
+
if (baseEntry.enabled === false)
|
|
455
|
+
throw new Error('不能禁用当前基座模型,请先设置其他模型为基座');
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
// 团队服务器地址:不像 baseModelId 那样要交叉校验(不引用 models[]),一个裸字符串。
|
|
459
|
+
let teamServerUrl = base?.teamServerUrl ?? null;
|
|
460
|
+
if (patch.teamServerUrl !== undefined) {
|
|
461
|
+
teamServerUrl = patch.teamServerUrl?.trim() || null;
|
|
265
462
|
}
|
|
266
463
|
|
|
267
464
|
let advisor = base?.advisor;
|
|
@@ -270,90 +467,197 @@ function applyConfigPatch(base: MinimalAgentConfig | null, patch: ConfigPatch):
|
|
|
270
467
|
advisor = undefined;
|
|
271
468
|
} else {
|
|
272
469
|
advisor = {
|
|
273
|
-
enabled: patch.advisor.enabled ?? advisor?.enabled ??
|
|
470
|
+
enabled: patch.advisor.enabled ?? advisor?.enabled ?? DEFAULT_ADVISOR_CONFIG.enabled,
|
|
274
471
|
mode: patch.advisor.mode ?? advisor?.mode ?? 'auto',
|
|
275
|
-
modelId:
|
|
472
|
+
modelId:
|
|
473
|
+
patch.advisor.modelId !== undefined
|
|
474
|
+
? patch.advisor.modelId
|
|
475
|
+
: (advisor?.modelId ?? null),
|
|
276
476
|
contextPolicy: 'full-relevant',
|
|
277
|
-
shareSensitive:
|
|
477
|
+
shareSensitive:
|
|
478
|
+
patch.advisor.shareSensitive ?? advisor?.shareSensitive ?? false,
|
|
278
479
|
inputTokenBudget: normalizeLimited(
|
|
279
480
|
patch.advisor.inputTokenBudget ?? advisor?.inputTokenBudget,
|
|
280
|
-
CONFIG_LIMITS.advisorInputTokenBudget,
|
|
481
|
+
CONFIG_LIMITS.advisorInputTokenBudget,
|
|
482
|
+
DEFAULT_ADVISOR_CONFIG.inputTokenBudget,
|
|
483
|
+
),
|
|
281
484
|
timeoutMs: normalizeLimited(
|
|
282
485
|
patch.advisor.timeoutMs ?? advisor?.timeoutMs,
|
|
283
|
-
CONFIG_LIMITS.advisorTimeoutMs,
|
|
486
|
+
CONFIG_LIMITS.advisorTimeoutMs,
|
|
487
|
+
DEFAULT_ADVISOR_CONFIG.timeoutMs,
|
|
488
|
+
),
|
|
284
489
|
};
|
|
285
490
|
}
|
|
286
491
|
}
|
|
287
492
|
// 顾问引用的模型若已被删除或禁用,级联置空(软失联——解析层已把 null/悬空 id 当
|
|
288
493
|
// 「未配置」优雅处理,不留死链接;这与基座模型的硬阻止是刻意的不对称:顾问本来就是
|
|
289
494
|
// 可降级的第二意见,不像基座那样「没有就整个应用跑不起来」)。
|
|
290
|
-
if (advisor?.modelId) {
|
|
495
|
+
if (advisor?.modelId && !advisor.modelId.startsWith('online:')) {
|
|
291
496
|
const referenced = models.find((entry) => entry.id === advisor!.modelId);
|
|
292
|
-
if (!referenced || referenced.enabled === false)
|
|
497
|
+
if (!referenced || referenced.enabled === false)
|
|
498
|
+
advisor = { ...advisor, modelId: null };
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
const workflowModels: WorkflowModelRoles = {
|
|
502
|
+
...(base?.workflowModels ?? {}),
|
|
503
|
+
};
|
|
504
|
+
for (const [role, modelId] of Object.entries(patch.workflowModels ?? {})) {
|
|
505
|
+
if (modelId) workflowModels[role] = modelId;
|
|
506
|
+
else delete workflowModels[role];
|
|
507
|
+
}
|
|
508
|
+
for (const role of Object.keys(workflowModels)) {
|
|
509
|
+
const modelId = workflowModels[role];
|
|
510
|
+
const configured = models.find((entry) => entry.id === modelId);
|
|
511
|
+
if (!configured || configured.enabled === false)
|
|
512
|
+
delete workflowModels[role];
|
|
293
513
|
}
|
|
294
514
|
|
|
295
515
|
// 比例类:保留 4 位小数(= 百分比 2 位),否则 UI 上下箭头按 0.01 步进会累加出
|
|
296
516
|
// `0.8500000000000001` 这种浮点脏值直接写进 config.json。
|
|
297
|
-
const compactRatio =
|
|
298
|
-
|
|
299
|
-
?
|
|
300
|
-
|
|
301
|
-
|
|
517
|
+
const compactRatio =
|
|
518
|
+
patch.compactRatio !== undefined
|
|
519
|
+
? patch.compactRatio > 0 && patch.compactRatio <= 1
|
|
520
|
+
? normalizeLimited(
|
|
521
|
+
patch.compactRatio,
|
|
522
|
+
CONFIG_LIMITS.compactRatio,
|
|
523
|
+
CONFIG_LIMITS.compactRatio.max,
|
|
524
|
+
)
|
|
525
|
+
: undefined
|
|
526
|
+
: base?.compactRatio;
|
|
302
527
|
|
|
303
|
-
|
|
528
|
+
let tools: ToolConfigs = { ...(base?.tools ?? {}) };
|
|
304
529
|
const drafts = structuredClone(base?.drafts ?? {});
|
|
305
530
|
const toolPatch = patch.tools;
|
|
306
531
|
if (toolPatch?.webSearch) {
|
|
307
|
-
if (toolPatch.webSearch.clear) {
|
|
308
|
-
|
|
532
|
+
if (toolPatch.webSearch.clear) {
|
|
533
|
+
tools = omitKey(tools, 'webSearch') ?? {};
|
|
534
|
+
if (drafts.tools) drafts.tools = omitKey(drafts.tools, 'webSearch');
|
|
535
|
+
} else
|
|
536
|
+
tools.webSearch = mergeRecord(tools.webSearch, {
|
|
537
|
+
provider: 'tavily',
|
|
538
|
+
...toolPatch.webSearch,
|
|
539
|
+
}) as ToolConfigs['webSearch'];
|
|
309
540
|
}
|
|
310
541
|
if (toolPatch?.imageGeneration) {
|
|
311
|
-
if (toolPatch.imageGeneration.clear) {
|
|
312
|
-
|
|
313
|
-
|
|
542
|
+
if (toolPatch.imageGeneration.clear) {
|
|
543
|
+
tools = omitKey(tools, 'imageGeneration') ?? {};
|
|
544
|
+
if (drafts.tools) drafts.tools = omitKey(drafts.tools, 'imageGeneration');
|
|
545
|
+
} else {
|
|
546
|
+
const next = mergeRecord(
|
|
547
|
+
tools.imageGeneration,
|
|
548
|
+
toolPatch.imageGeneration,
|
|
549
|
+
) as NonNullable<ToolConfigs['imageGeneration']>;
|
|
314
550
|
if (toolPatch.imageGeneration.clearApiKey) next.apiKey = '';
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
551
|
+
const cleanNext = omitKey(
|
|
552
|
+
omitKey(
|
|
553
|
+
next as typeof next & { clear?: boolean; clearApiKey?: boolean },
|
|
554
|
+
'clear',
|
|
555
|
+
),
|
|
556
|
+
'clearApiKey',
|
|
557
|
+
) as NonNullable<ToolConfigs['imageGeneration']>;
|
|
558
|
+
if (!cleanNext.baseURL || !cleanNext.apiKey || !cleanNext.model)
|
|
559
|
+
throw new Error('图片生成缺少 Base URL、API Key 或模型名称');
|
|
560
|
+
tools.imageGeneration = cleanNext;
|
|
319
561
|
}
|
|
320
562
|
}
|
|
321
563
|
if (toolPatch?.openRouter) {
|
|
322
|
-
if (toolPatch.openRouter.clear) {
|
|
323
|
-
|
|
564
|
+
if (toolPatch.openRouter.clear) {
|
|
565
|
+
tools = omitKey(tools, 'openRouter') ?? {};
|
|
566
|
+
if (drafts.tools) drafts.tools = omitKey(drafts.tools, 'openRouter');
|
|
567
|
+
} else
|
|
568
|
+
tools.openRouter = mergeRecord(
|
|
569
|
+
tools.openRouter,
|
|
570
|
+
toolPatch.openRouter,
|
|
571
|
+
) as ToolConfigs['openRouter'];
|
|
324
572
|
}
|
|
325
573
|
if (toolPatch?.textToSpeech) {
|
|
326
|
-
if (toolPatch.textToSpeech.clear) {
|
|
327
|
-
|
|
328
|
-
|
|
574
|
+
if (toolPatch.textToSpeech.clear) {
|
|
575
|
+
tools = omitKey(tools, 'textToSpeech') ?? {};
|
|
576
|
+
if (drafts.tools) drafts.tools = omitKey(drafts.tools, 'textToSpeech');
|
|
577
|
+
} else {
|
|
578
|
+
const next = mergeRecord(tools.textToSpeech, {
|
|
579
|
+
provider: 'minimax',
|
|
580
|
+
...toolPatch.textToSpeech,
|
|
581
|
+
}) as NonNullable<ToolConfigs['textToSpeech']>;
|
|
329
582
|
if (toolPatch.textToSpeech.clearApiKey) next.apiKey = '';
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
583
|
+
const cleanNext = omitKey(
|
|
584
|
+
omitKey(
|
|
585
|
+
next as typeof next & { clear?: boolean; clearApiKey?: boolean },
|
|
586
|
+
'clear',
|
|
587
|
+
),
|
|
588
|
+
'clearApiKey',
|
|
589
|
+
) as NonNullable<ToolConfigs['textToSpeech']>;
|
|
590
|
+
if (!cleanNext.apiKey || !cleanNext.groupId)
|
|
591
|
+
throw new Error('MiniMax 语音缺少 API Key 或 Group ID');
|
|
592
|
+
tools.textToSpeech = cleanNext;
|
|
334
593
|
}
|
|
335
594
|
}
|
|
336
595
|
// 数字字段必须在**写入时**就按同一份边界规整——此前这里是裸 spread,读回时才被
|
|
337
596
|
// parseSubagents 的边界收窄,于是「存进去 300、读出来 100」,UI 表现成静默变回原值。
|
|
338
|
-
const mergedSubagents = {
|
|
597
|
+
const mergedSubagents = {
|
|
598
|
+
...(base?.subagents ?? DEFAULT_SUBAGENTS_CONFIG),
|
|
599
|
+
...(patch.subagents ?? {}),
|
|
600
|
+
};
|
|
339
601
|
const subagents: SubagentsConfig = {
|
|
340
602
|
...mergedSubagents,
|
|
341
|
-
maxCallsPerTurn: normalizeLimited(
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
603
|
+
maxCallsPerTurn: normalizeLimited(
|
|
604
|
+
mergedSubagents.maxCallsPerTurn,
|
|
605
|
+
CONFIG_LIMITS.subagentMaxCallsPerTurn,
|
|
606
|
+
DEFAULT_SUBAGENTS_CONFIG.maxCallsPerTurn,
|
|
607
|
+
),
|
|
608
|
+
maxConcurrent: normalizeLimited(
|
|
609
|
+
mergedSubagents.maxConcurrent,
|
|
610
|
+
CONFIG_LIMITS.subagentMaxConcurrent,
|
|
611
|
+
DEFAULT_SUBAGENTS_CONFIG.maxConcurrent,
|
|
612
|
+
),
|
|
613
|
+
timeoutMs: normalizeLimited(
|
|
614
|
+
mergedSubagents.timeoutMs,
|
|
615
|
+
CONFIG_LIMITS.subagentTimeoutMs,
|
|
616
|
+
DEFAULT_SUBAGENTS_CONFIG.timeoutMs,
|
|
617
|
+
),
|
|
618
|
+
exploreMaxTurns: normalizeLimited(
|
|
619
|
+
mergedSubagents.exploreMaxTurns,
|
|
620
|
+
CONFIG_LIMITS.subagentExploreMaxTurns,
|
|
621
|
+
DEFAULT_SUBAGENTS_CONFIG.exploreMaxTurns,
|
|
622
|
+
),
|
|
623
|
+
generalMaxTurns: normalizeLimited(
|
|
624
|
+
mergedSubagents.generalMaxTurns,
|
|
625
|
+
CONFIG_LIMITS.subagentGeneralMaxTurns,
|
|
626
|
+
DEFAULT_SUBAGENTS_CONFIG.generalMaxTurns,
|
|
627
|
+
),
|
|
628
|
+
};
|
|
629
|
+
const browser: BrowserConfig = {
|
|
630
|
+
...(base?.browser ?? DEFAULT_BROWSER_CONFIG),
|
|
631
|
+
...(patch.browser ?? {}),
|
|
632
|
+
profileId: 'default',
|
|
346
633
|
};
|
|
347
|
-
const browser: BrowserConfig = { ...(base?.browser ?? DEFAULT_BROWSER_CONFIG), ...(patch.browser ?? {}), profileId: 'default' };
|
|
348
634
|
const computerUse: ComputerUseConfig = {
|
|
349
635
|
...(base?.computerUse ?? DEFAULT_COMPUTER_USE_CONFIG),
|
|
350
636
|
...(patch.computerUse ?? {}),
|
|
351
637
|
};
|
|
352
638
|
computerUse.observationFrames = 1;
|
|
353
639
|
computerUse.maxImagePixels = normalizeLimited(
|
|
354
|
-
computerUse.maxImagePixels,
|
|
355
|
-
|
|
356
|
-
|
|
640
|
+
computerUse.maxImagePixels,
|
|
641
|
+
CONFIG_LIMITS.computerUseMaxImagePixels,
|
|
642
|
+
DEFAULT_COMPUTER_USE_CONFIG.maxImagePixels,
|
|
643
|
+
);
|
|
644
|
+
if (!hasEntries(drafts.tools)) drafts.tools = undefined;
|
|
645
|
+
return {
|
|
646
|
+
models,
|
|
647
|
+
baseModelId,
|
|
648
|
+
teamServerUrl,
|
|
649
|
+
...(compactRatio !== undefined ? { compactRatio } : {}),
|
|
650
|
+
advisor,
|
|
651
|
+
// Preserve an explicit empty object through updateMinimalAgentConfig so
|
|
652
|
+
// removing the final role is distinguishable from an unrelated mutator
|
|
653
|
+
// that omitted this optional field.
|
|
654
|
+
workflowModels,
|
|
655
|
+
subagents,
|
|
656
|
+
browser,
|
|
657
|
+
computerUse,
|
|
658
|
+
tools,
|
|
659
|
+
...(hasEntries(drafts) ? { drafts } : {}),
|
|
660
|
+
};
|
|
357
661
|
}
|
|
358
662
|
|
|
359
663
|
/** 仅不完整工具凭据使用草稿桶;其余设置都通过 patch 立即生效。 */
|
|
@@ -361,9 +665,16 @@ export type AutosaveSection = 'tools';
|
|
|
361
665
|
export type AutosaveState = 'applied' | 'draft';
|
|
362
666
|
|
|
363
667
|
export class ConfigService {
|
|
364
|
-
private snapshot: ConfigSnapshot = {
|
|
668
|
+
private snapshot: ConfigSnapshot = {
|
|
669
|
+
source: 'none',
|
|
670
|
+
readOnly: false,
|
|
671
|
+
config: null,
|
|
672
|
+
revision: 0,
|
|
673
|
+
};
|
|
365
674
|
|
|
366
|
-
get revision(): number {
|
|
675
|
+
get revision(): number {
|
|
676
|
+
return this.snapshot.revision;
|
|
677
|
+
}
|
|
367
678
|
|
|
368
679
|
async reload(): Promise<ConfigSnapshot> {
|
|
369
680
|
const envOnly = process.env.MINIMAL_AGENT_CONFIG_MODE === 'env';
|
|
@@ -374,11 +685,15 @@ export class ConfigService {
|
|
|
374
685
|
readOnly: envOnly,
|
|
375
686
|
config: json ?? env,
|
|
376
687
|
};
|
|
377
|
-
const changed =
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
688
|
+
const changed =
|
|
689
|
+
this.snapshot.revision === 0 ||
|
|
690
|
+
this.snapshot.source !== next.source ||
|
|
691
|
+
this.snapshot.readOnly !== next.readOnly ||
|
|
692
|
+
JSON.stringify(this.snapshot.config) !== JSON.stringify(next.config);
|
|
693
|
+
this.snapshot = {
|
|
694
|
+
...next,
|
|
695
|
+
revision: changed ? this.snapshot.revision + 1 : this.snapshot.revision,
|
|
696
|
+
};
|
|
382
697
|
return this.getSnapshot();
|
|
383
698
|
}
|
|
384
699
|
|
|
@@ -394,21 +709,166 @@ export class ConfigService {
|
|
|
394
709
|
return resolveProvider(entry, config?.compactRatio);
|
|
395
710
|
}
|
|
396
711
|
|
|
712
|
+
/**
|
|
713
|
+
* 团队登录服务器的默认地址:env 优先于 config.json,跟随 minimal-desktop 的
|
|
714
|
+
* `resolve_social_server()`(`src-tauri/src/lib.rs`)已验证的同一优先级——这两个
|
|
715
|
+
* 项目共享 `KAKADEAI_SOCIAL_SERVER_URL` 这一个环境变量名,语义必须一致,不能
|
|
716
|
+
* minimal-agent 这边搞成 config 优先。
|
|
717
|
+
*
|
|
718
|
+
* ⚠️ 不能简单读 `getSnapshot().config?.teamServerUrl`:那个 snapshot 在存在
|
|
719
|
+
* config.json 时完全不看 env(`reload()` 里 `config: json ?? env`,env 只在
|
|
720
|
+
* **没有** config.json 时才整体生效),会让"config.json 存在但没配地址、同时设了
|
|
721
|
+
* env"这种常见场景里 env 被错误地忽略。这里必须独立读 `process.env`。
|
|
722
|
+
*
|
|
723
|
+
* 返回值带 `source`,供诊断和私有部署使用;正式用户不需要接触服务器地址。
|
|
724
|
+
*/
|
|
725
|
+
async resolveTeamServerUrl(): Promise<{
|
|
726
|
+
value: string | null;
|
|
727
|
+
source: 'env' | 'config' | 'default' | 'none';
|
|
728
|
+
}> {
|
|
729
|
+
const rawEnvValue = process.env.KAKADEAI_SOCIAL_SERVER_URL;
|
|
730
|
+
if (rawEnvValue !== undefined)
|
|
731
|
+
return {
|
|
732
|
+
value: rawEnvValue.trim() || null,
|
|
733
|
+
source: 'env',
|
|
734
|
+
};
|
|
735
|
+
const { config } = await this.getSnapshot();
|
|
736
|
+
const configValue = config?.teamServerUrl ?? null;
|
|
737
|
+
return configValue
|
|
738
|
+
? { value: configValue, source: 'config' }
|
|
739
|
+
: { value: 'https://kakadeai.com', source: 'default' };
|
|
740
|
+
}
|
|
741
|
+
|
|
397
742
|
async providerForModel(modelId?: string | null): Promise<Provider | null> {
|
|
398
743
|
if (!modelId) return this.provider();
|
|
744
|
+
const ref = modelRefFromLegacy(modelId);
|
|
745
|
+
if (ref.source === 'gateway') {
|
|
746
|
+
const resolved = await this.providerForRef(ref);
|
|
747
|
+
return resolved;
|
|
748
|
+
}
|
|
399
749
|
const { config } = await this.getSnapshot();
|
|
400
750
|
const entry = config?.models.find((m) => m.id === modelId);
|
|
401
751
|
if (!entry?.apiKey) return null;
|
|
402
752
|
return resolveProvider(entry, config?.compactRatio);
|
|
403
753
|
}
|
|
404
754
|
|
|
755
|
+
/** Resolve a portable workflow role from fresh cross-process config. */
|
|
756
|
+
async providerForWorkflowRole(role: string): Promise<Provider | null> {
|
|
757
|
+
const { config } = await this.reload();
|
|
758
|
+
const modelId = config?.workflowModels?.[role];
|
|
759
|
+
return modelId ? this.providerForModel(modelId) : null;
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
private async providerForGatewayModel(
|
|
763
|
+
modelId: string,
|
|
764
|
+
): Promise<Provider | null> {
|
|
765
|
+
// A remembered gateway model must not make standalone mode inspect the
|
|
766
|
+
// team credential file or attempt a network request. The configured
|
|
767
|
+
// server is the explicit opt-in boundary; credentials are read only after
|
|
768
|
+
// it is present.
|
|
769
|
+
const configured = await this.resolveTeamServerUrl();
|
|
770
|
+
if (!configured.value) return null;
|
|
771
|
+
const resolved = await resolveOnlineModel(
|
|
772
|
+
modelId,
|
|
773
|
+
configured.value ?? undefined,
|
|
774
|
+
).catch(() => null);
|
|
775
|
+
if (!resolved) return null;
|
|
776
|
+
const { credentials, model } = resolved;
|
|
777
|
+
if (model.availability && model.availability.state !== 'available')
|
|
778
|
+
return null;
|
|
779
|
+
return gatewayProviderFromModel(credentials, model);
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
/** Resolve the persisted source-aware identity without making callers encode
|
|
783
|
+
* gateway selections as the legacy `online:<id>` string. */
|
|
784
|
+
async providerForRef(ref?: ModelRef | null): Promise<Provider | null> {
|
|
785
|
+
if (!ref) return this.provider();
|
|
786
|
+
return ref.source === 'gateway'
|
|
787
|
+
? this.providerForGatewayModel(ref.modelId)
|
|
788
|
+
: this.providerForModel(ref.modelId);
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
/**
|
|
792
|
+
* Resolve a persisted model reference and retain the reason when a gateway
|
|
793
|
+
* model cannot be used. The reason is intentionally kept at the local
|
|
794
|
+
* session boundary: it is UI/selection state, not a server-side session or
|
|
795
|
+
* prompt record.
|
|
796
|
+
*/
|
|
797
|
+
async resolveModelRef(ref?: ModelRef | null): Promise<ModelResolution> {
|
|
798
|
+
if (!ref)
|
|
799
|
+
return {
|
|
800
|
+
provider: await this.providerForModel(null),
|
|
801
|
+
fallbackReason: null,
|
|
802
|
+
};
|
|
803
|
+
if (ref.source === 'local')
|
|
804
|
+
return {
|
|
805
|
+
provider: await this.providerForRef(ref),
|
|
806
|
+
fallbackReason: null,
|
|
807
|
+
};
|
|
808
|
+
|
|
809
|
+
const configured = await this.resolveTeamServerUrl();
|
|
810
|
+
if (!configured.value)
|
|
811
|
+
return { provider: null, fallbackReason: 'logged_out' };
|
|
812
|
+
const credentials = await loadTeamCredentials(configured.value);
|
|
813
|
+
if (!credentials) return { provider: null, fallbackReason: 'logged_out' };
|
|
814
|
+
try {
|
|
815
|
+
const models = await fetchOnlineModels(credentials);
|
|
816
|
+
const model = models.find((item) => item.id === ref.modelId);
|
|
817
|
+
if (!model) return { provider: null, fallbackReason: 'grant_revoked' };
|
|
818
|
+
if (
|
|
819
|
+
model.availability?.state !== undefined &&
|
|
820
|
+
model.availability.state !== 'available'
|
|
821
|
+
)
|
|
822
|
+
return {
|
|
823
|
+
provider: null,
|
|
824
|
+
fallbackReason: gatewayFallbackReason(model),
|
|
825
|
+
};
|
|
826
|
+
return {
|
|
827
|
+
provider: gatewayProviderFromModel(credentials, model),
|
|
828
|
+
fallbackReason: null,
|
|
829
|
+
};
|
|
830
|
+
} catch (error) {
|
|
831
|
+
return {
|
|
832
|
+
provider: null,
|
|
833
|
+
fallbackReason: gatewayFallbackReason(undefined, error),
|
|
834
|
+
};
|
|
835
|
+
}
|
|
836
|
+
}
|
|
837
|
+
|
|
838
|
+
/** Online defaults are server-owned and fetched only when team credentials
|
|
839
|
+
* exist. No standalone launch calls this path. */
|
|
840
|
+
async onlineDefaultProvider(): Promise<Provider | null> {
|
|
841
|
+
const configured = await this.resolveTeamServerUrl();
|
|
842
|
+
if (!configured.value) return null;
|
|
843
|
+
const credentials = await import('../team/auth.js')
|
|
844
|
+
.then((module) =>
|
|
845
|
+
module.loadTeamCredentials(configured.value ?? undefined),
|
|
846
|
+
)
|
|
847
|
+
.catch(() => null);
|
|
848
|
+
if (!credentials) return null;
|
|
849
|
+
const models = await import('../team/auth.js')
|
|
850
|
+
.then((module) => module.fetchOnlineModels(credentials))
|
|
851
|
+
.catch(() => []);
|
|
852
|
+
const model = models.find(
|
|
853
|
+
(item) =>
|
|
854
|
+
item.isDefault &&
|
|
855
|
+
(!item.availability || item.availability.state === 'available'),
|
|
856
|
+
);
|
|
857
|
+
if (!model) return null;
|
|
858
|
+
return gatewayProviderFromModel(credentials, model);
|
|
859
|
+
}
|
|
860
|
+
|
|
405
861
|
/** 带 apiKey 的原始列表——仅服务端内部消费(桌面端 store.ts 的 readConfig() 会脱敏成 ModelView)。 */
|
|
406
862
|
async models(): Promise<ModelConfig[]> {
|
|
407
863
|
return (await this.getSnapshot()).config?.models ?? [];
|
|
408
864
|
}
|
|
409
865
|
|
|
410
866
|
async model(id: string): Promise<ModelConfig | null> {
|
|
411
|
-
return (
|
|
867
|
+
return (
|
|
868
|
+
(await this.getSnapshot()).config?.models.find(
|
|
869
|
+
(entry) => entry.id === id,
|
|
870
|
+
) ?? null
|
|
871
|
+
);
|
|
412
872
|
}
|
|
413
873
|
|
|
414
874
|
/**
|
|
@@ -419,16 +879,71 @@ export class ConfigService {
|
|
|
419
879
|
* ModelConfig 镜像到返回对象顶层,让 advisor.ts 的 `advisor.supportsVision` 读取方式
|
|
420
880
|
* 保持不变——它是解析后的便利视图,不是存储形状本身。
|
|
421
881
|
*/
|
|
422
|
-
async advisor(): Promise<
|
|
882
|
+
async advisor(): Promise<
|
|
883
|
+
(AdvisorConfig & { provider: Provider; supportsVision: boolean }) | null
|
|
884
|
+
> {
|
|
423
885
|
// fresh reload(不用 getSnapshot 的缓存)——CLI/TUI/Desktop/Cron 共享同一份 config.json,
|
|
424
886
|
// 跨进程时缓存会读到过期状态(如 A 进程 /advisor off 后 B 进程仍判定已启用)。这条不变量
|
|
425
887
|
// 由 test/configService.test.ts 的 "advisor() always does a fresh reload" 用例锁定。
|
|
426
888
|
const { config } = await this.reload();
|
|
427
889
|
const advisor = config?.advisor;
|
|
428
890
|
if (!advisor?.modelId) return null;
|
|
891
|
+
const advisorRef = modelRefFromLegacy(advisor.modelId);
|
|
892
|
+
if (advisorRef.source === 'gateway') {
|
|
893
|
+
const provider = await this.providerForRef(advisorRef);
|
|
894
|
+
return provider
|
|
895
|
+
? {
|
|
896
|
+
...advisor,
|
|
897
|
+
provider,
|
|
898
|
+
supportsVision: provider.supportsVision !== false,
|
|
899
|
+
}
|
|
900
|
+
: null;
|
|
901
|
+
}
|
|
429
902
|
const entry = config!.models.find((m) => m.id === advisor.modelId);
|
|
430
903
|
if (!entry?.apiKey) return null;
|
|
431
|
-
return {
|
|
904
|
+
return {
|
|
905
|
+
...advisor,
|
|
906
|
+
provider: resolveProvider(entry),
|
|
907
|
+
supportsVision: entry.supportsVision !== false,
|
|
908
|
+
};
|
|
909
|
+
}
|
|
910
|
+
|
|
911
|
+
/**
|
|
912
|
+
* 同步版 advisor 可用性探测——仅用于 resolveAvailability 这类工具列表探测场景。
|
|
913
|
+
* 读当前内存快照(可能略滞后于磁盘最新值),但调用频率高、对实时性要求不高,
|
|
914
|
+
* 下一轮 LLM 请求前 configService.reload() 会刷新。
|
|
915
|
+
*
|
|
916
|
+
* 为什么不用 advisor() 的 fresh reload:
|
|
917
|
+
* resolveAvailability 是在主循环构造工具集时同步调用的,不能 await;
|
|
918
|
+
* 而且每次工具集构造都读盘的话开销太大(主循环每轮都可能构造一次)。
|
|
919
|
+
*/
|
|
920
|
+
advisorAvailabilitySync(): {
|
|
921
|
+
enabled: boolean;
|
|
922
|
+
mode: 'auto' | 'manual';
|
|
923
|
+
modelId: string | null;
|
|
924
|
+
configured: boolean;
|
|
925
|
+
} {
|
|
926
|
+
const config = this.snapshot.config;
|
|
927
|
+
const advisor = config?.advisor;
|
|
928
|
+
if (!advisor?.modelId)
|
|
929
|
+
return { enabled: false, mode: 'auto', modelId: null, configured: false };
|
|
930
|
+
if (modelRefFromLegacy(advisor.modelId).source === 'gateway') {
|
|
931
|
+
return {
|
|
932
|
+
enabled: advisor.enabled !== false,
|
|
933
|
+
mode: advisor.mode === 'manual' ? 'manual' : 'auto',
|
|
934
|
+
modelId: advisor.modelId,
|
|
935
|
+
configured: true,
|
|
936
|
+
};
|
|
937
|
+
}
|
|
938
|
+
const entry = config?.models?.find((m) => m.id === advisor.modelId);
|
|
939
|
+
if (!entry?.apiKey)
|
|
940
|
+
return { enabled: false, mode: 'auto', modelId: null, configured: false };
|
|
941
|
+
return {
|
|
942
|
+
enabled: advisor.enabled !== false,
|
|
943
|
+
mode: advisor.mode === 'manual' ? 'manual' : 'auto',
|
|
944
|
+
modelId: advisor.modelId,
|
|
945
|
+
configured: true,
|
|
946
|
+
};
|
|
432
947
|
}
|
|
433
948
|
|
|
434
949
|
async subagents(): Promise<SubagentsConfig> {
|
|
@@ -440,7 +955,9 @@ export class ConfigService {
|
|
|
440
955
|
}
|
|
441
956
|
|
|
442
957
|
async computerUse(): Promise<ComputerUseConfig> {
|
|
443
|
-
return (
|
|
958
|
+
return (
|
|
959
|
+
(await this.reload()).config?.computerUse ?? DEFAULT_COMPUTER_USE_CONFIG
|
|
960
|
+
);
|
|
444
961
|
}
|
|
445
962
|
|
|
446
963
|
async tool<K extends ToolConfigKey>(key: K): Promise<ToolConfigs[K]> {
|
|
@@ -450,53 +967,104 @@ export class ConfigService {
|
|
|
450
967
|
async toolStatus(): Promise<Record<ToolConfigKey, ToolStatus>> {
|
|
451
968
|
const tools = (await this.reload()).config?.tools ?? {};
|
|
452
969
|
return {
|
|
453
|
-
webSearch: {
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
970
|
+
webSearch: {
|
|
971
|
+
state: tools.webSearch ? 'configured' : 'unconfigured',
|
|
972
|
+
configured: !!tools.webSearch,
|
|
973
|
+
},
|
|
974
|
+
imageGeneration: {
|
|
975
|
+
state: tools.imageGeneration ? 'configured' : 'unconfigured',
|
|
976
|
+
configured: !!tools.imageGeneration,
|
|
977
|
+
},
|
|
978
|
+
openRouter: {
|
|
979
|
+
state: tools.openRouter ? 'configured' : 'unconfigured',
|
|
980
|
+
configured: !!tools.openRouter,
|
|
981
|
+
},
|
|
982
|
+
textToSpeech: {
|
|
983
|
+
state: tools.textToSpeech ? 'configured' : 'unconfigured',
|
|
984
|
+
configured: !!tools.textToSpeech,
|
|
985
|
+
},
|
|
457
986
|
};
|
|
458
987
|
}
|
|
459
988
|
|
|
460
989
|
async savePatch(patch: ConfigPatch): Promise<ConfigSnapshot> {
|
|
461
990
|
const current = await this.getSnapshot();
|
|
462
|
-
if (current.readOnly)
|
|
463
|
-
|
|
991
|
+
if (current.readOnly)
|
|
992
|
+
throw new Error('当前使用环境变量只读配置,不能从设置页修改');
|
|
993
|
+
await updateMinimalAgentConfig((latest) =>
|
|
994
|
+
applyConfigPatch(latest ?? current.config, patch),
|
|
995
|
+
);
|
|
464
996
|
return this.reload();
|
|
465
997
|
}
|
|
466
998
|
|
|
467
999
|
/** 现在只服务 tools 分区的实时录入表单(imageGeneration/textToSpeech 多字段、可能中途非法)。 */
|
|
468
|
-
async autosave(
|
|
1000
|
+
async autosave(
|
|
1001
|
+
section: AutosaveSection,
|
|
1002
|
+
patch: ConfigPatch,
|
|
1003
|
+
): Promise<{
|
|
1004
|
+
snapshot: ConfigSnapshot;
|
|
1005
|
+
state: AutosaveState;
|
|
1006
|
+
draftFields: string[];
|
|
1007
|
+
}> {
|
|
469
1008
|
void section;
|
|
470
1009
|
const current = await this.getSnapshot();
|
|
471
|
-
if (current.readOnly)
|
|
1010
|
+
if (current.readOnly)
|
|
1011
|
+
throw new Error('当前使用环境变量只读配置,不能从设置页修改');
|
|
472
1012
|
if (!current.config) throw new Error('请先添加至少一个模型');
|
|
473
1013
|
let state: AutosaveState = 'applied';
|
|
474
1014
|
const draftFields: string[] = [];
|
|
475
1015
|
await updateMinimalAgentConfig((latest) => {
|
|
476
1016
|
let active = latest ?? current.config!;
|
|
477
|
-
if (patch.subagents)
|
|
478
|
-
|
|
1017
|
+
if (patch.subagents)
|
|
1018
|
+
active = {
|
|
1019
|
+
version: 4,
|
|
1020
|
+
...applyConfigPatch(active, { subagents: patch.subagents }),
|
|
1021
|
+
savedAt: active.savedAt,
|
|
1022
|
+
};
|
|
1023
|
+
if (patch.computerUse)
|
|
1024
|
+
active = {
|
|
1025
|
+
version: 4,
|
|
1026
|
+
...applyConfigPatch(active, { computerUse: patch.computerUse }),
|
|
1027
|
+
savedAt: active.savedAt,
|
|
1028
|
+
};
|
|
479
1029
|
const drafts = structuredClone(active.drafts ?? {});
|
|
480
1030
|
for (const key of Object.keys(patch.tools ?? {})) {
|
|
481
|
-
const rawPiece =
|
|
1031
|
+
const rawPiece =
|
|
1032
|
+
patch.tools![key as keyof NonNullable<ConfigPatch['tools']>];
|
|
482
1033
|
if (!rawPiece) continue;
|
|
483
|
-
const previous =
|
|
484
|
-
const merged = mergeRecord(
|
|
1034
|
+
const previous = drafts.tools?.[key];
|
|
1035
|
+
const merged = mergeRecord(
|
|
1036
|
+
previous,
|
|
1037
|
+
rawPiece as Record<string, unknown>,
|
|
1038
|
+
);
|
|
485
1039
|
try {
|
|
486
|
-
active = {
|
|
487
|
-
|
|
1040
|
+
active = {
|
|
1041
|
+
version: 4,
|
|
1042
|
+
...applyConfigPatch(active, {
|
|
1043
|
+
tools: { [key]: merged } as ConfigPatch['tools'],
|
|
1044
|
+
}),
|
|
1045
|
+
savedAt: active.savedAt,
|
|
1046
|
+
};
|
|
1047
|
+
drafts.tools = omitKey(drafts.tools, key);
|
|
488
1048
|
} catch {
|
|
489
1049
|
state = 'draft';
|
|
490
1050
|
draftFields.push(key);
|
|
491
|
-
|
|
1051
|
+
const toolDrafts = drafts.tools ?? {};
|
|
1052
|
+
toolDrafts[key] = merged;
|
|
1053
|
+
drafts.tools = toolDrafts;
|
|
492
1054
|
}
|
|
493
1055
|
}
|
|
494
|
-
if (!hasEntries(drafts.tools))
|
|
1056
|
+
if (!hasEntries(drafts.tools)) drafts.tools = undefined;
|
|
495
1057
|
return {
|
|
496
|
-
models: active.models,
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
1058
|
+
models: active.models,
|
|
1059
|
+
baseModelId: active.baseModelId,
|
|
1060
|
+
...(active.compactRatio !== undefined
|
|
1061
|
+
? { compactRatio: active.compactRatio }
|
|
1062
|
+
: {}),
|
|
1063
|
+
advisor: active.advisor,
|
|
1064
|
+
subagents: active.subagents,
|
|
1065
|
+
browser: active.browser,
|
|
1066
|
+
computerUse: active.computerUse,
|
|
1067
|
+
tools: active.tools,
|
|
500
1068
|
...(hasEntries(drafts) ? { drafts } : {}),
|
|
501
1069
|
};
|
|
502
1070
|
});
|
|
@@ -507,14 +1075,22 @@ export class ConfigService {
|
|
|
507
1075
|
export const configService = new ConfigService();
|
|
508
1076
|
|
|
509
1077
|
export const SENSITIVE_ENV_KEYS = new Set([
|
|
510
|
-
'MINIMAL_AGENT_API_KEY',
|
|
511
|
-
'
|
|
1078
|
+
'MINIMAL_AGENT_API_KEY',
|
|
1079
|
+
'TAVILY_API_KEY',
|
|
1080
|
+
'OPENAI_IMAGE_API_KEY',
|
|
1081
|
+
'OPENROUTER_API_KEY',
|
|
1082
|
+
'MINIMAX_API_KEY',
|
|
1083
|
+
'MINIMAX_GROUP_ID',
|
|
512
1084
|
]);
|
|
513
1085
|
|
|
514
1086
|
/** Child processes inherit normal runtime variables, never model or tool credentials. */
|
|
515
|
-
export function sanitizeCredentialEnv(
|
|
516
|
-
|
|
517
|
-
|
|
1087
|
+
export function sanitizeCredentialEnv(
|
|
1088
|
+
source: NodeJS.ProcessEnv = process.env,
|
|
1089
|
+
): NodeJS.ProcessEnv {
|
|
1090
|
+
let env: NodeJS.ProcessEnv = { ...source };
|
|
1091
|
+
for (const key of SENSITIVE_ENV_KEYS) {
|
|
1092
|
+
env = omitKey(env, key) ?? {};
|
|
1093
|
+
}
|
|
518
1094
|
return env;
|
|
519
1095
|
}
|
|
520
1096
|
|
|
@@ -527,28 +1103,33 @@ export async function trustedToolEnv(
|
|
|
527
1103
|
let cfg = await service.tool('imageGeneration');
|
|
528
1104
|
if (!cfg) {
|
|
529
1105
|
const openRouter = await service.tool('openRouter');
|
|
530
|
-
if (openRouter)
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
1106
|
+
if (openRouter)
|
|
1107
|
+
cfg = {
|
|
1108
|
+
provider: 'openrouter',
|
|
1109
|
+
baseURL: 'https://openrouter.ai/api/v1',
|
|
1110
|
+
apiKey: openRouter.apiKey,
|
|
1111
|
+
model: 'google/gemini-3-pro-image-preview',
|
|
1112
|
+
protocol: 'chat',
|
|
1113
|
+
};
|
|
537
1114
|
}
|
|
538
1115
|
if (cfg) {
|
|
539
1116
|
env.OPENAI_IMAGE_PROVIDER = cfg.provider;
|
|
540
1117
|
env.OPENAI_IMAGE_BASE_URL = cfg.baseURL;
|
|
541
1118
|
env.OPENAI_IMAGE_API_KEY = cfg.apiKey;
|
|
542
1119
|
env.OPENAI_IMAGE_MODEL = cfg.model;
|
|
543
|
-
if (cfg.protocol && cfg.protocol !== 'auto')
|
|
544
|
-
|
|
1120
|
+
if (cfg.protocol && cfg.protocol !== 'auto')
|
|
1121
|
+
env.OPENAI_IMAGE_PROTOCOL = cfg.protocol;
|
|
1122
|
+
else env.OPENAI_IMAGE_PROTOCOL = undefined;
|
|
545
1123
|
}
|
|
546
1124
|
} else if (kind === 'openRouter') {
|
|
547
1125
|
const cfg = await service.tool('openRouter');
|
|
548
1126
|
if (cfg) env.OPENROUTER_API_KEY = cfg.apiKey;
|
|
549
1127
|
} else {
|
|
550
1128
|
const cfg = await service.tool('textToSpeech');
|
|
551
|
-
if (cfg) {
|
|
1129
|
+
if (cfg) {
|
|
1130
|
+
env.MINIMAX_API_KEY = cfg.apiKey;
|
|
1131
|
+
env.MINIMAX_GROUP_ID = cfg.groupId;
|
|
1132
|
+
}
|
|
552
1133
|
}
|
|
553
1134
|
return env;
|
|
554
1135
|
}
|