@cjhyy/code-shell-core 0.5.0-rc.1 → 0.6.0-rc.1
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 +13 -10
- package/dist/agent/agent-definition-registry.d.ts +21 -3
- package/dist/agent/agent-definition-registry.js +49 -18
- package/dist/agent/agent-definition.d.ts +32 -0
- package/dist/agent/agent-definition.js +49 -4
- package/dist/arena/arena.js +11 -12
- package/dist/arena/context/context-tools.js +47 -6
- package/dist/arena/context/within-root.d.ts +7 -0
- package/dist/arena/context/within-root.js +15 -0
- package/dist/arena/iterate/phases/argue.js +8 -2
- package/dist/arena/iterate/phases/revise.js +1 -1
- package/dist/arena/iterate/phases/tournament.js +3 -3
- package/dist/arena/ledger.js +9 -1
- package/dist/arena/phases/adjudication.js +1 -4
- package/dist/arena/phases/build-consensus.js +1 -4
- package/dist/arena/phases/cross-review.js +2 -8
- package/dist/arena/phases/debate-rounds.js +1 -4
- package/dist/arena/phases/participant-research.js +1 -4
- package/dist/arena/phases/planning-detail-expansion.js +8 -10
- package/dist/arena/planner.js +0 -1
- package/dist/arena/providers/docs.d.ts +1 -0
- package/dist/arena/providers/docs.js +5 -2
- package/dist/arena/providers/repo.d.ts +1 -0
- package/dist/arena/providers/repo.js +5 -2
- package/dist/arena/strategies/utils.d.ts +1 -4
- package/dist/arena/strategies/utils.js +7 -51
- package/dist/arena/transitions.d.ts +7 -2
- package/dist/arena/transitions.js +9 -4
- package/dist/arena/types.d.ts +8 -2
- package/dist/automation/cron-expr.d.ts +41 -0
- package/dist/automation/cron-expr.js +169 -0
- package/dist/automation/index.d.ts +41 -0
- package/dist/automation/index.js +39 -0
- package/dist/automation/runner.d.ts +84 -0
- package/dist/automation/runner.js +92 -0
- package/dist/automation/scheduler.d.ts +194 -0
- package/dist/automation/scheduler.js +623 -0
- package/dist/automation/store.d.ts +39 -0
- package/dist/automation/store.js +122 -0
- package/dist/automation/write-policy.d.ts +34 -0
- package/dist/automation/write-policy.js +113 -0
- package/dist/automation/write-run.d.ts +51 -0
- package/dist/automation/write-run.js +38 -0
- package/dist/capability-control/disabled-lists.d.ts +25 -0
- package/dist/capability-control/disabled-lists.js +57 -0
- package/dist/capability-control/index.d.ts +5 -0
- package/dist/capability-control/index.js +3 -0
- package/dist/capability-control/overlay.d.ts +64 -0
- package/dist/capability-control/overlay.js +116 -0
- package/dist/capability-control/project.d.ts +65 -0
- package/dist/capability-control/project.js +147 -0
- package/dist/capability-control/service.d.ts +70 -0
- package/dist/capability-control/service.js +147 -0
- package/dist/capability-control/types.d.ts +62 -0
- package/dist/capability-control/types.js +17 -0
- package/dist/cc-orchestrator/agent-adapter.d.ts +50 -0
- package/dist/cc-orchestrator/agent-adapter.js +133 -0
- package/dist/cc-orchestrator/cc-capability.d.ts +19 -0
- package/dist/cc-orchestrator/cc-capability.js +53 -0
- package/dist/cc-orchestrator/codex-session-discovery.d.ts +24 -0
- package/dist/cc-orchestrator/codex-session-discovery.js +191 -0
- package/dist/cc-orchestrator/codex-session-history.d.ts +25 -0
- package/dist/cc-orchestrator/codex-session-history.js +187 -0
- package/dist/cc-orchestrator/external-agent-changes.d.ts +19 -0
- package/dist/cc-orchestrator/external-agent-changes.js +214 -0
- package/dist/cc-orchestrator/external-agent-driver.d.ts +18 -0
- package/dist/cc-orchestrator/external-agent-driver.js +69 -0
- package/dist/cc-orchestrator/index.d.ts +8 -0
- package/dist/cc-orchestrator/index.js +8 -0
- package/dist/cc-orchestrator/relevance-judge.d.ts +15 -0
- package/dist/cc-orchestrator/relevance-judge.js +29 -0
- package/dist/cc-orchestrator/session-discovery.d.ts +46 -0
- package/dist/cc-orchestrator/session-discovery.js +125 -0
- package/dist/cc-orchestrator/session-history.d.ts +19 -0
- package/dist/cc-orchestrator/session-history.js +67 -0
- package/dist/cli/agent-server-stdio.d.ts +26 -1
- package/dist/cli/agent-server-stdio.js +221 -28
- package/dist/cli/agent-server-tcp.d.ts +20 -0
- package/dist/cli/agent-server-tcp.js +134 -0
- package/dist/cli/graceful-shutdown.d.ts +27 -0
- package/dist/cli/graceful-shutdown.js +30 -0
- package/dist/context/compaction.d.ts +56 -0
- package/dist/context/compaction.js +209 -15
- package/dist/context/manager.d.ts +0 -20
- package/dist/context/manager.js +25 -41
- package/dist/cost-tracker.js +5 -61
- package/dist/credentials/cipher.d.ts +49 -0
- package/dist/credentials/cipher.js +45 -0
- package/dist/credentials/cookie-jar.d.ts +24 -0
- package/dist/credentials/cookie-jar.js +40 -0
- package/dist/credentials/index.d.ts +7 -0
- package/dist/credentials/index.js +5 -0
- package/dist/credentials/inject-credential-tool.d.ts +20 -0
- package/dist/credentials/inject-credential-tool.js +130 -0
- package/dist/credentials/store.d.ts +72 -0
- package/dist/credentials/store.js +184 -0
- package/dist/credentials/types.d.ts +56 -0
- package/dist/credentials/use-credential-tool.d.ts +29 -0
- package/dist/credentials/use-credential-tool.js +205 -0
- package/dist/credentials/use-gate.d.ts +56 -0
- package/dist/credentials/use-gate.js +52 -0
- package/dist/cron/cron-runtime.d.ts +2 -0
- package/dist/cron/cron-runtime.js +2 -0
- package/dist/cron/cron-store.d.ts +2 -0
- package/dist/cron/cron-store.js +2 -0
- package/dist/cron/scheduler.d.ts +5 -32
- package/dist/cron/scheduler.js +5 -113
- package/dist/data/model-metadata.d.ts +77 -0
- package/dist/data/model-metadata.js +56 -0
- package/dist/data/model-metadata.json +216 -0
- package/dist/data/openrouter-models.d.ts +18 -7
- package/dist/data/openrouter-models.js +35 -8
- package/dist/data/openrouter-sync.js +1 -1
- package/dist/engine/aux-key.d.ts +10 -0
- package/dist/engine/aux-key.js +11 -0
- package/dist/engine/dynamic-tool-defs.d.ts +19 -0
- package/dist/engine/dynamic-tool-defs.js +36 -0
- package/dist/engine/engine.d.ts +431 -108
- package/dist/engine/engine.js +1630 -279
- package/dist/engine/friendly-error.d.ts +18 -0
- package/dist/engine/friendly-error.js +63 -0
- package/dist/engine/goal.d.ts +187 -0
- package/dist/engine/goal.js +196 -0
- package/dist/engine/image-compression.d.ts +81 -0
- package/dist/engine/image-compression.js +186 -0
- package/dist/engine/image-policy.d.ts +165 -0
- package/dist/engine/image-policy.js +189 -0
- package/dist/engine/model-connections-pool.d.ts +17 -0
- package/dist/engine/model-connections-pool.js +66 -0
- package/dist/engine/model-facade.d.ts +10 -0
- package/dist/engine/model-facade.js +25 -0
- package/dist/engine/patch-orphaned-tools.js +3 -0
- package/dist/engine/reactive-threshold.d.ts +13 -0
- package/dist/engine/reactive-threshold.js +17 -0
- package/dist/engine/resolve-llm-config.d.ts +16 -0
- package/dist/engine/resolve-llm-config.js +44 -0
- package/dist/engine/runtime.js +9 -1
- package/dist/engine/sandbox-cache-key.d.ts +10 -0
- package/dist/engine/sandbox-cache-key.js +9 -0
- package/dist/engine/sandbox-config.d.ts +31 -0
- package/dist/engine/sandbox-config.js +38 -0
- package/dist/engine/session-title.d.ts +13 -0
- package/dist/engine/session-title.js +34 -0
- package/dist/engine/session-usage.d.ts +19 -0
- package/dist/engine/session-usage.js +25 -0
- package/dist/engine/steer-queue.d.ts +32 -0
- package/dist/engine/steer-queue.js +26 -0
- package/dist/engine/streaming-tool-queue.d.ts +12 -0
- package/dist/engine/streaming-tool-queue.js +50 -10
- package/dist/engine/turn-loop.d.ts +94 -9
- package/dist/engine/turn-loop.js +775 -342
- package/dist/engine/types.d.ts +175 -0
- package/dist/engine/types.js +13 -0
- package/dist/external-agents/config.d.ts +2 -0
- package/dist/external-agents/config.js +15 -0
- package/dist/external-agents/types.d.ts +31 -0
- package/dist/external-agents/types.js +1 -0
- package/dist/git/parse-log.d.ts +13 -0
- package/dist/git/parse-log.js +21 -0
- package/dist/git/utils.d.ts +8 -6
- package/dist/git/utils.js +62 -26
- package/dist/git/worktree.d.ts +48 -0
- package/dist/git/worktree.js +111 -20
- package/dist/hooks/events.d.ts +23 -1
- package/dist/hooks/goal-stop-hook.d.ts +58 -0
- package/dist/hooks/goal-stop-hook.js +279 -0
- package/dist/hooks/hook-output.d.ts +33 -0
- package/dist/hooks/hook-output.js +79 -0
- package/dist/hooks/registry.d.ts +15 -0
- package/dist/hooks/registry.js +57 -1
- package/dist/hooks/shell-runner.d.ts +2 -0
- package/dist/hooks/shell-runner.js +48 -18
- package/dist/index.d.ts +79 -11
- package/dist/index.js +89 -6
- package/dist/llm/capabilities/param-specs.d.ts +14 -0
- package/dist/llm/capabilities/param-specs.js +62 -0
- package/dist/llm/capabilities/reasoning-control.d.ts +24 -0
- package/dist/llm/capabilities/reasoning-control.js +38 -0
- package/dist/llm/capabilities/rules.js +37 -3
- package/dist/llm/capabilities/types.d.ts +32 -2
- package/dist/llm/clamp-max-tokens.d.ts +11 -0
- package/dist/llm/clamp-max-tokens.js +17 -0
- package/dist/llm/client-base.d.ts +69 -5
- package/dist/llm/client-base.js +185 -25
- package/dist/llm/client-factory.d.ts +3 -3
- package/dist/llm/client-factory.js +2 -2
- package/dist/llm/model-cache.js +4 -2
- package/dist/llm/model-pool.d.ts +29 -11
- package/dist/llm/model-pool.js +36 -20
- package/dist/llm/provider-auth.d.ts +41 -0
- package/dist/llm/provider-auth.js +76 -0
- package/dist/llm/provider-catalog.d.ts +6 -2
- package/dist/llm/providers/anthropic.d.ts +32 -2
- package/dist/llm/providers/anthropic.js +207 -18
- package/dist/llm/providers/openai.d.ts +47 -2
- package/dist/llm/providers/openai.js +478 -71
- package/dist/llm/reasoning-setting.d.ts +51 -0
- package/dist/llm/reasoning-setting.js +40 -0
- package/dist/llm/stop-reason.d.ts +13 -0
- package/dist/llm/stop-reason.js +21 -0
- package/dist/llm/stream-watchdog.js +5 -1
- package/dist/llm/strip-vision.d.ts +32 -0
- package/dist/llm/strip-vision.js +94 -0
- package/dist/llm/token-counter.js +9 -2
- package/dist/llm/types.d.ts +9 -6
- package/dist/logging/logger.js +18 -4
- package/dist/logging/sanitize-messages.d.ts +10 -0
- package/dist/logging/sanitize-messages.js +112 -0
- package/dist/lsp/client.js +28 -8
- package/dist/lsp/manager.d.ts +1 -1
- package/dist/lsp/manager.js +42 -11
- package/dist/lsp/root-path.d.ts +9 -0
- package/dist/lsp/root-path.js +12 -0
- package/dist/model-catalog/builtin.d.ts +12 -0
- package/dist/model-catalog/builtin.js +407 -0
- package/dist/model-catalog/gen-connections.d.ts +20 -0
- package/dist/model-catalog/gen-connections.js +28 -0
- package/dist/model-catalog/index.d.ts +41 -0
- package/dist/model-catalog/index.js +90 -0
- package/dist/model-catalog/params.d.ts +20 -0
- package/dist/model-catalog/params.js +45 -0
- package/dist/model-catalog/resolve.d.ts +48 -0
- package/dist/model-catalog/resolve.js +33 -0
- package/dist/model-catalog/save-entry.d.ts +32 -0
- package/dist/model-catalog/save-entry.js +104 -0
- package/dist/model-catalog/types.d.ts +561 -0
- package/dist/model-catalog/types.js +93 -0
- package/dist/model-catalog/upsert.d.ts +9 -0
- package/dist/model-catalog/upsert.js +8 -0
- package/dist/onboarding.d.ts +12 -82
- package/dist/onboarding.js +75 -333
- package/dist/plugins/gitOps.d.ts +53 -0
- package/dist/plugins/gitOps.js +130 -5
- package/dist/plugins/installer/checkUpdate.d.ts +16 -0
- package/dist/plugins/installer/checkUpdate.js +32 -0
- package/dist/plugins/installer/codex/convertAgents.d.ts +7 -0
- package/dist/plugins/installer/codex/convertAgents.js +55 -0
- package/dist/plugins/installer/codex/convertCommands.d.ts +19 -0
- package/dist/plugins/installer/codex/convertCommands.js +46 -0
- package/dist/plugins/installer/codex/convertMcp.d.ts +11 -0
- package/dist/plugins/installer/codex/convertMcp.js +85 -0
- package/dist/plugins/installer/codex/convertSkills.d.ts +12 -0
- package/dist/plugins/installer/codex/convertSkills.js +33 -0
- package/dist/plugins/installer/detectFormat.d.ts +2 -0
- package/dist/plugins/installer/detectFormat.js +6 -0
- package/dist/plugins/installer/install.d.ts +7 -0
- package/dist/plugins/installer/install.js +123 -0
- package/dist/plugins/installer/installFromArchive.d.ts +43 -0
- package/dist/plugins/installer/installFromArchive.js +134 -0
- package/dist/plugins/installer/installFromSource.d.ts +13 -0
- package/dist/plugins/installer/installFromSource.js +51 -0
- package/dist/plugins/installer/list.d.ts +9 -0
- package/dist/plugins/installer/list.js +30 -0
- package/dist/plugins/installer/loadPluginAgents.d.ts +3 -0
- package/dist/plugins/installer/loadPluginAgents.js +27 -0
- package/dist/plugins/installer/loadPluginMcp.d.ts +14 -0
- package/dist/plugins/installer/loadPluginMcp.js +107 -0
- package/dist/plugins/installer/parseSource.d.ts +20 -0
- package/dist/plugins/installer/parseSource.js +74 -0
- package/dist/plugins/installer/paths.d.ts +5 -0
- package/dist/plugins/installer/paths.js +25 -0
- package/dist/plugins/installer/pruneDisabled.d.ts +24 -0
- package/dist/plugins/installer/pruneDisabled.js +73 -0
- package/dist/plugins/installer/types.d.ts +52 -0
- package/dist/plugins/installer/types.js +28 -0
- package/dist/plugins/installer/uninstall.d.ts +2 -0
- package/dist/plugins/installer/uninstall.js +18 -0
- package/dist/plugins/installer/unzip.d.ts +14 -0
- package/dist/plugins/installer/unzip.js +82 -0
- package/dist/plugins/installer/update.d.ts +28 -0
- package/dist/plugins/installer/update.js +97 -0
- package/dist/plugins/loadPluginHooks.d.ts +54 -1
- package/dist/plugins/loadPluginHooks.js +83 -1
- package/dist/plugins/marketplaceManager.d.ts +15 -1
- package/dist/plugins/marketplaceManager.js +58 -4
- package/dist/plugins/pluginCommandHook.js +34 -14
- package/dist/plugins/pluginContent.d.ts +30 -0
- package/dist/plugins/pluginContent.js +83 -0
- package/dist/plugins/pluginInstaller.d.ts +34 -0
- package/dist/plugins/pluginInstaller.js +168 -35
- package/dist/plugins/schemas.js +41 -6
- package/dist/plugins/types.d.ts +10 -0
- package/dist/preset/index.d.ts +7 -1
- package/dist/preset/index.js +112 -6
- package/dist/product/types.d.ts +1 -1
- package/dist/prompt/composer.d.ts +31 -0
- package/dist/prompt/composer.js +95 -25
- package/dist/prompt/instruction-scanner.js +5 -3
- package/dist/prompt/section-loader.js +1 -0
- package/dist/prompt/sections/base.md +1 -0
- package/dist/prompt/sections/browser.md +10 -0
- package/dist/prompt/sections/coding.md +4 -0
- package/dist/prompt/sections/orchestration.md +8 -0
- package/dist/protocol/chat-session-manager.d.ts +15 -2
- package/dist/protocol/chat-session-manager.js +46 -0
- package/dist/protocol/chat-session.d.ts +82 -0
- package/dist/protocol/chat-session.js +121 -1
- package/dist/protocol/client.d.ts +27 -0
- package/dist/protocol/client.js +38 -0
- package/dist/protocol/redact.d.ts +50 -0
- package/dist/protocol/redact.js +71 -0
- package/dist/protocol/server.d.ts +153 -2
- package/dist/protocol/server.js +807 -39
- package/dist/protocol/tcp-transport.d.ts +45 -0
- package/dist/protocol/tcp-transport.js +74 -0
- package/dist/protocol/transport.js +3 -2
- package/dist/protocol/types.d.ts +69 -0
- package/dist/protocol/types.js +22 -0
- package/dist/quota/credentials.d.ts +3 -0
- package/dist/quota/credentials.js +80 -0
- package/dist/quota/index.d.ts +36 -0
- package/dist/quota/index.js +155 -0
- package/dist/quota/types.d.ts +48 -0
- package/dist/quota/types.js +13 -0
- package/dist/remote/bridge.d.ts +11 -2
- package/dist/remote/bridge.js +61 -33
- package/dist/review/review-prompt.d.ts +28 -0
- package/dist/review/review-prompt.js +81 -0
- package/dist/run/ArtifactTracker.js +5 -4
- package/dist/run/EngineRunner.d.ts +27 -1
- package/dist/run/EngineRunner.js +46 -8
- package/dist/run/FileRunStore.js +40 -11
- package/dist/run/RunApprovalBackend.js +25 -5
- package/dist/run/RunLock.d.ts +28 -2
- package/dist/run/RunLock.js +49 -7
- package/dist/run/RunManager.d.ts +12 -0
- package/dist/run/RunManager.js +86 -3
- package/dist/run/RunQueue.d.ts +1 -0
- package/dist/run/RunQueue.js +8 -2
- package/dist/run/factory.d.ts +8 -1
- package/dist/run/factory.js +1 -0
- package/dist/run/index.d.ts +2 -2
- package/dist/run/index.js +1 -1
- package/dist/run/redirect-target.d.ts +7 -0
- package/dist/run/redirect-target.js +13 -0
- package/dist/run/types.js +1 -1
- package/dist/runtime/background-shell.d.ts +139 -0
- package/dist/runtime/background-shell.js +495 -0
- package/dist/runtime/output-clean.d.ts +24 -0
- package/dist/runtime/output-clean.js +41 -0
- package/dist/runtime/ring-file.d.ts +64 -0
- package/dist/runtime/ring-file.js +174 -0
- package/dist/runtime/safe-spawn.js +15 -34
- package/dist/runtime/spawn-common.d.ts +148 -0
- package/dist/runtime/spawn-common.js +303 -0
- package/dist/runtime/truncate-output.d.ts +22 -0
- package/dist/runtime/truncate-output.js +49 -0
- package/dist/runtime/utf8-cut.d.ts +11 -0
- package/dist/runtime/utf8-cut.js +23 -0
- package/dist/services/auto-dream.d.ts +4 -0
- package/dist/services/auto-dream.js +26 -26
- package/dist/services/browser-open.d.ts +13 -0
- package/dist/services/browser-open.js +16 -0
- package/dist/services/diagnostics.d.ts +1 -2
- package/dist/services/diagnostics.js +12 -7
- package/dist/services/dream-consolidation.d.ts +57 -0
- package/dist/services/dream-consolidation.js +151 -0
- package/dist/services/extract-memories.d.ts +14 -1
- package/dist/services/extract-memories.js +45 -6
- package/dist/services/memory-orchestrator.d.ts +21 -0
- package/dist/services/memory-orchestrator.js +125 -32
- package/dist/services/notifier.d.ts +8 -0
- package/dist/services/notifier.js +39 -25
- package/dist/services/oauth.js +5 -7
- package/dist/services/session-memory-sort.d.ts +8 -0
- package/dist/services/session-memory-sort.js +9 -0
- package/dist/services/session-memory.js +30 -19
- package/dist/session/file-history.d.ts +124 -1
- package/dist/session/file-history.js +232 -9
- package/dist/session/memory.d.ts +116 -2
- package/dist/session/memory.js +250 -28
- package/dist/session/session-manager.d.ts +63 -1
- package/dist/session/session-manager.js +186 -5
- package/dist/session/simple-diff.d.ts +23 -0
- package/dist/session/simple-diff.js +84 -0
- package/dist/session/transcript.d.ts +25 -1
- package/dist/session/transcript.js +35 -2
- package/dist/session/undo-target.d.ts +67 -0
- package/dist/session/undo-target.js +144 -0
- package/dist/settings/disk-defaults.d.ts +42 -0
- package/dist/settings/disk-defaults.js +33 -0
- package/dist/settings/feature-flags.d.ts +64 -0
- package/dist/settings/feature-flags.js +61 -0
- package/dist/settings/manager.d.ts +101 -1
- package/dist/settings/manager.js +345 -15
- package/dist/settings/migrate-config.d.ts +45 -0
- package/dist/settings/migrate-config.js +125 -0
- package/dist/settings/personalization.d.ts +19 -0
- package/dist/settings/personalization.js +7 -0
- package/dist/settings/schema-export.d.ts +25 -0
- package/dist/settings/schema-export.js +38 -0
- package/dist/settings/schema.d.ts +1827 -396
- package/dist/settings/schema.js +361 -81
- package/dist/skills/scanner.d.ts +9 -0
- package/dist/skills/scanner.js +30 -2
- package/dist/stt/resolve-transcribe.d.ts +31 -0
- package/dist/stt/resolve-transcribe.js +108 -0
- package/dist/stt/transcribe.d.ts +51 -0
- package/dist/stt/transcribe.js +65 -0
- package/dist/tool-system/browser-bridge.d.ts +226 -0
- package/dist/tool-system/browser-bridge.js +163 -0
- package/dist/tool-system/builtin/add-marketplace.d.ts +12 -0
- package/dist/tool-system/builtin/add-marketplace.js +76 -0
- package/dist/tool-system/builtin/agent-heartbeat.d.ts +49 -0
- package/dist/tool-system/builtin/agent-heartbeat.js +89 -0
- package/dist/tool-system/builtin/agent-notifications.d.ts +12 -3
- package/dist/tool-system/builtin/agent-notifications.js +9 -3
- package/dist/tool-system/builtin/agent-output-file.d.ts +38 -0
- package/dist/tool-system/builtin/agent-output-file.js +72 -0
- package/dist/tool-system/builtin/agent-registry.d.ts +23 -0
- package/dist/tool-system/builtin/agent-registry.js +16 -4
- package/dist/tool-system/builtin/agent.d.ts +50 -11
- package/dist/tool-system/builtin/agent.js +576 -73
- package/dist/tool-system/builtin/apply-patch/applier.js +66 -8
- package/dist/tool-system/builtin/apply-patch/backup-targets.d.ts +10 -0
- package/dist/tool-system/builtin/apply-patch/backup-targets.js +30 -0
- package/dist/tool-system/builtin/apply-patch/index.js +7 -3
- package/dist/tool-system/builtin/arena.js +6 -7
- package/dist/tool-system/builtin/background-jobs.d.ts +76 -0
- package/dist/tool-system/builtin/background-jobs.js +124 -0
- package/dist/tool-system/builtin/background-shell-tools.d.ts +20 -0
- package/dist/tool-system/builtin/background-shell-tools.js +108 -0
- package/dist/tool-system/builtin/background-work.d.ts +67 -0
- package/dist/tool-system/builtin/background-work.js +86 -0
- package/dist/tool-system/builtin/bash-output-style.d.ts +32 -0
- package/dist/tool-system/builtin/bash-output-style.js +40 -0
- package/dist/tool-system/builtin/bash.d.ts +5 -2
- package/dist/tool-system/builtin/bash.js +98 -62
- package/dist/tool-system/builtin/browser-tools.d.ts +33 -0
- package/dist/tool-system/builtin/browser-tools.js +312 -0
- package/dist/tool-system/builtin/cancel-goal.d.ts +31 -0
- package/dist/tool-system/builtin/cancel-goal.js +64 -0
- package/dist/tool-system/builtin/check-quota.d.ts +15 -0
- package/dist/tool-system/builtin/check-quota.js +34 -0
- package/dist/tool-system/builtin/complete-goal.d.ts +25 -0
- package/dist/tool-system/builtin/complete-goal.js +45 -0
- package/dist/tool-system/builtin/config.js +7 -0
- package/dist/tool-system/builtin/cron.d.ts +7 -0
- package/dist/tool-system/builtin/cron.js +105 -9
- package/dist/tool-system/builtin/drive-claude-code.d.ts +30 -0
- package/dist/tool-system/builtin/drive-claude-code.js +157 -0
- package/dist/tool-system/builtin/edit-model-catalog.d.ts +3 -0
- package/dist/tool-system/builtin/edit-model-catalog.js +94 -0
- package/dist/tool-system/builtin/edit.d.ts +2 -1
- package/dist/tool-system/builtin/edit.js +21 -12
- package/dist/tool-system/builtin/eol.d.ts +29 -0
- package/dist/tool-system/builtin/eol.js +37 -0
- package/dist/tool-system/builtin/file-cache.d.ts +6 -0
- package/dist/tool-system/builtin/file-cache.js +8 -0
- package/dist/tool-system/builtin/generate-image.d.ts +60 -0
- package/dist/tool-system/builtin/generate-image.js +374 -0
- package/dist/tool-system/builtin/generate-video.d.ts +55 -0
- package/dist/tool-system/builtin/generate-video.js +361 -0
- package/dist/tool-system/builtin/grep.js +6 -2
- package/dist/tool-system/builtin/image-providers.d.ts +86 -0
- package/dist/tool-system/builtin/image-providers.js +190 -0
- package/dist/tool-system/builtin/image-uploader.d.ts +33 -0
- package/dist/tool-system/builtin/image-uploader.js +74 -0
- package/dist/tool-system/builtin/index.d.ts +24 -1
- package/dist/tool-system/builtin/index.js +310 -25
- package/dist/tool-system/builtin/mcp-tools.js +23 -3
- package/dist/tool-system/builtin/memory.js +45 -7
- package/dist/tool-system/builtin/notebook-edit.d.ts +2 -1
- package/dist/tool-system/builtin/notebook-edit.js +1 -1
- package/dist/tool-system/builtin/powershell.js +4 -1
- package/dist/tool-system/builtin/read.d.ts +2 -1
- package/dist/tool-system/builtin/read.js +11 -3
- package/dist/tool-system/builtin/repl.js +4 -1
- package/dist/tool-system/builtin/skill.js +9 -0
- package/dist/tool-system/builtin/sleep.js +12 -5
- package/dist/tool-system/builtin/task.js +4 -1
- package/dist/tool-system/builtin/tool-search.js +25 -7
- package/dist/tool-system/builtin/update-automation-memory.d.ts +24 -0
- package/dist/tool-system/builtin/update-automation-memory.js +60 -0
- package/dist/tool-system/builtin/video-providers.d.ts +154 -0
- package/dist/tool-system/builtin/video-providers.js +235 -0
- package/dist/tool-system/builtin/view-image.d.ts +18 -0
- package/dist/tool-system/builtin/view-image.js +96 -0
- package/dist/tool-system/builtin/web-fetch.js +12 -2
- package/dist/tool-system/builtin/web-search.d.ts +4 -1
- package/dist/tool-system/builtin/web-search.js +57 -10
- package/dist/tool-system/builtin/worktree.d.ts +2 -1
- package/dist/tool-system/builtin/worktree.js +31 -4
- package/dist/tool-system/builtin/write.d.ts +2 -1
- package/dist/tool-system/builtin/write.js +1 -1
- package/dist/tool-system/context.d.ts +166 -6
- package/dist/tool-system/executor.d.ts +3 -2
- package/dist/tool-system/executor.js +197 -119
- package/dist/tool-system/investigation-guard.d.ts +4 -1
- package/dist/tool-system/investigation-guard.js +20 -4
- package/dist/tool-system/mcp-manager.d.ts +78 -16
- package/dist/tool-system/mcp-manager.js +357 -28
- package/dist/tool-system/path-policy.d.ts +99 -0
- package/dist/tool-system/path-policy.js +578 -0
- package/dist/tool-system/permission.d.ts +36 -1
- package/dist/tool-system/permission.js +322 -117
- package/dist/tool-system/plan-mode-allowlist.d.ts +32 -0
- package/dist/tool-system/plan-mode-allowlist.js +65 -0
- package/dist/tool-system/registry.d.ts +1 -0
- package/dist/tool-system/registry.js +26 -1
- package/dist/tool-system/sandbox/index.d.ts +8 -0
- package/dist/tool-system/sandbox/index.js +7 -2
- package/dist/tool-system/validate-tool-metadata.d.ts +36 -0
- package/dist/tool-system/validate-tool-metadata.js +63 -0
- package/dist/tool-system/validation.d.ts +10 -0
- package/dist/tool-system/validation.js +16 -3
- package/dist/types.d.ts +301 -15
- package/dist/updater.js +20 -9
- package/dist/utils/envUtils.d.ts +0 -9
- package/dist/utils/envUtils.js +3 -28
- package/dist/utils/exec.d.ts +40 -0
- package/dist/utils/exec.js +144 -0
- package/dist/utils/format.js +4 -2
- package/dist/utils/json.d.ts +12 -0
- package/dist/utils/json.js +92 -0
- package/dist/utils/lockfile.js +9 -2
- package/dist/utils/theme.d.ts +7 -1
- package/dist/utils/theme.js +5 -3
- package/dist/utils/toolDisplay.js +0 -1
- package/package.json +14 -7
- package/dist/agent/coordinator.d.ts +0 -49
- package/dist/agent/coordinator.js +0 -77
- package/dist/settings/manager.test.js +0 -73
- package/dist/tool-system/builtin/remote-trigger.d.ts +0 -6
- package/dist/tool-system/builtin/remote-trigger.js +0 -54
- package/dist/tool-system/builtin/send-message.d.ts +0 -6
- package/dist/tool-system/builtin/send-message.js +0 -47
- /package/dist/{settings/manager.test.d.ts → credentials/types.js} +0 -0
|
@@ -0,0 +1,578 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PathPolicy — shared classifier for file-tool path safety.
|
|
3
|
+
*
|
|
4
|
+
* Today the file tools (Read, Write, Edit, ApplyPatch, NotebookEdit) operate
|
|
5
|
+
* directly on host paths with no shared safety layer. acceptEdits / Bash
|
|
6
|
+
* sandboxing don't help here: a Write that an LLM points at ~/.aws/credentials
|
|
7
|
+
* or a path outside the workspace gets silently honored.
|
|
8
|
+
*
|
|
9
|
+
* This module is the MVP boundary called out in
|
|
10
|
+
* docs/superpowers/plans/2026-05-27-core-quality-iteration.md, Workstream B.
|
|
11
|
+
*
|
|
12
|
+
* Decision shape:
|
|
13
|
+
* "allow" — proceed without prompting (in-workspace and not sensitive)
|
|
14
|
+
* "ask" — caller must obtain user approval (outside workspace, OR
|
|
15
|
+
* sensitive-path read)
|
|
16
|
+
* "deny" — refuse outright (sensitive-path write)
|
|
17
|
+
*
|
|
18
|
+
* The classifier is pure: it resolves symlinks (best effort), checks against
|
|
19
|
+
* an explicit sensitive list, then compares against the workspace root. It
|
|
20
|
+
* never reads the file; the caller is the one with IO.
|
|
21
|
+
*
|
|
22
|
+
* Rollout escape hatch:
|
|
23
|
+
* CODESHELL_PATH_POLICY=off → classifyPath returns "allow" for everything
|
|
24
|
+
* (logged once per process). This is the reversible-rollout switch
|
|
25
|
+
* recorded in the plan's Definition of Done.
|
|
26
|
+
*
|
|
27
|
+
* acceptEdits cannot bypass this layer — by design, acceptEdits is a
|
|
28
|
+
* permission-system shortcut that lets routine in-workspace edits skip an
|
|
29
|
+
* approval round-trip; it is not an authority to write anywhere on disk.
|
|
30
|
+
* Callers must consult classifyPath before honoring acceptEdits.
|
|
31
|
+
*/
|
|
32
|
+
import { realpathSync, existsSync, readFileSync, writeFileSync, mkdirSync, renameSync, } from "node:fs";
|
|
33
|
+
import { homedir } from "node:os";
|
|
34
|
+
import { randomUUID } from "node:crypto";
|
|
35
|
+
import { dirname, isAbsolute, join, resolve as resolvePath, sep } from "node:path";
|
|
36
|
+
/**
|
|
37
|
+
* Default sensitive path patterns. These are evaluated AFTER home-expansion
|
|
38
|
+
* and resolution, so a literal "$HOME/.ssh" and a symlink at /tmp/x → ~/.ssh
|
|
39
|
+
* are both caught.
|
|
40
|
+
*
|
|
41
|
+
* Mirrors the existing list in sandbox/index.ts so Bash and file tools agree
|
|
42
|
+
* on what "sensitive" means — keep them in sync when adding entries.
|
|
43
|
+
*/
|
|
44
|
+
const SENSITIVE_DIR_PATTERNS = [
|
|
45
|
+
".ssh",
|
|
46
|
+
".aws",
|
|
47
|
+
".config/gcloud",
|
|
48
|
+
".code-shell",
|
|
49
|
+
".claude",
|
|
50
|
+
".gnupg",
|
|
51
|
+
".kube",
|
|
52
|
+
".docker",
|
|
53
|
+
];
|
|
54
|
+
/**
|
|
55
|
+
* Files that are sensitive regardless of where they live: an `.env` next to
|
|
56
|
+
* the code, an `id_rsa` in a random folder, etc.
|
|
57
|
+
*/
|
|
58
|
+
// Data/config extensions a credential FILE typically carries. Deliberately
|
|
59
|
+
// excludes source-code extensions (.ts/.tsx/.js/.py/.go/.rs/…) so a code file
|
|
60
|
+
// whose NAME happens to contain a secret-y word is never treated as a secret.
|
|
61
|
+
const SECRET_DATA_EXT = String.raw `(json|ya?ml|txt|ini|conf|cfg|toml|xml|properties|env|key|secret)`;
|
|
62
|
+
const SENSITIVE_FILE_PATTERNS = [
|
|
63
|
+
/^\.env(\..+)?$/i, // .env, .env.local, .env.production, …
|
|
64
|
+
/^id_(rsa|dsa|ecdsa|ed25519)(\.pub)?$/i,
|
|
65
|
+
/\.pem$/i,
|
|
66
|
+
/\.p12$/i,
|
|
67
|
+
/\.pfx$/i,
|
|
68
|
+
// Credential/secret ARTIFACT files: the secret word is the dominant stem AND
|
|
69
|
+
// the file carries a data/config extension (or none). This still catches
|
|
70
|
+
// credentials.json / secrets.yaml / auth.json / token.txt / api-secret.conf,
|
|
71
|
+
// but NOT source files like authController.ts, token-counter.ts,
|
|
72
|
+
// oauth-handler.ts whose code extension excludes them.
|
|
73
|
+
//
|
|
74
|
+
// Was previously bare substrings (/auth/i, /token/i, …) tested against the
|
|
75
|
+
// basename, which denied WRITES to any code file containing those words —
|
|
76
|
+
// breaking the agent's ability to edit ordinary auth/token source. See
|
|
77
|
+
// path-policy-sensitive-file.test.ts.
|
|
78
|
+
new RegExp(String.raw `^[^/]*\b(secrets?|credentials?|auth|token|apikey|api[-_]?key)\b[^/]*\.${SECRET_DATA_EXT}$`, "i"),
|
|
79
|
+
// Bare (extensionless) credential files: `secret`, `credentials`, `token`.
|
|
80
|
+
/^(secrets?|credentials?|token)$/i,
|
|
81
|
+
// Well-known credential files matched by EXACT name (anchored both ends, so
|
|
82
|
+
// npmrc-helper.ts / git-credentials.md stay writable). These carry no
|
|
83
|
+
// secret-y stem and aren't .env/.pem, so the patterns above miss them, yet
|
|
84
|
+
// they routinely hold auth tokens / passwords:
|
|
85
|
+
// .git-credentials (git creds), .npmrc (_authToken), .netrc (login creds),
|
|
86
|
+
// .pgpass (postgres passwords).
|
|
87
|
+
/^\.git-credentials$/i,
|
|
88
|
+
/^\.npmrc$/i,
|
|
89
|
+
/^\.netrc$/i,
|
|
90
|
+
/^\.pgpass$/i,
|
|
91
|
+
];
|
|
92
|
+
const ENV_DISABLE = "CODESHELL_PATH_POLICY";
|
|
93
|
+
let warnedDisabled = false;
|
|
94
|
+
function policyDisabled() {
|
|
95
|
+
const v = process.env[ENV_DISABLE];
|
|
96
|
+
return v === "off" || v === "0" || v === "false";
|
|
97
|
+
}
|
|
98
|
+
function expandTilde(p) {
|
|
99
|
+
if (p === "~")
|
|
100
|
+
return homedir();
|
|
101
|
+
if (p.startsWith("~/") || p.startsWith("~" + sep)) {
|
|
102
|
+
return homedir() + p.slice(1);
|
|
103
|
+
}
|
|
104
|
+
return p;
|
|
105
|
+
}
|
|
106
|
+
/** sessionId → set of approved directory grants (prefix + operation). */
|
|
107
|
+
const sessionPathGrants = new Map();
|
|
108
|
+
/** True if a grant for `grantOp` authorizes an operation of `wantOp`. */
|
|
109
|
+
function grantCoversOp(grantOp, wantOp) {
|
|
110
|
+
// write ⊇ {read, write}; read ⊇ {read}.
|
|
111
|
+
return grantOp === "write" || grantOp === wantOp;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Normalize a path for comparison. Windows file systems are case-INsensitive,
|
|
115
|
+
* so `C:\Users\Admin\.ssh` and `c:\users\admin\.ssh` are the same path — a
|
|
116
|
+
* case-sensitive `startsWith` would let a sensitive-path or workspace-boundary
|
|
117
|
+
* check be bypassed by varying case (or just fail to match a legit prefix).
|
|
118
|
+
* Lowercase on win32 only; POSIX stays exact (macOS APFS can be case-sensitive,
|
|
119
|
+
* and the existing contract is case-sensitive there).
|
|
120
|
+
*/
|
|
121
|
+
function normPath(p) {
|
|
122
|
+
return process.platform === "win32" ? p.toLowerCase() : p;
|
|
123
|
+
}
|
|
124
|
+
/** Normalize a directory to an absolute prefix ending in `sep` for prefix tests. */
|
|
125
|
+
function dirPrefix(absPath) {
|
|
126
|
+
const d = absPath.endsWith(sep) ? absPath : absPath + sep;
|
|
127
|
+
return d;
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* True if `resolved` sits inside any grant whose prefix covers it AND whose
|
|
131
|
+
* operation authorizes `operation` (write grants cover reads; read grants do
|
|
132
|
+
* not cover writes).
|
|
133
|
+
*/
|
|
134
|
+
function coveredBy(grants, resolved, operation) {
|
|
135
|
+
const target = normPath(resolved.endsWith(sep) ? resolved : resolved + sep);
|
|
136
|
+
for (const g of grants) {
|
|
137
|
+
if (!grantCoversOp(g.op, operation))
|
|
138
|
+
continue;
|
|
139
|
+
const gn = normPath(g.prefix);
|
|
140
|
+
if (target === gn || target.startsWith(gn))
|
|
141
|
+
return true;
|
|
142
|
+
}
|
|
143
|
+
return false;
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Read persisted project grants. Two on-disk shapes are accepted:
|
|
147
|
+
* - legacy bare string "/dir/" → interpreted as a READ-only grant
|
|
148
|
+
* (conservative: pre-fix entries were written without an operation, and we
|
|
149
|
+
* must not retroactively treat them as write authority).
|
|
150
|
+
* - object { path: "/dir/", op: "write" } → operation as stored.
|
|
151
|
+
*/
|
|
152
|
+
function projectPathGrants(cwd) {
|
|
153
|
+
const file = join(cwd, ".code-shell", "settings.local.json");
|
|
154
|
+
if (!existsSync(file))
|
|
155
|
+
return [];
|
|
156
|
+
try {
|
|
157
|
+
const s = JSON.parse(readFileSync(file, "utf-8"));
|
|
158
|
+
if (!Array.isArray(s.pathApprovals))
|
|
159
|
+
return [];
|
|
160
|
+
const out = [];
|
|
161
|
+
for (const entry of s.pathApprovals) {
|
|
162
|
+
if (typeof entry === "string") {
|
|
163
|
+
out.push({ prefix: entry, op: "read" });
|
|
164
|
+
}
|
|
165
|
+
else if (entry &&
|
|
166
|
+
typeof entry === "object" &&
|
|
167
|
+
typeof entry.path === "string") {
|
|
168
|
+
const op = entry.op === "write" ? "write" : "read";
|
|
169
|
+
out.push({ prefix: entry.path, op });
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
return out;
|
|
173
|
+
}
|
|
174
|
+
catch {
|
|
175
|
+
return [];
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
/** Has the user already approved `operation` on a directory covering `resolved`? */
|
|
179
|
+
function isPathPreApproved(resolved, operation, cwd, sessionId) {
|
|
180
|
+
if (sessionId) {
|
|
181
|
+
const s = sessionPathGrants.get(sessionId);
|
|
182
|
+
if (s && coveredBy(s, resolved, operation))
|
|
183
|
+
return true;
|
|
184
|
+
}
|
|
185
|
+
return coveredBy(projectPathGrants(cwd), resolved, operation);
|
|
186
|
+
}
|
|
187
|
+
function storedPrefix(g) {
|
|
188
|
+
return typeof g === "string" ? g : g.path;
|
|
189
|
+
}
|
|
190
|
+
function storedOp(g) {
|
|
191
|
+
return typeof g === "string" ? "read" : g.op;
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Record a session/project grant for the DIRECTORY containing `resolved`,
|
|
195
|
+
* tagged with the OPERATION the user approved. A write grant subsumes read,
|
|
196
|
+
* so granting write where a read grant already exists upgrades it.
|
|
197
|
+
*/
|
|
198
|
+
function recordPathApproval(scope, resolved, operation, cwd, sessionId) {
|
|
199
|
+
if (scope === "once")
|
|
200
|
+
return;
|
|
201
|
+
const prefix = dirPrefix(dirname(resolved));
|
|
202
|
+
if (scope === "session") {
|
|
203
|
+
if (!sessionId)
|
|
204
|
+
return;
|
|
205
|
+
let s = sessionPathGrants.get(sessionId);
|
|
206
|
+
if (!s) {
|
|
207
|
+
s = new Set();
|
|
208
|
+
sessionPathGrants.set(sessionId, s);
|
|
209
|
+
}
|
|
210
|
+
// Drop a weaker (read) grant for the same prefix when upgrading to write,
|
|
211
|
+
// and skip adding a redundant read when a write grant already covers it.
|
|
212
|
+
for (const g of s) {
|
|
213
|
+
if (g.prefix !== prefix)
|
|
214
|
+
continue;
|
|
215
|
+
if (grantCoversOp(g.op, operation))
|
|
216
|
+
return; // already covered
|
|
217
|
+
if (operation === "write" && g.op === "read")
|
|
218
|
+
s.delete(g); // upgrade
|
|
219
|
+
}
|
|
220
|
+
s.add({ prefix, op: operation });
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
223
|
+
// project: persist to settings.local.json (atomic, idempotent).
|
|
224
|
+
const dir = join(cwd, ".code-shell");
|
|
225
|
+
const file = join(dir, "settings.local.json");
|
|
226
|
+
try {
|
|
227
|
+
// Don't resurrect a deleted project root: a recursive mkdir of
|
|
228
|
+
// <cwd>/.code-shell recreates `cwd` itself as an empty shell when cwd is
|
|
229
|
+
// gone. Persistence here is best-effort, so skip when the root is missing.
|
|
230
|
+
if (!existsSync(cwd))
|
|
231
|
+
return;
|
|
232
|
+
if (!existsSync(dir))
|
|
233
|
+
mkdirSync(dir, { recursive: true });
|
|
234
|
+
let settings = {};
|
|
235
|
+
if (existsSync(file)) {
|
|
236
|
+
try {
|
|
237
|
+
settings = JSON.parse(readFileSync(file, "utf-8"));
|
|
238
|
+
}
|
|
239
|
+
catch {
|
|
240
|
+
/* start fresh on parse error */
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
if (!Array.isArray(settings.pathApprovals))
|
|
244
|
+
settings.pathApprovals = [];
|
|
245
|
+
const existing = settings.pathApprovals.find((g) => storedPrefix(g) === prefix);
|
|
246
|
+
if (existing) {
|
|
247
|
+
// Already covered (read-or-better matching the request)? Nothing to do.
|
|
248
|
+
if (grantCoversOp(storedOp(existing), operation))
|
|
249
|
+
return;
|
|
250
|
+
// Upgrade read → write in place.
|
|
251
|
+
if (operation === "write") {
|
|
252
|
+
settings.pathApprovals = settings.pathApprovals.map((g) => storedPrefix(g) === prefix ? { path: prefix, op: "write" } : g);
|
|
253
|
+
}
|
|
254
|
+
else {
|
|
255
|
+
return;
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
else {
|
|
259
|
+
settings.pathApprovals.push({ path: prefix, op: operation });
|
|
260
|
+
}
|
|
261
|
+
// randomUUID() guards against temp-name collisions between writers in
|
|
262
|
+
// the same millisecond; the rename keeps the swap atomic.
|
|
263
|
+
const tmp = `${file}.${randomUUID()}.tmp`;
|
|
264
|
+
writeFileSync(tmp, JSON.stringify(settings, null, 2) + "\n", "utf-8");
|
|
265
|
+
renameSync(tmp, file);
|
|
266
|
+
}
|
|
267
|
+
catch {
|
|
268
|
+
// Persistence is best-effort; an unwritable disk must not break the op.
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
/** Test seam: clear the in-memory session grants. */
|
|
272
|
+
export function _resetSessionPathGrants() {
|
|
273
|
+
sessionPathGrants.clear();
|
|
274
|
+
}
|
|
275
|
+
/**
|
|
276
|
+
* Best-effort resolution. realpath fails when the path doesn't exist yet —
|
|
277
|
+
* the common case for Write creating a new file. We walk up to the nearest
|
|
278
|
+
* existing ancestor, realpath *that*, then re-append the remaining segments.
|
|
279
|
+
*
|
|
280
|
+
* Why this matters: on macOS, /var is a symlink to /private/var, so a
|
|
281
|
+
* tmpdir() workspace at /var/folders/... realpaths to /private/var/folders/...
|
|
282
|
+
* If we naively `resolve()` a non-existing child of the workspace, its
|
|
283
|
+
* prefix won't match the realpathed workspace and an in-workspace write
|
|
284
|
+
* would be misclassified as outside-workspace.
|
|
285
|
+
*/
|
|
286
|
+
function safeRealpath(p) {
|
|
287
|
+
const abs = isAbsolute(p) ? p : resolvePath(process.cwd(), p);
|
|
288
|
+
// Walk up to the nearest existing ancestor.
|
|
289
|
+
let candidate = abs;
|
|
290
|
+
const segments = [];
|
|
291
|
+
// Cap the walk so a pathological input can't spin forever.
|
|
292
|
+
for (let i = 0; i < 64; i++) {
|
|
293
|
+
try {
|
|
294
|
+
const resolved = realpathSync(candidate);
|
|
295
|
+
if (segments.length === 0)
|
|
296
|
+
return resolved;
|
|
297
|
+
return resolvePath(resolved, ...segments.reverse());
|
|
298
|
+
}
|
|
299
|
+
catch {
|
|
300
|
+
const parent = dirname(candidate);
|
|
301
|
+
if (parent === candidate) {
|
|
302
|
+
// Reached root without finding anything that exists — return the
|
|
303
|
+
// original absolute form so the caller still has a usable path.
|
|
304
|
+
return abs;
|
|
305
|
+
}
|
|
306
|
+
segments.push(candidate.slice(parent.length + (parent.endsWith(sep) ? 0 : 1)));
|
|
307
|
+
candidate = parent;
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
return abs;
|
|
311
|
+
}
|
|
312
|
+
function isInsideDir(child, parent) {
|
|
313
|
+
const c = normPath(child);
|
|
314
|
+
const par = normPath(parent);
|
|
315
|
+
const p = par.endsWith(sep) ? par : par + sep;
|
|
316
|
+
return c === par || c.startsWith(p);
|
|
317
|
+
}
|
|
318
|
+
/**
|
|
319
|
+
* Returns the matching sensitive-dir entry (with the user's home prefix) if
|
|
320
|
+
* `resolved` lives underneath any sensitive directory, else undefined.
|
|
321
|
+
*/
|
|
322
|
+
function matchSensitiveDir(resolved) {
|
|
323
|
+
const home = homedir();
|
|
324
|
+
for (const rel of SENSITIVE_DIR_PATTERNS) {
|
|
325
|
+
const full = home + sep + rel;
|
|
326
|
+
if (isInsideDir(resolved, full))
|
|
327
|
+
return "~/" + rel;
|
|
328
|
+
}
|
|
329
|
+
return undefined;
|
|
330
|
+
}
|
|
331
|
+
/**
|
|
332
|
+
* Returns the matching pattern label if the basename matches a sensitive
|
|
333
|
+
* file rule, else undefined.
|
|
334
|
+
*/
|
|
335
|
+
function matchSensitiveFile(resolved) {
|
|
336
|
+
const base = resolved.slice(resolved.lastIndexOf(sep) + 1);
|
|
337
|
+
for (const re of SENSITIVE_FILE_PATTERNS) {
|
|
338
|
+
if (re.test(base))
|
|
339
|
+
return base;
|
|
340
|
+
}
|
|
341
|
+
return undefined;
|
|
342
|
+
}
|
|
343
|
+
function isSafeCodeShellDiagnosticRead(resolved) {
|
|
344
|
+
const home = homedir();
|
|
345
|
+
const root = home + sep + ".code-shell";
|
|
346
|
+
if (!isInsideDir(resolved, root))
|
|
347
|
+
return false;
|
|
348
|
+
const rel = resolved.slice(root.length + 1);
|
|
349
|
+
const parts = rel.split(sep).filter(Boolean);
|
|
350
|
+
if (parts[0] === "sessions" && /^s-[A-Za-z0-9_-]+$/.test(parts[1] ?? "")) {
|
|
351
|
+
return parts[2] === "tool-results" || parts[2] === "logs" || parts[2] === "transcript";
|
|
352
|
+
}
|
|
353
|
+
if (parts[0] === "logs") {
|
|
354
|
+
const name = parts[1] ?? "";
|
|
355
|
+
return /^(desktop|tui|agent|main)-.+\.log$/i.test(name);
|
|
356
|
+
}
|
|
357
|
+
return false;
|
|
358
|
+
}
|
|
359
|
+
/**
|
|
360
|
+
* Classify a file path against the workspace + sensitive-path policy.
|
|
361
|
+
*
|
|
362
|
+
* Decision matrix:
|
|
363
|
+
*
|
|
364
|
+
* read write
|
|
365
|
+
* inside workspace, not sens. allow allow
|
|
366
|
+
* inside workspace, sensitive ask deny
|
|
367
|
+
* outside workspace, not sens. ask ask
|
|
368
|
+
* outside workspace, sensitive ask deny
|
|
369
|
+
*
|
|
370
|
+
* Sensitive wins over workspace placement: a `.env` checked into the project
|
|
371
|
+
* still asks on read and denies on write.
|
|
372
|
+
*/
|
|
373
|
+
export function classifyPath(rawPath, opts) {
|
|
374
|
+
if (typeof rawPath !== "string" || rawPath.length === 0) {
|
|
375
|
+
return { decision: "deny", reason: "empty path", resolvedPath: "" };
|
|
376
|
+
}
|
|
377
|
+
if (policyDisabled()) {
|
|
378
|
+
if (!warnedDisabled) {
|
|
379
|
+
// One-shot stderr nudge so an operator who flipped the flag sees it
|
|
380
|
+
// surfaced. We deliberately don't import the logger here to keep this
|
|
381
|
+
// module dependency-light — sanitize-messages can find this entry
|
|
382
|
+
// separately when callers log their PathPolicy decisions.
|
|
383
|
+
// eslint-disable-next-line no-console
|
|
384
|
+
console.warn(`[path-policy] CODESHELL_PATH_POLICY=${process.env[ENV_DISABLE]} — file path enforcement is OFF`);
|
|
385
|
+
warnedDisabled = true;
|
|
386
|
+
}
|
|
387
|
+
return { decision: "allow", reason: "policy disabled", resolvedPath: rawPath };
|
|
388
|
+
}
|
|
389
|
+
const expanded = expandTilde(rawPath);
|
|
390
|
+
const resolved = safeRealpath(expanded);
|
|
391
|
+
const workspace = safeRealpath(opts.workspaceRoot);
|
|
392
|
+
const sensitiveDir = matchSensitiveDir(resolved);
|
|
393
|
+
const sensitiveFile = matchSensitiveFile(resolved);
|
|
394
|
+
const sensitiveLabel = sensitiveDir ?? sensitiveFile;
|
|
395
|
+
const insideWorkspace = isInsideDir(resolved, workspace);
|
|
396
|
+
// Sensitive: write is always denied, read always asks. Workspace placement
|
|
397
|
+
// doesn't soften the rule — an `.env` in the project still asks on read.
|
|
398
|
+
if (sensitiveLabel) {
|
|
399
|
+
if (opts.operation === "read" && isSafeCodeShellDiagnosticRead(resolved)) {
|
|
400
|
+
return {
|
|
401
|
+
decision: "allow",
|
|
402
|
+
reason: "safe CodeShell diagnostic read",
|
|
403
|
+
resolvedPath: resolved,
|
|
404
|
+
};
|
|
405
|
+
}
|
|
406
|
+
if (opts.operation === "write") {
|
|
407
|
+
return {
|
|
408
|
+
decision: "deny",
|
|
409
|
+
reason: `sensitive path (${sensitiveLabel}): writes are not permitted`,
|
|
410
|
+
resolvedPath: resolved,
|
|
411
|
+
};
|
|
412
|
+
}
|
|
413
|
+
return {
|
|
414
|
+
decision: "ask",
|
|
415
|
+
reason: `sensitive path (${sensitiveLabel}): read requires approval`,
|
|
416
|
+
resolvedPath: resolved,
|
|
417
|
+
};
|
|
418
|
+
}
|
|
419
|
+
if (insideWorkspace) {
|
|
420
|
+
return { decision: "allow", reason: "inside workspace", resolvedPath: resolved };
|
|
421
|
+
}
|
|
422
|
+
// Outside workspace: ask for both read and write. The conservative bias
|
|
423
|
+
// matches the plan's leaning answer to Q1 — ask on sensitive reads, deny
|
|
424
|
+
// on silently-allowed writes; outside-workspace falls in between.
|
|
425
|
+
return {
|
|
426
|
+
decision: "ask",
|
|
427
|
+
reason: "outside workspace: caller approval required",
|
|
428
|
+
resolvedPath: resolved,
|
|
429
|
+
};
|
|
430
|
+
}
|
|
431
|
+
/**
|
|
432
|
+
* Convenience wrapper for the file-tool integration. Pass the ToolContext's
|
|
433
|
+
* cwd (or undefined for non-LLM call sites), the target path, and the
|
|
434
|
+
* operation; returns either null (proceed) or an error string (refuse).
|
|
435
|
+
*
|
|
436
|
+
* Semantics:
|
|
437
|
+
* - decision="allow" → returns null.
|
|
438
|
+
* - decision="deny" → returns a "blocked by path policy" message.
|
|
439
|
+
* - decision="ask" → MVP: without a hooked-up askUser path here, we
|
|
440
|
+
* translate ask → refuse with an explanatory error
|
|
441
|
+
* so the LLM sees the refusal and can choose a
|
|
442
|
+
* different path. This is the conservative choice
|
|
443
|
+
* the plan calls out for the MVP rollout.
|
|
444
|
+
*
|
|
445
|
+
* `workspaceRoot === undefined` is the explicit signal that the caller is
|
|
446
|
+
* NOT an LLM-driven tool invocation (the ToolRegistry always threads ctx
|
|
447
|
+
* through, ctx always carries cwd). Standalone tests, scripts importing
|
|
448
|
+
* a tool function directly, and a few legacy CLI paths can be in this
|
|
449
|
+
* shape — we bypass policy for them rather than pretending process.cwd()
|
|
450
|
+
* is a meaningful workspace. The CODESHELL_PATH_POLICY=off env switch
|
|
451
|
+
* remains the rollback knob for the LLM-driven path.
|
|
452
|
+
*/
|
|
453
|
+
export function enforcePathPolicy(filePath, operation, workspaceRoot) {
|
|
454
|
+
if (workspaceRoot === undefined)
|
|
455
|
+
return null;
|
|
456
|
+
const c = classifyPath(filePath, { workspaceRoot, operation });
|
|
457
|
+
if (c.decision === "allow")
|
|
458
|
+
return null;
|
|
459
|
+
if (c.decision === "deny") {
|
|
460
|
+
return `Error: blocked by path policy — ${c.reason}. Path: ${c.resolvedPath}`;
|
|
461
|
+
}
|
|
462
|
+
// ask — MVP refuses with explanatory message until askUser plumbing
|
|
463
|
+
// lands. The conservative bias matches the plan's leaning answer for Q1.
|
|
464
|
+
return `Error: path requires approval — ${c.reason}. Path: ${c.resolvedPath}. ` +
|
|
465
|
+
`Set CODESHELL_PATH_POLICY=off to disable enforcement during a rollback.`;
|
|
466
|
+
}
|
|
467
|
+
export async function enforcePathPolicyWithApproval(filePath, operation, ctx) {
|
|
468
|
+
if (ctx?.cwd === undefined)
|
|
469
|
+
return null;
|
|
470
|
+
// bypassPermissions ("完全访问") skips the path-approval layer entirely,
|
|
471
|
+
// matching the tool-permission backend and CC (bypass skips ALL checks,
|
|
472
|
+
// including path validation). This is what makes "完全访问" actually mean
|
|
473
|
+
// full access — previously path policy ran regardless of mode, so a user
|
|
474
|
+
// who chose full access still got prompted for project-external reads.
|
|
475
|
+
if (ctx.permissionMode === "bypassPermissions")
|
|
476
|
+
return null;
|
|
477
|
+
const c = classifyPath(filePath, { workspaceRoot: ctx.cwd, operation });
|
|
478
|
+
if (c.decision === "allow")
|
|
479
|
+
return null;
|
|
480
|
+
if (c.decision === "deny") {
|
|
481
|
+
return `Error: blocked by path policy — ${c.reason}. Path: ${c.resolvedPath}`;
|
|
482
|
+
}
|
|
483
|
+
if (operation === "write" && ctx.planMode) {
|
|
484
|
+
return `Error: blocked by path policy — ${c.reason}. Path: ${c.resolvedPath}. ` +
|
|
485
|
+
`Plan mode does not allow file writes.`;
|
|
486
|
+
}
|
|
487
|
+
// Already approved this directory for this OPERATION (this session or
|
|
488
|
+
// persisted for the project)? Proceed without re-prompting — this is the fix
|
|
489
|
+
// for "I keep having to click allow for the same folder". A read grant does
|
|
490
|
+
// NOT cover a write; a write grant covers both.
|
|
491
|
+
if (isPathPreApproved(c.resolvedPath, operation, ctx.cwd, ctx.sessionId))
|
|
492
|
+
return null;
|
|
493
|
+
if (!ctx.askUser) {
|
|
494
|
+
return `Error: path requires approval — ${c.reason}. Path: ${c.resolvedPath}. ` +
|
|
495
|
+
`No interactive approval UI is available in this run.`;
|
|
496
|
+
}
|
|
497
|
+
// Serialize concurrent asks (per session) and RE-CHECK grants when our turn
|
|
498
|
+
// comes. Parallel tools hitting the same not-yet-approved directory all pass
|
|
499
|
+
// the pre-approved check above before the first grant lands, so each used to
|
|
500
|
+
// queue its own card — the user got a burst of identical 路径权限 prompts.
|
|
501
|
+
// Now the first "本目录允许" answer silently absorbs the queued rest.
|
|
502
|
+
const chainKey = ctx.sessionId ?? "__global__";
|
|
503
|
+
const prevTurn = askChains.get(chainKey) ?? Promise.resolve();
|
|
504
|
+
let release;
|
|
505
|
+
askChains.set(chainKey, new Promise((r) => (release = r)));
|
|
506
|
+
try {
|
|
507
|
+
await prevTurn;
|
|
508
|
+
if (isPathPreApproved(c.resolvedPath, operation, ctx.cwd, ctx.sessionId))
|
|
509
|
+
return null;
|
|
510
|
+
return await promptForPathApproval(c, operation, ctx);
|
|
511
|
+
}
|
|
512
|
+
finally {
|
|
513
|
+
release();
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
/** The actual interactive ask — split out so the serialized section reads flat.
|
|
517
|
+
* Caller has already verified ctx.askUser and ctx.cwd are present. */
|
|
518
|
+
async function promptForPathApproval(c, operation, ctx) {
|
|
519
|
+
// Title by the ACTUAL reason, not always "工作区外": a sensitive file
|
|
520
|
+
// (e.g. ~/.ssh, .env) can sit INSIDE the workspace, so the old fixed
|
|
521
|
+
// "工作区外路径" header was misleading for sensitive-path asks.
|
|
522
|
+
const isSensitive = c.reason.startsWith("sensitive");
|
|
523
|
+
const what = operation === "read" ? "读取" : "写入";
|
|
524
|
+
const title = isSensitive
|
|
525
|
+
? `工具想${what}敏感文件`
|
|
526
|
+
: `工具想${what}工作区外路径`;
|
|
527
|
+
const header = isSensitive ? "敏感文件权限" : "路径权限";
|
|
528
|
+
// Scope options carry remembered grants for the directory of this path, so
|
|
529
|
+
// the same folder isn't re-prompted. Labels are matched by exact string
|
|
530
|
+
// (the ask is optionsOnly — no free-text box that could silently fail to
|
|
531
|
+
// match). 仅本次 carries no memory.
|
|
532
|
+
const grantDir = dirname(c.resolvedPath);
|
|
533
|
+
const ALLOW_ONCE = "允许本次";
|
|
534
|
+
const ALLOW_SESSION = "本目录本会话允许";
|
|
535
|
+
const ALLOW_PROJECT = "本目录本项目允许";
|
|
536
|
+
const answer = (await ctx.askUser(`${title}:\n${c.resolvedPath}\n\n原因:${c.reason}\n是否允许本次操作?`, {
|
|
537
|
+
header,
|
|
538
|
+
options: [
|
|
539
|
+
{ label: ALLOW_ONCE, description: "仅允许当前这一次文件操作继续执行", tone: "ok" },
|
|
540
|
+
{
|
|
541
|
+
label: ALLOW_SESSION,
|
|
542
|
+
description: `本会话内不再询问 ${grantDir} 下的文件`,
|
|
543
|
+
tone: "ok",
|
|
544
|
+
},
|
|
545
|
+
{
|
|
546
|
+
label: ALLOW_PROJECT,
|
|
547
|
+
description: `永久允许 ${grantDir} 下的文件(写入 .code-shell/settings.local.json)`,
|
|
548
|
+
tone: "ok",
|
|
549
|
+
},
|
|
550
|
+
{ label: "拒绝", description: "阻止当前文件操作", tone: "danger" },
|
|
551
|
+
],
|
|
552
|
+
// Closed-set decision: no free-text "其它…" box. The answer is matched
|
|
553
|
+
// against the labels below by exact string, so a typed answer must not
|
|
554
|
+
// be allowed (it could never match and would silently deny).
|
|
555
|
+
optionsOnly: true,
|
|
556
|
+
})).trim();
|
|
557
|
+
if (answer === ALLOW_ONCE)
|
|
558
|
+
return null;
|
|
559
|
+
if (answer === ALLOW_SESSION) {
|
|
560
|
+
recordPathApproval("session", c.resolvedPath, operation, ctx.cwd, ctx.sessionId);
|
|
561
|
+
return null;
|
|
562
|
+
}
|
|
563
|
+
if (answer === ALLOW_PROJECT) {
|
|
564
|
+
recordPathApproval("project", c.resolvedPath, operation, ctx.cwd, ctx.sessionId);
|
|
565
|
+
return null;
|
|
566
|
+
}
|
|
567
|
+
return `Error: path approval denied by user — ${c.reason}. Path: ${c.resolvedPath}`;
|
|
568
|
+
}
|
|
569
|
+
/** Per-session prompt chains for enforcePathPolicyWithApproval (see comment there). */
|
|
570
|
+
const askChains = new Map();
|
|
571
|
+
/**
|
|
572
|
+
* Internal: reset the "disabled warning" latch. Tests flip the env var
|
|
573
|
+
* between cases and need each one to be able to re-trigger the warning.
|
|
574
|
+
* Not exported on the public surface beyond test usage.
|
|
575
|
+
*/
|
|
576
|
+
export function __resetPathPolicyWarnLatchForTests() {
|
|
577
|
+
warnedDisabled = false;
|
|
578
|
+
}
|
|
@@ -34,11 +34,13 @@ export declare class AutoApprovalBackend implements ApprovalBackend {
|
|
|
34
34
|
* the next time the user opens the project.
|
|
35
35
|
*/
|
|
36
36
|
export declare class InteractiveApprovalBackend implements ApprovalBackend {
|
|
37
|
-
private
|
|
37
|
+
private sessionAllowRules;
|
|
38
|
+
private sessionDenyRules;
|
|
38
39
|
private promptFn;
|
|
39
40
|
private cwd;
|
|
40
41
|
private savedProjectRules;
|
|
41
42
|
private onProjectRules;
|
|
43
|
+
private promptTurn;
|
|
42
44
|
setPromptFn(fn: (request: ApprovalRequest) => Promise<ApprovalResult>): void;
|
|
43
45
|
/**
|
|
44
46
|
* Has someone installed a real prompt callback? Engine consults this
|
|
@@ -58,10 +60,43 @@ export declare class InteractiveApprovalBackend implements ApprovalBackend {
|
|
|
58
60
|
*/
|
|
59
61
|
setOnProjectRules(fn: (rules: PermissionRule[]) => void): void;
|
|
60
62
|
requestApproval(req: ApprovalRequest): Promise<ApprovalResult>;
|
|
63
|
+
/** Session-rule lookup — operation-scoped (see sessionAllowRules doc). Deny
|
|
64
|
+
* wins over allow if both somehow match (conservative). Null = no rule. */
|
|
65
|
+
private checkSessionRules;
|
|
66
|
+
/** The actual interactive ask + rule recording (runs inside the prompt turn). */
|
|
67
|
+
private promptAndRecord;
|
|
61
68
|
}
|
|
69
|
+
/**
|
|
70
|
+
* Build the `file_path` argsPattern that narrows a remembered file-tool grant
|
|
71
|
+
* to a path scope. Pure: takes an ALREADY-RESOLVED absolute path (the caller
|
|
72
|
+
* normalizes against cwd so `../` can't slip past the prefix) and the scope.
|
|
73
|
+
*
|
|
74
|
+
* "file" → `^<esc(abs)>$` only this exact file
|
|
75
|
+
* "dir" → `^<esc(dir(abs))>/` this file's directory + subdirectories
|
|
76
|
+
* "tool" → null no narrowing (tool-wide; legacy behavior)
|
|
77
|
+
*
|
|
78
|
+
* The dir form keeps a trailing slash so `/repo/src/` can't match a sibling
|
|
79
|
+
* like `/repo/src-secret/x.ts`. Regex metacharacters in the path are escaped.
|
|
80
|
+
*/
|
|
81
|
+
export declare function pathRuleArgsPattern(absPath: string, scope: "file" | "dir" | "tool"): {
|
|
82
|
+
file_path: string;
|
|
83
|
+
} | null;
|
|
84
|
+
/**
|
|
85
|
+
* Does `rule` match this tool call? Shared by the {@link PermissionClassifier}
|
|
86
|
+
* (project/user rules) and {@link InteractiveApprovalBackend}'s session cache
|
|
87
|
+
* so the two never diverge on what "the same operation" means. Handles the
|
|
88
|
+
* JSON round-trip where a persisted RegExp comes back as a string.
|
|
89
|
+
*/
|
|
90
|
+
export declare function ruleMatches(rule: PermissionRule, toolName: string, args: Record<string, unknown>): boolean;
|
|
62
91
|
export declare function getInteractiveApprovalBackend(): InteractiveApprovalBackend;
|
|
63
92
|
export declare function setInteractiveApprovalFn(fn: (request: ApprovalRequest) => Promise<ApprovalResult>): void;
|
|
64
93
|
type BashSafetyLevel = "safe-read" | "safe-write" | "unsafe" | "dangerous";
|
|
94
|
+
/**
|
|
95
|
+
* True when an otherwise-safe-read shell segment must be downgraded to `unsafe`
|
|
96
|
+
* because it dumps the environment or reads a sensitive credential/secret path.
|
|
97
|
+
* Exported for direct unit testing of the predicate.
|
|
98
|
+
*/
|
|
99
|
+
export declare function segmentIsSensitiveRead(segment: string): boolean;
|
|
65
100
|
export declare function classifyBashCommand(command: string): BashSafetyLevel;
|
|
66
101
|
export declare class DenialTracker {
|
|
67
102
|
private denials;
|