minimal-agent 0.10.0 → 0.12.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 +11 -3
- package/package.json +52 -5
- package/plugins/computer_use/.claude-plugin/plugin.json +6 -0
- package/plugins/computer_use/README.md +94 -0
- package/plugins/computer_use/app/_template.md +142 -0
- package/plugins/computer_use/app/chrome.md +165 -0
- package/plugins/computer_use/app/finder.md +76 -0
- package/plugins/computer_use/app/macos-system-settings.md +81 -0
- package/plugins/computer_use/app/microsoft-excel.md +80 -0
- package/plugins/computer_use/app/microsoft-powerpoint.md +80 -0
- package/plugins/computer_use/app/microsoft-word.md +79 -0
- package/plugins/computer_use/app/wechat.md +413 -0
- package/plugins/computer_use/app/weread.md +319 -0
- package/plugins/computer_use/commands/computeruse.md +25 -0
- package/plugins/computer_use/commands/cu.md +9 -0
- package/plugins/computer_use/plugin.js +26 -0
- package/plugins/computer_use/plugin.ts +30 -0
- package/plugins/computer_use/src/capture.js +90 -0
- package/plugins/computer_use/src/capture.ts +104 -0
- package/plugins/computer_use/src/commands.js +91 -0
- package/plugins/computer_use/src/commands.ts +103 -0
- package/plugins/computer_use/src/daemon.js +134 -0
- package/plugins/computer_use/src/daemon.ts +161 -0
- package/plugins/computer_use/src/manuals.js +135 -0
- package/plugins/computer_use/src/manuals.ts +159 -0
- package/plugins/computer_use/src/verify.js +168 -0
- package/plugins/computer_use/src/verify.ts +219 -0
- package/plugins/computer_use/test/commands.test.ts +73 -0
- package/plugins/cron-runner/plugin.ts +38 -0
- package/plugins/cron-runner/src/commands.ts +373 -0
- package/plugins/cron-runner/src/cronTime.ts +155 -0
- package/plugins/cron-runner/src/lock.ts +79 -0
- package/plugins/cron-runner/src/runs.ts +99 -0
- package/plugins/cron-runner/src/scheduleEngine.ts +76 -0
- package/plugins/cron-runner/src/scheduler.ts +376 -0
- package/plugins/cron-runner/src/schema.ts +139 -0
- package/plugins/cron-runner/src/service.ts +193 -0
- package/plugins/cron-runner/src/store.ts +333 -0
- package/plugins/cron-runner/src/types.ts +141 -0
- package/plugins/cron-runner/test/cronTime.test.ts +41 -0
- package/plugins/cron-runner/test/plugin-integration.test.ts +47 -0
- package/plugins/cron-runner/test/scheduleEngine.test.ts +41 -0
- package/plugins/cron-runner/test/service.test.ts +76 -0
- package/plugins/cron-runner/test/store-and-scheduler.test.ts +501 -0
- package/plugins/workflow-runner/plugin.js +56 -2
- package/plugins/workflow-runner/plugin.ts +113 -0
- package/plugins/workflow-runner/src/agentCommon.js +43 -4
- package/plugins/workflow-runner/src/agentCommon.ts +435 -0
- package/plugins/workflow-runner/src/controlFlow/branch.js +40 -0
- package/plugins/workflow-runner/src/controlFlow/branch.ts +46 -0
- package/plugins/workflow-runner/src/controlFlow/loop.js +211 -0
- package/plugins/workflow-runner/src/controlFlow/loop.ts +248 -0
- package/plugins/workflow-runner/src/controlFlow/pause.js +129 -0
- package/plugins/workflow-runner/src/controlFlow/pause.ts +141 -0
- package/plugins/workflow-runner/src/controlFlow/signal.js +21 -0
- package/plugins/workflow-runner/src/controlFlow/signal.ts +29 -0
- package/plugins/workflow-runner/src/controlFlow/types.js +18 -0
- package/plugins/workflow-runner/src/controlFlow/types.ts +63 -0
- package/plugins/workflow-runner/src/dslManifest.ts +127 -0
- package/plugins/workflow-runner/src/expressions.ts +622 -0
- package/plugins/workflow-runner/src/index.js +13 -4
- package/plugins/workflow-runner/src/index.ts +338 -0
- package/plugins/workflow-runner/src/jsonPayload.ts +84 -0
- package/plugins/workflow-runner/src/loader.js +23 -0
- package/plugins/workflow-runner/src/loader.ts +721 -0
- package/plugins/workflow-runner/src/multimodal.js +2 -1
- package/plugins/workflow-runner/src/multimodal.ts +64 -0
- package/plugins/workflow-runner/src/recovery.ts +149 -0
- package/plugins/workflow-runner/src/runner.js +75 -659
- package/plugins/workflow-runner/src/runner.ts +604 -0
- package/plugins/workflow-runner/src/stepExecutors/agent.ts +234 -0
- package/plugins/workflow-runner/src/stepExecutors/assert.ts +50 -0
- package/plugins/workflow-runner/src/stepExecutors/echo.js +31 -0
- package/plugins/workflow-runner/src/stepExecutors/echo.ts +38 -0
- package/plugins/workflow-runner/src/stepExecutors/llm.ts +133 -0
- package/plugins/workflow-runner/src/stepExecutors/set.js +49 -0
- package/plugins/workflow-runner/src/stepExecutors/set.ts +56 -0
- package/plugins/workflow-runner/src/stepExecutors/skill.js +5 -6
- package/plugins/workflow-runner/src/stepExecutors/skill.ts +140 -0
- package/plugins/workflow-runner/src/stepExecutors/tool.js +80 -8
- package/plugins/workflow-runner/src/stepExecutors/tool.ts +256 -0
- package/plugins/workflow-runner/src/stepExecutors/verify.ts +68 -0
- package/plugins/workflow-runner/src/stepPipeline.js +277 -0
- package/plugins/workflow-runner/src/stepPipeline.ts +366 -0
- package/plugins/workflow-runner/src/taskIdentity.ts +405 -0
- package/plugins/workflow-runner/src/toolRuntime.js +48 -0
- package/plugins/workflow-runner/src/toolRuntime.ts +64 -0
- package/plugins/workflow-runner/src/types.js +37 -1
- package/plugins/workflow-runner/src/types.ts +602 -0
- package/plugins/workflow-runner/src/workflowState.ts +733 -0
- package/plugins/workflow-runner/test/agent.test.ts +587 -0
- package/plugins/workflow-runner/test/agentLlmError.test.ts +373 -0
- package/plugins/workflow-runner/test/bashBlacklist.sweep.test.ts +65 -0
- package/plugins/workflow-runner/test/captureMisbind.test.ts +99 -0
- package/plugins/workflow-runner/test/cli.e2e.test.ts +147 -0
- package/plugins/workflow-runner/test/context-files.test.ts +320 -0
- package/plugins/workflow-runner/test/e2e.test.ts +370 -0
- package/plugins/workflow-runner/test/engineRobustness.test.ts +226 -0
- package/plugins/workflow-runner/test/expressions.test.ts +397 -0
- package/plugins/workflow-runner/test/foundation.test.ts +126 -0
- package/plugins/workflow-runner/test/graceful.test.ts +139 -0
- package/plugins/workflow-runner/test/greedyInputs.load.test.ts +47 -0
- package/plugins/workflow-runner/test/hostCapability.test.ts +108 -0
- package/plugins/workflow-runner/test/illustrate.load.test.ts +183 -0
- package/plugins/workflow-runner/test/jsonPayload.test.ts +83 -0
- package/plugins/workflow-runner/test/loader.test.ts +617 -0
- package/plugins/workflow-runner/test/maxTurnsRetryDelay.test.ts +33 -0
- package/plugins/workflow-runner/test/multimodal.test.ts +342 -0
- package/plugins/workflow-runner/test/output-schema.test.ts +213 -0
- package/plugins/workflow-runner/test/parseWorkflowArgs.test.ts +87 -0
- package/plugins/workflow-runner/test/pluginRunner.isolation.test.ts +269 -0
- package/plugins/workflow-runner/test/ppt.load.test.ts +1220 -0
- package/plugins/workflow-runner/test/ralphLoop.e2e.test.ts +220 -0
- package/plugins/workflow-runner/test/recovery.test.ts +265 -0
- package/plugins/workflow-runner/test/runner.test.ts +911 -0
- package/plugins/workflow-runner/test/runnerPreflight.test.ts +379 -0
- package/plugins/workflow-runner/test/skillTurnDone.test.ts +71 -0
- package/plugins/workflow-runner/test/stateLifecycle.test.ts +150 -0
- package/plugins/workflow-runner/test/stepPipeline.test.ts +114 -0
- package/plugins/workflow-runner/test/taskArbitration.test.ts +338 -0
- package/plugins/workflow-runner/test/taskIdentity.test.ts +237 -0
- package/plugins/workflow-runner/test/taskLifecycle.test.ts +270 -0
- package/plugins/workflow-runner/test/toolArtifactOverwrite.test.ts +221 -0
- package/plugins/workflow-runner/test/toolCapabilityBoundary.test.ts +29 -0
- package/plugins/workflow-runner/test/toolRuntimeIntegration.test.ts +168 -0
- package/plugins/workflow-runner/test/webppt.load.test.ts +250 -0
- package/plugins/workflow-runner/test/word.load.test.ts +208 -0
- package/plugins/workflow-runner/test/workflows.smoke.test.ts +76 -0
- package/plugins/workflow-runner/test/yamlGuardConditions.test.ts +207 -0
- package/skills/browser-automation/SKILL.md +9 -19
- package/skills/web-crawler/SKILL.md +9 -11
- package/src/askBridge.ts +249 -0
- package/src/bootstrap/cwdArg.js +10 -3
- package/src/bootstrap/cwdArg.ts +31 -0
- package/src/bootstrap/workingDir.ts +48 -0
- package/src/cli/args.js +70 -7
- package/src/cli/args.ts +146 -0
- package/src/cli/computerUse.js +185 -0
- package/src/cli/computerUse.ts +221 -0
- package/src/cli/configMenu.js +274 -202
- package/src/cli/configMenu.tsx +521 -0
- package/src/cli/memoryAccess.js +45 -0
- package/src/cli/memoryAccess.ts +53 -0
- package/src/cli/print.js +119 -5
- package/src/cli/print.ts +597 -0
- package/src/cli/quickModelSetup.js +60 -0
- package/src/cli/quickModelSetup.tsx +96 -0
- package/src/cli/streamJson.js +80 -4
- package/src/cli/streamJson.ts +433 -0
- package/src/cli/wizards/advisorWizard.js +1 -0
- package/src/cli/wizards/advisorWizard.tsx +195 -0
- package/src/cli/wizards/agentRuntimeWizard.js +84 -0
- package/src/cli/wizards/agentRuntimeWizard.tsx +134 -0
- package/src/cli/wizards/browserWizard.js +55 -0
- package/src/cli/wizards/browserWizard.tsx +77 -0
- package/src/cli/wizards/contextAdvisorWizard.js +84 -0
- package/src/cli/wizards/contextAdvisorWizard.tsx +124 -0
- package/src/cli/wizards/fieldForm.js +7 -4
- package/src/cli/wizards/fieldForm.tsx +250 -0
- package/src/cli/wizards/imageWizard.js +4 -5
- package/src/cli/wizards/imageWizard.tsx +96 -0
- package/src/cli/wizards/openRouterWizard.tsx +48 -0
- package/src/cli/wizards/providerPresets.js +1 -8
- package/src/cli/wizards/providerPresets.ts +106 -0
- package/src/cli/wizards/providerWizard.js +152 -205
- package/src/cli/wizards/providerWizard.tsx +266 -0
- package/src/cli/wizards/searchWizard.tsx +47 -0
- package/src/cli/wizards/ttsWizard.js +4 -3
- package/src/cli/wizards/ttsWizard.tsx +61 -0
- package/src/config/configDefaults.js +25 -0
- package/src/config/configDefaults.ts +33 -0
- package/src/config/configDiagnostics.js +117 -0
- package/src/config/configDiagnostics.ts +166 -0
- package/src/config/configFile.js +52 -32
- package/src/config/configFile.ts +633 -0
- package/src/config/configManagement.js +126 -0
- package/src/config/configManagement.ts +159 -0
- package/src/config/configService.js +66 -11
- package/src/config/configService.ts +554 -0
- package/src/config/numericLimits.js +61 -0
- package/src/config/numericLimits.ts +80 -0
- package/src/config.ts +109 -0
- package/src/context/agentHome.ts +11 -0
- package/src/context/archive.ts +144 -0
- package/src/context/budgetPlan.js +54 -0
- package/src/context/budgetPlan.ts +88 -0
- package/src/context/compact.js +179 -106
- package/src/context/compact.ts +584 -0
- package/src/context/contextBundle.js +119 -0
- package/src/context/contextBundle.ts +176 -0
- package/src/context/contextLock.ts +84 -0
- package/src/context/microCompactLite.ts +218 -0
- package/src/context/persistContext.ts +177 -0
- package/src/context/reactiveCompact.js +19 -4
- package/src/context/reactiveCompact.ts +240 -0
- package/src/context/recentDirs.ts +42 -0
- package/src/context/sessionPath.ts +206 -0
- package/src/context/sessionRegistry.js +8 -2
- package/src/context/sessionRegistry.ts +414 -0
- package/src/context/snipCompact.ts +163 -0
- package/src/context/tokenCounter.ts +106 -0
- package/src/context/workspaceIndex.js +2 -2
- package/src/context/workspaceIndex.ts +751 -0
- package/src/entry.compiled.tsx +20 -0
- package/src/harness/claimValidation.js +9 -0
- package/src/harness/claimValidation.ts +22 -0
- package/src/harness/evidenceLedger.js +92 -0
- package/src/harness/evidenceLedger.ts +96 -0
- package/src/harness/stallGuard.js +60 -0
- package/src/harness/stallGuard.ts +81 -0
- package/src/humanIntervention.js +129 -0
- package/src/humanIntervention.ts +216 -0
- package/src/infra/verification.ts +295 -0
- package/src/irreversible/rules.js +216 -0
- package/src/irreversible/rules.ts +261 -0
- package/src/llm/client.js +40 -3
- package/src/llm/client.ts +621 -0
- package/src/llm/promptCache.js +27 -0
- package/src/llm/promptCache.ts +41 -0
- package/src/llm/providers/anthropicFormat.js +41 -5
- package/src/llm/providers/anthropicFormat.ts +290 -0
- package/src/llm/providers/openaiFormat.js +186 -8
- package/src/llm/providers/openaiFormat.ts +428 -0
- package/src/loop.js +614 -63
- package/src/loop.ts +1454 -0
- package/src/main.js +67 -10
- package/src/main.tsx +344 -0
- package/src/memory/codec.js +204 -0
- package/src/memory/codec.ts +249 -0
- package/src/memory/constants.js +34 -0
- package/src/memory/constants.ts +50 -0
- package/src/memory/episodicMemory.js +684 -0
- package/src/memory/episodicMemory.ts +815 -0
- package/src/memory/fileStore.js +148 -0
- package/src/memory/fileStore.ts +184 -0
- package/src/memory/memoryService.js +570 -0
- package/src/memory/memoryService.ts +859 -0
- package/src/memory/migration.js +128 -0
- package/src/memory/migration.ts +160 -0
- package/src/memory/repository.js +789 -0
- package/src/memory/repository.ts +893 -0
- package/src/memory/retrieval.js +108 -0
- package/src/memory/retrieval.ts +145 -0
- package/src/memory/searchIndex.js +107 -0
- package/src/memory/searchIndex.ts +153 -0
- package/src/memory/topicView.js +82 -0
- package/src/memory/topicView.ts +95 -0
- package/src/memory/turnJournal.js +95 -0
- package/src/memory/turnJournal.ts +120 -0
- package/src/memory/types.js +2 -0
- package/src/memory/types.ts +186 -0
- package/src/messageQueue.ts +123 -0
- package/src/multimodal.js +128 -46
- package/src/multimodal.ts +349 -0
- package/src/plugin-sdk.js +21 -1
- package/src/plugin-sdk.ts +134 -0
- package/src/plugins/builtinPluginIds.js +47 -0
- package/src/plugins/builtinPluginIds.ts +51 -0
- package/src/plugins/builtinRegistry.ts +43 -0
- package/src/plugins/commandRouter.js +40 -26
- package/src/plugins/commandRouter.ts +261 -0
- package/src/plugins/hookEngine.ts +341 -0
- package/src/plugins/pluginApi.ts +133 -0
- package/src/plugins/pluginLifecycle.ts +34 -0
- package/src/plugins/pluginLoader.ts +147 -0
- package/src/plugins/pluginManifest.js +127 -0
- package/src/plugins/pluginManifest.ts +152 -0
- package/src/plugins/pluginRunner.js +47 -2
- package/src/plugins/pluginRunner.ts +207 -0
- package/src/plugins/transcript.ts +208 -0
- package/src/prompts/lint.js +169 -0
- package/src/prompts/lint.ts +220 -0
- package/src/prompts/pluginList.js +32 -16
- package/src/prompts/pluginList.ts +86 -0
- package/src/prompts/policyRegistry.js +318 -0
- package/src/prompts/policyRegistry.ts +358 -0
- package/src/prompts/projectInstructions.js +24 -0
- package/src/prompts/projectInstructions.ts +81 -0
- package/src/prompts/promptAssembly.js +311 -202
- package/src/prompts/promptAssembly.ts +510 -0
- package/src/prompts/skillList.js +106 -45
- package/src/prompts/skillList.ts +198 -0
- package/src/prompts/system.js +31 -6
- package/src/prompts/system.ts +81 -0
- package/src/runtime/agentRuntimeContext.js +53 -0
- package/src/runtime/agentRuntimeContext.ts +112 -0
- package/src/session/runAgentTurn.js +71 -12
- package/src/session/runAgentTurn.ts +181 -0
- package/src/session/runTurn.js +29 -0
- package/src/session/runTurn.ts +160 -0
- package/src/session/runtimeStore.js +149 -0
- package/src/session/runtimeStore.ts +197 -0
- package/src/session/sessionState.js +79 -3
- package/src/session/sessionState.ts +134 -0
- package/src/toolGate.ts +50 -0
- package/src/tools/advisor/advisor.js +76 -15
- package/src/tools/advisor/advisor.ts +512 -0
- package/src/tools/agent/agent.js +245 -29
- package/src/tools/agent/agent.ts +837 -0
- package/src/tools/agent/agentTaskService.js +60 -0
- package/src/tools/agent/agentTaskService.ts +113 -0
- package/src/tools/agent/agentTypes.js +20 -8
- package/src/tools/agent/agentTypes.ts +94 -0
- package/src/tools/agent/coordinator.ts +111 -0
- package/src/tools/agent/worktree.ts +296 -0
- package/src/tools/ask/ask.js +17 -0
- package/src/tools/ask/ask.ts +122 -0
- package/src/tools/bash/attachImage.js +223 -0
- package/src/tools/bash/attachImage.ts +281 -0
- package/src/tools/bash/bash.js +132 -14
- package/src/tools/bash/bash.ts +642 -0
- package/src/tools/bash/semantics.ts +103 -0
- package/src/tools/bash/warnings.js +17 -11
- package/src/tools/bash/warnings.ts +117 -0
- package/src/tools/capabilities.js +42 -0
- package/src/tools/capabilities.ts +69 -0
- package/src/tools/computeruse/accessibilityPermission.js +261 -0
- package/src/tools/computeruse/accessibilityPermission.ts +294 -0
- package/src/tools/computeruse/axSearch.js +96 -0
- package/src/tools/computeruse/axSearch.ts +120 -0
- package/src/tools/computeruse/axSnap.js +272 -0
- package/src/tools/computeruse/axSnap.ts +349 -0
- package/src/tools/computeruse/capabilityLabels.js +95 -0
- package/src/tools/computeruse/capabilityLabels.ts +115 -0
- package/src/tools/computeruse/computerUseService.js +2235 -0
- package/src/tools/computeruse/computerUseService.ts +2686 -0
- package/src/tools/computeruse/computeruse.js +2742 -0
- package/src/tools/computeruse/computeruse.ts +3265 -0
- package/src/tools/computeruse/diagnosticTrace.js +154 -0
- package/src/tools/computeruse/diagnosticTrace.ts +195 -0
- package/src/tools/computeruse/diagnostics.js +369 -0
- package/src/tools/computeruse/diagnostics.ts +453 -0
- package/src/tools/computeruse/grants.js +53 -0
- package/src/tools/computeruse/grants.ts +70 -0
- package/src/tools/computeruse/humanBoundary.js +159 -0
- package/src/tools/computeruse/humanBoundary.ts +199 -0
- package/src/tools/computeruse/intervention.js +77 -0
- package/src/tools/computeruse/intervention.ts +110 -0
- package/src/tools/computeruse/manualSchema.js +709 -0
- package/src/tools/computeruse/manualSchema.ts +916 -0
- package/src/tools/computeruse/observationImages.js +705 -0
- package/src/tools/computeruse/observationImages.ts +889 -0
- package/src/tools/computeruse/protocol.js +60 -0
- package/src/tools/computeruse/protocol.ts +515 -0
- package/src/tools/computeruse/telemetryLog.js +57 -0
- package/src/tools/computeruse/telemetryLog.ts +67 -0
- package/src/tools/computeruse/vision/appGuide.js +325 -0
- package/src/tools/computeruse/vision/appGuide.ts +482 -0
- package/src/tools/computeruse/vision/focus.js +291 -0
- package/src/tools/computeruse/vision/focus.ts +382 -0
- package/src/tools/computeruse/vision/frames.js +248 -0
- package/src/tools/computeruse/vision/frames.ts +329 -0
- package/src/tools/computeruse/vision/geometry.js +131 -0
- package/src/tools/computeruse/vision/geometry.ts +163 -0
- package/src/tools/computeruse/vision/imaging.js +201 -0
- package/src/tools/computeruse/vision/imaging.ts +281 -0
- package/src/tools/computeruse/vision/png.js +274 -0
- package/src/tools/computeruse/vision/png.ts +317 -0
- package/src/tools/computeruse/vision/raster.js +320 -0
- package/src/tools/computeruse/vision/raster.ts +408 -0
- package/src/tools/computeruse/vision/settle.js +131 -0
- package/src/tools/computeruse/vision/settle.ts +220 -0
- package/src/tools/computeruse/vision/telemetry.js +196 -0
- package/src/tools/computeruse/vision/telemetry.ts +241 -0
- package/src/tools/computeruse/visionOcr.js +115 -0
- package/src/tools/computeruse/visionOcr.ts +128 -0
- package/src/tools/computeruse/waitFor.js +237 -0
- package/src/tools/computeruse/waitFor.ts +291 -0
- package/src/tools/download/download.js +10 -6
- package/src/tools/download/download.ts +187 -0
- package/src/tools/edit/edit.js +30 -18
- package/src/tools/edit/edit.ts +349 -0
- package/src/tools/edit/multi-edit.js +25 -14
- package/src/tools/edit/multi-edit.ts +223 -0
- package/src/tools/effectiveToolSet.js +73 -0
- package/src/tools/effectiveToolSet.ts +91 -0
- package/src/tools/genimage/genimage.ts +257 -0
- package/src/tools/genspeech/genspeech.ts +83 -0
- package/src/tools/glob/glob.js +176 -29
- package/src/tools/glob/glob.ts +283 -0
- package/src/tools/grep/grep.js +61 -19
- package/src/tools/grep/grep.ts +239 -0
- package/src/tools/grep/rgPath.ts +206 -0
- package/src/tools/index.js +99 -5
- package/src/tools/index.ts +293 -0
- package/src/tools/lsp/config.js +113 -0
- package/src/tools/lsp/config.ts +113 -0
- package/src/tools/lsp/lsp.js +256 -0
- package/src/tools/lsp/lsp.ts +274 -0
- package/src/tools/lsp/service.js +303 -0
- package/src/tools/lsp/service.ts +296 -0
- package/src/tools/memory/memory.js +520 -0
- package/src/tools/memory/memory.ts +625 -0
- package/src/tools/memory/policy.js +53 -0
- package/src/tools/memory/policy.ts +72 -0
- package/src/tools/notebook/notebookEdit.js +114 -0
- package/src/tools/notebook/notebookEdit.ts +126 -0
- package/src/tools/powershell/powershell.js +210 -0
- package/src/tools/powershell/powershell.ts +239 -0
- package/src/tools/presentresult/presentresult.js +145 -0
- package/src/tools/presentresult/presentresult.ts +156 -0
- package/src/tools/read/read.js +371 -163
- package/src/tools/read/read.ts +558 -0
- package/src/tools/shared/diffSummary.js +44 -0
- package/src/tools/shared/diffSummary.ts +63 -0
- package/src/tools/shared/fileActivity.js +51 -0
- package/src/tools/shared/fileActivity.ts +66 -0
- package/src/tools/shared/fileMutationBus.js +13 -0
- package/src/tools/shared/fileMutationBus.ts +14 -0
- package/src/tools/shared/fileState.js +73 -65
- package/src/tools/shared/fileState.ts +145 -0
- package/src/tools/shared/fileUtils.js +81 -4
- package/src/tools/shared/fileUtils.ts +504 -0
- package/src/tools/shared/killTree.ts +50 -0
- package/src/tools/shared/permanentDeleteGuard.js +26 -0
- package/src/tools/shared/permanentDeleteGuard.ts +34 -0
- package/src/tools/shared/schemas.ts +20 -0
- package/src/tools/shared/toolOutputStore.js +97 -0
- package/src/tools/shared/toolOutputStore.ts +110 -0
- package/src/tools/skill/skill.js +86 -0
- package/src/tools/skill/skill.ts +94 -0
- package/src/tools/snip/snip.js +67 -0
- package/src/tools/snip/snip.ts +80 -0
- package/src/tools/tasks/backgroundTaskService.js +275 -0
- package/src/tools/tasks/backgroundTaskService.ts +369 -0
- package/src/tools/tasks/processEvidence.js +24 -0
- package/src/tools/tasks/processEvidence.ts +34 -0
- package/src/tools/tasks/taskManagement.js +163 -0
- package/src/tools/tasks/taskManagement.ts +180 -0
- package/src/tools/tasks/taskOutput.js +113 -0
- package/src/tools/tasks/taskOutput.ts +145 -0
- package/src/tools/tasks/taskStop.js +74 -0
- package/src/tools/tasks/taskStop.ts +99 -0
- package/src/tools/tasks/taskStore.js +46 -0
- package/src/tools/tasks/taskStore.ts +77 -0
- package/src/tools/terminal/terminalCapture.js +167 -0
- package/src/tools/terminal/terminalCapture.ts +179 -0
- package/src/tools/trash/trash.js +233 -0
- package/src/tools/trash/trash.ts +283 -0
- package/src/tools/types.js +1 -1
- package/src/tools/types.ts +24 -0
- package/src/tools/webbrowser/agentBrowserPath.ts +286 -0
- package/src/tools/webbrowser/browser.js +214 -11
- package/src/tools/webbrowser/browser.ts +293 -0
- package/src/tools/webbrowser/browserControl.js +153 -48
- package/src/tools/webbrowser/browserControl.ts +262 -0
- package/src/tools/webbrowser/browserDaemon.js +147 -39
- package/src/tools/webbrowser/browserDaemon.ts +270 -0
- package/src/tools/webbrowser/browserOutcome.js +273 -0
- package/src/tools/webbrowser/browserOutcome.ts +306 -0
- package/src/tools/webbrowser/browserRunRegistry.js +150 -0
- package/src/tools/webbrowser/browserRunRegistry.ts +179 -0
- package/src/tools/webbrowser/browserService.js +20 -6
- package/src/tools/webbrowser/browserService.ts +56 -0
- package/src/tools/webbrowser/browserStream.js +30 -1
- package/src/tools/webbrowser/browserStream.ts +216 -0
- package/src/tools/webbrowser/browserTabs.js +143 -0
- package/src/tools/webbrowser/browserTabs.ts +210 -0
- package/src/tools/webbrowser/webbrowser.js +831 -150
- package/src/tools/webbrowser/webbrowser.ts +1537 -0
- package/src/tools/webfetch/preapproved.ts +274 -0
- package/src/tools/webfetch/webfetch.js +24 -23
- package/src/tools/webfetch/webfetch.ts +383 -0
- package/src/tools/websearch/websearch.js +12 -11
- package/src/tools/websearch/websearch.ts +292 -0
- package/src/tools/worktree/sessionWorktree.js +128 -0
- package/src/tools/worktree/sessionWorktree.ts +158 -0
- package/src/tools/write/write.js +27 -44
- package/src/tools/write/write.ts +136 -0
- package/src/types.ts +960 -0
- package/src/ui/App.js +216 -12
- package/src/ui/App.tsx +879 -0
- package/src/ui/AskQuestion.tsx +300 -0
- package/src/ui/InputBox.js +77 -1
- package/src/ui/InputBox.tsx +567 -0
- package/src/ui/LiveArea.tsx +344 -0
- package/src/ui/MessageList.js +27 -6
- package/src/ui/MessageList.tsx +559 -0
- package/src/ui/ModelPicker.tsx +107 -0
- package/src/ui/QueuedMessagesBanner.tsx +51 -0
- package/src/ui/Root.js +6 -8
- package/src/ui/Root.tsx +100 -0
- package/src/ui/SessionPicker.tsx +169 -0
- package/src/ui/StatusLine.js +22 -1
- package/src/ui/StatusLine.tsx +145 -0
- package/src/ui/WorkspacePicker.tsx +84 -0
- package/src/ui/advisorCommand.js +54 -0
- package/src/ui/advisorCommand.ts +63 -0
- package/src/ui/computerCommand.js +19 -0
- package/src/ui/computerCommand.ts +35 -0
- package/src/ui/figures.ts +45 -0
- package/src/ui/hooks/askSlot.ts +112 -0
- package/src/ui/hooks/useChat.js +271 -7
- package/src/ui/hooks/useChat.ts +1578 -0
- package/src/ui/hooks/useInputHistory.ts +235 -0
- package/src/ui/hooks/usePasteHandler.ts +220 -0
- package/src/ui/hooks/useTerminalSize.ts +41 -0
- package/src/ui/hooks/useTextBuffer.ts +108 -0
- package/src/ui/hooks/useTokenUsage.ts +44 -0
- package/src/ui/liveViewport.ts +189 -0
- package/src/ui/markdown.ts +230 -0
- package/src/ui/textBuffer.ts +239 -0
- package/src/ui/theme.ts +35 -0
- package/src/utils/clipboardImage.js +358 -0
- package/src/utils/clipboardImage.ts +420 -0
- package/src/utils/debugLog.ts +50 -0
- package/src/utils/greenRoot.ts +35 -0
- package/src/utils/packageRoot.ts +40 -0
- package/src/utils/pathContains.js +28 -0
- package/src/utils/pathContains.ts +31 -0
- package/src/utils/resourcePaths.ts +71 -0
- package/src/utils/stripThink.ts +49 -0
- package/src/utils/zodToJson.ts +73 -0
- package/src/web/browserBridge.js +365 -66
- package/src/web/browserBridge.ts +672 -0
- package/src/web/protocol.ts +1804 -0
- package/vendor/computer-use/arm64-darwin/cu-helperd +0 -0
- package/workflows/ppt.yaml +13 -2
- package/workflows/wechat-reading-shots.yaml +315 -0
- package/workflows/word.yaml +11 -1
- package/skills/docx/scripts/office/helpers/__pycache__/__init__.cpython-314.pyc +0 -0
- package/skills/docx/scripts/office/helpers/__pycache__/merge_runs.cpython-314.pyc +0 -0
- package/skills/docx/scripts/office/helpers/__pycache__/simplify_redlines.cpython-314.pyc +0 -0
- package/skills/docx/scripts/office/validators/__pycache__/__init__.cpython-314.pyc +0 -0
- package/skills/docx/scripts/office/validators/__pycache__/base.cpython-314.pyc +0 -0
- package/skills/docx/scripts/office/validators/__pycache__/docx.cpython-314.pyc +0 -0
- package/skills/docx/scripts/office/validators/__pycache__/pptx.cpython-314.pyc +0 -0
- package/skills/docx/scripts/office/validators/__pycache__/redlining.cpython-314.pyc +0 -0
- package/skills/image-stats/scripts/__pycache__/image_stats.cpython-314.pyc +0 -0
- package/skills/image-stats/scripts/__pycache__/image_stats_test.cpython-314.pyc +0 -0
- package/skills/ui-design/scripts/__pycache__/assemble_material.cpython-314.pyc +0 -0
- package/skills/ui-design/scripts/__pycache__/assemble_material_test.cpython-314.pyc +0 -0
- package/skills/ui-design/scripts/__pycache__/chart_sanity_check.cpython-314.pyc +0 -0
- package/skills/ui-design/scripts/__pycache__/check_language_policy.cpython-314.pyc +0 -0
- package/skills/ui-design/scripts/__pycache__/check_ui_artifact.cpython-314.pyc +0 -0
- package/skills/ui-design/scripts/__pycache__/check_ui_artifact_pptx_test.cpython-314.pyc +0 -0
- package/skills/ui-design/scripts/__pycache__/deck_pages_check.cpython-314.pyc +0 -0
- package/skills/ui-design/scripts/__pycache__/deck_pages_check_test.cpython-314.pyc +0 -0
- package/skills/ui-design/scripts/__pycache__/deck_visual_style_check.cpython-314.pyc +0 -0
- package/skills/ui-design/scripts/__pycache__/deck_visual_style_check_test.cpython-314.pyc +0 -0
- package/skills/ui-design/scripts/__pycache__/design_budget_check.cpython-314.pyc +0 -0
- package/skills/ui-design/scripts/__pycache__/design_budget_check_test.cpython-314.pyc +0 -0
- package/skills/ui-design/scripts/__pycache__/design_repair_hints.cpython-314.pyc +0 -0
- package/skills/ui-design/scripts/__pycache__/design_repair_hints_test.cpython-314.pyc +0 -0
- package/skills/ui-design/scripts/__pycache__/design_style_check.cpython-314.pyc +0 -0
- package/skills/ui-design/scripts/__pycache__/design_style_check_test.cpython-314.pyc +0 -0
- package/skills/ui-design/scripts/__pycache__/draft_coverage_check.cpython-314.pyc +0 -0
- package/skills/ui-design/scripts/__pycache__/extract_source_assets.cpython-314.pyc +0 -0
- package/skills/ui-design/scripts/__pycache__/extract_source_assets_test.cpython-314.pyc +0 -0
- package/skills/ui-design/scripts/__pycache__/generated_image_review_collect.cpython-314.pyc +0 -0
- package/skills/ui-design/scripts/__pycache__/generated_image_review_collect_test.cpython-314.pyc +0 -0
- package/skills/ui-design/scripts/__pycache__/image_plan_lint.cpython-314.pyc +0 -0
- package/skills/ui-design/scripts/__pycache__/image_plan_lint_test.cpython-314.pyc +0 -0
- package/skills/ui-design/scripts/__pycache__/image_prompt_rewrite.cpython-314.pyc +0 -0
- package/skills/ui-design/scripts/__pycache__/image_prompt_rewrite_test.cpython-314.pyc +0 -0
- package/skills/ui-design/scripts/__pycache__/layout_balance.cpython-314.pyc +0 -0
- package/skills/ui-design/scripts/__pycache__/layout_balance_test.cpython-314.pyc +0 -0
- package/skills/ui-design/scripts/__pycache__/normalize_design_json.cpython-314.pyc +0 -0
- package/skills/ui-design/scripts/__pycache__/normalize_design_json_test.cpython-314.pyc +0 -0
- package/skills/ui-design/scripts/__pycache__/plan_source_layout.cpython-314.pyc +0 -0
- package/skills/ui-design/scripts/__pycache__/plan_source_layout_test.cpython-314.pyc +0 -0
- package/skills/ui-design/scripts/__pycache__/resolve_source_hint.cpython-314.pyc +0 -0
- package/skills/ui-design/scripts/__pycache__/resolve_source_hint_test.cpython-314.pyc +0 -0
- package/skills/ui-design/scripts/__pycache__/resolve_source_input.cpython-314.pyc +0 -0
- package/skills/ui-design/scripts/__pycache__/resolve_source_input_test.cpython-314.pyc +0 -0
- package/skills/ui-design/scripts/__pycache__/scaffold_ui_artifact.cpython-314.pyc +0 -0
- package/skills/ui-design/scripts/__pycache__/source_usage_check.cpython-314.pyc +0 -0
- package/skills/ui-design/scripts/__pycache__/source_usage_check_test.cpython-314.pyc +0 -0
- package/skills/ui-design/scripts/__pycache__/text_readability_pixel.cpython-314.pyc +0 -0
- package/skills/ui-design/scripts/__pycache__/visual_quality_gate.cpython-314.pyc +0 -0
- package/skills/ui-design/scripts/__pycache__/visual_quality_gate_test.cpython-314.pyc +0 -0
- /package/skills/ai-image/scripts/{generate_test.mjs → generate.selftest.mjs} +0 -0
- /package/skills/ai-image/scripts/{generate_from_plan_test.mjs → generate_from_plan.selftest.mjs} +0 -0
|
@@ -0,0 +1,512 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ============================================================
|
|
3
|
+
* src/tools/advisor/advisor.ts —— Advisor 工具(顾问模型第二意见)
|
|
4
|
+
* ------------------------------------------------------------
|
|
5
|
+
* 独立配置的第二个 LLM,供主循环的模型在关键判断点(方案/诊断成型后动手写改之前、
|
|
6
|
+
* 受阻或要改方向时、收尾汇报之前)主动调用,拿到一个不同模型的第二意见。
|
|
7
|
+
* 时机规则的真身在 `prompts/promptAssembly.ts` 的 Advisor 工具规则 + 本文件的
|
|
8
|
+
* `description`;配额曲线在 `loop.ts::advisorQuotaFor`。
|
|
9
|
+
*
|
|
10
|
+
* 设计要点(吸收自 Claude Code 官方 /advisor 的架构,但按 minimal-agent
|
|
11
|
+
* 的实际约束做了取舍——不照搬服务端单请求内编排/模型分级/加密返回等
|
|
12
|
+
* 与我们架构无关的部分,见设计计划):
|
|
13
|
+
* - 输入 schema 几乎为空(只有可选的 note):模型调用前自己写的方案/
|
|
14
|
+
* 诊断文字,早就随 assistant 消息 push 进了 history,顾问能从
|
|
15
|
+
* ctx.history 里直接看到,不需要模型重复手填一段结构化 question。
|
|
16
|
+
* - 顾问自己独立的 persona system prompt,与主 provider 的五层提示词
|
|
17
|
+
* 完全无关。
|
|
18
|
+
* - 顾问不给工具权限,只做单轮只读推理(tools: [])——避免变成一个
|
|
19
|
+
* 带自己循环的子 agent,那是远比"第二意见"更大的功能。
|
|
20
|
+
* - 未配置 / 被 /advisor off 关闭 / 调用报错,全部走 ToolResult 的
|
|
21
|
+
* ok:false 路径——这条路径本来就有成熟的"错误喂回 LLM 自行判断"
|
|
22
|
+
* 机制,loop.ts 不需要为此新增任何代码。
|
|
23
|
+
* ============================================================
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
import { z } from 'zod';
|
|
27
|
+
|
|
28
|
+
import { configService } from '../../config/configService.js';
|
|
29
|
+
import { CONFIG_LIMITS, normalizeLimited } from '../../config/numericLimits.js';
|
|
30
|
+
import { countMessagesTokens } from '../../context/tokenCounter.js';
|
|
31
|
+
import { chat } from '../../llm/client.js';
|
|
32
|
+
import { derivePromptCacheContext } from '../../llm/promptCache.js';
|
|
33
|
+
import type { Message, Tool, ToolCallContext, ToolResult } from '../../types.js';
|
|
34
|
+
import { toToolParameters } from '../../utils/zodToJson.js';
|
|
35
|
+
import { DEFAULT_MAX_RESULT_SIZE_CHARS } from '../types.js';
|
|
36
|
+
|
|
37
|
+
// ---------------- 1. Zod 输入 schema ----------------
|
|
38
|
+
const inputSchema = z.object({
|
|
39
|
+
note: z
|
|
40
|
+
.string()
|
|
41
|
+
.max(2000, 'note 太长(>2000 字)')
|
|
42
|
+
.optional()
|
|
43
|
+
.describe(
|
|
44
|
+
'可选。大多数情况下不需要填——你此前说过的方案/诊断已经在会话历史里,顾问会自动看到。' +
|
|
45
|
+
'只有需要额外强调、历史里没体现的点时才填一句话。',
|
|
46
|
+
),
|
|
47
|
+
});
|
|
48
|
+
type AdvisorInput = z.infer<typeof inputSchema>;
|
|
49
|
+
|
|
50
|
+
const parameters = toToolParameters(inputSchema);
|
|
51
|
+
|
|
52
|
+
// ---------------- 2. Description(给 LLM 看的工具说明) ----------------
|
|
53
|
+
const description = `- Consults an independent, separately-configured second LLM ("顾问模型" / advisor) for a second opinion on your current plan, diagnosis, or progress.
|
|
54
|
+
- Call with no arguments in most cases — the advisor receives a bounded, relevant view of the current session (the original request, recent discussion, current plan/diagnosis, and key tool results). Only pass \`note\` if something important is not otherwise clear.
|
|
55
|
+
- Exception: if your current plan/diagnosis relies on things you saw in images (screenshots, photos, diagrams), summarize the key visual facts in \`note\` in 1-2 sentences (what each image shows and what you concluded from it) — the advisor may not be able to see the images themselves, and any visual judgment you never wrote down as text does not exist for it.
|
|
56
|
+
- Call this: (1) after a plan/diagnosis has taken shape but before you commit to it (write code, declare a conclusion); (2) when you're stuck — the same error keeps recurring, your fixes aren't converging, or the user has pushed back and you're reconsidering direction; (3) before declaring a multi-step task complete and reporting back to the user.
|
|
57
|
+
- The advisor has no tool access and does a single read-only pass over the transcript — it cannot verify anything itself, only reason about what it's shown.
|
|
58
|
+
- If this tool errors (advisor not configured in shared settings, disabled, manual-only, or a call failure), that's not a signal to stop — treat it as "no second opinion available this time" and continue with your own judgment. Do not block or retry in a loop.`;
|
|
59
|
+
|
|
60
|
+
// ---------------- 3. 顾问 persona system prompt ----------------
|
|
61
|
+
const ADVISOR_SYSTEM_PROMPT = `你是一位经验丰富的全栈开发专家技术顾问,为正在独立执行任务的另一个 AI 助手提供第二意见评审。
|
|
62
|
+
你的职责:技术方案评估、代码评审、疑难问题的根因定位与解决方向判断。
|
|
63
|
+
|
|
64
|
+
你会看到该助手当前会话中受预算约束的相关记录——包括首要需求、近期交流、它的思考过程、当前方案
|
|
65
|
+
和关键工具结果与执行结果,以及它现在想让你重点关注的内容(如果有)。
|
|
66
|
+
|
|
67
|
+
审视时请做到:
|
|
68
|
+
- 像资深工程师做方案评审/代码评审一样直接、具体——发现问题就明说,不要无条件附和或说套话。
|
|
69
|
+
- 优先检查:方案是否遗漏了风险或边界情况、诊断是否真的找到了根因还是只是让症状消失、
|
|
70
|
+
是否存在更简单或更稳妥的替代方向。
|
|
71
|
+
- 分清它「跑出来的」和「读代码推出来的」:后者常被当成事实陈述。它声称已验证、而记录里
|
|
72
|
+
找不到对应工具结果的关键结论,直接指出来并要求补验证。
|
|
73
|
+
- 测试全绿不等于改对了:留意「测的是被改的那个函数,而真实调用路径根本走不到它」这类
|
|
74
|
+
情况——绿灯来自覆盖错了层,不是行为正确。
|
|
75
|
+
- 若它此前已征求过你的意见,检查后续动作是否真的落实到位:改了一半、只动了表层、
|
|
76
|
+
或换个方式绕开,都要点出来。
|
|
77
|
+
- 结论必须基于你在历史记录里看到的实际证据,不要脱离上下文给泛泛的通用建议;
|
|
78
|
+
被省略的记录(尤其工具结果原文)所涉及的判断,明说你无法核实。
|
|
79
|
+
- 篇幅精炼:只讲最关键的 1-3 点,不要复述历史、不要客套开场白。`;
|
|
80
|
+
|
|
81
|
+
// ---------------- 4. 消息构造(纯函数,单独导出可测) ----------------
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* 把主循环的 history 转成喂给顾问模型的 messages。
|
|
85
|
+
*
|
|
86
|
+
* 过滤掉所有 role==='system' 的消息——同时去掉了 minimal-agent 自己的系统提示词
|
|
87
|
+
* 和 TUI 的 ephemeral 提示消息(两者都是 role:'system'),换成顾问自己的 persona。
|
|
88
|
+
*/
|
|
89
|
+
const MAX_TOOL_RESULT_CHARS = 12_000;
|
|
90
|
+
|
|
91
|
+
/** 归一化后的工具结果前缀;裁剪统计要靠它反查,两处必须共用同一常量。 */
|
|
92
|
+
const TOOL_RESULT_PREFIX = '[工具结果 ';
|
|
93
|
+
|
|
94
|
+
function truncateMiddle(value: string, limit = MAX_TOOL_RESULT_CHARS): string {
|
|
95
|
+
if (value.length <= limit) return value;
|
|
96
|
+
const half = Math.floor((limit - 100) / 2);
|
|
97
|
+
return `${value.slice(0, half)}\n\n[... 顾问上下文已省略 ${value.length - half * 2} 个字符 ...]\n\n${value.slice(-half)}`;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function redactSensitiveText(value: string, secrets: readonly string[]): string {
|
|
101
|
+
let output = value;
|
|
102
|
+
for (const secret of secrets) {
|
|
103
|
+
if (secret.length >= 6) output = output.split(secret).join('[REDACTED]');
|
|
104
|
+
}
|
|
105
|
+
return output
|
|
106
|
+
.replace(/\b(sk-[A-Za-z0-9._-]{12,}|tvly-[A-Za-z0-9._-]{12,})\b/g, '[REDACTED]')
|
|
107
|
+
.replace(/((?:api[_-]?key|token|secret|password)\s*[:=]\s*)[^\s,;]+/gi, '$1[REDACTED]');
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export function redactAdvisorHistory(history: readonly Message[], secrets: readonly string[]): Message[] {
|
|
111
|
+
return history.map((message) => {
|
|
112
|
+
if (message.role === 'user') {
|
|
113
|
+
const content = typeof message.content === 'string'
|
|
114
|
+
? redactSensitiveText(message.content, secrets)
|
|
115
|
+
: message.content.map((part) => part.type === 'text'
|
|
116
|
+
? { ...part, text: redactSensitiveText(part.text, secrets) }
|
|
117
|
+
: part);
|
|
118
|
+
return { ...message, content };
|
|
119
|
+
}
|
|
120
|
+
if (message.role === 'assistant') return {
|
|
121
|
+
...message,
|
|
122
|
+
content: message.content ? redactSensitiveText(message.content, secrets) : message.content,
|
|
123
|
+
// ⚠️ 思维链必须一起脱敏。它现在会被送给顾问(见 normalizeAdvisorMessage),
|
|
124
|
+
// 而顾问常常是**另一家 provider**;模型在思考里复述 apiKey/token 完全可能发生。
|
|
125
|
+
// 少脱这一处,等于开一个只在「顾问启用」时才生效的泄漏口子。
|
|
126
|
+
...(typeof message.reasoning_content === 'string'
|
|
127
|
+
? { reasoning_content: redactSensitiveText(message.reasoning_content, secrets) }
|
|
128
|
+
: {}),
|
|
129
|
+
...(typeof message.reasoning === 'string'
|
|
130
|
+
? { reasoning: redactSensitiveText(message.reasoning, secrets) }
|
|
131
|
+
: {}),
|
|
132
|
+
tool_calls: message.tool_calls?.map((call) => ({
|
|
133
|
+
...call,
|
|
134
|
+
function: { ...call.function, arguments: redactSensitiveText(call.function.arguments, secrets) },
|
|
135
|
+
})),
|
|
136
|
+
};
|
|
137
|
+
return { ...message, content: redactSensitiveText(message.content, secrets) };
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
interface AdvisorImageBudget {
|
|
142
|
+
count: number;
|
|
143
|
+
encodedBytes: number;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const MAX_ADVISOR_IMAGES = 4;
|
|
147
|
+
const MAX_ADVISOR_IMAGE_BYTES = 8 * 1024 * 1024;
|
|
148
|
+
|
|
149
|
+
function contentForAdvisor(
|
|
150
|
+
message: Extract<Message, { role: 'user' }>,
|
|
151
|
+
supportsVision: boolean,
|
|
152
|
+
allowImages: boolean,
|
|
153
|
+
imageBudget: AdvisorImageBudget,
|
|
154
|
+
): Extract<Message, { role: 'user' }>['content'] {
|
|
155
|
+
if (!Array.isArray(message.content)) return truncateMiddle(message.content ?? '');
|
|
156
|
+
const parts = message.content.map((part) => {
|
|
157
|
+
if (part.type === 'text') return { ...part, text: truncateMiddle(part.text) };
|
|
158
|
+
if (part.type === 'image_url' && supportsVision && allowImages) {
|
|
159
|
+
const encodedBytes = part.image_url.url.startsWith('data:')
|
|
160
|
+
? new TextEncoder().encode(part.image_url.url).byteLength
|
|
161
|
+
: 0;
|
|
162
|
+
if (
|
|
163
|
+
imageBudget.count >= MAX_ADVISOR_IMAGES ||
|
|
164
|
+
imageBudget.encodedBytes + encodedBytes > MAX_ADVISOR_IMAGE_BYTES
|
|
165
|
+
) {
|
|
166
|
+
return { type: 'text' as const, text: '[图片未发送:超过顾问视觉输入的数量或体积限制。]' };
|
|
167
|
+
}
|
|
168
|
+
imageBudget.count++;
|
|
169
|
+
imageBudget.encodedBytes += encodedBytes;
|
|
170
|
+
return part;
|
|
171
|
+
}
|
|
172
|
+
if (part.type === 'image_url' && supportsVision && !allowImages) {
|
|
173
|
+
return { type: 'text' as const, text: '[图片未发送:未允许向顾问模型共享敏感原文或图像。]' };
|
|
174
|
+
}
|
|
175
|
+
if (part.type === 'image_url') {
|
|
176
|
+
return { type: 'text' as const, text: '[图片未发送:顾问模型未启用视觉能力;请结合会话中已有的图片描述评审。]' };
|
|
177
|
+
}
|
|
178
|
+
return { type: 'text' as const, text: '[视频内容未发送给顾问模型。]' };
|
|
179
|
+
});
|
|
180
|
+
return parts;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
function normalizeAdvisorMessage(
|
|
184
|
+
message: Message,
|
|
185
|
+
supportsVision: boolean,
|
|
186
|
+
allowImages: boolean,
|
|
187
|
+
imageBudget: AdvisorImageBudget,
|
|
188
|
+
): Message | null {
|
|
189
|
+
if (message.role === 'system') return null;
|
|
190
|
+
if (message.role === 'tool') {
|
|
191
|
+
return { role: 'user', content: `${TOOL_RESULT_PREFIX}${message.tool_call_id}]\n${truncateMiddle(message.content)}` };
|
|
192
|
+
}
|
|
193
|
+
if (message.role === 'assistant') {
|
|
194
|
+
const calls = message.tool_calls?.map((call) =>
|
|
195
|
+
`[调用工具 ${call.function.name}] ${truncateMiddle(call.function.arguments, 2000)}`,
|
|
196
|
+
).join('\n');
|
|
197
|
+
// 思维链一并送给顾问:评审「方案对不对」很大程度上要看**它是怎么想到这个方案的**——
|
|
198
|
+
// 推理里的跳跃(把推断当已验证、跳过一个没排除的可能)在最终结论文本里往往看不出来。
|
|
199
|
+
// 截断比正文更狠(2000):重思考模型的 reasoning 常比 content 长几倍,不设限会把
|
|
200
|
+
// 预算吃光、反而挤掉更该保留的工具结果原文。
|
|
201
|
+
// reasoning_details(结构化数组)不传:结构因 provider 而异,既无法可靠脱敏也无法
|
|
202
|
+
// 稳定转成文本,传过去只会变成噪声。
|
|
203
|
+
const thinking = typeof message.reasoning_content === 'string' && message.reasoning_content.length > 0
|
|
204
|
+
? message.reasoning_content
|
|
205
|
+
: (typeof message.reasoning === 'string' ? message.reasoning : '');
|
|
206
|
+
const thinkingLine = thinking.length > 0
|
|
207
|
+
? `[思考过程]\n${truncateMiddle(thinking, 2000)}`
|
|
208
|
+
: '';
|
|
209
|
+
return {
|
|
210
|
+
role: 'assistant',
|
|
211
|
+
content: [thinkingLine, message.content ?? '', calls ?? ''].filter(Boolean).join('\n'),
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
const attachmentSummary = message.attachments?.length
|
|
215
|
+
? `\n[附件] ${message.attachments.map((item) => `${item.name} (${item.mime}, ${item.size} bytes)`).join('、')}`
|
|
216
|
+
: '';
|
|
217
|
+
const content = contentForAdvisor(message, supportsVision, allowImages, imageBudget);
|
|
218
|
+
if (typeof content === 'string') return { role: 'user', content: `${content}${attachmentSummary}` };
|
|
219
|
+
return attachmentSummary
|
|
220
|
+
? { role: 'user', content: [...content, { type: 'text', text: attachmentSummary }] }
|
|
221
|
+
: { role: 'user', content };
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
function fitAdvisorBudget(messages: Message[], inputBudget: number): Message[] {
|
|
225
|
+
if (countMessagesTokens(messages) <= inputBudget) return messages;
|
|
226
|
+
const system = messages[0]!;
|
|
227
|
+
const closing = messages[messages.length - 1]!;
|
|
228
|
+
const body = messages.slice(1, -1);
|
|
229
|
+
const firstUserIndex = body.findIndex((message) => message.role === 'user');
|
|
230
|
+
const selected: Message[] = [];
|
|
231
|
+
let used = countMessagesTokens([system, closing]);
|
|
232
|
+
if (firstUserIndex >= 0) {
|
|
233
|
+
const first = body[firstUserIndex]!;
|
|
234
|
+
selected.push(first);
|
|
235
|
+
used += countMessagesTokens([first]);
|
|
236
|
+
}
|
|
237
|
+
const recent: Message[] = [];
|
|
238
|
+
for (let index = body.length - 1; index >= 0; index--) {
|
|
239
|
+
if (index === firstUserIndex) continue;
|
|
240
|
+
const message = body[index]!;
|
|
241
|
+
const cost = countMessagesTokens([message]);
|
|
242
|
+
if (used + cost > inputBudget) continue;
|
|
243
|
+
recent.unshift(message);
|
|
244
|
+
used += cost;
|
|
245
|
+
}
|
|
246
|
+
const droppedCount = body.length - selected.length - recent.length;
|
|
247
|
+
if (droppedCount > 0) {
|
|
248
|
+
// 单独点出被丢掉的**工具结果**条数:顾问最有价值的用法之一是发现执行助手误读了
|
|
249
|
+
// 自己跑出来的证据,而裁剪一旦只留下助手的转述、丢掉原始工具输出,这个能力就归零。
|
|
250
|
+
// 与其让它对着转述给出自信结论,不如如实告诉它「这里有盲区」。
|
|
251
|
+
const kept = new Set<Message>([...selected, ...recent]);
|
|
252
|
+
const droppedToolResults = body.filter((message) =>
|
|
253
|
+
!kept.has(message)
|
|
254
|
+
&& message.role === 'user'
|
|
255
|
+
&& typeof message.content === 'string'
|
|
256
|
+
&& message.content.startsWith(TOOL_RESULT_PREFIX)).length;
|
|
257
|
+
const toolNote = droppedToolResults > 0
|
|
258
|
+
? `其中 ${droppedToolResults} 条是工具执行结果原文——这部分证据你看不到,只能看到执行助手的转述,涉及它们的判断请明确说明无法核实。`
|
|
259
|
+
: '';
|
|
260
|
+
selected.push({
|
|
261
|
+
role: 'user',
|
|
262
|
+
content: `[较早历史因顾问上下文预算省略 ${droppedCount} 条;已保留最初需求和最近相关记录。${toolNote}]`,
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
return [system, ...selected, ...recent, closing];
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
export function buildAdvisorMessages(
|
|
269
|
+
history: readonly Message[],
|
|
270
|
+
note?: string,
|
|
271
|
+
options: {
|
|
272
|
+
supportsVision?: boolean;
|
|
273
|
+
allowImages?: boolean;
|
|
274
|
+
contextWindow?: number;
|
|
275
|
+
maxTokens?: number;
|
|
276
|
+
inputTokenBudget?: number;
|
|
277
|
+
} = {},
|
|
278
|
+
): Message[] {
|
|
279
|
+
const imageBudget: AdvisorImageBudget = { count: 0, encodedBytes: 0 };
|
|
280
|
+
const nonSystem = history
|
|
281
|
+
.map((message) => normalizeAdvisorMessage(
|
|
282
|
+
message,
|
|
283
|
+
options.supportsVision === true,
|
|
284
|
+
options.allowImages ?? (options.supportsVision === true),
|
|
285
|
+
imageBudget,
|
|
286
|
+
))
|
|
287
|
+
.filter((message): message is Message => message !== null);
|
|
288
|
+
|
|
289
|
+
const closing = [
|
|
290
|
+
'以上是执行助手当前会话的受预算约束相关记录(保留最初需求、近期交流、当前方案与关键工具结果)。',
|
|
291
|
+
note ? `\n执行助手额外补充:\n${note}` : '',
|
|
292
|
+
'\n请审视以上内容——它当前的方案/诊断/进展是否存在问题、遗漏的风险,或有没有更好的方向?给出你的意见。',
|
|
293
|
+
]
|
|
294
|
+
.filter(Boolean)
|
|
295
|
+
.join('\n');
|
|
296
|
+
|
|
297
|
+
const messages: Message[] = [
|
|
298
|
+
{ role: 'system', content: ADVISOR_SYSTEM_PROMPT },
|
|
299
|
+
...nonSystem,
|
|
300
|
+
{ role: 'user', content: closing },
|
|
301
|
+
];
|
|
302
|
+
const contextWindow = options.contextWindow ?? 128_000;
|
|
303
|
+
const maxTokens = options.maxTokens ?? 4096;
|
|
304
|
+
const configuredBudget = normalizeLimited(
|
|
305
|
+
options.inputTokenBudget, CONFIG_LIMITS.advisorInputTokenBudget, 128_000);
|
|
306
|
+
const providerBudget = Math.max(CONFIG_LIMITS.advisorInputTokenBudget.min, contextWindow - maxTokens - 4096);
|
|
307
|
+
const inputBudget = Math.min(configuredBudget, providerBudget);
|
|
308
|
+
return fitAdvisorBudget(messages, inputBudget);
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
/** 轻量错误分类:把第三方 HTTP/网络报错文本归到几类可操作的提示上。 */
|
|
312
|
+
function classifyAdvisorError(e: Error): string {
|
|
313
|
+
const msg = e.message ?? String(e);
|
|
314
|
+
const lower = msg.toLowerCase();
|
|
315
|
+
if (
|
|
316
|
+
lower.includes('context') ||
|
|
317
|
+
lower.includes('too long') ||
|
|
318
|
+
lower.includes('maximum context') ||
|
|
319
|
+
lower.includes('token limit')
|
|
320
|
+
) {
|
|
321
|
+
return `顾问模型上下文超限(当前会话历史过长,超出顾问的上下文窗口):${msg}`;
|
|
322
|
+
}
|
|
323
|
+
if (lower.includes('429') || lower.includes('rate limit') || lower.includes('too many requests')) {
|
|
324
|
+
return `顾问模型被限流:${msg}`;
|
|
325
|
+
}
|
|
326
|
+
if (lower.includes('timeout') || lower.includes('timed out') || lower.includes('network') || lower.includes('econnreset')) {
|
|
327
|
+
return `顾问模型响应超时或网络错误:${msg}`;
|
|
328
|
+
}
|
|
329
|
+
return `顾问模型调用失败:${msg}`;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
// ---------------- 5. 执行逻辑(导出供测试 DI) ----------------
|
|
333
|
+
|
|
334
|
+
/**
|
|
335
|
+
* 真正的执行逻辑,chatFn 可注入 stub(测试用,镜像 compact.ts::runCompaction 的
|
|
336
|
+
* `chatFn: typeof chat = chat` 模式),不需要 mock.module。
|
|
337
|
+
*/
|
|
338
|
+
export async function runAdvisor(
|
|
339
|
+
input: AdvisorInput,
|
|
340
|
+
signal?: AbortSignal,
|
|
341
|
+
ctx?: ToolCallContext,
|
|
342
|
+
chatFn: typeof chat = chat,
|
|
343
|
+
runtime: { timeoutMs?: number } = {},
|
|
344
|
+
): Promise<ToolResult> {
|
|
345
|
+
const advisor = await configService.advisor();
|
|
346
|
+
if (!advisor) {
|
|
347
|
+
return {
|
|
348
|
+
ok: false,
|
|
349
|
+
error:
|
|
350
|
+
'顾问模型未配置。请在设置 → 模型中完成专家顾问配置。' +
|
|
351
|
+
'可继续凭自己判断推进,不必等待顾问。',
|
|
352
|
+
};
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
if (!advisor.enabled) {
|
|
356
|
+
return {
|
|
357
|
+
ok: false,
|
|
358
|
+
error: '顾问模型当前已关闭(/advisor on 开启)。可继续凭自己判断推进,不必等待顾问。',
|
|
359
|
+
};
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
if (advisor.mode === 'manual' && ctx?.advisorInvocation !== 'manual') {
|
|
363
|
+
return { ok: false, error: '顾问模型当前为“仅手动”模式;请使用 /advisor ask 发起咨询。' };
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
if (ctx?.advisorCalls && ctx.advisorCalls.used >= ctx.advisorCalls.max) {
|
|
367
|
+
// ⚠️ 必须说明「配额会增长」:配额随本回合工作量上调(loop.ts::advisorQuotaFor),
|
|
368
|
+
// 若这里只说「已达上限」,模型会当成本回合的硬上限而不再尝试 —— 那样长回合里
|
|
369
|
+
// 「批次做完再复核一次」的配额永远不会被用到,增长机制等于没做。
|
|
370
|
+
return {
|
|
371
|
+
ok: false,
|
|
372
|
+
error:
|
|
373
|
+
`本回合顾问咨询配额暂时用完(当前 ${ctx.advisorCalls.max} 次)。` +
|
|
374
|
+
'配额会随本回合的工作量增长,继续推进一段后可以再次咨询;现在请按已有意见继续。',
|
|
375
|
+
};
|
|
376
|
+
}
|
|
377
|
+
if (ctx?.advisorCalls) ctx.advisorCalls.used++;
|
|
378
|
+
|
|
379
|
+
const provider = advisor.provider;
|
|
380
|
+
const snapshot = await configService.getSnapshot();
|
|
381
|
+
const secrets = [
|
|
382
|
+
...(snapshot.config?.models.map((entry) => entry.apiKey) ?? []),
|
|
383
|
+
snapshot.config?.tools.webSearch?.apiKey,
|
|
384
|
+
snapshot.config?.tools.imageGeneration?.apiKey,
|
|
385
|
+
snapshot.config?.tools.openRouter?.apiKey,
|
|
386
|
+
snapshot.config?.tools.textToSpeech?.apiKey,
|
|
387
|
+
].filter((value): value is string => !!value);
|
|
388
|
+
const history = advisor.shareSensitive
|
|
389
|
+
? [...(ctx?.history ?? [])]
|
|
390
|
+
: redactAdvisorHistory(ctx?.history ?? [], secrets);
|
|
391
|
+
const messages = buildAdvisorMessages(history, input.note, {
|
|
392
|
+
supportsVision: advisor.supportsVision,
|
|
393
|
+
allowImages: advisor.supportsVision && advisor.shareSensitive,
|
|
394
|
+
contextWindow: provider.contextWindow,
|
|
395
|
+
maxTokens: provider.maxTokens,
|
|
396
|
+
inputTokenBudget: advisor.inputTokenBudget,
|
|
397
|
+
});
|
|
398
|
+
|
|
399
|
+
// Advisor 是可降级的第二意见:使用独立、可配置的总时限,不影响主模型的流式时限。
|
|
400
|
+
// 默认 180 秒,直接使用所选模型的上下文窗口配置。
|
|
401
|
+
const timeoutMs = runtime.timeoutMs ?? advisor.timeoutMs;
|
|
402
|
+
const timeoutController = new AbortController();
|
|
403
|
+
const timer = setTimeout(() => timeoutController.abort(new Error('advisor-timeout')), timeoutMs);
|
|
404
|
+
const combinedSignal = signal ? AbortSignal.any([signal, timeoutController.signal]) : timeoutController.signal;
|
|
405
|
+
|
|
406
|
+
let content = '';
|
|
407
|
+
let reasoningChars = 0;
|
|
408
|
+
// 发布端节流:LoopEvent 消费端直接更新对应 toolCallId,这里控制事件频率,
|
|
409
|
+
// 而流式 delta 每秒可达几十上百条——阶段切换(思考中→整理意见)立即发保证切换及时可见,
|
|
410
|
+
// 同阶段后续 delta 以 ≥200ms 门控,与主循环流式 setState 的 100ms 节流纪律同量级。
|
|
411
|
+
let lastPhase: 'reasoning' | 'text' | null = null;
|
|
412
|
+
let lastPublishAt = 0;
|
|
413
|
+
const publishProgress = (phase: 'reasoning' | 'text', buildLine: () => string) => {
|
|
414
|
+
const now = Date.now();
|
|
415
|
+
if (phase === lastPhase && now - lastPublishAt < 200) return;
|
|
416
|
+
lastPhase = phase;
|
|
417
|
+
lastPublishAt = now;
|
|
418
|
+
ctx?.reportProgress?.(buildLine());
|
|
419
|
+
};
|
|
420
|
+
// 步内实时进度:治"顾问模型思考阶段 reasoning_delta 被静默丢弃、TUI 看起来像卡住"
|
|
421
|
+
// (2026-07 真实反馈:2m05s 无任何变化,用户误判死机)。无 UI 宿主时 no-op,零副作用。
|
|
422
|
+
const streamOnce = async (advisorMessages: Message[]): Promise<string> => {
|
|
423
|
+
let output = '';
|
|
424
|
+
for await (const ev of chatFn({
|
|
425
|
+
provider,
|
|
426
|
+
messages: advisorMessages,
|
|
427
|
+
tools: [],
|
|
428
|
+
signal: combinedSignal,
|
|
429
|
+
promptCache: derivePromptCacheContext(ctx?.promptCache, 'advisor'),
|
|
430
|
+
})) {
|
|
431
|
+
if (ev.type === 'reasoning_delta' && ev.delta) {
|
|
432
|
+
reasoningChars += ev.delta.length;
|
|
433
|
+
publishProgress('reasoning', () => `顾问模型思考中…(已生成 ${reasoningChars} 字思考)`);
|
|
434
|
+
} else if (ev.type === 'text_delta') {
|
|
435
|
+
output += ev.delta;
|
|
436
|
+
publishProgress('text', () => `顾问模型正在整理意见…(已输出 ${output.length} 字)`);
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
return output;
|
|
440
|
+
};
|
|
441
|
+
|
|
442
|
+
const sentImageParts = messages.some(
|
|
443
|
+
(message) => Array.isArray(message.content) && message.content.some((part) => part.type === 'image_url'),
|
|
444
|
+
);
|
|
445
|
+
let degradedWithoutImages = false;
|
|
446
|
+
try {
|
|
447
|
+
try {
|
|
448
|
+
content = await streamOnce(messages);
|
|
449
|
+
} catch (e) {
|
|
450
|
+
// 剥图重试保底:配置声称 supportsVision 但 provider 实际拒收图片(能力不符/图片
|
|
451
|
+
// 格式不合口味)时,把图片降级成占位文本重试一次,别白丢一次咨询。只在真的发过
|
|
452
|
+
// 图、且失败不是超时/用户中断时触发;共用同一个超时预算(timer 不重置,不叠加等待)。
|
|
453
|
+
if (!sentImageParts || timeoutController.signal.aborted || signal?.aborted) throw e;
|
|
454
|
+
content = await streamOnce(
|
|
455
|
+
buildAdvisorMessages(history, input.note, {
|
|
456
|
+
supportsVision: false,
|
|
457
|
+
contextWindow: provider.contextWindow,
|
|
458
|
+
maxTokens: provider.maxTokens,
|
|
459
|
+
inputTokenBudget: advisor.inputTokenBudget,
|
|
460
|
+
}),
|
|
461
|
+
);
|
|
462
|
+
degradedWithoutImages = true;
|
|
463
|
+
}
|
|
464
|
+
} catch (e) {
|
|
465
|
+
if (timeoutController.signal.aborted && !signal?.aborted) {
|
|
466
|
+
return {
|
|
467
|
+
ok: false,
|
|
468
|
+
error:
|
|
469
|
+
`顾问模型响应超过 ${Math.round(timeoutMs / 1000)}s 未完成,已跳过。` +
|
|
470
|
+
'可继续凭自己判断推进,不必等待顾问。',
|
|
471
|
+
};
|
|
472
|
+
}
|
|
473
|
+
return {
|
|
474
|
+
ok: false,
|
|
475
|
+
error: `${classifyAdvisorError(e as Error)} 可继续凭自己判断推进,不必等待顾问。`,
|
|
476
|
+
};
|
|
477
|
+
} finally {
|
|
478
|
+
clearTimeout(timer);
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
if (degradedWithoutImages) {
|
|
482
|
+
// 让主模型如实理解这份意见的局限:顾问没看到图,评的是纯文本上下文。
|
|
483
|
+
content = `(顾问未能接收图片,本意见基于纯文本上下文。)\n${content}`;
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
if (content.length > DEFAULT_MAX_RESULT_SIZE_CHARS) {
|
|
487
|
+
content =
|
|
488
|
+
content.slice(0, DEFAULT_MAX_RESULT_SIZE_CHARS) +
|
|
489
|
+
`\n\n... (输出超过 ${DEFAULT_MAX_RESULT_SIZE_CHARS} 字符,已截断)`;
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
return { ok: true, content };
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
// 无宿主上下文的调用视为用户手动咨询;生产主循环使用 callWithContext。
|
|
496
|
+
async function call(input: AdvisorInput, signal?: AbortSignal): Promise<ToolResult> {
|
|
497
|
+
return runAdvisor(input, signal, { advisorInvocation: 'manual' });
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
// ---------------- 6. 导出 ----------------
|
|
501
|
+
export const advisorTool: Tool<AdvisorInput> = {
|
|
502
|
+
name: 'Advisor',
|
|
503
|
+
description,
|
|
504
|
+
inputSchema,
|
|
505
|
+
parameters,
|
|
506
|
+
isReadOnly: true,
|
|
507
|
+
isConcurrencySafe: false,
|
|
508
|
+
maxResultSizeChars: DEFAULT_MAX_RESULT_SIZE_CHARS,
|
|
509
|
+
interruptBehavior: () => 'cancel',
|
|
510
|
+
call,
|
|
511
|
+
callWithContext: runAdvisor,
|
|
512
|
+
};
|