fix(coding-agent): scoped python kernel cleanup to sessions
scoped retained-kernel ownership to agent sessions and cleaned it up on session disposal cleaned up warmed python owners on session startup failure and rejected new direct and tool-based python starts during disposal, including async hook, preflight, and warmup races
This commit is contained in:
@@ -294,13 +294,49 @@ describe("PythonKernel (external gateway)", () => {
|
||||
expect(chunks.join("")).toContain("result");
|
||||
expect(displays).toEqual([{ type: "json", data: { answer: 42 } }]);
|
||||
|
||||
await kernel.shutdown();
|
||||
const shutdown = await kernel.shutdown();
|
||||
expect(shutdown).toEqual({ confirmed: true });
|
||||
expect(fetchMock).toHaveBeenCalledWith("http://gateway.test/api/kernels/kernel-1", {
|
||||
method: "DELETE",
|
||||
headers: {},
|
||||
});
|
||||
});
|
||||
|
||||
it("returns an unconfirmed shutdown result when kernel deletion is not acknowledged", async () => {
|
||||
let deleteAttempts = 0;
|
||||
const fetchMock = vi.fn(async (url: string, init?: RequestInit) => {
|
||||
if (url.endsWith("/api/kernels") && init?.method === "POST") {
|
||||
return new Response(JSON.stringify({ id: "kernel-delete-failure" }), { status: 201 });
|
||||
}
|
||||
if (url.includes("/api/kernels/") && init?.method === "DELETE") {
|
||||
deleteAttempts += 1;
|
||||
if (deleteAttempts === 1) {
|
||||
return new Response("delete failed", { status: 500, statusText: "Server Error" });
|
||||
}
|
||||
return new Response("already gone", { status: 404, statusText: "Not Found" });
|
||||
}
|
||||
return new Response("", { status: 200 });
|
||||
});
|
||||
using _hook = hookFetch((input, init) => fetchMock(String(input), init));
|
||||
|
||||
const kernelPromise = PythonKernel.start({ cwd: "/" });
|
||||
await Bun.sleep(10);
|
||||
const ws = FakeWebSocket.lastInstance;
|
||||
if (!ws) throw new Error("WebSocket not initialized");
|
||||
ws.setSendHandler(data => {
|
||||
const msg = typeof data === "string" ? (JSON.parse(data) as JupyterMessage) : decodeMessage(data);
|
||||
sendOkExecution(ws, msg.header.msg_id);
|
||||
});
|
||||
|
||||
const kernel = await kernelPromise;
|
||||
await expect(kernel.shutdown()).resolves.toEqual({ confirmed: false });
|
||||
expect(deleteAttempts).toBe(1);
|
||||
await expect(kernel.shutdown()).resolves.toEqual({ confirmed: true });
|
||||
expect(deleteAttempts).toBe(2);
|
||||
await expect(kernel.shutdown()).resolves.toEqual({ confirmed: true });
|
||||
expect(deleteAttempts).toBe(2);
|
||||
});
|
||||
|
||||
it("initializes the IPython prelude", async () => {
|
||||
const fetchMock = vi.fn(async (url: string, init?: RequestInit) => {
|
||||
if (url.endsWith("/api/kernels") && init?.method === "POST") {
|
||||
|
||||
Reference in New Issue
Block a user