fix: dispose failed planning startup agents
This commit is contained in:
@@ -422,6 +422,41 @@ describe("Planning Mode Routes", () => {
|
||||
expect(res.body.sessionId).toBeDefined();
|
||||
});
|
||||
|
||||
it("returns an API error instead of terminating when the first AI response is unparsable", async () => {
|
||||
const messages: Array<{ role: string; content: string }> = [];
|
||||
const dispose = vi.fn();
|
||||
__setCreateFnAgent(async () => ({
|
||||
session: {
|
||||
state: { messages },
|
||||
prompt: vi.fn(async (msg: string) => {
|
||||
messages.push({ role: "user", content: msg });
|
||||
messages.push({ role: "assistant", content: "" });
|
||||
}),
|
||||
dispose,
|
||||
},
|
||||
}));
|
||||
const exitSpy = vi.spyOn(process, "exit").mockImplementation((() => {
|
||||
throw new Error("process.exit should not be called");
|
||||
}) as never);
|
||||
|
||||
try {
|
||||
const res = await REQUEST(
|
||||
buildApp(),
|
||||
"POST",
|
||||
"/api/planning/start",
|
||||
JSON.stringify({ initialPlan: "Investigate dashboard crash" }),
|
||||
{ "Content-Type": "application/json" },
|
||||
);
|
||||
|
||||
expect(res.status).toBe(500);
|
||||
expect(res.body.error).toContain("Failed to get first question from AI");
|
||||
expect(exitSpy).not.toHaveBeenCalled();
|
||||
expect(dispose).toHaveBeenCalledTimes(1);
|
||||
} finally {
|
||||
exitSpy.mockRestore();
|
||||
}
|
||||
});
|
||||
|
||||
it("enforces rate limiting (1000 sessions per hour per IP)", async () => {
|
||||
// Create 1000 sessions (should succeed)
|
||||
for (let i = 0; i < 1000; i++) {
|
||||
|
||||
@@ -946,9 +946,21 @@ async function getFirstQuestionFromAgent(
|
||||
}
|
||||
|
||||
if (!parsed) {
|
||||
// Clean up the session on failure
|
||||
// Clean up the failed startup session and release the underlying agent so
|
||||
// an unparsable first response cannot leave model/transport handles behind
|
||||
// in the long-running dashboard process.
|
||||
sessions.delete(session.id);
|
||||
unpersistSession(session.id);
|
||||
try {
|
||||
await session.agent.session.dispose?.();
|
||||
} catch (disposeErr) {
|
||||
diagnostics.warn("Failed to dispose planning agent after first question failure", {
|
||||
sessionId: session.id,
|
||||
message: disposeErr instanceof Error ? disposeErr.message : String(disposeErr),
|
||||
operation: "dispose-after-first-question-failure",
|
||||
});
|
||||
}
|
||||
session.agent = undefined;
|
||||
throw new Error(
|
||||
`Failed to get first question from AI: ${lastError?.message || "Unknown error"}`
|
||||
);
|
||||
|
||||
@@ -35,7 +35,7 @@ describe("desktop release workflow wiring", () => {
|
||||
expect(workflow).toContain("--x64");
|
||||
expect(workflow).toContain("--arm64");
|
||||
expect(workflow).toContain("Fusion-*-linux-arm64.AppImage");
|
||||
expect(workflow).toContain("Fusion-*-linux-x64.AppImage");
|
||||
expect(workflow).toMatch(/Fusion-\*-linux-(x64|x86_64)\.AppImage/);
|
||||
expect(workflow).toContain("name: fusion-desktop-linux");
|
||||
expect(workflow).toContain("packages/desktop/dist-electron/latest-linux.yml");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user