@cjhyy/code-shell-core 0.5.0-rc.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/LICENSE +21 -0
- package/README.md +162 -0
- package/dist/agent/coordinator.d.ts +49 -0
- package/dist/agent/coordinator.js +77 -0
- package/dist/arena/arena.d.ts +43 -0
- package/dist/arena/arena.js +334 -0
- package/dist/arena/context/context-tools.d.ts +16 -0
- package/dist/arena/context/context-tools.js +231 -0
- package/dist/arena/detect-mode.d.ts +20 -0
- package/dist/arena/detect-mode.js +78 -0
- package/dist/arena/digest-builder.d.ts +25 -0
- package/dist/arena/digest-builder.js +120 -0
- package/dist/arena/index.d.ts +29 -0
- package/dist/arena/index.js +29 -0
- package/dist/arena/iterate/convergence.d.ts +25 -0
- package/dist/arena/iterate/convergence.js +103 -0
- package/dist/arena/iterate/formats/index.d.ts +22 -0
- package/dist/arena/iterate/formats/index.js +283 -0
- package/dist/arena/iterate/index.d.ts +11 -0
- package/dist/arena/iterate/index.js +9 -0
- package/dist/arena/iterate/iterative-arena.d.ts +23 -0
- package/dist/arena/iterate/iterative-arena.js +237 -0
- package/dist/arena/iterate/parse.d.ts +42 -0
- package/dist/arena/iterate/parse.js +123 -0
- package/dist/arena/iterate/phases/argue.d.ts +22 -0
- package/dist/arena/iterate/phases/argue.js +159 -0
- package/dist/arena/iterate/phases/revise.d.ts +16 -0
- package/dist/arena/iterate/phases/revise.js +62 -0
- package/dist/arena/iterate/phases/tournament.d.ts +34 -0
- package/dist/arena/iterate/phases/tournament.js +113 -0
- package/dist/arena/iterate/tools/web-tools.d.ts +13 -0
- package/dist/arena/iterate/tools/web-tools.js +54 -0
- package/dist/arena/iterate/types.d.ts +152 -0
- package/dist/arena/iterate/types.js +8 -0
- package/dist/arena/ledger.d.ts +47 -0
- package/dist/arena/ledger.js +151 -0
- package/dist/arena/lenses/architecture.d.ts +5 -0
- package/dist/arena/lenses/architecture.js +22 -0
- package/dist/arena/lenses/engineering.d.ts +5 -0
- package/dist/arena/lenses/engineering.js +22 -0
- package/dist/arena/lenses/general.d.ts +5 -0
- package/dist/arena/lenses/general.js +20 -0
- package/dist/arena/lenses/index.d.ts +16 -0
- package/dist/arena/lenses/index.js +47 -0
- package/dist/arena/lenses/product.d.ts +5 -0
- package/dist/arena/lenses/product.js +22 -0
- package/dist/arena/model-presets.d.ts +23 -0
- package/dist/arena/model-presets.js +44 -0
- package/dist/arena/phases/adjudication.d.ts +24 -0
- package/dist/arena/phases/adjudication.js +144 -0
- package/dist/arena/phases/build-consensus.d.ts +29 -0
- package/dist/arena/phases/build-consensus.js +86 -0
- package/dist/arena/phases/claim-registry.d.ts +26 -0
- package/dist/arena/phases/claim-registry.js +60 -0
- package/dist/arena/phases/cross-review.d.ts +45 -0
- package/dist/arena/phases/cross-review.js +224 -0
- package/dist/arena/phases/debate-rounds.d.ts +27 -0
- package/dist/arena/phases/debate-rounds.js +162 -0
- package/dist/arena/phases/participant-research.d.ts +38 -0
- package/dist/arena/phases/participant-research.js +317 -0
- package/dist/arena/phases/planning-detail-expansion.d.ts +38 -0
- package/dist/arena/phases/planning-detail-expansion.js +121 -0
- package/dist/arena/planner.d.ts +27 -0
- package/dist/arena/planner.js +312 -0
- package/dist/arena/providers/docs.d.ts +6 -0
- package/dist/arena/providers/docs.js +108 -0
- package/dist/arena/providers/git.d.ts +8 -0
- package/dist/arena/providers/git.js +174 -0
- package/dist/arena/providers/index.d.ts +32 -0
- package/dist/arena/providers/index.js +132 -0
- package/dist/arena/providers/none.d.ts +7 -0
- package/dist/arena/providers/none.js +11 -0
- package/dist/arena/providers/repo.d.ts +6 -0
- package/dist/arena/providers/repo.js +255 -0
- package/dist/arena/render/session.d.ts +17 -0
- package/dist/arena/render/session.js +190 -0
- package/dist/arena/render/terminal.d.ts +34 -0
- package/dist/arena/render/terminal.js +286 -0
- package/dist/arena/strategies/discussion.d.ts +25 -0
- package/dist/arena/strategies/discussion.js +143 -0
- package/dist/arena/strategies/index.d.ts +15 -0
- package/dist/arena/strategies/index.js +28 -0
- package/dist/arena/strategies/language-wrapper.d.ts +17 -0
- package/dist/arena/strategies/language-wrapper.js +102 -0
- package/dist/arena/strategies/lens-wrapper.d.ts +16 -0
- package/dist/arena/strategies/lens-wrapper.js +236 -0
- package/dist/arena/strategies/planning.d.ts +30 -0
- package/dist/arena/strategies/planning.js +225 -0
- package/dist/arena/strategies/review.d.ts +26 -0
- package/dist/arena/strategies/review.js +168 -0
- package/dist/arena/strategies/utils.d.ts +39 -0
- package/dist/arena/strategies/utils.js +647 -0
- package/dist/arena/tools/selector.d.ts +17 -0
- package/dist/arena/tools/selector.js +61 -0
- package/dist/arena/transitions.d.ts +48 -0
- package/dist/arena/transitions.js +92 -0
- package/dist/arena/types.d.ts +508 -0
- package/dist/arena/types.js +27 -0
- package/dist/cli/agent-server-stdio.d.ts +32 -0
- package/dist/cli/agent-server-stdio.js +116 -0
- package/dist/colorizer.d.ts +23 -0
- package/dist/colorizer.js +12 -0
- package/dist/context/compaction.d.ts +116 -0
- package/dist/context/compaction.js +534 -0
- package/dist/context/manager.d.ts +145 -0
- package/dist/context/manager.js +423 -0
- package/dist/context/token-counter.d.ts +27 -0
- package/dist/context/token-counter.js +91 -0
- package/dist/context/tool-result-storage.d.ts +96 -0
- package/dist/context/tool-result-storage.js +296 -0
- package/dist/cost-tracker.d.ts +64 -0
- package/dist/cost-tracker.js +314 -0
- package/dist/cron/scheduler.d.ts +34 -0
- package/dist/cron/scheduler.js +115 -0
- package/dist/data/deepseek-models.json +46 -0
- package/dist/data/gemini-models.json +67 -0
- package/dist/data/openai-models.json +175 -0
- package/dist/data/openrouter-models.d.ts +35 -0
- package/dist/data/openrouter-models.js +36 -0
- package/dist/data/openrouter-models.json +4626 -0
- package/dist/data/openrouter-sync.d.ts +16 -0
- package/dist/data/openrouter-sync.js +53 -0
- package/dist/data/static-catalogs.d.ts +25 -0
- package/dist/data/static-catalogs.js +26 -0
- package/dist/data/zai-models.json +56 -0
- package/dist/engine/cost-store.d.ts +22 -0
- package/dist/engine/cost-store.js +15 -0
- package/dist/engine/engine.d.ts +295 -0
- package/dist/engine/engine.js +1467 -0
- package/dist/engine/model-facade.d.ts +24 -0
- package/dist/engine/model-facade.js +194 -0
- package/dist/engine/parse-task.d.ts +59 -0
- package/dist/engine/parse-task.js +135 -0
- package/dist/engine/patch-orphaned-tools.d.ts +52 -0
- package/dist/engine/patch-orphaned-tools.js +95 -0
- package/dist/engine/query.d.ts +50 -0
- package/dist/engine/query.js +107 -0
- package/dist/engine/runtime.d.ts +50 -0
- package/dist/engine/runtime.js +59 -0
- package/dist/engine/streaming-tool-queue.d.ts +34 -0
- package/dist/engine/streaming-tool-queue.js +61 -0
- package/dist/engine/token-budget.d.ts +22 -0
- package/dist/engine/token-budget.js +40 -0
- package/dist/engine/tool-summary.d.ts +13 -0
- package/dist/engine/tool-summary.js +35 -0
- package/dist/engine/turn-loop.d.ts +122 -0
- package/dist/engine/turn-loop.js +602 -0
- package/dist/engine/turn-state.d.ts +20 -0
- package/dist/engine/turn-state.js +17 -0
- package/dist/exceptions.d.ts +61 -0
- package/dist/exceptions.js +116 -0
- package/dist/git/utils.d.ts +35 -0
- package/dist/git/utils.js +98 -0
- package/dist/git/worktree.d.ts +36 -0
- package/dist/git/worktree.js +139 -0
- package/dist/hooks/events.d.ts +114 -0
- package/dist/hooks/events.js +4 -0
- package/dist/hooks/inject.d.ts +19 -0
- package/dist/hooks/inject.js +29 -0
- package/dist/hooks/registry.d.ts +15 -0
- package/dist/hooks/registry.js +84 -0
- package/dist/hooks/shell-runner.d.ts +45 -0
- package/dist/hooks/shell-runner.js +173 -0
- package/dist/index.d.ts +117 -0
- package/dist/index.js +145 -0
- package/dist/llm/api-key-sanitize.d.ts +22 -0
- package/dist/llm/api-key-sanitize.js +81 -0
- package/dist/llm/capabilities/index.d.ts +20 -0
- package/dist/llm/capabilities/index.js +39 -0
- package/dist/llm/capabilities/rules.d.ts +26 -0
- package/dist/llm/capabilities/rules.js +215 -0
- package/dist/llm/capabilities/types.d.ts +114 -0
- package/dist/llm/capabilities/types.js +24 -0
- package/dist/llm/client-base.d.ts +29 -0
- package/dist/llm/client-base.js +137 -0
- package/dist/llm/client-factory.d.ts +10 -0
- package/dist/llm/client-factory.js +29 -0
- package/dist/llm/model-cache.d.ts +21 -0
- package/dist/llm/model-cache.js +46 -0
- package/dist/llm/model-fetcher.d.ts +29 -0
- package/dist/llm/model-fetcher.js +282 -0
- package/dist/llm/model-pool.d.ts +95 -0
- package/dist/llm/model-pool.js +246 -0
- package/dist/llm/provider-catalog.d.ts +32 -0
- package/dist/llm/provider-catalog.js +50 -0
- package/dist/llm/provider-kinds.d.ts +21 -0
- package/dist/llm/provider-kinds.js +132 -0
- package/dist/llm/providers/anthropic.d.ts +19 -0
- package/dist/llm/providers/anthropic.js +267 -0
- package/dist/llm/providers/openai.d.ts +64 -0
- package/dist/llm/providers/openai.js +606 -0
- package/dist/llm/retry.d.ts +12 -0
- package/dist/llm/retry.js +17 -0
- package/dist/llm/stream-watchdog.d.ts +42 -0
- package/dist/llm/stream-watchdog.js +70 -0
- package/dist/llm/token-counter.d.ts +17 -0
- package/dist/llm/token-counter.js +36 -0
- package/dist/llm/types.d.ts +37 -0
- package/dist/llm/types.js +4 -0
- package/dist/logging/logger.d.ts +109 -0
- package/dist/logging/logger.js +390 -0
- package/dist/logging/sanitize-messages.d.ts +42 -0
- package/dist/logging/sanitize-messages.js +150 -0
- package/dist/logging/session-recorder.d.ts +90 -0
- package/dist/logging/session-recorder.js +304 -0
- package/dist/lsp/client.d.ts +41 -0
- package/dist/lsp/client.js +166 -0
- package/dist/lsp/manager.d.ts +40 -0
- package/dist/lsp/manager.js +124 -0
- package/dist/lsp/servers.d.ts +16 -0
- package/dist/lsp/servers.js +60 -0
- package/dist/migrate-models.d.ts +45 -0
- package/dist/migrate-models.js +179 -0
- package/dist/onboarding.d.ts +189 -0
- package/dist/onboarding.js +625 -0
- package/dist/plugins/gitOps.d.ts +32 -0
- package/dist/plugins/gitOps.js +62 -0
- package/dist/plugins/installedPlugins.d.ts +15 -0
- package/dist/plugins/installedPlugins.js +56 -0
- package/dist/plugins/knownMarketplaces.d.ts +10 -0
- package/dist/plugins/knownMarketplaces.js +46 -0
- package/dist/plugins/loadPluginHooks.d.ts +52 -0
- package/dist/plugins/loadPluginHooks.js +162 -0
- package/dist/plugins/marketplaceManager.d.ts +49 -0
- package/dist/plugins/marketplaceManager.js +142 -0
- package/dist/plugins/parseMarketplaceInput.d.ts +16 -0
- package/dist/plugins/parseMarketplaceInput.js +73 -0
- package/dist/plugins/pluginCommandHook.d.ts +54 -0
- package/dist/plugins/pluginCommandHook.js +180 -0
- package/dist/plugins/pluginCommandsLoader.d.ts +25 -0
- package/dist/plugins/pluginCommandsLoader.js +114 -0
- package/dist/plugins/pluginInstaller.d.ts +36 -0
- package/dist/plugins/pluginInstaller.js +228 -0
- package/dist/plugins/schemas.d.ts +9 -0
- package/dist/plugins/schemas.js +136 -0
- package/dist/plugins/types.d.ts +75 -0
- package/dist/plugins/types.js +7 -0
- package/dist/plugins/varRewrite.d.ts +42 -0
- package/dist/plugins/varRewrite.js +129 -0
- package/dist/preset/index.d.ts +60 -0
- package/dist/preset/index.js +159 -0
- package/dist/product/define.d.ts +60 -0
- package/dist/product/define.js +123 -0
- package/dist/product/index.d.ts +5 -0
- package/dist/product/index.js +4 -0
- package/dist/product/types.d.ts +111 -0
- package/dist/product/types.js +21 -0
- package/dist/prompt/composer.d.ts +49 -0
- package/dist/prompt/composer.js +168 -0
- package/dist/prompt/instruction-scanner.d.ts +54 -0
- package/dist/prompt/instruction-scanner.js +187 -0
- package/dist/prompt/section-cache.d.ts +15 -0
- package/dist/prompt/section-cache.js +31 -0
- package/dist/prompt/section-loader.d.ts +26 -0
- package/dist/prompt/section-loader.js +53 -0
- package/dist/prompt/sections/base.md +35 -0
- package/dist/prompt/sections/coding.md +31 -0
- package/dist/prompt/sections/orchestration.md +16 -0
- package/dist/prompt/sections/tone.md +9 -0
- package/dist/protocol/chat-session-manager.d.ts +33 -0
- package/dist/protocol/chat-session-manager.js +65 -0
- package/dist/protocol/chat-session.d.ts +49 -0
- package/dist/protocol/chat-session.js +86 -0
- package/dist/protocol/client.d.ts +104 -0
- package/dist/protocol/client.js +260 -0
- package/dist/protocol/factories.d.ts +93 -0
- package/dist/protocol/factories.js +64 -0
- package/dist/protocol/helpers.d.ts +42 -0
- package/dist/protocol/helpers.js +55 -0
- package/dist/protocol/index.d.ts +4 -0
- package/dist/protocol/index.js +4 -0
- package/dist/protocol/server.d.ts +80 -0
- package/dist/protocol/server.js +855 -0
- package/dist/protocol/transport.d.ts +37 -0
- package/dist/protocol/transport.js +85 -0
- package/dist/protocol/types.d.ts +221 -0
- package/dist/protocol/types.js +61 -0
- package/dist/remote/bridge.d.ts +37 -0
- package/dist/remote/bridge.js +103 -0
- package/dist/run/ArtifactTracker.d.ts +38 -0
- package/dist/run/ArtifactTracker.js +132 -0
- package/dist/run/CheckpointWriter.d.ts +53 -0
- package/dist/run/CheckpointWriter.js +135 -0
- package/dist/run/EngineRunner.d.ts +72 -0
- package/dist/run/EngineRunner.js +119 -0
- package/dist/run/Evaluator.d.ts +56 -0
- package/dist/run/Evaluator.js +62 -0
- package/dist/run/FileRunStore.d.ts +39 -0
- package/dist/run/FileRunStore.js +176 -0
- package/dist/run/Heartbeat.d.ts +54 -0
- package/dist/run/Heartbeat.js +123 -0
- package/dist/run/RunApprovalBackend.d.ts +53 -0
- package/dist/run/RunApprovalBackend.js +85 -0
- package/dist/run/RunLock.d.ts +40 -0
- package/dist/run/RunLock.js +98 -0
- package/dist/run/RunManager.d.ts +82 -0
- package/dist/run/RunManager.js +587 -0
- package/dist/run/RunQueue.d.ts +27 -0
- package/dist/run/RunQueue.js +73 -0
- package/dist/run/RunStore.d.ts +25 -0
- package/dist/run/RunStore.js +9 -0
- package/dist/run/factory.d.ts +63 -0
- package/dist/run/factory.js +51 -0
- package/dist/run/index.d.ts +17 -0
- package/dist/run/index.js +23 -0
- package/dist/run/types.d.ts +136 -0
- package/dist/run/types.js +28 -0
- package/dist/runtime/safe-spawn.d.ts +118 -0
- package/dist/runtime/safe-spawn.js +268 -0
- package/dist/services/analytics.d.ts +29 -0
- package/dist/services/analytics.js +84 -0
- package/dist/services/auto-dream.d.ts +49 -0
- package/dist/services/auto-dream.js +124 -0
- package/dist/services/diagnostics.d.ts +41 -0
- package/dist/services/diagnostics.js +99 -0
- package/dist/services/extract-memories.d.ts +31 -0
- package/dist/services/extract-memories.js +71 -0
- package/dist/services/index.d.ts +11 -0
- package/dist/services/index.js +17 -0
- package/dist/services/memory-orchestrator.d.ts +60 -0
- package/dist/services/memory-orchestrator.js +133 -0
- package/dist/services/notifier.d.ts +25 -0
- package/dist/services/notifier.js +69 -0
- package/dist/services/oauth.d.ts +27 -0
- package/dist/services/oauth.js +153 -0
- package/dist/services/session-memory.d.ts +37 -0
- package/dist/services/session-memory.js +81 -0
- package/dist/session/file-history.d.ts +41 -0
- package/dist/session/file-history.js +115 -0
- package/dist/session/memory.d.ts +104 -0
- package/dist/session/memory.js +310 -0
- package/dist/session/session-manager.d.ts +41 -0
- package/dist/session/session-manager.js +261 -0
- package/dist/session/transcript.d.ts +39 -0
- package/dist/session/transcript.js +186 -0
- package/dist/settings/manager.d.ts +36 -0
- package/dist/settings/manager.js +187 -0
- package/dist/settings/schema.d.ts +1219 -0
- package/dist/settings/schema.js +246 -0
- package/dist/skills/frontmatter.d.ts +13 -0
- package/dist/skills/frontmatter.js +70 -0
- package/dist/skills/index.d.ts +7 -0
- package/dist/skills/index.js +6 -0
- package/dist/skills/scanner.d.ts +31 -0
- package/dist/skills/scanner.js +171 -0
- package/dist/state.d.ts +160 -0
- package/dist/state.js +267 -0
- package/dist/tool-system/builtin/agent-notifications.d.ts +84 -0
- package/dist/tool-system/builtin/agent-notifications.js +197 -0
- package/dist/tool-system/builtin/agent-registry.d.ts +67 -0
- package/dist/tool-system/builtin/agent-registry.js +126 -0
- package/dist/tool-system/builtin/agent-transcript-translator.d.ts +22 -0
- package/dist/tool-system/builtin/agent-transcript-translator.js +160 -0
- package/dist/tool-system/builtin/agent.d.ts +16 -0
- package/dist/tool-system/builtin/agent.js +364 -0
- package/dist/tool-system/builtin/apply-patch/applier.d.ts +26 -0
- package/dist/tool-system/builtin/apply-patch/applier.js +250 -0
- package/dist/tool-system/builtin/apply-patch/index.d.ts +20 -0
- package/dist/tool-system/builtin/apply-patch/index.js +101 -0
- package/dist/tool-system/builtin/apply-patch/parser.d.ts +17 -0
- package/dist/tool-system/builtin/apply-patch/parser.js +212 -0
- package/dist/tool-system/builtin/apply-patch/seek-sequence.d.ts +18 -0
- package/dist/tool-system/builtin/apply-patch/seek-sequence.js +123 -0
- package/dist/tool-system/builtin/apply-patch/types.d.ts +49 -0
- package/dist/tool-system/builtin/apply-patch/types.js +13 -0
- package/dist/tool-system/builtin/arena.d.ts +31 -0
- package/dist/tool-system/builtin/arena.js +416 -0
- package/dist/tool-system/builtin/ask-user.d.ts +20 -0
- package/dist/tool-system/builtin/ask-user.js +99 -0
- package/dist/tool-system/builtin/bash.d.ts +21 -0
- package/dist/tool-system/builtin/bash.js +149 -0
- package/dist/tool-system/builtin/brief.d.ts +6 -0
- package/dist/tool-system/builtin/brief.js +41 -0
- package/dist/tool-system/builtin/config.d.ts +6 -0
- package/dist/tool-system/builtin/config.js +67 -0
- package/dist/tool-system/builtin/cron.d.ts +10 -0
- package/dist/tool-system/builtin/cron.js +61 -0
- package/dist/tool-system/builtin/edit.d.ts +6 -0
- package/dist/tool-system/builtin/edit.js +100 -0
- package/dist/tool-system/builtin/file-cache.d.ts +11 -0
- package/dist/tool-system/builtin/file-cache.js +39 -0
- package/dist/tool-system/builtin/glob.d.ts +7 -0
- package/dist/tool-system/builtin/glob.js +76 -0
- package/dist/tool-system/builtin/grep.d.ts +7 -0
- package/dist/tool-system/builtin/grep.js +150 -0
- package/dist/tool-system/builtin/index.d.ts +20 -0
- package/dist/tool-system/builtin/index.js +449 -0
- package/dist/tool-system/builtin/lsp.d.ts +6 -0
- package/dist/tool-system/builtin/lsp.js +141 -0
- package/dist/tool-system/builtin/mcp-tools.d.ts +10 -0
- package/dist/tool-system/builtin/mcp-tools.js +102 -0
- package/dist/tool-system/builtin/memory.d.ts +26 -0
- package/dist/tool-system/builtin/memory.js +226 -0
- package/dist/tool-system/builtin/notebook-edit.d.ts +6 -0
- package/dist/tool-system/builtin/notebook-edit.js +124 -0
- package/dist/tool-system/builtin/plan.d.ts +15 -0
- package/dist/tool-system/builtin/plan.js +55 -0
- package/dist/tool-system/builtin/powershell.d.ts +11 -0
- package/dist/tool-system/builtin/powershell.js +64 -0
- package/dist/tool-system/builtin/read.d.ts +6 -0
- package/dist/tool-system/builtin/read.js +70 -0
- package/dist/tool-system/builtin/remote-trigger.d.ts +6 -0
- package/dist/tool-system/builtin/remote-trigger.js +54 -0
- package/dist/tool-system/builtin/repl.d.ts +11 -0
- package/dist/tool-system/builtin/repl.js +82 -0
- package/dist/tool-system/builtin/send-message.d.ts +6 -0
- package/dist/tool-system/builtin/send-message.js +47 -0
- package/dist/tool-system/builtin/skill-prompt.d.ts +20 -0
- package/dist/tool-system/builtin/skill-prompt.js +52 -0
- package/dist/tool-system/builtin/skill.d.ts +9 -0
- package/dist/tool-system/builtin/skill.js +55 -0
- package/dist/tool-system/builtin/sleep.d.ts +6 -0
- package/dist/tool-system/builtin/sleep.js +33 -0
- package/dist/tool-system/builtin/task.d.ts +48 -0
- package/dist/tool-system/builtin/task.js +139 -0
- package/dist/tool-system/builtin/tool-search.d.ts +11 -0
- package/dist/tool-system/builtin/tool-search.js +90 -0
- package/dist/tool-system/builtin/web-fetch.d.ts +10 -0
- package/dist/tool-system/builtin/web-fetch.js +328 -0
- package/dist/tool-system/builtin/web-search.d.ts +25 -0
- package/dist/tool-system/builtin/web-search.js +174 -0
- package/dist/tool-system/builtin/worktree.d.ts +10 -0
- package/dist/tool-system/builtin/worktree.js +95 -0
- package/dist/tool-system/builtin/write.d.ts +6 -0
- package/dist/tool-system/builtin/write.js +36 -0
- package/dist/tool-system/context.d.ts +165 -0
- package/dist/tool-system/context.js +18 -0
- package/dist/tool-system/executor.d.ts +49 -0
- package/dist/tool-system/executor.js +495 -0
- package/dist/tool-system/investigation-guard.d.ts +46 -0
- package/dist/tool-system/investigation-guard.js +155 -0
- package/dist/tool-system/mcp-manager.d.ts +67 -0
- package/dist/tool-system/mcp-manager.js +249 -0
- package/dist/tool-system/permission.d.ts +111 -0
- package/dist/tool-system/permission.js +774 -0
- package/dist/tool-system/registry.d.ts +32 -0
- package/dist/tool-system/registry.js +131 -0
- package/dist/tool-system/sandbox/bwrap.d.ts +13 -0
- package/dist/tool-system/sandbox/bwrap.js +63 -0
- package/dist/tool-system/sandbox/index.d.ts +78 -0
- package/dist/tool-system/sandbox/index.js +222 -0
- package/dist/tool-system/sandbox/off.d.ts +2 -0
- package/dist/tool-system/sandbox/off.js +8 -0
- package/dist/tool-system/sandbox/seatbelt.d.ts +18 -0
- package/dist/tool-system/sandbox/seatbelt.js +109 -0
- package/dist/tool-system/task-guard.d.ts +29 -0
- package/dist/tool-system/task-guard.js +70 -0
- package/dist/tool-system/validation.d.ts +5 -0
- package/dist/tool-system/validation.js +39 -0
- package/dist/types.d.ts +376 -0
- package/dist/types.js +4 -0
- package/dist/updater.d.ts +65 -0
- package/dist/updater.js +405 -0
- package/dist/utils/debug.d.ts +41 -0
- package/dist/utils/debug.js +110 -0
- package/dist/utils/earlyInput.d.ts +43 -0
- package/dist/utils/earlyInput.js +166 -0
- package/dist/utils/env.d.ts +24 -0
- package/dist/utils/env.js +104 -0
- package/dist/utils/envUtils.d.ts +51 -0
- package/dist/utils/envUtils.js +135 -0
- package/dist/utils/execFileNoThrow.d.ts +39 -0
- package/dist/utils/execFileNoThrow.js +74 -0
- package/dist/utils/format.d.ts +44 -0
- package/dist/utils/format.js +236 -0
- package/dist/utils/intl.d.ts +22 -0
- package/dist/utils/intl.js +83 -0
- package/dist/utils/lockfile.d.ts +15 -0
- package/dist/utils/lockfile.js +30 -0
- package/dist/utils/memoize.d.ts +19 -0
- package/dist/utils/memoize.js +14 -0
- package/dist/utils/semver.d.ts +6 -0
- package/dist/utils/semver.js +13 -0
- package/dist/utils/sliceAnsi.d.ts +12 -0
- package/dist/utils/sliceAnsi.js +62 -0
- package/dist/utils/systemTheme.d.ts +40 -0
- package/dist/utils/systemTheme.js +108 -0
- package/dist/utils/task-sanitizer.d.ts +6 -0
- package/dist/utils/task-sanitizer.js +24 -0
- package/dist/utils/theme.d.ts +93 -0
- package/dist/utils/theme.js +544 -0
- package/dist/utils/toolDisplay.d.ts +31 -0
- package/dist/utils/toolDisplay.js +188 -0
- package/package.json +72 -0
|
@@ -0,0 +1,495 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tool executor — orchestrates permission checks, hooks, and execution.
|
|
3
|
+
*/
|
|
4
|
+
import { setMaxListeners } from "node:events";
|
|
5
|
+
import { logger as rootLogger, getCurrentSid } from "../logging/logger.js";
|
|
6
|
+
import { recordToolCall, recordToolResult } from "../logging/session-recorder.js";
|
|
7
|
+
import { validateToolArgs } from "./validation.js";
|
|
8
|
+
// A1 hardening: hooks must never promote a non-`allow` classifier
|
|
9
|
+
// decision to `allow`. They may otherwise adjust the decision freely
|
|
10
|
+
// (e.g. tighten `allow` to `deny`/`ask`, or relax `deny` to `ask` to
|
|
11
|
+
// request interactive confirmation — both legitimate audit patterns).
|
|
12
|
+
// The user remains the only source of `allow` when the classifier
|
|
13
|
+
// said `ask`/`deny`.
|
|
14
|
+
//
|
|
15
|
+
// See standard §S4 and spec docs/superpowers/specs/2026-05-26-a1-permission-hardening-design.md.
|
|
16
|
+
function clampHookDecision(classifier, hook) {
|
|
17
|
+
if (!hook)
|
|
18
|
+
return { decision: classifier, rejectedUpgrade: false };
|
|
19
|
+
if (hook === "allow" && classifier !== "allow") {
|
|
20
|
+
return { decision: classifier, rejectedUpgrade: true };
|
|
21
|
+
}
|
|
22
|
+
return { decision: hook, rejectedUpgrade: false };
|
|
23
|
+
}
|
|
24
|
+
export class ToolExecutor {
|
|
25
|
+
registry;
|
|
26
|
+
permission;
|
|
27
|
+
hooks;
|
|
28
|
+
signal;
|
|
29
|
+
/** Per-Engine ToolContext injected for every tool call. */
|
|
30
|
+
toolCtx;
|
|
31
|
+
/**
|
|
32
|
+
* Turn-scoped logger set by TurnLoop at the top of each iteration.
|
|
33
|
+
* Falls back to the root logger so executor calls outside the turn loop
|
|
34
|
+
* (tests, ad-hoc tool runs) still write log lines, just without turn tags.
|
|
35
|
+
*/
|
|
36
|
+
log = rootLogger;
|
|
37
|
+
guard;
|
|
38
|
+
taskGuard;
|
|
39
|
+
constructor(registry, permission, hooks) {
|
|
40
|
+
this.registry = registry;
|
|
41
|
+
this.permission = permission;
|
|
42
|
+
this.hooks = hooks;
|
|
43
|
+
}
|
|
44
|
+
setInvestigationGuard(guard) {
|
|
45
|
+
this.guard = guard;
|
|
46
|
+
}
|
|
47
|
+
getInvestigationGuard() {
|
|
48
|
+
return this.guard;
|
|
49
|
+
}
|
|
50
|
+
setTaskGuard(guard) {
|
|
51
|
+
this.taskGuard = guard;
|
|
52
|
+
}
|
|
53
|
+
getTaskGuard() {
|
|
54
|
+
return this.taskGuard;
|
|
55
|
+
}
|
|
56
|
+
/** Set the abort signal for cascading cancellation. */
|
|
57
|
+
setSignal(signal) {
|
|
58
|
+
this.signal = signal;
|
|
59
|
+
// Each parallel tool call attaches its own `abort` listener to this
|
|
60
|
+
// session-wide signal (registry.ts:115). With 11+ concurrent tools —
|
|
61
|
+
// common when an Agent subagent fans out reads — Node's default
|
|
62
|
+
// limit of 10 fires a MaxListenersExceededWarning. The listeners are
|
|
63
|
+
// removed correctly in finally blocks; raise the ceiling to silence
|
|
64
|
+
// the noise without masking real leaks.
|
|
65
|
+
if (signal)
|
|
66
|
+
setMaxListeners(50, signal);
|
|
67
|
+
}
|
|
68
|
+
/** Set the per-Engine ToolContext (askUser, llmConfig, modelPool, etc.). */
|
|
69
|
+
setContext(ctx) {
|
|
70
|
+
this.toolCtx = ctx;
|
|
71
|
+
}
|
|
72
|
+
/** Inject a turn-scoped logger so tool-execution lines carry turn/turnId. */
|
|
73
|
+
setLogger(log) {
|
|
74
|
+
this.log = log;
|
|
75
|
+
this.permission.setLogger(log);
|
|
76
|
+
}
|
|
77
|
+
/** Check if a tool is safe for concurrent execution (read-only). */
|
|
78
|
+
isConcurrencySafe(toolName) {
|
|
79
|
+
const tool = this.registry.getTool(toolName);
|
|
80
|
+
return tool?.isConcurrencySafe ?? false;
|
|
81
|
+
}
|
|
82
|
+
async executeSingle(callIn) {
|
|
83
|
+
// Local `call` so we can rewrite args via pre_tool_use updatedInput
|
|
84
|
+
// without mutating the caller's object.
|
|
85
|
+
let call = callIn;
|
|
86
|
+
// 0. Plan mode: only allow read-only tools — no file writes at all
|
|
87
|
+
if (this.toolCtx?.planMode) {
|
|
88
|
+
const allowedInPlan = new Set([
|
|
89
|
+
"EnterPlanMode",
|
|
90
|
+
"ExitPlanMode",
|
|
91
|
+
"Read",
|
|
92
|
+
"Glob",
|
|
93
|
+
"Grep",
|
|
94
|
+
"WebSearch",
|
|
95
|
+
"WebFetch",
|
|
96
|
+
"AskUserQuestion",
|
|
97
|
+
"Agent",
|
|
98
|
+
"ToolSearch",
|
|
99
|
+
"TodoWrite",
|
|
100
|
+
]);
|
|
101
|
+
if (!allowedInPlan.has(call.toolName)) {
|
|
102
|
+
if (call.toolName === "Bash" && this.isReadOnlyBashCommand(call.args)) {
|
|
103
|
+
// Read-only bash is fine
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
return {
|
|
107
|
+
id: call.id,
|
|
108
|
+
toolName: call.toolName,
|
|
109
|
+
error: `Plan mode: ${call.toolName} is blocked. Only read-only tools (Read, Glob, Grep, WebSearch, WebFetch, Bash read-only) are allowed. You MUST output your plan as text in the conversation instead. Do NOT retry this tool call.`,
|
|
110
|
+
isError: true,
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
// 0.5. Validate tool args
|
|
116
|
+
const toolDef = this.registry.getTool(call.toolName);
|
|
117
|
+
if (toolDef?.inputSchema) {
|
|
118
|
+
const validationError = validateToolArgs(call.toolName, call.args, toolDef.inputSchema);
|
|
119
|
+
if (validationError) {
|
|
120
|
+
return {
|
|
121
|
+
id: call.id,
|
|
122
|
+
toolName: call.toolName,
|
|
123
|
+
error: `Invalid input: ${validationError}`,
|
|
124
|
+
isError: true,
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
// 0.6. Pre-tool-use hook (can approve/deny before execution)
|
|
129
|
+
const hookResult = await this.hooks.emit("pre_tool_use", {
|
|
130
|
+
toolName: call.toolName,
|
|
131
|
+
args: call.args,
|
|
132
|
+
toolCallId: call.id,
|
|
133
|
+
});
|
|
134
|
+
if (hookResult.decision === "deny") {
|
|
135
|
+
return {
|
|
136
|
+
id: call.id,
|
|
137
|
+
toolName: call.toolName,
|
|
138
|
+
error: `Blocked by pre_tool_use hook: ${hookResult.messages?.join(", ") ?? "denied"}`,
|
|
139
|
+
isError: true,
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
// pre_tool_use can rewrite the args for sanitizer / normalizer use
|
|
143
|
+
// cases. Re-validate against the schema so a malformed rewrite
|
|
144
|
+
// surfaces as the standard "Invalid input" error rather than
|
|
145
|
+
// silently corrupting downstream execution.
|
|
146
|
+
if (hookResult.updatedInput !== undefined) {
|
|
147
|
+
call = { ...call, args: hookResult.updatedInput };
|
|
148
|
+
if (toolDef?.inputSchema) {
|
|
149
|
+
const revalidation = validateToolArgs(call.toolName, call.args, toolDef.inputSchema);
|
|
150
|
+
if (revalidation) {
|
|
151
|
+
return {
|
|
152
|
+
id: call.id,
|
|
153
|
+
toolName: call.toolName,
|
|
154
|
+
error: `Invalid input (after pre_tool_use rewrite): ${revalidation}`,
|
|
155
|
+
isError: true,
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
this.log.info("hook.updated_input", {
|
|
160
|
+
cat: "tool",
|
|
161
|
+
tool: call.toolName,
|
|
162
|
+
toolCallId: call.id,
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
// A1 hardening: pre_tool_use can no longer pre-approve a tool via
|
|
166
|
+
// `decision === "allow"`. Hooks may relax `allow` and may force
|
|
167
|
+
// `ask`/`deny`, but they cannot promote a `deny`/`ask` decision to
|
|
168
|
+
// `allow`. The only paths to `allow` are the classifier (rules,
|
|
169
|
+
// safe-read, allowlist) and the user (interactive approval).
|
|
170
|
+
if (hookResult.decision === "allow") {
|
|
171
|
+
this.log.info("permission.hook_upgrade_rejected", {
|
|
172
|
+
cat: "permission",
|
|
173
|
+
tool: call.toolName,
|
|
174
|
+
site: "pre_tool_use",
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
// pre_tool_use can request interactive confirmation via
|
|
178
|
+
// decision: "ask". We invoke the same handleAsk path the classifier
|
|
179
|
+
// uses, but feed the hook's messages so the user sees the handler's
|
|
180
|
+
// reasoning. If the user approves, we skip the classifier below —
|
|
181
|
+
// the hook and the user together have decided.
|
|
182
|
+
if (hookResult.decision === "ask") {
|
|
183
|
+
const reason = hookResult.messages?.join("\n") ?? undefined;
|
|
184
|
+
const approved = await this.permission.handleAsk(call.toolName, call.args, reason);
|
|
185
|
+
if (!approved) {
|
|
186
|
+
return {
|
|
187
|
+
id: call.id,
|
|
188
|
+
toolName: call.toolName,
|
|
189
|
+
error: `Tool call denied by user (pre_tool_use ask).`,
|
|
190
|
+
isError: true,
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
// 0.7. Investigation guard — block redundant reads and tag soft reminders
|
|
195
|
+
// onto results. See investigation-guard.ts for the rules; they enforce the
|
|
196
|
+
// soft prompt guidance in coding.md ("never re-read", "3-call budget").
|
|
197
|
+
const guardDecision = this.guard?.preToolCheck(call);
|
|
198
|
+
if (guardDecision?.block) {
|
|
199
|
+
this.log.info("guard.block", { cat: "guard", tool: call.toolName, reason: guardDecision.block.slice(0, 200) });
|
|
200
|
+
return {
|
|
201
|
+
id: call.id,
|
|
202
|
+
toolName: call.toolName,
|
|
203
|
+
error: guardDecision.block,
|
|
204
|
+
isError: true,
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
// 1. Permission check — skipped only when pre_tool_use issued an
|
|
208
|
+
// `ask` that the user just approved (we don't double-prompt).
|
|
209
|
+
if (hookResult.decision !== "ask") {
|
|
210
|
+
const classifierDecision = this.permission.classify(call.toolName, call.args);
|
|
211
|
+
this.log.info("permission.classify", {
|
|
212
|
+
cat: "permission",
|
|
213
|
+
tool: call.toolName,
|
|
214
|
+
decision: classifierDecision,
|
|
215
|
+
mode: this.permission.getMode(),
|
|
216
|
+
});
|
|
217
|
+
// on_permission_check hook: lets handlers audit and *downgrade*
|
|
218
|
+
// the classifier decision (e.g. `allow → ask/deny`, `deny →
|
|
219
|
+
// ask`). Promotion to `allow` is rejected by clampHookDecision
|
|
220
|
+
// — only the classifier and the user can grant `allow`.
|
|
221
|
+
const permHook = await this.hooks.emit("on_permission_check", {
|
|
222
|
+
toolName: call.toolName,
|
|
223
|
+
args: call.args,
|
|
224
|
+
toolCallId: call.id,
|
|
225
|
+
classifierDecision,
|
|
226
|
+
});
|
|
227
|
+
// A1 hardening: clamp hook decision to downgrades only.
|
|
228
|
+
const clamped = clampHookDecision(classifierDecision, permHook.decision);
|
|
229
|
+
const decision = clamped.decision;
|
|
230
|
+
if (clamped.rejectedUpgrade) {
|
|
231
|
+
this.log.info("permission.hook_upgrade_rejected", {
|
|
232
|
+
cat: "permission",
|
|
233
|
+
tool: call.toolName,
|
|
234
|
+
site: "on_permission_check",
|
|
235
|
+
attempted: permHook.decision,
|
|
236
|
+
classifier: classifierDecision,
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
if (decision !== classifierDecision) {
|
|
240
|
+
this.log.info("permission.hook_override", {
|
|
241
|
+
cat: "permission",
|
|
242
|
+
tool: call.toolName,
|
|
243
|
+
from: classifierDecision,
|
|
244
|
+
to: decision,
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
if (decision === "deny") {
|
|
248
|
+
return {
|
|
249
|
+
id: call.id,
|
|
250
|
+
toolName: call.toolName,
|
|
251
|
+
error: `Permission denied for tool: ${call.toolName}`,
|
|
252
|
+
isError: true,
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
if (decision === "ask") {
|
|
256
|
+
const reason = permHook.messages?.join("\n");
|
|
257
|
+
const approved = await this.permission.handleAsk(call.toolName, call.args, reason);
|
|
258
|
+
if (!approved) {
|
|
259
|
+
return {
|
|
260
|
+
id: call.id,
|
|
261
|
+
toolName: call.toolName,
|
|
262
|
+
error: `Permission denied by user for tool: ${call.toolName}`,
|
|
263
|
+
isError: true,
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
// 2. Pre-tool hook
|
|
269
|
+
await this.hooks.emit("on_tool_start", {
|
|
270
|
+
toolName: call.toolName,
|
|
271
|
+
args: call.args,
|
|
272
|
+
toolCallId: call.id,
|
|
273
|
+
});
|
|
274
|
+
// 3. Execute. Use a span so begin/end share one cat and end carries
|
|
275
|
+
// duration_ms; widen args truncation from 200→2000 so Edit/Write payloads
|
|
276
|
+
// (file_path + a chunk of code) actually show what was changed instead of
|
|
277
|
+
// just the path. End-event also records a result snippet on failure for
|
|
278
|
+
// post-mortem without having to crack the full transcript.
|
|
279
|
+
const span = this.log.span("tool.exec", {
|
|
280
|
+
cat: "tool",
|
|
281
|
+
tool: call.toolName,
|
|
282
|
+
toolCallId: call.id,
|
|
283
|
+
args: JSON.stringify(call.args).slice(0, 2000),
|
|
284
|
+
});
|
|
285
|
+
const sid = getCurrentSid();
|
|
286
|
+
recordToolCall(sid, { id: call.id, toolName: call.toolName, args: call.args });
|
|
287
|
+
const toolStartedAt = Date.now();
|
|
288
|
+
let result;
|
|
289
|
+
try {
|
|
290
|
+
result = await this.registry.executeTool(call.toolName, call.args, {
|
|
291
|
+
signal: this.signal,
|
|
292
|
+
ctx: this.toolCtx,
|
|
293
|
+
});
|
|
294
|
+
}
|
|
295
|
+
catch (err) {
|
|
296
|
+
span.fail(err);
|
|
297
|
+
recordToolResult(sid, {
|
|
298
|
+
id: call.id,
|
|
299
|
+
toolName: call.toolName,
|
|
300
|
+
ok: false,
|
|
301
|
+
durationMs: Date.now() - toolStartedAt,
|
|
302
|
+
error: err instanceof Error ? err.stack ?? err.message : String(err),
|
|
303
|
+
});
|
|
304
|
+
throw err;
|
|
305
|
+
}
|
|
306
|
+
result.id = call.id;
|
|
307
|
+
// Prepend any non-blocking guard reminder onto a successful result so the
|
|
308
|
+
// model sees it on its next turn alongside the content it just fetched.
|
|
309
|
+
if (guardDecision?.prepend && !result.error && result.result) {
|
|
310
|
+
result.result = `${guardDecision.prepend}\n${result.result}`;
|
|
311
|
+
}
|
|
312
|
+
const payload = result.result ?? result.error ?? "";
|
|
313
|
+
span.end({
|
|
314
|
+
ok: !result.error,
|
|
315
|
+
chars: payload.length,
|
|
316
|
+
// Snippet only on failure — successful tool output can be huge and is
|
|
317
|
+
// already on the transcript. Failures are rare and the first 500 chars
|
|
318
|
+
// usually contain the message we need.
|
|
319
|
+
...(result.error ? { errorSnippet: payload.slice(0, 500) } : {}),
|
|
320
|
+
});
|
|
321
|
+
recordToolResult(sid, {
|
|
322
|
+
id: call.id,
|
|
323
|
+
toolName: call.toolName,
|
|
324
|
+
ok: !result.error,
|
|
325
|
+
durationMs: Date.now() - toolStartedAt,
|
|
326
|
+
output: result.error ? undefined : result.result,
|
|
327
|
+
error: result.error,
|
|
328
|
+
});
|
|
329
|
+
// 4. Post-tool hook
|
|
330
|
+
await this.hooks.emit("on_tool_end", {
|
|
331
|
+
toolName: call.toolName,
|
|
332
|
+
toolCallId: call.id,
|
|
333
|
+
result: result.result,
|
|
334
|
+
error: result.error,
|
|
335
|
+
});
|
|
336
|
+
// 5. Post-tool-use hook (after execution, can observe/modify result)
|
|
337
|
+
const postHook = await this.hooks.emit("post_tool_use", {
|
|
338
|
+
toolName: call.toolName,
|
|
339
|
+
toolCallId: call.id,
|
|
340
|
+
result: result.result,
|
|
341
|
+
error: result.error,
|
|
342
|
+
});
|
|
343
|
+
// Append handler-supplied context (linter output, type-check result,
|
|
344
|
+
// etc.) onto the tool result so the model sees it on the next turn.
|
|
345
|
+
// Tagged with a separator so the model can tell hook output from
|
|
346
|
+
// tool output. Skip when the tool errored — additional context on a
|
|
347
|
+
// failed tool would be confusing.
|
|
348
|
+
if (postHook.additionalContext && !result.error) {
|
|
349
|
+
const tag = "--- additional context from post_tool_use hook ---";
|
|
350
|
+
result.result = result.result
|
|
351
|
+
? `${result.result}\n\n${tag}\n${postHook.additionalContext}`
|
|
352
|
+
: `${tag}\n${postHook.additionalContext}`;
|
|
353
|
+
this.log.info("hook.additional_context", {
|
|
354
|
+
cat: "tool",
|
|
355
|
+
tool: call.toolName,
|
|
356
|
+
toolCallId: call.id,
|
|
357
|
+
chars: postHook.additionalContext.length,
|
|
358
|
+
});
|
|
359
|
+
}
|
|
360
|
+
// 6. file_changed hook for Write/Edit tools
|
|
361
|
+
if ((call.toolName === "Write" || call.toolName === "Edit") && !result.error) {
|
|
362
|
+
await this.hooks.emit("file_changed", {
|
|
363
|
+
toolName: call.toolName,
|
|
364
|
+
filePath: call.args.file_path,
|
|
365
|
+
});
|
|
366
|
+
}
|
|
367
|
+
return result;
|
|
368
|
+
}
|
|
369
|
+
isReadOnlyBashCommand(args) {
|
|
370
|
+
const cmd = String(args.command ?? "").trim();
|
|
371
|
+
// Reject shell metacharacters that could chain a write command after a
|
|
372
|
+
// read-only prefix: ; && || ` $(...) redirections. A single pipe (|) is
|
|
373
|
+
// still allowed — the per-part whitelist further down validates it.
|
|
374
|
+
const DANGEROUS = /;|&&|\|\||`|\$\(|>/;
|
|
375
|
+
if (DANGEROUS.test(cmd))
|
|
376
|
+
return false;
|
|
377
|
+
// Extract the base command (first word, ignoring env vars like VAR=val)
|
|
378
|
+
const baseCmd = cmd.replace(/^(\w+=\S+\s+)*/, "").split(/\s/)[0];
|
|
379
|
+
const readOnlyCommands = new Set([
|
|
380
|
+
"ls",
|
|
381
|
+
"find",
|
|
382
|
+
"cat",
|
|
383
|
+
"head",
|
|
384
|
+
"tail",
|
|
385
|
+
"wc",
|
|
386
|
+
"file",
|
|
387
|
+
"stat",
|
|
388
|
+
"tree",
|
|
389
|
+
"du",
|
|
390
|
+
"df",
|
|
391
|
+
"pwd",
|
|
392
|
+
"echo",
|
|
393
|
+
"which",
|
|
394
|
+
"type",
|
|
395
|
+
"env",
|
|
396
|
+
"printenv",
|
|
397
|
+
"grep",
|
|
398
|
+
"rg",
|
|
399
|
+
"ag",
|
|
400
|
+
"awk",
|
|
401
|
+
"less",
|
|
402
|
+
"more",
|
|
403
|
+
"sort",
|
|
404
|
+
"uniq",
|
|
405
|
+
"diff",
|
|
406
|
+
"readlink",
|
|
407
|
+
"realpath",
|
|
408
|
+
"basename",
|
|
409
|
+
"dirname",
|
|
410
|
+
"date",
|
|
411
|
+
"whoami",
|
|
412
|
+
"uname",
|
|
413
|
+
"hostname",
|
|
414
|
+
"id",
|
|
415
|
+
"groups",
|
|
416
|
+
"locale",
|
|
417
|
+
"uptime",
|
|
418
|
+
]);
|
|
419
|
+
// Whitelisted read-only git/tool subcommands.
|
|
420
|
+
// Deliberately exclude `node -e`, `python -c`, `sed -n`, etc. — those can
|
|
421
|
+
// execute arbitrary code even though they "look read-only".
|
|
422
|
+
const readOnlyPrefixes = [
|
|
423
|
+
"git log",
|
|
424
|
+
"git status",
|
|
425
|
+
"git diff",
|
|
426
|
+
"git branch",
|
|
427
|
+
"git show",
|
|
428
|
+
"git blame",
|
|
429
|
+
"git remote",
|
|
430
|
+
"git tag",
|
|
431
|
+
"git stash list",
|
|
432
|
+
"git rev-parse",
|
|
433
|
+
"npx tsc --noEmit",
|
|
434
|
+
];
|
|
435
|
+
if (readOnlyCommands.has(baseCmd))
|
|
436
|
+
return true;
|
|
437
|
+
if (readOnlyPrefixes.some((p) => cmd.startsWith(p)))
|
|
438
|
+
return true;
|
|
439
|
+
// Allow piped commands if all parts are read-only. Redirections were
|
|
440
|
+
// already rejected above, so a bare `|` here is a real pipeline.
|
|
441
|
+
if (cmd.includes("|")) {
|
|
442
|
+
const parts = cmd.split("|").map((p) => p.trim());
|
|
443
|
+
return parts.every((part) => {
|
|
444
|
+
const partBase = part.replace(/^(\w+=\S+\s+)*/, "").split(/\s/)[0];
|
|
445
|
+
return readOnlyCommands.has(partBase);
|
|
446
|
+
});
|
|
447
|
+
}
|
|
448
|
+
return false;
|
|
449
|
+
}
|
|
450
|
+
async executeAll(calls) {
|
|
451
|
+
const results = [];
|
|
452
|
+
// Separate safe (concurrent) and unsafe (sequential) tools
|
|
453
|
+
const safe = [];
|
|
454
|
+
const unsafe = [];
|
|
455
|
+
for (const call of calls) {
|
|
456
|
+
const tool = this.registry.getTool(call.toolName);
|
|
457
|
+
if (tool?.isConcurrencySafe) {
|
|
458
|
+
safe.push(call);
|
|
459
|
+
}
|
|
460
|
+
else {
|
|
461
|
+
unsafe.push(call);
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
// Execute safe tools concurrently
|
|
465
|
+
if (safe.length > 0) {
|
|
466
|
+
const safeResults = await Promise.all(safe.map((c) => this.executeSingle(c)));
|
|
467
|
+
results.push(...safeResults);
|
|
468
|
+
}
|
|
469
|
+
// Execute unsafe tools sequentially
|
|
470
|
+
for (const call of unsafe) {
|
|
471
|
+
const result = await this.executeSingle(call);
|
|
472
|
+
results.push(result);
|
|
473
|
+
}
|
|
474
|
+
return results;
|
|
475
|
+
}
|
|
476
|
+
/**
|
|
477
|
+
* Convert tool results into Message entries for the transcript.
|
|
478
|
+
*/
|
|
479
|
+
resultsToMessages(toolCalls, results) {
|
|
480
|
+
const blocks = [];
|
|
481
|
+
for (const result of results) {
|
|
482
|
+
blocks.push({
|
|
483
|
+
type: "tool_result",
|
|
484
|
+
tool_use_id: result.id,
|
|
485
|
+
content: result.error ? `Error: ${result.error}` : (result.result ?? "(no output)"),
|
|
486
|
+
});
|
|
487
|
+
}
|
|
488
|
+
return [
|
|
489
|
+
{
|
|
490
|
+
role: "user",
|
|
491
|
+
content: blocks,
|
|
492
|
+
},
|
|
493
|
+
];
|
|
494
|
+
}
|
|
495
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Investigation Guard — runtime enforcement of read budgets.
|
|
3
|
+
*
|
|
4
|
+
* Background: prompt rules in coding.md ("never re-read the same file",
|
|
5
|
+
* "after ~3 read-only calls, change strategy") describe a behavior the model
|
|
6
|
+
* frequently violates under context pressure. This module turns those soft
|
|
7
|
+
* rules into in-loop reminders/blocks delivered through tool results.
|
|
8
|
+
*
|
|
9
|
+
* Three independent counters share one guard instance per Engine:
|
|
10
|
+
* A) dedupe — same Read offset hit ≥2 times → reminder; ≥3 times → block
|
|
11
|
+
* B) read-budget — N consecutive read-only tools with no side-effecting
|
|
12
|
+
* tool in between → reminder on the next read
|
|
13
|
+
* C) silent turns — N consecutive turns of only read-only tools with no
|
|
14
|
+
* text output → injected into the *next* turn's user
|
|
15
|
+
* message by turn-loop (the guard just tracks state).
|
|
16
|
+
*
|
|
17
|
+
* Everything decays naturally on any write/Bash/AskUser tool (state reset).
|
|
18
|
+
*/
|
|
19
|
+
import type { ToolCall, ToolResult } from "../types.js";
|
|
20
|
+
export interface GuardDecision {
|
|
21
|
+
block?: string;
|
|
22
|
+
prepend?: string;
|
|
23
|
+
}
|
|
24
|
+
export declare class InvestigationGuard {
|
|
25
|
+
private readHistory;
|
|
26
|
+
private consecutiveReads;
|
|
27
|
+
private silentTurns;
|
|
28
|
+
private turnHasText;
|
|
29
|
+
private turnHasSideEffect;
|
|
30
|
+
private lastReminderSilent;
|
|
31
|
+
/**
|
|
32
|
+
* Soft mode replaces hard blocks (3rd-read dedupe) with a stronger
|
|
33
|
+
* reminder. Used in headless runs (`code-shell run …`) where there is
|
|
34
|
+
* no human to retry the task, so an unattended automation should not
|
|
35
|
+
* be killed by the guard — it should still get the signal to change
|
|
36
|
+
* strategy, but the tool call goes through.
|
|
37
|
+
*/
|
|
38
|
+
private softMode;
|
|
39
|
+
setSoftMode(soft: boolean): void;
|
|
40
|
+
preToolCheck(call: ToolCall): GuardDecision | undefined;
|
|
41
|
+
noteText(text: string | undefined): void;
|
|
42
|
+
noteToolResult(_call: ToolCall, _result: ToolResult): void;
|
|
43
|
+
turnEnded(turnNumber: number): string | undefined;
|
|
44
|
+
private isMutatingTool;
|
|
45
|
+
private buildKey;
|
|
46
|
+
}
|