minimal-agent 0.13.0 → 0.15.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 +60 -4
- 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 +197 -206
- package/plugins/workflow-runner/src/index.ts +248 -249
- 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 +400 -489
- package/plugins/workflow-runner/src/runner.ts +488 -560
- 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 +151 -307
- package/plugins/workflow-runner/src/taskIdentity.ts +207 -355
- 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 -211
- package/plugins/workflow-runner/src/types.ts +154 -623
- 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 +474 -802
- package/plugins/workflow-runner/src/workflowState.ts +575 -970
- 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/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 +536 -0
- package/src/advisor/advisorCoordinator.ts +693 -0
- package/src/cli/configMenu.tsx +0 -1
- package/src/cli/print.js +27 -6
- package/src/cli/print.ts +48 -4
- package/src/cli/quickModelSetup.js +12 -1
- package/src/cli/quickModelSetup.tsx +15 -3
- 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/cli/wizards/providerPresets.js +18 -8
- package/src/cli/wizards/providerPresets.ts +18 -8
- 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 +419 -74
- package/src/config/configService.ts +703 -145
- 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 +34 -5
- package/src/infra/verification.ts +39 -4
- 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 +328 -55
- package/src/loop.ts +382 -58
- 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 +139 -7
- package/src/plugins/pluginRunner.ts +173 -9
- package/src/plugins/slashFallback.js +113 -0
- package/src/plugins/slashFallback.ts +137 -0
- package/src/prompts/policyRegistry.js +12 -32
- package/src/prompts/policyRegistry.ts +15 -32
- 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 +115 -50
- package/src/tools/advisor/advisor.ts +124 -51
- 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 +22 -15
- package/src/tools/bash/bash.ts +23 -17
- 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/presentresult/presentresult.js +6 -15
- package/src/tools/presentresult/presentresult.ts +6 -15
- package/src/tools/read/read.js +62 -9
- package/src/tools/read/read.ts +63 -9
- package/src/tools/shared/fileUtils.js +1 -2
- package/src/tools/shared/fileUtils.ts +1 -2
- 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 +125 -0
- package/src/tools/toolPromptGuides.ts +146 -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 -34
- package/src/tools/webbrowser/browserControl.ts +210 -56
- 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 +679 -144
- package/src/tools/webbrowser/webbrowser.ts +975 -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 +459 -40
- 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/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 -639
- package/plugins/workflow-runner/src/expressions.ts +0 -640
- 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 -126
- package/plugins/workflow-runner/src/recovery.ts +0 -156
- 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 -42
- package/plugins/workflow-runner/src/stepExecutors/assert.ts +0 -52
- 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 -227
- package/plugins/workflow-runner/src/stepExecutors/tool.ts +0 -274
- package/plugins/workflow-runner/src/stepExecutors/verify.js +0 -61
- package/plugins/workflow-runner/src/stepExecutors/verify.ts +0 -72
- 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 -167
- 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/e2eNewLayout.test.ts +0 -314
- package/plugins/workflow-runner/test/engineRobustness.test.ts +0 -234
- package/plugins/workflow-runner/test/expressions.test.ts +0 -439
- 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 -106
- 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 -221
- package/plugins/workflow-runner/test/recovery.test.ts +0 -265
- package/plugins/workflow-runner/test/research.load.test.ts +0 -328
- package/plugins/workflow-runner/test/runner.test.ts +0 -911
- package/plugins/workflow-runner/test/runnerPreflight.test.ts +0 -436
- package/plugins/workflow-runner/test/skillTurnDone.test.ts +0 -71
- package/plugins/workflow-runner/test/stateLifecycle.test.ts +0 -198
- package/plugins/workflow-runner/test/stepPipeline.test.ts +0 -114
- package/plugins/workflow-runner/test/taskArbitration.test.ts +0 -354
- package/plugins/workflow-runner/test/taskLifecycle.test.ts +0 -324
- 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 -201
- package/workflows/research.yaml +0 -873
- 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/workflows/{research.md → research/DESIGN.md} +0 -0
|
@@ -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
|
+
}
|
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import type { ConversationMode } from '../runtime/agentRuntimeContext.js';
|
|
3
|
+
import { onlineToolCategory } from '../team/toolPolicy.js';
|
|
4
|
+
import type {
|
|
5
|
+
GatewayProvider,
|
|
6
|
+
LlmStreamEvent,
|
|
7
|
+
Message,
|
|
8
|
+
Tool,
|
|
9
|
+
} from '../types.js';
|
|
10
|
+
import { prepareMessagesForProvider } from './messageProvenance.js';
|
|
11
|
+
|
|
12
|
+
const IDLE_TIMEOUT_MS =
|
|
13
|
+
Number(process.env.MINIMAL_AGENT_GATEWAY_IDLE_TIMEOUT_MS) > 0
|
|
14
|
+
? Number(process.env.MINIMAL_AGENT_GATEWAY_IDLE_TIMEOUT_MS)
|
|
15
|
+
: 120_000;
|
|
16
|
+
const DEADLINE_MS =
|
|
17
|
+
Number(process.env.MINIMAL_AGENT_GATEWAY_DEADLINE_MS) > 0
|
|
18
|
+
? Number(process.env.MINIMAL_AGENT_GATEWAY_DEADLINE_MS)
|
|
19
|
+
: 15 * 60_000;
|
|
20
|
+
|
|
21
|
+
function gatewayTools(
|
|
22
|
+
tools: Tool[],
|
|
23
|
+
allowedCategories?: readonly string[],
|
|
24
|
+
): Array<{
|
|
25
|
+
type: 'function';
|
|
26
|
+
category: NonNullable<ReturnType<typeof onlineToolCategory>>;
|
|
27
|
+
function: {
|
|
28
|
+
name: string;
|
|
29
|
+
description?: string;
|
|
30
|
+
parameters: Record<string, unknown>;
|
|
31
|
+
};
|
|
32
|
+
}> {
|
|
33
|
+
const allowed = allowedCategories ? new Set(allowedCategories) : null;
|
|
34
|
+
return tools.flatMap((tool) => {
|
|
35
|
+
const category = onlineToolCategory(tool.name);
|
|
36
|
+
if (!category || (allowed && !allowed.has(category))) return [];
|
|
37
|
+
return [
|
|
38
|
+
{
|
|
39
|
+
type: 'function' as const,
|
|
40
|
+
category,
|
|
41
|
+
function: {
|
|
42
|
+
name: tool.name,
|
|
43
|
+
...(typeof tool.description === 'string'
|
|
44
|
+
? { description: tool.description }
|
|
45
|
+
: {}),
|
|
46
|
+
parameters: tool.parameters as Record<string, unknown>,
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
];
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function safeMessages(messages: Message[]): unknown[] {
|
|
54
|
+
return messages.map((message) => {
|
|
55
|
+
const {
|
|
56
|
+
id: _id,
|
|
57
|
+
timestamp: _timestamp,
|
|
58
|
+
attachments: _attachments,
|
|
59
|
+
browserReferences: _browserReferences,
|
|
60
|
+
provenance: _provenance,
|
|
61
|
+
display_metadata: _displayMetadata,
|
|
62
|
+
tool_result_policy: _toolResultPolicy,
|
|
63
|
+
_ephemeral,
|
|
64
|
+
...safe
|
|
65
|
+
} = message as Message & Record<string, unknown>;
|
|
66
|
+
return safe;
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
type GatewayEvent = {
|
|
71
|
+
type?: string;
|
|
72
|
+
code?: string;
|
|
73
|
+
retryable?: boolean;
|
|
74
|
+
message?: string;
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
export class GatewayClientError extends Error {
|
|
78
|
+
constructor(
|
|
79
|
+
public readonly code: string,
|
|
80
|
+
public readonly retryable: boolean,
|
|
81
|
+
message: string,
|
|
82
|
+
) {
|
|
83
|
+
// ⚠️ code 必须烘进 message 文本,不能只存在 .code 属性上:这个错误穿过
|
|
84
|
+
// loop.ts(只取 `.message`)→ 引擎 runner(转成纯字符串 blocker)→
|
|
85
|
+
// workflowController 的 isUsageLimitError 文本兜底,每一层都只剩字符串。
|
|
86
|
+
// 不烘进去,`isUsageLimitError` 的「同时认 code 与文本」这句注释就是一句空话——
|
|
87
|
+
// 之前正是这样:message 是纯人话(如「设备授权不存在或已过期」),从不包含
|
|
88
|
+
// DEVICE_REVOKED 之类的码,文本兜底恒为 false,usage_limited 在真实调用链上
|
|
89
|
+
// 从未被触发过。
|
|
90
|
+
super(`[${code}] ${message}`);
|
|
91
|
+
this.name = 'GatewayClientError';
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* 服务端「配额/授权不给了」的错误码 —— **唯一真值**。
|
|
97
|
+
*
|
|
98
|
+
* 与 Goal 的 `usage_limited` 状态一一对应,也正是它与 `budget_limited` 的分界:
|
|
99
|
+
* · `budget_limited` = **用户自己设的** token 额度用完 → 加预算即可继续。
|
|
100
|
+
* · `usage_limited` = **服务端配额/授权**不给了 → 加预算没用,要去解决授权或等周期重置。
|
|
101
|
+
* 两者都可 resume,但恢复动作完全不同,所以不能合并成一个状态。
|
|
102
|
+
*
|
|
103
|
+
* 服务端侧的产出点:`minimal-server/src/core/gatewayInference.ts`。
|
|
104
|
+
*/
|
|
105
|
+
const USAGE_LIMIT_CODES = new Set([
|
|
106
|
+
'REQUEST_QUOTA_EXHAUSTED',
|
|
107
|
+
'TOKEN_QUOTA_EXHAUSTED',
|
|
108
|
+
'CONCURRENCY_EXCEEDED',
|
|
109
|
+
'GRANT_REVOKED',
|
|
110
|
+
'DEVICE_REVOKED',
|
|
111
|
+
]);
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* 判断一个错误是不是「服务端配额/授权不给了」。
|
|
115
|
+
*
|
|
116
|
+
* ⚠️ 同时认 `GatewayClientError.code` 与错误文本:错误穿过 loop → 引擎 → 工作流
|
|
117
|
+
* 这几层之后往往只剩一个字符串(`llm_error` 事件带的是 message),
|
|
118
|
+
* 只认 code 会在真实链路上恒为 false。
|
|
119
|
+
*/
|
|
120
|
+
export function isUsageLimitError(error: unknown): boolean {
|
|
121
|
+
if (error instanceof GatewayClientError)
|
|
122
|
+
return USAGE_LIMIT_CODES.has(error.code);
|
|
123
|
+
const text = error instanceof Error ? error.message : String(error ?? '');
|
|
124
|
+
return [...USAGE_LIMIT_CODES].some((code) => text.includes(code));
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function combinedSignal(signal?: AbortSignal): AbortSignal {
|
|
128
|
+
const timeout = AbortSignal.timeout(DEADLINE_MS);
|
|
129
|
+
return signal ? AbortSignal.any([signal, timeout]) : timeout;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
async function readWithIdleTimeout<T>(
|
|
133
|
+
promise: Promise<T>,
|
|
134
|
+
signal: AbortSignal,
|
|
135
|
+
): Promise<T> {
|
|
136
|
+
let timer: ReturnType<typeof setTimeout> | undefined;
|
|
137
|
+
let onAbort: (() => void) | undefined;
|
|
138
|
+
try {
|
|
139
|
+
return await Promise.race([
|
|
140
|
+
promise,
|
|
141
|
+
new Promise<T>((_, reject) => {
|
|
142
|
+
timer = setTimeout(
|
|
143
|
+
() =>
|
|
144
|
+
reject(
|
|
145
|
+
new GatewayClientError(
|
|
146
|
+
'PROVIDER_TIMEOUT',
|
|
147
|
+
true,
|
|
148
|
+
'在线模型流式响应超时',
|
|
149
|
+
),
|
|
150
|
+
),
|
|
151
|
+
IDLE_TIMEOUT_MS,
|
|
152
|
+
);
|
|
153
|
+
onAbort = () => reject(new DOMException('aborted', 'AbortError'));
|
|
154
|
+
signal.addEventListener('abort', onAbort, { once: true });
|
|
155
|
+
}),
|
|
156
|
+
]);
|
|
157
|
+
} finally {
|
|
158
|
+
if (timer) clearTimeout(timer);
|
|
159
|
+
if (onAbort) signal.removeEventListener('abort', onAbort);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/** Gateway transport only. The caller retains the existing Agent/tool loop. */
|
|
164
|
+
export async function* chatGateway(args: {
|
|
165
|
+
provider: GatewayProvider;
|
|
166
|
+
messages: Message[];
|
|
167
|
+
tools: Tool[];
|
|
168
|
+
signal?: AbortSignal;
|
|
169
|
+
conversationMode?: ConversationMode;
|
|
170
|
+
responseFormat?: {
|
|
171
|
+
type: 'json_schema';
|
|
172
|
+
json_schema: {
|
|
173
|
+
name: string;
|
|
174
|
+
schema: Record<string, unknown>;
|
|
175
|
+
strict?: boolean;
|
|
176
|
+
};
|
|
177
|
+
};
|
|
178
|
+
}): AsyncGenerator<LlmStreamEvent> {
|
|
179
|
+
const {
|
|
180
|
+
provider,
|
|
181
|
+
messages,
|
|
182
|
+
tools,
|
|
183
|
+
signal: parentSignal,
|
|
184
|
+
conversationMode,
|
|
185
|
+
responseFormat,
|
|
186
|
+
} = args;
|
|
187
|
+
if (!provider.gatewayUrl || !provider.gatewayToken || !provider.remoteModelId)
|
|
188
|
+
throw new GatewayClientError(
|
|
189
|
+
'DEVICE_REVOKED',
|
|
190
|
+
false,
|
|
191
|
+
'在线模型设备授权不存在或已过期',
|
|
192
|
+
);
|
|
193
|
+
const url = `${provider.gatewayUrl.replace(/\/$/, '')}/api/inference/chat/stream`;
|
|
194
|
+
const payload = JSON.stringify({
|
|
195
|
+
requestId: randomUUID(),
|
|
196
|
+
modelId: provider.remoteModelId,
|
|
197
|
+
// Keep the gateway payload limited to provider-facing protocol content.
|
|
198
|
+
// Session IDs, provenance metadata, display metadata, and local-only
|
|
199
|
+
// fields must never cross the remote inference boundary.
|
|
200
|
+
messages: safeMessages(
|
|
201
|
+
prepareMessagesForProvider(messages, conversationMode ?? 'private'),
|
|
202
|
+
),
|
|
203
|
+
tools: gatewayTools(
|
|
204
|
+
tools,
|
|
205
|
+
provider.gatewayToolCategories ??
|
|
206
|
+
provider.gatewayDefaultToolCategories ??
|
|
207
|
+
provider.gatewayMaxToolCategories,
|
|
208
|
+
),
|
|
209
|
+
toolCategories:
|
|
210
|
+
provider.gatewayToolCategories ?? provider.gatewayMaxToolCategories ?? [],
|
|
211
|
+
...(responseFormat ? { responseFormat } : {}),
|
|
212
|
+
});
|
|
213
|
+
let activeReader: { cancel(): Promise<void> } | undefined;
|
|
214
|
+
const signal = combinedSignal(parentSignal);
|
|
215
|
+
try {
|
|
216
|
+
const response = await fetch(url, {
|
|
217
|
+
method: 'POST',
|
|
218
|
+
signal,
|
|
219
|
+
headers: {
|
|
220
|
+
'content-type': 'application/json',
|
|
221
|
+
authorization: `Bearer ${provider.gatewayToken}`,
|
|
222
|
+
},
|
|
223
|
+
body: payload,
|
|
224
|
+
});
|
|
225
|
+
if (!response.ok) {
|
|
226
|
+
const body = (await response.json().catch(() => ({}))) as {
|
|
227
|
+
error?: string;
|
|
228
|
+
code?: string;
|
|
229
|
+
retryable?: boolean;
|
|
230
|
+
};
|
|
231
|
+
// ⚠️ 服务端**明确表态**优先,状态码只是它没表态时的推断。
|
|
232
|
+
// 曾经写成 `body.retryable === true || status === 429 || …`:服务端发
|
|
233
|
+
// `{code:'TOKEN_QUOTA_EXHAUSTED', retryable:false}` 配 HTTP 429 时,
|
|
234
|
+
// `retryable:false` 被 `|| 429` 直接覆盖 —— 于是「额度用完了,别重试」
|
|
235
|
+
// 被当成限流反复重试,既拖慢失败反馈,也把真正的原因埋掉。
|
|
236
|
+
// 配额耗尽与限流是两回事:前者要去加额度,后者等一会儿就好。
|
|
237
|
+
const retryable =
|
|
238
|
+
typeof body.retryable === 'boolean'
|
|
239
|
+
? body.retryable
|
|
240
|
+
: response.status === 408 ||
|
|
241
|
+
response.status === 425 ||
|
|
242
|
+
response.status === 429 ||
|
|
243
|
+
response.status >= 500;
|
|
244
|
+
throw new GatewayClientError(
|
|
245
|
+
body.code ?? `HTTP_${response.status}`,
|
|
246
|
+
retryable,
|
|
247
|
+
body.error ?? `在线模型请求失败(${response.status})`,
|
|
248
|
+
);
|
|
249
|
+
}
|
|
250
|
+
if (!response.body)
|
|
251
|
+
throw new GatewayClientError(
|
|
252
|
+
'PROVIDER_UNAVAILABLE',
|
|
253
|
+
true,
|
|
254
|
+
'在线模型未返回流式响应',
|
|
255
|
+
);
|
|
256
|
+
const reader = response.body.getReader();
|
|
257
|
+
activeReader = reader;
|
|
258
|
+
const decoder = new TextDecoder();
|
|
259
|
+
let buffer = '';
|
|
260
|
+
while (true) {
|
|
261
|
+
const next = await readWithIdleTimeout(reader.read(), signal);
|
|
262
|
+
if (next.done) break;
|
|
263
|
+
buffer += decoder.decode(next.value, { stream: true });
|
|
264
|
+
const frames = buffer.split(/\n\n/);
|
|
265
|
+
buffer = frames.pop() ?? '';
|
|
266
|
+
for (const frame of frames) {
|
|
267
|
+
const line = frame
|
|
268
|
+
.split(/\r?\n/)
|
|
269
|
+
.find((item) => item.startsWith('data:'));
|
|
270
|
+
if (!line) continue;
|
|
271
|
+
let event: GatewayEvent;
|
|
272
|
+
try {
|
|
273
|
+
event = JSON.parse(line.slice(5).trim());
|
|
274
|
+
} catch {
|
|
275
|
+
continue;
|
|
276
|
+
}
|
|
277
|
+
if (event.type === 'error')
|
|
278
|
+
throw new GatewayClientError(
|
|
279
|
+
event.code ?? 'PROVIDER_UNAVAILABLE',
|
|
280
|
+
event.retryable === true,
|
|
281
|
+
event.message ?? '在线模型流式调用失败',
|
|
282
|
+
);
|
|
283
|
+
if (event.type === 'usage') {
|
|
284
|
+
yield event as LlmStreamEvent;
|
|
285
|
+
continue;
|
|
286
|
+
}
|
|
287
|
+
if (event.type === 'done') return;
|
|
288
|
+
yield event as LlmStreamEvent;
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
return;
|
|
292
|
+
} finally {
|
|
293
|
+
// Release the response body on both normal `done` and error/abort paths.
|
|
294
|
+
// Without this, a completed stream can keep an HTTP connection/socket
|
|
295
|
+
// alive until the runtime's garbage collector notices it.
|
|
296
|
+
await activeReader?.cancel().catch(() => {});
|
|
297
|
+
activeReader = undefined;
|
|
298
|
+
}
|
|
299
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
function provenanceLabel(provenance) {
|
|
2
|
+
if (provenance.mode === 'private') {
|
|
3
|
+
return '[历史来源:主人私聊,仅限内部使用;群聊中不得复述或公开]';
|
|
4
|
+
}
|
|
5
|
+
const fields = [
|
|
6
|
+
'历史来源:群聊公开消息',
|
|
7
|
+
provenance.groupId ? `群=${provenance.groupId}` : '',
|
|
8
|
+
provenance.senderName ? `发送者=${provenance.senderName}` : '',
|
|
9
|
+
provenance.senderId ? `发送者ID=${provenance.senderId}` : '',
|
|
10
|
+
provenance.taskId ? `父任务=${provenance.taskId}` : '',
|
|
11
|
+
].filter(Boolean);
|
|
12
|
+
return `[${fields.join(';')}]`;
|
|
13
|
+
}
|
|
14
|
+
function decorateContent(content, provenance) {
|
|
15
|
+
if (!provenance || content === null)
|
|
16
|
+
return content;
|
|
17
|
+
const label = provenanceLabel(provenance);
|
|
18
|
+
if (typeof content === 'string')
|
|
19
|
+
return `${label}\n${content}`;
|
|
20
|
+
const firstText = content.findIndex((part) => part.type === 'text');
|
|
21
|
+
if (firstText < 0)
|
|
22
|
+
return [{ type: 'text', text: label }, ...content];
|
|
23
|
+
return content.map((part, index) => index === firstText && part.type === 'text'
|
|
24
|
+
? { ...part, text: `${label}\n${part.text}` }
|
|
25
|
+
: part);
|
|
26
|
+
}
|
|
27
|
+
/** Clone messages for a provider request without leaking local UI/provenance fields. */
|
|
28
|
+
export function prepareMessagesForProvider(messages, conversationMode = 'private') {
|
|
29
|
+
return messages
|
|
30
|
+
.filter((message) => !(message.role === 'system' && message._ephemeral === true))
|
|
31
|
+
.map((message) => {
|
|
32
|
+
const { id, timestamp, attachments, browserReferences, display_metadata, tool_result_policy, provenance, ...rest } = message;
|
|
33
|
+
if (rest.role === 'user' || rest.role === 'assistant') {
|
|
34
|
+
// 旧 Session 在引入 provenance 之前没有来源元数据。进入群聊时必须保守地
|
|
35
|
+
// 把这些历史当作主人私聊,而不是让模型误以为它们已经公开。
|
|
36
|
+
const effectiveProvenance = provenance ?? (conversationMode === 'group'
|
|
37
|
+
? { mode: 'private', visibility: 'owner-private' }
|
|
38
|
+
: undefined);
|
|
39
|
+
return {
|
|
40
|
+
...rest,
|
|
41
|
+
content: decorateContent(rest.content, effectiveProvenance),
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
return rest;
|
|
45
|
+
});
|
|
46
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import type { ChatContentPart, ConversationProvenance, Message } from '../types.js';
|
|
2
|
+
|
|
3
|
+
function provenanceLabel(provenance: ConversationProvenance): string {
|
|
4
|
+
if (provenance.mode === 'private') {
|
|
5
|
+
return '[历史来源:主人私聊,仅限内部使用;群聊中不得复述或公开]';
|
|
6
|
+
}
|
|
7
|
+
const fields = [
|
|
8
|
+
'历史来源:群聊公开消息',
|
|
9
|
+
provenance.groupId ? `群=${provenance.groupId}` : '',
|
|
10
|
+
provenance.senderName ? `发送者=${provenance.senderName}` : '',
|
|
11
|
+
provenance.senderId ? `发送者ID=${provenance.senderId}` : '',
|
|
12
|
+
provenance.taskId ? `父任务=${provenance.taskId}` : '',
|
|
13
|
+
].filter(Boolean);
|
|
14
|
+
return `[${fields.join(';')}]`;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function decorateContent(
|
|
18
|
+
content: string | ChatContentPart[] | null,
|
|
19
|
+
provenance: ConversationProvenance | undefined,
|
|
20
|
+
): string | ChatContentPart[] | null {
|
|
21
|
+
if (!provenance || content === null) return content;
|
|
22
|
+
const label = provenanceLabel(provenance);
|
|
23
|
+
if (typeof content === 'string') return `${label}\n${content}`;
|
|
24
|
+
const firstText = content.findIndex((part) => part.type === 'text');
|
|
25
|
+
if (firstText < 0) return [{ type: 'text', text: label }, ...content];
|
|
26
|
+
return content.map((part, index) => index === firstText && part.type === 'text'
|
|
27
|
+
? { ...part, text: `${label}\n${part.text}` }
|
|
28
|
+
: part);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** Clone messages for a provider request without leaking local UI/provenance fields. */
|
|
32
|
+
export function prepareMessagesForProvider(
|
|
33
|
+
messages: readonly Message[],
|
|
34
|
+
conversationMode: 'private' | 'group' = 'private',
|
|
35
|
+
): Message[] {
|
|
36
|
+
return messages
|
|
37
|
+
.filter((message) => !(message.role === 'system' && message._ephemeral === true))
|
|
38
|
+
.map((message) => {
|
|
39
|
+
const {
|
|
40
|
+
id, timestamp, attachments, browserReferences, display_metadata, tool_result_policy, provenance, ...rest
|
|
41
|
+
} = message as Message & Record<string, unknown>;
|
|
42
|
+
if (rest.role === 'user' || rest.role === 'assistant') {
|
|
43
|
+
// 旧 Session 在引入 provenance 之前没有来源元数据。进入群聊时必须保守地
|
|
44
|
+
// 把这些历史当作主人私聊,而不是让模型误以为它们已经公开。
|
|
45
|
+
const effectiveProvenance = provenance ?? (conversationMode === 'group'
|
|
46
|
+
? { mode: 'private' as const, visibility: 'owner-private' as const }
|
|
47
|
+
: undefined);
|
|
48
|
+
return {
|
|
49
|
+
...rest,
|
|
50
|
+
content: decorateContent(
|
|
51
|
+
rest.content as string | ChatContentPart[] | null,
|
|
52
|
+
effectiveProvenance,
|
|
53
|
+
),
|
|
54
|
+
} as Message;
|
|
55
|
+
}
|
|
56
|
+
return rest as Message;
|
|
57
|
+
});
|
|
58
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { ConversationMode } from '../runtime/agentRuntimeContext.js';
|
|
2
|
+
import type { CacheEditsState, LlmStreamEvent, Message, Provider, PromptCacheContext, Tool } from '../types.js';
|
|
3
|
+
|
|
4
|
+
export interface NormalizedModelRequest {
|
|
5
|
+
provider: Provider;
|
|
6
|
+
messages: Message[];
|
|
7
|
+
tools: Tool[];
|
|
8
|
+
signal?: AbortSignal;
|
|
9
|
+
responseFormat?: { type: 'json_schema'; json_schema: { name: string; schema: Record<string, unknown>; strict?: boolean } };
|
|
10
|
+
promptCache?: PromptCacheContext;
|
|
11
|
+
conversationMode?: ConversationMode;
|
|
12
|
+
cacheEdits?: CacheEditsState;
|
|
13
|
+
/** Current-turn images must not be silently removed when they are required. */
|
|
14
|
+
visionPolicy?: 'required' | 'optional-history';
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/** Both local and gateway transports consume the same normalized request. */
|
|
18
|
+
export interface ModelProvider {
|
|
19
|
+
readonly source: 'local' | 'gateway';
|
|
20
|
+
readonly descriptor: Provider;
|
|
21
|
+
stream(request: NormalizedModelRequest): AsyncGenerator<LlmStreamEvent, void, void>;
|
|
22
|
+
}
|