From 09f12c2351ba20408b1b3030dbe32fdfbd6599b5 Mon Sep 17 00:00:00 2001 From: marius-kilocode Date: Thu, 9 Jul 2026 18:00:31 +0200 Subject: [PATCH] fix(vscode): gate sandbox controls on setting --- .changeset/sandbox-controls-setting.md | 5 +++++ .../tests/unit/new-worktree-dialog-sandbox.test.ts | 5 ++++- .../tests/unit/prompt-input-connection-guard.test.ts | 6 +++--- packages/kilo-vscode/tests/unit/sandbox-bootstrap.test.ts | 4 +++- .../webview-ui/agent-manager/NewWorktreeDialog.tsx | 4 ++-- .../webview-ui/src/components/chat/PromptInput.tsx | 5 ++++- 6 files changed, 21 insertions(+), 8 deletions(-) create mode 100644 .changeset/sandbox-controls-setting.md diff --git a/.changeset/sandbox-controls-setting.md b/.changeset/sandbox-controls-setting.md new file mode 100644 index 0000000000..9d35cb5047 --- /dev/null +++ b/.changeset/sandbox-controls-setting.md @@ -0,0 +1,5 @@ +--- +"kilo-code": patch +--- + +Show sandbox controls only after sandboxing is enabled in settings. diff --git a/packages/kilo-vscode/tests/unit/new-worktree-dialog-sandbox.test.ts b/packages/kilo-vscode/tests/unit/new-worktree-dialog-sandbox.test.ts index 30ecca6c7d..dc00f65473 100644 --- a/packages/kilo-vscode/tests/unit/new-worktree-dialog-sandbox.test.ts +++ b/packages/kilo-vscode/tests/unit/new-worktree-dialog-sandbox.test.ts @@ -18,7 +18,10 @@ describe("NewWorktreeDialog sandbox toggle", () => { 'vscode.postMessage({ type: "setSandboxDefault", enabled: next, requestID: sandboxRequestID })', ) expect(src).toContain("sandbox: sandboxVisible() ? sandboxOverride() : undefined") - expect(src).toContain("const sandboxVisible = () => features().sandboxControls") + expect(src).toContain("const { config, globalConfig, features } = useConfig()") + expect(src).toContain( + "const sandboxVisible = () => features().sandboxControls && globalConfig().sandbox?.enabled === true", + ) expect(provider).toContain("await this.fetchAndSendSandboxDefault(message.contextDirectory, message.requestID)") expect(src).not.toContain("createSignal(config().sandbox?.enabled === true)") expect(src).not.toContain("visible as isSandboxVisible") diff --git a/packages/kilo-vscode/tests/unit/prompt-input-connection-guard.test.ts b/packages/kilo-vscode/tests/unit/prompt-input-connection-guard.test.ts index c19fcf9621..96e8a2f198 100644 --- a/packages/kilo-vscode/tests/unit/prompt-input-connection-guard.test.ts +++ b/packages/kilo-vscode/tests/unit/prompt-input-connection-guard.test.ts @@ -81,11 +81,11 @@ describe("PromptInput sandbox toggle", () => { expect(src).not.toContain("setSandboxTarget") }) - it("keeps persisted sandbox state visible independently of the configured default", () => { + it("shows sandbox controls only when the global sandbox setting is enabled", () => { expect(src).toContain( - 'const sandboxVisible = () => features().sandboxControls && !session.currentSessionID()?.startsWith("cloud:")', + 'globalConfig().sandbox?.enabled === true &&\n !session.currentSessionID()?.startsWith("cloud:")', ) - expect(src).not.toContain("config().sandbox?.enabled === true") + expect(src).toContain("features().sandboxControls &&") expect(src).toContain("") expect(src).toContain("{ action: toggleSandbox, enabled: () => sandboxVisible() && !sandboxDisabled() }") expect(src).toContain('if (!sandboxVisible()) hidden.add("sandbox")') diff --git a/packages/kilo-vscode/tests/unit/sandbox-bootstrap.test.ts b/packages/kilo-vscode/tests/unit/sandbox-bootstrap.test.ts index 2815cf0b6c..d4d9072b66 100644 --- a/packages/kilo-vscode/tests/unit/sandbox-bootstrap.test.ts +++ b/packages/kilo-vscode/tests/unit/sandbox-bootstrap.test.ts @@ -124,7 +124,9 @@ describe("Agent Manager sandbox startup", () => { }) test("uses the persisted sandbox default for UI and only sends explicit overrides", () => { - expect(dialog).toContain("const sandboxVisible = () => features().sandboxControls") + expect(dialog).toContain( + "const sandboxVisible = () => features().sandboxControls && globalConfig().sandbox?.enabled === true", + ) expect(dialog).toContain('vscode.postMessage({ type: "requestSandboxDefault", requestID: sandboxRequestID })') expect(dialog).toContain( 'vscode.postMessage({ type: "setSandboxDefault", enabled: next, requestID: sandboxRequestID })', diff --git a/packages/kilo-vscode/webview-ui/agent-manager/NewWorktreeDialog.tsx b/packages/kilo-vscode/webview-ui/agent-manager/NewWorktreeDialog.tsx index 5e54247108..d87e8ab3bd 100644 --- a/packages/kilo-vscode/webview-ui/agent-manager/NewWorktreeDialog.tsx +++ b/packages/kilo-vscode/webview-ui/agent-manager/NewWorktreeDialog.tsx @@ -72,7 +72,7 @@ export const NewWorktreeDialog: Component<{ onClose: () => void; defaultBaseBran const server = useServer() const session = useSession() const provider = useProvider() - const { config, features } = useConfig() + const { config, globalConfig, features } = useConfig() const metrics = tracker(vscode) const track = (button: string, properties?: Record) => metrics.track(button, "configure_worktree_dialog", properties) @@ -110,7 +110,7 @@ export const NewWorktreeDialog: Component<{ onClose: () => void; defaultBaseBran const [sandboxReason, setSandboxReason] = createSignal() const [sandboxRevision, setSandboxRevision] = createSignal(-1) const sandboxRequestID = crypto.randomUUID() - const sandboxVisible = () => features().sandboxControls + const sandboxVisible = () => features().sandboxControls && globalConfig().sandbox?.enabled === true const speech = useSpeechToText(vscode, server, { t }) const canUseSpeech = () => canUseSpeechToText(config(), provider.authStates()) const speechModel = () => selectedSpeechToTextModel(config()) diff --git a/packages/kilo-vscode/webview-ui/src/components/chat/PromptInput.tsx b/packages/kilo-vscode/webview-ui/src/components/chat/PromptInput.tsx index 83fea9348f..b235cc8f8b 100644 --- a/packages/kilo-vscode/webview-ui/src/components/chat/PromptInput.tsx +++ b/packages/kilo-vscode/webview-ui/src/components/chat/PromptInput.tsx @@ -174,7 +174,10 @@ export const PromptInput: Component = (props) => { const id = session.currentSessionID() return id?.startsWith("cloud:") ? undefined : id } - const sandboxVisible = () => features().sandboxControls && !session.currentSessionID()?.startsWith("cloud:") + const sandboxVisible = () => + features().sandboxControls && + globalConfig().sandbox?.enabled === true && + !session.currentSessionID()?.startsWith("cloud:") const sandbox = () => { const id = sandboxID() return id ? sandboxes()[id] : undefined