fix: handle codex app-server interrupt shutdown

This commit is contained in:
Peter Steinberger
2026-04-11 15:20:52 +01:00
parent b489c8f55b
commit 545490c592
2 changed files with 58 additions and 4 deletions

View File

@@ -119,6 +119,56 @@ describe("runCodexAppServerAttempt", () => {
);
});
it("does not leak unhandled rejections when shutdown closes before interrupt", async () => {
const unhandledRejections: unknown[] = [];
const onUnhandledRejection = (reason: unknown) => {
unhandledRejections.push(reason);
};
process.on("unhandledRejection", onUnhandledRejection);
try {
const requests: Array<{ method: string; params: unknown }> = [];
const request = vi.fn(async (method: string, params?: unknown) => {
requests.push({ method, params });
if (method === "thread/start") {
return { thread: { id: "thread-1" }, model: "gpt-5.4-codex", modelProvider: "openai" };
}
if (method === "turn/start") {
return { turn: { id: "turn-1", status: "inProgress" } };
}
if (method === "turn/interrupt") {
throw new Error("codex app-server client is closed");
}
return {};
});
__testing.setCodexAppServerClientFactoryForTests(
async () =>
({
request,
addNotificationHandler: () => () => undefined,
addRequestHandler: () => () => undefined,
}) as never,
);
const abortController = new AbortController();
const params = createParams(
path.join(tempDir, "session.jsonl"),
path.join(tempDir, "workspace"),
);
params.abortSignal = abortController.signal;
const run = runCodexAppServerAttempt(params);
await vi.waitFor(() =>
expect(requests.some((entry) => entry.method === "turn/start")).toBe(true),
);
abortController.abort("shutdown");
await expect(run).resolves.toMatchObject({ aborted: true });
await new Promise((resolve) => setImmediate(resolve));
expect(unhandledRejections).toEqual([]);
} finally {
process.off("unhandledRejection", onUnhandledRejection);
}
});
it("forwards image attachments to the app-server turn input", async () => {
const requests: Array<{ method: string; params: unknown }> = [];
let notify: (notification: CodexServerNotification) => Promise<void> = async () => undefined;

View File

@@ -227,10 +227,14 @@ export async function runCodexAppServerAttempt(
);
const abortListener = () => {
void client.request("turn/interrupt", {
threadId: thread.threadId,
turnId: activeTurnId,
});
void client
.request("turn/interrupt", {
threadId: thread.threadId,
turnId: activeTurnId,
})
.catch((error: unknown) => {
embeddedAgentLog.debug("codex app-server turn interrupt failed during abort", { error });
});
resolveCompletion?.();
};
runAbortController.signal.addEventListener("abort", abortListener, { once: true });