minimal-agent 0.13.0 → 0.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -5
- package/package.json +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 +503 -0
- package/src/advisor/advisorCoordinator.ts +657 -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/streamJson.js +5 -6
- package/src/cli/streamJson.ts +8 -4
- package/src/cli/wizards/advisorWizard.js +4 -2
- package/src/cli/wizards/advisorWizard.tsx +8 -2
- package/src/cli/wizards/contextAdvisorWizard.js +5 -4
- package/src/cli/wizards/contextAdvisorWizard.tsx +7 -6
- package/src/collaboration/taskFlow.js +115 -0
- package/src/collaboration/taskFlow.ts +169 -0
- package/src/config/configDefaults.js +41 -1
- package/src/config/configDefaults.ts +43 -1
- package/src/config/configDiagnostics.ts +2 -2
- package/src/config/configFile.js +40 -9
- package/src/config/configFile.ts +60 -11
- package/src/config/configService.js +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 +319 -54
- package/src/loop.ts +373 -57
- package/src/main.js +4 -1
- package/src/main.tsx +4 -1
- package/src/managedExecution/index.js +2 -0
- package/src/managedExecution/index.ts +2 -0
- package/src/managedExecution/registry.js +15 -0
- package/src/managedExecution/registry.ts +20 -0
- package/src/managedExecution/types.js +1 -0
- package/src/managedExecution/types.ts +86 -0
- package/src/modelRef.js +9 -0
- package/src/modelRef.ts +31 -0
- package/src/multimodal.js +14 -28
- package/src/multimodal.ts +17 -33
- package/src/net/lanDiscovery.js +65 -0
- package/src/net/lanDiscovery.ts +82 -0
- package/src/plugin-sdk.js +28 -3
- package/src/plugin-sdk.ts +39 -6
- package/src/plugins/pluginApi.ts +16 -1
- package/src/plugins/pluginLoader.js +4 -0
- package/src/plugins/pluginLoader.ts +6 -0
- package/src/plugins/pluginRunner.js +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 +90 -46
- package/src/tools/advisor/advisor.ts +100 -47
- 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 +117 -0
- package/src/tools/toolPromptGuides.ts +137 -0
- package/src/tools/webbrowser/agentBrowserPath.js +19 -9
- package/src/tools/webbrowser/agentBrowserPath.ts +70 -28
- package/src/tools/webbrowser/browser.js +27 -11
- package/src/tools/webbrowser/browser.ts +68 -24
- package/src/tools/webbrowser/browserAutomation.js +329 -0
- package/src/tools/webbrowser/browserAutomation.ts +467 -0
- package/src/tools/webbrowser/browserAxState.js +97 -0
- package/src/tools/webbrowser/browserAxState.ts +142 -0
- package/src/tools/webbrowser/browserBatch.js +1578 -0
- package/src/tools/webbrowser/browserBatch.ts +2382 -0
- package/src/tools/webbrowser/browserCdp.js +208 -0
- package/src/tools/webbrowser/browserCdp.ts +255 -0
- package/src/tools/webbrowser/browserConditions.js +24 -0
- package/src/tools/webbrowser/browserConditions.ts +45 -0
- package/src/tools/webbrowser/browserContent.js +281 -0
- package/src/tools/webbrowser/browserContent.ts +438 -0
- package/src/tools/webbrowser/browserControl.js +131 -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 +440 -38
- 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() ?? '';
|
|
@@ -350,6 +451,8 @@ export async function* runQuery(
|
|
|
350
451
|
currentUserInput: userInput,
|
|
351
452
|
userMessageId: options.userMessageId,
|
|
352
453
|
memoryAccess: runtimeContext.memoryAccess,
|
|
454
|
+
conversationMode: runtimeContext.conversationMode ?? 'private',
|
|
455
|
+
surface: runtimeContext.surface,
|
|
353
456
|
workspaceId: runtimeContext.workspaceId,
|
|
354
457
|
sessionId: runtimeContext.sessionId,
|
|
355
458
|
cwd: runtimeContext.cwd,
|
|
@@ -362,10 +465,11 @@ export async function* runQuery(
|
|
|
362
465
|
// 实际下发给模型的工具集:allowedTools 未传时给全量;
|
|
363
466
|
// 传入数组时严格按数组过滤,[] 表示本轮不下发任何工具。
|
|
364
467
|
// 仅影响呈现给 LLM 的工具清单,不影响 executeTool 的内部调度能力。
|
|
365
|
-
const hostTools = options.availableTools ?? ALL_TOOLS;
|
|
468
|
+
const hostTools = filterToolsForProvider(options.availableTools ?? ALL_TOOLS, provider);
|
|
366
469
|
const availableTools = hostTools.filter((tool) =>
|
|
367
470
|
(tool.name !== 'Agent' || subagents.enabled)
|
|
368
|
-
&& (tool.name !== 'ComputerUse'
|
|
471
|
+
&& (tool.name !== 'ComputerUse'
|
|
472
|
+
|| (runtimeContext.computer !== undefined && runtimeContext.computerIntent?.needed === true)));
|
|
369
473
|
const effectiveToolSet = createEffectiveToolSet({
|
|
370
474
|
availableTools,
|
|
371
475
|
runtime: runtimeContext,
|
|
@@ -378,6 +482,30 @@ export async function* runQuery(
|
|
|
378
482
|
&& runtimeContext.surface !== 'workflow'
|
|
379
483
|
&& effectiveToolSet.names.has('PresentResult');
|
|
380
484
|
let currentEffectiveToolSet = effectiveToolSet;
|
|
485
|
+
const advisorCoordinator = new AdvisorCoordinator({
|
|
486
|
+
history,
|
|
487
|
+
currentRequest: userInput,
|
|
488
|
+
currentRequestMessageId: currentUserMessageId,
|
|
489
|
+
advisorAvailable: effectiveToolSet.names.has('Advisor')
|
|
490
|
+
&& effectiveToolSet.names.has('AdvisorDecision'),
|
|
491
|
+
});
|
|
492
|
+
toolCallContext.advisorRuntime = advisorCoordinator;
|
|
493
|
+
// 组装每轮请求的易变上下文。三个构建点(首轮 / autoCompact 后 / snip 后)共用,
|
|
494
|
+
// 避免 Advisor 相关提醒在复制粘贴中三处漂移。提醒均为 transient:只进当轮请求副本,
|
|
495
|
+
// 不进会话历史(与 DELIVERY_CORRECTION_REMINDER 同构,见 buildModelMessages)。
|
|
496
|
+
const buildRequestTransientContext = (): string => {
|
|
497
|
+
const advisorAvailable = currentEffectiveToolSet.names.has('Advisor')
|
|
498
|
+
&& currentEffectiveToolSet.names.has('AdvisorDecision');
|
|
499
|
+
const parts = [
|
|
500
|
+
transientFor(activeTools),
|
|
501
|
+
deliveryCorrectionAttempted && !resultPresented ? DELIVERY_CORRECTION_REMINDER : '',
|
|
502
|
+
advisorAvailable ? advisorCoordinator.transientReminder() : '',
|
|
503
|
+
advisorAvailable
|
|
504
|
+
? advisorCoordinator.usageLine(toolCallContext.advisorCalls!.used, toolCallContext.advisorCalls!.max)
|
|
505
|
+
: '',
|
|
506
|
+
];
|
|
507
|
+
return parts.filter(Boolean).join('\n\n');
|
|
508
|
+
};
|
|
381
509
|
let lastRequestTransientContext = [options.transientBundle?.text, options.transientContext]
|
|
382
510
|
.filter(Boolean).join('\n\n');
|
|
383
511
|
const evidenceLedger = new EvidenceLedger();
|
|
@@ -439,6 +567,16 @@ export async function* runQuery(
|
|
|
439
567
|
message: `未作为图片附加:${skipped.source}(${skipped.reason}),已按普通文本处理。`,
|
|
440
568
|
};
|
|
441
569
|
}
|
|
570
|
+
let visionRequiredThisTurn = Array.isArray(userContent)
|
|
571
|
+
&& userContent.some((part) => part.type === 'image_url' || part.type === 'video_url');
|
|
572
|
+
if (visionRequiredThisTurn && provider.supportsVision === false) {
|
|
573
|
+
yield {
|
|
574
|
+
type: 'error',
|
|
575
|
+
error: '当前模型已配置为不支持视觉输入,图片未发送。请切换到支持图片的模型。',
|
|
576
|
+
code: 'llm_error',
|
|
577
|
+
};
|
|
578
|
+
return;
|
|
579
|
+
}
|
|
442
580
|
|
|
443
581
|
// 1. 用户消息入栈(v2:填 timestamp,让 MessageList 的 turn 边界横线
|
|
444
582
|
// 能拿到首条 user 的真实时间,否则 fallback Date.now() 会丢失"提交时刻"语义)
|
|
@@ -448,12 +586,16 @@ export async function* runQuery(
|
|
|
448
586
|
...(options.userAttachments?.length ? { attachments: options.userAttachments } : {}),
|
|
449
587
|
...(options.userBrowserReferences?.length ? { browserReferences: options.userBrowserReferences } : {}),
|
|
450
588
|
timestamp: Date.now(),
|
|
451
|
-
id:
|
|
589
|
+
id: currentUserMessageId,
|
|
590
|
+
...(options.messageProvenance ? { provenance: options.messageProvenance } : {}),
|
|
452
591
|
};
|
|
453
|
-
history.
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
592
|
+
const alreadyCommitted = history.some((message) => message.role === 'user' && message.id === userMessage.id);
|
|
593
|
+
if (!alreadyCommitted) {
|
|
594
|
+
history.push(userMessage);
|
|
595
|
+
// 立即通知 UI:用户消息已入栈,触发 bump 让 <Static> 马上把它 commit 进 scrollback,
|
|
596
|
+
// 不必等整轮 assistant 落定(保证用户输入第一时间置顶,符合 T-A-O-R 展示顺序)。
|
|
597
|
+
yield { type: 'user_message_committed', message: userMessage };
|
|
598
|
+
}
|
|
457
599
|
|
|
458
600
|
// 反应式压缩自救:本 runQuery 内最多触发 MAX_REACTIVE_PER_QUERY 次(防本轮死循环);
|
|
459
601
|
// 跨 runQuery 的连续失败由 reactiveCompact.ts 的 circuit breaker(consecutiveFailures)兜底。
|
|
@@ -463,6 +605,15 @@ export async function* runQuery(
|
|
|
463
605
|
// 上一轮 LLM 返回的真实 prompt token 数(done.promptTokens;provider 开 include_usage 才有)。
|
|
464
606
|
// 作为 autoCompact 的权威判据,校正本地估算的系统性低估;压缩后清空(历史已变,旧值失真)。
|
|
465
607
|
let lastPromptTokens: number | undefined;
|
|
608
|
+
|
|
609
|
+
// ── 回合级资源账本 ────────────────────────────────────────────────
|
|
610
|
+
// ⚠️ 与 lastPromptTokens 生命周期**故意不同**:那个是「当前 history 有多大」的快照,
|
|
611
|
+
// 压缩后必须清零;这个是「本回合总共花了多少」的累计,压缩不影响已经花掉的钱。
|
|
612
|
+
// ⚠️ 它只记账,**不参与任何控制流** —— loop 绝不因预算中断进行中的工具循环。
|
|
613
|
+
const turnUsage = { inputTokens: 0, outputTokens: 0, totalCalls: 0, reportedCalls: 0 };
|
|
614
|
+
|
|
615
|
+
// 归档退化只报一次:每条超长结果都报会把上下文刷满,反而盖掉别的信号。
|
|
616
|
+
let archiveDegradedWarned = false;
|
|
466
617
|
// 提示词缓存观测位(纯展示,不参与任何控制流)。与 lastPromptTokens 同生命周期:
|
|
467
618
|
// 压缩后一并清空,避免把压缩前的命中数据挂到压缩后的新历史上误导排障。
|
|
468
619
|
let lastCachedPromptTokens: number | undefined;
|
|
@@ -472,10 +623,24 @@ export async function* runQuery(
|
|
|
472
623
|
|
|
473
624
|
let turn = 0;
|
|
474
625
|
let workStarted = false;
|
|
626
|
+
|
|
475
627
|
while (turn < maxTurns) {
|
|
476
628
|
turn++;
|
|
477
629
|
incrementTurn(sessionState?.microCompact);
|
|
478
630
|
expireOldEntries(sessionState?.microCompact);
|
|
631
|
+
// Path C 批量回收旧工具结果:可重放的观察类工具结果(Read/Grep/Bash…)
|
|
632
|
+
// 超过阈值时只保留热尾部。必须在 buildContextBudgetPlan 之前跑——预算估算和
|
|
633
|
+
// autoCompact 判据直接看到瘦身后的历史,避免无谓的全量压缩。
|
|
634
|
+
// cacheEdits 模式(Anthropic 格式 + provider.cacheEdits):不改 history,删除目标
|
|
635
|
+
// 记入状态、由请求层组装 cache_edits 发送--服务端 KV 原地删除,前缀缓存完整保留。
|
|
636
|
+
// 本地估算的虚高部分(服务端已删内容)由 autoCompactIfNeeded 的 serverDeletedTokens
|
|
637
|
+
// 校正,判据不会因此过早触发压缩。
|
|
638
|
+
const cacheEditsMode = provider.apiFormat === 'anthropic' && provider.cacheEdits === true;
|
|
639
|
+
reclaimOldToolResults(
|
|
640
|
+
history,
|
|
641
|
+
sessionState?.microCompact,
|
|
642
|
+
cacheEditsMode ? 'cacheEdits' : 'inline',
|
|
643
|
+
);
|
|
479
644
|
|
|
480
645
|
if (signal?.aborted) {
|
|
481
646
|
pushManualInterruptMarker(history);
|
|
@@ -499,10 +664,7 @@ export async function* runQuery(
|
|
|
499
664
|
|
|
500
665
|
// 2. 自动压缩(判据 = max(上一轮真实 usage, 历史估算 + 工具 schema),
|
|
501
666
|
// 阈值 provider 无关、按 contextWindow × compactRatio 比例触发)
|
|
502
|
-
let requestTransientContext =
|
|
503
|
-
transientFor(activeTools),
|
|
504
|
-
deliveryCorrectionAttempted && !resultPresented ? DELIVERY_CORRECTION_REMINDER : '',
|
|
505
|
-
].filter(Boolean).join('\n\n');
|
|
667
|
+
let requestTransientContext = buildRequestTransientContext();
|
|
506
668
|
lastRequestTransientContext = requestTransientContext;
|
|
507
669
|
const preCompactBudget = buildContextBudgetPlan({
|
|
508
670
|
provider,
|
|
@@ -516,6 +678,11 @@ export async function* runQuery(
|
|
|
516
678
|
actualPromptTokens: lastPromptTokens,
|
|
517
679
|
toolsTokens: getToolsSchemaTokens(activeTools, effectiveToolSet.signature),
|
|
518
680
|
transientTokens: countTextTokens(requestTransientContext),
|
|
681
|
+
// cacheEdits 判据校正:本地 history 未瘦身,扣掉服务端已删部分的估算,
|
|
682
|
+
// 防止虚高估算劫持 Math.max 导致压缩过早触发(压缩会重建整个前缀缓存)。
|
|
683
|
+
...(cacheEditsMode && sessionState?.microCompact
|
|
684
|
+
? { serverDeletedTokens: sessionState.microCompact.cacheEdits.activeDeletedTokens }
|
|
685
|
+
: {}),
|
|
519
686
|
signal,
|
|
520
687
|
promptCache: options.promptCache,
|
|
521
688
|
chat: chatFn,
|
|
@@ -525,6 +692,9 @@ export async function* runQuery(
|
|
|
525
692
|
yield { type: 'stage_change', stage: 'compacting' };
|
|
526
693
|
history.length = 0;
|
|
527
694
|
history.push(...compact.messages);
|
|
695
|
+
// cacheEdits 状态随压缩作废:pinned 的翻译后下标指向已不存在的消息序列,
|
|
696
|
+
// 服务端缓存前缀也已失效——不重置会导致删除指令插错位置甚至重复删除。
|
|
697
|
+
if (sessionState?.microCompact) resetCacheEdits(sessionState.microCompact);
|
|
528
698
|
lastPromptTokens = undefined;
|
|
529
699
|
lastCachedPromptTokens = undefined;
|
|
530
700
|
lastCacheMissTokens = undefined;
|
|
@@ -553,6 +723,8 @@ export async function* runQuery(
|
|
|
553
723
|
if (snipped.messagesRemoved > 0) {
|
|
554
724
|
history.length = 0;
|
|
555
725
|
history.push(...snipped.messages);
|
|
726
|
+
// 同 compact:snip 砍头后 pinned 下标失位,cacheEdits 必须重置
|
|
727
|
+
if (sessionState?.microCompact) resetCacheEdits(sessionState.microCompact);
|
|
556
728
|
lastPromptTokens = undefined;
|
|
557
729
|
lastCachedPromptTokens = undefined;
|
|
558
730
|
lastCacheMissTokens = undefined;
|
|
@@ -588,10 +760,7 @@ export async function* runQuery(
|
|
|
588
760
|
|
|
589
761
|
// Re-open transient capacity after compaction. If the compacted tail is still
|
|
590
762
|
// 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');
|
|
763
|
+
requestTransientContext = buildRequestTransientContext();
|
|
595
764
|
lastRequestTransientContext = requestTransientContext;
|
|
596
765
|
let postCompactBudget = buildContextBudgetPlan({
|
|
597
766
|
provider,
|
|
@@ -616,10 +785,7 @@ export async function* runQuery(
|
|
|
616
785
|
messagesRemoved: snipped.messagesRemoved,
|
|
617
786
|
tokensFreed: snipped.tokensFreed,
|
|
618
787
|
};
|
|
619
|
-
requestTransientContext =
|
|
620
|
-
transientFor(activeTools),
|
|
621
|
-
deliveryCorrectionAttempted && !resultPresented ? DELIVERY_CORRECTION_REMINDER : '',
|
|
622
|
-
].filter(Boolean).join('\n\n');
|
|
788
|
+
requestTransientContext = buildRequestTransientContext();
|
|
623
789
|
lastRequestTransientContext = requestTransientContext;
|
|
624
790
|
postCompactBudget = buildContextBudgetPlan({
|
|
625
791
|
provider,
|
|
@@ -644,6 +810,10 @@ export async function* runQuery(
|
|
|
644
810
|
runtime: runtimeContext,
|
|
645
811
|
requiredNames: [...effectiveToolSet.requiredNames],
|
|
646
812
|
});
|
|
813
|
+
advisorCoordinator.setAvailable(
|
|
814
|
+
currentEffectiveToolSet.names.has('Advisor')
|
|
815
|
+
&& currentEffectiveToolSet.names.has('AdvisorDecision'),
|
|
816
|
+
);
|
|
647
817
|
if (emergencyFit.removedNames.length > 0) {
|
|
648
818
|
yield {
|
|
649
819
|
type: 'warning',
|
|
@@ -689,12 +859,22 @@ export async function* runQuery(
|
|
|
689
859
|
|
|
690
860
|
try {
|
|
691
861
|
const modelMessages = buildModelMessages(history, requestTransientContext, userMessage.id);
|
|
862
|
+
advisorCoordinator.markTransientDelivered();
|
|
692
863
|
for await (const ev of chatFn({
|
|
693
864
|
provider,
|
|
694
865
|
messages: modelMessages,
|
|
695
866
|
tools: currentEffectiveToolSet.tools,
|
|
696
867
|
signal,
|
|
868
|
+
conversationMode: runtimeContext.conversationMode ?? 'private',
|
|
697
869
|
promptCache: options.promptCache,
|
|
870
|
+
visionPolicy: visionRequiredThisTurn ? 'required' : 'optional-history',
|
|
871
|
+
// cacheEdits 回收状态(同一会话级对象跨轮复用;compact 后由 resetCacheEdits 清空)
|
|
872
|
+
...(cacheEditsMode && sessionState?.microCompact
|
|
873
|
+
? { cacheEdits: sessionState.microCompact.cacheEdits }
|
|
874
|
+
: {}),
|
|
875
|
+
...(options.responseFormat
|
|
876
|
+
? { responseFormat: options.responseFormat }
|
|
877
|
+
: {}),
|
|
698
878
|
})) {
|
|
699
879
|
if (ev.type === 'text_delta') {
|
|
700
880
|
if (!stageStreamingYielded) {
|
|
@@ -716,6 +896,8 @@ export async function* runQuery(
|
|
|
716
896
|
// reasoning_details 是结构化数组,没有 delta 字符串,跳过 yield
|
|
717
897
|
reasoningDetails.push(...ev.items);
|
|
718
898
|
}
|
|
899
|
+
} else if (ev.type === 'usage') {
|
|
900
|
+
if (typeof ev.promptTokens === 'number') lastPromptTokens = ev.promptTokens;
|
|
719
901
|
} else if (ev.type === 'done') {
|
|
720
902
|
// R2:记下流的终止原因。'length'(max_tokens 截断)会在本轮收尾时触发 warning。
|
|
721
903
|
lastFinishReason = ev.stopReason;
|
|
@@ -726,6 +908,18 @@ export async function* runQuery(
|
|
|
726
908
|
if (typeof ev.cacheMissTokens === 'number') lastCacheMissTokens = ev.cacheMissTokens;
|
|
727
909
|
if (typeof ev.cacheWriteTokens === 'number') lastCacheWriteTokens = ev.cacheWriteTokens;
|
|
728
910
|
if (ev.promptCacheUsage) lastPromptCacheUsage = ev.promptCacheUsage;
|
|
911
|
+
|
|
912
|
+
// 回合级记账(与上面的 lastPromptTokens 不同:那个压缩后要清零,这个不清)。
|
|
913
|
+
// ⚠️ 只有 provider 真报了才计入 reportedCalls —— 「没人告诉我」不能被当成
|
|
914
|
+
// 「真的是 0」,否则预算闸永远不触发。覆盖率由 trustworthy 如实暴露。
|
|
915
|
+
turnUsage.totalCalls += 1;
|
|
916
|
+
const reportedIn = typeof ev.promptTokens === 'number' ? ev.promptTokens : undefined;
|
|
917
|
+
const reportedOut = typeof ev.outputTokens === 'number' ? ev.outputTokens : undefined;
|
|
918
|
+
if (reportedIn !== undefined || reportedOut !== undefined) {
|
|
919
|
+
turnUsage.reportedCalls += 1;
|
|
920
|
+
turnUsage.inputTokens += reportedIn ?? 0;
|
|
921
|
+
turnUsage.outputTokens += reportedOut ?? 0;
|
|
922
|
+
}
|
|
729
923
|
}
|
|
730
924
|
}
|
|
731
925
|
} catch (e) {
|
|
@@ -824,24 +1018,54 @@ export async function* runQuery(
|
|
|
824
1018
|
...(reasoningDetails.length > 0 ? { reasoning_details: reasoningDetails } : {}),
|
|
825
1019
|
timestamp: Date.now(),
|
|
826
1020
|
id: crypto.randomUUID(),
|
|
1021
|
+
...(options.messageProvenance ? { provenance: options.messageProvenance } : {}),
|
|
827
1022
|
};
|
|
828
|
-
history.push(assistantMsg);
|
|
829
|
-
yield { type: 'assistant_message', message: assistantMsg as Extract<Message, { role: 'assistant' }> };
|
|
830
|
-
|
|
831
1023
|
// 4. 没有工具调用 → 整轮交互结束
|
|
832
1024
|
if (toolCallsByIndex.length === 0) {
|
|
1025
|
+
const emptyResponse = stripThinkBlocks(assistantText).length === 0;
|
|
833
1026
|
// A model occasionally writes a path/link in prose and stops without formally
|
|
834
1027
|
// presenting it. Give it one model-only corrective pass; the reminder is
|
|
835
1028
|
// transient and therefore never pollutes persisted conversation history.
|
|
836
1029
|
if (
|
|
837
|
-
formalDeliveryRequired
|
|
1030
|
+
!emptyResponse && formalDeliveryRequired
|
|
838
1031
|
&& !resultPresented
|
|
839
1032
|
&& !deliveryCorrectionAttempted
|
|
840
1033
|
&& turn < maxTurns
|
|
841
1034
|
) {
|
|
842
1035
|
deliveryCorrectionAttempted = true;
|
|
1036
|
+
yield {
|
|
1037
|
+
type: 'assistant_draft_discarded',
|
|
1038
|
+
reason: 'formal_delivery',
|
|
1039
|
+
textLength: assistantText.length,
|
|
1040
|
+
};
|
|
1041
|
+
continue;
|
|
1042
|
+
}
|
|
1043
|
+
const deliveryCheckpoint = emptyResponse
|
|
1044
|
+
? { allow: true as const }
|
|
1045
|
+
: advisorCoordinator.beforeDelivery();
|
|
1046
|
+
if (!deliveryCheckpoint.allow && turn < maxTurns) {
|
|
1047
|
+
yield {
|
|
1048
|
+
type: 'assistant_draft_discarded',
|
|
1049
|
+
reason: 'advisor_checkpoint',
|
|
1050
|
+
textLength: assistantText.length,
|
|
1051
|
+
};
|
|
843
1052
|
continue;
|
|
844
1053
|
}
|
|
1054
|
+
if (emptyResponse) {
|
|
1055
|
+
// A no-tool response is already the model's terminal signal. Do not turn it
|
|
1056
|
+
// into another model round, and do not persist/broadcast an empty bubble.
|
|
1057
|
+
yield {
|
|
1058
|
+
type: 'assistant_draft_discarded',
|
|
1059
|
+
reason: 'empty_response',
|
|
1060
|
+
textLength: assistantText.length,
|
|
1061
|
+
};
|
|
1062
|
+
} else {
|
|
1063
|
+
history.push(assistantMsg);
|
|
1064
|
+
yield {
|
|
1065
|
+
type: 'assistant_message',
|
|
1066
|
+
message: assistantMsg as Extract<Message, { role: 'assistant' }>,
|
|
1067
|
+
};
|
|
1068
|
+
}
|
|
845
1069
|
// R2:最终回答被 provider 因 max_tokens 截断(finish_reason='length')时,
|
|
846
1070
|
// 不能静默当正常 end_turn 收尾 —— 否则 -p 宿主会拿到半截答案却以为成功。
|
|
847
1071
|
// 走通用 warning(text/json 都落 stderr,不污染 json 那一行 stdout 契约)。
|
|
@@ -884,26 +1108,41 @@ export async function* runQuery(
|
|
|
884
1108
|
: {}),
|
|
885
1109
|
promptCacheUsage: lastPromptCacheUsage,
|
|
886
1110
|
};
|
|
1111
|
+
// 资源账本挂到**每一条** turn_done 出口上。⚠️ 别只挂在 'completed' 那条:
|
|
1112
|
+
// 失败、交付不全、声明未核实的回合照样烧了 token,漏掉它们会让预算系统性少算,
|
|
1113
|
+
// 而少算的方向恰好是「让循环继续跑」—— 这正是本仓库记过账的
|
|
1114
|
+
// 「turn_done.completionStatus 被 print.ts 整条丢弃」同一类错误的反面。
|
|
1115
|
+
const usage: TurnResourceUsage = {
|
|
1116
|
+
...turnUsage,
|
|
1117
|
+
trustworthy: turnUsage.totalCalls > 0 && turnUsage.reportedCalls === turnUsage.totalCalls,
|
|
1118
|
+
};
|
|
887
1119
|
if (formalDeliveryRequired && !resultPresented) {
|
|
888
1120
|
const warning = '交付未完成:用户明确要求提供文件,但本轮没有成功提交可打开的文件结果。';
|
|
889
1121
|
yield { type: 'warning', message: warning };
|
|
890
|
-
yield { type: 'turn_done', completionStatus: 'delivery_incomplete', warning };
|
|
1122
|
+
yield { type: 'turn_done', completionStatus: 'delivery_incomplete', warning, usage };
|
|
891
1123
|
} else if (resultPresented && presentedResultUnverified) {
|
|
892
1124
|
const warning = '结果已提交,但至少一个文件尚未通过结构化验证。';
|
|
893
1125
|
yield { type: 'warning', message: warning };
|
|
894
|
-
yield { type: 'turn_done', completionStatus: 'delivery_unverified', warning };
|
|
1126
|
+
yield { type: 'turn_done', completionStatus: 'delivery_unverified', warning, usage };
|
|
895
1127
|
} else if (!claimValidation.verified) {
|
|
896
1128
|
yield {
|
|
897
1129
|
type: 'turn_done',
|
|
898
1130
|
completionStatus: 'claims_unverified',
|
|
899
1131
|
warning: claimValidation.warnings.join('\n'),
|
|
1132
|
+
usage,
|
|
900
1133
|
};
|
|
901
1134
|
} else {
|
|
902
|
-
yield { type: 'turn_done', completionStatus: 'completed' };
|
|
1135
|
+
yield { type: 'turn_done', completionStatus: 'completed', usage };
|
|
903
1136
|
}
|
|
904
1137
|
return;
|
|
905
1138
|
}
|
|
906
1139
|
|
|
1140
|
+
history.push(assistantMsg);
|
|
1141
|
+
yield {
|
|
1142
|
+
type: 'assistant_message',
|
|
1143
|
+
message: assistantMsg as Extract<Message, { role: 'assistant' }>,
|
|
1144
|
+
};
|
|
1145
|
+
|
|
907
1146
|
// 5. 执行工具调用
|
|
908
1147
|
//
|
|
909
1148
|
// R1:按 isConcurrencySafe 决定并发策略 —— 工具早就声明了这个字段,此前却没有
|
|
@@ -956,6 +1195,10 @@ export async function* runQuery(
|
|
|
956
1195
|
},
|
|
957
1196
|
};
|
|
958
1197
|
}
|
|
1198
|
+
const checkpoint = advisorCoordinator.beforeTool(tc, advisorSemanticsFor(tc));
|
|
1199
|
+
if (!checkpoint.allow) {
|
|
1200
|
+
return { tc, result: checkpoint.deferred! };
|
|
1201
|
+
}
|
|
959
1202
|
// 工具执行闸门:宿主注入后覆盖所有工具,包括只读工具。deny → 把 reason 当
|
|
960
1203
|
// tool_result 回填,模型据此改道/如实报告,不抛异常不中断整轮。宿主异常一律 deny。
|
|
961
1204
|
const isReadOnly = getToolByName(tc.function.name)?.isReadOnly === true;
|
|
@@ -992,19 +1235,35 @@ export async function* runQuery(
|
|
|
992
1235
|
tc.function.name,
|
|
993
1236
|
tc.function.arguments,
|
|
994
1237
|
signal,
|
|
995
|
-
{
|
|
1238
|
+
{
|
|
1239
|
+
...toolCallContext,
|
|
1240
|
+
cwd:
|
|
1241
|
+
sessionState?.activeWorktree?.path ?? toolCallContext.cwd,
|
|
1242
|
+
toolCallId: tc.id,
|
|
1243
|
+
reportProgress,
|
|
1244
|
+
},
|
|
996
1245
|
);
|
|
1246
|
+
if (tc.function.name === 'ComputerUse' && sessionState) {
|
|
1247
|
+
sessionState.computerAutomation = { lastUsedAt: Date.now() };
|
|
1248
|
+
}
|
|
1249
|
+
const downloadableArtifact =
|
|
1250
|
+
result.ok && result.metadata?.kind === 'browser_download'
|
|
1251
|
+
? result.metadata.artifact
|
|
1252
|
+
: undefined;
|
|
1253
|
+
const formalManifest =
|
|
1254
|
+
result.ok && result.metadata?.kind === 'result_manifest'
|
|
1255
|
+
? result.metadata.manifest
|
|
1256
|
+
: downloadableArtifact;
|
|
997
1257
|
if (
|
|
998
|
-
|
|
999
|
-
&&
|
|
1000
|
-
|
|
1001
|
-
&& options.commitFormalDelivery
|
|
1258
|
+
formalManifest &&
|
|
1259
|
+
(tc.function.name === 'PresentResult' || downloadableArtifact) &&
|
|
1260
|
+
options.commitFormalDelivery
|
|
1002
1261
|
) {
|
|
1003
1262
|
try {
|
|
1004
1263
|
await options.commitFormalDelivery({
|
|
1005
1264
|
toolCallId: tc.id,
|
|
1006
1265
|
turnId: userMessage.id ?? options.userMessageId ?? 'turn:unknown',
|
|
1007
|
-
manifest:
|
|
1266
|
+
manifest: formalManifest,
|
|
1008
1267
|
});
|
|
1009
1268
|
} catch (error) {
|
|
1010
1269
|
result = {
|
|
@@ -1015,6 +1274,10 @@ export async function* runQuery(
|
|
|
1015
1274
|
};
|
|
1016
1275
|
}
|
|
1017
1276
|
}
|
|
1277
|
+
if (tc.function.name === 'Advisor' || tc.function.name === 'AdvisorDecision') {
|
|
1278
|
+
// 串行批次内下一项可能就是 AdvisorDecision/受保护写入,必须立即推进协调器状态。
|
|
1279
|
+
advisorCoordinator.observeToolResult(tc, result);
|
|
1280
|
+
}
|
|
1018
1281
|
options.onToolEnd?.(tc.id, false);
|
|
1019
1282
|
return { tc, result };
|
|
1020
1283
|
} catch (e) {
|
|
@@ -1133,32 +1396,75 @@ export async function* runQuery(
|
|
|
1133
1396
|
}
|
|
1134
1397
|
|
|
1135
1398
|
// 本回合工作量累计 → 上调顾问配额(只增不减;已用掉的次数不会因此回退)。
|
|
1136
|
-
executedToolCalls += settled.
|
|
1399
|
+
executedToolCalls += settled.filter(({ result }) =>
|
|
1400
|
+
result.ok || result.disposition !== 'deferred').length;
|
|
1137
1401
|
toolCallContext.advisorCalls!.max = advisorQuotaFor(executedToolCalls);
|
|
1138
1402
|
|
|
1139
1403
|
// 严格按 toolCallsByIndex 顺序把 tool 消息回填 history(settled 已保持该顺序)。
|
|
1140
1404
|
let anyAborted = false;
|
|
1141
1405
|
for (const { tc, result } of settled) {
|
|
1142
|
-
|
|
1406
|
+
if (tc.function.name !== 'Advisor' && tc.function.name !== 'AdvisorDecision') {
|
|
1407
|
+
advisorCoordinator.observeToolResult(tc, result);
|
|
1408
|
+
}
|
|
1409
|
+
if (result.ok || result.disposition !== 'deferred') {
|
|
1410
|
+
evidenceLedger.record(tc.function.name, tc.function.arguments, result);
|
|
1411
|
+
}
|
|
1143
1412
|
if (!result.ok && result.error === 'aborted') anyAborted = true;
|
|
1144
|
-
|
|
1413
|
+
const browserArtifact =
|
|
1414
|
+
result.ok && result.metadata?.kind === 'browser_download'
|
|
1415
|
+
? result.metadata.artifact
|
|
1416
|
+
: undefined;
|
|
1417
|
+
if (
|
|
1418
|
+
result.ok &&
|
|
1419
|
+
((tc.function.name === 'PresentResult' &&
|
|
1420
|
+
result.metadata?.kind === 'result_manifest') ||
|
|
1421
|
+
browserArtifact)
|
|
1422
|
+
) {
|
|
1145
1423
|
resultPresented = true;
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1424
|
+
// `not_run` only says that no optional, format-specific validator was
|
|
1425
|
+
// invoked. The PresentResult boundary has still checked the file and
|
|
1426
|
+
// persisted its fingerprint, so it must not downgrade a normal delivery.
|
|
1427
|
+
const manifest =
|
|
1428
|
+
result.metadata?.kind === 'result_manifest'
|
|
1429
|
+
? result.metadata.manifest
|
|
1430
|
+
: browserArtifact;
|
|
1431
|
+
presentedResultUnverified ||=
|
|
1432
|
+
manifest?.files.some((file) => file.verification === 'failed') ??
|
|
1433
|
+
false;
|
|
1149
1434
|
}
|
|
1150
1435
|
const rawContent = result.ok ? result.content : `Error: ${result.error}`;
|
|
1151
|
-
|
|
1436
|
+
const basePolicy = result.policy ?? getToolResultPolicy(tc.function.name);
|
|
1437
|
+
// 超大结果先归档再压缩:归档失败时保留原文,绝不因为磁盘问题破坏上下文。
|
|
1438
|
+
const archived = result.ok && sessionState
|
|
1439
|
+
? await archiveToolResult(rawContent, sessionState.microCompact.archiveKey, tc.function.name)
|
|
1440
|
+
: { content: rawContent, originalChars: rawContent.length };
|
|
1441
|
+
// 归档退化必须发声:撞会话上限后每条超长结果都退回原文塞进上下文,
|
|
1442
|
+
// 也就是这个机制本来要解决的问题原样回来了 —— 静默的话没人会发现。
|
|
1443
|
+
if (archived.degraded && !archiveDegradedWarned) {
|
|
1444
|
+
archiveDegradedWarned = true;
|
|
1445
|
+
yield {
|
|
1446
|
+
type: 'warning',
|
|
1447
|
+
message: archived.degraded === 'session-limit'
|
|
1448
|
+
? '工具结果归档已达本会话上限,后续超长结果将直接进入上下文(归档不再生效)。建议 /new 开新会话。'
|
|
1449
|
+
: '工具结果归档写盘失败,后续超长结果将直接进入上下文。请检查磁盘空间与权限。',
|
|
1450
|
+
};
|
|
1451
|
+
}
|
|
1452
|
+
const policy = archived.archiveRef
|
|
1453
|
+
? { ...basePolicy, archiveRef: archived.archiveRef, originalChars: archived.originalChars }
|
|
1454
|
+
: basePolicy;
|
|
1455
|
+
// 微压缩仅对成功结果走;policy 会保护高价值结果,避免不可逆中段丢失。
|
|
1152
1456
|
const content = result.ok
|
|
1153
|
-
? microCompact(tc.function.name,
|
|
1457
|
+
? microCompact(tc.function.name, archived.content, sessionState?.microCompact, policy)
|
|
1154
1458
|
: rawContent;
|
|
1155
1459
|
history.push({
|
|
1156
1460
|
role: 'tool',
|
|
1157
1461
|
content,
|
|
1158
1462
|
tool_call_id: tc.id,
|
|
1463
|
+
tool_result_policy: policy,
|
|
1159
1464
|
...(result.metadata ? { display_metadata: result.metadata } : {}),
|
|
1160
1465
|
timestamp: Date.now(),
|
|
1161
1466
|
id: crypto.randomUUID(),
|
|
1467
|
+
...(options.messageProvenance ? { provenance: options.messageProvenance } : {}),
|
|
1162
1468
|
});
|
|
1163
1469
|
}
|
|
1164
1470
|
|
|
@@ -1169,6 +1475,10 @@ export async function* runQuery(
|
|
|
1169
1475
|
({ result }) =>
|
|
1170
1476
|
!result.ok && result.error.includes('第三次调用已在执行前拒绝'),
|
|
1171
1477
|
);
|
|
1478
|
+
const preemptedSignature = preemptedRepeatedFailure
|
|
1479
|
+
? settled.find(({ result }) =>
|
|
1480
|
+
!result.ok && result.error.includes('第三次调用已在执行前拒绝'))?.tc
|
|
1481
|
+
: undefined;
|
|
1172
1482
|
const stall = preemptedRepeatedFailure
|
|
1173
1483
|
? ({
|
|
1174
1484
|
stalled: true as const,
|
|
@@ -1183,12 +1493,15 @@ export async function* runQuery(
|
|
|
1183
1493
|
: { stalled: false as const };
|
|
1184
1494
|
if (stall.stalled) {
|
|
1185
1495
|
const message = stall.reason === 'repeated_failure'
|
|
1186
|
-
? '
|
|
1187
|
-
: '
|
|
1496
|
+
? '检测到同一工具以相同参数连续返回相同失败;请停止原样重试,改做只读诊断、切换路径或咨询 Advisor。'
|
|
1497
|
+
: '检测到连续工具轮次没有产生新文本或新结果;请切换路径或咨询 Advisor。';
|
|
1498
|
+
advisorCoordinator.notifyStalled(
|
|
1499
|
+
stall.signature
|
|
1500
|
+
?? (preemptedSignature
|
|
1501
|
+
? `${preemptedSignature.function.name}\0${preemptedSignature.function.arguments}`
|
|
1502
|
+
: stall.reason),
|
|
1503
|
+
);
|
|
1188
1504
|
yield { type: 'warning', message };
|
|
1189
|
-
yield { type: 'harness_diagnostics', diagnostics: harnessDiagnostics('stalled') };
|
|
1190
|
-
yield { type: 'error', error: message, code: 'stalled' };
|
|
1191
|
-
return;
|
|
1192
1505
|
}
|
|
1193
1506
|
|
|
1194
1507
|
// 关键节点截图回填:把工具附带的实况截图作为一条带图 user 消息喂给(视觉)模型自我核验,
|
|
@@ -1201,6 +1514,7 @@ export async function* runQuery(
|
|
|
1201
1514
|
// The history copy is intentionally downgraded after the turn. Emit a detached snapshot
|
|
1202
1515
|
// so TUI/Desktop stores do not see their already-rendered image mutate underneath them.
|
|
1203
1516
|
if (injectedShot) {
|
|
1517
|
+
visionRequiredThisTurn = true;
|
|
1204
1518
|
yield { type: 'injected_message', message: structuredClone(injectedShot) };
|
|
1205
1519
|
}
|
|
1206
1520
|
|
|
@@ -1311,7 +1625,7 @@ async function injectVisionFollowups(
|
|
|
1311
1625
|
settled: { result: ToolResult }[],
|
|
1312
1626
|
provider: Provider,
|
|
1313
1627
|
): Promise<Message | null> {
|
|
1314
|
-
if (provider.supportsVision
|
|
1628
|
+
if (provider.supportsVision === false) return null;
|
|
1315
1629
|
const images = settled.flatMap((s) => s.result.visionImages ?? []);
|
|
1316
1630
|
if (images.length === 0) return null;
|
|
1317
1631
|
evictPriorVisionShots(history);
|
|
@@ -1441,6 +1755,8 @@ function friendlyToolDescription(toolName: string, rawArgsJson: string): string
|
|
|
1441
1755
|
return `Fetching ...`;
|
|
1442
1756
|
}
|
|
1443
1757
|
}
|
|
1758
|
+
case 'BrowserBatch': return `Browser batch (${Array.isArray(args.steps) ? args.steps.length : 0} steps)`;
|
|
1759
|
+
case 'BrowserCDP': return `Browser CDP ${truncate(String(args.method ?? args.operation ?? ''), 45)}`;
|
|
1444
1760
|
case 'WebSearch': return `Searching \`${truncate(args.query, 50)}\``;
|
|
1445
1761
|
case 'Agent': {
|
|
1446
1762
|
const t = typeof args.agent_type === 'string' ? args.agent_type : 'explore';
|