From 28f97e6152aa9b0d70c150a685b28080312671f9 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Mon, 12 Jan 2026 01:31:57 +0000 Subject: [PATCH] refactor(sandbox): normalize main session aliases --- .../sandbox.resolveSandboxContext.test.ts | 46 +++++++++++++++ src/agents/sandbox.ts | 56 +++++++++++++++---- 2 files changed, 92 insertions(+), 10 deletions(-) diff --git a/src/agents/sandbox.resolveSandboxContext.test.ts b/src/agents/sandbox.resolveSandboxContext.test.ts index 79d8003eb..9b555800c 100644 --- a/src/agents/sandbox.resolveSandboxContext.test.ts +++ b/src/agents/sandbox.resolveSandboxContext.test.ts @@ -71,4 +71,50 @@ describe("resolveSandboxContext", () => { vi.doUnmock("node:child_process"); }, 15_000); + + it("treats main session aliases as main in non-main mode", async () => { + vi.resetModules(); + + const spawn = vi.fn(() => { + throw new Error("spawn should not be called"); + }); + vi.doMock("node:child_process", async (importOriginal) => { + const actual = + await importOriginal(); + return { ...actual, spawn }; + }); + + const { ensureSandboxWorkspaceForSession, resolveSandboxContext } = + await import("./sandbox.js"); + + const cfg: ClawdbotConfig = { + session: { mainKey: "work" }, + agents: { + defaults: { + sandbox: { mode: "non-main", scope: "session" }, + }, + list: [{ id: "main" }], + }, + }; + + expect( + await resolveSandboxContext({ + config: cfg, + sessionKey: "main", + workspaceDir: "/tmp/clawdbot-test", + }), + ).toBeNull(); + + expect( + await ensureSandboxWorkspaceForSession({ + config: cfg, + sessionKey: "work", + workspaceDir: "/tmp/clawdbot-test", + }), + ).toBeNull(); + + expect(spawn).not.toHaveBeenCalled(); + + vi.doUnmock("node:child_process"); + }, 15_000); }); diff --git a/src/agents/sandbox.ts b/src/agents/sandbox.ts index 94a2c916a..0c2204f3b 100644 --- a/src/agents/sandbox.ts +++ b/src/agents/sandbox.ts @@ -19,12 +19,9 @@ import { loadConfig, STATE_DIR_CLAWDBOT, } from "../config/config.js"; +import { resolveAgentMainSessionKey } from "../config/sessions.js"; import { PROVIDER_IDS } from "../providers/registry.js"; -import { - buildAgentMainSessionKey, - normalizeAgentId, - normalizeMainKey, -} from "../routing/session-key.js"; +import { normalizeAgentId, normalizeMainKey } from "../routing/session-key.js"; import { defaultRuntime } from "../runtime.js"; import { resolveUserPath } from "../utils.js"; import { @@ -558,12 +555,35 @@ function resolveMainSessionKeyForSandbox(params: { agentId: string; }): string { if (params.cfg?.session?.scope === "global") return "global"; - return buildAgentMainSessionKey({ + return resolveAgentMainSessionKey({ + cfg: params.cfg, agentId: params.agentId, - mainKey: normalizeMainKey(params.cfg?.session?.mainKey), }); } +function resolveComparableSessionKeyForSandbox(params: { + cfg?: ClawdbotConfig; + agentId: string; + sessionKey: string; +}): string { + const trimmed = params.sessionKey.trim(); + if (!trimmed) return trimmed; + + const mainKey = normalizeMainKey(params.cfg?.session?.mainKey); + const agentMainSessionKey = resolveAgentMainSessionKey({ + cfg: params.cfg, + agentId: params.agentId, + }); + const isMainAlias = + trimmed === "main" || + trimmed === mainKey || + trimmed === agentMainSessionKey; + + if (params.cfg?.session?.scope === "global" && isMainAlias) return "global"; + if (isMainAlias) return agentMainSessionKey; + return trimmed; +} + export function resolveSandboxRuntimeStatus(params: { cfg?: ClawdbotConfig; sessionKey?: string; @@ -584,7 +604,11 @@ export function resolveSandboxRuntimeStatus(params: { const sandboxCfg = resolveSandboxConfigForAgent(cfg, agentId); const mainSessionKey = resolveMainSessionKeyForSandbox({ cfg, agentId }); const sandboxed = sessionKey - ? shouldSandboxSession(sandboxCfg, sessionKey, mainSessionKey) + ? shouldSandboxSession( + sandboxCfg, + resolveComparableSessionKeyForSandbox({ cfg, agentId, sessionKey }), + mainSessionKey, + ) : false; return { agentId, @@ -1305,7 +1329,13 @@ export async function resolveSandboxContext(params: { cfg: params.config, agentId, }); - if (!shouldSandboxSession(cfg, rawSessionKey, mainSessionKey)) return null; + const comparableSessionKey = resolveComparableSessionKeyForSandbox({ + cfg: params.config, + agentId, + sessionKey: rawSessionKey, + }); + if (!shouldSandboxSession(cfg, comparableSessionKey, mainSessionKey)) + return null; await maybePruneSandboxes(cfg); @@ -1388,7 +1418,13 @@ export async function ensureSandboxWorkspaceForSession(params: { cfg: params.config, agentId, }); - if (!shouldSandboxSession(cfg, rawSessionKey, mainSessionKey)) return null; + const comparableSessionKey = resolveComparableSessionKeyForSandbox({ + cfg: params.config, + agentId, + sessionKey: rawSessionKey, + }); + if (!shouldSandboxSession(cfg, comparableSessionKey, mainSessionKey)) + return null; const agentWorkspaceDir = resolveUserPath( params.workspaceDir?.trim() || DEFAULT_AGENT_WORKSPACE_DIR,