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/llm/client.ts
CHANGED
|
@@ -24,7 +24,14 @@
|
|
|
24
24
|
* ============================================================
|
|
25
25
|
*/
|
|
26
26
|
|
|
27
|
-
import type {
|
|
27
|
+
import type {
|
|
28
|
+
GatewayProvider,
|
|
29
|
+
LlmStreamEvent,
|
|
30
|
+
LocalProvider,
|
|
31
|
+
Message,
|
|
32
|
+
Provider,
|
|
33
|
+
Tool,
|
|
34
|
+
} from '../types.js';
|
|
28
35
|
import { dlog } from '../utils/debugLog.js';
|
|
29
36
|
import {
|
|
30
37
|
buildOpenAIRequest,
|
|
@@ -35,7 +42,13 @@ import {
|
|
|
35
42
|
isResponseFormatUnsupported,
|
|
36
43
|
} from './providers/openaiFormat.js';
|
|
37
44
|
import { promptMaterialHash, shortCacheScope } from './promptCache.js';
|
|
38
|
-
import {
|
|
45
|
+
import {
|
|
46
|
+
buildAnthropicRequest,
|
|
47
|
+
createAnthropicSSEParser,
|
|
48
|
+
} from './providers/anthropicFormat.js';
|
|
49
|
+
import { chatGateway } from './gatewayClient.js';
|
|
50
|
+
import type { ModelProvider } from './modelProvider.js';
|
|
51
|
+
import { prepareMessagesForProvider } from './messageProvenance.js';
|
|
39
52
|
|
|
40
53
|
/**
|
|
41
54
|
* 记住"这个 baseURL::model 不支持 response_format.json_schema",避免每次都先撞 400。
|
|
@@ -132,7 +145,8 @@ let backoffTier = 0;
|
|
|
132
145
|
* retryAfterMs 有值(429 的 Retry-After 头)时听服务器的(仍封顶 60s、仍抬档)。
|
|
133
146
|
*/
|
|
134
147
|
function nextRetryDelayMs(retryAfterMs?: number): number {
|
|
135
|
-
const tierMs =
|
|
148
|
+
const tierMs =
|
|
149
|
+
BACKOFF_TIERS_MS[Math.min(backoffTier, BACKOFF_TIERS_MS.length - 1)];
|
|
136
150
|
backoffTier = Math.min(backoffTier + 1, BACKOFF_TIERS_MS.length - 1);
|
|
137
151
|
if (retryAfterMs !== undefined) return Math.min(retryAfterMs, 60_000);
|
|
138
152
|
const jittered = tierMs * (0.85 + Math.random() * 0.3);
|
|
@@ -158,7 +172,12 @@ export function _nextRetryDelayMs(retryAfterMs?: number): number {
|
|
|
158
172
|
|
|
159
173
|
/** 可重试的 HTTP 状态码。4xx 除 408/425/429 外不重试(重试无意义)。 */
|
|
160
174
|
function isRetryableStatus(status: number): boolean {
|
|
161
|
-
return
|
|
175
|
+
return (
|
|
176
|
+
status === 408 ||
|
|
177
|
+
status === 425 ||
|
|
178
|
+
status === 429 ||
|
|
179
|
+
(status >= 500 && status < 600)
|
|
180
|
+
);
|
|
162
181
|
}
|
|
163
182
|
|
|
164
183
|
/** 鉴权类错误:明确告诉用户"key 失效/过期/无权访问",避免重试无意义。 */
|
|
@@ -253,7 +272,9 @@ export function createIdleWatchdog(
|
|
|
253
272
|
};
|
|
254
273
|
arm();
|
|
255
274
|
return {
|
|
256
|
-
signal: userSignal
|
|
275
|
+
signal: userSignal
|
|
276
|
+
? AbortSignal.any([userSignal, ctrl.signal])
|
|
277
|
+
: ctrl.signal,
|
|
257
278
|
kick: arm,
|
|
258
279
|
dispose: () => {
|
|
259
280
|
if (timer) clearTimeout(timer);
|
|
@@ -281,8 +302,42 @@ export function createIdleWatchdog(
|
|
|
281
302
|
* }
|
|
282
303
|
* ```
|
|
283
304
|
*/
|
|
284
|
-
export
|
|
285
|
-
|
|
305
|
+
export function createModelProvider(provider: Provider): ModelProvider {
|
|
306
|
+
return {
|
|
307
|
+
source: provider.source === 'gateway' ? 'gateway' : 'local',
|
|
308
|
+
descriptor: provider,
|
|
309
|
+
stream: (request) => {
|
|
310
|
+
if (provider.source === 'gateway') {
|
|
311
|
+
if (
|
|
312
|
+
request.responseFormat &&
|
|
313
|
+
provider.gatewayCapabilities?.structuredOutput === false
|
|
314
|
+
) {
|
|
315
|
+
throw new Error('当前在线模型不支持结构化输出');
|
|
316
|
+
}
|
|
317
|
+
return chatGateway({
|
|
318
|
+
provider: provider as GatewayProvider,
|
|
319
|
+
messages: request.messages,
|
|
320
|
+
tools: request.tools,
|
|
321
|
+
signal: request.signal,
|
|
322
|
+
conversationMode: request.conversationMode,
|
|
323
|
+
responseFormat: request.responseFormat,
|
|
324
|
+
});
|
|
325
|
+
}
|
|
326
|
+
return streamLocal({ ...request, provider: provider as LocalProvider });
|
|
327
|
+
},
|
|
328
|
+
};
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
export async function* chat(
|
|
332
|
+
args: Omit<Parameters<typeof streamLocal>[0], 'provider'> & {
|
|
333
|
+
provider: Provider;
|
|
334
|
+
},
|
|
335
|
+
): AsyncGenerator<LlmStreamEvent, void, void> {
|
|
336
|
+
yield* createModelProvider(args.provider).stream(args);
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
async function* streamLocal(args: {
|
|
340
|
+
provider: LocalProvider;
|
|
286
341
|
messages: Message[];
|
|
287
342
|
tools: Tool[];
|
|
288
343
|
signal?: AbortSignal;
|
|
@@ -292,12 +347,36 @@ export async function* chat(args: {
|
|
|
292
347
|
* 传了会在发请求前直接抛错,不浪费一次网络往返。*/
|
|
293
348
|
responseFormat?: {
|
|
294
349
|
type: 'json_schema';
|
|
295
|
-
json_schema: {
|
|
350
|
+
json_schema: {
|
|
351
|
+
name: string;
|
|
352
|
+
schema: Record<string, unknown>;
|
|
353
|
+
strict?: boolean;
|
|
354
|
+
};
|
|
296
355
|
};
|
|
297
356
|
promptCache?: import('../types.js').PromptCacheContext;
|
|
357
|
+
/** 当前调用所在会话模式;群聊下未标注的旧历史按私聊数据处理。 */
|
|
358
|
+
conversationMode?: import('../runtime/agentRuntimeContext.js').ConversationMode;
|
|
359
|
+
/**
|
|
360
|
+
* cache_edits 式回收状态(Anthropic 格式 + provider.cacheEdits 开启时由 loop 传入)。
|
|
361
|
+
* 传入后 buildAnthropicRequest 会随请求发送删除指令并维护 pinned/pendingNew——
|
|
362
|
+
* 该状态有跨轮生命周期(pinned 每轮重发),调用方必须传同一份会话级对象,
|
|
363
|
+
* 并在 compact 后调 resetCacheEdits。OpenAI 格式忽略此参数。
|
|
364
|
+
*/
|
|
365
|
+
cacheEdits?: import('../types.js').CacheEditsState;
|
|
366
|
+
visionPolicy?: 'required' | 'optional-history';
|
|
298
367
|
}): AsyncGenerator<LlmStreamEvent, void, void> {
|
|
299
|
-
const {
|
|
300
|
-
|
|
368
|
+
const {
|
|
369
|
+
provider,
|
|
370
|
+
messages,
|
|
371
|
+
tools,
|
|
372
|
+
signal,
|
|
373
|
+
responseFormat,
|
|
374
|
+
promptCache,
|
|
375
|
+
conversationMode,
|
|
376
|
+
cacheEdits,
|
|
377
|
+
} = args;
|
|
378
|
+
const format: 'openai' | 'anthropic' =
|
|
379
|
+
provider.apiFormat === 'anthropic' ? 'anthropic' : 'openai';
|
|
301
380
|
|
|
302
381
|
if (format === 'anthropic' && responseFormat) {
|
|
303
382
|
throw new Error(
|
|
@@ -310,12 +389,10 @@ export async function* chat(args: {
|
|
|
310
389
|
// 过滤并清洁 messages 数组,剥离 timestamp, id, _ephemeral 等本地渲染元数据。
|
|
311
390
|
// 两种格式共用同一份清洗后的内部形状——Anthropic 分支的翻译(system 摘除/
|
|
312
391
|
// tool_calls→tool_use 等)在 buildAnthropicRequest 内部做,不在这里做。
|
|
313
|
-
let cleanedMessages: Message[] =
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
return rest;
|
|
318
|
-
});
|
|
392
|
+
let cleanedMessages: Message[] = prepareMessagesForProvider(
|
|
393
|
+
messages,
|
|
394
|
+
conversationMode ?? 'private',
|
|
395
|
+
);
|
|
319
396
|
|
|
320
397
|
const includeUsage = process.env.MINIMAL_AGENT_DISABLE_USAGE !== '1';
|
|
321
398
|
const rfKey = `${provider.baseURL}::${provider.model}`;
|
|
@@ -323,7 +400,9 @@ export async function* chat(args: {
|
|
|
323
400
|
// OpenAI 分支专属的「就地退化」状态;Anthropic 无此概念,两个 flag 恒为 false,
|
|
324
401
|
// 下面的退化 if 分支天然不会进入(不需要额外的 format 判断散落各处)。
|
|
325
402
|
let wantResponseFormat =
|
|
326
|
-
format === 'openai' &&
|
|
403
|
+
format === 'openai' &&
|
|
404
|
+
responseFormat !== undefined &&
|
|
405
|
+
!rfUnsupported.has(rfKey);
|
|
327
406
|
let wantMaxTokens =
|
|
328
407
|
format === 'openai' &&
|
|
329
408
|
typeof provider.maxTokens === 'number' &&
|
|
@@ -341,9 +420,15 @@ export async function* chat(args: {
|
|
|
341
420
|
const c = (m as { content?: unknown }).content;
|
|
342
421
|
if (!Array.isArray(c)) return m;
|
|
343
422
|
const textOnly = c.filter((p: any) => p.type === 'text');
|
|
344
|
-
const newContent =
|
|
345
|
-
|
|
346
|
-
|
|
423
|
+
const newContent =
|
|
424
|
+
textOnly.length > 0
|
|
425
|
+
? textOnly.map((p: any) => ({ type: 'text' as const, text: p.text }))
|
|
426
|
+
: [
|
|
427
|
+
{
|
|
428
|
+
type: 'text' as const,
|
|
429
|
+
text: '[图片内容未发送:当前模型不支持图片输入]',
|
|
430
|
+
},
|
|
431
|
+
];
|
|
347
432
|
return { ...m, content: newContent } as Message;
|
|
348
433
|
});
|
|
349
434
|
|
|
@@ -354,7 +439,12 @@ export async function* chat(args: {
|
|
|
354
439
|
const postChat = (): Promise<Response> => {
|
|
355
440
|
const req =
|
|
356
441
|
format === 'anthropic'
|
|
357
|
-
? buildAnthropicRequest({
|
|
442
|
+
? buildAnthropicRequest({
|
|
443
|
+
provider,
|
|
444
|
+
cleanedMessages,
|
|
445
|
+
tools,
|
|
446
|
+
...(cacheEdits ? { cacheEdits } : {}),
|
|
447
|
+
})
|
|
358
448
|
: buildOpenAIRequest({
|
|
359
449
|
provider,
|
|
360
450
|
cleanedMessages,
|
|
@@ -366,7 +456,12 @@ export async function* chat(args: {
|
|
|
366
456
|
withPromptCacheKey: wantPromptCacheKey,
|
|
367
457
|
responseFormat,
|
|
368
458
|
});
|
|
369
|
-
return fetch(req.url, {
|
|
459
|
+
return fetch(req.url, {
|
|
460
|
+
method: 'POST',
|
|
461
|
+
headers: req.headers,
|
|
462
|
+
body: req.body,
|
|
463
|
+
signal: watchdog.signal,
|
|
464
|
+
});
|
|
370
465
|
};
|
|
371
466
|
|
|
372
467
|
const reqId = ++reqCounter;
|
|
@@ -387,7 +482,10 @@ export async function* chat(args: {
|
|
|
387
482
|
toolSchemaHash: promptMaterialHash(
|
|
388
483
|
tools.map((tool) => ({
|
|
389
484
|
name: tool.name,
|
|
390
|
-
description:
|
|
485
|
+
description:
|
|
486
|
+
typeof tool.description === 'function'
|
|
487
|
+
? tool.description()
|
|
488
|
+
: tool.description,
|
|
391
489
|
parameters: tool.parameters,
|
|
392
490
|
})),
|
|
393
491
|
),
|
|
@@ -409,11 +507,16 @@ export async function* chat(args: {
|
|
|
409
507
|
): Promise<'break' | 'retry'> => {
|
|
410
508
|
if (signal?.aborted) throw makeAbortError();
|
|
411
509
|
const err = e as Error;
|
|
412
|
-
lastErr = new Error(
|
|
510
|
+
lastErr = new Error(
|
|
511
|
+
`LLM 网络错误 [${err.name ?? 'NetworkError'}]:${err.message}`,
|
|
512
|
+
);
|
|
413
513
|
dlog('llm', `req#${reqId} attempt ${attempt} ${label}:`, err.message);
|
|
414
514
|
if (attempt >= DEFAULT_RETRY.maxAttempts) return 'break';
|
|
415
515
|
const delay = nextRetryDelayMs();
|
|
416
|
-
dlog(
|
|
516
|
+
dlog(
|
|
517
|
+
'llm',
|
|
518
|
+
`req#${reqId} retry in ${delay}ms (attempt ${attempt + 1}/${DEFAULT_RETRY.maxAttempts}, tier=${backoffTier})`,
|
|
519
|
+
);
|
|
417
520
|
await sleep(delay, signal);
|
|
418
521
|
return 'retry';
|
|
419
522
|
};
|
|
@@ -435,7 +538,10 @@ export async function* chat(args: {
|
|
|
435
538
|
// 1) 用户主动 abort → handleNetworkError 里立刻抛
|
|
436
539
|
// 2) AbortSignal.timeout 触发的超时(Fetch 空闲超时还没回)
|
|
437
540
|
// 3) 其它网络错误(ECONNRESET / fetch failed / DNS 失败)→ 全部按"可重试"处理
|
|
438
|
-
if (
|
|
541
|
+
if (
|
|
542
|
+
(await handleNetworkError(e, attempt, 'failed (network)')) === 'break'
|
|
543
|
+
)
|
|
544
|
+
break;
|
|
439
545
|
continue;
|
|
440
546
|
}
|
|
441
547
|
|
|
@@ -456,7 +562,14 @@ export async function* chat(args: {
|
|
|
456
562
|
resp = await postChat();
|
|
457
563
|
watchdog.kick();
|
|
458
564
|
} catch (e) {
|
|
459
|
-
if (
|
|
565
|
+
if (
|
|
566
|
+
(await handleNetworkError(
|
|
567
|
+
e,
|
|
568
|
+
attempt,
|
|
569
|
+
'cache-key fallback fetch failed',
|
|
570
|
+
)) === 'break'
|
|
571
|
+
)
|
|
572
|
+
break;
|
|
460
573
|
continue;
|
|
461
574
|
}
|
|
462
575
|
if (resp.ok) break;
|
|
@@ -464,7 +577,10 @@ export async function* chat(args: {
|
|
|
464
577
|
}
|
|
465
578
|
|
|
466
579
|
// 退化①:response_format.json_schema 不支持
|
|
467
|
-
if (
|
|
580
|
+
if (
|
|
581
|
+
wantResponseFormat &&
|
|
582
|
+
isResponseFormatUnsupported(resp.status, errText)
|
|
583
|
+
) {
|
|
468
584
|
// 记住该 model 不支持,单进程内后续结构化调用直接跳过 response_format(不再撞 400)。
|
|
469
585
|
rfUnsupported.add(rfKey);
|
|
470
586
|
console.warn(
|
|
@@ -478,7 +594,11 @@ export async function* chat(args: {
|
|
|
478
594
|
watchdog.kick();
|
|
479
595
|
} catch (e) {
|
|
480
596
|
// 退化二次 fetch 也网络错:把错误外提到外层重试循环
|
|
481
|
-
if (
|
|
597
|
+
if (
|
|
598
|
+
(await handleNetworkError(e, attempt, 'fallback fetch failed')) ===
|
|
599
|
+
'break'
|
|
600
|
+
)
|
|
601
|
+
break;
|
|
482
602
|
continue;
|
|
483
603
|
}
|
|
484
604
|
if (resp.ok) break;
|
|
@@ -498,7 +618,11 @@ export async function* chat(args: {
|
|
|
498
618
|
resp = await postChat();
|
|
499
619
|
watchdog.kick();
|
|
500
620
|
} catch (e) {
|
|
501
|
-
if (
|
|
621
|
+
if (
|
|
622
|
+
(await handleNetworkError(e, attempt, 'fallback fetch failed')) ===
|
|
623
|
+
'break'
|
|
624
|
+
)
|
|
625
|
+
break;
|
|
502
626
|
continue;
|
|
503
627
|
}
|
|
504
628
|
if (resp.ok) break;
|
|
@@ -510,9 +634,18 @@ export async function* chat(args: {
|
|
|
510
634
|
if (!imagesStripped && isImageInputRejected(resp.status, errText)) {
|
|
511
635
|
const hasImages = cleanedMessages.some((m) => {
|
|
512
636
|
const c = (m as { content?: unknown }).content;
|
|
513
|
-
return
|
|
637
|
+
return (
|
|
638
|
+
Array.isArray(c) &&
|
|
639
|
+
c.some((p: any) => p.type === 'image_url' || p.type === 'video_url')
|
|
640
|
+
);
|
|
514
641
|
});
|
|
515
642
|
if (hasImages) {
|
|
643
|
+
if (args.visionPolicy === 'required') {
|
|
644
|
+
throw new Error(
|
|
645
|
+
`当前模型拒绝了本轮必需的图片输入,已停止而未删除图片重试。` +
|
|
646
|
+
`请改用支持视觉的模型或检查模型配置。Provider 返回:${errText.slice(0, 500)}`,
|
|
647
|
+
);
|
|
648
|
+
}
|
|
516
649
|
console.warn(
|
|
517
650
|
`[minimal-agent] provider model "${provider.model}" 不支持图片输入,` +
|
|
518
651
|
`已剥离图片重发(仅保留文本内容)。如该模型实际支持视觉,请在模型设置中确认。`,
|
|
@@ -523,7 +656,11 @@ export async function* chat(args: {
|
|
|
523
656
|
resp = await postChat();
|
|
524
657
|
watchdog.kick();
|
|
525
658
|
} catch (e) {
|
|
526
|
-
if (
|
|
659
|
+
if (
|
|
660
|
+
(await handleNetworkError(e, attempt, 'fallback fetch failed')) ===
|
|
661
|
+
'break'
|
|
662
|
+
)
|
|
663
|
+
break;
|
|
527
664
|
continue;
|
|
528
665
|
}
|
|
529
666
|
if (resp.ok) break;
|
|
@@ -538,13 +675,20 @@ export async function* chat(args: {
|
|
|
538
675
|
|
|
539
676
|
// 5xx / 408 / 425 / 429:可重试
|
|
540
677
|
lastErr = buildChatError(resp, errText);
|
|
541
|
-
dlog(
|
|
678
|
+
dlog(
|
|
679
|
+
'llm',
|
|
680
|
+
`req#${reqId} attempt ${attempt} failed [${resp.status}]:`,
|
|
681
|
+
errText.slice(0, 200),
|
|
682
|
+
);
|
|
542
683
|
if (attempt >= DEFAULT_RETRY.maxAttempts) break;
|
|
543
684
|
|
|
544
685
|
// 429 优先用 Retry-After 头,否则走自适应档位退避
|
|
545
686
|
const retryAfterMs = parseRetryAfter(resp);
|
|
546
687
|
const delay = nextRetryDelayMs(retryAfterMs);
|
|
547
|
-
dlog(
|
|
688
|
+
dlog(
|
|
689
|
+
'llm',
|
|
690
|
+
`req#${reqId} retry in ${delay}ms (attempt ${attempt + 1}/${DEFAULT_RETRY.maxAttempts}, tier=${backoffTier}, retryAfter=${retryAfterMs ?? 'tier'})`,
|
|
691
|
+
);
|
|
548
692
|
await sleep(delay, signal);
|
|
549
693
|
}
|
|
550
694
|
|
|
@@ -567,7 +711,10 @@ export async function* chat(args: {
|
|
|
567
711
|
const decoder = new TextDecoder();
|
|
568
712
|
/** 临时缓冲:HTTP chunk 不一定按行切,要自己拼 */
|
|
569
713
|
let buffer = '';
|
|
570
|
-
const parser =
|
|
714
|
+
const parser =
|
|
715
|
+
format === 'anthropic'
|
|
716
|
+
? createAnthropicSSEParser()
|
|
717
|
+
: createOpenAISSEParser();
|
|
571
718
|
|
|
572
719
|
try {
|
|
573
720
|
while (true) {
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import { onlineToolCategory } from '../team/toolPolicy.js';
|
|
3
|
+
import { prepareMessagesForProvider } from './messageProvenance.js';
|
|
4
|
+
const IDLE_TIMEOUT_MS = Number(process.env.MINIMAL_AGENT_GATEWAY_IDLE_TIMEOUT_MS) > 0
|
|
5
|
+
? Number(process.env.MINIMAL_AGENT_GATEWAY_IDLE_TIMEOUT_MS)
|
|
6
|
+
: 120_000;
|
|
7
|
+
const DEADLINE_MS = Number(process.env.MINIMAL_AGENT_GATEWAY_DEADLINE_MS) > 0
|
|
8
|
+
? Number(process.env.MINIMAL_AGENT_GATEWAY_DEADLINE_MS)
|
|
9
|
+
: 15 * 60_000;
|
|
10
|
+
function gatewayTools(tools, allowedCategories) {
|
|
11
|
+
const allowed = allowedCategories ? new Set(allowedCategories) : null;
|
|
12
|
+
return tools.flatMap((tool) => {
|
|
13
|
+
const category = onlineToolCategory(tool.name);
|
|
14
|
+
if (!category || (allowed && !allowed.has(category)))
|
|
15
|
+
return [];
|
|
16
|
+
return [
|
|
17
|
+
{
|
|
18
|
+
type: 'function',
|
|
19
|
+
category,
|
|
20
|
+
function: {
|
|
21
|
+
name: tool.name,
|
|
22
|
+
...(typeof tool.description === 'string'
|
|
23
|
+
? { description: tool.description }
|
|
24
|
+
: {}),
|
|
25
|
+
parameters: tool.parameters,
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
];
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
function safeMessages(messages) {
|
|
32
|
+
return messages.map((message) => {
|
|
33
|
+
const { id: _id, timestamp: _timestamp, attachments: _attachments, browserReferences: _browserReferences, provenance: _provenance, display_metadata: _displayMetadata, tool_result_policy: _toolResultPolicy, _ephemeral, ...safe } = message;
|
|
34
|
+
return safe;
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
export class GatewayClientError extends Error {
|
|
38
|
+
code;
|
|
39
|
+
retryable;
|
|
40
|
+
constructor(code, retryable, message) {
|
|
41
|
+
// ⚠️ code 必须烘进 message 文本,不能只存在 .code 属性上:这个错误穿过
|
|
42
|
+
// loop.ts(只取 `.message`)→ 引擎 runner(转成纯字符串 blocker)→
|
|
43
|
+
// workflowController 的 isUsageLimitError 文本兜底,每一层都只剩字符串。
|
|
44
|
+
// 不烘进去,`isUsageLimitError` 的「同时认 code 与文本」这句注释就是一句空话——
|
|
45
|
+
// 之前正是这样:message 是纯人话(如「设备授权不存在或已过期」),从不包含
|
|
46
|
+
// DEVICE_REVOKED 之类的码,文本兜底恒为 false,usage_limited 在真实调用链上
|
|
47
|
+
// 从未被触发过。
|
|
48
|
+
super(`[${code}] ${message}`);
|
|
49
|
+
this.code = code;
|
|
50
|
+
this.retryable = retryable;
|
|
51
|
+
this.name = 'GatewayClientError';
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* 服务端「配额/授权不给了」的错误码 —— **唯一真值**。
|
|
56
|
+
*
|
|
57
|
+
* 与 Goal 的 `usage_limited` 状态一一对应,也正是它与 `budget_limited` 的分界:
|
|
58
|
+
* · `budget_limited` = **用户自己设的** token 额度用完 → 加预算即可继续。
|
|
59
|
+
* · `usage_limited` = **服务端配额/授权**不给了 → 加预算没用,要去解决授权或等周期重置。
|
|
60
|
+
* 两者都可 resume,但恢复动作完全不同,所以不能合并成一个状态。
|
|
61
|
+
*
|
|
62
|
+
* 服务端侧的产出点:`minimal-server/src/core/gatewayInference.ts`。
|
|
63
|
+
*/
|
|
64
|
+
const USAGE_LIMIT_CODES = new Set([
|
|
65
|
+
'REQUEST_QUOTA_EXHAUSTED',
|
|
66
|
+
'TOKEN_QUOTA_EXHAUSTED',
|
|
67
|
+
'CONCURRENCY_EXCEEDED',
|
|
68
|
+
'GRANT_REVOKED',
|
|
69
|
+
'DEVICE_REVOKED',
|
|
70
|
+
]);
|
|
71
|
+
/**
|
|
72
|
+
* 判断一个错误是不是「服务端配额/授权不给了」。
|
|
73
|
+
*
|
|
74
|
+
* ⚠️ 同时认 `GatewayClientError.code` 与错误文本:错误穿过 loop → 引擎 → 工作流
|
|
75
|
+
* 这几层之后往往只剩一个字符串(`llm_error` 事件带的是 message),
|
|
76
|
+
* 只认 code 会在真实链路上恒为 false。
|
|
77
|
+
*/
|
|
78
|
+
export function isUsageLimitError(error) {
|
|
79
|
+
if (error instanceof GatewayClientError)
|
|
80
|
+
return USAGE_LIMIT_CODES.has(error.code);
|
|
81
|
+
const text = error instanceof Error ? error.message : String(error ?? '');
|
|
82
|
+
return [...USAGE_LIMIT_CODES].some((code) => text.includes(code));
|
|
83
|
+
}
|
|
84
|
+
function combinedSignal(signal) {
|
|
85
|
+
const timeout = AbortSignal.timeout(DEADLINE_MS);
|
|
86
|
+
return signal ? AbortSignal.any([signal, timeout]) : timeout;
|
|
87
|
+
}
|
|
88
|
+
async function readWithIdleTimeout(promise, signal) {
|
|
89
|
+
let timer;
|
|
90
|
+
let onAbort;
|
|
91
|
+
try {
|
|
92
|
+
return await Promise.race([
|
|
93
|
+
promise,
|
|
94
|
+
new Promise((_, reject) => {
|
|
95
|
+
timer = setTimeout(() => reject(new GatewayClientError('PROVIDER_TIMEOUT', true, '在线模型流式响应超时')), IDLE_TIMEOUT_MS);
|
|
96
|
+
onAbort = () => reject(new DOMException('aborted', 'AbortError'));
|
|
97
|
+
signal.addEventListener('abort', onAbort, { once: true });
|
|
98
|
+
}),
|
|
99
|
+
]);
|
|
100
|
+
}
|
|
101
|
+
finally {
|
|
102
|
+
if (timer)
|
|
103
|
+
clearTimeout(timer);
|
|
104
|
+
if (onAbort)
|
|
105
|
+
signal.removeEventListener('abort', onAbort);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
/** Gateway transport only. The caller retains the existing Agent/tool loop. */
|
|
109
|
+
export async function* chatGateway(args) {
|
|
110
|
+
const { provider, messages, tools, signal: parentSignal, conversationMode, responseFormat, } = args;
|
|
111
|
+
if (!provider.gatewayUrl || !provider.gatewayToken || !provider.remoteModelId)
|
|
112
|
+
throw new GatewayClientError('DEVICE_REVOKED', false, '在线模型设备授权不存在或已过期');
|
|
113
|
+
const url = `${provider.gatewayUrl.replace(/\/$/, '')}/api/inference/chat/stream`;
|
|
114
|
+
const payload = JSON.stringify({
|
|
115
|
+
requestId: randomUUID(),
|
|
116
|
+
modelId: provider.remoteModelId,
|
|
117
|
+
// Keep the gateway payload limited to provider-facing protocol content.
|
|
118
|
+
// Session IDs, provenance metadata, display metadata, and local-only
|
|
119
|
+
// fields must never cross the remote inference boundary.
|
|
120
|
+
messages: safeMessages(prepareMessagesForProvider(messages, conversationMode ?? 'private')),
|
|
121
|
+
tools: gatewayTools(tools, provider.gatewayToolCategories ??
|
|
122
|
+
provider.gatewayDefaultToolCategories ??
|
|
123
|
+
provider.gatewayMaxToolCategories),
|
|
124
|
+
toolCategories: provider.gatewayToolCategories ?? provider.gatewayMaxToolCategories ?? [],
|
|
125
|
+
...(responseFormat ? { responseFormat } : {}),
|
|
126
|
+
});
|
|
127
|
+
let activeReader;
|
|
128
|
+
const signal = combinedSignal(parentSignal);
|
|
129
|
+
try {
|
|
130
|
+
const response = await fetch(url, {
|
|
131
|
+
method: 'POST',
|
|
132
|
+
signal,
|
|
133
|
+
headers: {
|
|
134
|
+
'content-type': 'application/json',
|
|
135
|
+
authorization: `Bearer ${provider.gatewayToken}`,
|
|
136
|
+
},
|
|
137
|
+
body: payload,
|
|
138
|
+
});
|
|
139
|
+
if (!response.ok) {
|
|
140
|
+
const body = (await response.json().catch(() => ({})));
|
|
141
|
+
// ⚠️ 服务端**明确表态**优先,状态码只是它没表态时的推断。
|
|
142
|
+
// 曾经写成 `body.retryable === true || status === 429 || …`:服务端发
|
|
143
|
+
// `{code:'TOKEN_QUOTA_EXHAUSTED', retryable:false}` 配 HTTP 429 时,
|
|
144
|
+
// `retryable:false` 被 `|| 429` 直接覆盖 —— 于是「额度用完了,别重试」
|
|
145
|
+
// 被当成限流反复重试,既拖慢失败反馈,也把真正的原因埋掉。
|
|
146
|
+
// 配额耗尽与限流是两回事:前者要去加额度,后者等一会儿就好。
|
|
147
|
+
const retryable = typeof body.retryable === 'boolean'
|
|
148
|
+
? body.retryable
|
|
149
|
+
: response.status === 408 ||
|
|
150
|
+
response.status === 425 ||
|
|
151
|
+
response.status === 429 ||
|
|
152
|
+
response.status >= 500;
|
|
153
|
+
throw new GatewayClientError(body.code ?? `HTTP_${response.status}`, retryable, body.error ?? `在线模型请求失败(${response.status})`);
|
|
154
|
+
}
|
|
155
|
+
if (!response.body)
|
|
156
|
+
throw new GatewayClientError('PROVIDER_UNAVAILABLE', true, '在线模型未返回流式响应');
|
|
157
|
+
const reader = response.body.getReader();
|
|
158
|
+
activeReader = reader;
|
|
159
|
+
const decoder = new TextDecoder();
|
|
160
|
+
let buffer = '';
|
|
161
|
+
while (true) {
|
|
162
|
+
const next = await readWithIdleTimeout(reader.read(), signal);
|
|
163
|
+
if (next.done)
|
|
164
|
+
break;
|
|
165
|
+
buffer += decoder.decode(next.value, { stream: true });
|
|
166
|
+
const frames = buffer.split(/\n\n/);
|
|
167
|
+
buffer = frames.pop() ?? '';
|
|
168
|
+
for (const frame of frames) {
|
|
169
|
+
const line = frame
|
|
170
|
+
.split(/\r?\n/)
|
|
171
|
+
.find((item) => item.startsWith('data:'));
|
|
172
|
+
if (!line)
|
|
173
|
+
continue;
|
|
174
|
+
let event;
|
|
175
|
+
try {
|
|
176
|
+
event = JSON.parse(line.slice(5).trim());
|
|
177
|
+
}
|
|
178
|
+
catch {
|
|
179
|
+
continue;
|
|
180
|
+
}
|
|
181
|
+
if (event.type === 'error')
|
|
182
|
+
throw new GatewayClientError(event.code ?? 'PROVIDER_UNAVAILABLE', event.retryable === true, event.message ?? '在线模型流式调用失败');
|
|
183
|
+
if (event.type === 'usage') {
|
|
184
|
+
yield event;
|
|
185
|
+
continue;
|
|
186
|
+
}
|
|
187
|
+
if (event.type === 'done')
|
|
188
|
+
return;
|
|
189
|
+
yield event;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
finally {
|
|
195
|
+
// Release the response body on both normal `done` and error/abort paths.
|
|
196
|
+
// Without this, a completed stream can keep an HTTP connection/socket
|
|
197
|
+
// alive until the runtime's garbage collector notices it.
|
|
198
|
+
await activeReader?.cancel().catch(() => { });
|
|
199
|
+
activeReader = undefined;
|
|
200
|
+
}
|
|
201
|
+
}
|