refactor(agent): dedupe harness and command workflows

This commit is contained in:
Peter Steinberger
2026-02-16 14:52:09 +00:00
parent 04892ee230
commit f717a13039
204 changed files with 7366 additions and 11540 deletions

View File

@@ -12,166 +12,121 @@ afterEach(() => {
resetProcessRegistryForTests();
});
test("background exec is not killed when tool signal aborts", async () => {
const tool = createExecTool({ allowBackground: true, backgroundMs: 0 });
const abortController = new AbortController();
async function waitForFinishedSession(sessionId: string) {
let finished = getFinishedSession(sessionId);
const deadline = Date.now() + (process.platform === "win32" ? 10_000 : 2_000);
while (!finished && Date.now() < deadline) {
await sleep(20);
finished = getFinishedSession(sessionId);
}
return finished;
}
const result = await tool.execute(
function cleanupRunningSession(sessionId: string) {
const running = getSession(sessionId);
const pid = running?.pid;
if (pid) {
killProcessTree(pid);
}
return running;
}
async function expectBackgroundSessionSurvivesAbort(params: {
tool: ReturnType<typeof createExecTool>;
executeParams: Record<string, unknown>;
}) {
const abortController = new AbortController();
const result = await params.tool.execute(
"toolcall",
{ command: 'node -e "setTimeout(() => {}, 5000)"', background: true },
params.executeParams,
abortController.signal,
);
expect(result.details.status).toBe("running");
const sessionId = (result.details as { sessionId: string }).sessionId;
abortController.abort();
await sleep(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);
}
cleanupRunningSession(sessionId);
}
}
async function expectBackgroundSessionTimesOut(params: {
tool: ReturnType<typeof createExecTool>;
executeParams: Record<string, unknown>;
signal?: AbortSignal;
abortAfterStart?: boolean;
}) {
const abortController = new AbortController();
const signal = params.signal ?? abortController.signal;
const result = await params.tool.execute("toolcall", params.executeParams, signal);
expect(result.details.status).toBe("running");
const sessionId = (result.details as { sessionId: string }).sessionId;
if (params.abortAfterStart) {
abortController.abort();
}
const finished = await waitForFinishedSession(sessionId);
try {
expect(finished).toBeTruthy();
expect(finished?.status).toBe("failed");
} finally {
cleanupRunningSession(sessionId);
}
}
test("background exec is not killed when tool signal aborts", async () => {
const tool = createExecTool({ allowBackground: true, backgroundMs: 0 });
await expectBackgroundSessionSurvivesAbort({
tool,
executeParams: { command: 'node -e "setTimeout(() => {}, 5000)"', background: true },
});
});
test("pty 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, pty: true },
abortController.signal,
);
expect(result.details.status).toBe("running");
const sessionId = (result.details as { sessionId: string }).sessionId;
abortController.abort();
await sleep(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);
}
}
await expectBackgroundSessionSurvivesAbort({
tool,
executeParams: { command: 'node -e "setTimeout(() => {}, 5000)"', background: true, pty: true },
});
});
test("background exec still times out after tool signal abort", async () => {
const tool = createExecTool({ allowBackground: true, backgroundMs: 0 });
const abortController = new AbortController();
const result = await tool.execute(
"toolcall",
{
await expectBackgroundSessionTimesOut({
tool,
executeParams: {
command: 'node -e "setTimeout(() => {}, 5000)"',
background: true,
timeout: 0.2,
},
abortController.signal,
);
expect(result.details.status).toBe("running");
const sessionId = (result.details as { sessionId: string }).sessionId;
abortController.abort();
let finished = getFinishedSession(sessionId);
const deadline = Date.now() + (process.platform === "win32" ? 10_000 : 2_000);
while (!finished && Date.now() < deadline) {
await sleep(20);
finished = getFinishedSession(sessionId);
}
const running = getSession(sessionId);
try {
expect(finished).toBeTruthy();
expect(finished?.status).toBe("failed");
} finally {
const pid = running?.pid;
if (pid) {
killProcessTree(pid);
}
}
abortAfterStart: true,
});
});
test("yielded background exec is not killed when tool signal aborts", async () => {
const tool = createExecTool({ allowBackground: true, backgroundMs: 10 });
const abortController = new AbortController();
const result = await tool.execute(
"toolcall",
{ command: 'node -e "setTimeout(() => {}, 5000)"', yieldMs: 5 },
abortController.signal,
);
expect(result.details.status).toBe("running");
const sessionId = (result.details as { sessionId: string }).sessionId;
abortController.abort();
await sleep(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);
}
}
await expectBackgroundSessionSurvivesAbort({
tool,
executeParams: { command: 'node -e "setTimeout(() => {}, 5000)"', yieldMs: 5 },
});
});
test("yielded background exec still times out", async () => {
const tool = createExecTool({ allowBackground: true, backgroundMs: 10 });
const result = await tool.execute("toolcall", {
command: 'node -e "setTimeout(() => {}, 5000)"',
yieldMs: 5,
timeout: 0.2,
await expectBackgroundSessionTimesOut({
tool,
executeParams: {
command: 'node -e "setTimeout(() => {}, 5000)"',
yieldMs: 5,
timeout: 0.2,
},
});
expect(result.details.status).toBe("running");
const sessionId = (result.details as { sessionId: string }).sessionId;
let finished = getFinishedSession(sessionId);
const deadline = Date.now() + (process.platform === "win32" ? 10_000 : 2_000);
while (!finished && Date.now() < deadline) {
await sleep(20);
finished = getFinishedSession(sessionId);
}
const running = getSession(sessionId);
try {
expect(finished).toBeTruthy();
expect(finished?.status).toBe("failed");
} finally {
const pid = running?.pid;
if (pid) {
killProcessTree(pid);
}
}
});