minimal-agent 0.12.0 → 0.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -5
- package/package.json +62 -5
- package/plugins/computer_use/.claude-plugin/plugin.json +1 -1
- package/plugins/cron-runner/src/commands.js +4 -4
- package/plugins/cron-runner/src/commands.ts +4 -4
- package/plugins/workflow-runner/.claude-plugin/plugin.json +2 -2
- package/plugins/workflow-runner/commands/workflow.md +57 -90
- package/plugins/workflow-runner/commands/workflows.md +4 -4
- package/plugins/workflow-runner/plugin.js +24 -13
- package/plugins/workflow-runner/plugin.ts +28 -13
- package/plugins/workflow-runner/src/discovery.js +160 -0
- package/plugins/workflow-runner/src/discovery.ts +194 -0
- package/plugins/workflow-runner/src/goalAnchor.js +53 -0
- package/plugins/workflow-runner/src/goalAnchor.ts +59 -0
- package/plugins/workflow-runner/src/index.js +204 -185
- package/plugins/workflow-runner/src/index.ts +249 -224
- package/plugins/workflow-runner/src/inputCoercion.js +40 -0
- package/plugins/workflow-runner/src/inputCoercion.ts +43 -0
- package/plugins/workflow-runner/src/journal.js +224 -0
- package/plugins/workflow-runner/src/journal.ts +274 -0
- package/plugins/workflow-runner/src/jsonSchema.js +132 -0
- package/plugins/workflow-runner/src/jsonSchema.ts +162 -0
- package/plugins/workflow-runner/src/moduleResolution.js +64 -0
- package/plugins/workflow-runner/src/moduleResolution.ts +71 -0
- package/plugins/workflow-runner/src/resourceInputs.js +220 -0
- package/plugins/workflow-runner/src/resourceInputs.ts +300 -0
- package/plugins/workflow-runner/src/runner.js +404 -467
- package/plugins/workflow-runner/src/runner.ts +491 -535
- package/plugins/workflow-runner/src/runtime/config.js +46 -0
- package/plugins/workflow-runner/src/runtime/config.ts +69 -0
- package/plugins/workflow-runner/src/runtime/context.js +1304 -0
- package/plugins/workflow-runner/src/runtime/context.ts +1786 -0
- package/plugins/workflow-runner/src/runtime/exec.js +34 -0
- package/plugins/workflow-runner/src/runtime/exec.ts +53 -0
- package/plugins/workflow-runner/src/runtime/flow.js +8 -0
- package/plugins/workflow-runner/src/runtime/flow.ts +12 -0
- package/plugins/workflow-runner/src/runtime/fs.js +27 -0
- package/plugins/workflow-runner/src/runtime/fs.ts +41 -0
- package/plugins/workflow-runner/src/runtime/model.js +6 -0
- package/plugins/workflow-runner/src/runtime/model.ts +8 -0
- package/plugins/workflow-runner/src/runtime/output.js +7 -0
- package/plugins/workflow-runner/src/runtime/output.ts +21 -0
- package/plugins/workflow-runner/src/runtime/scheduler.js +141 -0
- package/plugins/workflow-runner/src/runtime/scheduler.ts +166 -0
- package/plugins/workflow-runner/src/runtime/state.js +16 -0
- package/plugins/workflow-runner/src/runtime/state.ts +19 -0
- package/plugins/workflow-runner/src/runtime/tools.js +3 -0
- package/plugins/workflow-runner/src/runtime/tools.ts +6 -0
- package/plugins/workflow-runner/src/sandbox.js +141 -0
- package/plugins/workflow-runner/src/sandbox.ts +182 -0
- package/plugins/workflow-runner/src/scriptGuard.d.mts +38 -0
- package/plugins/workflow-runner/src/scriptGuard.mjs +449 -0
- package/plugins/workflow-runner/src/taskIdentity.js +172 -152
- package/plugins/workflow-runner/src/taskIdentity.ts +233 -200
- package/plugins/workflow-runner/src/toolRuntime.js +29 -8
- package/plugins/workflow-runner/src/toolRuntime.ts +36 -11
- package/plugins/workflow-runner/src/types.js +7 -105
- package/plugins/workflow-runner/src/types.ts +155 -520
- package/plugins/workflow-runner/src/util.js +47 -0
- package/plugins/workflow-runner/src/util.ts +51 -0
- package/plugins/workflow-runner/src/workflowContext.js +20 -0
- package/plugins/workflow-runner/src/workflowContext.ts +58 -0
- package/plugins/workflow-runner/src/workflowDependencies.js +36 -0
- package/plugins/workflow-runner/src/workflowDependencies.ts +59 -0
- package/plugins/workflow-runner/src/workflowFingerprint.js +118 -0
- package/plugins/workflow-runner/src/workflowFingerprint.ts +122 -0
- package/plugins/workflow-runner/src/workflowState.js +542 -469
- package/plugins/workflow-runner/src/workflowState.ts +639 -589
- package/plugins/workflow-runner/test/api.test.ts +223 -0
- package/plugins/workflow-runner/test/apiCapabilities.test.ts +614 -0
- package/plugins/workflow-runner/test/apiConcurrencyGate.test.ts +104 -0
- package/plugins/workflow-runner/test/apiEcho.test.ts +210 -0
- package/plugins/workflow-runner/test/apiReadText.test.ts +178 -0
- package/plugins/workflow-runner/test/apiResourceLedger.test.ts +184 -0
- package/plugins/workflow-runner/test/childWorkflow.test.ts +304 -0
- package/plugins/workflow-runner/test/discovery.test.ts +234 -0
- package/plugins/workflow-runner/test/goalAnchor.test.ts +106 -0
- package/plugins/workflow-runner/test/goalController.test.ts +317 -0
- package/plugins/workflow-runner/test/journalLanes.test.ts +198 -0
- package/plugins/workflow-runner/test/listResources.test.ts +106 -0
- package/plugins/workflow-runner/test/resourceInputs.test.ts +294 -0
- package/plugins/workflow-runner/test/sandbox.test.ts +126 -0
- package/plugins/workflow-runner/test/scheduler.test.ts +70 -0
- package/plugins/workflow-runner/test/scriptGuard.test.ts +276 -0
- package/plugins/workflow-runner/test/skillDocSync.test.ts +78 -0
- package/plugins/workflow-runner/test/taskIdentity.test.ts +288 -179
- package/plugins/workflow-runner/test/workflowFingerprint.test.ts +106 -0
- package/plugins/workflow-runner/test/workflowState.test.ts +429 -0
- package/skills/pptx/examples/assets/cover-bg.svg +23 -0
- package/skills/pptx/examples/assets/icon-spark.svg +5 -0
- package/skills/skill-creator/scripts/run_eval.py +3 -2
- package/skills/ui-design/SKILL.md +5 -2
- package/skills/ui-design/references/layout-composition.md +1 -1
- package/skills/workflow-creator/SKILL.md +344 -0
- package/skills/workflow-creator/assets/template/workflow.mjs +48 -0
- package/skills/workflow-creator/references/api.md +216 -0
- package/skills/workflow-creator/references/migration.md +103 -0
- package/skills/workflow-creator/references/patterns.md +779 -0
- package/skills/workflow-creator/scripts/list-resources.mjs +205 -0
- package/skills/workflow-creator/scripts/scaffold.mjs +148 -0
- package/skills/workflow-creator/scripts/validate.mjs +275 -0
- package/skills/workflow-creator/scripts/workflow-creator.test.mjs +82 -0
- package/src/advisor/advisorCoordinator.js +503 -0
- package/src/advisor/advisorCoordinator.ts +657 -0
- package/src/cli/configMenu.tsx +0 -1
- package/src/cli/print.js +44 -6
- package/src/cli/print.ts +73 -4
- package/src/cli/streamJson.js +5 -6
- package/src/cli/streamJson.ts +8 -4
- package/src/cli/wizards/advisorWizard.js +4 -2
- package/src/cli/wizards/advisorWizard.tsx +8 -2
- package/src/cli/wizards/contextAdvisorWizard.js +5 -4
- package/src/cli/wizards/contextAdvisorWizard.tsx +7 -6
- package/src/collaboration/taskFlow.js +115 -0
- package/src/collaboration/taskFlow.ts +169 -0
- package/src/config/configDefaults.js +41 -1
- package/src/config/configDefaults.ts +43 -1
- package/src/config/configDiagnostics.ts +2 -2
- package/src/config/configFile.js +40 -9
- package/src/config/configFile.ts +60 -11
- package/src/config/configService.js +443 -74
- package/src/config/configService.ts +723 -142
- package/src/config/numericLimits.js +22 -0
- package/src/config/numericLimits.ts +24 -0
- package/src/config.js +1 -0
- package/src/config.ts +3 -2
- package/src/context/budgetPlan.js +1 -1
- package/src/context/budgetPlan.ts +1 -1
- package/src/context/compact.js +7 -2
- package/src/context/compact.ts +9 -2
- package/src/context/microCompactLite.js +212 -13
- package/src/context/microCompactLite.ts +261 -12
- package/src/context/sessionRegistry.js +8 -1
- package/src/context/sessionRegistry.ts +18 -5
- package/src/context/toolResultArchive.js +72 -0
- package/src/context/toolResultArchive.ts +95 -0
- package/src/goal/errors.js +8 -0
- package/src/goal/errors.ts +18 -0
- package/src/goal/goalRuntime.js +344 -0
- package/src/goal/goalRuntime.ts +466 -0
- package/src/goal/goalStore.js +200 -0
- package/src/goal/goalStore.ts +219 -0
- package/src/goal/index.js +4 -0
- package/src/goal/index.ts +4 -0
- package/src/goal/statusMapping.js +71 -0
- package/src/goal/statusMapping.ts +95 -0
- package/src/goal/types.js +7 -0
- package/src/goal/types.ts +179 -0
- package/src/goal/workflowController.js +306 -0
- package/src/goal/workflowController.ts +360 -0
- package/src/harness/evidenceLedger.js +2 -0
- package/src/harness/evidenceLedger.ts +1 -0
- package/src/harness/stallGuard.js +9 -3
- package/src/harness/stallGuard.ts +7 -3
- package/src/images/imageResolver.js +161 -0
- package/src/images/imageResolver.ts +189 -0
- package/src/infra/verification.js +56 -20
- package/src/infra/verification.ts +87 -20
- package/src/llm/client.js +79 -21
- package/src/llm/client.ts +182 -35
- package/src/llm/gatewayClient.js +201 -0
- package/src/llm/gatewayClient.ts +299 -0
- package/src/llm/messageProvenance.js +46 -0
- package/src/llm/messageProvenance.ts +58 -0
- package/src/llm/modelProvider.js +1 -0
- package/src/llm/modelProvider.ts +22 -0
- package/src/llm/providers/anthropicFormat.js +108 -1
- package/src/llm/providers/anthropicFormat.ts +115 -4
- package/src/llm/providers/openaiFormat.js +9 -0
- package/src/llm/providers/openaiFormat.ts +12 -3
- package/src/loop.js +319 -54
- package/src/loop.ts +373 -57
- package/src/main.js +4 -1
- package/src/main.tsx +4 -1
- package/src/managedExecution/index.js +2 -0
- package/src/managedExecution/index.ts +2 -0
- package/src/managedExecution/registry.js +15 -0
- package/src/managedExecution/registry.ts +20 -0
- package/src/managedExecution/types.js +1 -0
- package/src/managedExecution/types.ts +86 -0
- package/src/modelRef.js +9 -0
- package/src/modelRef.ts +31 -0
- package/src/multimodal.js +14 -28
- package/src/multimodal.ts +17 -33
- package/src/net/lanDiscovery.js +65 -0
- package/src/net/lanDiscovery.ts +82 -0
- package/src/plugin-sdk.js +28 -3
- package/src/plugin-sdk.ts +39 -6
- package/src/plugins/pluginApi.ts +16 -1
- package/src/plugins/pluginLoader.js +4 -0
- package/src/plugins/pluginLoader.ts +6 -0
- package/src/plugins/pluginRunner.js +152 -9
- package/src/plugins/pluginRunner.ts +186 -8
- package/src/plugins/slashFallback.js +113 -0
- package/src/plugins/slashFallback.ts +137 -0
- package/src/prompts/policyRegistry.js +15 -8
- package/src/prompts/policyRegistry.ts +18 -8
- package/src/prompts/promptAssembly.js +103 -12
- package/src/prompts/promptAssembly.ts +114 -12
- package/src/prompts/skillList.js +21 -3
- package/src/prompts/skillList.ts +27 -2
- package/src/prompts/stableSystemPrompt.js +18 -0
- package/src/prompts/stableSystemPrompt.ts +33 -0
- package/src/runtime/agentRuntimeContext.js +8 -2
- package/src/runtime/agentRuntimeContext.ts +17 -4
- package/src/session/runAgentTurn.js +78 -12
- package/src/session/runAgentTurn.ts +105 -23
- package/src/session/runTurn.js +10 -9
- package/src/session/runTurn.ts +17 -8
- package/src/session/sessionState.ts +2 -0
- package/src/team/auth.js +186 -0
- package/src/team/auth.ts +298 -0
- package/src/team/cli.js +105 -0
- package/src/team/cli.ts +122 -0
- package/src/team/onlineToolContract.js +64 -0
- package/src/team/onlineToolContract.ts +109 -0
- package/src/team/toolPolicy.js +36 -0
- package/src/team/toolPolicy.ts +75 -0
- package/src/tools/advisor/advisor.js +148 -37
- package/src/tools/advisor/advisor.ts +161 -38
- package/src/tools/advisor/advisorDecision.js +71 -0
- package/src/tools/advisor/advisorDecision.ts +85 -0
- package/src/tools/agent/agent.js +18 -5
- package/src/tools/agent/agent.ts +21 -7
- package/src/tools/agent/agentControl.js +246 -0
- package/src/tools/agent/agentControl.ts +284 -0
- package/src/tools/agent/agentTaskService.js +40 -1
- package/src/tools/agent/agentTaskService.ts +44 -1
- package/src/tools/agent/agentTypes.js +14 -2
- package/src/tools/agent/agentTypes.ts +15 -2
- package/src/tools/applypatch/applyPatch.js +362 -0
- package/src/tools/applypatch/applyPatch.ts +428 -0
- package/src/tools/bash/bash.js +70 -18
- package/src/tools/bash/bash.ts +75 -20
- package/src/tools/capabilities.js +10 -5
- package/src/tools/capabilities.ts +10 -5
- package/src/tools/computeruse/computerIntent.js +32 -0
- package/src/tools/computeruse/computerIntent.ts +46 -0
- package/src/tools/computeruse/telemetryLog.js +12 -0
- package/src/tools/computeruse/telemetryLog.ts +16 -0
- package/src/tools/computeruse/vision/telemetry.js +1 -0
- package/src/tools/computeruse/vision/telemetry.ts +1 -0
- package/src/tools/effectiveToolSet.js +4 -0
- package/src/tools/effectiveToolSet.ts +4 -0
- package/src/tools/index.js +21 -2
- package/src/tools/index.ts +33 -2
- package/src/tools/powershell/powershell.js +18 -1
- package/src/tools/powershell/powershell.ts +18 -2
- package/src/tools/presentresult/presentresult.js +6 -15
- package/src/tools/presentresult/presentresult.ts +6 -15
- package/src/tools/read/read.js +69 -11
- package/src/tools/read/read.ts +70 -11
- package/src/tools/shared/fileUtils.js +1 -2
- package/src/tools/shared/fileUtils.ts +1 -2
- package/src/tools/shared/permanentDeleteGuard.js +34 -3
- package/src/tools/shared/permanentDeleteGuard.ts +43 -6
- package/src/tools/shared/toolOutputStore.js +23 -1
- package/src/tools/shared/toolOutputStore.ts +22 -1
- package/src/tools/skill/skill.js +9 -3
- package/src/tools/skill/skill.ts +8 -4
- package/src/tools/toolPromptGuides.js +117 -0
- package/src/tools/toolPromptGuides.ts +137 -0
- package/src/tools/webbrowser/agentBrowserPath.js +19 -9
- package/src/tools/webbrowser/agentBrowserPath.ts +70 -28
- package/src/tools/webbrowser/browser.js +27 -11
- package/src/tools/webbrowser/browser.ts +68 -24
- package/src/tools/webbrowser/browserAutomation.js +329 -0
- package/src/tools/webbrowser/browserAutomation.ts +467 -0
- package/src/tools/webbrowser/browserAxState.js +97 -0
- package/src/tools/webbrowser/browserAxState.ts +142 -0
- package/src/tools/webbrowser/browserBatch.js +1578 -0
- package/src/tools/webbrowser/browserBatch.ts +2382 -0
- package/src/tools/webbrowser/browserCdp.js +208 -0
- package/src/tools/webbrowser/browserCdp.ts +255 -0
- package/src/tools/webbrowser/browserConditions.js +24 -0
- package/src/tools/webbrowser/browserConditions.ts +45 -0
- package/src/tools/webbrowser/browserContent.js +281 -0
- package/src/tools/webbrowser/browserContent.ts +438 -0
- package/src/tools/webbrowser/browserControl.js +131 -30
- package/src/tools/webbrowser/browserControl.ts +210 -50
- package/src/tools/webbrowser/browserDaemon.js +22 -14
- package/src/tools/webbrowser/browserDaemon.ts +54 -21
- package/src/tools/webbrowser/browserFrames.js +47 -0
- package/src/tools/webbrowser/browserFrames.ts +54 -0
- package/src/tools/webbrowser/browserInteractionResolver.js +17 -0
- package/src/tools/webbrowser/browserInteractionResolver.ts +40 -0
- package/src/tools/webbrowser/browserLocator.js +140 -0
- package/src/tools/webbrowser/browserLocator.ts +194 -0
- package/src/tools/webbrowser/browserOutcome.js +10 -17
- package/src/tools/webbrowser/browserOutcome.ts +48 -31
- package/src/tools/webbrowser/browserRecovery.js +58 -0
- package/src/tools/webbrowser/browserRecovery.ts +83 -0
- package/src/tools/webbrowser/browserRunRegistry.js +35 -3
- package/src/tools/webbrowser/browserRunRegistry.ts +91 -15
- package/src/tools/webbrowser/browserService.js +28 -2
- package/src/tools/webbrowser/browserService.ts +49 -9
- package/src/tools/webbrowser/browserStream.js +132 -33
- package/src/tools/webbrowser/browserStream.ts +224 -50
- package/src/tools/webbrowser/browserTabs.js +48 -15
- package/src/tools/webbrowser/browserTabs.ts +178 -96
- package/src/tools/webbrowser/webbrowser.js +683 -144
- package/src/tools/webbrowser/webbrowser.ts +979 -236
- package/src/tools/webfetch/preapproved.js +0 -2
- package/src/tools/webfetch/preapproved.ts +0 -2
- package/src/tools/websearch/websearch.js +129 -8
- package/src/tools/websearch/websearch.ts +138 -6
- package/src/types.ts +450 -42
- package/src/ui/App.js +377 -58
- package/src/ui/App.tsx +576 -111
- package/src/ui/LiveArea.js +14 -8
- package/src/ui/LiveArea.tsx +14 -10
- package/src/ui/MessageList.js +63 -20
- package/src/ui/MessageList.tsx +101 -16
- package/src/ui/ModelPicker.js +7 -4
- package/src/ui/ModelPicker.tsx +55 -8
- package/src/ui/hooks/useChat.js +152 -163
- package/src/ui/hooks/useChat.ts +396 -334
- package/src/ui/loginCommand.js +56 -0
- package/src/ui/loginCommand.ts +63 -0
- package/src/ui/toolProgress.js +41 -0
- package/src/ui/toolProgress.ts +44 -0
- package/src/web/browserBridge.js +20 -2
- package/src/web/browserBridge.ts +20 -2
- package/src/web/protocol.ts +1897 -194
- package/workflows/adaptive-research/workflow.mjs +86 -0
- package/workflows/e2e-capabilities/workflow.mjs +102 -0
- package/workflows/e2e-llm/workflow.mjs +52 -0
- package/workflows/e2e-smoke/workflow.mjs +59 -0
- package/workflows/e2e-vision-schema/README.md +48 -0
- package/workflows/e2e-vision-schema/workflow.mjs +149 -0
- package/workflows/e2e-write-greet/workflow.mjs +37 -0
- package/workflows/file-census/README.md +14 -0
- package/workflows/file-census/scripts/.gitkeep +2 -0
- package/workflows/file-census/workflow.mjs +110 -0
- package/workflows/goal/README.md +166 -0
- package/workflows/goal/prompts/audit.md +57 -0
- package/workflows/goal/prompts/contract.md +56 -0
- package/workflows/goal/prompts/iterate.md +37 -0
- package/workflows/goal/workflow.mjs +501 -0
- package/workflows/illustrate/README.md +90 -0
- package/workflows/illustrate/assets/engines.json +20 -0
- package/workflows/illustrate/assets/extract-baidu.js +21 -0
- package/workflows/illustrate/assets/extract-bing.js +33 -0
- package/workflows/illustrate/assets/extract-google.js +31 -0
- package/workflows/illustrate/workflow.mjs +301 -0
- package/workflows/plan/workflow.mjs +389 -0
- package/workflows/question-crops/README.md +35 -0
- package/workflows/question-crops/prompts/inventory.md +23 -0
- package/workflows/question-crops/scripts/image-ops.mjs +305 -0
- package/workflows/question-crops/scripts/image-ops.test.mjs +95 -0
- package/workflows/question-crops/workflow.mjs +172 -0
- package/workflows/ralph-loop/workflow.mjs +162 -0
- package/workflows/research/DESIGN.md +449 -0
- package/workflows/research/README.md +74 -0
- package/workflows/research/prompts/s0-goal.md +43 -0
- package/workflows/research/prompts/s1-early-exit.md +22 -0
- package/workflows/research/prompts/s1-map.md +58 -0
- package/workflows/research/prompts/s2-options.md +53 -0
- package/workflows/research/prompts/s3-deep-dive.md +56 -0
- package/workflows/research/prompts/s4-verify.md +74 -0
- package/workflows/research/prompts/s5-synthesis.md +81 -0
- package/workflows/research/prompts/s5b-fill-gaps.md +32 -0
- package/workflows/research/prompts/s6-package.md +32 -0
- package/workflows/research/scripts/build-report.mjs +98 -0
- package/workflows/research/workflow.mjs +288 -0
- package/workflows/wechat-reading-shots/prompts/collect-params.md +16 -0
- package/workflows/wechat-reading-shots/prompts/evaluate.md +20 -0
- package/workflows/wechat-reading-shots/prompts/open-book.md +28 -0
- package/workflows/wechat-reading-shots/prompts/read-page.md +13 -0
- package/workflows/wechat-reading-shots/prompts/turn-batch.md +24 -0
- package/workflows/wechat-reading-shots/test/control-flow.test.ts +329 -0
- package/workflows/wechat-reading-shots/workflow.mjs +266 -0
- package/workflows/wrong-question-docx/README.md +55 -0
- package/workflows/wrong-question-docx/prompts/figure-qa.md +8 -0
- package/workflows/wrong-question-docx/prompts/transcribe.md +25 -0
- package/workflows/wrong-question-docx/scripts/workbook.mjs +857 -0
- package/workflows/wrong-question-docx/test/control-flow.test.ts +165 -0
- package/workflows/wrong-question-docx/test/workbook.test.mjs +303 -0
- package/workflows/wrong-question-docx/workflow.mjs +485 -0
- package/plugins/workflow-runner/src/agentCommon.js +0 -354
- package/plugins/workflow-runner/src/agentCommon.ts +0 -435
- package/plugins/workflow-runner/src/controlFlow/branch.js +0 -40
- package/plugins/workflow-runner/src/controlFlow/branch.ts +0 -46
- package/plugins/workflow-runner/src/controlFlow/loop.js +0 -211
- package/plugins/workflow-runner/src/controlFlow/loop.ts +0 -248
- package/plugins/workflow-runner/src/controlFlow/pause.js +0 -129
- package/plugins/workflow-runner/src/controlFlow/pause.ts +0 -141
- package/plugins/workflow-runner/src/controlFlow/signal.js +0 -21
- package/plugins/workflow-runner/src/controlFlow/signal.ts +0 -29
- package/plugins/workflow-runner/src/controlFlow/types.js +0 -18
- package/plugins/workflow-runner/src/controlFlow/types.ts +0 -63
- package/plugins/workflow-runner/src/dslManifest.js +0 -72
- package/plugins/workflow-runner/src/dslManifest.ts +0 -127
- package/plugins/workflow-runner/src/expressions.js +0 -618
- package/plugins/workflow-runner/src/expressions.ts +0 -622
- package/plugins/workflow-runner/src/loader.js +0 -573
- package/plugins/workflow-runner/src/loader.ts +0 -721
- package/plugins/workflow-runner/src/multimodal.js +0 -38
- package/plugins/workflow-runner/src/multimodal.ts +0 -64
- package/plugins/workflow-runner/src/recovery.js +0 -119
- package/plugins/workflow-runner/src/recovery.ts +0 -149
- package/plugins/workflow-runner/src/stepExecutors/agent.js +0 -173
- package/plugins/workflow-runner/src/stepExecutors/agent.ts +0 -234
- package/plugins/workflow-runner/src/stepExecutors/assert.js +0 -41
- package/plugins/workflow-runner/src/stepExecutors/assert.ts +0 -50
- package/plugins/workflow-runner/src/stepExecutors/echo.js +0 -31
- package/plugins/workflow-runner/src/stepExecutors/echo.ts +0 -38
- package/plugins/workflow-runner/src/stepExecutors/llm.js +0 -99
- package/plugins/workflow-runner/src/stepExecutors/llm.ts +0 -133
- package/plugins/workflow-runner/src/stepExecutors/set.js +0 -49
- package/plugins/workflow-runner/src/stepExecutors/set.ts +0 -56
- package/plugins/workflow-runner/src/stepExecutors/skill.js +0 -111
- package/plugins/workflow-runner/src/stepExecutors/skill.ts +0 -140
- package/plugins/workflow-runner/src/stepExecutors/tool.js +0 -208
- package/plugins/workflow-runner/src/stepExecutors/tool.ts +0 -256
- package/plugins/workflow-runner/src/stepExecutors/verify.js +0 -58
- package/plugins/workflow-runner/src/stepExecutors/verify.ts +0 -68
- package/plugins/workflow-runner/src/stepPipeline.js +0 -277
- package/plugins/workflow-runner/src/stepPipeline.ts +0 -366
- package/plugins/workflow-runner/test/agent.test.ts +0 -587
- package/plugins/workflow-runner/test/agentLlmError.test.ts +0 -373
- package/plugins/workflow-runner/test/bashBlacklist.sweep.test.ts +0 -65
- package/plugins/workflow-runner/test/captureMisbind.test.ts +0 -99
- package/plugins/workflow-runner/test/cli.e2e.test.ts +0 -147
- package/plugins/workflow-runner/test/context-files.test.ts +0 -320
- package/plugins/workflow-runner/test/e2e.test.ts +0 -370
- package/plugins/workflow-runner/test/engineRobustness.test.ts +0 -226
- package/plugins/workflow-runner/test/expressions.test.ts +0 -397
- package/plugins/workflow-runner/test/foundation.test.ts +0 -126
- package/plugins/workflow-runner/test/graceful.test.ts +0 -139
- package/plugins/workflow-runner/test/greedyInputs.load.test.ts +0 -47
- package/plugins/workflow-runner/test/hostCapability.test.ts +0 -108
- package/plugins/workflow-runner/test/illustrate.load.test.ts +0 -183
- package/plugins/workflow-runner/test/jsonPayload.test.ts +0 -83
- package/plugins/workflow-runner/test/loader.test.ts +0 -617
- package/plugins/workflow-runner/test/maxTurnsRetryDelay.test.ts +0 -33
- package/plugins/workflow-runner/test/multimodal.test.ts +0 -342
- package/plugins/workflow-runner/test/output-schema.test.ts +0 -213
- package/plugins/workflow-runner/test/parseWorkflowArgs.test.ts +0 -87
- package/plugins/workflow-runner/test/pluginRunner.isolation.test.ts +0 -269
- package/plugins/workflow-runner/test/ppt.load.test.ts +0 -1220
- package/plugins/workflow-runner/test/ralphLoop.e2e.test.ts +0 -220
- package/plugins/workflow-runner/test/recovery.test.ts +0 -265
- package/plugins/workflow-runner/test/runner.test.ts +0 -911
- package/plugins/workflow-runner/test/runnerPreflight.test.ts +0 -379
- package/plugins/workflow-runner/test/skillTurnDone.test.ts +0 -71
- package/plugins/workflow-runner/test/stateLifecycle.test.ts +0 -150
- package/plugins/workflow-runner/test/stepPipeline.test.ts +0 -114
- package/plugins/workflow-runner/test/taskArbitration.test.ts +0 -338
- package/plugins/workflow-runner/test/taskLifecycle.test.ts +0 -270
- package/plugins/workflow-runner/test/toolArtifactOverwrite.test.ts +0 -221
- package/plugins/workflow-runner/test/toolCapabilityBoundary.test.ts +0 -29
- package/plugins/workflow-runner/test/toolRuntimeIntegration.test.ts +0 -168
- package/plugins/workflow-runner/test/webppt.load.test.ts +0 -250
- package/plugins/workflow-runner/test/word.load.test.ts +0 -208
- package/plugins/workflow-runner/test/workflows.smoke.test.ts +0 -76
- package/plugins/workflow-runner/test/yamlGuardConditions.test.ts +0 -207
- package/src/advisorState.js +0 -22
- package/src/cli/configWizard.js +0 -293
- package/src/tools/toolProgressBridge.js +0 -48
- package/workflows/adaptive-research.yaml +0 -50
- package/workflows/book-review-short.yaml +0 -125
- package/workflows/e2e-write-greet.yaml +0 -31
- package/workflows/illustrate.yaml +0 -227
- package/workflows/plan.yaml +0 -470
- package/workflows/ppt.yaml +0 -1670
- package/workflows/ralph-loop.yaml +0 -199
- package/workflows/schema.json +0 -176
- package/workflows/webppt.yaml +0 -496
- package/workflows/wechat-reading-shots.yaml +0 -315
- package/workflows/word.yaml +0 -720
- package/workflows/youtube-shorts.yaml +0 -202
package/src/loop.js
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
|
* "工作过程显示"事件:
|
|
@@ -28,9 +28,10 @@
|
|
|
28
28
|
*/
|
|
29
29
|
import crypto from 'node:crypto';
|
|
30
30
|
import { autoCompactIfNeeded, getCompactThreshold } from './context/compact.js';
|
|
31
|
-
import { microCompact, incrementTurn, expireOldEntries } from './context/microCompactLite.js';
|
|
31
|
+
import { microCompact, incrementTurn, expireOldEntries, reclaimOldToolResults, resetCacheEdits, getToolResultPolicy, } from './context/microCompactLite.js';
|
|
32
32
|
import { isPromptTooLongError, reactiveCompactIfApplicable, } from './context/reactiveCompact.js';
|
|
33
33
|
import { snipCompactIfNeeded } from './context/snipCompact.js';
|
|
34
|
+
import { archiveToolResult } from './context/toolResultArchive.js';
|
|
34
35
|
import { countMessagesTokens, countTextTokens, estimateToolsTokens, } from './context/tokenCounter.js';
|
|
35
36
|
import { buildContextBudgetPlan, fitToolsForEmergency } from './context/budgetPlan.js';
|
|
36
37
|
import { resizeContextBundle } from './context/contextBundle.js';
|
|
@@ -40,20 +41,26 @@ import { ALL_TOOLS, executeTool as defaultExecuteTool, getToolByName, getToolGat
|
|
|
40
41
|
import { configService } from './config/configService.js';
|
|
41
42
|
import { capabilityForToolName } from './tools/capabilities.js';
|
|
42
43
|
import { createEffectiveToolSet } from './tools/effectiveToolSet.js';
|
|
44
|
+
import { classifyComputerAutomationIntent } from './tools/computeruse/computerIntent.js';
|
|
45
|
+
import { recordComputerUseExposure } from './tools/computeruse/telemetryLog.js';
|
|
46
|
+
import { filterToolsForProvider } from './team/toolPolicy.js';
|
|
43
47
|
import { hydrateSessionWorktree } from './tools/worktree/sessionWorktree.js';
|
|
44
48
|
import { capabilitiesForTools, enabledPolicyIds, } from './prompts/policyRegistry.js';
|
|
45
49
|
import { EvidenceLedger } from './harness/evidenceLedger.js';
|
|
46
50
|
import { StallGuard } from './harness/stallGuard.js';
|
|
47
51
|
import { validateFinalClaims } from './harness/claimValidation.js';
|
|
52
|
+
import { AdvisorCoordinator, isWorkspaceMutationTool, } from './advisor/advisorCoordinator.js';
|
|
53
|
+
import { classifyCommand } from './irreversible/rules.js';
|
|
48
54
|
import { createAgentRuntimeContext, } from './runtime/agentRuntimeContext.js';
|
|
49
|
-
|
|
50
|
-
const
|
|
51
|
-
const
|
|
52
|
-
const
|
|
55
|
+
import { stripThinkBlocks } from './utils/stripThink.js';
|
|
56
|
+
const DELIVERY_ARTIFACT = '(?:文件|合同|协议|方案|文档|表格|报告|图片|图像|海报|幻灯片|PPT|PDF|代码|脚本|压缩包|网页|页面|HTML|DOCX|XLSX|CSV|ZIP)';
|
|
57
|
+
const DELIVERY_ACTION = '(?:发送|(?<![开研])发|交付|提交|(?<!宣)传(?!统)|制作|生成|创建|导出|输出|修改|更新|完成|写|起草|做|设计|整理)';
|
|
58
|
+
const ENGLISH_DELIVERY_ARTIFACT = '(?:file|contract|document|report|spreadsheet|workbook|slides?|presentation|pdf|image|code|script|archive|zip|html|webpage|website)';
|
|
59
|
+
const ENGLISH_DELIVERY_ACTION = '(?:send|deliver|submit|provide|create|generate|export|update|modify|finish|produce|draft|write|make|build)';
|
|
53
60
|
/** Whether this turn explicitly asks for a concrete artifact that should be formally delivered. */
|
|
54
61
|
export function expectsFormalDelivery(input) {
|
|
55
62
|
const compact = input.replace(/\s+/g, '');
|
|
56
|
-
const chinese = new RegExp(`${DELIVERY_ACTION}(?:给)?我?.{0,24}${DELIVERY_ARTIFACT}|${DELIVERY_ARTIFACT}.{0,24}${DELIVERY_ACTION}(?:给)
|
|
63
|
+
const chinese = new RegExp(`${DELIVERY_ACTION}(?:给)?我?.{0,24}${DELIVERY_ARTIFACT}|${DELIVERY_ARTIFACT}.{0,24}${DELIVERY_ACTION}(?:给)?我?|给我.{0,24}${DELIVERY_ARTIFACT}|${DELIVERY_ARTIFACT}.{0,24}给我`, 'i');
|
|
57
64
|
const english = new RegExp(`\\b${ENGLISH_DELIVERY_ACTION}\\b.{0,80}\\b${ENGLISH_DELIVERY_ARTIFACT}\\b|\\b${ENGLISH_DELIVERY_ARTIFACT}\\b.{0,80}\\b${ENGLISH_DELIVERY_ACTION}\\b`, 'i');
|
|
58
65
|
return chinese.test(compact) || english.test(input);
|
|
59
66
|
}
|
|
@@ -92,7 +99,15 @@ function getToolsSchemaTokens(tools, signature) {
|
|
|
92
99
|
return cached;
|
|
93
100
|
}
|
|
94
101
|
/**
|
|
95
|
-
*
|
|
102
|
+
* 主工具循环的默认轮数上限 —— **唯一真值**。
|
|
103
|
+
*
|
|
104
|
+
* 2026-08 从 50 提到 200(4 倍)。它此前是 `options.maxTurns ?? 200` 里的一个裸字面量,
|
|
105
|
+
* 没有名字也就没有引用点,于是「跟着它一起动的东西」(顾问配额封顶)改不动 ——
|
|
106
|
+
* 见下面的 `ADVISOR_QUOTA_MAX`。
|
|
107
|
+
*/
|
|
108
|
+
export const DEFAULT_MAX_TURNS = 200;
|
|
109
|
+
/**
|
|
110
|
+
* 顾问咨询配额:基线 1 次,之后每累计 N 次工具调用解锁 1 次,封顶见 ADVISOR_QUOTA_MAX。
|
|
96
111
|
*
|
|
97
112
|
* 曾经是硬编码 1 次/回合。那个数字隐含「一个用户回合 = 一个决策点」的假设,
|
|
98
113
|
* 在短问答里成立,在「继续,完成后续所有工作」这类一句话跑几十步的长回合里
|
|
@@ -105,11 +120,48 @@ function getToolsSchemaTokens(tools, signature) {
|
|
|
105
120
|
* 刷工具调用换配额的成本远高于收益。
|
|
106
121
|
*/
|
|
107
122
|
const ADVISOR_QUOTA_BASE = 1;
|
|
108
|
-
const ADVISOR_QUOTA_MAX = 3;
|
|
109
123
|
const ADVISOR_QUOTA_UNLOCK_EVERY_TOOL_CALLS = 15;
|
|
124
|
+
/**
|
|
125
|
+
* 配额封顶**由 `DEFAULT_MAX_TURNS` 推导,不是独立的魔术数字**。
|
|
126
|
+
*
|
|
127
|
+
* 上面那段注释的论据是「一个长回合里真正值钱的咨询点至少三个」——那是按当时
|
|
128
|
+
* `maxTurns = 50` 算的。轮数上限后来提到 200(4 倍工作量塞进同一个回合),
|
|
129
|
+
* 「高风险批次做完」这类节点随之变多,封顶却没跟着动,于是配额重新变成瓶颈:
|
|
130
|
+
* 这正是它当年从「硬编码 1 次」被改掉的同一个毛病,只是换了个刻度。
|
|
131
|
+
*
|
|
132
|
+
* 用公式而不是再写一个字面量,两者才会一起动:
|
|
133
|
+
* 50 轮 → 3(复现历史值) · 200 轮 → 5 · 下限恒为 3
|
|
134
|
+
*/
|
|
135
|
+
const ADVISOR_QUOTA_MAX = Math.max(3, Math.round(DEFAULT_MAX_TURNS / 40));
|
|
110
136
|
export function advisorQuotaFor(executedToolCalls) {
|
|
111
137
|
return Math.min(ADVISOR_QUOTA_MAX, ADVISOR_QUOTA_BASE + Math.floor(executedToolCalls / ADVISOR_QUOTA_UNLOCK_EVERY_TOOL_CALLS));
|
|
112
138
|
}
|
|
139
|
+
function advisorSemanticsFor(call) {
|
|
140
|
+
let parsed = {};
|
|
141
|
+
try {
|
|
142
|
+
parsed = JSON.parse(call.function.arguments);
|
|
143
|
+
}
|
|
144
|
+
catch { /* zod layer reports malformed input */ }
|
|
145
|
+
const mutatesWorkspace = isWorkspaceMutationTool(call.function.name);
|
|
146
|
+
if (call.function.name === 'Bash' || call.function.name === 'PowerShell') {
|
|
147
|
+
const command = typeof parsed.command === 'string' ? parsed.command : '';
|
|
148
|
+
const risk = command ? classifyCommand(command) : null;
|
|
149
|
+
return { mutatesWorkspace, highRisk: risk?.riskLevel === 'critical', riskReason: risk?.why };
|
|
150
|
+
}
|
|
151
|
+
if (call.function.name === 'ComputerUse') {
|
|
152
|
+
const highRisk = parsed.intent === 'submit'
|
|
153
|
+
|| (parsed.action === 'window' && parsed.windowOperation === 'close');
|
|
154
|
+
return { mutatesWorkspace, highRisk, ...(highRisk ? { riskReason: '原生界面操作不可逆或可能提交外部动作' } : {}) };
|
|
155
|
+
}
|
|
156
|
+
if (call.function.name === 'WebBrowser' || call.function.name === 'BrowserBatch') {
|
|
157
|
+
const highRisk = parsed.intent === 'submit'
|
|
158
|
+
|| (call.function.name === 'BrowserBatch'
|
|
159
|
+
&& Array.isArray(parsed.steps)
|
|
160
|
+
&& parsed.steps.some((step) => typeof step === 'object' && step !== null && step.intent === 'submit'));
|
|
161
|
+
return { mutatesWorkspace, highRisk, ...(highRisk ? { riskReason: '浏览器动作将进行发布、购买、删除或最终提交' } : {}) };
|
|
162
|
+
}
|
|
163
|
+
return { mutatesWorkspace, highRisk: false };
|
|
164
|
+
}
|
|
113
165
|
/**
|
|
114
166
|
* 执行一次"用户输入 → 模型回答完成"的完整流程。
|
|
115
167
|
*
|
|
@@ -120,8 +172,8 @@ export async function* runQuery(userInput, options) {
|
|
|
120
172
|
// Tool screenshots are transient evidence for the tool continuation that produced them.
|
|
121
173
|
// Never carry a prior turn's base64 image into a new user request.
|
|
122
174
|
evictPriorVisionShots(history);
|
|
123
|
-
const maxTurns = options.maxTurns ??
|
|
124
|
-
const
|
|
175
|
+
const maxTurns = options.maxTurns ?? DEFAULT_MAX_TURNS;
|
|
176
|
+
const baseRuntimeContext = options.runtimeContext ?? createAgentRuntimeContext({
|
|
125
177
|
surface: 'headless',
|
|
126
178
|
profile: 'main',
|
|
127
179
|
cwd: process.cwd(),
|
|
@@ -135,10 +187,22 @@ export async function* runQuery(userInput, options) {
|
|
|
135
187
|
computerIntervention: options.computerIntervention,
|
|
136
188
|
toolGate: options.toolGate,
|
|
137
189
|
});
|
|
190
|
+
const computerIntent = baseRuntimeContext.computerIntent
|
|
191
|
+
?? classifyComputerAutomationIntent(userInput, sessionState?.computerAutomation);
|
|
192
|
+
const runtimeContext = createAgentRuntimeContext({
|
|
193
|
+
...baseRuntimeContext,
|
|
194
|
+
computerIntent,
|
|
195
|
+
});
|
|
196
|
+
if (runtimeContext.computer) {
|
|
197
|
+
void recordComputerUseExposure(computerIntent.needed, computerIntent.source);
|
|
198
|
+
}
|
|
199
|
+
if (!computerIntent.needed && sessionState)
|
|
200
|
+
delete sessionState.computerAutomation;
|
|
138
201
|
// DI fallback:测试可传 stub,生产走真实模块单例
|
|
139
202
|
const chatFn = options.chat ?? defaultChat;
|
|
140
203
|
const executeToolFn = options.executeTool ?? defaultExecuteTool;
|
|
141
204
|
const explicitFileDeliveryRequested = expectsFormalDelivery(userInput);
|
|
205
|
+
const currentUserMessageId = options.userMessageId ?? crypto.randomUUID();
|
|
142
206
|
const transientFor = (tools) => {
|
|
143
207
|
const opaque = options.transientContext?.trim() ?? '';
|
|
144
208
|
const bundle = options.transientBundle;
|
|
@@ -185,6 +249,8 @@ export async function* runQuery(userInput, options) {
|
|
|
185
249
|
currentUserInput: userInput,
|
|
186
250
|
userMessageId: options.userMessageId,
|
|
187
251
|
memoryAccess: runtimeContext.memoryAccess,
|
|
252
|
+
conversationMode: runtimeContext.conversationMode ?? 'private',
|
|
253
|
+
surface: runtimeContext.surface,
|
|
188
254
|
workspaceId: runtimeContext.workspaceId,
|
|
189
255
|
sessionId: runtimeContext.sessionId,
|
|
190
256
|
cwd: runtimeContext.cwd,
|
|
@@ -195,9 +261,10 @@ export async function* runQuery(userInput, options) {
|
|
|
195
261
|
// 实际下发给模型的工具集:allowedTools 未传时给全量;
|
|
196
262
|
// 传入数组时严格按数组过滤,[] 表示本轮不下发任何工具。
|
|
197
263
|
// 仅影响呈现给 LLM 的工具清单,不影响 executeTool 的内部调度能力。
|
|
198
|
-
const hostTools = options.availableTools ?? ALL_TOOLS;
|
|
264
|
+
const hostTools = filterToolsForProvider(options.availableTools ?? ALL_TOOLS, provider);
|
|
199
265
|
const availableTools = hostTools.filter((tool) => (tool.name !== 'Agent' || subagents.enabled)
|
|
200
|
-
&& (tool.name !== 'ComputerUse'
|
|
266
|
+
&& (tool.name !== 'ComputerUse'
|
|
267
|
+
|| (runtimeContext.computer !== undefined && runtimeContext.computerIntent?.needed === true)));
|
|
201
268
|
const effectiveToolSet = createEffectiveToolSet({
|
|
202
269
|
availableTools,
|
|
203
270
|
runtime: runtimeContext,
|
|
@@ -210,6 +277,30 @@ export async function* runQuery(userInput, options) {
|
|
|
210
277
|
&& runtimeContext.surface !== 'workflow'
|
|
211
278
|
&& effectiveToolSet.names.has('PresentResult');
|
|
212
279
|
let currentEffectiveToolSet = effectiveToolSet;
|
|
280
|
+
const advisorCoordinator = new AdvisorCoordinator({
|
|
281
|
+
history,
|
|
282
|
+
currentRequest: userInput,
|
|
283
|
+
currentRequestMessageId: currentUserMessageId,
|
|
284
|
+
advisorAvailable: effectiveToolSet.names.has('Advisor')
|
|
285
|
+
&& effectiveToolSet.names.has('AdvisorDecision'),
|
|
286
|
+
});
|
|
287
|
+
toolCallContext.advisorRuntime = advisorCoordinator;
|
|
288
|
+
// 组装每轮请求的易变上下文。三个构建点(首轮 / autoCompact 后 / snip 后)共用,
|
|
289
|
+
// 避免 Advisor 相关提醒在复制粘贴中三处漂移。提醒均为 transient:只进当轮请求副本,
|
|
290
|
+
// 不进会话历史(与 DELIVERY_CORRECTION_REMINDER 同构,见 buildModelMessages)。
|
|
291
|
+
const buildRequestTransientContext = () => {
|
|
292
|
+
const advisorAvailable = currentEffectiveToolSet.names.has('Advisor')
|
|
293
|
+
&& currentEffectiveToolSet.names.has('AdvisorDecision');
|
|
294
|
+
const parts = [
|
|
295
|
+
transientFor(activeTools),
|
|
296
|
+
deliveryCorrectionAttempted && !resultPresented ? DELIVERY_CORRECTION_REMINDER : '',
|
|
297
|
+
advisorAvailable ? advisorCoordinator.transientReminder() : '',
|
|
298
|
+
advisorAvailable
|
|
299
|
+
? advisorCoordinator.usageLine(toolCallContext.advisorCalls.used, toolCallContext.advisorCalls.max)
|
|
300
|
+
: '',
|
|
301
|
+
];
|
|
302
|
+
return parts.filter(Boolean).join('\n\n');
|
|
303
|
+
};
|
|
213
304
|
let lastRequestTransientContext = [options.transientBundle?.text, options.transientContext]
|
|
214
305
|
.filter(Boolean).join('\n\n');
|
|
215
306
|
const evidenceLedger = new EvidenceLedger();
|
|
@@ -269,6 +360,16 @@ export async function* runQuery(userInput, options) {
|
|
|
269
360
|
message: `未作为图片附加:${skipped.source}(${skipped.reason}),已按普通文本处理。`,
|
|
270
361
|
};
|
|
271
362
|
}
|
|
363
|
+
let visionRequiredThisTurn = Array.isArray(userContent)
|
|
364
|
+
&& userContent.some((part) => part.type === 'image_url' || part.type === 'video_url');
|
|
365
|
+
if (visionRequiredThisTurn && provider.supportsVision === false) {
|
|
366
|
+
yield {
|
|
367
|
+
type: 'error',
|
|
368
|
+
error: '当前模型已配置为不支持视觉输入,图片未发送。请切换到支持图片的模型。',
|
|
369
|
+
code: 'llm_error',
|
|
370
|
+
};
|
|
371
|
+
return;
|
|
372
|
+
}
|
|
272
373
|
// 1. 用户消息入栈(v2:填 timestamp,让 MessageList 的 turn 边界横线
|
|
273
374
|
// 能拿到首条 user 的真实时间,否则 fallback Date.now() 会丢失"提交时刻"语义)
|
|
274
375
|
const userMessage = {
|
|
@@ -277,12 +378,16 @@ export async function* runQuery(userInput, options) {
|
|
|
277
378
|
...(options.userAttachments?.length ? { attachments: options.userAttachments } : {}),
|
|
278
379
|
...(options.userBrowserReferences?.length ? { browserReferences: options.userBrowserReferences } : {}),
|
|
279
380
|
timestamp: Date.now(),
|
|
280
|
-
id:
|
|
381
|
+
id: currentUserMessageId,
|
|
382
|
+
...(options.messageProvenance ? { provenance: options.messageProvenance } : {}),
|
|
281
383
|
};
|
|
282
|
-
history.
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
384
|
+
const alreadyCommitted = history.some((message) => message.role === 'user' && message.id === userMessage.id);
|
|
385
|
+
if (!alreadyCommitted) {
|
|
386
|
+
history.push(userMessage);
|
|
387
|
+
// 立即通知 UI:用户消息已入栈,触发 bump 让 <Static> 马上把它 commit 进 scrollback,
|
|
388
|
+
// 不必等整轮 assistant 落定(保证用户输入第一时间置顶,符合 T-A-O-R 展示顺序)。
|
|
389
|
+
yield { type: 'user_message_committed', message: userMessage };
|
|
390
|
+
}
|
|
286
391
|
// 反应式压缩自救:本 runQuery 内最多触发 MAX_REACTIVE_PER_QUERY 次(防本轮死循环);
|
|
287
392
|
// 跨 runQuery 的连续失败由 reactiveCompact.ts 的 circuit breaker(consecutiveFailures)兜底。
|
|
288
393
|
let reactiveCount = 0;
|
|
@@ -290,6 +395,13 @@ export async function* runQuery(userInput, options) {
|
|
|
290
395
|
// 上一轮 LLM 返回的真实 prompt token 数(done.promptTokens;provider 开 include_usage 才有)。
|
|
291
396
|
// 作为 autoCompact 的权威判据,校正本地估算的系统性低估;压缩后清空(历史已变,旧值失真)。
|
|
292
397
|
let lastPromptTokens;
|
|
398
|
+
// ── 回合级资源账本 ────────────────────────────────────────────────
|
|
399
|
+
// ⚠️ 与 lastPromptTokens 生命周期**故意不同**:那个是「当前 history 有多大」的快照,
|
|
400
|
+
// 压缩后必须清零;这个是「本回合总共花了多少」的累计,压缩不影响已经花掉的钱。
|
|
401
|
+
// ⚠️ 它只记账,**不参与任何控制流** —— loop 绝不因预算中断进行中的工具循环。
|
|
402
|
+
const turnUsage = { inputTokens: 0, outputTokens: 0, totalCalls: 0, reportedCalls: 0 };
|
|
403
|
+
// 归档退化只报一次:每条超长结果都报会把上下文刷满,反而盖掉别的信号。
|
|
404
|
+
let archiveDegradedWarned = false;
|
|
293
405
|
// 提示词缓存观测位(纯展示,不参与任何控制流)。与 lastPromptTokens 同生命周期:
|
|
294
406
|
// 压缩后一并清空,避免把压缩前的命中数据挂到压缩后的新历史上误导排障。
|
|
295
407
|
let lastCachedPromptTokens;
|
|
@@ -302,6 +414,15 @@ export async function* runQuery(userInput, options) {
|
|
|
302
414
|
turn++;
|
|
303
415
|
incrementTurn(sessionState?.microCompact);
|
|
304
416
|
expireOldEntries(sessionState?.microCompact);
|
|
417
|
+
// Path C 批量回收旧工具结果:可重放的观察类工具结果(Read/Grep/Bash…)
|
|
418
|
+
// 超过阈值时只保留热尾部。必须在 buildContextBudgetPlan 之前跑——预算估算和
|
|
419
|
+
// autoCompact 判据直接看到瘦身后的历史,避免无谓的全量压缩。
|
|
420
|
+
// cacheEdits 模式(Anthropic 格式 + provider.cacheEdits):不改 history,删除目标
|
|
421
|
+
// 记入状态、由请求层组装 cache_edits 发送--服务端 KV 原地删除,前缀缓存完整保留。
|
|
422
|
+
// 本地估算的虚高部分(服务端已删内容)由 autoCompactIfNeeded 的 serverDeletedTokens
|
|
423
|
+
// 校正,判据不会因此过早触发压缩。
|
|
424
|
+
const cacheEditsMode = provider.apiFormat === 'anthropic' && provider.cacheEdits === true;
|
|
425
|
+
reclaimOldToolResults(history, sessionState?.microCompact, cacheEditsMode ? 'cacheEdits' : 'inline');
|
|
305
426
|
if (signal?.aborted) {
|
|
306
427
|
pushManualInterruptMarker(history);
|
|
307
428
|
if (!workStarted) {
|
|
@@ -324,10 +445,7 @@ export async function* runQuery(userInput, options) {
|
|
|
324
445
|
}
|
|
325
446
|
// 2. 自动压缩(判据 = max(上一轮真实 usage, 历史估算 + 工具 schema),
|
|
326
447
|
// 阈值 provider 无关、按 contextWindow × compactRatio 比例触发)
|
|
327
|
-
let requestTransientContext =
|
|
328
|
-
transientFor(activeTools),
|
|
329
|
-
deliveryCorrectionAttempted && !resultPresented ? DELIVERY_CORRECTION_REMINDER : '',
|
|
330
|
-
].filter(Boolean).join('\n\n');
|
|
448
|
+
let requestTransientContext = buildRequestTransientContext();
|
|
331
449
|
lastRequestTransientContext = requestTransientContext;
|
|
332
450
|
const preCompactBudget = buildContextBudgetPlan({
|
|
333
451
|
provider,
|
|
@@ -341,6 +459,11 @@ export async function* runQuery(userInput, options) {
|
|
|
341
459
|
actualPromptTokens: lastPromptTokens,
|
|
342
460
|
toolsTokens: getToolsSchemaTokens(activeTools, effectiveToolSet.signature),
|
|
343
461
|
transientTokens: countTextTokens(requestTransientContext),
|
|
462
|
+
// cacheEdits 判据校正:本地 history 未瘦身,扣掉服务端已删部分的估算,
|
|
463
|
+
// 防止虚高估算劫持 Math.max 导致压缩过早触发(压缩会重建整个前缀缓存)。
|
|
464
|
+
...(cacheEditsMode && sessionState?.microCompact
|
|
465
|
+
? { serverDeletedTokens: sessionState.microCompact.cacheEdits.activeDeletedTokens }
|
|
466
|
+
: {}),
|
|
344
467
|
signal,
|
|
345
468
|
promptCache: options.promptCache,
|
|
346
469
|
chat: chatFn,
|
|
@@ -350,6 +473,10 @@ export async function* runQuery(userInput, options) {
|
|
|
350
473
|
yield { type: 'stage_change', stage: 'compacting' };
|
|
351
474
|
history.length = 0;
|
|
352
475
|
history.push(...compact.messages);
|
|
476
|
+
// cacheEdits 状态随压缩作废:pinned 的翻译后下标指向已不存在的消息序列,
|
|
477
|
+
// 服务端缓存前缀也已失效——不重置会导致删除指令插错位置甚至重复删除。
|
|
478
|
+
if (sessionState?.microCompact)
|
|
479
|
+
resetCacheEdits(sessionState.microCompact);
|
|
353
480
|
lastPromptTokens = undefined;
|
|
354
481
|
lastCachedPromptTokens = undefined;
|
|
355
482
|
lastCacheMissTokens = undefined;
|
|
@@ -380,6 +507,9 @@ export async function* runQuery(userInput, options) {
|
|
|
380
507
|
if (snipped.messagesRemoved > 0) {
|
|
381
508
|
history.length = 0;
|
|
382
509
|
history.push(...snipped.messages);
|
|
510
|
+
// 同 compact:snip 砍头后 pinned 下标失位,cacheEdits 必须重置
|
|
511
|
+
if (sessionState?.microCompact)
|
|
512
|
+
resetCacheEdits(sessionState.microCompact);
|
|
383
513
|
lastPromptTokens = undefined;
|
|
384
514
|
lastCachedPromptTokens = undefined;
|
|
385
515
|
lastCacheMissTokens = undefined;
|
|
@@ -415,10 +545,7 @@ export async function* runQuery(userInput, options) {
|
|
|
415
545
|
}
|
|
416
546
|
// Re-open transient capacity after compaction. If the compacted tail is still
|
|
417
547
|
// too large, trim old history before sacrificing any model-visible ability.
|
|
418
|
-
requestTransientContext =
|
|
419
|
-
transientFor(activeTools),
|
|
420
|
-
deliveryCorrectionAttempted && !resultPresented ? DELIVERY_CORRECTION_REMINDER : '',
|
|
421
|
-
].filter(Boolean).join('\n\n');
|
|
548
|
+
requestTransientContext = buildRequestTransientContext();
|
|
422
549
|
lastRequestTransientContext = requestTransientContext;
|
|
423
550
|
let postCompactBudget = buildContextBudgetPlan({
|
|
424
551
|
provider,
|
|
@@ -443,10 +570,7 @@ export async function* runQuery(userInput, options) {
|
|
|
443
570
|
messagesRemoved: snipped.messagesRemoved,
|
|
444
571
|
tokensFreed: snipped.tokensFreed,
|
|
445
572
|
};
|
|
446
|
-
requestTransientContext =
|
|
447
|
-
transientFor(activeTools),
|
|
448
|
-
deliveryCorrectionAttempted && !resultPresented ? DELIVERY_CORRECTION_REMINDER : '',
|
|
449
|
-
].filter(Boolean).join('\n\n');
|
|
573
|
+
requestTransientContext = buildRequestTransientContext();
|
|
450
574
|
lastRequestTransientContext = requestTransientContext;
|
|
451
575
|
postCompactBudget = buildContextBudgetPlan({
|
|
452
576
|
provider,
|
|
@@ -470,6 +594,8 @@ export async function* runQuery(userInput, options) {
|
|
|
470
594
|
runtime: runtimeContext,
|
|
471
595
|
requiredNames: [...effectiveToolSet.requiredNames],
|
|
472
596
|
});
|
|
597
|
+
advisorCoordinator.setAvailable(currentEffectiveToolSet.names.has('Advisor')
|
|
598
|
+
&& currentEffectiveToolSet.names.has('AdvisorDecision'));
|
|
473
599
|
if (emergencyFit.removedNames.length > 0) {
|
|
474
600
|
yield {
|
|
475
601
|
type: 'warning',
|
|
@@ -509,12 +635,22 @@ export async function* runQuery(userInput, options) {
|
|
|
509
635
|
workStarted = true;
|
|
510
636
|
try {
|
|
511
637
|
const modelMessages = buildModelMessages(history, requestTransientContext, userMessage.id);
|
|
638
|
+
advisorCoordinator.markTransientDelivered();
|
|
512
639
|
for await (const ev of chatFn({
|
|
513
640
|
provider,
|
|
514
641
|
messages: modelMessages,
|
|
515
642
|
tools: currentEffectiveToolSet.tools,
|
|
516
643
|
signal,
|
|
644
|
+
conversationMode: runtimeContext.conversationMode ?? 'private',
|
|
517
645
|
promptCache: options.promptCache,
|
|
646
|
+
visionPolicy: visionRequiredThisTurn ? 'required' : 'optional-history',
|
|
647
|
+
// cacheEdits 回收状态(同一会话级对象跨轮复用;compact 后由 resetCacheEdits 清空)
|
|
648
|
+
...(cacheEditsMode && sessionState?.microCompact
|
|
649
|
+
? { cacheEdits: sessionState.microCompact.cacheEdits }
|
|
650
|
+
: {}),
|
|
651
|
+
...(options.responseFormat
|
|
652
|
+
? { responseFormat: options.responseFormat }
|
|
653
|
+
: {}),
|
|
518
654
|
})) {
|
|
519
655
|
if (ev.type === 'text_delta') {
|
|
520
656
|
if (!stageStreamingYielded) {
|
|
@@ -541,6 +677,10 @@ export async function* runQuery(userInput, options) {
|
|
|
541
677
|
reasoningDetails.push(...ev.items);
|
|
542
678
|
}
|
|
543
679
|
}
|
|
680
|
+
else if (ev.type === 'usage') {
|
|
681
|
+
if (typeof ev.promptTokens === 'number')
|
|
682
|
+
lastPromptTokens = ev.promptTokens;
|
|
683
|
+
}
|
|
544
684
|
else if (ev.type === 'done') {
|
|
545
685
|
// R2:记下流的终止原因。'length'(max_tokens 截断)会在本轮收尾时触发 warning。
|
|
546
686
|
lastFinishReason = ev.stopReason;
|
|
@@ -556,6 +696,17 @@ export async function* runQuery(userInput, options) {
|
|
|
556
696
|
lastCacheWriteTokens = ev.cacheWriteTokens;
|
|
557
697
|
if (ev.promptCacheUsage)
|
|
558
698
|
lastPromptCacheUsage = ev.promptCacheUsage;
|
|
699
|
+
// 回合级记账(与上面的 lastPromptTokens 不同:那个压缩后要清零,这个不清)。
|
|
700
|
+
// ⚠️ 只有 provider 真报了才计入 reportedCalls —— 「没人告诉我」不能被当成
|
|
701
|
+
// 「真的是 0」,否则预算闸永远不触发。覆盖率由 trustworthy 如实暴露。
|
|
702
|
+
turnUsage.totalCalls += 1;
|
|
703
|
+
const reportedIn = typeof ev.promptTokens === 'number' ? ev.promptTokens : undefined;
|
|
704
|
+
const reportedOut = typeof ev.outputTokens === 'number' ? ev.outputTokens : undefined;
|
|
705
|
+
if (reportedIn !== undefined || reportedOut !== undefined) {
|
|
706
|
+
turnUsage.reportedCalls += 1;
|
|
707
|
+
turnUsage.inputTokens += reportedIn ?? 0;
|
|
708
|
+
turnUsage.outputTokens += reportedOut ?? 0;
|
|
709
|
+
}
|
|
559
710
|
}
|
|
560
711
|
}
|
|
561
712
|
}
|
|
@@ -644,21 +795,53 @@ export async function* runQuery(userInput, options) {
|
|
|
644
795
|
...(reasoningDetails.length > 0 ? { reasoning_details: reasoningDetails } : {}),
|
|
645
796
|
timestamp: Date.now(),
|
|
646
797
|
id: crypto.randomUUID(),
|
|
798
|
+
...(options.messageProvenance ? { provenance: options.messageProvenance } : {}),
|
|
647
799
|
};
|
|
648
|
-
history.push(assistantMsg);
|
|
649
|
-
yield { type: 'assistant_message', message: assistantMsg };
|
|
650
800
|
// 4. 没有工具调用 → 整轮交互结束
|
|
651
801
|
if (toolCallsByIndex.length === 0) {
|
|
802
|
+
const emptyResponse = stripThinkBlocks(assistantText).length === 0;
|
|
652
803
|
// A model occasionally writes a path/link in prose and stops without formally
|
|
653
804
|
// presenting it. Give it one model-only corrective pass; the reminder is
|
|
654
805
|
// transient and therefore never pollutes persisted conversation history.
|
|
655
|
-
if (formalDeliveryRequired
|
|
806
|
+
if (!emptyResponse && formalDeliveryRequired
|
|
656
807
|
&& !resultPresented
|
|
657
808
|
&& !deliveryCorrectionAttempted
|
|
658
809
|
&& turn < maxTurns) {
|
|
659
810
|
deliveryCorrectionAttempted = true;
|
|
811
|
+
yield {
|
|
812
|
+
type: 'assistant_draft_discarded',
|
|
813
|
+
reason: 'formal_delivery',
|
|
814
|
+
textLength: assistantText.length,
|
|
815
|
+
};
|
|
660
816
|
continue;
|
|
661
817
|
}
|
|
818
|
+
const deliveryCheckpoint = emptyResponse
|
|
819
|
+
? { allow: true }
|
|
820
|
+
: advisorCoordinator.beforeDelivery();
|
|
821
|
+
if (!deliveryCheckpoint.allow && turn < maxTurns) {
|
|
822
|
+
yield {
|
|
823
|
+
type: 'assistant_draft_discarded',
|
|
824
|
+
reason: 'advisor_checkpoint',
|
|
825
|
+
textLength: assistantText.length,
|
|
826
|
+
};
|
|
827
|
+
continue;
|
|
828
|
+
}
|
|
829
|
+
if (emptyResponse) {
|
|
830
|
+
// A no-tool response is already the model's terminal signal. Do not turn it
|
|
831
|
+
// into another model round, and do not persist/broadcast an empty bubble.
|
|
832
|
+
yield {
|
|
833
|
+
type: 'assistant_draft_discarded',
|
|
834
|
+
reason: 'empty_response',
|
|
835
|
+
textLength: assistantText.length,
|
|
836
|
+
};
|
|
837
|
+
}
|
|
838
|
+
else {
|
|
839
|
+
history.push(assistantMsg);
|
|
840
|
+
yield {
|
|
841
|
+
type: 'assistant_message',
|
|
842
|
+
message: assistantMsg,
|
|
843
|
+
};
|
|
844
|
+
}
|
|
662
845
|
// R2:最终回答被 provider 因 max_tokens 截断(finish_reason='length')时,
|
|
663
846
|
// 不能静默当正常 end_turn 收尾 —— 否则 -p 宿主会拿到半截答案却以为成功。
|
|
664
847
|
// 走通用 warning(text/json 都落 stderr,不污染 json 那一行 stdout 契约)。
|
|
@@ -698,28 +881,42 @@ export async function* runQuery(userInput, options) {
|
|
|
698
881
|
: {}),
|
|
699
882
|
promptCacheUsage: lastPromptCacheUsage,
|
|
700
883
|
};
|
|
884
|
+
// 资源账本挂到**每一条** turn_done 出口上。⚠️ 别只挂在 'completed' 那条:
|
|
885
|
+
// 失败、交付不全、声明未核实的回合照样烧了 token,漏掉它们会让预算系统性少算,
|
|
886
|
+
// 而少算的方向恰好是「让循环继续跑」—— 这正是本仓库记过账的
|
|
887
|
+
// 「turn_done.completionStatus 被 print.ts 整条丢弃」同一类错误的反面。
|
|
888
|
+
const usage = {
|
|
889
|
+
...turnUsage,
|
|
890
|
+
trustworthy: turnUsage.totalCalls > 0 && turnUsage.reportedCalls === turnUsage.totalCalls,
|
|
891
|
+
};
|
|
701
892
|
if (formalDeliveryRequired && !resultPresented) {
|
|
702
893
|
const warning = '交付未完成:用户明确要求提供文件,但本轮没有成功提交可打开的文件结果。';
|
|
703
894
|
yield { type: 'warning', message: warning };
|
|
704
|
-
yield { type: 'turn_done', completionStatus: 'delivery_incomplete', warning };
|
|
895
|
+
yield { type: 'turn_done', completionStatus: 'delivery_incomplete', warning, usage };
|
|
705
896
|
}
|
|
706
897
|
else if (resultPresented && presentedResultUnverified) {
|
|
707
898
|
const warning = '结果已提交,但至少一个文件尚未通过结构化验证。';
|
|
708
899
|
yield { type: 'warning', message: warning };
|
|
709
|
-
yield { type: 'turn_done', completionStatus: 'delivery_unverified', warning };
|
|
900
|
+
yield { type: 'turn_done', completionStatus: 'delivery_unverified', warning, usage };
|
|
710
901
|
}
|
|
711
902
|
else if (!claimValidation.verified) {
|
|
712
903
|
yield {
|
|
713
904
|
type: 'turn_done',
|
|
714
905
|
completionStatus: 'claims_unverified',
|
|
715
906
|
warning: claimValidation.warnings.join('\n'),
|
|
907
|
+
usage,
|
|
716
908
|
};
|
|
717
909
|
}
|
|
718
910
|
else {
|
|
719
|
-
yield { type: 'turn_done', completionStatus: 'completed' };
|
|
911
|
+
yield { type: 'turn_done', completionStatus: 'completed', usage };
|
|
720
912
|
}
|
|
721
913
|
return;
|
|
722
914
|
}
|
|
915
|
+
history.push(assistantMsg);
|
|
916
|
+
yield {
|
|
917
|
+
type: 'assistant_message',
|
|
918
|
+
message: assistantMsg,
|
|
919
|
+
};
|
|
723
920
|
// 5. 执行工具调用
|
|
724
921
|
//
|
|
725
922
|
// R1:按 isConcurrencySafe 决定并发策略 —— 工具早就声明了这个字段,此前却没有
|
|
@@ -762,6 +959,10 @@ export async function* runQuery(userInput, options) {
|
|
|
762
959
|
},
|
|
763
960
|
};
|
|
764
961
|
}
|
|
962
|
+
const checkpoint = advisorCoordinator.beforeTool(tc, advisorSemanticsFor(tc));
|
|
963
|
+
if (!checkpoint.allow) {
|
|
964
|
+
return { tc, result: checkpoint.deferred };
|
|
965
|
+
}
|
|
765
966
|
// 工具执行闸门:宿主注入后覆盖所有工具,包括只读工具。deny → 把 reason 当
|
|
766
967
|
// tool_result 回填,模型据此改道/如实报告,不抛异常不中断整轮。宿主异常一律 deny。
|
|
767
968
|
const isReadOnly = getToolByName(tc.function.name)?.isReadOnly === true;
|
|
@@ -788,16 +989,29 @@ export async function* runQuery(userInput, options) {
|
|
|
788
989
|
options.onToolStart?.(tc.function.name, tc.id);
|
|
789
990
|
// 浅拷贝填入本次调用的 toolCallId:进度按调用实例路由(并行同名工具不串扰)。
|
|
790
991
|
// advisorCalls/agentCalls 是共享对象引用,计数仍全轮共享,不受拷贝影响。
|
|
791
|
-
let result = await executeToolFn(tc.function.name, tc.function.arguments, signal, {
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
992
|
+
let result = await executeToolFn(tc.function.name, tc.function.arguments, signal, {
|
|
993
|
+
...toolCallContext,
|
|
994
|
+
cwd: sessionState?.activeWorktree?.path ?? toolCallContext.cwd,
|
|
995
|
+
toolCallId: tc.id,
|
|
996
|
+
reportProgress,
|
|
997
|
+
});
|
|
998
|
+
if (tc.function.name === 'ComputerUse' && sessionState) {
|
|
999
|
+
sessionState.computerAutomation = { lastUsedAt: Date.now() };
|
|
1000
|
+
}
|
|
1001
|
+
const downloadableArtifact = result.ok && result.metadata?.kind === 'browser_download'
|
|
1002
|
+
? result.metadata.artifact
|
|
1003
|
+
: undefined;
|
|
1004
|
+
const formalManifest = result.ok && result.metadata?.kind === 'result_manifest'
|
|
1005
|
+
? result.metadata.manifest
|
|
1006
|
+
: downloadableArtifact;
|
|
1007
|
+
if (formalManifest &&
|
|
1008
|
+
(tc.function.name === 'PresentResult' || downloadableArtifact) &&
|
|
1009
|
+
options.commitFormalDelivery) {
|
|
796
1010
|
try {
|
|
797
1011
|
await options.commitFormalDelivery({
|
|
798
1012
|
toolCallId: tc.id,
|
|
799
1013
|
turnId: userMessage.id ?? options.userMessageId ?? 'turn:unknown',
|
|
800
|
-
manifest:
|
|
1014
|
+
manifest: formalManifest,
|
|
801
1015
|
});
|
|
802
1016
|
}
|
|
803
1017
|
catch (error) {
|
|
@@ -809,6 +1023,10 @@ export async function* runQuery(userInput, options) {
|
|
|
809
1023
|
};
|
|
810
1024
|
}
|
|
811
1025
|
}
|
|
1026
|
+
if (tc.function.name === 'Advisor' || tc.function.name === 'AdvisorDecision') {
|
|
1027
|
+
// 串行批次内下一项可能就是 AdvisorDecision/受保护写入,必须立即推进协调器状态。
|
|
1028
|
+
advisorCoordinator.observeToolResult(tc, result);
|
|
1029
|
+
}
|
|
812
1030
|
options.onToolEnd?.(tc.id, false);
|
|
813
1031
|
return { tc, result };
|
|
814
1032
|
}
|
|
@@ -920,36 +1138,79 @@ export async function* runQuery(userInput, options) {
|
|
|
920
1138
|
}
|
|
921
1139
|
}
|
|
922
1140
|
// 本回合工作量累计 → 上调顾问配额(只增不减;已用掉的次数不会因此回退)。
|
|
923
|
-
executedToolCalls += settled.length;
|
|
1141
|
+
executedToolCalls += settled.filter(({ result }) => result.ok || result.disposition !== 'deferred').length;
|
|
924
1142
|
toolCallContext.advisorCalls.max = advisorQuotaFor(executedToolCalls);
|
|
925
1143
|
// 严格按 toolCallsByIndex 顺序把 tool 消息回填 history(settled 已保持该顺序)。
|
|
926
1144
|
let anyAborted = false;
|
|
927
1145
|
for (const { tc, result } of settled) {
|
|
928
|
-
|
|
1146
|
+
if (tc.function.name !== 'Advisor' && tc.function.name !== 'AdvisorDecision') {
|
|
1147
|
+
advisorCoordinator.observeToolResult(tc, result);
|
|
1148
|
+
}
|
|
1149
|
+
if (result.ok || result.disposition !== 'deferred') {
|
|
1150
|
+
evidenceLedger.record(tc.function.name, tc.function.arguments, result);
|
|
1151
|
+
}
|
|
929
1152
|
if (!result.ok && result.error === 'aborted')
|
|
930
1153
|
anyAborted = true;
|
|
931
|
-
|
|
1154
|
+
const browserArtifact = result.ok && result.metadata?.kind === 'browser_download'
|
|
1155
|
+
? result.metadata.artifact
|
|
1156
|
+
: undefined;
|
|
1157
|
+
if (result.ok &&
|
|
1158
|
+
((tc.function.name === 'PresentResult' &&
|
|
1159
|
+
result.metadata?.kind === 'result_manifest') ||
|
|
1160
|
+
browserArtifact)) {
|
|
932
1161
|
resultPresented = true;
|
|
933
|
-
|
|
1162
|
+
// `not_run` only says that no optional, format-specific validator was
|
|
1163
|
+
// invoked. The PresentResult boundary has still checked the file and
|
|
1164
|
+
// persisted its fingerprint, so it must not downgrade a normal delivery.
|
|
1165
|
+
const manifest = result.metadata?.kind === 'result_manifest'
|
|
1166
|
+
? result.metadata.manifest
|
|
1167
|
+
: browserArtifact;
|
|
1168
|
+
presentedResultUnverified ||=
|
|
1169
|
+
manifest?.files.some((file) => file.verification === 'failed') ??
|
|
1170
|
+
false;
|
|
934
1171
|
}
|
|
935
1172
|
const rawContent = result.ok ? result.content : `Error: ${result.error}`;
|
|
936
|
-
|
|
1173
|
+
const basePolicy = result.policy ?? getToolResultPolicy(tc.function.name);
|
|
1174
|
+
// 超大结果先归档再压缩:归档失败时保留原文,绝不因为磁盘问题破坏上下文。
|
|
1175
|
+
const archived = result.ok && sessionState
|
|
1176
|
+
? await archiveToolResult(rawContent, sessionState.microCompact.archiveKey, tc.function.name)
|
|
1177
|
+
: { content: rawContent, originalChars: rawContent.length };
|
|
1178
|
+
// 归档退化必须发声:撞会话上限后每条超长结果都退回原文塞进上下文,
|
|
1179
|
+
// 也就是这个机制本来要解决的问题原样回来了 —— 静默的话没人会发现。
|
|
1180
|
+
if (archived.degraded && !archiveDegradedWarned) {
|
|
1181
|
+
archiveDegradedWarned = true;
|
|
1182
|
+
yield {
|
|
1183
|
+
type: 'warning',
|
|
1184
|
+
message: archived.degraded === 'session-limit'
|
|
1185
|
+
? '工具结果归档已达本会话上限,后续超长结果将直接进入上下文(归档不再生效)。建议 /new 开新会话。'
|
|
1186
|
+
: '工具结果归档写盘失败,后续超长结果将直接进入上下文。请检查磁盘空间与权限。',
|
|
1187
|
+
};
|
|
1188
|
+
}
|
|
1189
|
+
const policy = archived.archiveRef
|
|
1190
|
+
? { ...basePolicy, archiveRef: archived.archiveRef, originalChars: archived.originalChars }
|
|
1191
|
+
: basePolicy;
|
|
1192
|
+
// 微压缩仅对成功结果走;policy 会保护高价值结果,避免不可逆中段丢失。
|
|
937
1193
|
const content = result.ok
|
|
938
|
-
? microCompact(tc.function.name,
|
|
1194
|
+
? microCompact(tc.function.name, archived.content, sessionState?.microCompact, policy)
|
|
939
1195
|
: rawContent;
|
|
940
1196
|
history.push({
|
|
941
1197
|
role: 'tool',
|
|
942
1198
|
content,
|
|
943
1199
|
tool_call_id: tc.id,
|
|
1200
|
+
tool_result_policy: policy,
|
|
944
1201
|
...(result.metadata ? { display_metadata: result.metadata } : {}),
|
|
945
1202
|
timestamp: Date.now(),
|
|
946
1203
|
id: crypto.randomUUID(),
|
|
1204
|
+
...(options.messageProvenance ? { provenance: options.messageProvenance } : {}),
|
|
947
1205
|
});
|
|
948
1206
|
}
|
|
949
1207
|
// If the third identical observation is also the configured final turn, retain the
|
|
950
1208
|
// established max_turns termination contract. The stall guard is valuable only when
|
|
951
1209
|
// it can stop a failing path before the caller's explicit turn budget does.
|
|
952
1210
|
const preemptedRepeatedFailure = settled.some(({ result }) => !result.ok && result.error.includes('第三次调用已在执行前拒绝'));
|
|
1211
|
+
const preemptedSignature = preemptedRepeatedFailure
|
|
1212
|
+
? settled.find(({ result }) => !result.ok && result.error.includes('第三次调用已在执行前拒绝'))?.tc
|
|
1213
|
+
: undefined;
|
|
953
1214
|
const stall = preemptedRepeatedFailure
|
|
954
1215
|
? ({
|
|
955
1216
|
stalled: true,
|
|
@@ -960,12 +1221,13 @@ export async function* runQuery(userInput, options) {
|
|
|
960
1221
|
: { stalled: false };
|
|
961
1222
|
if (stall.stalled) {
|
|
962
1223
|
const message = stall.reason === 'repeated_failure'
|
|
963
|
-
? '
|
|
964
|
-
: '
|
|
1224
|
+
? '检测到同一工具以相同参数连续返回相同失败;请停止原样重试,改做只读诊断、切换路径或咨询 Advisor。'
|
|
1225
|
+
: '检测到连续工具轮次没有产生新文本或新结果;请切换路径或咨询 Advisor。';
|
|
1226
|
+
advisorCoordinator.notifyStalled(stall.signature
|
|
1227
|
+
?? (preemptedSignature
|
|
1228
|
+
? `${preemptedSignature.function.name}\0${preemptedSignature.function.arguments}`
|
|
1229
|
+
: stall.reason));
|
|
965
1230
|
yield { type: 'warning', message };
|
|
966
|
-
yield { type: 'harness_diagnostics', diagnostics: harnessDiagnostics('stalled') };
|
|
967
|
-
yield { type: 'error', error: message, code: 'stalled' };
|
|
968
|
-
return;
|
|
969
1231
|
}
|
|
970
1232
|
// 关键节点截图回填:把工具附带的实况截图作为一条带图 user 消息喂给(视觉)模型自我核验,
|
|
971
1233
|
// 并 yield injected_message 让宿主在对话里即时展示这张实况截图。
|
|
@@ -978,6 +1240,7 @@ export async function* runQuery(userInput, options) {
|
|
|
978
1240
|
// The history copy is intentionally downgraded after the turn. Emit a detached snapshot
|
|
979
1241
|
// so TUI/Desktop stores do not see their already-rendered image mutate underneath them.
|
|
980
1242
|
if (injectedShot) {
|
|
1243
|
+
visionRequiredThisTurn = true;
|
|
981
1244
|
yield { type: 'injected_message', message: structuredClone(injectedShot) };
|
|
982
1245
|
}
|
|
983
1246
|
if (anyAborted || signal?.aborted) {
|
|
@@ -1070,7 +1333,7 @@ function isVisionShotText(text) {
|
|
|
1070
1333
|
* 把失败伪装成成功——见 types.ts 里 ToolResult 那段注释。
|
|
1071
1334
|
*/
|
|
1072
1335
|
async function injectVisionFollowups(history, settled, provider) {
|
|
1073
|
-
if (provider.supportsVision
|
|
1336
|
+
if (provider.supportsVision === false)
|
|
1074
1337
|
return null;
|
|
1075
1338
|
const images = settled.flatMap((s) => s.result.visionImages ?? []);
|
|
1076
1339
|
if (images.length === 0)
|
|
@@ -1196,6 +1459,8 @@ function friendlyToolDescription(toolName, rawArgsJson) {
|
|
|
1196
1459
|
return `Fetching ...`;
|
|
1197
1460
|
}
|
|
1198
1461
|
}
|
|
1462
|
+
case 'BrowserBatch': return `Browser batch (${Array.isArray(args.steps) ? args.steps.length : 0} steps)`;
|
|
1463
|
+
case 'BrowserCDP': return `Browser CDP ${truncate(String(args.method ?? args.operation ?? ''), 45)}`;
|
|
1199
1464
|
case 'WebSearch': return `Searching \`${truncate(args.query, 50)}\``;
|
|
1200
1465
|
case 'Agent': {
|
|
1201
1466
|
const t = typeof args.agent_type === 'string' ? args.agent_type : 'explore';
|