diff --git a/package.json b/package.json index 45f11935004..69f10411241 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "openclaw", - "version": "2026.2.22-1", + "version": "2026.2.22-2", "description": "Multi-channel AI gateway with extensible messaging integrations", "keywords": [], "homepage": "https://github.com/openclaw/openclaw#readme", diff --git a/src/agents/pi-tools-agent-config.test.ts b/src/agents/pi-tools-agent-config.test.ts index a87fdf884cc..96cac05019b 100644 --- a/src/agents/pi-tools-agent-config.test.ts +++ b/src/agents/pi-tools-agent-config.test.ts @@ -604,6 +604,31 @@ describe("Agent-specific tool filtering", () => { expect(resultDetails?.status).toBe("completed"); }); + it("keeps sandbox as the implicit exec host default without forcing gateway approvals", async () => { + const tools = createOpenClawCodingTools({ + config: {}, + sessionKey: "agent:main:main", + workspaceDir: "/tmp/test-main-implicit-sandbox", + agentDir: "/tmp/agent-main-implicit-sandbox", + }); + const execTool = tools.find((tool) => tool.name === "exec"); + expect(execTool).toBeDefined(); + + const result = await execTool!.execute("call-implicit-sandbox-default", { + command: "echo done", + yieldMs: 10, + }); + const details = result?.details as { status?: string } | undefined; + expect(details?.status).toBe("completed"); + + await expect( + execTool!.execute("call-implicit-sandbox-gateway", { + command: "echo done", + host: "gateway", + }), + ).rejects.toThrow("exec host not allowed"); + }); + it("fails closed when exec host=sandbox is requested without sandbox runtime", async () => { const tools = createOpenClawCodingTools({ config: {}, @@ -618,7 +643,7 @@ describe("Agent-specific tool filtering", () => { command: "echo done", host: "sandbox", }), - ).rejects.toThrow("exec host not allowed"); + ).rejects.toThrow("exec host=sandbox is configured"); }); it("should apply agent-specific exec host defaults over global defaults", async () => { diff --git a/src/agents/pi-tools.ts b/src/agents/pi-tools.ts index 361ca903fc6..f40226c960c 100644 --- a/src/agents/pi-tools.ts +++ b/src/agents/pi-tools.ts @@ -364,13 +364,9 @@ export function createOpenClawCodingTools(options?: { return [tool]; }); const { cleanupMs: cleanupMsOverride, ...execDefaults } = options?.exec ?? {}; - // Fail-closed baseline: when no sandbox context exists, default exec to gateway - // so we never silently treat "sandbox" as host execution. - const resolvedExecHost = - options?.exec?.host ?? execConfig.host ?? (sandbox ? "sandbox" : "gateway"); const execTool = createExecTool({ ...execDefaults, - host: resolvedExecHost, + host: options?.exec?.host ?? execConfig.host, security: options?.exec?.security ?? execConfig.security, ask: options?.exec?.ask ?? execConfig.ask, node: options?.exec?.node ?? execConfig.node, diff --git a/src/config/types.tools.ts b/src/config/types.tools.ts index 98a59cdfd8e..164eacc6ae0 100644 --- a/src/config/types.tools.ts +++ b/src/config/types.tools.ts @@ -215,7 +215,7 @@ export function parseToolsBySenderTypedKey( export type GroupToolPolicyBySenderConfig = Record; export type ExecToolConfig = { - /** Exec host routing (default: sandbox with sandbox runtime, otherwise gateway). */ + /** Exec host routing (default: sandbox). */ host?: "sandbox" | "gateway" | "node"; /** Exec security mode (default: deny). */ security?: "deny" | "allowlist" | "full";