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,642 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ============================================================
|
|
3
|
+
* src/tools/bash.ts —— Bash 工具(万能命令兜底)
|
|
4
|
+
* ------------------------------------------------------------
|
|
5
|
+
* 对应 kakadeai 主仓库的 BashTool。
|
|
6
|
+
* 这是 minimal-agent 的"瑞士军刀" —— 当 Read/Edit/Write/Glob/Grep
|
|
7
|
+
* 搞不定时(rm 文件、chmod、git、npm/curl、跑测试...),就用它。
|
|
8
|
+
*
|
|
9
|
+
* 设计原则(重要):
|
|
10
|
+
*
|
|
11
|
+
* 1. 优先用专用工具,Bash 只做兜底
|
|
12
|
+
* - 读文件 → Read(不要 cat/head/tail)
|
|
13
|
+
* - 写文件 → Write(不要 echo > / cat <<EOF)
|
|
14
|
+
* - 改文件 → Edit(不要 sed/awk)
|
|
15
|
+
* - 列文件 → Glob(不要 find/ls)
|
|
16
|
+
* - 搜内容 → Grep(不要 grep/rg)
|
|
17
|
+
* 这条放进 description 里反复强调,让模型自觉。
|
|
18
|
+
*
|
|
19
|
+
* 2. 安全黑名单:拦截灾难性命令
|
|
20
|
+
* - rm -rf / 、 rm -rf $HOME、 rm -rf ~
|
|
21
|
+
* - mkfs / format c: / dd if=...of=/dev/sd*
|
|
22
|
+
* - shutdown / reboot / halt / poweroff
|
|
23
|
+
* - fork bomb :(){ :|:& };:
|
|
24
|
+
* - curl ... | sh / wget ... | bash (管道到 shell)
|
|
25
|
+
* 不可绕过:在 call() 入口正则匹配,命中直接拒绝。
|
|
26
|
+
* 注意:黑名单是兜底,不是真正的沙盒;模型规规矩矩工作时不会误伤。
|
|
27
|
+
*
|
|
28
|
+
* 3. 超时保护:默认 120s,可显式设到最多 600s(10 分钟)
|
|
29
|
+
* - 防止跑死的命令吃光会话;
|
|
30
|
+
* - AbortSignal + timer 双保险。
|
|
31
|
+
*
|
|
32
|
+
* 4. 跨平台:
|
|
33
|
+
* - 用 spawn({ shell: true }),让平台自己挑 shell
|
|
34
|
+
* (Unix → /bin/sh -c,Windows → cmd.exe /d /s /c)
|
|
35
|
+
* - 对模型透明:在 prompt 里说"假定 bash 语法",
|
|
36
|
+
* Windows 用户可能要自行调整跨平台命令(这是 minimal 版本,不做转译)
|
|
37
|
+
*
|
|
38
|
+
* 5. 输出兜底:
|
|
39
|
+
* - stdout / stderr / exit code 一起返回(结构化文本)
|
|
40
|
+
* - 超过 30K 字符截断,避免把 npm install 的 200K 日志灌进上下文
|
|
41
|
+
* ============================================================
|
|
42
|
+
*/
|
|
43
|
+
|
|
44
|
+
import { spawn } from 'node:child_process';
|
|
45
|
+
import { existsSync } from 'node:fs';
|
|
46
|
+
import { join } from 'node:path';
|
|
47
|
+
import { z } from 'zod';
|
|
48
|
+
|
|
49
|
+
import { getWorkingDir } from '../../bootstrap/workingDir.js';
|
|
50
|
+
import type { Tool, ToolCallContext, ToolResult } from '../../types.js';
|
|
51
|
+
import { sanitizeCredentialEnv } from '../../config/configService.js';
|
|
52
|
+
import { DEFAULT_MAX_RESULT_SIZE_CHARS } from '../types.js';
|
|
53
|
+
import { killTree } from '../shared/killTree.js';
|
|
54
|
+
import { toToolParameters } from '../../utils/zodToJson.js';
|
|
55
|
+
import {
|
|
56
|
+
MAX_ATTACH_IMAGES,
|
|
57
|
+
extractAttachImageDirectives,
|
|
58
|
+
resolveAttachImages,
|
|
59
|
+
} from './attachImage.js';
|
|
60
|
+
import { interpretCommandResult } from './semantics.js';
|
|
61
|
+
import { scanDestructiveCommand } from './warnings.js';
|
|
62
|
+
import { requestUserChoice } from '../../askBridge.js';
|
|
63
|
+
import { classifyCommand } from '../../irreversible/rules.js';
|
|
64
|
+
import { startBackgroundTask } from '../tasks/backgroundTaskService.js';
|
|
65
|
+
import { processExecutionMetadata } from '../tasks/processEvidence.js';
|
|
66
|
+
import { permanentDeleteReason } from '../shared/permanentDeleteGuard.js';
|
|
67
|
+
import { ToolOutputStore } from '../shared/toolOutputStore.js';
|
|
68
|
+
|
|
69
|
+
// ---------------- 0. 常量 ----------------
|
|
70
|
+
|
|
71
|
+
const DEFAULT_TIMEOUT_MS = 120_000; // 2 min
|
|
72
|
+
const MAX_TIMEOUT_MS = 600_000; // 10 min
|
|
73
|
+
|
|
74
|
+
// ---------------- 0b. Windows shell 探测(模块加载时一次性) ----------------
|
|
75
|
+
//
|
|
76
|
+
// 根因(2026-06-12 实跑事故):本工具 description 宣称 bash,但 win32 下
|
|
77
|
+
// spawn({shell:true}) 实际是 cmd.exe —— 模型写的 `mkdir -p doc/scripts`(完全正确的
|
|
78
|
+
// bash 语法)被 cmd 直接报语法错误;且中文 Windows 下 cmd 错误输出是 GBK 编码、
|
|
79
|
+
// 被 setEncoding('utf8') 解码成乱码,模型既看不懂错误也换不对路,瞎试几轮后停摆,
|
|
80
|
+
// 工作流执行步因此在第一条命令上整体卡死。
|
|
81
|
+
//
|
|
82
|
+
// 对策(三层):
|
|
83
|
+
// ① 优先探测真 bash(Git Bash)——POSIX 语法直接成立,description 不再撒谎;
|
|
84
|
+
// 排除 System32\bash.exe(WSL:跑在 Linux 环境,cwd/路径语义完全不同,不可用)。
|
|
85
|
+
// ② 找不到才回落 cmd,命令前缀 `chcp 65001 >nul & ` 强制 UTF-8 输出(根治乱码)。
|
|
86
|
+
// ③ description 运行时声明实际 shell(见下),模型按真实语法写命令。
|
|
87
|
+
// 可用 MINIMAL_AGENT_BASH_PATH 显式指定(绿色版 U 盘机器上 Git 安装位置不可预知)。
|
|
88
|
+
function detectWindowsBash(): string | null {
|
|
89
|
+
if (process.platform !== 'win32') return null;
|
|
90
|
+
const explicit = process.env.MINIMAL_AGENT_BASH_PATH;
|
|
91
|
+
if (explicit && existsSync(explicit)) return explicit;
|
|
92
|
+
const pf = process.env.ProgramFiles ?? 'C:\\Program Files';
|
|
93
|
+
const pf86 = process.env['ProgramFiles(x86)'] ?? 'C:\\Program Files (x86)';
|
|
94
|
+
const local = process.env.LOCALAPPDATA ?? '';
|
|
95
|
+
const candidates = [
|
|
96
|
+
join(pf, 'Git', 'bin', 'bash.exe'),
|
|
97
|
+
join(pf, 'Git', 'usr', 'bin', 'bash.exe'),
|
|
98
|
+
join(pf86, 'Git', 'bin', 'bash.exe'),
|
|
99
|
+
local ? join(local, 'Programs', 'Git', 'bin', 'bash.exe') : '',
|
|
100
|
+
].filter(Boolean);
|
|
101
|
+
for (const c of candidates) {
|
|
102
|
+
if (existsSync(c)) return c;
|
|
103
|
+
}
|
|
104
|
+
// PATH 兜底扫描(跳过 System32 的 WSL bash)
|
|
105
|
+
for (const dir of (process.env.PATH ?? '').split(';')) {
|
|
106
|
+
if (!dir || /\\system32\\?/i.test(dir)) continue;
|
|
107
|
+
const p = join(dir, 'bash.exe');
|
|
108
|
+
if (existsSync(p)) return p;
|
|
109
|
+
}
|
|
110
|
+
return null;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const WIN_BASH = detectWindowsBash();
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* G1:stdout / stderr 即时缓冲上限(各自)。命令狂输出(如 cat 大文件 / yes)时,
|
|
117
|
+
* 在最终统一截断之前先把内存封顶,防 OOM。取最终展示截断阈值的 2 倍 —— 正常输出不受影响,
|
|
118
|
+
* 超大输出也只占有限内存(最终仍按 DEFAULT_MAX_RESULT_SIZE_CHARS 截断展示)。
|
|
119
|
+
*/
|
|
120
|
+
const MAX_STREAM_BUFFER_CHARS = DEFAULT_MAX_RESULT_SIZE_CHARS * 2;
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* 灾难性命令黑名单。
|
|
124
|
+
* 命中任意一条就直接拒绝执行 —— 这层是"防猪队友"而不是真沙盒。
|
|
125
|
+
*
|
|
126
|
+
* 选词依据(每条注释解释为啥要拦):
|
|
127
|
+
* - 历史上"误删生产/格盘/锁机"的真实事故都对应一类模式
|
|
128
|
+
* - 工程协作里这些命令几乎不会被合法用到(如要用,肯定是用户在终端自己手敲)
|
|
129
|
+
*
|
|
130
|
+
* 工具仅是"模型可调用的能力",模型规规矩矩地写代码、跑测试时永远不会触到这些。
|
|
131
|
+
*/
|
|
132
|
+
const FORBIDDEN_PATTERNS: Array<{ pattern: RegExp; reason: string }> = [
|
|
133
|
+
// ---- rm -rf / 及变体 ----
|
|
134
|
+
// 经典:"rm -rf /"、"rm -rf /*"、"rm -fr /usr"、"rm -rf $HOME"、"rm -rf ~"
|
|
135
|
+
// 也覆盖 sudo rm -rf /
|
|
136
|
+
{
|
|
137
|
+
pattern: /\brm\s+(?:-[a-zA-Z]*[rRf][a-zA-Z]*\s+)+(?:\/+\*?|\$HOME|~)(?:\s|$)/,
|
|
138
|
+
reason: '禁止递归删除根目录或家目录(rm -rf / 类)',
|
|
139
|
+
},
|
|
140
|
+
// 拦截 "rm -rf <绝对路径>" 中目标是关键系统目录的情况(防误伤其它正常 rm -rf ./tmp)
|
|
141
|
+
{
|
|
142
|
+
pattern: /\brm\s+(?:-[a-zA-Z]*[rRf][a-zA-Z]*\s+)+(?:\/etc|\/usr|\/bin|\/sbin|\/var|\/boot|\/lib|\/System|\/Windows|\/Users|\/home)(?:\/|\s|$)/,
|
|
143
|
+
reason: '禁止递归删除系统关键目录',
|
|
144
|
+
},
|
|
145
|
+
|
|
146
|
+
// ---- 格盘 / 改写裸设备 ----
|
|
147
|
+
{
|
|
148
|
+
pattern: /\bmkfs(?:\.[a-z0-9]+)?\b/i,
|
|
149
|
+
reason: '禁止格式化文件系统(mkfs)',
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
// 末尾不能用 \b:盘符 "C:" 后是非 word char,\b 匹配不到。
|
|
153
|
+
// 改成"盘符后跟空白/反斜杠/结尾"。
|
|
154
|
+
pattern: /\bformat\s+[A-Za-z]:(?=\s|\\|$)/i,
|
|
155
|
+
reason: '禁止 Windows 格盘命令(format X:)',
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
pattern: /\bdd\s+[^|;&]*\bof=\/dev\/(?:sd[a-z]|nvme|disk|hd[a-z]|mmcblk)/i,
|
|
159
|
+
reason: '禁止 dd 写入裸磁盘设备',
|
|
160
|
+
},
|
|
161
|
+
{
|
|
162
|
+
pattern: />\s*\/dev\/(?:sd[a-z]|nvme|disk|hd[a-z]|mmcblk)/i,
|
|
163
|
+
reason: '禁止重定向到裸磁盘设备',
|
|
164
|
+
},
|
|
165
|
+
|
|
166
|
+
// ---- 关机 / 重启 ----
|
|
167
|
+
// 只匹配「命令位置」的关机词:串首 / 换行 / ; & | ` ( { / $( / sudo·doas·exec·nohup / systemctl 之后。
|
|
168
|
+
// 事故(2026-07-02):此前 /\b(?:shutdown|…)\b/ 对整段命令裸词扫描(注释也算),
|
|
169
|
+
// ppt.yaml stage_export 的 bash 注释「硬门 halt」被误杀 → 整步执行前被拒 →
|
|
170
|
+
// onError:halt → 确定性死端(resume 原样命令原样再拒,永久卡死)。
|
|
171
|
+
// "halt" 是工作流引擎自身词汇(onError: halt),注释/echo 文案里出现属正常写作。
|
|
172
|
+
// 已知窄化(防猪队友层可接受):env/timeout 等间接调用不再拦;注释里 ASCII "; halt" 仍会误伤
|
|
173
|
+
// (workflows 全库有 bashBlacklist.sweep.test.ts 兜底扫描)。
|
|
174
|
+
{
|
|
175
|
+
pattern: /(?:^|[\n;&|`({]|\$\(|\b(?:sudo|doas|exec|nohup)\s+|\bsystemctl\s+)\s*(?:shutdown|reboot|halt|poweroff)\b/i,
|
|
176
|
+
reason: '禁止关机/重启命令',
|
|
177
|
+
},
|
|
178
|
+
{
|
|
179
|
+
pattern: /\binit\s+[06]\b/,
|
|
180
|
+
reason: '禁止 init 0/6(关机/重启)',
|
|
181
|
+
},
|
|
182
|
+
|
|
183
|
+
// ---- Fork bomb ----
|
|
184
|
+
{
|
|
185
|
+
pattern: /:\s*\(\s*\)\s*\{\s*:\s*\|\s*:\s*&\s*\}\s*;\s*:/,
|
|
186
|
+
reason: '禁止 fork bomb',
|
|
187
|
+
},
|
|
188
|
+
|
|
189
|
+
// ---- 管道到 shell(典型恶意安装脚本模式) ----
|
|
190
|
+
// curl ... | sh / wget ... | bash / curl ... | python
|
|
191
|
+
{
|
|
192
|
+
pattern: /\b(?:curl|wget|fetch)\b[^|;&]*\|\s*(?:sh|bash|zsh|dash|python|perl|ruby|node)\b/i,
|
|
193
|
+
reason: '禁止从网络管道到 shell 解释器(curl ... | sh 模式)',
|
|
194
|
+
},
|
|
195
|
+
|
|
196
|
+
// ---- chmod 整盘 777 ----
|
|
197
|
+
{
|
|
198
|
+
pattern: /\bchmod\s+(?:-R\s+)?[0-7]*7[0-7]*\s+\/(?:\s|$)/,
|
|
199
|
+
reason: '禁止对根目录 chmod 777',
|
|
200
|
+
},
|
|
201
|
+
|
|
202
|
+
// ---- Windows 系统级删除 ----
|
|
203
|
+
{
|
|
204
|
+
pattern: /\b(?:del|erase)\s+\/[sS][^|;&]*[A-Za-z]:\\?\s*$/,
|
|
205
|
+
reason: '禁止 Windows 全盘删除(del /s 根目录)',
|
|
206
|
+
},
|
|
207
|
+
{
|
|
208
|
+
pattern: /\brmdir\s+\/[sS][^|;&]*[A-Za-z]:\\?\s*$/,
|
|
209
|
+
reason: '禁止 Windows 全盘 rmdir',
|
|
210
|
+
},
|
|
211
|
+
{
|
|
212
|
+
pattern: /\bdiskpart\b/i,
|
|
213
|
+
reason: '禁止 diskpart',
|
|
214
|
+
},
|
|
215
|
+
];
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* 检查命令是否命中黑名单。
|
|
219
|
+
* 返回命中的原因字符串,否则返回 null。
|
|
220
|
+
*/
|
|
221
|
+
export function checkForbidden(command: string): string | null {
|
|
222
|
+
for (const { pattern, reason } of FORBIDDEN_PATTERNS) {
|
|
223
|
+
if (pattern.test(command)) return reason;
|
|
224
|
+
}
|
|
225
|
+
return null;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* 子进程环境变量:从 process.env 复制后**剥离 agent 自己的密钥**
|
|
230
|
+
* (`MINIMAL_AGENT_*` / `TAVILY_*`)。
|
|
231
|
+
*
|
|
232
|
+
* 动机(安全):main.tsx 启动时会把 saved config 里的 key 注入 process.env。
|
|
233
|
+
* 若原样把 process.env 透传给 Bash 子进程,模型只要跑 `echo $MINIMAL_AGENT_API_KEY`
|
|
234
|
+
* 就能把 key(绿色版里是卖家预置的)读进 tool result、回传 LLM、甚至打到 -p stdout。
|
|
235
|
+
* 这些 env 是 agent 自身配置,子进程命令并不需要 —— 直接删掉,最小化泄露面。
|
|
236
|
+
* 其余变量(PATH / HOME / 用户自己 export 的)照常透传。
|
|
237
|
+
*
|
|
238
|
+
* 导出供测试断言。
|
|
239
|
+
*/
|
|
240
|
+
export function sanitizedChildEnv(
|
|
241
|
+
base: NodeJS.ProcessEnv = process.env,
|
|
242
|
+
): NodeJS.ProcessEnv {
|
|
243
|
+
const env = sanitizeCredentialEnv(base);
|
|
244
|
+
for (const key of Object.keys(env)) {
|
|
245
|
+
if (key.startsWith('MINIMAL_AGENT_') || key.startsWith('TAVILY_')) {
|
|
246
|
+
delete env[key];
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
// 保证子进程里的 Python 用 UTF-8 解码(PEP 540 UTF-8 Mode)。否则中文 Windows 上
|
|
250
|
+
// locale.getpreferredencoding() = cp936/GBK,`subprocess.run(text=True)` / `open()`
|
|
251
|
+
// 默认就用 GBK 解码子进程的 UTF-8 输出 → 撞非法字节必崩(曾致 ppt 工作流 build_probe
|
|
252
|
+
// `UnicodeDecodeError: 'gbk' codec can't decode byte 0xa8` 直接 halt 整条工作流)。
|
|
253
|
+
// 这是 Bash 工具「保证 UTF-8 世界」既有立场(cmd 回落已 chcp 65001)的加性延伸:
|
|
254
|
+
// 一处覆盖所有 bash 步 spawn 的 python(内联 heredoc + 技能脚本),现在与将来。
|
|
255
|
+
// 跨平台安全:类 Unix 本就 UTF-8 → no-op;顺带治 POSIX/C locale(ASCII) 同类坑。
|
|
256
|
+
env.PYTHONUTF8 = '1';
|
|
257
|
+
env.PYTHONIOENCODING = 'utf-8';
|
|
258
|
+
return env;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// ---------------- 1. Zod 输入 schema ----------------
|
|
262
|
+
const inputSchema = z.object({
|
|
263
|
+
command: z
|
|
264
|
+
.string()
|
|
265
|
+
.min(1, '必须提供 command')
|
|
266
|
+
.describe('要执行的 shell 命令(假定 bash 语法)'),
|
|
267
|
+
timeout: z
|
|
268
|
+
.number()
|
|
269
|
+
.int()
|
|
270
|
+
.positive()
|
|
271
|
+
.max(MAX_TIMEOUT_MS)
|
|
272
|
+
.optional()
|
|
273
|
+
.describe(`超时(毫秒),最多 ${MAX_TIMEOUT_MS}(${MAX_TIMEOUT_MS / 60_000} 分钟);不填默认 ${DEFAULT_TIMEOUT_MS}`),
|
|
274
|
+
description: z
|
|
275
|
+
.string()
|
|
276
|
+
.optional()
|
|
277
|
+
.describe('用一句话主动语态描述命令做什么(5-10 词),如 "List files in current directory"'),
|
|
278
|
+
run_in_background: z.boolean().optional().describe('长时间构建、测试或开发服务器设为 true;用 TaskOutput 获取增量输出、TaskStop 停止'),
|
|
279
|
+
});
|
|
280
|
+
type BashInput = z.infer<typeof inputSchema>;
|
|
281
|
+
|
|
282
|
+
// ---------------- 2. JSON Schema(由 Zod 自动派生) ----------------
|
|
283
|
+
const parameters = toToolParameters(inputSchema);
|
|
284
|
+
|
|
285
|
+
// ---------------- 3. Description(给 LLM 看的工具说明) ----------------
|
|
286
|
+
//
|
|
287
|
+
// 提炼自 kakadeai/tools/BashTool/prompt.ts 的 getSimplePrompt()。
|
|
288
|
+
// 核心逻辑:
|
|
289
|
+
// - 先点明"专用工具优先"的原则,把每类操作映射到对应工具
|
|
290
|
+
// - 再列 Bash 自身的使用规范(绝对路径 / 不 cd / 超时 / 多命令并行...)
|
|
291
|
+
// - 最后强调安全黑名单(让模型知道哪些命令会被拒,避免反复试)
|
|
292
|
+
const description = `Executes a given bash command and returns its output.
|
|
293
|
+
|
|
294
|
+
The working directory persists between commands, but shell state does not. The shell environment is initialized from the user's profile.
|
|
295
|
+
|
|
296
|
+
IMPORTANT: Avoid using this tool to run \`find\`, \`grep\`, \`cat\`, \`head\`, \`tail\`, \`sed\`, \`awk\`, or \`echo\` commands, unless explicitly instructed or after you have verified that a dedicated tool cannot accomplish your task. Instead, use the appropriate dedicated tool as this will provide a much better experience for the user:
|
|
297
|
+
|
|
298
|
+
- File search: Use Glob (NOT find or ls)
|
|
299
|
+
- Content search: Use Grep (NOT grep or rg)
|
|
300
|
+
- Read files: Use Read (NOT cat/head/tail)
|
|
301
|
+
- Edit files: Use Edit (NOT sed/awk)
|
|
302
|
+
- Write files: Use Write (NOT echo >/cat <<EOF)
|
|
303
|
+
- Communication: Output text directly (NOT echo/printf)
|
|
304
|
+
While the Bash tool can do similar things, it's better to use the built-in tools as they provide a better user experience and make it easier to review tool calls and give permission.
|
|
305
|
+
|
|
306
|
+
# Instructions
|
|
307
|
+
- If your command will create new directories or files, first use this tool to run \`ls\` to verify the parent directory exists and is the correct location.
|
|
308
|
+
- Always quote file paths that contain spaces with double quotes in your command (e.g., cd "path with spaces/file.txt").
|
|
309
|
+
- Try to maintain your current working directory throughout the session by using absolute paths and avoiding usage of \`cd\`. You may use \`cd\` if the User explicitly requests it.
|
|
310
|
+
- You may specify an optional timeout in milliseconds (up to ${MAX_TIMEOUT_MS}ms / ${MAX_TIMEOUT_MS / 60_000} minutes). By default, your command will timeout after ${DEFAULT_TIMEOUT_MS}ms (${DEFAULT_TIMEOUT_MS / 60_000} minutes).
|
|
311
|
+
- When issuing multiple commands:
|
|
312
|
+
- If the commands are independent and can run in parallel, make multiple Bash tool calls in a single message.
|
|
313
|
+
- If the commands depend on each other and must run sequentially, use a single Bash call with '&&' to chain them.
|
|
314
|
+
- Use ';' only when you need to run commands sequentially but don't care if earlier commands fail.
|
|
315
|
+
- DO NOT use newlines to separate commands (newlines are ok in quoted strings and here-strings).
|
|
316
|
+
- For git commands:
|
|
317
|
+
- Prefer to create a new commit rather than amending an existing commit.
|
|
318
|
+
- Before running destructive operations (e.g., git reset --hard, git push --force, git checkout --), consider whether there is a safer alternative that achieves the same goal. Only use destructive operations when they are truly the best approach.
|
|
319
|
+
- Never skip hooks (--no-verify) or bypass signing (--no-gpg-sign, -c commit.gpgsign=false) unless the user has explicitly asked for it. If a hook fails, investigate and fix the underlying issue.
|
|
320
|
+
- Avoid unnecessary \`sleep\` commands; do not retry failing commands in a sleep loop — diagnose the root cause.
|
|
321
|
+
|
|
322
|
+
# Viewing images
|
|
323
|
+
Read cannot open images. To look at one (screenshot, chart, photo), print a line by itself:
|
|
324
|
+
\`<<<ATTACH_IMAGE:/abs/path.png>>>\` — optionally \`|detail=low\` (cheap overview) and \`|maxLongSide=960\`.
|
|
325
|
+
The image is attached for you to see and the line is stripped from the output. Up to ${MAX_ATTACH_IMAGES} per call; the file must live under the working directory or the system temp directory.
|
|
326
|
+
|
|
327
|
+
# Exit code semantics
|
|
328
|
+
Some commands return non-zero exit codes for informational (non-error) reasons. Bash recognizes these and reports them as success (ok=true) — do NOT retry just because exit code is 1:
|
|
329
|
+
- \`grep\` / \`rg\` exit 1 → no match found (not an error)
|
|
330
|
+
- \`find\` exit 1 → some directories inaccessible (non-fatal, partial results still returned)
|
|
331
|
+
- \`diff\` / \`cmp\` exit 1 → files differ (informational, not an error)
|
|
332
|
+
- \`test\` / \`[\` exit 1 → condition is false (the answer to a question, not a failure)
|
|
333
|
+
Only exit codes ≥ 2 from these commands indicate a real failure. For all other commands, non-zero exit codes are treated as failures normally.
|
|
334
|
+
|
|
335
|
+
# Safety
|
|
336
|
+
The following command patterns are blocked at the tool level and will fail before execution (no need to try them):
|
|
337
|
+
- \`rm -rf /\` and variants targeting root, $HOME, ~, or system directories (/etc, /usr, /bin, /Windows, /Users, /home, ...)
|
|
338
|
+
- Filesystem destruction: \`mkfs\`, \`format X:\`, \`dd of=/dev/sdX\`, redirects to raw block devices
|
|
339
|
+
- Power management: \`shutdown\`, \`reboot\`, \`halt\`, \`poweroff\`, \`init 0/6\`
|
|
340
|
+
- Fork bombs
|
|
341
|
+
- Pipe-to-shell from network: \`curl ... | sh\`, \`wget ... | bash\`, etc.
|
|
342
|
+
- \`chmod 777 /\`, Windows full-disk \`del /s\` / \`rmdir /s\`, \`diskpart\`
|
|
343
|
+
|
|
344
|
+
If you have a legitimate use case that requires one of the above patterns, ask the user to run the command themselves in their terminal — do not try to bypass the check.
|
|
345
|
+
|
|
346
|
+
Separately, Bash scans for common destructive-but-recoverable patterns (\`git reset --hard\`, \`git push -f\` / \`--force\` / \`--force-with-lease\`, \`git checkout .\`, \`git restore .\`, \`git clean -f\`, \`git stash drop/clear\`, \`git branch -D\`, \`git commit --amend\` / \`--no-verify\`, \`rm -rf <path>\`, \`DROP TABLE\`, \`TRUNCATE\`, \`DELETE FROM\`, \`kubectl delete\`, \`terraform destroy\`, etc.) and prepends a \`⚠️ 警告:\` line to the output. These commands are NOT blocked — the warning is informational. Treat it as a signal to double-check intent and surface the warning to the user when relevant.${
|
|
347
|
+
process.platform !== 'win32'
|
|
348
|
+
? ''
|
|
349
|
+
: WIN_BASH
|
|
350
|
+
? `
|
|
351
|
+
|
|
352
|
+
# Shell (this machine)
|
|
353
|
+
Commands run in a real bash (Git Bash at ${WIN_BASH}). POSIX syntax works: \`mkdir -p\`, \`$VAR\`, \`$(...)\`, \`&&\`, \`[ -f x ]\` are all fine. Note the working directory is a Windows path — both \`D:\\path\` and \`/d/path\` forms work in this bash.`
|
|
354
|
+
: `
|
|
355
|
+
|
|
356
|
+
# Shell (this machine) — IMPORTANT
|
|
357
|
+
No bash found on this Windows machine: commands run in cmd.exe (UTF-8 enforced). POSIX syntax does NOT work here:
|
|
358
|
+
- NO \`mkdir -p\` — cmd's \`mkdir a\\b\\c\` creates intermediate directories by default; better: the Write tool auto-creates parent directories
|
|
359
|
+
- NO \`$VAR\` / \`$(...)\` / \`[ -f x ]\` / \`export\` — use \`%VAR%\`, \`if exist\`, \`set\`
|
|
360
|
+
- Use \`dir\` / \`copy\` / \`del\` / \`type\` instead of \`ls\` / \`cp\` / \`rm\` / \`cat\`
|
|
361
|
+
\`&&\` and \`;\`-style chaining: \`&&\` works in cmd; \`;\` does not (use \`&\`).`
|
|
362
|
+
}`;
|
|
363
|
+
|
|
364
|
+
// ---------------- 4. call() —— 真正干活 ----------------
|
|
365
|
+
async function call(input: BashInput, signal?: AbortSignal, context?: ToolCallContext): Promise<ToolResult> {
|
|
366
|
+
const command = input.command;
|
|
367
|
+
const timeoutMs = Math.min(input.timeout ?? DEFAULT_TIMEOUT_MS, MAX_TIMEOUT_MS);
|
|
368
|
+
|
|
369
|
+
// 4a. 安全黑名单(命中即拒)
|
|
370
|
+
const forbiddenReason = checkForbidden(command);
|
|
371
|
+
if (forbiddenReason) {
|
|
372
|
+
return {
|
|
373
|
+
ok: false,
|
|
374
|
+
error: `安全检查拒绝执行:${forbiddenReason}\n命令:${command}`,
|
|
375
|
+
};
|
|
376
|
+
}
|
|
377
|
+
const permanentDelete = permanentDeleteReason(command, 'bash');
|
|
378
|
+
if (permanentDelete) return { ok:false, error:`永久删除检查拒绝执行:${permanentDelete}\n命令:${command}` };
|
|
379
|
+
|
|
380
|
+
// 4a-1. 不可逆动作闸门:毁掉「最后一份副本」的命令要**停下来问人**。
|
|
381
|
+
//
|
|
382
|
+
// 与上面黑名单的分工:黑名单是「灾难且无正当用途」→ 直接拒;这里是
|
|
383
|
+
// 「不可逆但有正当用途」(force push / 删库 / 毁 reflog)→ 问一句。
|
|
384
|
+
// 判据与规则表都在内核 `src/irreversible/rules.ts`,与 ComputerUse 共用。
|
|
385
|
+
//
|
|
386
|
+
// ★ 选项顺序不能改:askBridge 在**没有交互宿主**时(`-p`、cron、子 agent)
|
|
387
|
+
// 自动选第一项。所以第一项必须是「取消」——无人可问时 fail-closed。
|
|
388
|
+
const irreversible = classifyCommand(command);
|
|
389
|
+
if (irreversible) {
|
|
390
|
+
const [answer] = await requestUserChoice(
|
|
391
|
+
[
|
|
392
|
+
{
|
|
393
|
+
question: `这条命令不可逆:${irreversible.why}。\n\n命令:${command}\n\n确认执行吗?`,
|
|
394
|
+
header: '不可逆操作',
|
|
395
|
+
options: [
|
|
396
|
+
{ label: '取消,不执行 (推荐)', description: '放弃这一步,请 AI 换个可回滚的做法' },
|
|
397
|
+
{ label: '我已确认后果,执行', description: '我清楚这一步做完找不回来' },
|
|
398
|
+
],
|
|
399
|
+
},
|
|
400
|
+
],
|
|
401
|
+
signal,
|
|
402
|
+
);
|
|
403
|
+
const approved = answer?.selected?.[0]?.includes('执行') === true
|
|
404
|
+
&& answer.selected[0]?.includes('取消') !== true;
|
|
405
|
+
if (!approved) {
|
|
406
|
+
return {
|
|
407
|
+
ok: false,
|
|
408
|
+
// 明确要求模型**停下**而不是改写命令绕过去。没有这句,模型收到拒绝后
|
|
409
|
+
// 极可能把 `--force` 换成 `-f`、把 `push --force` 换成 `push origin +main`
|
|
410
|
+
// 再试一次 —— 那既绕过了闸门,也不是用户想要的。
|
|
411
|
+
error:
|
|
412
|
+
`已被用户/策略拒绝执行(不可逆操作:${irreversible.why})。\n` +
|
|
413
|
+
`命令:${command}\n\n` +
|
|
414
|
+
`请**不要**改写命令重试(换 -f、换 +refspec、拆成多条都算绕过)。` +
|
|
415
|
+
`改用可回滚的做法,或直接把这一步交还给用户手动执行。`,
|
|
416
|
+
};
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
// 4a-2. 破坏性命令告警扫描(仅警告,不阻塞)
|
|
421
|
+
// 命中后把警告挂到结果输出顶部,让模型/用户看见苗头。
|
|
422
|
+
const destructiveWarning = scanDestructiveCommand(command);
|
|
423
|
+
|
|
424
|
+
if (input.run_in_background) {
|
|
425
|
+
try {
|
|
426
|
+
const cwd = context?.cwd ?? getWorkingDir();
|
|
427
|
+
const task = await startBackgroundTask(
|
|
428
|
+
WIN_BASH
|
|
429
|
+
? { command, executable: WIN_BASH, args: ['-c', command], cwd, env: sanitizedChildEnv(), sessionId: context?.sessionId, workspaceId: context?.workspaceId, shellKind: 'bash' as const }
|
|
430
|
+
: {
|
|
431
|
+
command: process.platform === 'win32' ? `chcp 65001 >nul & ${command}` : command,
|
|
432
|
+
cwd,
|
|
433
|
+
env: sanitizedChildEnv(),
|
|
434
|
+
shell: true,
|
|
435
|
+
sessionId: context?.sessionId,
|
|
436
|
+
workspaceId: context?.workspaceId,
|
|
437
|
+
shellKind: 'bash' as const,
|
|
438
|
+
},
|
|
439
|
+
context?.sessionState?.backgroundTasks,
|
|
440
|
+
);
|
|
441
|
+
return {
|
|
442
|
+
ok: true,
|
|
443
|
+
content: `${destructiveWarning ? `⚠️ 警告: ${destructiveWarning}\n` : ''}后台任务已启动。task_id=${task.id.replace(/^proc_/,'')}\nprocess_id=${task.id}\nlog=${task.logPath}\n使用 TaskOutput 增量读取输出,TaskStop 停止进程。`,
|
|
444
|
+
metadata: processExecutionMetadata({ shell: 'bash', mode: 'background', command, status: 'started', taskId: task.id }),
|
|
445
|
+
};
|
|
446
|
+
} catch (error) {
|
|
447
|
+
return { ok: false, error: `后台命令启动失败:${(error as Error).message}` };
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
// 4b. spawn —— 平台分派(见 0b 探测注释):
|
|
452
|
+
// Unix: /bin/sh -c <command>(shell:true)
|
|
453
|
+
// Windows + Git Bash: bash.exe -c <command>(POSIX 语法成立)
|
|
454
|
+
// Windows 无 bash: cmd.exe,命令前缀 chcp 65001 强制 UTF-8 输出
|
|
455
|
+
let stdout = '';
|
|
456
|
+
let stderr = '';
|
|
457
|
+
const outputStore = new ToolOutputStore({
|
|
458
|
+
workspaceId: context?.workspaceId,
|
|
459
|
+
sessionId: context?.sessionId,
|
|
460
|
+
});
|
|
461
|
+
let exitCode: number | null = null;
|
|
462
|
+
let timedOut = false;
|
|
463
|
+
let killedBySignal = false;
|
|
464
|
+
|
|
465
|
+
// 用 AbortController 把外部 signal + 我们自己的 timeout 合并成一个
|
|
466
|
+
const ac = new AbortController();
|
|
467
|
+
const onAbort = () => ac.abort();
|
|
468
|
+
signal?.addEventListener('abort', onAbort, { once: true });
|
|
469
|
+
const timer = setTimeout(() => {
|
|
470
|
+
timedOut = true;
|
|
471
|
+
ac.abort();
|
|
472
|
+
}, timeoutMs);
|
|
473
|
+
|
|
474
|
+
try {
|
|
475
|
+
await new Promise<void>((resolveP, rejectP) => {
|
|
476
|
+
const spawnOpts = {
|
|
477
|
+
cwd: context?.cwd ?? getWorkingDir(),
|
|
478
|
+
env: sanitizedChildEnv(),
|
|
479
|
+
windowsHide: true,
|
|
480
|
+
// POSIX:detached 自成进程组,中断时 killTree 的 kill(-pid) 才能掀整组;win32 不 detached(taskkill /T 掀树)。
|
|
481
|
+
detached: process.platform !== 'win32',
|
|
482
|
+
};
|
|
483
|
+
const child = WIN_BASH
|
|
484
|
+
? spawn(WIN_BASH, ['-c', command], spawnOpts)
|
|
485
|
+
: spawn(
|
|
486
|
+
process.platform === 'win32'
|
|
487
|
+
? `chcp 65001 >nul & ${command}`
|
|
488
|
+
: command,
|
|
489
|
+
{ ...spawnOpts, shell: true },
|
|
490
|
+
);
|
|
491
|
+
|
|
492
|
+
// 中断/超时收尾:杀**整棵进程树**(不是只杀直接 shell)。否则 win32 上 shell 被杀后,在飞的
|
|
493
|
+
// 孙进程(bun test / 构建 / 起服务…)变孤儿继续跑、还占着 stdout 管道让 'close' 永不触发 ——
|
|
494
|
+
// 表现为「点了停止、agent 停了、活儿还在后台跑」的"停不下来"(与 2026-07-02 生图事故同源)。
|
|
495
|
+
// 不再把 signal 交给 spawn 自己 kill(那只杀直接子进程,还会抢在 killTree 前把 shell 杀掉、
|
|
496
|
+
// 让 taskkill /T 够不到已成孤儿的孙进程);改由这里统一 killTree + 立即 resolve,不空等 'close'。
|
|
497
|
+
const onKill = () => {
|
|
498
|
+
killedBySignal = true;
|
|
499
|
+
killTree(child);
|
|
500
|
+
resolveP();
|
|
501
|
+
};
|
|
502
|
+
if (ac.signal.aborted) onKill();
|
|
503
|
+
else ac.signal.addEventListener('abort', onKill, { once: true });
|
|
504
|
+
|
|
505
|
+
child.stdout?.setEncoding('utf8');
|
|
506
|
+
child.stderr?.setEncoding('utf8');
|
|
507
|
+
child.stdout?.on('data', (chunk) => {
|
|
508
|
+
if (!outputStore.append('stdout', chunk)) {
|
|
509
|
+
child.stdout?.pause();
|
|
510
|
+
outputStore.onDrain(() => child.stdout?.resume());
|
|
511
|
+
}
|
|
512
|
+
});
|
|
513
|
+
child.stderr?.on('data', (chunk) => {
|
|
514
|
+
if (!outputStore.append('stderr', chunk)) {
|
|
515
|
+
child.stderr?.pause();
|
|
516
|
+
outputStore.onDrain(() => child.stderr?.resume());
|
|
517
|
+
}
|
|
518
|
+
});
|
|
519
|
+
child.on('error', (e) => {
|
|
520
|
+
ac.signal.removeEventListener('abort', onKill);
|
|
521
|
+
rejectP(e);
|
|
522
|
+
});
|
|
523
|
+
child.on('close', (code, sig) => {
|
|
524
|
+
ac.signal.removeEventListener('abort', onKill);
|
|
525
|
+
exitCode = code;
|
|
526
|
+
if (sig) killedBySignal = true;
|
|
527
|
+
resolveP();
|
|
528
|
+
});
|
|
529
|
+
});
|
|
530
|
+
} catch (e) {
|
|
531
|
+
return {
|
|
532
|
+
ok: false,
|
|
533
|
+
error: `执行命令失败:${(e as Error).message}\n命令:${command}`,
|
|
534
|
+
metadata: processExecutionMetadata({ shell: 'bash', mode: 'foreground', command, status: 'failed', exitCode: null }),
|
|
535
|
+
};
|
|
536
|
+
} finally {
|
|
537
|
+
clearTimeout(timer);
|
|
538
|
+
signal?.removeEventListener('abort', onAbort);
|
|
539
|
+
}
|
|
540
|
+
const storedOutput = await outputStore.finish();
|
|
541
|
+
stdout = storedOutput.stdout;
|
|
542
|
+
stderr = storedOutput.stderr;
|
|
543
|
+
|
|
544
|
+
// 4b-2. ATTACH_IMAGE sentinel:脚本可以打一行 `<<<ATTACH_IMAGE:path>>>` 让模型亲眼看图。
|
|
545
|
+
// 协议行**从 content 剥掉**(那是给宿主看的,不是给模型读的噪声);被拒的条目
|
|
546
|
+
// 如实写进输出(静默丢弃会让脚本作者以为图发出去了,无从 debug)。
|
|
547
|
+
// `ToolResult` 的成功与失败分支都能带 visionImages,所以脚本**不必**为了
|
|
548
|
+
// 附图而假装成功——非零退出码照样能把图带给模型。
|
|
549
|
+
const attach = extractAttachImageDirectives(stdout);
|
|
550
|
+
stdout = attach.text;
|
|
551
|
+
const { images: visionImages, rejections: attachRejections } =
|
|
552
|
+
await resolveAttachImages(attach.directives);
|
|
553
|
+
|
|
554
|
+
// 4c. 拼输出
|
|
555
|
+
const parts: string[] = [];
|
|
556
|
+
if (stdout) parts.push(`<stdout>\n${stdout.replace(/\s+$/, '')}\n</stdout>`);
|
|
557
|
+
if (stderr) parts.push(`<stderr>\n${stderr.replace(/\s+$/, '')}\n</stderr>`);
|
|
558
|
+
if (visionImages.length > 0) {
|
|
559
|
+
parts.push(`[已附加 ${visionImages.length} 张图片供你查看]`);
|
|
560
|
+
}
|
|
561
|
+
if (attachRejections.length > 0) {
|
|
562
|
+
parts.push(
|
|
563
|
+
`[附图被拒绝]\n${attachRejections.map((r) => `- ${r}`).join('\n')}`,
|
|
564
|
+
);
|
|
565
|
+
}
|
|
566
|
+
if (parts.length === 0) parts.push('<no output>');
|
|
567
|
+
|
|
568
|
+
if (timedOut) {
|
|
569
|
+
parts.push(`\n[超时 ${timeoutMs}ms 后被中断]`);
|
|
570
|
+
} else if (killedBySignal && !timedOut) {
|
|
571
|
+
parts.push(`\n[被信号中断]`);
|
|
572
|
+
}
|
|
573
|
+
parts.push(`\n[exit code: ${exitCode === null ? 'killed' : exitCode}]`);
|
|
574
|
+
const outputReference = storedOutput.path
|
|
575
|
+
? `\n[完整输出: ${storedOutput.path}; bytes=${storedOutput.bytes}; sha256=${storedOutput.sha256}; complete=${storedOutput.complete}]`
|
|
576
|
+
: '';
|
|
577
|
+
|
|
578
|
+
let combinedOutput = parts.join('\n');
|
|
579
|
+
|
|
580
|
+
// 4d. 大小兜底
|
|
581
|
+
if (combinedOutput.length + outputReference.length > DEFAULT_MAX_RESULT_SIZE_CHARS) {
|
|
582
|
+
const suffix = `\n\n... (输出超过 ${DEFAULT_MAX_RESULT_SIZE_CHARS} 字符,预览已截断)${outputReference}`;
|
|
583
|
+
combinedOutput =
|
|
584
|
+
combinedOutput.slice(0, Math.max(0, DEFAULT_MAX_RESULT_SIZE_CHARS - suffix.length)) + suffix;
|
|
585
|
+
} else combinedOutput += outputReference;
|
|
586
|
+
|
|
587
|
+
// 4e. 中断 / 超时仍然算硬失败
|
|
588
|
+
// 失败也带上已解析的附图:脚本挂掉前截的那张诊断图正是最该看的。
|
|
589
|
+
if (timedOut || killedBySignal) {
|
|
590
|
+
return {
|
|
591
|
+
ok: false,
|
|
592
|
+
error: combinedOutput,
|
|
593
|
+
metadata: processExecutionMetadata({ shell: 'bash', mode: 'foreground', command, status: 'failed', exitCode }),
|
|
594
|
+
...(visionImages.length > 0 ? { visionImages } : {}),
|
|
595
|
+
};
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
// 4f. 语义化退出码判定
|
|
599
|
+
// 交给 semantics 模块:grep/rg/find/diff/test 有"信息性退出码",
|
|
600
|
+
// 其它命令用默认语义(exitCode !== 0 即错)。
|
|
601
|
+
// destructiveWarning 命中时把告警贴在输出头部,模型/用户都能看见。
|
|
602
|
+
const semantic = interpretCommandResult(
|
|
603
|
+
command,
|
|
604
|
+
exitCode ?? 0,
|
|
605
|
+
stdout,
|
|
606
|
+
stderr,
|
|
607
|
+
);
|
|
608
|
+
const finalContent = destructiveWarning
|
|
609
|
+
? `⚠️ 警告: ${destructiveWarning}\n\n${combinedOutput}`
|
|
610
|
+
: combinedOutput;
|
|
611
|
+
|
|
612
|
+
if (semantic.isError) {
|
|
613
|
+
return {
|
|
614
|
+
ok: false,
|
|
615
|
+
error:
|
|
616
|
+
`命令失败 (exit ${exitCode}): ${stderr || stdout || semantic.message || ''}`.trim() +
|
|
617
|
+
`\n\n${finalContent}`,
|
|
618
|
+
metadata: processExecutionMetadata({ shell: 'bash', mode: 'foreground', command, status: 'failed', exitCode }),
|
|
619
|
+
...(visionImages.length > 0 ? { visionImages } : {}),
|
|
620
|
+
};
|
|
621
|
+
}
|
|
622
|
+
return {
|
|
623
|
+
ok: true,
|
|
624
|
+
content: finalContent,
|
|
625
|
+
metadata: processExecutionMetadata({ shell: 'bash', mode: 'foreground', command, status: 'completed', exitCode }),
|
|
626
|
+
...(visionImages.length > 0 ? { visionImages } : {}),
|
|
627
|
+
};
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
// ---------------- 5. 导出 ----------------
|
|
631
|
+
export const bashTool: Tool<BashInput> = {
|
|
632
|
+
name: 'Bash',
|
|
633
|
+
description,
|
|
634
|
+
inputSchema,
|
|
635
|
+
parameters,
|
|
636
|
+
isReadOnly: false,
|
|
637
|
+
isConcurrencySafe: false, // 默认按"会改状态"算;保守不并发
|
|
638
|
+
maxResultSizeChars: DEFAULT_MAX_RESULT_SIZE_CHARS,
|
|
639
|
+
interruptBehavior: () => 'block',
|
|
640
|
+
call: (input, signal) => call(input, signal),
|
|
641
|
+
callWithContext: (input, signal, context) => call(input, signal, context),
|
|
642
|
+
};
|