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
package/src/loop.ts
CHANGED
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
* d. 否则并行执行所有工具调用(OpenAI 协议要求 tool 消息按 tool_calls 顺序回填)
|
|
19
19
|
* e. 进入下一轮(让模型看到 tool_result 后继续推理)
|
|
20
20
|
*
|
|
21
|
-
* 失控保护:maxTurns(默认
|
|
21
|
+
* 失控保护:maxTurns(默认 200)。
|
|
22
22
|
* 中断支持:AbortSignal 透传到 chat() 和 tool.call()。
|
|
23
23
|
*
|
|
24
24
|
* "工作过程显示"事件:
|
|
@@ -29,12 +29,20 @@
|
|
|
29
29
|
|
|
30
30
|
import crypto from 'node:crypto';
|
|
31
31
|
import { autoCompactIfNeeded, getCompactThreshold } from './context/compact.js';
|
|
32
|
-
import {
|
|
32
|
+
import {
|
|
33
|
+
microCompact,
|
|
34
|
+
incrementTurn,
|
|
35
|
+
expireOldEntries,
|
|
36
|
+
reclaimOldToolResults,
|
|
37
|
+
resetCacheEdits,
|
|
38
|
+
getToolResultPolicy,
|
|
39
|
+
} from './context/microCompactLite.js';
|
|
33
40
|
import {
|
|
34
41
|
isPromptTooLongError,
|
|
35
42
|
reactiveCompactIfApplicable,
|
|
36
43
|
} from './context/reactiveCompact.js';
|
|
37
44
|
import { snipCompactIfNeeded } from './context/snipCompact.js';
|
|
45
|
+
import { archiveToolResult } from './context/toolResultArchive.js';
|
|
38
46
|
import {
|
|
39
47
|
countMessagesTokens,
|
|
40
48
|
countTextTokens,
|
|
@@ -63,10 +71,14 @@ import type {
|
|
|
63
71
|
ToolCall,
|
|
64
72
|
ToolCallContext,
|
|
65
73
|
ToolResult,
|
|
74
|
+
TurnResourceUsage,
|
|
66
75
|
} from './types.js';
|
|
67
76
|
import { configService } from './config/configService.js';
|
|
68
77
|
import { capabilityForToolName } from './tools/capabilities.js';
|
|
69
78
|
import { createEffectiveToolSet } from './tools/effectiveToolSet.js';
|
|
79
|
+
import { classifyComputerAutomationIntent } from './tools/computeruse/computerIntent.js';
|
|
80
|
+
import { recordComputerUseExposure } from './tools/computeruse/telemetryLog.js';
|
|
81
|
+
import { filterToolsForProvider } from './team/toolPolicy.js';
|
|
70
82
|
import { hydrateSessionWorktree } from './tools/worktree/sessionWorktree.js';
|
|
71
83
|
import {
|
|
72
84
|
capabilitiesForTools,
|
|
@@ -75,21 +87,28 @@ import {
|
|
|
75
87
|
import { EvidenceLedger } from './harness/evidenceLedger.js';
|
|
76
88
|
import { StallGuard } from './harness/stallGuard.js';
|
|
77
89
|
import { validateFinalClaims } from './harness/claimValidation.js';
|
|
90
|
+
import {
|
|
91
|
+
AdvisorCoordinator,
|
|
92
|
+
isWorkspaceMutationTool,
|
|
93
|
+
type AdvisorToolSemantics,
|
|
94
|
+
} from './advisor/advisorCoordinator.js';
|
|
95
|
+
import { classifyCommand } from './irreversible/rules.js';
|
|
78
96
|
import {
|
|
79
97
|
createAgentRuntimeContext,
|
|
80
98
|
type AgentRuntimeContext,
|
|
81
99
|
} from './runtime/agentRuntimeContext.js';
|
|
100
|
+
import { stripThinkBlocks } from './utils/stripThink.js';
|
|
82
101
|
|
|
83
|
-
const DELIVERY_ARTIFACT = '(
|
|
84
|
-
const DELIVERY_ACTION = '(
|
|
85
|
-
const ENGLISH_DELIVERY_ARTIFACT = '(?:file|document|report|spreadsheet|workbook|slides?|presentation|pdf|image|code|script|archive|zip|html|webpage|website)';
|
|
86
|
-
const ENGLISH_DELIVERY_ACTION = '(?:send|deliver|submit|provide|create|generate|export|update|modify|finish|produce)';
|
|
102
|
+
const DELIVERY_ARTIFACT = '(?:文件|合同|协议|方案|文档|表格|报告|图片|图像|海报|幻灯片|PPT|PDF|代码|脚本|压缩包|网页|页面|HTML|DOCX|XLSX|CSV|ZIP)';
|
|
103
|
+
const DELIVERY_ACTION = '(?:发送|(?<![开研])发|交付|提交|(?<!宣)传(?!统)|制作|生成|创建|导出|输出|修改|更新|完成|写|起草|做|设计|整理)';
|
|
104
|
+
const ENGLISH_DELIVERY_ARTIFACT = '(?:file|contract|document|report|spreadsheet|workbook|slides?|presentation|pdf|image|code|script|archive|zip|html|webpage|website)';
|
|
105
|
+
const ENGLISH_DELIVERY_ACTION = '(?:send|deliver|submit|provide|create|generate|export|update|modify|finish|produce|draft|write|make|build)';
|
|
87
106
|
|
|
88
107
|
/** Whether this turn explicitly asks for a concrete artifact that should be formally delivered. */
|
|
89
108
|
export function expectsFormalDelivery(input: string): boolean {
|
|
90
109
|
const compact = input.replace(/\s+/g, '');
|
|
91
110
|
const chinese = new RegExp(
|
|
92
|
-
`${DELIVERY_ACTION}(?:给)?我?.{0,24}${DELIVERY_ARTIFACT}|${DELIVERY_ARTIFACT}.{0,24}${DELIVERY_ACTION}(?:给)
|
|
111
|
+
`${DELIVERY_ACTION}(?:给)?我?.{0,24}${DELIVERY_ARTIFACT}|${DELIVERY_ARTIFACT}.{0,24}${DELIVERY_ACTION}(?:给)?我?|给我.{0,24}${DELIVERY_ARTIFACT}|${DELIVERY_ARTIFACT}.{0,24}给我`,
|
|
93
112
|
'i',
|
|
94
113
|
);
|
|
95
114
|
const english = new RegExp(
|
|
@@ -136,7 +155,16 @@ function getToolsSchemaTokens(tools: readonly Tool[], signature?: string): numbe
|
|
|
136
155
|
}
|
|
137
156
|
|
|
138
157
|
/**
|
|
139
|
-
*
|
|
158
|
+
* 主工具循环的默认轮数上限 —— **唯一真值**。
|
|
159
|
+
*
|
|
160
|
+
* 2026-08 从 50 提到 200(4 倍)。它此前是 `options.maxTurns ?? 200` 里的一个裸字面量,
|
|
161
|
+
* 没有名字也就没有引用点,于是「跟着它一起动的东西」(顾问配额封顶)改不动 ——
|
|
162
|
+
* 见下面的 `ADVISOR_QUOTA_MAX`。
|
|
163
|
+
*/
|
|
164
|
+
export const DEFAULT_MAX_TURNS = 200;
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* 顾问咨询配额:基线 1 次,之后每累计 N 次工具调用解锁 1 次,封顶见 ADVISOR_QUOTA_MAX。
|
|
140
168
|
*
|
|
141
169
|
* 曾经是硬编码 1 次/回合。那个数字隐含「一个用户回合 = 一个决策点」的假设,
|
|
142
170
|
* 在短问答里成立,在「继续,完成后续所有工作」这类一句话跑几十步的长回合里
|
|
@@ -149,9 +177,21 @@ function getToolsSchemaTokens(tools: readonly Tool[], signature?: string): numbe
|
|
|
149
177
|
* 刷工具调用换配额的成本远高于收益。
|
|
150
178
|
*/
|
|
151
179
|
const ADVISOR_QUOTA_BASE = 1;
|
|
152
|
-
const ADVISOR_QUOTA_MAX = 3;
|
|
153
180
|
const ADVISOR_QUOTA_UNLOCK_EVERY_TOOL_CALLS = 15;
|
|
154
181
|
|
|
182
|
+
/**
|
|
183
|
+
* 配额封顶**由 `DEFAULT_MAX_TURNS` 推导,不是独立的魔术数字**。
|
|
184
|
+
*
|
|
185
|
+
* 上面那段注释的论据是「一个长回合里真正值钱的咨询点至少三个」——那是按当时
|
|
186
|
+
* `maxTurns = 50` 算的。轮数上限后来提到 200(4 倍工作量塞进同一个回合),
|
|
187
|
+
* 「高风险批次做完」这类节点随之变多,封顶却没跟着动,于是配额重新变成瓶颈:
|
|
188
|
+
* 这正是它当年从「硬编码 1 次」被改掉的同一个毛病,只是换了个刻度。
|
|
189
|
+
*
|
|
190
|
+
* 用公式而不是再写一个字面量,两者才会一起动:
|
|
191
|
+
* 50 轮 → 3(复现历史值) · 200 轮 → 5 · 下限恒为 3
|
|
192
|
+
*/
|
|
193
|
+
const ADVISOR_QUOTA_MAX = Math.max(3, Math.round(DEFAULT_MAX_TURNS / 40));
|
|
194
|
+
|
|
155
195
|
export function advisorQuotaFor(executedToolCalls: number): number {
|
|
156
196
|
return Math.min(
|
|
157
197
|
ADVISOR_QUOTA_MAX,
|
|
@@ -159,6 +199,30 @@ export function advisorQuotaFor(executedToolCalls: number): number {
|
|
|
159
199
|
);
|
|
160
200
|
}
|
|
161
201
|
|
|
202
|
+
function advisorSemanticsFor(call: ToolCall): AdvisorToolSemantics {
|
|
203
|
+
let parsed: Record<string, unknown> = {};
|
|
204
|
+
try { parsed = JSON.parse(call.function.arguments) as Record<string, unknown>; } catch { /* zod layer reports malformed input */ }
|
|
205
|
+
const mutatesWorkspace = isWorkspaceMutationTool(call.function.name);
|
|
206
|
+
if (call.function.name === 'Bash' || call.function.name === 'PowerShell') {
|
|
207
|
+
const command = typeof parsed.command === 'string' ? parsed.command : '';
|
|
208
|
+
const risk = command ? classifyCommand(command) : null;
|
|
209
|
+
return { mutatesWorkspace, highRisk: risk?.riskLevel === 'critical', riskReason: risk?.why };
|
|
210
|
+
}
|
|
211
|
+
if (call.function.name === 'ComputerUse') {
|
|
212
|
+
const highRisk = parsed.intent === 'submit'
|
|
213
|
+
|| (parsed.action === 'window' && parsed.windowOperation === 'close');
|
|
214
|
+
return { mutatesWorkspace, highRisk, ...(highRisk ? { riskReason: '原生界面操作不可逆或可能提交外部动作' } : {}) };
|
|
215
|
+
}
|
|
216
|
+
if (call.function.name === 'WebBrowser' || call.function.name === 'BrowserBatch') {
|
|
217
|
+
const highRisk = parsed.intent === 'submit'
|
|
218
|
+
|| (call.function.name === 'BrowserBatch'
|
|
219
|
+
&& Array.isArray(parsed.steps)
|
|
220
|
+
&& parsed.steps.some((step) => typeof step === 'object' && step !== null && (step as Record<string, unknown>).intent === 'submit'));
|
|
221
|
+
return { mutatesWorkspace, highRisk, ...(highRisk ? { riskReason: '浏览器动作将进行发布、购买、删除或最终提交' } : {}) };
|
|
222
|
+
}
|
|
223
|
+
return { mutatesWorkspace, highRisk: false };
|
|
224
|
+
}
|
|
225
|
+
|
|
162
226
|
export interface RunQueryOptions {
|
|
163
227
|
provider: Provider;
|
|
164
228
|
/** Canonical host-issued trust context. Legacy fields below remain adapters. */
|
|
@@ -174,6 +238,25 @@ export interface RunQueryOptions {
|
|
|
174
238
|
allowedTools?: string[];
|
|
175
239
|
/** Tools the host/workflow explicitly requires; emergency schema reduction preserves them. */
|
|
176
240
|
requiredTools?: string[];
|
|
241
|
+
/**
|
|
242
|
+
* provider 原生结构化输出(`response_format.json_schema`),原样透传给 `chat()`。
|
|
243
|
+
*
|
|
244
|
+
* ⚠️ **只在本轮没有工具时才该传**:response_format 会强制**每一条** assistant
|
|
245
|
+
* 消息都是 JSON,而工具调用轮的 assistant 消息带的是 tool_calls —— 两者互斥,
|
|
246
|
+
* 同时给会让带工具的循环直接崩。工作流引擎因此只在 `llm()`(零工具)和
|
|
247
|
+
* schema 修复轮(零工具)上传它;`agent()` 的结构化输出走「跑完再修复」那条路。
|
|
248
|
+
*
|
|
249
|
+
* Anthropic 格式的 provider 不支持,`chat()` 会在发请求前抛错 —— 所以它是
|
|
250
|
+
* **加成**而不是地基,地基是引擎侧的「校验 + 有界修复」。
|
|
251
|
+
*/
|
|
252
|
+
responseFormat?: {
|
|
253
|
+
type: 'json_schema';
|
|
254
|
+
json_schema: {
|
|
255
|
+
name: string;
|
|
256
|
+
schema: Record<string, unknown>;
|
|
257
|
+
strict?: boolean;
|
|
258
|
+
};
|
|
259
|
+
};
|
|
177
260
|
/**
|
|
178
261
|
* Host-provided tool catalog. Production hosts normally omit this and use ALL_TOOLS;
|
|
179
262
|
* tests and embedders with custom executors must provide matching schemas so the
|
|
@@ -244,8 +327,12 @@ export interface RunQueryOptions {
|
|
|
244
327
|
promptCache?: import('./types.js').PromptCacheContext;
|
|
245
328
|
/** 仅供宿主 UI 恢复附件卡片;不会发送给 LLM。 */
|
|
246
329
|
userAttachments?: import('./types.js').MessageAttachmentRef[];
|
|
330
|
+
/** Host-authorized files for typed workflow inputs; runtime-only paths. */
|
|
331
|
+
inputResources?: readonly import('./types.js').InvocationResource[];
|
|
247
332
|
/** Stable host-provided identity for the user message of this turn. */
|
|
248
333
|
userMessageId?: string;
|
|
334
|
+
/** Trusted local origin for every message created by this run. */
|
|
335
|
+
messageProvenance?: import('./types.js').ConversationProvenance;
|
|
249
336
|
/** Browser element chips are persisted for UI but stripped before provider calls. */
|
|
250
337
|
userBrowserReferences?: import('./types.js').MessageBrowserReference[];
|
|
251
338
|
/** 显式传入的图片附件(-p --image / stream-json images)。 */
|
|
@@ -282,8 +369,8 @@ export async function* runQuery(
|
|
|
282
369
|
// Tool screenshots are transient evidence for the tool continuation that produced them.
|
|
283
370
|
// Never carry a prior turn's base64 image into a new user request.
|
|
284
371
|
evictPriorVisionShots(history);
|
|
285
|
-
const maxTurns = options.maxTurns ??
|
|
286
|
-
const
|
|
372
|
+
const maxTurns = options.maxTurns ?? DEFAULT_MAX_TURNS;
|
|
373
|
+
const baseRuntimeContext = options.runtimeContext ?? createAgentRuntimeContext({
|
|
287
374
|
surface: 'headless',
|
|
288
375
|
profile: 'main',
|
|
289
376
|
cwd: process.cwd(),
|
|
@@ -297,10 +384,24 @@ export async function* runQuery(
|
|
|
297
384
|
computerIntervention: options.computerIntervention,
|
|
298
385
|
toolGate: options.toolGate,
|
|
299
386
|
});
|
|
387
|
+
const computerIntent = baseRuntimeContext.computerIntent
|
|
388
|
+
?? classifyComputerAutomationIntent(userInput, sessionState?.computerAutomation);
|
|
389
|
+
const runtimeContext = createAgentRuntimeContext({
|
|
390
|
+
...baseRuntimeContext,
|
|
391
|
+
computerIntent,
|
|
392
|
+
});
|
|
393
|
+
if (runtimeContext.computer) {
|
|
394
|
+
void recordComputerUseExposure(
|
|
395
|
+
computerIntent.needed,
|
|
396
|
+
computerIntent.source,
|
|
397
|
+
);
|
|
398
|
+
}
|
|
399
|
+
if (!computerIntent.needed && sessionState) delete sessionState.computerAutomation;
|
|
300
400
|
// DI fallback:测试可传 stub,生产走真实模块单例
|
|
301
401
|
const chatFn = options.chat ?? defaultChat;
|
|
302
402
|
const executeToolFn = options.executeTool ?? defaultExecuteTool;
|
|
303
403
|
const explicitFileDeliveryRequested = expectsFormalDelivery(userInput);
|
|
404
|
+
const currentUserMessageId = options.userMessageId ?? crypto.randomUUID();
|
|
304
405
|
|
|
305
406
|
const transientFor = (tools: readonly Tool[]): string => {
|
|
306
407
|
const opaque = options.transientContext?.trim() ?? '';
|
|
@@ -335,7 +436,15 @@ export async function* runQuery(
|
|
|
335
436
|
history,
|
|
336
437
|
sessionState,
|
|
337
438
|
// 配额随本回合工作量增长(见 advisorQuotaFor);下面每轮工具执行完后上调。
|
|
338
|
-
|
|
439
|
+
// mandatoryUsed/mandatoryMax 是独立的"强制检查点"配额池,不随工具调用数
|
|
440
|
+
// 增长——它是安全闸门的固定配额,不是"干得越多奖励越多"的自愿咨询配额;
|
|
441
|
+
// 复用 ADVISOR_QUOTA_MAX 而不新增字面量,避免和自愿池的封顶各写各的漂移。
|
|
442
|
+
advisorCalls: {
|
|
443
|
+
used: 0,
|
|
444
|
+
max: advisorQuotaFor(0),
|
|
445
|
+
mandatoryUsed: 0,
|
|
446
|
+
mandatoryMax: ADVISOR_QUOTA_MAX,
|
|
447
|
+
},
|
|
339
448
|
advisorInvocation: 'agent',
|
|
340
449
|
// Agent 等派生工具起嵌套子查询所需的运行态:与主循环同一 provider、
|
|
341
450
|
// 同一审批闸门(子 agent 不绕过宿主策略)。agentCalls 与 advisorCalls 同构,
|
|
@@ -350,6 +459,8 @@ export async function* runQuery(
|
|
|
350
459
|
currentUserInput: userInput,
|
|
351
460
|
userMessageId: options.userMessageId,
|
|
352
461
|
memoryAccess: runtimeContext.memoryAccess,
|
|
462
|
+
conversationMode: runtimeContext.conversationMode ?? 'private',
|
|
463
|
+
surface: runtimeContext.surface,
|
|
353
464
|
workspaceId: runtimeContext.workspaceId,
|
|
354
465
|
sessionId: runtimeContext.sessionId,
|
|
355
466
|
cwd: runtimeContext.cwd,
|
|
@@ -362,10 +473,11 @@ export async function* runQuery(
|
|
|
362
473
|
// 实际下发给模型的工具集:allowedTools 未传时给全量;
|
|
363
474
|
// 传入数组时严格按数组过滤,[] 表示本轮不下发任何工具。
|
|
364
475
|
// 仅影响呈现给 LLM 的工具清单,不影响 executeTool 的内部调度能力。
|
|
365
|
-
const hostTools = options.availableTools ?? ALL_TOOLS;
|
|
476
|
+
const hostTools = filterToolsForProvider(options.availableTools ?? ALL_TOOLS, provider);
|
|
366
477
|
const availableTools = hostTools.filter((tool) =>
|
|
367
478
|
(tool.name !== 'Agent' || subagents.enabled)
|
|
368
|
-
&& (tool.name !== 'ComputerUse'
|
|
479
|
+
&& (tool.name !== 'ComputerUse'
|
|
480
|
+
|| (runtimeContext.computer !== undefined && runtimeContext.computerIntent?.needed === true)));
|
|
369
481
|
const effectiveToolSet = createEffectiveToolSet({
|
|
370
482
|
availableTools,
|
|
371
483
|
runtime: runtimeContext,
|
|
@@ -378,6 +490,30 @@ export async function* runQuery(
|
|
|
378
490
|
&& runtimeContext.surface !== 'workflow'
|
|
379
491
|
&& effectiveToolSet.names.has('PresentResult');
|
|
380
492
|
let currentEffectiveToolSet = effectiveToolSet;
|
|
493
|
+
const advisorCoordinator = new AdvisorCoordinator({
|
|
494
|
+
history,
|
|
495
|
+
currentRequest: userInput,
|
|
496
|
+
currentRequestMessageId: currentUserMessageId,
|
|
497
|
+
advisorAvailable: effectiveToolSet.names.has('Advisor')
|
|
498
|
+
&& effectiveToolSet.names.has('AdvisorDecision'),
|
|
499
|
+
});
|
|
500
|
+
toolCallContext.advisorRuntime = advisorCoordinator;
|
|
501
|
+
// 组装每轮请求的易变上下文。三个构建点(首轮 / autoCompact 后 / snip 后)共用,
|
|
502
|
+
// 避免 Advisor 相关提醒在复制粘贴中三处漂移。提醒均为 transient:只进当轮请求副本,
|
|
503
|
+
// 不进会话历史(与 DELIVERY_CORRECTION_REMINDER 同构,见 buildModelMessages)。
|
|
504
|
+
const buildRequestTransientContext = (): string => {
|
|
505
|
+
const advisorAvailable = currentEffectiveToolSet.names.has('Advisor')
|
|
506
|
+
&& currentEffectiveToolSet.names.has('AdvisorDecision');
|
|
507
|
+
const parts = [
|
|
508
|
+
transientFor(activeTools),
|
|
509
|
+
deliveryCorrectionAttempted && !resultPresented ? DELIVERY_CORRECTION_REMINDER : '',
|
|
510
|
+
advisorAvailable ? advisorCoordinator.transientReminder() : '',
|
|
511
|
+
advisorAvailable
|
|
512
|
+
? advisorCoordinator.usageLine(toolCallContext.advisorCalls!.used, toolCallContext.advisorCalls!.max)
|
|
513
|
+
: '',
|
|
514
|
+
];
|
|
515
|
+
return parts.filter(Boolean).join('\n\n');
|
|
516
|
+
};
|
|
381
517
|
let lastRequestTransientContext = [options.transientBundle?.text, options.transientContext]
|
|
382
518
|
.filter(Boolean).join('\n\n');
|
|
383
519
|
const evidenceLedger = new EvidenceLedger();
|
|
@@ -439,6 +575,16 @@ export async function* runQuery(
|
|
|
439
575
|
message: `未作为图片附加:${skipped.source}(${skipped.reason}),已按普通文本处理。`,
|
|
440
576
|
};
|
|
441
577
|
}
|
|
578
|
+
let visionRequiredThisTurn = Array.isArray(userContent)
|
|
579
|
+
&& userContent.some((part) => part.type === 'image_url' || part.type === 'video_url');
|
|
580
|
+
if (visionRequiredThisTurn && provider.supportsVision === false) {
|
|
581
|
+
yield {
|
|
582
|
+
type: 'error',
|
|
583
|
+
error: '当前模型已配置为不支持视觉输入,图片未发送。请切换到支持图片的模型。',
|
|
584
|
+
code: 'llm_error',
|
|
585
|
+
};
|
|
586
|
+
return;
|
|
587
|
+
}
|
|
442
588
|
|
|
443
589
|
// 1. 用户消息入栈(v2:填 timestamp,让 MessageList 的 turn 边界横线
|
|
444
590
|
// 能拿到首条 user 的真实时间,否则 fallback Date.now() 会丢失"提交时刻"语义)
|
|
@@ -448,12 +594,16 @@ export async function* runQuery(
|
|
|
448
594
|
...(options.userAttachments?.length ? { attachments: options.userAttachments } : {}),
|
|
449
595
|
...(options.userBrowserReferences?.length ? { browserReferences: options.userBrowserReferences } : {}),
|
|
450
596
|
timestamp: Date.now(),
|
|
451
|
-
id:
|
|
597
|
+
id: currentUserMessageId,
|
|
598
|
+
...(options.messageProvenance ? { provenance: options.messageProvenance } : {}),
|
|
452
599
|
};
|
|
453
|
-
history.
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
600
|
+
const alreadyCommitted = history.some((message) => message.role === 'user' && message.id === userMessage.id);
|
|
601
|
+
if (!alreadyCommitted) {
|
|
602
|
+
history.push(userMessage);
|
|
603
|
+
// 立即通知 UI:用户消息已入栈,触发 bump 让 <Static> 马上把它 commit 进 scrollback,
|
|
604
|
+
// 不必等整轮 assistant 落定(保证用户输入第一时间置顶,符合 T-A-O-R 展示顺序)。
|
|
605
|
+
yield { type: 'user_message_committed', message: userMessage };
|
|
606
|
+
}
|
|
457
607
|
|
|
458
608
|
// 反应式压缩自救:本 runQuery 内最多触发 MAX_REACTIVE_PER_QUERY 次(防本轮死循环);
|
|
459
609
|
// 跨 runQuery 的连续失败由 reactiveCompact.ts 的 circuit breaker(consecutiveFailures)兜底。
|
|
@@ -463,6 +613,15 @@ export async function* runQuery(
|
|
|
463
613
|
// 上一轮 LLM 返回的真实 prompt token 数(done.promptTokens;provider 开 include_usage 才有)。
|
|
464
614
|
// 作为 autoCompact 的权威判据,校正本地估算的系统性低估;压缩后清空(历史已变,旧值失真)。
|
|
465
615
|
let lastPromptTokens: number | undefined;
|
|
616
|
+
|
|
617
|
+
// ── 回合级资源账本 ────────────────────────────────────────────────
|
|
618
|
+
// ⚠️ 与 lastPromptTokens 生命周期**故意不同**:那个是「当前 history 有多大」的快照,
|
|
619
|
+
// 压缩后必须清零;这个是「本回合总共花了多少」的累计,压缩不影响已经花掉的钱。
|
|
620
|
+
// ⚠️ 它只记账,**不参与任何控制流** —— loop 绝不因预算中断进行中的工具循环。
|
|
621
|
+
const turnUsage = { inputTokens: 0, outputTokens: 0, totalCalls: 0, reportedCalls: 0 };
|
|
622
|
+
|
|
623
|
+
// 归档退化只报一次:每条超长结果都报会把上下文刷满,反而盖掉别的信号。
|
|
624
|
+
let archiveDegradedWarned = false;
|
|
466
625
|
// 提示词缓存观测位(纯展示,不参与任何控制流)。与 lastPromptTokens 同生命周期:
|
|
467
626
|
// 压缩后一并清空,避免把压缩前的命中数据挂到压缩后的新历史上误导排障。
|
|
468
627
|
let lastCachedPromptTokens: number | undefined;
|
|
@@ -472,10 +631,24 @@ export async function* runQuery(
|
|
|
472
631
|
|
|
473
632
|
let turn = 0;
|
|
474
633
|
let workStarted = false;
|
|
634
|
+
|
|
475
635
|
while (turn < maxTurns) {
|
|
476
636
|
turn++;
|
|
477
637
|
incrementTurn(sessionState?.microCompact);
|
|
478
638
|
expireOldEntries(sessionState?.microCompact);
|
|
639
|
+
// Path C 批量回收旧工具结果:可重放的观察类工具结果(Read/Grep/Bash…)
|
|
640
|
+
// 超过阈值时只保留热尾部。必须在 buildContextBudgetPlan 之前跑——预算估算和
|
|
641
|
+
// autoCompact 判据直接看到瘦身后的历史,避免无谓的全量压缩。
|
|
642
|
+
// cacheEdits 模式(Anthropic 格式 + provider.cacheEdits):不改 history,删除目标
|
|
643
|
+
// 记入状态、由请求层组装 cache_edits 发送--服务端 KV 原地删除,前缀缓存完整保留。
|
|
644
|
+
// 本地估算的虚高部分(服务端已删内容)由 autoCompactIfNeeded 的 serverDeletedTokens
|
|
645
|
+
// 校正,判据不会因此过早触发压缩。
|
|
646
|
+
const cacheEditsMode = provider.apiFormat === 'anthropic' && provider.cacheEdits === true;
|
|
647
|
+
reclaimOldToolResults(
|
|
648
|
+
history,
|
|
649
|
+
sessionState?.microCompact,
|
|
650
|
+
cacheEditsMode ? 'cacheEdits' : 'inline',
|
|
651
|
+
);
|
|
479
652
|
|
|
480
653
|
if (signal?.aborted) {
|
|
481
654
|
pushManualInterruptMarker(history);
|
|
@@ -499,10 +672,7 @@ export async function* runQuery(
|
|
|
499
672
|
|
|
500
673
|
// 2. 自动压缩(判据 = max(上一轮真实 usage, 历史估算 + 工具 schema),
|
|
501
674
|
// 阈值 provider 无关、按 contextWindow × compactRatio 比例触发)
|
|
502
|
-
let requestTransientContext =
|
|
503
|
-
transientFor(activeTools),
|
|
504
|
-
deliveryCorrectionAttempted && !resultPresented ? DELIVERY_CORRECTION_REMINDER : '',
|
|
505
|
-
].filter(Boolean).join('\n\n');
|
|
675
|
+
let requestTransientContext = buildRequestTransientContext();
|
|
506
676
|
lastRequestTransientContext = requestTransientContext;
|
|
507
677
|
const preCompactBudget = buildContextBudgetPlan({
|
|
508
678
|
provider,
|
|
@@ -516,6 +686,11 @@ export async function* runQuery(
|
|
|
516
686
|
actualPromptTokens: lastPromptTokens,
|
|
517
687
|
toolsTokens: getToolsSchemaTokens(activeTools, effectiveToolSet.signature),
|
|
518
688
|
transientTokens: countTextTokens(requestTransientContext),
|
|
689
|
+
// cacheEdits 判据校正:本地 history 未瘦身,扣掉服务端已删部分的估算,
|
|
690
|
+
// 防止虚高估算劫持 Math.max 导致压缩过早触发(压缩会重建整个前缀缓存)。
|
|
691
|
+
...(cacheEditsMode && sessionState?.microCompact
|
|
692
|
+
? { serverDeletedTokens: sessionState.microCompact.cacheEdits.activeDeletedTokens }
|
|
693
|
+
: {}),
|
|
519
694
|
signal,
|
|
520
695
|
promptCache: options.promptCache,
|
|
521
696
|
chat: chatFn,
|
|
@@ -525,6 +700,9 @@ export async function* runQuery(
|
|
|
525
700
|
yield { type: 'stage_change', stage: 'compacting' };
|
|
526
701
|
history.length = 0;
|
|
527
702
|
history.push(...compact.messages);
|
|
703
|
+
// cacheEdits 状态随压缩作废:pinned 的翻译后下标指向已不存在的消息序列,
|
|
704
|
+
// 服务端缓存前缀也已失效——不重置会导致删除指令插错位置甚至重复删除。
|
|
705
|
+
if (sessionState?.microCompact) resetCacheEdits(sessionState.microCompact);
|
|
528
706
|
lastPromptTokens = undefined;
|
|
529
707
|
lastCachedPromptTokens = undefined;
|
|
530
708
|
lastCacheMissTokens = undefined;
|
|
@@ -553,6 +731,8 @@ export async function* runQuery(
|
|
|
553
731
|
if (snipped.messagesRemoved > 0) {
|
|
554
732
|
history.length = 0;
|
|
555
733
|
history.push(...snipped.messages);
|
|
734
|
+
// 同 compact:snip 砍头后 pinned 下标失位,cacheEdits 必须重置
|
|
735
|
+
if (sessionState?.microCompact) resetCacheEdits(sessionState.microCompact);
|
|
556
736
|
lastPromptTokens = undefined;
|
|
557
737
|
lastCachedPromptTokens = undefined;
|
|
558
738
|
lastCacheMissTokens = undefined;
|
|
@@ -588,10 +768,7 @@ export async function* runQuery(
|
|
|
588
768
|
|
|
589
769
|
// Re-open transient capacity after compaction. If the compacted tail is still
|
|
590
770
|
// too large, trim old history before sacrificing any model-visible ability.
|
|
591
|
-
requestTransientContext =
|
|
592
|
-
transientFor(activeTools),
|
|
593
|
-
deliveryCorrectionAttempted && !resultPresented ? DELIVERY_CORRECTION_REMINDER : '',
|
|
594
|
-
].filter(Boolean).join('\n\n');
|
|
771
|
+
requestTransientContext = buildRequestTransientContext();
|
|
595
772
|
lastRequestTransientContext = requestTransientContext;
|
|
596
773
|
let postCompactBudget = buildContextBudgetPlan({
|
|
597
774
|
provider,
|
|
@@ -616,10 +793,7 @@ export async function* runQuery(
|
|
|
616
793
|
messagesRemoved: snipped.messagesRemoved,
|
|
617
794
|
tokensFreed: snipped.tokensFreed,
|
|
618
795
|
};
|
|
619
|
-
requestTransientContext =
|
|
620
|
-
transientFor(activeTools),
|
|
621
|
-
deliveryCorrectionAttempted && !resultPresented ? DELIVERY_CORRECTION_REMINDER : '',
|
|
622
|
-
].filter(Boolean).join('\n\n');
|
|
796
|
+
requestTransientContext = buildRequestTransientContext();
|
|
623
797
|
lastRequestTransientContext = requestTransientContext;
|
|
624
798
|
postCompactBudget = buildContextBudgetPlan({
|
|
625
799
|
provider,
|
|
@@ -644,6 +818,10 @@ export async function* runQuery(
|
|
|
644
818
|
runtime: runtimeContext,
|
|
645
819
|
requiredNames: [...effectiveToolSet.requiredNames],
|
|
646
820
|
});
|
|
821
|
+
advisorCoordinator.setAvailable(
|
|
822
|
+
currentEffectiveToolSet.names.has('Advisor')
|
|
823
|
+
&& currentEffectiveToolSet.names.has('AdvisorDecision'),
|
|
824
|
+
);
|
|
647
825
|
if (emergencyFit.removedNames.length > 0) {
|
|
648
826
|
yield {
|
|
649
827
|
type: 'warning',
|
|
@@ -689,12 +867,22 @@ export async function* runQuery(
|
|
|
689
867
|
|
|
690
868
|
try {
|
|
691
869
|
const modelMessages = buildModelMessages(history, requestTransientContext, userMessage.id);
|
|
870
|
+
advisorCoordinator.markTransientDelivered();
|
|
692
871
|
for await (const ev of chatFn({
|
|
693
872
|
provider,
|
|
694
873
|
messages: modelMessages,
|
|
695
874
|
tools: currentEffectiveToolSet.tools,
|
|
696
875
|
signal,
|
|
876
|
+
conversationMode: runtimeContext.conversationMode ?? 'private',
|
|
697
877
|
promptCache: options.promptCache,
|
|
878
|
+
visionPolicy: visionRequiredThisTurn ? 'required' : 'optional-history',
|
|
879
|
+
// cacheEdits 回收状态(同一会话级对象跨轮复用;compact 后由 resetCacheEdits 清空)
|
|
880
|
+
...(cacheEditsMode && sessionState?.microCompact
|
|
881
|
+
? { cacheEdits: sessionState.microCompact.cacheEdits }
|
|
882
|
+
: {}),
|
|
883
|
+
...(options.responseFormat
|
|
884
|
+
? { responseFormat: options.responseFormat }
|
|
885
|
+
: {}),
|
|
698
886
|
})) {
|
|
699
887
|
if (ev.type === 'text_delta') {
|
|
700
888
|
if (!stageStreamingYielded) {
|
|
@@ -716,6 +904,8 @@ export async function* runQuery(
|
|
|
716
904
|
// reasoning_details 是结构化数组,没有 delta 字符串,跳过 yield
|
|
717
905
|
reasoningDetails.push(...ev.items);
|
|
718
906
|
}
|
|
907
|
+
} else if (ev.type === 'usage') {
|
|
908
|
+
if (typeof ev.promptTokens === 'number') lastPromptTokens = ev.promptTokens;
|
|
719
909
|
} else if (ev.type === 'done') {
|
|
720
910
|
// R2:记下流的终止原因。'length'(max_tokens 截断)会在本轮收尾时触发 warning。
|
|
721
911
|
lastFinishReason = ev.stopReason;
|
|
@@ -726,6 +916,18 @@ export async function* runQuery(
|
|
|
726
916
|
if (typeof ev.cacheMissTokens === 'number') lastCacheMissTokens = ev.cacheMissTokens;
|
|
727
917
|
if (typeof ev.cacheWriteTokens === 'number') lastCacheWriteTokens = ev.cacheWriteTokens;
|
|
728
918
|
if (ev.promptCacheUsage) lastPromptCacheUsage = ev.promptCacheUsage;
|
|
919
|
+
|
|
920
|
+
// 回合级记账(与上面的 lastPromptTokens 不同:那个压缩后要清零,这个不清)。
|
|
921
|
+
// ⚠️ 只有 provider 真报了才计入 reportedCalls —— 「没人告诉我」不能被当成
|
|
922
|
+
// 「真的是 0」,否则预算闸永远不触发。覆盖率由 trustworthy 如实暴露。
|
|
923
|
+
turnUsage.totalCalls += 1;
|
|
924
|
+
const reportedIn = typeof ev.promptTokens === 'number' ? ev.promptTokens : undefined;
|
|
925
|
+
const reportedOut = typeof ev.outputTokens === 'number' ? ev.outputTokens : undefined;
|
|
926
|
+
if (reportedIn !== undefined || reportedOut !== undefined) {
|
|
927
|
+
turnUsage.reportedCalls += 1;
|
|
928
|
+
turnUsage.inputTokens += reportedIn ?? 0;
|
|
929
|
+
turnUsage.outputTokens += reportedOut ?? 0;
|
|
930
|
+
}
|
|
729
931
|
}
|
|
730
932
|
}
|
|
731
933
|
} catch (e) {
|
|
@@ -824,24 +1026,54 @@ export async function* runQuery(
|
|
|
824
1026
|
...(reasoningDetails.length > 0 ? { reasoning_details: reasoningDetails } : {}),
|
|
825
1027
|
timestamp: Date.now(),
|
|
826
1028
|
id: crypto.randomUUID(),
|
|
1029
|
+
...(options.messageProvenance ? { provenance: options.messageProvenance } : {}),
|
|
827
1030
|
};
|
|
828
|
-
history.push(assistantMsg);
|
|
829
|
-
yield { type: 'assistant_message', message: assistantMsg as Extract<Message, { role: 'assistant' }> };
|
|
830
|
-
|
|
831
1031
|
// 4. 没有工具调用 → 整轮交互结束
|
|
832
1032
|
if (toolCallsByIndex.length === 0) {
|
|
1033
|
+
const emptyResponse = stripThinkBlocks(assistantText).length === 0;
|
|
833
1034
|
// A model occasionally writes a path/link in prose and stops without formally
|
|
834
1035
|
// presenting it. Give it one model-only corrective pass; the reminder is
|
|
835
1036
|
// transient and therefore never pollutes persisted conversation history.
|
|
836
1037
|
if (
|
|
837
|
-
formalDeliveryRequired
|
|
1038
|
+
!emptyResponse && formalDeliveryRequired
|
|
838
1039
|
&& !resultPresented
|
|
839
1040
|
&& !deliveryCorrectionAttempted
|
|
840
1041
|
&& turn < maxTurns
|
|
841
1042
|
) {
|
|
842
1043
|
deliveryCorrectionAttempted = true;
|
|
1044
|
+
yield {
|
|
1045
|
+
type: 'assistant_draft_discarded',
|
|
1046
|
+
reason: 'formal_delivery',
|
|
1047
|
+
textLength: assistantText.length,
|
|
1048
|
+
};
|
|
1049
|
+
continue;
|
|
1050
|
+
}
|
|
1051
|
+
const deliveryCheckpoint = emptyResponse
|
|
1052
|
+
? { allow: true as const }
|
|
1053
|
+
: advisorCoordinator.beforeDelivery();
|
|
1054
|
+
if (!deliveryCheckpoint.allow && turn < maxTurns) {
|
|
1055
|
+
yield {
|
|
1056
|
+
type: 'assistant_draft_discarded',
|
|
1057
|
+
reason: 'advisor_checkpoint',
|
|
1058
|
+
textLength: assistantText.length,
|
|
1059
|
+
};
|
|
843
1060
|
continue;
|
|
844
1061
|
}
|
|
1062
|
+
if (emptyResponse) {
|
|
1063
|
+
// A no-tool response is already the model's terminal signal. Do not turn it
|
|
1064
|
+
// into another model round, and do not persist/broadcast an empty bubble.
|
|
1065
|
+
yield {
|
|
1066
|
+
type: 'assistant_draft_discarded',
|
|
1067
|
+
reason: 'empty_response',
|
|
1068
|
+
textLength: assistantText.length,
|
|
1069
|
+
};
|
|
1070
|
+
} else {
|
|
1071
|
+
history.push(assistantMsg);
|
|
1072
|
+
yield {
|
|
1073
|
+
type: 'assistant_message',
|
|
1074
|
+
message: assistantMsg as Extract<Message, { role: 'assistant' }>,
|
|
1075
|
+
};
|
|
1076
|
+
}
|
|
845
1077
|
// R2:最终回答被 provider 因 max_tokens 截断(finish_reason='length')时,
|
|
846
1078
|
// 不能静默当正常 end_turn 收尾 —— 否则 -p 宿主会拿到半截答案却以为成功。
|
|
847
1079
|
// 走通用 warning(text/json 都落 stderr,不污染 json 那一行 stdout 契约)。
|
|
@@ -884,26 +1116,41 @@ export async function* runQuery(
|
|
|
884
1116
|
: {}),
|
|
885
1117
|
promptCacheUsage: lastPromptCacheUsage,
|
|
886
1118
|
};
|
|
1119
|
+
// 资源账本挂到**每一条** turn_done 出口上。⚠️ 别只挂在 'completed' 那条:
|
|
1120
|
+
// 失败、交付不全、声明未核实的回合照样烧了 token,漏掉它们会让预算系统性少算,
|
|
1121
|
+
// 而少算的方向恰好是「让循环继续跑」—— 这正是本仓库记过账的
|
|
1122
|
+
// 「turn_done.completionStatus 被 print.ts 整条丢弃」同一类错误的反面。
|
|
1123
|
+
const usage: TurnResourceUsage = {
|
|
1124
|
+
...turnUsage,
|
|
1125
|
+
trustworthy: turnUsage.totalCalls > 0 && turnUsage.reportedCalls === turnUsage.totalCalls,
|
|
1126
|
+
};
|
|
887
1127
|
if (formalDeliveryRequired && !resultPresented) {
|
|
888
1128
|
const warning = '交付未完成:用户明确要求提供文件,但本轮没有成功提交可打开的文件结果。';
|
|
889
1129
|
yield { type: 'warning', message: warning };
|
|
890
|
-
yield { type: 'turn_done', completionStatus: 'delivery_incomplete', warning };
|
|
1130
|
+
yield { type: 'turn_done', completionStatus: 'delivery_incomplete', warning, usage };
|
|
891
1131
|
} else if (resultPresented && presentedResultUnverified) {
|
|
892
1132
|
const warning = '结果已提交,但至少一个文件尚未通过结构化验证。';
|
|
893
1133
|
yield { type: 'warning', message: warning };
|
|
894
|
-
yield { type: 'turn_done', completionStatus: 'delivery_unverified', warning };
|
|
1134
|
+
yield { type: 'turn_done', completionStatus: 'delivery_unverified', warning, usage };
|
|
895
1135
|
} else if (!claimValidation.verified) {
|
|
896
1136
|
yield {
|
|
897
1137
|
type: 'turn_done',
|
|
898
1138
|
completionStatus: 'claims_unverified',
|
|
899
1139
|
warning: claimValidation.warnings.join('\n'),
|
|
1140
|
+
usage,
|
|
900
1141
|
};
|
|
901
1142
|
} else {
|
|
902
|
-
yield { type: 'turn_done', completionStatus: 'completed' };
|
|
1143
|
+
yield { type: 'turn_done', completionStatus: 'completed', usage };
|
|
903
1144
|
}
|
|
904
1145
|
return;
|
|
905
1146
|
}
|
|
906
1147
|
|
|
1148
|
+
history.push(assistantMsg);
|
|
1149
|
+
yield {
|
|
1150
|
+
type: 'assistant_message',
|
|
1151
|
+
message: assistantMsg as Extract<Message, { role: 'assistant' }>,
|
|
1152
|
+
};
|
|
1153
|
+
|
|
907
1154
|
// 5. 执行工具调用
|
|
908
1155
|
//
|
|
909
1156
|
// R1:按 isConcurrencySafe 决定并发策略 —— 工具早就声明了这个字段,此前却没有
|
|
@@ -956,6 +1203,10 @@ export async function* runQuery(
|
|
|
956
1203
|
},
|
|
957
1204
|
};
|
|
958
1205
|
}
|
|
1206
|
+
const checkpoint = advisorCoordinator.beforeTool(tc, advisorSemanticsFor(tc));
|
|
1207
|
+
if (!checkpoint.allow) {
|
|
1208
|
+
return { tc, result: checkpoint.deferred! };
|
|
1209
|
+
}
|
|
959
1210
|
// 工具执行闸门:宿主注入后覆盖所有工具,包括只读工具。deny → 把 reason 当
|
|
960
1211
|
// tool_result 回填,模型据此改道/如实报告,不抛异常不中断整轮。宿主异常一律 deny。
|
|
961
1212
|
const isReadOnly = getToolByName(tc.function.name)?.isReadOnly === true;
|
|
@@ -992,19 +1243,35 @@ export async function* runQuery(
|
|
|
992
1243
|
tc.function.name,
|
|
993
1244
|
tc.function.arguments,
|
|
994
1245
|
signal,
|
|
995
|
-
{
|
|
1246
|
+
{
|
|
1247
|
+
...toolCallContext,
|
|
1248
|
+
cwd:
|
|
1249
|
+
sessionState?.activeWorktree?.path ?? toolCallContext.cwd,
|
|
1250
|
+
toolCallId: tc.id,
|
|
1251
|
+
reportProgress,
|
|
1252
|
+
},
|
|
996
1253
|
);
|
|
1254
|
+
if (tc.function.name === 'ComputerUse' && sessionState) {
|
|
1255
|
+
sessionState.computerAutomation = { lastUsedAt: Date.now() };
|
|
1256
|
+
}
|
|
1257
|
+
const downloadableArtifact =
|
|
1258
|
+
result.ok && result.metadata?.kind === 'browser_download'
|
|
1259
|
+
? result.metadata.artifact
|
|
1260
|
+
: undefined;
|
|
1261
|
+
const formalManifest =
|
|
1262
|
+
result.ok && result.metadata?.kind === 'result_manifest'
|
|
1263
|
+
? result.metadata.manifest
|
|
1264
|
+
: downloadableArtifact;
|
|
997
1265
|
if (
|
|
998
|
-
|
|
999
|
-
&&
|
|
1000
|
-
|
|
1001
|
-
&& options.commitFormalDelivery
|
|
1266
|
+
formalManifest &&
|
|
1267
|
+
(tc.function.name === 'PresentResult' || downloadableArtifact) &&
|
|
1268
|
+
options.commitFormalDelivery
|
|
1002
1269
|
) {
|
|
1003
1270
|
try {
|
|
1004
1271
|
await options.commitFormalDelivery({
|
|
1005
1272
|
toolCallId: tc.id,
|
|
1006
1273
|
turnId: userMessage.id ?? options.userMessageId ?? 'turn:unknown',
|
|
1007
|
-
manifest:
|
|
1274
|
+
manifest: formalManifest,
|
|
1008
1275
|
});
|
|
1009
1276
|
} catch (error) {
|
|
1010
1277
|
result = {
|
|
@@ -1015,6 +1282,10 @@ export async function* runQuery(
|
|
|
1015
1282
|
};
|
|
1016
1283
|
}
|
|
1017
1284
|
}
|
|
1285
|
+
if (tc.function.name === 'Advisor' || tc.function.name === 'AdvisorDecision') {
|
|
1286
|
+
// 串行批次内下一项可能就是 AdvisorDecision/受保护写入,必须立即推进协调器状态。
|
|
1287
|
+
advisorCoordinator.observeToolResult(tc, result);
|
|
1288
|
+
}
|
|
1018
1289
|
options.onToolEnd?.(tc.id, false);
|
|
1019
1290
|
return { tc, result };
|
|
1020
1291
|
} catch (e) {
|
|
@@ -1133,32 +1404,75 @@ export async function* runQuery(
|
|
|
1133
1404
|
}
|
|
1134
1405
|
|
|
1135
1406
|
// 本回合工作量累计 → 上调顾问配额(只增不减;已用掉的次数不会因此回退)。
|
|
1136
|
-
executedToolCalls += settled.
|
|
1407
|
+
executedToolCalls += settled.filter(({ result }) =>
|
|
1408
|
+
result.ok || result.disposition !== 'deferred').length;
|
|
1137
1409
|
toolCallContext.advisorCalls!.max = advisorQuotaFor(executedToolCalls);
|
|
1138
1410
|
|
|
1139
1411
|
// 严格按 toolCallsByIndex 顺序把 tool 消息回填 history(settled 已保持该顺序)。
|
|
1140
1412
|
let anyAborted = false;
|
|
1141
1413
|
for (const { tc, result } of settled) {
|
|
1142
|
-
|
|
1414
|
+
if (tc.function.name !== 'Advisor' && tc.function.name !== 'AdvisorDecision') {
|
|
1415
|
+
advisorCoordinator.observeToolResult(tc, result);
|
|
1416
|
+
}
|
|
1417
|
+
if (result.ok || result.disposition !== 'deferred') {
|
|
1418
|
+
evidenceLedger.record(tc.function.name, tc.function.arguments, result);
|
|
1419
|
+
}
|
|
1143
1420
|
if (!result.ok && result.error === 'aborted') anyAborted = true;
|
|
1144
|
-
|
|
1421
|
+
const browserArtifact =
|
|
1422
|
+
result.ok && result.metadata?.kind === 'browser_download'
|
|
1423
|
+
? result.metadata.artifact
|
|
1424
|
+
: undefined;
|
|
1425
|
+
if (
|
|
1426
|
+
result.ok &&
|
|
1427
|
+
((tc.function.name === 'PresentResult' &&
|
|
1428
|
+
result.metadata?.kind === 'result_manifest') ||
|
|
1429
|
+
browserArtifact)
|
|
1430
|
+
) {
|
|
1145
1431
|
resultPresented = true;
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1432
|
+
// `not_run` only says that no optional, format-specific validator was
|
|
1433
|
+
// invoked. The PresentResult boundary has still checked the file and
|
|
1434
|
+
// persisted its fingerprint, so it must not downgrade a normal delivery.
|
|
1435
|
+
const manifest =
|
|
1436
|
+
result.metadata?.kind === 'result_manifest'
|
|
1437
|
+
? result.metadata.manifest
|
|
1438
|
+
: browserArtifact;
|
|
1439
|
+
presentedResultUnverified ||=
|
|
1440
|
+
manifest?.files.some((file) => file.verification === 'failed') ??
|
|
1441
|
+
false;
|
|
1149
1442
|
}
|
|
1150
1443
|
const rawContent = result.ok ? result.content : `Error: ${result.error}`;
|
|
1151
|
-
|
|
1444
|
+
const basePolicy = result.policy ?? getToolResultPolicy(tc.function.name);
|
|
1445
|
+
// 超大结果先归档再压缩:归档失败时保留原文,绝不因为磁盘问题破坏上下文。
|
|
1446
|
+
const archived = result.ok && sessionState
|
|
1447
|
+
? await archiveToolResult(rawContent, sessionState.microCompact.archiveKey, tc.function.name)
|
|
1448
|
+
: { content: rawContent, originalChars: rawContent.length };
|
|
1449
|
+
// 归档退化必须发声:撞会话上限后每条超长结果都退回原文塞进上下文,
|
|
1450
|
+
// 也就是这个机制本来要解决的问题原样回来了 —— 静默的话没人会发现。
|
|
1451
|
+
if (archived.degraded && !archiveDegradedWarned) {
|
|
1452
|
+
archiveDegradedWarned = true;
|
|
1453
|
+
yield {
|
|
1454
|
+
type: 'warning',
|
|
1455
|
+
message: archived.degraded === 'session-limit'
|
|
1456
|
+
? '工具结果归档已达本会话上限,后续超长结果将直接进入上下文(归档不再生效)。建议 /new 开新会话。'
|
|
1457
|
+
: '工具结果归档写盘失败,后续超长结果将直接进入上下文。请检查磁盘空间与权限。',
|
|
1458
|
+
};
|
|
1459
|
+
}
|
|
1460
|
+
const policy = archived.archiveRef
|
|
1461
|
+
? { ...basePolicy, archiveRef: archived.archiveRef, originalChars: archived.originalChars }
|
|
1462
|
+
: basePolicy;
|
|
1463
|
+
// 微压缩仅对成功结果走;policy 会保护高价值结果,避免不可逆中段丢失。
|
|
1152
1464
|
const content = result.ok
|
|
1153
|
-
? microCompact(tc.function.name,
|
|
1465
|
+
? microCompact(tc.function.name, archived.content, sessionState?.microCompact, policy)
|
|
1154
1466
|
: rawContent;
|
|
1155
1467
|
history.push({
|
|
1156
1468
|
role: 'tool',
|
|
1157
1469
|
content,
|
|
1158
1470
|
tool_call_id: tc.id,
|
|
1471
|
+
tool_result_policy: policy,
|
|
1159
1472
|
...(result.metadata ? { display_metadata: result.metadata } : {}),
|
|
1160
1473
|
timestamp: Date.now(),
|
|
1161
1474
|
id: crypto.randomUUID(),
|
|
1475
|
+
...(options.messageProvenance ? { provenance: options.messageProvenance } : {}),
|
|
1162
1476
|
});
|
|
1163
1477
|
}
|
|
1164
1478
|
|
|
@@ -1169,6 +1483,10 @@ export async function* runQuery(
|
|
|
1169
1483
|
({ result }) =>
|
|
1170
1484
|
!result.ok && result.error.includes('第三次调用已在执行前拒绝'),
|
|
1171
1485
|
);
|
|
1486
|
+
const preemptedSignature = preemptedRepeatedFailure
|
|
1487
|
+
? settled.find(({ result }) =>
|
|
1488
|
+
!result.ok && result.error.includes('第三次调用已在执行前拒绝'))?.tc
|
|
1489
|
+
: undefined;
|
|
1172
1490
|
const stall = preemptedRepeatedFailure
|
|
1173
1491
|
? ({
|
|
1174
1492
|
stalled: true as const,
|
|
@@ -1183,12 +1501,15 @@ export async function* runQuery(
|
|
|
1183
1501
|
: { stalled: false as const };
|
|
1184
1502
|
if (stall.stalled) {
|
|
1185
1503
|
const message = stall.reason === 'repeated_failure'
|
|
1186
|
-
? '
|
|
1187
|
-
: '
|
|
1504
|
+
? '检测到同一工具以相同参数连续返回相同失败;请停止原样重试,改做只读诊断、切换路径或咨询 Advisor。'
|
|
1505
|
+
: '检测到连续工具轮次没有产生新文本或新结果;请切换路径或咨询 Advisor。';
|
|
1506
|
+
advisorCoordinator.notifyStalled(
|
|
1507
|
+
stall.signature
|
|
1508
|
+
?? (preemptedSignature
|
|
1509
|
+
? `${preemptedSignature.function.name}\0${preemptedSignature.function.arguments}`
|
|
1510
|
+
: stall.reason),
|
|
1511
|
+
);
|
|
1188
1512
|
yield { type: 'warning', message };
|
|
1189
|
-
yield { type: 'harness_diagnostics', diagnostics: harnessDiagnostics('stalled') };
|
|
1190
|
-
yield { type: 'error', error: message, code: 'stalled' };
|
|
1191
|
-
return;
|
|
1192
1513
|
}
|
|
1193
1514
|
|
|
1194
1515
|
// 关键节点截图回填:把工具附带的实况截图作为一条带图 user 消息喂给(视觉)模型自我核验,
|
|
@@ -1201,6 +1522,7 @@ export async function* runQuery(
|
|
|
1201
1522
|
// The history copy is intentionally downgraded after the turn. Emit a detached snapshot
|
|
1202
1523
|
// so TUI/Desktop stores do not see their already-rendered image mutate underneath them.
|
|
1203
1524
|
if (injectedShot) {
|
|
1525
|
+
visionRequiredThisTurn = true;
|
|
1204
1526
|
yield { type: 'injected_message', message: structuredClone(injectedShot) };
|
|
1205
1527
|
}
|
|
1206
1528
|
|
|
@@ -1311,7 +1633,7 @@ async function injectVisionFollowups(
|
|
|
1311
1633
|
settled: { result: ToolResult }[],
|
|
1312
1634
|
provider: Provider,
|
|
1313
1635
|
): Promise<Message | null> {
|
|
1314
|
-
if (provider.supportsVision
|
|
1636
|
+
if (provider.supportsVision === false) return null;
|
|
1315
1637
|
const images = settled.flatMap((s) => s.result.visionImages ?? []);
|
|
1316
1638
|
if (images.length === 0) return null;
|
|
1317
1639
|
evictPriorVisionShots(history);
|
|
@@ -1441,6 +1763,8 @@ function friendlyToolDescription(toolName: string, rawArgsJson: string): string
|
|
|
1441
1763
|
return `Fetching ...`;
|
|
1442
1764
|
}
|
|
1443
1765
|
}
|
|
1766
|
+
case 'BrowserBatch': return `Browser batch (${Array.isArray(args.steps) ? args.steps.length : 0} steps)`;
|
|
1767
|
+
case 'BrowserCDP': return `Browser CDP ${truncate(String(args.method ?? args.operation ?? ''), 45)}`;
|
|
1444
1768
|
case 'WebSearch': return `Searching \`${truncate(args.query, 50)}\``;
|
|
1445
1769
|
case 'Agent': {
|
|
1446
1770
|
const t = typeof args.agent_type === 'string' ? args.agent_type : 'explore';
|