fix: release finalized Pi runtimes
This commit is contained in:
@@ -111,13 +111,21 @@ export function sessionRoutes(
|
||||
}
|
||||
}
|
||||
d.hub.publish(id, e.type, e);
|
||||
if (
|
||||
e.type === "system_event"
|
||||
&& e.event === "agent_end"
|
||||
&& d.mgr.get(id) !== rt
|
||||
&& boundRuntimes.get(id) === rt
|
||||
) {
|
||||
boundRuntimes.delete(id);
|
||||
if (e.type === "system_event" && e.event === "agent_end") {
|
||||
if (d.mgr.get(id) !== rt && boundRuntimes.get(id) === rt) {
|
||||
boundRuntimes.delete(id);
|
||||
} else if (typeof runner.sessionShow === "function") {
|
||||
void withSessionLifecycle(id, async () => {
|
||||
if (d.mgr.get(id) !== rt || boundRuntimes.get(id) !== rt) return;
|
||||
const manifest = await runner.sessionShow(id, workspace);
|
||||
if (manifest?.status !== "finalized") return;
|
||||
if (d.mgr.teardownIfCurrent(id, rt) && boundRuntimes.get(id) === rt) {
|
||||
boundRuntimes.delete(id);
|
||||
}
|
||||
}).catch((error: unknown) => {
|
||||
console.error(`[session:${id}] terminal runtime cleanup failed:`, error);
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
|
||||
@@ -1304,6 +1304,47 @@ test("an unexpectedly exited runtime publishes its terminal sequence then releas
|
||||
]);
|
||||
});
|
||||
|
||||
test("agent_end releases the Pi runtime after the session was finalized", async () => {
|
||||
let listener: ((event: any) => void) | undefined;
|
||||
const bridge = {
|
||||
onClientEvent: (next: (event: any) => void) => { listener = next; },
|
||||
emitClientEvent: (event: any) => listener?.(event),
|
||||
};
|
||||
const runtime = { bridge };
|
||||
let current: any;
|
||||
let teardownCalls = 0;
|
||||
const app = buildApp(loadConfig({ THT_HARNESS_DIR: "../harness" }), {
|
||||
mgr: {
|
||||
get: () => current,
|
||||
createFor: () => { current = runtime; return runtime; },
|
||||
configure: async () => {},
|
||||
start: () => {},
|
||||
teardownIfCurrent: (_id: string, expected: any) => {
|
||||
if (current !== expected) return false;
|
||||
teardownCalls += 1;
|
||||
current = undefined;
|
||||
return true;
|
||||
},
|
||||
} as any,
|
||||
thtRunner: {
|
||||
sessionNew: async () => ({ id: "s1" }),
|
||||
sessionShow: async () => ({ status: "finalized" }),
|
||||
searchPack: async () => {},
|
||||
failSession: async () => {},
|
||||
} as any,
|
||||
readiness: { ensure: async () => ({ ok: true }) } as any,
|
||||
getSettings: () => ({ workspace: "local" }) as any,
|
||||
});
|
||||
await app.inject({ method: "POST", url: "/sessions", payload: { question: "q" } });
|
||||
await new Promise((resolve) => setImmediate(resolve));
|
||||
|
||||
bridge.emitClientEvent({ type: "system_event", event: "agent_end" });
|
||||
await new Promise((resolve) => setImmediate(resolve));
|
||||
|
||||
expect(teardownCalls).toBe(1);
|
||||
expect(current).toBeUndefined();
|
||||
});
|
||||
|
||||
test("POST resume tears down a created runtime when bridge binding fails", async () => {
|
||||
const delivered: string[] = [];
|
||||
const actualHub = new SseHub();
|
||||
|
||||
Reference in New Issue
Block a user