@machina.ai/cell-cli-core 1.38.1-rc2 → 1.40.1-rc2
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/dist/docs/AFTER_MERGE_PROMPT.md +1 -1
- package/dist/docs/admin/enterprise-controls.md +1 -1
- package/dist/docs/changelogs/index.md +42 -0
- package/dist/docs/changelogs/latest.md +254 -361
- package/dist/docs/changelogs/preview.md +237 -406
- package/dist/docs/cli/acp-mode.md +6 -6
- package/dist/docs/cli/auto-memory.md +143 -0
- package/dist/docs/cli/checkpointing.md +5 -5
- package/dist/docs/cli/cli-reference.md +12 -11
- package/dist/docs/cli/creating-skills.md +2 -2
- package/dist/docs/cli/custom-commands.md +15 -14
- package/dist/docs/cli/enterprise.md +17 -14
- package/dist/docs/cli/gemini-ignore.md +2 -2
- package/dist/docs/cli/generation-settings.md +21 -20
- package/dist/docs/cli/model-routing.md +2 -2
- package/dist/docs/cli/model-steering.md +1 -1
- package/dist/docs/cli/plan-mode.md +11 -6
- package/dist/docs/cli/sandbox.md +7 -5
- package/dist/docs/cli/settings.md +32 -28
- package/dist/docs/cli/system-prompt.md +8 -8
- package/dist/docs/cli/telemetry.md +18 -11
- package/dist/docs/cli/themes.md +2 -2
- package/dist/docs/cli/trusted-folders.md +41 -13
- package/dist/docs/cli/tutorials/mcp-setup.md +1 -1
- package/dist/docs/cli/tutorials/memory-management.md +3 -1
- package/dist/docs/cli/tutorials/plan-mode-steering.md +2 -2
- package/dist/docs/cli/tutorials/session-management.md +1 -1
- package/dist/docs/cli/tutorials/shell-commands.md +1 -1
- package/dist/docs/cli/tutorials/task-planning.md +3 -3
- package/dist/docs/core/index.md +5 -6
- package/dist/docs/core/local-model-routing.md +1 -1
- package/dist/docs/core/remote-agents.md +1 -1
- package/dist/docs/core/subagents.md +38 -8
- package/dist/docs/extensions/best-practices.md +5 -4
- package/dist/docs/extensions/reference.md +6 -5
- package/dist/docs/extensions/releasing.md +6 -5
- package/dist/docs/extensions/writing-extensions.md +11 -11
- package/dist/docs/get-started/{authentication.md → authentication.mdx} +139 -93
- package/dist/docs/get-started/gemini-3.md +1 -1
- package/dist/docs/get-started/index.md +4 -4
- package/dist/docs/get-started/installation.mdx +201 -0
- package/dist/docs/hooks/best-practices.md +18 -17
- package/dist/docs/hooks/index.md +10 -8
- package/dist/docs/hooks/reference.md +10 -10
- package/dist/docs/ide-integration/ide-companion-spec.md +14 -14
- package/dist/docs/ide-integration/index.md +4 -4
- package/dist/docs/index.md +2 -2
- package/dist/docs/integration-tests.md +84 -2
- package/dist/docs/issue-and-pr-automation.md +8 -7
- package/dist/docs/npm.md +2 -2
- package/dist/docs/reference/commands.md +11 -11
- package/dist/docs/reference/configuration.md +150 -47
- package/dist/docs/reference/keyboard-shortcuts.md +79 -2
- package/dist/docs/reference/memport.md +2 -3
- package/dist/docs/reference/policy-engine.md +60 -26
- package/dist/docs/reference/tools.md +38 -4
- package/dist/docs/release-confidence.md +1 -1
- package/dist/docs/releases.md +19 -19
- package/dist/docs/resources/faq.md +5 -5
- package/dist/docs/resources/tos-privacy.md +10 -9
- package/dist/docs/resources/troubleshooting.md +17 -16
- package/dist/docs/resources/uninstall.md +5 -4
- package/dist/docs/sidebar.json +13 -1
- package/dist/docs/tools/ask-user.md +3 -3
- package/dist/docs/tools/file-system.md +7 -7
- package/dist/docs/tools/mcp-resources.md +44 -0
- package/dist/docs/tools/mcp-server.md +42 -39
- package/dist/docs/tools/shell.md +5 -5
- package/dist/docs/tools/tracker.md +61 -0
- package/dist/package.json +5 -4
- package/dist/src/agent/content-utils.d.ts +0 -6
- package/dist/src/agent/content-utils.js +0 -14
- package/dist/src/agent/content-utils.js.map +1 -1
- package/dist/src/agent/content-utils.test.js +1 -18
- package/dist/src/agent/content-utils.test.js.map +1 -1
- package/dist/src/agent/event-translator.js +8 -3
- package/dist/src/agent/event-translator.js.map +1 -1
- package/dist/src/agent/event-translator.test.js +14 -9
- package/dist/src/agent/event-translator.test.js.map +1 -1
- package/dist/src/agent/legacy-agent-session.js +9 -3
- package/dist/src/agent/legacy-agent-session.js.map +1 -1
- package/dist/src/agent/legacy-agent-session.test.js +4 -3
- package/dist/src/agent/legacy-agent-session.test.js.map +1 -1
- package/dist/src/agent/tool-display-utils.d.ts +30 -0
- package/dist/src/agent/tool-display-utils.js +69 -0
- package/dist/src/agent/tool-display-utils.js.map +1 -0
- package/dist/src/agent/tool-display-utils.test.js +101 -0
- package/dist/src/agent/tool-display-utils.test.js.map +1 -0
- package/dist/src/agent/types.d.ts +25 -5
- package/dist/src/agents/a2aUtils.js +28 -15
- package/dist/src/agents/a2aUtils.js.map +1 -1
- package/dist/src/agents/a2aUtils.test.js +43 -0
- package/dist/src/agents/a2aUtils.test.js.map +1 -1
- package/dist/src/agents/agent-tool.d.ts +31 -0
- package/dist/src/agents/agent-tool.js +155 -0
- package/dist/src/agents/agent-tool.js.map +1 -0
- package/dist/src/agents/agent-tool.test.js +110 -0
- package/dist/src/agents/agent-tool.test.js.map +1 -0
- package/dist/src/agents/agentLoader.d.ts +79 -4
- package/dist/src/agents/agentLoader.js +40 -2
- package/dist/src/agents/agentLoader.js.map +1 -1
- package/dist/src/agents/agentLoader.test.js +32 -0
- package/dist/src/agents/agentLoader.test.js.map +1 -1
- package/dist/src/agents/browser/analyzeScreenshot.js +1 -1
- package/dist/src/agents/browser/analyzeScreenshot.js.map +1 -1
- package/dist/src/agents/browser/analyzeScreenshot.test.js +19 -7
- package/dist/src/agents/browser/analyzeScreenshot.test.js.map +1 -1
- package/dist/src/agents/browser/browserAgentInvocation.d.ts +2 -2
- package/dist/src/agents/browser/browserAgentInvocation.js +2 -1
- package/dist/src/agents/browser/browserAgentInvocation.js.map +1 -1
- package/dist/src/agents/browser/browserAgentInvocation.test.js +61 -17
- package/dist/src/agents/browser/browserAgentInvocation.test.js.map +1 -1
- package/dist/src/agents/browser/mcpToolWrapper.js +1 -1
- package/dist/src/agents/browser/mcpToolWrapper.js.map +1 -1
- package/dist/src/agents/browser/mcpToolWrapper.test.js +22 -10
- package/dist/src/agents/browser/mcpToolWrapper.test.js.map +1 -1
- package/dist/src/agents/codebase-investigator.js +2 -2
- package/dist/src/agents/codebase-investigator.js.map +1 -1
- package/dist/src/agents/generalist-agent.js +3 -2
- package/dist/src/agents/generalist-agent.js.map +1 -1
- package/dist/src/agents/generalist-agent.test.js +1 -0
- package/dist/src/agents/generalist-agent.test.js.map +1 -1
- package/dist/src/agents/local-executor.d.ts +1 -1
- package/dist/src/agents/local-executor.js +10 -7
- package/dist/src/agents/local-executor.js.map +1 -1
- package/dist/src/agents/local-executor.test.js +5 -3
- package/dist/src/agents/local-executor.test.js.map +1 -1
- package/dist/src/agents/local-invocation.d.ts +2 -2
- package/dist/src/agents/local-invocation.js +8 -2
- package/dist/src/agents/local-invocation.js.map +1 -1
- package/dist/src/agents/local-invocation.test.js +29 -13
- package/dist/src/agents/local-invocation.test.js.map +1 -1
- package/dist/src/agents/registry.d.ts +2 -0
- package/dist/src/agents/registry.js +20 -19
- package/dist/src/agents/registry.js.map +1 -1
- package/dist/src/agents/registry.test.js +19 -30
- package/dist/src/agents/registry.test.js.map +1 -1
- package/dist/src/agents/remote-invocation.d.ts +3 -4
- package/dist/src/agents/remote-invocation.js +2 -1
- package/dist/src/agents/remote-invocation.js.map +1 -1
- package/dist/src/agents/remote-invocation.test.js +45 -18
- package/dist/src/agents/remote-invocation.test.js.map +1 -1
- package/dist/src/agents/skill-extraction-agent.d.ts +3 -2
- package/dist/src/agents/skill-extraction-agent.js +99 -56
- package/dist/src/agents/skill-extraction-agent.js.map +1 -1
- package/dist/src/agents/skill-extraction-agent.test.js +54 -0
- package/dist/src/agents/skill-extraction-agent.test.js.map +1 -0
- package/dist/src/availability/policyCatalog.js +1 -1
- package/dist/src/availability/policyCatalog.js.map +1 -1
- package/dist/src/availability/policyCatalog.test.js +1 -1
- package/dist/src/availability/policyCatalog.test.js.map +1 -1
- package/dist/src/code_assist/oauth2.js +14 -4
- package/dist/src/code_assist/oauth2.js.map +1 -1
- package/dist/src/commands/memory.d.ts +77 -0
- package/dist/src/commands/memory.js +494 -0
- package/dist/src/commands/memory.js.map +1 -1
- package/dist/src/commands/memory.test.js +720 -1
- package/dist/src/commands/memory.test.js.map +1 -1
- package/dist/src/config/config-agents-reload.test.js +26 -31
- package/dist/src/config/config-agents-reload.test.js.map +1 -1
- package/dist/src/config/config.d.ts +24 -10
- package/dist/src/config/config.js +148 -82
- package/dist/src/config/config.js.map +1 -1
- package/dist/src/config/config.test.js +373 -10
- package/dist/src/config/config.test.js.map +1 -1
- package/dist/src/config/constants.d.ts +1 -0
- package/dist/src/config/constants.js +2 -0
- package/dist/src/config/constants.js.map +1 -1
- package/dist/src/config/defaultModelConfigs.js +7 -7
- package/dist/src/config/defaultModelConfigs.js.map +1 -1
- package/dist/src/config/memory.js +1 -1
- package/dist/src/config/memory.js.map +1 -1
- package/dist/src/config/path-validation.test.js +15 -6
- package/dist/src/config/path-validation.test.js.map +1 -1
- package/dist/src/config/projectRegistry.js +113 -32
- package/dist/src/config/projectRegistry.js.map +1 -1
- package/dist/src/config/projectRegistry.test.js +51 -0
- package/dist/src/config/projectRegistry.test.js.map +1 -1
- package/dist/src/config/storage.d.ts +5 -1
- package/dist/src/config/storage.js +14 -1
- package/dist/src/config/storage.js.map +1 -1
- package/dist/src/config/storage.test.js +12 -0
- package/dist/src/config/storage.test.js.map +1 -1
- package/dist/src/confirmation-bus/message-bus.d.ts +4 -1
- package/dist/src/confirmation-bus/message-bus.js +39 -1
- package/dist/src/confirmation-bus/message-bus.js.map +1 -1
- package/dist/src/confirmation-bus/message-bus.test.js +43 -0
- package/dist/src/confirmation-bus/message-bus.test.js.map +1 -1
- package/dist/src/context/config/configLoader.d.ts +13 -0
- package/dist/src/context/config/configLoader.js +65 -0
- package/dist/src/context/config/configLoader.js.map +1 -0
- package/dist/src/context/config/configLoader.test.d.ts +6 -0
- package/dist/src/context/config/configLoader.test.js +79 -0
- package/dist/src/context/config/configLoader.test.js.map +1 -0
- package/dist/src/context/config/profiles.d.ts +17 -0
- package/dist/src/context/config/profiles.js +93 -0
- package/dist/src/context/config/profiles.js.map +1 -0
- package/dist/src/context/config/registry.d.ts +21 -0
- package/dist/src/context/config/registry.js +32 -0
- package/dist/src/context/config/registry.js.map +1 -0
- package/dist/src/context/config/schema.d.ts +45 -0
- package/dist/src/context/config/schema.js +47 -0
- package/dist/src/context/config/schema.js.map +1 -0
- package/dist/src/context/config/types.d.ts +39 -0
- package/dist/src/context/config/types.js +7 -0
- package/dist/src/context/config/types.js.map +1 -0
- package/dist/src/context/contextManager.barrier.test.d.ts +6 -0
- package/dist/src/context/contextManager.barrier.test.js +56 -0
- package/dist/src/context/contextManager.barrier.test.js.map +1 -0
- package/dist/src/context/contextManager.d.ts +49 -0
- package/dist/src/context/contextManager.js +120 -0
- package/dist/src/context/contextManager.js.map +1 -0
- package/dist/src/context/eventBus.d.ts +28 -0
- package/dist/src/context/eventBus.js +27 -0
- package/dist/src/context/eventBus.js.map +1 -0
- package/dist/src/context/graph/behaviorRegistry.d.ts +28 -0
- package/dist/src/context/graph/behaviorRegistry.js +14 -0
- package/dist/src/context/graph/behaviorRegistry.js.map +1 -0
- package/dist/src/context/graph/builtinBehaviors.d.ts +11 -0
- package/dist/src/context/graph/builtinBehaviors.js +145 -0
- package/dist/src/context/graph/builtinBehaviors.js.map +1 -0
- package/dist/src/context/graph/fromGraph.d.ts +9 -0
- package/dist/src/context/graph/fromGraph.js +34 -0
- package/dist/src/context/graph/fromGraph.js.map +1 -0
- package/dist/src/context/graph/mapper.d.ts +16 -0
- package/dist/src/context/graph/mapper.js +16 -0
- package/dist/src/context/graph/mapper.js.map +1 -0
- package/dist/src/context/graph/render.d.ts +15 -0
- package/dist/src/context/graph/render.js +72 -0
- package/dist/src/context/graph/render.js.map +1 -0
- package/dist/src/context/graph/toGraph.d.ts +10 -0
- package/dist/src/context/graph/toGraph.js +172 -0
- package/dist/src/context/graph/toGraph.js.map +1 -0
- package/dist/src/context/graph/types.d.ts +139 -0
- package/dist/src/context/graph/types.js +36 -0
- package/dist/src/context/graph/types.js.map +1 -0
- package/dist/src/context/historyObserver.d.ts +27 -0
- package/dist/src/context/historyObserver.js +64 -0
- package/dist/src/context/historyObserver.js.map +1 -0
- package/dist/src/context/pipeline/contextWorkingBuffer.d.ts +33 -0
- package/dist/src/context/pipeline/contextWorkingBuffer.js +197 -0
- package/dist/src/context/pipeline/contextWorkingBuffer.js.map +1 -0
- package/dist/src/context/pipeline/contextWorkingBuffer.test.d.ts +6 -0
- package/dist/src/context/pipeline/contextWorkingBuffer.test.js +89 -0
- package/dist/src/context/pipeline/contextWorkingBuffer.test.js.map +1 -0
- package/dist/src/context/pipeline/environment.d.ts +27 -0
- package/dist/src/context/pipeline/environment.js +2 -0
- package/dist/src/context/pipeline/environment.js.map +1 -0
- package/dist/src/context/pipeline/environmentImpl.d.ts +28 -0
- package/dist/src/context/pipeline/environmentImpl.js +40 -0
- package/dist/src/context/pipeline/environmentImpl.js.map +1 -0
- package/dist/src/context/pipeline/environmentImpl.test.d.ts +1 -0
- package/dist/src/context/pipeline/environmentImpl.test.js +32 -0
- package/dist/src/context/pipeline/environmentImpl.test.js.map +1 -0
- package/dist/src/context/pipeline/inbox.d.ts +15 -0
- package/dist/src/context/pipeline/inbox.js +52 -0
- package/dist/src/context/pipeline/inbox.js.map +1 -0
- package/dist/src/context/pipeline/inbox.test.d.ts +1 -0
- package/dist/src/context/pipeline/inbox.test.js +36 -0
- package/dist/src/context/pipeline/inbox.test.js.map +1 -0
- package/dist/src/context/pipeline/orchestrator.d.ts +22 -0
- package/dist/src/context/pipeline/orchestrator.js +126 -0
- package/dist/src/context/pipeline/orchestrator.js.map +1 -0
- package/dist/src/context/pipeline/orchestrator.test.d.ts +6 -0
- package/dist/src/context/pipeline/orchestrator.test.js +154 -0
- package/dist/src/context/pipeline/orchestrator.test.js.map +1 -0
- package/dist/src/context/pipeline.d.ts +52 -0
- package/dist/src/context/pipeline.js +7 -0
- package/dist/src/context/pipeline.js.map +1 -0
- package/dist/src/context/processors/blobDegradationProcessor.d.ts +6 -0
- package/dist/src/context/processors/blobDegradationProcessor.js +127 -0
- package/dist/src/context/processors/blobDegradationProcessor.js.map +1 -0
- package/dist/src/context/processors/blobDegradationProcessor.test.d.ts +6 -0
- package/dist/src/context/processors/blobDegradationProcessor.test.js +72 -0
- package/dist/src/context/processors/blobDegradationProcessor.test.js.map +1 -0
- package/dist/src/context/processors/historyTruncationProcessor.d.ts +11 -0
- package/dist/src/context/processors/historyTruncationProcessor.js +61 -0
- package/dist/src/context/processors/historyTruncationProcessor.js.map +1 -0
- package/dist/src/context/processors/nodeDistillationProcessor.d.ts +8 -0
- package/dist/src/context/processors/nodeDistillationProcessor.js +167 -0
- package/dist/src/context/processors/nodeDistillationProcessor.js.map +1 -0
- package/dist/src/context/processors/nodeDistillationProcessor.test.d.ts +6 -0
- package/dist/src/context/processors/nodeDistillationProcessor.test.js +77 -0
- package/dist/src/context/processors/nodeDistillationProcessor.test.js.map +1 -0
- package/dist/src/context/processors/nodeTruncationProcessor.d.ts +8 -0
- package/dist/src/context/processors/nodeTruncationProcessor.js +109 -0
- package/dist/src/context/processors/nodeTruncationProcessor.js.map +1 -0
- package/dist/src/context/processors/nodeTruncationProcessor.test.d.ts +6 -0
- package/dist/src/context/processors/nodeTruncationProcessor.test.js +71 -0
- package/dist/src/context/processors/nodeTruncationProcessor.test.js.map +1 -0
- package/dist/src/context/processors/rollingSummaryProcessor.d.ts +8 -0
- package/dist/src/context/processors/rollingSummaryProcessor.js +129 -0
- package/dist/src/context/processors/rollingSummaryProcessor.js.map +1 -0
- package/dist/src/context/processors/rollingSummaryProcessor.test.d.ts +1 -0
- package/dist/src/context/processors/rollingSummaryProcessor.test.js +60 -0
- package/dist/src/context/processors/rollingSummaryProcessor.test.js.map +1 -0
- package/dist/src/context/processors/stateSnapshotAsyncProcessor.d.ts +9 -0
- package/dist/src/context/processors/stateSnapshotAsyncProcessor.js +75 -0
- package/dist/src/context/processors/stateSnapshotAsyncProcessor.js.map +1 -0
- package/dist/src/context/processors/stateSnapshotAsyncProcessor.test.d.ts +1 -0
- package/dist/src/context/processors/stateSnapshotAsyncProcessor.test.js +80 -0
- package/dist/src/context/processors/stateSnapshotAsyncProcessor.test.js.map +1 -0
- package/dist/src/context/processors/stateSnapshotProcessor.d.ts +9 -0
- package/dist/src/context/processors/stateSnapshotProcessor.js +130 -0
- package/dist/src/context/processors/stateSnapshotProcessor.js.map +1 -0
- package/dist/src/context/processors/stateSnapshotProcessor.test.d.ts +1 -0
- package/dist/src/context/processors/stateSnapshotProcessor.test.js +91 -0
- package/dist/src/context/processors/stateSnapshotProcessor.test.js.map +1 -0
- package/dist/src/context/processors/toolMaskingProcessor.d.ts +8 -0
- package/dist/src/context/processors/toolMaskingProcessor.js +194 -0
- package/dist/src/context/processors/toolMaskingProcessor.js.map +1 -0
- package/dist/src/context/processors/toolMaskingProcessor.test.d.ts +1 -0
- package/dist/src/context/processors/toolMaskingProcessor.test.js +50 -0
- package/dist/src/context/processors/toolMaskingProcessor.test.js.map +1 -0
- package/dist/src/context/system-tests/lifecycle.golden.test.d.ts +6 -0
- package/dist/src/context/system-tests/lifecycle.golden.test.js +195 -0
- package/dist/src/context/system-tests/lifecycle.golden.test.js.map +1 -0
- package/dist/src/context/system-tests/simulationHarness.d.ts +41 -0
- package/dist/src/context/system-tests/simulationHarness.js +88 -0
- package/dist/src/context/system-tests/simulationHarness.js.map +1 -0
- package/dist/src/context/testing/contextTestUtils.d.ts +44 -0
- package/dist/src/context/testing/contextTestUtils.js +176 -0
- package/dist/src/context/testing/contextTestUtils.js.map +1 -0
- package/dist/src/context/testing/testProfile.d.ts +7 -0
- package/dist/src/context/testing/testProfile.js +20 -0
- package/dist/src/context/testing/testProfile.js.map +1 -0
- package/dist/src/context/tracer.d.ts +19 -0
- package/dist/src/context/tracer.js +79 -0
- package/dist/src/context/tracer.js.map +1 -0
- package/dist/src/context/tracer.test.d.ts +6 -0
- package/dist/src/context/tracer.test.js +71 -0
- package/dist/src/context/tracer.test.js.map +1 -0
- package/dist/src/context/utils/contextTokenCalculator.d.ts +53 -0
- package/dist/src/context/utils/contextTokenCalculator.js +97 -0
- package/dist/src/context/utils/contextTokenCalculator.js.map +1 -0
- package/dist/src/context/utils/snapshotGenerator.d.ts +12 -0
- package/dist/src/context/utils/snapshotGenerator.js +43 -0
- package/dist/src/context/utils/snapshotGenerator.js.map +1 -0
- package/dist/src/core/agentChatHistory.d.ts +26 -0
- package/dist/src/core/agentChatHistory.js +50 -0
- package/dist/src/core/agentChatHistory.js.map +1 -0
- package/dist/src/core/client.js +3 -1
- package/dist/src/core/client.js.map +1 -1
- package/dist/src/core/client.test.js +4 -0
- package/dist/src/core/client.test.js.map +1 -1
- package/dist/src/core/contentGenerator.d.ts +8 -1
- package/dist/src/core/contentGenerator.js +46 -4
- package/dist/src/core/contentGenerator.js.map +1 -1
- package/dist/src/core/contentGenerator.test.js +174 -8
- package/dist/src/core/contentGenerator.test.js.map +1 -1
- package/dist/src/core/coreToolHookTriggers.d.ts +1 -1
- package/dist/src/core/coreToolHookTriggers.js +5 -1
- package/dist/src/core/coreToolHookTriggers.js.map +1 -1
- package/dist/src/core/coreToolHookTriggers.test.js +1 -1
- package/dist/src/core/coreToolHookTriggers.test.js.map +1 -1
- package/dist/src/core/geminiChat.d.ts +2 -1
- package/dist/src/core/geminiChat.js +7 -2
- package/dist/src/core/geminiChat.js.map +1 -1
- package/dist/src/core/geminiChat.test.js +19 -6
- package/dist/src/core/geminiChat.test.js.map +1 -1
- package/dist/src/core/geminiChat_network_retry.test.js +42 -0
- package/dist/src/core/geminiChat_network_retry.test.js.map +1 -1
- package/dist/src/core/localLiteRtLmClient.js +2 -0
- package/dist/src/core/localLiteRtLmClient.js.map +1 -1
- package/dist/src/core/localLiteRtLmClient.test.js +7 -0
- package/dist/src/core/localLiteRtLmClient.test.js.map +1 -1
- package/dist/src/core/loggingContentGenerator.js +19 -6
- package/dist/src/core/loggingContentGenerator.js.map +1 -1
- package/dist/src/core/loggingContentGenerator.test.js +55 -0
- package/dist/src/core/loggingContentGenerator.test.js.map +1 -1
- package/dist/src/core/prompts-substitution.test.js +1 -0
- package/dist/src/core/prompts-substitution.test.js.map +1 -1
- package/dist/src/core/prompts.d.ts +1 -1
- package/dist/src/core/prompts.js +2 -2
- package/dist/src/core/prompts.js.map +1 -1
- package/dist/src/core/prompts.test.js +39 -8
- package/dist/src/core/prompts.test.js.map +1 -1
- package/dist/src/generated/git-commit.d.ts +2 -2
- package/dist/src/generated/git-commit.js +2 -2
- package/dist/src/hooks/hookRunner.js +8 -0
- package/dist/src/hooks/hookRunner.js.map +1 -1
- package/dist/src/hooks/hookRunner.test.js +23 -0
- package/dist/src/hooks/hookRunner.test.js.map +1 -1
- package/dist/src/ide/ide-client.js +3 -4
- package/dist/src/ide/ide-client.js.map +1 -1
- package/dist/src/index.d.ts +7 -3
- package/dist/src/index.js +7 -2
- package/dist/src/index.js.map +1 -1
- package/dist/src/mcp/mcpLauncher.js +1 -1
- package/dist/src/mcp/mcpLauncher.js.map +1 -1
- package/dist/src/mcp/oauth-provider.test.js +24 -17
- package/dist/src/mcp/oauth-provider.test.js.map +1 -1
- package/dist/src/policy/config.d.ts +2 -0
- package/dist/src/policy/config.js +67 -12
- package/dist/src/policy/config.js.map +1 -1
- package/dist/src/policy/core-tools-mapping.test.d.ts +6 -0
- package/dist/src/policy/core-tools-mapping.test.js +44 -0
- package/dist/src/policy/core-tools-mapping.test.js.map +1 -0
- package/dist/src/policy/policies/agents.toml +10 -0
- package/dist/src/policy/policies/plan.toml +17 -43
- package/dist/src/policy/policies/read-only.toml +24 -38
- package/dist/src/policy/policy-engine.d.ts +1 -1
- package/dist/src/policy/policy-engine.js +72 -67
- package/dist/src/policy/policy-engine.js.map +1 -1
- package/dist/src/policy/policy-engine.test.js +71 -4
- package/dist/src/policy/policy-engine.test.js.map +1 -1
- package/dist/src/policy/sandboxPolicyManager.js +4 -4
- package/dist/src/policy/sandboxPolicyManager.js.map +1 -1
- package/dist/src/policy/shell-safety-regression.test.d.ts +6 -0
- package/dist/src/policy/shell-safety-regression.test.js +86 -0
- package/dist/src/policy/shell-safety-regression.test.js.map +1 -0
- package/dist/src/policy/shell-safety.test.js +24 -0
- package/dist/src/policy/shell-safety.test.js.map +1 -1
- package/dist/src/policy/shell-substitution.test.d.ts +6 -0
- package/dist/src/policy/shell-substitution.test.js +75 -0
- package/dist/src/policy/shell-substitution.test.js.map +1 -0
- package/dist/src/policy/toml-loader.test.js +25 -11
- package/dist/src/policy/toml-loader.test.js.map +1 -1
- package/dist/src/policy/types.d.ts +6 -2
- package/dist/src/policy/types.js +4 -2
- package/dist/src/policy/types.js.map +1 -1
- package/dist/src/prompts/promptProvider.d.ts +1 -1
- package/dist/src/prompts/promptProvider.js +41 -24
- package/dist/src/prompts/promptProvider.js.map +1 -1
- package/dist/src/prompts/promptProvider.test.js +36 -2
- package/dist/src/prompts/promptProvider.test.js.map +1 -1
- package/dist/src/prompts/snippets-memory-v2.test.d.ts +6 -0
- package/dist/src/prompts/snippets-memory-v2.test.js +94 -0
- package/dist/src/prompts/snippets-memory-v2.test.js.map +1 -0
- package/dist/src/prompts/snippets.d.ts +19 -1
- package/dist/src/prompts/snippets.js +33 -6
- package/dist/src/prompts/snippets.js.map +1 -1
- package/dist/src/prompts/snippets.legacy.d.ts +6 -1
- package/dist/src/prompts/snippets.legacy.js +14 -7
- package/dist/src/prompts/snippets.legacy.js.map +1 -1
- package/dist/src/prompts/utils.test.js +1 -0
- package/dist/src/prompts/utils.test.js.map +1 -1
- package/dist/src/routing/modelRouterService.js +1 -1
- package/dist/src/routing/modelRouterService.js.map +1 -1
- package/dist/src/sandbox/linux/LinuxSandboxManager.d.ts +2 -0
- package/dist/src/sandbox/linux/LinuxSandboxManager.js +43 -19
- package/dist/src/sandbox/linux/LinuxSandboxManager.js.map +1 -1
- package/dist/src/sandbox/linux/LinuxSandboxManager.test.js +16 -0
- package/dist/src/sandbox/linux/LinuxSandboxManager.test.js.map +1 -1
- package/dist/src/sandbox/linux/bwrapArgsBuilder.d.ts +3 -7
- package/dist/src/sandbox/linux/bwrapArgsBuilder.js +96 -105
- package/dist/src/sandbox/linux/bwrapArgsBuilder.js.map +1 -1
- package/dist/src/sandbox/linux/bwrapArgsBuilder.test.js +144 -41
- package/dist/src/sandbox/linux/bwrapArgsBuilder.test.js.map +1 -1
- package/dist/src/sandbox/macos/MacOsSandboxManager.js +19 -10
- package/dist/src/sandbox/macos/MacOsSandboxManager.js.map +1 -1
- package/dist/src/sandbox/macos/MacOsSandboxManager.test.js +24 -37
- package/dist/src/sandbox/macos/MacOsSandboxManager.test.js.map +1 -1
- package/dist/src/sandbox/macos/seatbeltArgsBuilder.d.ts +3 -9
- package/dist/src/sandbox/macos/seatbeltArgsBuilder.js +129 -96
- package/dist/src/sandbox/macos/seatbeltArgsBuilder.js.map +1 -1
- package/dist/src/sandbox/macos/seatbeltArgsBuilder.test.js +78 -77
- package/dist/src/sandbox/macos/seatbeltArgsBuilder.test.js.map +1 -1
- package/dist/src/sandbox/utils/fsUtils.d.ts +2 -3
- package/dist/src/sandbox/utils/fsUtils.js +12 -27
- package/dist/src/sandbox/utils/fsUtils.js.map +1 -1
- package/dist/src/sandbox/utils/fsUtils.test.js +87 -29
- package/dist/src/sandbox/utils/fsUtils.test.js.map +1 -1
- package/dist/src/sandbox/windows/GeminiSandbox.cs +186 -77
- package/dist/src/sandbox/windows/WindowsSandboxManager.d.ts +4 -16
- package/dist/src/sandbox/windows/WindowsSandboxManager.js +138 -204
- package/dist/src/sandbox/windows/WindowsSandboxManager.js.map +1 -1
- package/dist/src/sandbox/windows/WindowsSandboxManager.test.js +105 -122
- package/dist/src/sandbox/windows/WindowsSandboxManager.test.js.map +1 -1
- package/dist/src/scheduler/policy.js +1 -2
- package/dist/src/scheduler/policy.js.map +1 -1
- package/dist/src/scheduler/policy.test.js +58 -2
- package/dist/src/scheduler/policy.test.js.map +1 -1
- package/dist/src/scheduler/scheduler.d.ts +2 -1
- package/dist/src/scheduler/scheduler.js +13 -14
- package/dist/src/scheduler/scheduler.js.map +1 -1
- package/dist/src/scheduler/scheduler.test.js +66 -0
- package/dist/src/scheduler/scheduler.test.js.map +1 -1
- package/dist/src/scheduler/scheduler_hooks.test.js +1 -0
- package/dist/src/scheduler/scheduler_hooks.test.js.map +1 -1
- package/dist/src/scheduler/scheduler_parallel.test.js +2 -0
- package/dist/src/scheduler/scheduler_parallel.test.js.map +1 -1
- package/dist/src/scheduler/tool-executor.js +2 -0
- package/dist/src/scheduler/tool-executor.js.map +1 -1
- package/dist/src/services/chatRecordingService.d.ts +12 -153
- package/dist/src/services/chatRecordingService.js +444 -350
- package/dist/src/services/chatRecordingService.js.map +1 -1
- package/dist/src/services/chatRecordingService.test.js +174 -128
- package/dist/src/services/chatRecordingService.test.js.map +1 -1
- package/dist/src/services/chatRecordingTypes.d.ts +111 -0
- package/dist/src/services/chatRecordingTypes.js +10 -0
- package/dist/src/services/chatRecordingTypes.js.map +1 -0
- package/dist/src/services/gitService.d.ts +2 -0
- package/dist/src/services/gitService.js +10 -1
- package/dist/src/services/gitService.js.map +1 -1
- package/dist/src/services/gitService.test.js +6 -2
- package/dist/src/services/gitService.test.js.map +1 -1
- package/dist/src/services/keychainService.d.ts +2 -2
- package/dist/src/services/keychainService.js +9 -9
- package/dist/src/services/keychainService.js.map +1 -1
- package/dist/src/services/keychainService.test.js +7 -7
- package/dist/src/services/keychainService.test.js.map +1 -1
- package/dist/src/services/keychainTypes.d.ts +1 -1
- package/dist/src/services/memoryPatchUtils.d.ts +42 -0
- package/dist/src/services/memoryPatchUtils.js +216 -0
- package/dist/src/services/memoryPatchUtils.js.map +1 -0
- package/dist/src/services/memoryService.d.ts +21 -1
- package/dist/src/services/memoryService.js +405 -64
- package/dist/src/services/memoryService.js.map +1 -1
- package/dist/src/services/memoryService.test.js +686 -2
- package/dist/src/services/memoryService.test.js.map +1 -1
- package/dist/src/services/sandboxManager.d.ts +33 -19
- package/dist/src/services/sandboxManager.integration.test.js +728 -266
- package/dist/src/services/sandboxManager.integration.test.js.map +1 -1
- package/dist/src/services/sandboxManager.js +65 -62
- package/dist/src/services/sandboxManager.js.map +1 -1
- package/dist/src/services/sandboxManager.test.js +17 -114
- package/dist/src/services/sandboxManager.test.js.map +1 -1
- package/dist/src/services/sandboxedFileSystemService.js +72 -62
- package/dist/src/services/sandboxedFileSystemService.js.map +1 -1
- package/dist/src/services/sessionSummaryUtils.d.ts +1 -1
- package/dist/src/services/sessionSummaryUtils.js +111 -38
- package/dist/src/services/sessionSummaryUtils.js.map +1 -1
- package/dist/src/services/sessionSummaryUtils.test.js +204 -51
- package/dist/src/services/sessionSummaryUtils.test.js.map +1 -1
- package/dist/src/services/shellExecutionService.d.ts +19 -0
- package/dist/src/services/shellExecutionService.js +88 -34
- package/dist/src/services/shellExecutionService.js.map +1 -1
- package/dist/src/services/shellExecutionService.test.js +38 -4
- package/dist/src/services/shellExecutionService.test.js.map +1 -1
- package/dist/src/telemetry/activity-monitor.js +1 -0
- package/dist/src/telemetry/activity-monitor.js.map +1 -1
- package/dist/src/telemetry/config.js +3 -0
- package/dist/src/telemetry/config.js.map +1 -1
- package/dist/src/telemetry/conseca-logger.js +18 -20
- package/dist/src/telemetry/conseca-logger.js.map +1 -1
- package/dist/src/telemetry/conseca-logger.test.js +100 -0
- package/dist/src/telemetry/conseca-logger.test.js.map +1 -1
- package/dist/src/telemetry/event-loop-monitor.d.ts +17 -0
- package/dist/src/telemetry/event-loop-monitor.js +76 -0
- package/dist/src/telemetry/event-loop-monitor.js.map +1 -0
- package/dist/src/telemetry/index.d.ts +2 -1
- package/dist/src/telemetry/index.js +2 -1
- package/dist/src/telemetry/index.js.map +1 -1
- package/dist/src/telemetry/llmRole.d.ts +2 -1
- package/dist/src/telemetry/llmRole.js +1 -0
- package/dist/src/telemetry/llmRole.js.map +1 -1
- package/dist/src/telemetry/loggers.test.js +184 -8
- package/dist/src/telemetry/loggers.test.js.map +1 -1
- package/dist/src/telemetry/memory-monitor.d.ts +1 -0
- package/dist/src/telemetry/memory-monitor.js +8 -1
- package/dist/src/telemetry/memory-monitor.js.map +1 -1
- package/dist/src/telemetry/memory-monitor.test.js +6 -1
- package/dist/src/telemetry/memory-monitor.test.js.map +1 -1
- package/dist/src/telemetry/metrics.d.ts +12 -0
- package/dist/src/telemetry/metrics.js +19 -0
- package/dist/src/telemetry/metrics.js.map +1 -1
- package/dist/src/telemetry/sdk.js +20 -1
- package/dist/src/telemetry/sdk.js.map +1 -1
- package/dist/src/telemetry/trace.d.ts +23 -6
- package/dist/src/telemetry/trace.js +71 -22
- package/dist/src/telemetry/trace.js.map +1 -1
- package/dist/src/telemetry/trace.test.js +79 -15
- package/dist/src/telemetry/trace.test.js.map +1 -1
- package/dist/src/telemetry/types.js +61 -15
- package/dist/src/telemetry/types.js.map +1 -1
- package/dist/src/test-utils/mock-tool.d.ts +3 -2
- package/dist/src/test-utils/mock-tool.js +4 -3
- package/dist/src/test-utils/mock-tool.js.map +1 -1
- package/dist/src/tools/activate-skill.js +1 -1
- package/dist/src/tools/activate-skill.js.map +1 -1
- package/dist/src/tools/activate-skill.test.js +6 -2
- package/dist/src/tools/activate-skill.test.js.map +1 -1
- package/dist/src/tools/ask-user.d.ts +2 -2
- package/dist/src/tools/ask-user.js +1 -1
- package/dist/src/tools/ask-user.js.map +1 -1
- package/dist/src/tools/ask-user.test.js +9 -3
- package/dist/src/tools/ask-user.test.js.map +1 -1
- package/dist/src/tools/complete-task.d.ts +2 -2
- package/dist/src/tools/complete-task.js +1 -1
- package/dist/src/tools/complete-task.js.map +1 -1
- package/dist/src/tools/complete-task.test.js +9 -3
- package/dist/src/tools/complete-task.test.js.map +1 -1
- package/dist/src/tools/definitions/base-declarations.d.ts +2 -0
- package/dist/src/tools/definitions/base-declarations.js +3 -0
- package/dist/src/tools/definitions/base-declarations.js.map +1 -1
- package/dist/src/tools/definitions/coreTools.d.ts +3 -1
- package/dist/src/tools/definitions/coreTools.js +13 -1
- package/dist/src/tools/definitions/coreTools.js.map +1 -1
- package/dist/src/tools/definitions/model-family-sets/default-legacy.js +29 -1
- package/dist/src/tools/definitions/model-family-sets/default-legacy.js.map +1 -1
- package/dist/src/tools/definitions/model-family-sets/gemini-3.js +29 -1
- package/dist/src/tools/definitions/model-family-sets/gemini-3.js.map +1 -1
- package/dist/src/tools/definitions/types.d.ts +2 -0
- package/dist/src/tools/edit.js +20 -4
- package/dist/src/tools/edit.js.map +1 -1
- package/dist/src/tools/edit.test.js +41 -18
- package/dist/src/tools/edit.test.js.map +1 -1
- package/dist/src/tools/enter-plan-mode.d.ts +2 -2
- package/dist/src/tools/enter-plan-mode.js +1 -1
- package/dist/src/tools/enter-plan-mode.js.map +1 -1
- package/dist/src/tools/enter-plan-mode.test.js +10 -4
- package/dist/src/tools/enter-plan-mode.test.js.map +1 -1
- package/dist/src/tools/exit-plan-mode.d.ts +2 -2
- package/dist/src/tools/exit-plan-mode.js +9 -12
- package/dist/src/tools/exit-plan-mode.js.map +1 -1
- package/dist/src/tools/exit-plan-mode.test.js +44 -17
- package/dist/src/tools/exit-plan-mode.test.js.map +1 -1
- package/dist/src/tools/get-internal-docs.js +6 -3
- package/dist/src/tools/get-internal-docs.js.map +1 -1
- package/dist/src/tools/get-internal-docs.test.js +4 -4
- package/dist/src/tools/get-internal-docs.test.js.map +1 -1
- package/dist/src/tools/glob.js +1 -1
- package/dist/src/tools/glob.js.map +1 -1
- package/dist/src/tools/glob.test.js +16 -16
- package/dist/src/tools/glob.test.js.map +1 -1
- package/dist/src/tools/grep.js +21 -12
- package/dist/src/tools/grep.js.map +1 -1
- package/dist/src/tools/grep.test.js +18 -18
- package/dist/src/tools/grep.test.js.map +1 -1
- package/dist/src/tools/line-endings.test.js +3 -3
- package/dist/src/tools/line-endings.test.js.map +1 -1
- package/dist/src/tools/list-mcp-resources.d.ts +24 -0
- package/dist/src/tools/list-mcp-resources.js +74 -0
- package/dist/src/tools/list-mcp-resources.js.map +1 -0
- package/dist/src/tools/list-mcp-resources.test.d.ts +6 -0
- package/dist/src/tools/list-mcp-resources.test.js +79 -0
- package/dist/src/tools/list-mcp-resources.test.js.map +1 -0
- package/dist/src/tools/ls.js +2 -2
- package/dist/src/tools/ls.js.map +1 -1
- package/dist/src/tools/ls.test.js +21 -21
- package/dist/src/tools/ls.test.js.map +1 -1
- package/dist/src/tools/mcp-client-manager.d.ts +3 -1
- package/dist/src/tools/mcp-client-manager.js +24 -1
- package/dist/src/tools/mcp-client-manager.js.map +1 -1
- package/dist/src/tools/mcp-client-manager.test.js +43 -0
- package/dist/src/tools/mcp-client-manager.test.js.map +1 -1
- package/dist/src/tools/mcp-client.js +10 -12
- package/dist/src/tools/mcp-client.js.map +1 -1
- package/dist/src/tools/mcp-client.test.js +14 -2
- package/dist/src/tools/mcp-client.test.js.map +1 -1
- package/dist/src/tools/mcp-tool.d.ts +2 -2
- package/dist/src/tools/mcp-tool.js +1 -1
- package/dist/src/tools/mcp-tool.js.map +1 -1
- package/dist/src/tools/mcp-tool.test.js +51 -21
- package/dist/src/tools/mcp-tool.test.js.map +1 -1
- package/dist/src/tools/memoryTool.d.ts +4 -3
- package/dist/src/tools/memoryTool.js +43 -14
- package/dist/src/tools/memoryTool.js.map +1 -1
- package/dist/src/tools/memoryTool.test.js +29 -9
- package/dist/src/tools/memoryTool.test.js.map +1 -1
- package/dist/src/tools/read-file.js +1 -1
- package/dist/src/tools/read-file.js.map +1 -1
- package/dist/src/tools/read-file.test.js +17 -17
- package/dist/src/tools/read-file.test.js.map +1 -1
- package/dist/src/tools/read-many-files.js +4 -4
- package/dist/src/tools/read-many-files.js.map +1 -1
- package/dist/src/tools/read-many-files.test.js +70 -24
- package/dist/src/tools/read-many-files.test.js.map +1 -1
- package/dist/src/tools/read-mcp-resource.d.ts +25 -0
- package/dist/src/tools/read-mcp-resource.js +120 -0
- package/dist/src/tools/read-mcp-resource.js.map +1 -0
- package/dist/src/tools/read-mcp-resource.test.d.ts +6 -0
- package/dist/src/tools/read-mcp-resource.test.js +110 -0
- package/dist/src/tools/read-mcp-resource.test.js.map +1 -0
- package/dist/src/tools/ripGrep.d.ts +3 -2
- package/dist/src/tools/ripGrep.js +26 -55
- package/dist/src/tools/ripGrep.js.map +1 -1
- package/dist/src/tools/ripGrep.test.js +113 -167
- package/dist/src/tools/ripGrep.test.js.map +1 -1
- package/dist/src/tools/shell.d.ts +2 -2
- package/dist/src/tools/shell.js +51 -21
- package/dist/src/tools/shell.js.map +1 -1
- package/dist/src/tools/shell.test.js +479 -76
- package/dist/src/tools/shell.test.js.map +1 -1
- package/dist/src/tools/shellBackgroundTools.d.ts +3 -3
- package/dist/src/tools/shellBackgroundTools.integration.test.js +6 -2
- package/dist/src/tools/shellBackgroundTools.integration.test.js.map +1 -1
- package/dist/src/tools/shellBackgroundTools.js +2 -2
- package/dist/src/tools/shellBackgroundTools.js.map +1 -1
- package/dist/src/tools/shellBackgroundTools.test.js +30 -10
- package/dist/src/tools/shellBackgroundTools.test.js.map +1 -1
- package/dist/src/tools/tool-error.d.ts +1 -0
- package/dist/src/tools/tool-error.js +1 -0
- package/dist/src/tools/tool-error.js.map +1 -1
- package/dist/src/tools/tool-names.d.ts +5 -4
- package/dist/src/tools/tool-names.js +8 -2
- package/dist/src/tools/tool-names.js.map +1 -1
- package/dist/src/tools/tool-registry.js +137 -114
- package/dist/src/tools/tool-registry.js.map +1 -1
- package/dist/src/tools/tool-registry.test.js +3 -1
- package/dist/src/tools/tool-registry.test.js.map +1 -1
- package/dist/src/tools/tools.d.ts +6 -6
- package/dist/src/tools/tools.js +6 -2
- package/dist/src/tools/tools.js.map +1 -1
- package/dist/src/tools/topicTool.d.ts +2 -2
- package/dist/src/tools/topicTool.js +1 -1
- package/dist/src/tools/topicTool.js.map +1 -1
- package/dist/src/tools/topicTool.test.js +6 -2
- package/dist/src/tools/topicTool.test.js.map +1 -1
- package/dist/src/tools/trackerTools.d.ts +7 -7
- package/dist/src/tools/trackerTools.js +6 -6
- package/dist/src/tools/trackerTools.js.map +1 -1
- package/dist/src/tools/web-fetch.js +1 -1
- package/dist/src/tools/web-fetch.js.map +1 -1
- package/dist/src/tools/web-fetch.test.js +59 -23
- package/dist/src/tools/web-fetch.test.js.map +1 -1
- package/dist/src/tools/web-search.js +1 -1
- package/dist/src/tools/web-search.js.map +1 -1
- package/dist/src/tools/web-search.test.js +5 -5
- package/dist/src/tools/web-search.test.js.map +1 -1
- package/dist/src/tools/write-file.js +22 -4
- package/dist/src/tools/write-file.js.map +1 -1
- package/dist/src/tools/write-file.test.js +29 -11
- package/dist/src/tools/write-file.test.js.map +1 -1
- package/dist/src/tools/write-todos.js +1 -1
- package/dist/src/tools/write-todos.js.map +1 -1
- package/dist/src/utils/compatibility.js +6 -1
- package/dist/src/utils/compatibility.js.map +1 -1
- package/dist/src/utils/compatibility.test.js +23 -0
- package/dist/src/utils/compatibility.test.js.map +1 -1
- package/dist/src/utils/errors.d.ts +3 -0
- package/dist/src/utils/errors.js +6 -0
- package/dist/src/utils/errors.js.map +1 -1
- package/dist/src/utils/fileUtils.d.ts +1 -2
- package/dist/src/utils/fileUtils.js +80 -40
- package/dist/src/utils/fileUtils.js.map +1 -1
- package/dist/src/utils/fileUtils.test.js +61 -0
- package/dist/src/utils/fileUtils.test.js.map +1 -1
- package/dist/src/utils/filesearch/fileSearch.d.ts +2 -0
- package/dist/src/utils/filesearch/fileSearch.js +97 -6
- package/dist/src/utils/filesearch/fileSearch.js.map +1 -1
- package/dist/src/utils/filesearch/fileSearch.test.js +54 -0
- package/dist/src/utils/filesearch/fileSearch.test.js.map +1 -1
- package/dist/src/utils/filesearch/fileWatcher.d.ts +25 -0
- package/dist/src/utils/filesearch/fileWatcher.js +86 -0
- package/dist/src/utils/filesearch/fileWatcher.js.map +1 -0
- package/dist/src/utils/filesearch/fileWatcher.test.js +142 -0
- package/dist/src/utils/filesearch/fileWatcher.test.js.map +1 -0
- package/dist/src/utils/getFolderStructure.js +4 -2
- package/dist/src/utils/getFolderStructure.js.map +1 -1
- package/dist/src/utils/gitIgnoreParser.js +1 -1
- package/dist/src/utils/gitIgnoreParser.js.map +1 -1
- package/dist/src/utils/googleQuotaErrors.d.ts +2 -1
- package/dist/src/utils/googleQuotaErrors.js +30 -35
- package/dist/src/utils/googleQuotaErrors.js.map +1 -1
- package/dist/src/utils/googleQuotaErrors.test.js +24 -0
- package/dist/src/utils/googleQuotaErrors.test.js.map +1 -1
- package/dist/src/utils/ignoreFileParser.js +1 -1
- package/dist/src/utils/ignoreFileParser.js.map +1 -1
- package/dist/src/utils/memoryDiscovery.js +15 -5
- package/dist/src/utils/memoryDiscovery.js.map +1 -1
- package/dist/src/utils/oauth-flow.js +17 -5
- package/dist/src/utils/oauth-flow.js.map +1 -1
- package/dist/src/utils/oauth-flow.test.js +20 -0
- package/dist/src/utils/oauth-flow.test.js.map +1 -1
- package/dist/src/utils/paths.d.ts +9 -0
- package/dist/src/utils/paths.js +37 -0
- package/dist/src/utils/paths.js.map +1 -1
- package/dist/src/utils/paths.test.js +45 -1
- package/dist/src/utils/paths.test.js.map +1 -1
- package/dist/src/utils/planUtils.d.ts +11 -2
- package/dist/src/utils/planUtils.js +43 -11
- package/dist/src/utils/planUtils.js.map +1 -1
- package/dist/src/utils/planUtils.test.js +10 -9
- package/dist/src/utils/planUtils.test.js.map +1 -1
- package/dist/src/utils/process-utils.d.ts +2 -1
- package/dist/src/utils/process-utils.js +64 -33
- package/dist/src/utils/process-utils.js.map +1 -1
- package/dist/src/utils/process-utils.test.js +9 -0
- package/dist/src/utils/process-utils.test.js.map +1 -1
- package/dist/src/utils/retry.js +18 -6
- package/dist/src/utils/retry.js.map +1 -1
- package/dist/src/utils/retry.test.js +30 -0
- package/dist/src/utils/retry.test.js.map +1 -1
- package/dist/src/utils/sessionOperations.js +3 -2
- package/dist/src/utils/sessionOperations.js.map +1 -1
- package/dist/src/utils/shell-utils.d.ts +2 -0
- package/dist/src/utils/shell-utils.js +237 -107
- package/dist/src/utils/shell-utils.js.map +1 -1
- package/dist/src/utils/tool-utils.d.ts +1 -29
- package/dist/src/utils/tool-utils.js +0 -39
- package/dist/src/utils/tool-utils.js.map +1 -1
- package/dist/src/utils/tool-utils.test.js +2 -76
- package/dist/src/utils/tool-utils.test.js.map +1 -1
- package/dist/src/utils/tool-visibility.d.ts +40 -0
- package/dist/src/utils/tool-visibility.js +111 -0
- package/dist/src/utils/tool-visibility.js.map +1 -0
- package/dist/src/utils/tool-visibility.test.d.ts +6 -0
- package/dist/src/utils/tool-visibility.test.js +96 -0
- package/dist/src/utils/tool-visibility.test.js.map +1 -0
- package/dist/src/utils/trust.d.ts +64 -0
- package/dist/src/utils/trust.js +276 -0
- package/dist/src/utils/trust.js.map +1 -0
- package/dist/src/utils/trust.test.d.ts +6 -0
- package/dist/src/utils/trust.test.js +159 -0
- package/dist/src/utils/trust.test.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +5 -4
- package/dist/docs/get-started/installation.md +0 -181
- package/dist/src/agents/memory-manager-agent.d.ts +0 -25
- package/dist/src/agents/memory-manager-agent.js +0 -138
- package/dist/src/agents/memory-manager-agent.js.map +0 -1
- package/dist/src/agents/memory-manager-agent.test.js +0 -123
- package/dist/src/agents/memory-manager-agent.test.js.map +0 -1
- package/dist/src/agents/subagent-tool-wrapper.d.ts +0 -38
- package/dist/src/agents/subagent-tool-wrapper.js +0 -58
- package/dist/src/agents/subagent-tool-wrapper.js.map +0 -1
- package/dist/src/agents/subagent-tool-wrapper.test.js +0 -123
- package/dist/src/agents/subagent-tool-wrapper.test.js.map +0 -1
- package/dist/src/agents/subagent-tool.d.ts +0 -18
- package/dist/src/agents/subagent-tool.js +0 -134
- package/dist/src/agents/subagent-tool.js.map +0 -1
- package/dist/src/agents/subagent-tool.test.js +0 -287
- package/dist/src/agents/subagent-tool.test.js.map +0 -1
- package/dist/src/policy/policies/tracker.toml +0 -34
- package/dist/src/prompts/snippets-memory-manager.test.js +0 -31
- package/dist/src/prompts/snippets-memory-manager.test.js.map +0 -1
- /package/dist/src/{agents/memory-manager-agent.test.d.ts → agent/tool-display-utils.test.d.ts} +0 -0
- /package/dist/src/agents/{subagent-tool.test.d.ts → agent-tool.test.d.ts} +0 -0
- /package/dist/src/{prompts/snippets-memory-manager.test.d.ts → agents/skill-extraction-agent.test.d.ts} +0 -0
- /package/dist/src/{agents/subagent-tool-wrapper.test.d.ts → utils/filesearch/fileWatcher.test.d.ts} +0 -0
|
@@ -3,56 +3,66 @@
|
|
|
3
3
|
* Copyright 2026 Google LLC
|
|
4
4
|
* SPDX-License-Identifier: Apache-2.0
|
|
5
5
|
*/
|
|
6
|
-
import { describe, it, expect, beforeAll, afterAll } from 'vitest';
|
|
6
|
+
import { describe, it, expect, beforeAll, beforeEach, afterEach, afterAll, } from 'vitest';
|
|
7
7
|
import { createSandboxManager } from './sandboxManagerFactory.js';
|
|
8
8
|
import { ShellExecutionService } from './shellExecutionService.js';
|
|
9
9
|
import { getSecureSanitizationConfig } from './environmentSanitization.js';
|
|
10
|
-
import {
|
|
11
|
-
import { execFile
|
|
10
|
+
import { GOVERNANCE_FILES, } from './sandboxManager.js';
|
|
11
|
+
import { execFile } from 'node:child_process';
|
|
12
12
|
import { promisify } from 'node:util';
|
|
13
13
|
import os from 'node:os';
|
|
14
14
|
import fs from 'node:fs';
|
|
15
15
|
import path from 'node:path';
|
|
16
16
|
import http from 'node:http';
|
|
17
17
|
/**
|
|
18
|
-
*
|
|
18
|
+
* Cross-platform command wrappers using Node.js inline scripts.
|
|
19
|
+
* Ensures consistent execution behavior and reliable exit codes across
|
|
20
|
+
* different host operating systems and restricted sandbox environments.
|
|
19
21
|
*/
|
|
20
22
|
const Platform = {
|
|
21
23
|
isWindows: os.platform() === 'win32',
|
|
24
|
+
isMac: os.platform() === 'darwin',
|
|
22
25
|
/** Returns a command to create an empty file. */
|
|
23
26
|
touch(filePath) {
|
|
24
|
-
return
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
],
|
|
32
|
-
}
|
|
33
|
-
: { command: 'touch', args: [filePath] };
|
|
27
|
+
return {
|
|
28
|
+
command: process.execPath,
|
|
29
|
+
args: [
|
|
30
|
+
'-e',
|
|
31
|
+
`require("node:fs").writeFileSync(${JSON.stringify(filePath)}, "")`,
|
|
32
|
+
],
|
|
33
|
+
};
|
|
34
34
|
},
|
|
35
35
|
/** Returns a command to read a file's content. */
|
|
36
36
|
cat(filePath) {
|
|
37
|
-
return
|
|
38
|
-
|
|
39
|
-
|
|
37
|
+
return {
|
|
38
|
+
command: process.execPath,
|
|
39
|
+
args: [
|
|
40
|
+
'-e',
|
|
41
|
+
`console.log(require("node:fs").readFileSync(${JSON.stringify(filePath)}, "utf8"))`,
|
|
42
|
+
],
|
|
43
|
+
};
|
|
40
44
|
},
|
|
41
45
|
/** Returns a command to echo a string. */
|
|
42
46
|
echo(text) {
|
|
43
|
-
return
|
|
44
|
-
|
|
45
|
-
:
|
|
47
|
+
return {
|
|
48
|
+
command: process.execPath,
|
|
49
|
+
args: ['-e', `console.log(${JSON.stringify(text)})`],
|
|
50
|
+
};
|
|
46
51
|
},
|
|
47
52
|
/** Returns a command to perform a network request. */
|
|
48
53
|
curl(url) {
|
|
49
|
-
return {
|
|
54
|
+
return {
|
|
55
|
+
command: process.execPath,
|
|
56
|
+
args: [
|
|
57
|
+
'-e',
|
|
58
|
+
`require("node:http").get(${JSON.stringify(url)}, (res) => { res.on("data", (d) => process.stdout.write(d)); res.on("end", () => process.exit(0)); }).on("error", () => process.exit(1));`,
|
|
59
|
+
],
|
|
60
|
+
};
|
|
50
61
|
},
|
|
51
62
|
/** Returns a command that checks if the current terminal is interactive. */
|
|
52
63
|
isPty() {
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
: 'bash -c "if [ -t 1 ]; then echo True; else echo False; fi"';
|
|
64
|
+
// ShellExecutionService.execute expects a raw shell string
|
|
65
|
+
return `"${process.execPath}" -e "console.log(process.stdout.isTTY ? 'True' : 'False')"`;
|
|
56
66
|
},
|
|
57
67
|
/** Returns a path that is strictly outside the workspace and likely blocked. */
|
|
58
68
|
getExternalBlockedPath() {
|
|
@@ -80,54 +90,66 @@ async function runCommand(command) {
|
|
|
80
90
|
}
|
|
81
91
|
}
|
|
82
92
|
/**
|
|
83
|
-
*
|
|
84
|
-
*
|
|
93
|
+
* Asserts the result of a sandboxed command execution, and provides detailed
|
|
94
|
+
* diagnostics on failure.
|
|
85
95
|
*/
|
|
86
|
-
function
|
|
87
|
-
const
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
96
|
+
function assertResult(result, command, expected) {
|
|
97
|
+
const isSuccess = result.status === 0;
|
|
98
|
+
const shouldBeSuccess = expected === 'success';
|
|
99
|
+
if (isSuccess === shouldBeSuccess) {
|
|
100
|
+
if (shouldBeSuccess) {
|
|
101
|
+
expect(result.status).toBe(0);
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
expect(result.status).not.toBe(0);
|
|
105
|
+
}
|
|
106
|
+
return;
|
|
93
107
|
}
|
|
94
|
-
|
|
95
|
-
|
|
108
|
+
const commandLine = `${command.program} ${command.args.join(' ')}`;
|
|
109
|
+
const message = `Command ${shouldBeSuccess ? 'failed' : 'succeeded'} unexpectedly.
|
|
110
|
+
Command: ${commandLine}
|
|
111
|
+
CWD: ${command.cwd || 'N/A'}
|
|
112
|
+
Status: ${result.status} (expected ${expected})${result.stdout ? `\nStdout: ${result.stdout.trim()}` : ''}${result.stderr ? `\nStderr: ${result.stderr.trim()}` : ''}`;
|
|
113
|
+
throw new Error(message);
|
|
114
|
+
}
|
|
115
|
+
describe('SandboxManager Integration', () => {
|
|
116
|
+
let tempDirectories = [];
|
|
117
|
+
/**
|
|
118
|
+
* Creates a temporary directory and tracks it for automatic cleanup after each test.
|
|
119
|
+
* - macOS: Created in process.cwd() to avoid the seatbelt profile's global os.tmpdir() whitelist.
|
|
120
|
+
* - Win/Linux: Created in os.tmpdir() because enforcing sandbox restrictions inside a large directory can be very slow.
|
|
121
|
+
*/
|
|
122
|
+
function createTempDir(prefix = 'gemini-sandbox-test-') {
|
|
123
|
+
const baseDir = Platform.isMac
|
|
124
|
+
? path.join(process.cwd(), `.${prefix}`)
|
|
125
|
+
: path.join(os.tmpdir(), prefix);
|
|
126
|
+
const dir = fs.mkdtempSync(baseDir);
|
|
127
|
+
tempDirectories.push(dir);
|
|
128
|
+
return dir;
|
|
129
|
+
}
|
|
130
|
+
let workspace;
|
|
131
|
+
let manager;
|
|
132
|
+
beforeEach(() => {
|
|
133
|
+
tempDirectories = [];
|
|
134
|
+
// Create a fresh, isolated workspace for every test to prevent state
|
|
135
|
+
// leakage from causing intermittent or order-dependent test failures.
|
|
136
|
+
workspace = createTempDir('workspace-');
|
|
137
|
+
manager = createSandboxManager({ enabled: true }, { workspace });
|
|
138
|
+
});
|
|
139
|
+
afterEach(() => {
|
|
140
|
+
for (const dir of tempDirectories) {
|
|
96
141
|
try {
|
|
97
|
-
|
|
98
|
-
stdio: 'ignore',
|
|
99
|
-
});
|
|
100
|
-
return true;
|
|
142
|
+
fs.rmSync(dir, { recursive: true, force: true });
|
|
101
143
|
}
|
|
102
144
|
catch {
|
|
103
|
-
//
|
|
104
|
-
console.warn('sandbox-exec is present but cannot be used (likely running inside a sandbox already). Skipping sandbox tests.');
|
|
105
|
-
return false;
|
|
145
|
+
// Best-effort cleanup
|
|
106
146
|
}
|
|
107
147
|
}
|
|
108
|
-
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
try {
|
|
112
|
-
execSync('which bwrap', { stdio: 'ignore' });
|
|
113
|
-
return true;
|
|
114
|
-
}
|
|
115
|
-
catch {
|
|
116
|
-
throw new Error('Sandboxing tests on Linux require bubblewrap (bwrap) to be installed.');
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
return false;
|
|
120
|
-
}
|
|
121
|
-
describe('SandboxManager Integration', () => {
|
|
122
|
-
const workspace = process.cwd();
|
|
123
|
-
const manager = createSandboxManager({ enabled: true }, { workspace });
|
|
124
|
-
// Skip if we are on an unsupported platform or if it's a NoopSandboxManager
|
|
125
|
-
const shouldSkip = manager instanceof NoopSandboxManager ||
|
|
126
|
-
manager instanceof LocalSandboxManager ||
|
|
127
|
-
!ensureSandboxAvailable();
|
|
128
|
-
describe.skipIf(shouldSkip)('Cross-platform Sandbox Behavior', () => {
|
|
148
|
+
tempDirectories = [];
|
|
149
|
+
});
|
|
150
|
+
describe('Execution & Environment', () => {
|
|
129
151
|
describe('Basic Execution', () => {
|
|
130
|
-
it('
|
|
152
|
+
it('allows workspace execution', async () => {
|
|
131
153
|
const { command, args } = Platform.echo('sandbox test');
|
|
132
154
|
const sandboxed = await manager.prepareCommand({
|
|
133
155
|
command,
|
|
@@ -136,12 +158,12 @@ describe('SandboxManager Integration', () => {
|
|
|
136
158
|
env: process.env,
|
|
137
159
|
});
|
|
138
160
|
const result = await runCommand(sandboxed);
|
|
139
|
-
|
|
161
|
+
assertResult(result, sandboxed, 'success');
|
|
140
162
|
expect(result.stdout.trim()).toBe('sandbox test');
|
|
141
163
|
});
|
|
142
164
|
// The Windows sandbox wrapper (GeminiSandbox.exe) uses standard pipes
|
|
143
165
|
// for I/O interception, which breaks ConPTY pseudo-terminal inheritance.
|
|
144
|
-
it.skipIf(Platform.isWindows)('supports interactive
|
|
166
|
+
it.skipIf(Platform.isWindows)('supports interactive terminals', async () => {
|
|
145
167
|
const handle = await ShellExecutionService.execute(Platform.isPty(), workspace, () => { }, new AbortController().signal, true, {
|
|
146
168
|
sanitizationConfig: getSecureSanitizationConfig(),
|
|
147
169
|
sandboxManager: manager,
|
|
@@ -151,8 +173,175 @@ describe('SandboxManager Integration', () => {
|
|
|
151
173
|
expect(result.output).toContain('True');
|
|
152
174
|
});
|
|
153
175
|
});
|
|
176
|
+
describe('Virtual Commands', () => {
|
|
177
|
+
it('handles virtual read commands', async () => {
|
|
178
|
+
const testFile = path.join(workspace, 'read-virtual.txt');
|
|
179
|
+
fs.writeFileSync(testFile, 'virtual read success');
|
|
180
|
+
const sandboxed = await manager.prepareCommand({
|
|
181
|
+
command: '__read',
|
|
182
|
+
args: [testFile],
|
|
183
|
+
cwd: workspace,
|
|
184
|
+
env: process.env,
|
|
185
|
+
});
|
|
186
|
+
const result = await runCommand(sandboxed);
|
|
187
|
+
assertResult(result, sandboxed, 'success');
|
|
188
|
+
expect(result.stdout.trim()).toBe('virtual read success');
|
|
189
|
+
});
|
|
190
|
+
it('handles virtual write commands', async () => {
|
|
191
|
+
const testFile = path.join(workspace, 'write-virtual.txt');
|
|
192
|
+
const sandboxed = await manager.prepareCommand({
|
|
193
|
+
command: '__write',
|
|
194
|
+
args: [testFile],
|
|
195
|
+
cwd: workspace,
|
|
196
|
+
env: process.env,
|
|
197
|
+
});
|
|
198
|
+
// Executing __write directly via runCommand hangs because 'cat' waits for stdin.
|
|
199
|
+
// Instead, we verify the command was translated correctly.
|
|
200
|
+
if (Platform.isWindows) {
|
|
201
|
+
// On Windows, the native helper handles '__write'
|
|
202
|
+
expect(sandboxed.args.includes('__write')).toBe(true);
|
|
203
|
+
}
|
|
204
|
+
else {
|
|
205
|
+
// On macOS/Linux, it is translated to a shell command with 'tee -- "$@" > /dev/null'
|
|
206
|
+
expect(sandboxed.args.join(' ')).toContain('tee --');
|
|
207
|
+
}
|
|
208
|
+
});
|
|
209
|
+
});
|
|
210
|
+
describe('Environment Sanitization', () => {
|
|
211
|
+
it('scrubs sensitive environment variables', async () => {
|
|
212
|
+
const checkEnvCmd = {
|
|
213
|
+
command: process.execPath,
|
|
214
|
+
args: [
|
|
215
|
+
'-e',
|
|
216
|
+
'console.log(process.env.TEST_SECRET_TOKEN || "MISSING")',
|
|
217
|
+
],
|
|
218
|
+
};
|
|
219
|
+
const sandboxed = await manager.prepareCommand({
|
|
220
|
+
...checkEnvCmd,
|
|
221
|
+
cwd: workspace,
|
|
222
|
+
env: { ...process.env, TEST_SECRET_TOKEN: 'super-secret-value' },
|
|
223
|
+
policy: {
|
|
224
|
+
sanitizationConfig: {
|
|
225
|
+
enableEnvironmentVariableRedaction: true,
|
|
226
|
+
blockedEnvironmentVariables: ['TEST_SECRET_TOKEN'],
|
|
227
|
+
},
|
|
228
|
+
},
|
|
229
|
+
});
|
|
230
|
+
const result = await runCommand(sandboxed);
|
|
231
|
+
assertResult(result, sandboxed, 'success');
|
|
232
|
+
// By default, environment sanitization drops non-allowlisted vars or vars that look like secrets.
|
|
233
|
+
// Assuming TEST_SECRET_TOKEN is scrubbed:
|
|
234
|
+
expect(result.stdout.trim()).toBe('MISSING');
|
|
235
|
+
});
|
|
236
|
+
});
|
|
237
|
+
});
|
|
238
|
+
describe('Sandbox Policies & Modes', () => {
|
|
239
|
+
describe('Plan Mode Transitions', () => {
|
|
240
|
+
it('allows writing plans in plan mode', async () => {
|
|
241
|
+
// In Plan Mode, modeConfig sets readonly: true, allowOverrides: true
|
|
242
|
+
const planManager = createSandboxManager({ enabled: true }, { workspace, modeConfig: { readonly: true, allowOverrides: true } });
|
|
243
|
+
const plansDir = path.join(workspace, '.cell-cli/tmp/session-123/plans');
|
|
244
|
+
fs.mkdirSync(plansDir, { recursive: true });
|
|
245
|
+
const planFile = path.join(plansDir, 'feature-plan.md');
|
|
246
|
+
// The WriteFile tool requests explicit write access for the plan file path
|
|
247
|
+
const { command, args } = Platform.touch(planFile);
|
|
248
|
+
const sandboxed = await planManager.prepareCommand({
|
|
249
|
+
command,
|
|
250
|
+
args,
|
|
251
|
+
cwd: workspace,
|
|
252
|
+
env: process.env,
|
|
253
|
+
policy: { allowedPaths: [plansDir] },
|
|
254
|
+
});
|
|
255
|
+
const result = await runCommand(sandboxed);
|
|
256
|
+
assertResult(result, sandboxed, 'success');
|
|
257
|
+
expect(fs.existsSync(planFile)).toBe(true);
|
|
258
|
+
});
|
|
259
|
+
it('allows workspace writes after exiting plan mode', async () => {
|
|
260
|
+
// Upon exiting Plan Mode, the sandbox transitions to autoEdit/accepting_edits
|
|
261
|
+
// which sets readonly: false, allowOverrides: true
|
|
262
|
+
const editManager = createSandboxManager({ enabled: true }, { workspace, modeConfig: { readonly: false, allowOverrides: true } });
|
|
263
|
+
const taskFile = path.join(workspace, 'src/tasks/task.ts');
|
|
264
|
+
const taskDir = path.dirname(taskFile);
|
|
265
|
+
fs.mkdirSync(taskDir, { recursive: true });
|
|
266
|
+
// Simulate a generic edit anywhere in the workspace
|
|
267
|
+
const { command, args } = Platform.touch(taskFile);
|
|
268
|
+
const sandboxed = await editManager.prepareCommand({
|
|
269
|
+
command,
|
|
270
|
+
args,
|
|
271
|
+
cwd: workspace,
|
|
272
|
+
env: process.env,
|
|
273
|
+
policy: { allowedPaths: [taskDir] },
|
|
274
|
+
});
|
|
275
|
+
const result = await runCommand(sandboxed);
|
|
276
|
+
assertResult(result, sandboxed, 'success');
|
|
277
|
+
expect(fs.existsSync(taskFile)).toBe(true);
|
|
278
|
+
});
|
|
279
|
+
});
|
|
280
|
+
describe('Workspace Write Policies', () => {
|
|
281
|
+
it('enforces read-only mode', async () => {
|
|
282
|
+
const testFile = path.join(workspace, 'readonly-test.txt');
|
|
283
|
+
const { command, args } = Platform.touch(testFile);
|
|
284
|
+
const readonlyManager = createSandboxManager({ enabled: true }, {
|
|
285
|
+
workspace,
|
|
286
|
+
modeConfig: { readonly: true, allowOverrides: true },
|
|
287
|
+
});
|
|
288
|
+
const sandboxed = await readonlyManager.prepareCommand({
|
|
289
|
+
command,
|
|
290
|
+
args,
|
|
291
|
+
cwd: workspace,
|
|
292
|
+
env: process.env,
|
|
293
|
+
});
|
|
294
|
+
const result = await runCommand(sandboxed);
|
|
295
|
+
assertResult(result, sandboxed, 'failure');
|
|
296
|
+
});
|
|
297
|
+
it('allows writes for approved tools', async () => {
|
|
298
|
+
const testFile = path.join(workspace, 'approved-test.txt');
|
|
299
|
+
const command = Platform.isWindows ? 'cmd.exe' : 'sh';
|
|
300
|
+
const args = Platform.isWindows
|
|
301
|
+
? ['/c', `echo test > ${testFile}`]
|
|
302
|
+
: ['-c', `echo test > "${testFile}"`];
|
|
303
|
+
// The shell wrapper is stripped by getCommandRoots, so the root command evaluated is 'echo'
|
|
304
|
+
const approvedTool = 'echo';
|
|
305
|
+
const approvedManager = createSandboxManager({ enabled: true }, {
|
|
306
|
+
workspace,
|
|
307
|
+
modeConfig: {
|
|
308
|
+
readonly: true,
|
|
309
|
+
allowOverrides: true,
|
|
310
|
+
approvedTools: [approvedTool],
|
|
311
|
+
},
|
|
312
|
+
});
|
|
313
|
+
const sandboxed = await approvedManager.prepareCommand({
|
|
314
|
+
command,
|
|
315
|
+
args,
|
|
316
|
+
cwd: workspace,
|
|
317
|
+
env: process.env,
|
|
318
|
+
});
|
|
319
|
+
const result = await runCommand(sandboxed);
|
|
320
|
+
assertResult(result, sandboxed, 'success');
|
|
321
|
+
expect(fs.existsSync(testFile)).toBe(true);
|
|
322
|
+
});
|
|
323
|
+
it('allows writes in YOLO mode', async () => {
|
|
324
|
+
const testFile = path.join(workspace, 'yolo-test.txt');
|
|
325
|
+
const { command, args } = Platform.touch(testFile);
|
|
326
|
+
const yoloManager = createSandboxManager({ enabled: true }, {
|
|
327
|
+
workspace,
|
|
328
|
+
modeConfig: { readonly: true, yolo: true, allowOverrides: true },
|
|
329
|
+
});
|
|
330
|
+
const sandboxed = await yoloManager.prepareCommand({
|
|
331
|
+
command,
|
|
332
|
+
args,
|
|
333
|
+
cwd: workspace,
|
|
334
|
+
env: process.env,
|
|
335
|
+
});
|
|
336
|
+
const result = await runCommand(sandboxed);
|
|
337
|
+
assertResult(result, sandboxed, 'success');
|
|
338
|
+
expect(fs.existsSync(testFile)).toBe(true);
|
|
339
|
+
});
|
|
340
|
+
});
|
|
341
|
+
});
|
|
342
|
+
describe('File System Security', () => {
|
|
154
343
|
describe('File System Access', () => {
|
|
155
|
-
it('
|
|
344
|
+
it('prevents out-of-bounds access', async () => {
|
|
156
345
|
const blockedPath = Platform.getExternalBlockedPath();
|
|
157
346
|
const { command, args } = Platform.touch(blockedPath);
|
|
158
347
|
const sandboxed = await manager.prepareCommand({
|
|
@@ -162,233 +351,506 @@ describe('SandboxManager Integration', () => {
|
|
|
162
351
|
env: process.env,
|
|
163
352
|
});
|
|
164
353
|
const result = await runCommand(sandboxed);
|
|
165
|
-
|
|
354
|
+
assertResult(result, sandboxed, 'failure');
|
|
166
355
|
});
|
|
167
|
-
it('
|
|
168
|
-
const tempDir =
|
|
356
|
+
it('supports dynamic permission expansion', async () => {
|
|
357
|
+
const tempDir = createTempDir('expansion-');
|
|
169
358
|
const testFile = path.join(tempDir, 'test.txt');
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
expect(fs.existsSync(testFile)).toBe(true);
|
|
193
|
-
}
|
|
194
|
-
finally {
|
|
195
|
-
if (fs.existsSync(testFile))
|
|
196
|
-
fs.unlinkSync(testFile);
|
|
197
|
-
fs.rmSync(tempDir, { recursive: true, force: true });
|
|
198
|
-
}
|
|
359
|
+
const { command, args } = Platform.touch(testFile);
|
|
360
|
+
// First attempt: fails due to sandbox restrictions
|
|
361
|
+
const sandboxed1 = await manager.prepareCommand({
|
|
362
|
+
command,
|
|
363
|
+
args,
|
|
364
|
+
cwd: workspace,
|
|
365
|
+
env: process.env,
|
|
366
|
+
});
|
|
367
|
+
const result1 = await runCommand(sandboxed1);
|
|
368
|
+
assertResult(result1, sandboxed1, 'failure');
|
|
369
|
+
expect(fs.existsSync(testFile)).toBe(false);
|
|
370
|
+
// Second attempt: succeeds with additional permissions
|
|
371
|
+
const sandboxed2 = await manager.prepareCommand({
|
|
372
|
+
command,
|
|
373
|
+
args,
|
|
374
|
+
cwd: workspace,
|
|
375
|
+
env: process.env,
|
|
376
|
+
policy: { allowedPaths: [tempDir] },
|
|
377
|
+
});
|
|
378
|
+
const result2 = await runCommand(sandboxed2);
|
|
379
|
+
assertResult(result2, sandboxed2, 'success');
|
|
380
|
+
expect(fs.existsSync(testFile)).toBe(true);
|
|
199
381
|
});
|
|
200
|
-
it('
|
|
201
|
-
const allowedDir =
|
|
382
|
+
it('allows access to authorized paths', async () => {
|
|
383
|
+
const allowedDir = createTempDir('allowed-');
|
|
202
384
|
const testFile = path.join(allowedDir, 'test.txt');
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
expect(fs.existsSync(testFile)).toBe(true);
|
|
215
|
-
}
|
|
216
|
-
finally {
|
|
217
|
-
if (fs.existsSync(testFile))
|
|
218
|
-
fs.unlinkSync(testFile);
|
|
219
|
-
fs.rmSync(allowedDir, { recursive: true, force: true });
|
|
220
|
-
}
|
|
385
|
+
const { command, args } = Platform.touch(testFile);
|
|
386
|
+
const sandboxed = await manager.prepareCommand({
|
|
387
|
+
command,
|
|
388
|
+
args,
|
|
389
|
+
cwd: workspace,
|
|
390
|
+
env: process.env,
|
|
391
|
+
policy: { allowedPaths: [allowedDir] },
|
|
392
|
+
});
|
|
393
|
+
const result = await runCommand(sandboxed);
|
|
394
|
+
assertResult(result, sandboxed, 'success');
|
|
395
|
+
expect(fs.existsSync(testFile)).toBe(true);
|
|
221
396
|
});
|
|
222
|
-
it('
|
|
223
|
-
const tempWorkspace =
|
|
397
|
+
it('protects forbidden paths from writes', async () => {
|
|
398
|
+
const tempWorkspace = createTempDir('workspace-');
|
|
224
399
|
const forbiddenDir = path.join(tempWorkspace, 'forbidden');
|
|
225
400
|
const testFile = path.join(forbiddenDir, 'test.txt');
|
|
226
401
|
fs.mkdirSync(forbiddenDir);
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
expect(result.status).not.toBe(0);
|
|
241
|
-
}
|
|
242
|
-
finally {
|
|
243
|
-
fs.rmSync(tempWorkspace, { recursive: true, force: true });
|
|
244
|
-
}
|
|
402
|
+
const osManager = createSandboxManager({ enabled: true }, {
|
|
403
|
+
workspace: tempWorkspace,
|
|
404
|
+
forbiddenPaths: async () => [forbiddenDir],
|
|
405
|
+
});
|
|
406
|
+
const { command, args } = Platform.touch(testFile);
|
|
407
|
+
const sandboxed = await osManager.prepareCommand({
|
|
408
|
+
command,
|
|
409
|
+
args,
|
|
410
|
+
cwd: tempWorkspace,
|
|
411
|
+
env: process.env,
|
|
412
|
+
});
|
|
413
|
+
const result = await runCommand(sandboxed);
|
|
414
|
+
assertResult(result, sandboxed, 'failure');
|
|
245
415
|
});
|
|
246
|
-
|
|
247
|
-
|
|
416
|
+
// Windows icacls does not reliably block read-up access for Low Integrity
|
|
417
|
+
// processes, so we skip read-specific assertions on Windows. The internal
|
|
418
|
+
// tool architecture prevents read bypasses via the C# wrapper and __read.
|
|
419
|
+
it.skipIf(Platform.isWindows)('protects forbidden paths from reads', async () => {
|
|
420
|
+
const tempWorkspace = createTempDir('workspace-');
|
|
421
|
+
const forbiddenDir = path.join(tempWorkspace, 'forbidden');
|
|
422
|
+
const testFile = path.join(forbiddenDir, 'test.txt');
|
|
423
|
+
fs.mkdirSync(forbiddenDir);
|
|
424
|
+
fs.writeFileSync(testFile, 'secret data');
|
|
425
|
+
const osManager = createSandboxManager({ enabled: true }, {
|
|
426
|
+
workspace: tempWorkspace,
|
|
427
|
+
forbiddenPaths: async () => [forbiddenDir],
|
|
428
|
+
});
|
|
429
|
+
const { command, args } = Platform.cat(testFile);
|
|
430
|
+
const sandboxed = await osManager.prepareCommand({
|
|
431
|
+
command,
|
|
432
|
+
args,
|
|
433
|
+
cwd: tempWorkspace,
|
|
434
|
+
env: process.env,
|
|
435
|
+
});
|
|
436
|
+
const result = await runCommand(sandboxed);
|
|
437
|
+
assertResult(result, sandboxed, 'failure');
|
|
438
|
+
});
|
|
439
|
+
it('protects forbidden directories recursively', async () => {
|
|
440
|
+
const tempWorkspace = createTempDir('workspace-');
|
|
248
441
|
const forbiddenDir = path.join(tempWorkspace, 'forbidden');
|
|
249
442
|
const nestedDir = path.join(forbiddenDir, 'nested');
|
|
250
443
|
const nestedFile = path.join(nestedDir, 'test.txt');
|
|
444
|
+
// Create the base forbidden directory first so the manager can restrict access to it.
|
|
445
|
+
fs.mkdirSync(forbiddenDir);
|
|
446
|
+
const osManager = createSandboxManager({ enabled: true }, {
|
|
447
|
+
workspace: tempWorkspace,
|
|
448
|
+
forbiddenPaths: async () => [forbiddenDir],
|
|
449
|
+
});
|
|
450
|
+
// Execute a dummy command so the manager initializes its restrictions.
|
|
451
|
+
const dummyCommand = await osManager.prepareCommand({
|
|
452
|
+
...Platform.echo('init'),
|
|
453
|
+
cwd: tempWorkspace,
|
|
454
|
+
env: process.env,
|
|
455
|
+
});
|
|
456
|
+
await runCommand(dummyCommand);
|
|
457
|
+
// Now create the nested items. They will inherit the sandbox restrictions from their parent.
|
|
251
458
|
fs.mkdirSync(nestedDir, { recursive: true });
|
|
252
459
|
fs.writeFileSync(nestedFile, 'secret');
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
cwd: tempWorkspace,
|
|
263
|
-
env: process.env,
|
|
264
|
-
});
|
|
265
|
-
const result = await runCommand(sandboxed);
|
|
266
|
-
expect(result.status).not.toBe(0);
|
|
267
|
-
}
|
|
268
|
-
finally {
|
|
269
|
-
fs.rmSync(tempWorkspace, { recursive: true, force: true });
|
|
270
|
-
}
|
|
460
|
+
const { command, args } = Platform.touch(nestedFile);
|
|
461
|
+
const sandboxed = await osManager.prepareCommand({
|
|
462
|
+
command,
|
|
463
|
+
args,
|
|
464
|
+
cwd: tempWorkspace,
|
|
465
|
+
env: process.env,
|
|
466
|
+
});
|
|
467
|
+
const result = await runCommand(sandboxed);
|
|
468
|
+
assertResult(result, sandboxed, 'failure');
|
|
271
469
|
});
|
|
272
|
-
it('prioritizes
|
|
273
|
-
const tempWorkspace =
|
|
470
|
+
it('prioritizes denials over allowances', async () => {
|
|
471
|
+
const tempWorkspace = createTempDir('workspace-');
|
|
274
472
|
const conflictDir = path.join(tempWorkspace, 'conflict');
|
|
275
473
|
const testFile = path.join(conflictDir, 'test.txt');
|
|
276
474
|
fs.mkdirSync(conflictDir);
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
expect(result.status).not.toBe(0);
|
|
294
|
-
}
|
|
295
|
-
finally {
|
|
296
|
-
fs.rmSync(tempWorkspace, { recursive: true, force: true });
|
|
297
|
-
}
|
|
475
|
+
const osManager = createSandboxManager({ enabled: true }, {
|
|
476
|
+
workspace: tempWorkspace,
|
|
477
|
+
forbiddenPaths: async () => [conflictDir],
|
|
478
|
+
});
|
|
479
|
+
const { command, args } = Platform.touch(testFile);
|
|
480
|
+
const sandboxed = await osManager.prepareCommand({
|
|
481
|
+
command,
|
|
482
|
+
args,
|
|
483
|
+
cwd: tempWorkspace,
|
|
484
|
+
env: process.env,
|
|
485
|
+
policy: {
|
|
486
|
+
allowedPaths: [conflictDir],
|
|
487
|
+
},
|
|
488
|
+
});
|
|
489
|
+
const result = await runCommand(sandboxed);
|
|
490
|
+
assertResult(result, sandboxed, 'failure');
|
|
298
491
|
});
|
|
299
|
-
it('
|
|
300
|
-
const tempWorkspace =
|
|
492
|
+
it('handles missing paths gracefully', async () => {
|
|
493
|
+
const tempWorkspace = createTempDir('workspace-');
|
|
301
494
|
const nonExistentPath = path.join(tempWorkspace, 'does-not-exist');
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
expect(result.stdout.trim()).toBe('survived');
|
|
320
|
-
}
|
|
321
|
-
finally {
|
|
322
|
-
fs.rmSync(tempWorkspace, { recursive: true, force: true });
|
|
323
|
-
}
|
|
495
|
+
const osManager = createSandboxManager({ enabled: true }, {
|
|
496
|
+
workspace: tempWorkspace,
|
|
497
|
+
forbiddenPaths: async () => [nonExistentPath],
|
|
498
|
+
});
|
|
499
|
+
const { command, args } = Platform.echo('survived');
|
|
500
|
+
const sandboxed = await osManager.prepareCommand({
|
|
501
|
+
command,
|
|
502
|
+
args,
|
|
503
|
+
cwd: tempWorkspace,
|
|
504
|
+
env: process.env,
|
|
505
|
+
policy: {
|
|
506
|
+
allowedPaths: [nonExistentPath],
|
|
507
|
+
},
|
|
508
|
+
});
|
|
509
|
+
const result = await runCommand(sandboxed);
|
|
510
|
+
assertResult(result, sandboxed, 'success');
|
|
511
|
+
expect(result.stdout.trim()).toBe('survived');
|
|
324
512
|
});
|
|
325
|
-
it('prevents creation of
|
|
326
|
-
|
|
327
|
-
if (Platform.isWindows)
|
|
328
|
-
return;
|
|
329
|
-
const tempWorkspace = fs.mkdtempSync(path.join(os.tmpdir(), 'workspace-'));
|
|
513
|
+
it('prevents creation of forbidden files', async () => {
|
|
514
|
+
const tempWorkspace = createTempDir('workspace-');
|
|
330
515
|
const nonExistentFile = path.join(tempWorkspace, 'never-created.txt');
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
expect(fs.existsSync(nonExistentFile)).toBe(false);
|
|
348
|
-
}
|
|
349
|
-
finally {
|
|
350
|
-
fs.rmSync(tempWorkspace, { recursive: true, force: true });
|
|
351
|
-
}
|
|
516
|
+
const osManager = createSandboxManager({ enabled: true }, {
|
|
517
|
+
workspace: tempWorkspace,
|
|
518
|
+
forbiddenPaths: async () => [nonExistentFile],
|
|
519
|
+
});
|
|
520
|
+
// We use touch to attempt creation of the file
|
|
521
|
+
const { command: cmdTouch, args: argsTouch } = Platform.touch(nonExistentFile);
|
|
522
|
+
const sandboxedCmd = await osManager.prepareCommand({
|
|
523
|
+
command: cmdTouch,
|
|
524
|
+
args: argsTouch,
|
|
525
|
+
cwd: tempWorkspace,
|
|
526
|
+
env: process.env,
|
|
527
|
+
});
|
|
528
|
+
// Execute the command, we expect it to fail (permission denied or read-only file system)
|
|
529
|
+
const result = await runCommand(sandboxedCmd);
|
|
530
|
+
assertResult(result, sandboxedCmd, 'failure');
|
|
531
|
+
expect(fs.existsSync(nonExistentFile)).toBe(false);
|
|
352
532
|
});
|
|
353
|
-
it('
|
|
354
|
-
|
|
355
|
-
return;
|
|
356
|
-
const tempWorkspace = fs.mkdtempSync(path.join(os.tmpdir(), 'workspace-'));
|
|
533
|
+
it('restricts symlinks to forbidden targets', async () => {
|
|
534
|
+
const tempWorkspace = createTempDir('workspace-');
|
|
357
535
|
const targetFile = path.join(tempWorkspace, 'target.txt');
|
|
358
536
|
const symlinkFile = path.join(tempWorkspace, 'link.txt');
|
|
359
537
|
fs.writeFileSync(targetFile, 'secret data');
|
|
360
538
|
fs.symlinkSync(targetFile, symlinkFile);
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
539
|
+
const osManager = createSandboxManager({ enabled: true }, {
|
|
540
|
+
workspace: tempWorkspace,
|
|
541
|
+
forbiddenPaths: async () => [symlinkFile],
|
|
542
|
+
});
|
|
543
|
+
// Attempt to write to the target file directly
|
|
544
|
+
const { command: cmdTarget, args: argsTarget } = Platform.touch(targetFile);
|
|
545
|
+
const commandTarget = await osManager.prepareCommand({
|
|
546
|
+
command: cmdTarget,
|
|
547
|
+
args: argsTarget,
|
|
548
|
+
cwd: tempWorkspace,
|
|
549
|
+
env: process.env,
|
|
550
|
+
});
|
|
551
|
+
const resultTarget = await runCommand(commandTarget);
|
|
552
|
+
assertResult(resultTarget, commandTarget, 'failure');
|
|
553
|
+
// Attempt to write via the symlink
|
|
554
|
+
const { command: cmdLink, args: argsLink } = Platform.touch(symlinkFile);
|
|
555
|
+
const commandLink = await osManager.prepareCommand({
|
|
556
|
+
command: cmdLink,
|
|
557
|
+
args: argsLink,
|
|
558
|
+
cwd: tempWorkspace,
|
|
559
|
+
env: process.env,
|
|
560
|
+
});
|
|
561
|
+
const resultLink = await runCommand(commandLink);
|
|
562
|
+
assertResult(resultLink, commandLink, 'failure');
|
|
563
|
+
});
|
|
564
|
+
});
|
|
565
|
+
describe('Governance Files', () => {
|
|
566
|
+
it('prevents modification of governance files', async () => {
|
|
567
|
+
// Ensure workspace is initialized and governance files are created
|
|
568
|
+
const { command: echoCmd, args: echoArgs } = Platform.echo('test');
|
|
569
|
+
await manager.prepareCommand({
|
|
570
|
+
command: echoCmd,
|
|
571
|
+
args: echoArgs,
|
|
572
|
+
cwd: workspace,
|
|
573
|
+
env: process.env,
|
|
574
|
+
// Even if the entire workspace is explicitly allowed, governance files must be protected
|
|
575
|
+
policy: { allowedPaths: [workspace] },
|
|
576
|
+
});
|
|
577
|
+
for (const file of GOVERNANCE_FILES) {
|
|
578
|
+
const filePath = path.join(workspace, file.path);
|
|
579
|
+
// Try to append to/overwrite the file or create a file inside the directory
|
|
580
|
+
const { command, args } = file.isDirectory
|
|
581
|
+
? Platform.touch(path.join(filePath, 'evil.txt'))
|
|
582
|
+
: Platform.touch(filePath);
|
|
583
|
+
const sandboxed = await manager.prepareCommand({
|
|
584
|
+
command,
|
|
585
|
+
args,
|
|
586
|
+
cwd: workspace,
|
|
382
587
|
env: process.env,
|
|
383
588
|
});
|
|
384
|
-
const
|
|
385
|
-
|
|
386
|
-
}
|
|
387
|
-
finally {
|
|
388
|
-
fs.rmSync(tempWorkspace, { recursive: true, force: true });
|
|
589
|
+
const result = await runCommand(sandboxed);
|
|
590
|
+
assertResult(result, sandboxed, 'failure');
|
|
389
591
|
}
|
|
390
592
|
});
|
|
391
593
|
});
|
|
594
|
+
describe('Git Worktree Support', () => {
|
|
595
|
+
it('supports git worktrees', async () => {
|
|
596
|
+
const mainRepo = createTempDir('main-repo-');
|
|
597
|
+
const worktreeDir = createTempDir('worktree-');
|
|
598
|
+
const mainGitDir = path.join(mainRepo, '.git');
|
|
599
|
+
fs.mkdirSync(mainGitDir, { recursive: true });
|
|
600
|
+
fs.writeFileSync(path.join(mainGitDir, 'config'), '[core]\n\trepositoryformatversion = 0\n');
|
|
601
|
+
const worktreeGitDir = path.join(mainGitDir, 'worktrees', 'test-worktree');
|
|
602
|
+
fs.mkdirSync(worktreeGitDir, { recursive: true });
|
|
603
|
+
// Create the .git file in the worktree directory pointing to the worktree git dir
|
|
604
|
+
fs.writeFileSync(path.join(worktreeDir, '.git'), `gitdir: ${worktreeGitDir}\n`);
|
|
605
|
+
// Create the backlink from worktree git dir to the worktree's .git file
|
|
606
|
+
const backlinkPath = path.join(worktreeGitDir, 'gitdir');
|
|
607
|
+
fs.writeFileSync(backlinkPath, path.join(worktreeDir, '.git'));
|
|
608
|
+
// Create a file in the worktree git dir that we want to access
|
|
609
|
+
const secretFile = path.join(worktreeGitDir, 'secret.txt');
|
|
610
|
+
fs.writeFileSync(secretFile, 'git-secret');
|
|
611
|
+
const osManager = createSandboxManager({ enabled: true }, { workspace: worktreeDir });
|
|
612
|
+
const { command, args } = Platform.cat(secretFile);
|
|
613
|
+
const sandboxed = await osManager.prepareCommand({
|
|
614
|
+
command,
|
|
615
|
+
args,
|
|
616
|
+
cwd: worktreeDir,
|
|
617
|
+
env: process.env,
|
|
618
|
+
});
|
|
619
|
+
const result = await runCommand(sandboxed);
|
|
620
|
+
assertResult(result, sandboxed, 'success');
|
|
621
|
+
expect(result.stdout.trim()).toBe('git-secret');
|
|
622
|
+
});
|
|
623
|
+
it('protects git worktree metadata', async () => {
|
|
624
|
+
const mainRepo = createTempDir('main-repo-');
|
|
625
|
+
const worktreeDir = createTempDir('worktree-');
|
|
626
|
+
const mainGitDir = path.join(mainRepo, '.git');
|
|
627
|
+
fs.mkdirSync(mainGitDir, { recursive: true });
|
|
628
|
+
const worktreeGitDir = path.join(mainGitDir, 'worktrees', 'test-worktree');
|
|
629
|
+
fs.mkdirSync(worktreeGitDir, { recursive: true });
|
|
630
|
+
fs.writeFileSync(path.join(worktreeDir, '.git'), `gitdir: ${worktreeGitDir}\n`);
|
|
631
|
+
fs.writeFileSync(path.join(worktreeGitDir, 'gitdir'), path.join(worktreeDir, '.git'));
|
|
632
|
+
const targetFile = path.join(worktreeGitDir, 'secret.txt');
|
|
633
|
+
const osManager = createSandboxManager({ enabled: true },
|
|
634
|
+
// Use YOLO mode to ensure the workspace is fully writable, but git worktrees should still be read-only
|
|
635
|
+
{ workspace: worktreeDir, modeConfig: { yolo: true } });
|
|
636
|
+
const { command, args } = Platform.touch(targetFile);
|
|
637
|
+
const sandboxed = await osManager.prepareCommand({
|
|
638
|
+
command,
|
|
639
|
+
args,
|
|
640
|
+
cwd: worktreeDir,
|
|
641
|
+
env: process.env,
|
|
642
|
+
});
|
|
643
|
+
const result = await runCommand(sandboxed);
|
|
644
|
+
assertResult(result, sandboxed, 'failure');
|
|
645
|
+
expect(fs.existsSync(targetFile)).toBe(false);
|
|
646
|
+
});
|
|
647
|
+
});
|
|
648
|
+
});
|
|
649
|
+
describe('Governance Files', () => {
|
|
650
|
+
it('blocks write access to governance files in the workspace', async () => {
|
|
651
|
+
const tempWorkspace = createTempDir('workspace-');
|
|
652
|
+
const gitDir = path.join(tempWorkspace, '.git');
|
|
653
|
+
fs.mkdirSync(gitDir);
|
|
654
|
+
const testFile = path.join(gitDir, 'config');
|
|
655
|
+
const osManager = createSandboxManager({ enabled: true }, { workspace: tempWorkspace });
|
|
656
|
+
const { command, args } = Platform.touch(testFile);
|
|
657
|
+
const sandboxed = await osManager.prepareCommand({
|
|
658
|
+
command,
|
|
659
|
+
args,
|
|
660
|
+
cwd: tempWorkspace,
|
|
661
|
+
env: process.env,
|
|
662
|
+
});
|
|
663
|
+
const result = await runCommand(sandboxed);
|
|
664
|
+
assertResult(result, sandboxed, 'failure');
|
|
665
|
+
expect(fs.existsSync(testFile)).toBe(false);
|
|
666
|
+
});
|
|
667
|
+
it('allows write access to governance files when explicitly requested via additionalPermissions', async () => {
|
|
668
|
+
const tempWorkspace = createTempDir('workspace-');
|
|
669
|
+
const gitDir = path.join(tempWorkspace, '.git');
|
|
670
|
+
fs.mkdirSync(gitDir);
|
|
671
|
+
const testFile = path.join(gitDir, 'config');
|
|
672
|
+
const osManager = createSandboxManager({ enabled: true }, { workspace: tempWorkspace });
|
|
673
|
+
const { command, args } = Platform.touch(testFile);
|
|
674
|
+
const sandboxed = await osManager.prepareCommand({
|
|
675
|
+
command,
|
|
676
|
+
args,
|
|
677
|
+
cwd: tempWorkspace,
|
|
678
|
+
env: process.env,
|
|
679
|
+
policy: {
|
|
680
|
+
additionalPermissions: { fileSystem: { write: [gitDir] } },
|
|
681
|
+
},
|
|
682
|
+
});
|
|
683
|
+
const result = await runCommand(sandboxed);
|
|
684
|
+
assertResult(result, sandboxed, 'success');
|
|
685
|
+
expect(fs.existsSync(testFile)).toBe(true);
|
|
686
|
+
});
|
|
687
|
+
});
|
|
688
|
+
describe('Git Worktree Support', () => {
|
|
689
|
+
it('allows access to git common directory in a worktree', async () => {
|
|
690
|
+
const mainRepo = createTempDir('main-repo-');
|
|
691
|
+
const worktreeDir = createTempDir('worktree-');
|
|
692
|
+
const mainGitDir = path.join(mainRepo, '.git');
|
|
693
|
+
fs.mkdirSync(mainGitDir, { recursive: true });
|
|
694
|
+
fs.writeFileSync(path.join(mainGitDir, 'config'), '[core]\n\trepositoryformatversion = 0\n');
|
|
695
|
+
const worktreeGitDir = path.join(mainGitDir, 'worktrees', 'test-worktree');
|
|
696
|
+
fs.mkdirSync(worktreeGitDir, { recursive: true });
|
|
697
|
+
// Create the .git file in the worktree directory pointing to the worktree git dir
|
|
698
|
+
fs.writeFileSync(path.join(worktreeDir, '.git'), `gitdir: ${worktreeGitDir}\n`);
|
|
699
|
+
// Create the backlink from worktree git dir to the worktree's .git file
|
|
700
|
+
const backlinkPath = path.join(worktreeGitDir, 'gitdir');
|
|
701
|
+
fs.writeFileSync(backlinkPath, path.join(worktreeDir, '.git'));
|
|
702
|
+
// Create a file in the worktree git dir that we want to access
|
|
703
|
+
const secretFile = path.join(worktreeGitDir, 'secret.txt');
|
|
704
|
+
fs.writeFileSync(secretFile, 'git-secret');
|
|
705
|
+
const osManager = createSandboxManager({ enabled: true }, { workspace: worktreeDir });
|
|
706
|
+
const { command, args } = Platform.cat(secretFile);
|
|
707
|
+
const sandboxed = await osManager.prepareCommand({
|
|
708
|
+
command,
|
|
709
|
+
args,
|
|
710
|
+
cwd: worktreeDir,
|
|
711
|
+
env: process.env,
|
|
712
|
+
});
|
|
713
|
+
const result = await runCommand(sandboxed);
|
|
714
|
+
assertResult(result, sandboxed, 'success');
|
|
715
|
+
expect(result.stdout.trim()).toBe('git-secret');
|
|
716
|
+
});
|
|
717
|
+
it('blocks write access to git common directory in a worktree', async () => {
|
|
718
|
+
const mainRepo = createTempDir('main-repo-');
|
|
719
|
+
const worktreeDir = createTempDir('worktree-');
|
|
720
|
+
const mainGitDir = path.join(mainRepo, '.git');
|
|
721
|
+
fs.mkdirSync(mainGitDir, { recursive: true });
|
|
722
|
+
const worktreeGitDir = path.join(mainGitDir, 'worktrees', 'test-worktree');
|
|
723
|
+
fs.mkdirSync(worktreeGitDir, { recursive: true });
|
|
724
|
+
fs.writeFileSync(path.join(worktreeDir, '.git'), `gitdir: ${worktreeGitDir}\n`);
|
|
725
|
+
fs.writeFileSync(path.join(worktreeGitDir, 'gitdir'), path.join(worktreeDir, '.git'));
|
|
726
|
+
const targetFile = path.join(worktreeGitDir, 'secret.txt');
|
|
727
|
+
const osManager = createSandboxManager({ enabled: true },
|
|
728
|
+
// Use YOLO mode to ensure the workspace is fully writable, but git worktrees should still be read-only
|
|
729
|
+
{ workspace: worktreeDir, modeConfig: { yolo: true } });
|
|
730
|
+
const { command, args } = Platform.touch(targetFile);
|
|
731
|
+
const sandboxed = await osManager.prepareCommand({
|
|
732
|
+
command,
|
|
733
|
+
args,
|
|
734
|
+
cwd: worktreeDir,
|
|
735
|
+
env: process.env,
|
|
736
|
+
});
|
|
737
|
+
const result = await runCommand(sandboxed);
|
|
738
|
+
assertResult(result, sandboxed, 'failure');
|
|
739
|
+
expect(fs.existsSync(targetFile)).toBe(false);
|
|
740
|
+
});
|
|
741
|
+
it('blocks write access to git common directory in a worktree when not explicitly requested via additionalPermissions', async () => {
|
|
742
|
+
const mainRepo = createTempDir('main-repo-');
|
|
743
|
+
const worktreeDir = createTempDir('worktree-');
|
|
744
|
+
const mainGitDir = path.join(mainRepo, '.git');
|
|
745
|
+
fs.mkdirSync(mainGitDir, { recursive: true });
|
|
746
|
+
const worktreeGitDir = path.join(mainGitDir, 'worktrees', 'test-worktree');
|
|
747
|
+
fs.mkdirSync(worktreeGitDir, { recursive: true });
|
|
748
|
+
fs.writeFileSync(path.join(worktreeDir, '.git'), `gitdir: ${worktreeGitDir}\n`);
|
|
749
|
+
fs.writeFileSync(path.join(worktreeGitDir, 'gitdir'), path.join(worktreeDir, '.git'));
|
|
750
|
+
const targetFile = path.join(worktreeGitDir, 'secret.txt');
|
|
751
|
+
const osManager = createSandboxManager({ enabled: true }, { workspace: worktreeDir });
|
|
752
|
+
const { command, args } = Platform.touch(targetFile);
|
|
753
|
+
const sandboxed = await osManager.prepareCommand({
|
|
754
|
+
command,
|
|
755
|
+
args,
|
|
756
|
+
cwd: worktreeDir,
|
|
757
|
+
env: process.env,
|
|
758
|
+
});
|
|
759
|
+
const result = await runCommand(sandboxed);
|
|
760
|
+
assertResult(result, sandboxed, 'failure');
|
|
761
|
+
expect(fs.existsSync(targetFile)).toBe(false);
|
|
762
|
+
});
|
|
763
|
+
it('allows write access to git common directory in a worktree when explicitly requested via additionalPermissions', async () => {
|
|
764
|
+
const mainRepo = createTempDir('main-repo-');
|
|
765
|
+
const worktreeDir = createTempDir('worktree-');
|
|
766
|
+
const mainGitDir = path.join(mainRepo, '.git');
|
|
767
|
+
fs.mkdirSync(mainGitDir, { recursive: true });
|
|
768
|
+
const worktreeGitDir = path.join(mainGitDir, 'worktrees', 'test-worktree');
|
|
769
|
+
fs.mkdirSync(worktreeGitDir, { recursive: true });
|
|
770
|
+
fs.writeFileSync(path.join(worktreeDir, '.git'), `gitdir: ${worktreeGitDir}\n`);
|
|
771
|
+
fs.writeFileSync(path.join(worktreeGitDir, 'gitdir'), path.join(worktreeDir, '.git'));
|
|
772
|
+
const targetFile = path.join(worktreeGitDir, 'secret.txt');
|
|
773
|
+
const osManager = createSandboxManager({ enabled: true }, { workspace: worktreeDir });
|
|
774
|
+
const { command, args } = Platform.touch(targetFile);
|
|
775
|
+
const sandboxed = await osManager.prepareCommand({
|
|
776
|
+
command,
|
|
777
|
+
args,
|
|
778
|
+
cwd: worktreeDir,
|
|
779
|
+
env: process.env,
|
|
780
|
+
policy: {
|
|
781
|
+
additionalPermissions: { fileSystem: { write: [worktreeGitDir] } },
|
|
782
|
+
},
|
|
783
|
+
});
|
|
784
|
+
const result = await runCommand(sandboxed);
|
|
785
|
+
assertResult(result, sandboxed, 'success');
|
|
786
|
+
expect(fs.existsSync(targetFile)).toBe(true);
|
|
787
|
+
});
|
|
788
|
+
it('allows write access to external git directory in a non-worktree environment when explicitly requested via additionalPermissions', async () => {
|
|
789
|
+
const externalGitDir = createTempDir('external-git-');
|
|
790
|
+
const workspaceDir = createTempDir('workspace-');
|
|
791
|
+
fs.mkdirSync(externalGitDir, { recursive: true });
|
|
792
|
+
fs.writeFileSync(path.join(workspaceDir, '.git'), `gitdir: ${externalGitDir}\n`);
|
|
793
|
+
const targetFile = path.join(externalGitDir, 'secret.txt');
|
|
794
|
+
const osManager = createSandboxManager({ enabled: true }, { workspace: workspaceDir });
|
|
795
|
+
const { command, args } = Platform.touch(targetFile);
|
|
796
|
+
const sandboxed = await osManager.prepareCommand({
|
|
797
|
+
command,
|
|
798
|
+
args,
|
|
799
|
+
cwd: workspaceDir,
|
|
800
|
+
env: process.env,
|
|
801
|
+
policy: {
|
|
802
|
+
additionalPermissions: { fileSystem: { write: [externalGitDir] } },
|
|
803
|
+
},
|
|
804
|
+
});
|
|
805
|
+
const result = await runCommand(sandboxed);
|
|
806
|
+
assertResult(result, sandboxed, 'success');
|
|
807
|
+
expect(fs.existsSync(targetFile)).toBe(true);
|
|
808
|
+
});
|
|
809
|
+
});
|
|
810
|
+
describe('Git and Governance Write Access', () => {
|
|
811
|
+
it('allows write access to .gitignore when workspace is writable', async () => {
|
|
812
|
+
const testFile = path.join(workspace, '.gitignore');
|
|
813
|
+
fs.writeFileSync(testFile, 'initial');
|
|
814
|
+
const editManager = createSandboxManager({ enabled: true }, { workspace, modeConfig: { readonly: false, allowOverrides: true } });
|
|
815
|
+
const { command, args } = Platform.touch(testFile);
|
|
816
|
+
const sandboxed = await editManager.prepareCommand({
|
|
817
|
+
command,
|
|
818
|
+
args,
|
|
819
|
+
cwd: workspace,
|
|
820
|
+
env: process.env,
|
|
821
|
+
});
|
|
822
|
+
const result = await runCommand(sandboxed);
|
|
823
|
+
assertResult(result, sandboxed, 'success');
|
|
824
|
+
expect(fs.existsSync(testFile)).toBe(true);
|
|
825
|
+
});
|
|
826
|
+
it('automatically allows write access to .git when running git command and workspace is writable', async () => {
|
|
827
|
+
const gitDir = path.join(workspace, '.git');
|
|
828
|
+
if (!fs.existsSync(gitDir))
|
|
829
|
+
fs.mkdirSync(gitDir);
|
|
830
|
+
const lockFile = path.join(gitDir, 'index.lock');
|
|
831
|
+
const editManager = createSandboxManager({ enabled: true }, { workspace, modeConfig: { readonly: false, allowOverrides: true } });
|
|
832
|
+
// We use a command that looks like git to trigger the special handling.
|
|
833
|
+
// LinuxSandboxManager identifies the command root from the shell wrapper.
|
|
834
|
+
const { command: nodePath, args: nodeArgs } = Platform.touch(lockFile);
|
|
835
|
+
const commandString = Platform.isWindows
|
|
836
|
+
? `git --version > NUL && "${nodePath.replace(/\\/g, '/')}" ${nodeArgs
|
|
837
|
+
.map((a) => `'${a.replace(/\\/g, '/')}'`)
|
|
838
|
+
.join(' ')}`
|
|
839
|
+
: `git --version > /dev/null; "${nodePath}" ${nodeArgs
|
|
840
|
+
.map((a) => (a.includes(' ') || a.includes('(') ? `'${a}'` : a))
|
|
841
|
+
.join(' ')}`;
|
|
842
|
+
const sandboxed = await editManager.prepareCommand({
|
|
843
|
+
command: 'sh',
|
|
844
|
+
args: ['-c', commandString],
|
|
845
|
+
cwd: workspace,
|
|
846
|
+
env: process.env,
|
|
847
|
+
});
|
|
848
|
+
const result = await runCommand(sandboxed);
|
|
849
|
+
assertResult(result, sandboxed, 'success');
|
|
850
|
+
expect(fs.existsSync(lockFile)).toBe(true);
|
|
851
|
+
});
|
|
852
|
+
});
|
|
853
|
+
describe('Network Security', () => {
|
|
392
854
|
describe('Network Access', () => {
|
|
393
855
|
let server;
|
|
394
856
|
let url;
|
|
@@ -413,7 +875,7 @@ describe('SandboxManager Integration', () => {
|
|
|
413
875
|
});
|
|
414
876
|
// Windows Job Object rate limits exempt loopback (127.0.0.1) traffic,
|
|
415
877
|
// so this test cannot verify loopback blocking on Windows.
|
|
416
|
-
it.skipIf(Platform.isWindows)('
|
|
878
|
+
it.skipIf(Platform.isWindows)('prevents unauthorized network access', async () => {
|
|
417
879
|
const { command, args } = Platform.curl(url);
|
|
418
880
|
const sandboxed = await manager.prepareCommand({
|
|
419
881
|
command,
|
|
@@ -422,9 +884,9 @@ describe('SandboxManager Integration', () => {
|
|
|
422
884
|
env: process.env,
|
|
423
885
|
});
|
|
424
886
|
const result = await runCommand(sandboxed);
|
|
425
|
-
|
|
887
|
+
assertResult(result, sandboxed, 'failure');
|
|
426
888
|
});
|
|
427
|
-
it('
|
|
889
|
+
it('allows authorized network access', async () => {
|
|
428
890
|
const { command, args } = Platform.curl(url);
|
|
429
891
|
const sandboxed = await manager.prepareCommand({
|
|
430
892
|
command,
|
|
@@ -434,7 +896,7 @@ describe('SandboxManager Integration', () => {
|
|
|
434
896
|
policy: { networkAccess: true },
|
|
435
897
|
});
|
|
436
898
|
const result = await runCommand(sandboxed);
|
|
437
|
-
|
|
899
|
+
assertResult(result, sandboxed, 'success');
|
|
438
900
|
if (!Platform.isWindows) {
|
|
439
901
|
expect(result.stdout.trim()).toBe('ok');
|
|
440
902
|
}
|