diff --git a/src/agents/bash-tools.e2e.test.ts b/src/agents/bash-tools.e2e.test.ts index a242436a0..a619e186d 100644 --- a/src/agents/bash-tools.e2e.test.ts +++ b/src/agents/bash-tools.e2e.test.ts @@ -3,7 +3,7 @@ import { afterEach, beforeEach, describe, expect, it } from "vitest"; import { peekSystemEvents, resetSystemEventsForTest } from "../infra/system-events.js"; import { captureEnv } from "../test-utils/env.js"; import { getFinishedSession, resetProcessRegistryForTests } from "./bash-process-registry.js"; -import { createExecTool, createProcessTool, execTool, processTool } from "./bash-tools.js"; +import { createExecTool, createProcessTool } from "./bash-tools.js"; import { buildDockerExecArgs } from "./bash-tools.shared.js"; import { resolveShellFromPath, sanitizeBinaryOutput } from "./shell-utils.js"; @@ -16,6 +16,12 @@ const shortDelayCmd = isWin ? "Start-Sleep -Milliseconds 20" : "sleep 0.02"; const yieldDelayCmd = isWin ? "Start-Sleep -Milliseconds 90" : "sleep 0.09"; const longDelayCmd = isWin ? "Start-Sleep -Milliseconds 700" : "sleep 0.7"; const POLL_INTERVAL_MS = 15; +const TEST_EXEC_DEFAULTS = { security: "full" as const, ask: "off" as const }; +const createTestExecTool = ( + defaults?: Parameters[0], +): ReturnType => createExecTool({ ...TEST_EXEC_DEFAULTS, ...defaults }); +const execTool = createTestExecTool(); +const processTool = createProcessTool(); // Both PowerShell and bash use ; for command separation const joinCommands = (commands: string[]) => commands.join("; "); const echoAfterDelay = (message: string) => joinCommands([shortDelayCmd, `echo ${message}`]); @@ -144,7 +150,7 @@ describe("exec tool backgrounding", () => { }); it("uses default timeout when timeout is omitted", async () => { - const customBash = createExecTool({ timeoutSec: 0.1, backgroundMs: 10 }); + const customBash = createTestExecTool({ timeoutSec: 0.1, backgroundMs: 10 }); const customProcess = createProcessTool(); const result = await customBash.execute("call1", { @@ -168,7 +174,7 @@ describe("exec tool backgrounding", () => { }); it("rejects elevated requests when not allowed", async () => { - const customBash = createExecTool({ + const customBash = createTestExecTool({ elevated: { enabled: true, allowed: false, defaultLevel: "off" }, messageProvider: "telegram", sessionKey: "agent:main:main", @@ -183,7 +189,7 @@ describe("exec tool backgrounding", () => { }); it("does not default to elevated when not allowed", async () => { - const customBash = createExecTool({ + const customBash = createTestExecTool({ elevated: { enabled: true, allowed: false, defaultLevel: "on" }, backgroundMs: 1000, timeoutSec: 5, @@ -270,9 +276,9 @@ describe("exec tool backgrounding", () => { }); it("scopes process sessions by scopeKey", async () => { - const bashA = createExecTool({ backgroundMs: 10, scopeKey: "agent:alpha" }); + const bashA = createTestExecTool({ backgroundMs: 10, scopeKey: "agent:alpha" }); const processA = createProcessTool({ scopeKey: "agent:alpha" }); - const bashB = createExecTool({ backgroundMs: 10, scopeKey: "agent:beta" }); + const bashB = createTestExecTool({ backgroundMs: 10, scopeKey: "agent:beta" }); const processB = createProcessTool({ scopeKey: "agent:beta" }); const resultA = await bashA.execute("call1", { @@ -332,7 +338,7 @@ describe("exec exit codes", () => { describe("exec notifyOnExit", () => { it("enqueues a system event when a backgrounded exec exits", async () => { - const tool = createExecTool({ + const tool = createTestExecTool({ allowBackground: true, backgroundMs: 0, notifyOnExit: true, @@ -372,7 +378,7 @@ describe("exec notifyOnExit", () => { }); it("skips no-op completion events when command succeeds without output", async () => { - const tool = createExecTool({ + const tool = createTestExecTool({ allowBackground: true, backgroundMs: 0, notifyOnExit: true, @@ -392,7 +398,7 @@ describe("exec notifyOnExit", () => { }); it("can re-enable no-op completion events via notifyOnExitEmptySuccess", async () => { - const tool = createExecTool({ + const tool = createTestExecTool({ allowBackground: true, backgroundMs: 0, notifyOnExit: true, @@ -434,13 +440,15 @@ describe("exec PATH handling", () => { const prepend = isWin ? ["C:\\custom\\bin", "C:\\oss\\bin"] : ["/custom/bin", "/opt/oss/bin"]; process.env.PATH = basePath; - const tool = createExecTool({ pathPrepend: prepend }); + const tool = createTestExecTool({ pathPrepend: prepend }); const result = await tool.execute("call1", { command: isWin ? "Write-Output $env:PATH" : "echo $PATH", }); const text = normalizeText(result.content.find((c) => c.type === "text")?.text); - expect(text).toBe([...prepend, basePath].join(path.delimiter)); + const entries = text.split(path.delimiter); + expect(entries.slice(0, prepend.length)).toEqual(prepend); + expect(entries).toContain(basePath); }); }); diff --git a/src/agents/bash-tools.exec.background-abort.e2e.test.ts b/src/agents/bash-tools.exec.background-abort.e2e.test.ts index 6a5af48ad..2e1e2fb8b 100644 --- a/src/agents/bash-tools.exec.background-abort.e2e.test.ts +++ b/src/agents/bash-tools.exec.background-abort.e2e.test.ts @@ -13,6 +13,14 @@ const ABORT_WAIT_TIMEOUT_MS = process.platform === "win32" ? 1_500 : 450; const POLL_INTERVAL_MS = 15; const FINISHED_WAIT_TIMEOUT_MS = process.platform === "win32" ? 8_000 : 1_200; const BACKGROUND_TIMEOUT_SEC = process.platform === "win32" ? 0.2 : 0.12; +const TEST_EXEC_DEFAULTS = { + security: "full" as const, + ask: "off" as const, +}; + +const createTestExecTool = ( + defaults?: Parameters[0], +): ReturnType => createExecTool({ ...TEST_EXEC_DEFAULTS, ...defaults }); afterEach(() => { resetProcessRegistryForTests(); @@ -106,7 +114,7 @@ async function expectBackgroundSessionTimesOut(params: { } test("background exec is not killed when tool signal aborts", async () => { - const tool = createExecTool({ allowBackground: true, backgroundMs: 0 }); + const tool = createTestExecTool({ allowBackground: true, backgroundMs: 0 }); await expectBackgroundSessionSurvivesAbort({ tool, executeParams: { command: BACKGROUND_HOLD_CMD, background: true }, @@ -114,7 +122,7 @@ test("background exec is not killed when tool signal aborts", async () => { }); test("pty background exec is not killed when tool signal aborts", async () => { - const tool = createExecTool({ allowBackground: true, backgroundMs: 0 }); + const tool = createTestExecTool({ allowBackground: true, backgroundMs: 0 }); await expectBackgroundSessionSurvivesAbort({ tool, executeParams: { command: BACKGROUND_HOLD_CMD, background: true, pty: true }, @@ -122,7 +130,7 @@ test("pty background exec is not killed when tool signal aborts", async () => { }); test("background exec still times out after tool signal abort", async () => { - const tool = createExecTool({ allowBackground: true, backgroundMs: 0 }); + const tool = createTestExecTool({ allowBackground: true, backgroundMs: 0 }); await expectBackgroundSessionTimesOut({ tool, executeParams: { @@ -135,7 +143,7 @@ test("background exec still times out after tool signal abort", async () => { }); test("yielded background exec is not killed when tool signal aborts", async () => { - const tool = createExecTool({ allowBackground: true, backgroundMs: 10 }); + const tool = createTestExecTool({ allowBackground: true, backgroundMs: 10 }); await expectBackgroundSessionSurvivesAbort({ tool, executeParams: { command: BACKGROUND_HOLD_CMD, yieldMs: 5 }, @@ -143,7 +151,7 @@ test("yielded background exec is not killed when tool signal aborts", async () = }); test("yielded background exec still times out", async () => { - const tool = createExecTool({ allowBackground: true, backgroundMs: 10 }); + const tool = createTestExecTool({ allowBackground: true, backgroundMs: 10 }); await expectBackgroundSessionTimesOut({ tool, executeParams: { diff --git a/src/agents/bash-tools.exec.pty-fallback.e2e.test.ts b/src/agents/bash-tools.exec.pty-fallback.e2e.test.ts index 7a7f53a53..62e68653a 100644 --- a/src/agents/bash-tools.exec.pty-fallback.e2e.test.ts +++ b/src/agents/bash-tools.exec.pty-fallback.e2e.test.ts @@ -16,7 +16,7 @@ afterEach(() => { }); test("exec falls back when PTY spawn fails", async () => { - const tool = createExecTool({ allowBackground: false }); + const tool = createExecTool({ allowBackground: false, security: "full", ask: "off" }); const result = await tool.execute("toolcall", { command: "printf ok", pty: true, diff --git a/src/agents/bash-tools.exec.pty.e2e.test.ts b/src/agents/bash-tools.exec.pty.e2e.test.ts index 9acb22ea4..10de0bfdb 100644 --- a/src/agents/bash-tools.exec.pty.e2e.test.ts +++ b/src/agents/bash-tools.exec.pty.e2e.test.ts @@ -7,7 +7,7 @@ afterEach(() => { }); test("exec supports pty output", async () => { - const tool = createExecTool({ allowBackground: false }); + const tool = createExecTool({ allowBackground: false, security: "full", ask: "off" }); const result = await tool.execute("toolcall", { command: 'node -e "process.stdout.write(String.fromCharCode(111,107))"', pty: true, diff --git a/src/agents/bash-tools.process.send-keys.e2e.test.ts b/src/agents/bash-tools.process.send-keys.e2e.test.ts index a2e894722..5c40d363e 100644 --- a/src/agents/bash-tools.process.send-keys.e2e.test.ts +++ b/src/agents/bash-tools.process.send-keys.e2e.test.ts @@ -8,7 +8,7 @@ afterEach(() => { }); async function startPtySession(command: string) { - const execTool = createExecTool(); + const execTool = createExecTool({ security: "full", ask: "off" }); const processTool = createProcessTool(); const result = await execTool.execute("toolcall", { command,