diff --git a/src/agents/bash-tools.exec.background-abort.test.ts b/src/agents/bash-tools.exec.background-abort.test.ts new file mode 100644 index 000000000..facd98557 --- /dev/null +++ b/src/agents/bash-tools.exec.background-abort.test.ts @@ -0,0 +1,38 @@ +import { afterEach, expect, test } from "vitest"; + +import { createExecTool } from "./bash-tools.exec"; +import { getFinishedSession, getSession, resetProcessRegistryForTests } from "./bash-process-registry"; +import { killProcessTree } from "./shell-utils"; + +afterEach(() => { + resetProcessRegistryForTests(); +}); + +test("background exec is not killed when tool signal aborts", async () => { + const tool = createExecTool({ allowBackground: true, backgroundMs: 0 }); + const abortController = new AbortController(); + + const result = await tool.execute( + "toolcall", + { command: "node -e \"setTimeout(() => {}, 5000)\"", background: true }, + abortController.signal, + ); + + expect(result.details.status).toBe("running"); + const sessionId = (result.details as { sessionId: string }).sessionId; + + abortController.abort(); + + await new Promise((resolve) => setTimeout(resolve, 150)); + + const running = getSession(sessionId); + const finished = getFinishedSession(sessionId); + + try { + expect(finished).toBeUndefined(); + expect(running?.exited).toBe(false); + } finally { + const pid = running?.pid; + if (pid) killProcessTree(pid); + } +}); diff --git a/src/agents/bash-tools.exec.ts b/src/agents/bash-tools.exec.ts index efa57a4ab..fb088557c 100644 --- a/src/agents/bash-tools.exec.ts +++ b/src/agents/bash-tools.exec.ts @@ -263,6 +263,7 @@ export function createExecTool( }; const onAbort = () => { + if (yielded || session.backgrounded) return; killSession(session); };