fix(pi): require Reload after the native process exits (#8718)

Reject new turns before accepting work into a known dead Pi runtime. Keep process liveness separate from explicit session cleanup so Reload can still release resources and resume the conversation.

Co-authored-by: paseo-bot[bot] <266920839+paseo-bot[bot]@users.noreply.github.com>
This commit is contained in:
paseo-bot[bot]
2026-11-04 01:56:00 +00:00
committed by GitHub
parent 064243288b
commit 66676d49e6
2 changed files with 32 additions and 0 deletions
@@ -2194,6 +2194,33 @@ describe("PiRpcAgentSession", () => {
});
});
test.each([false, true])(
"rejects new turns with Reload guidance after the Pi process exits (active: %s)",
async (active) => {
const { pi, session, events } = await createSession();
try {
if (active) {
await session.startTurn("before crash");
}
const runtime = pi.latestSession();
runtime.emit({ type: "process_exit", error: "Pi exited" });
if (active) {
await expect(events.nextTurnFailure()).resolves.toMatchObject({ error: "Pi exited" });
}
const promptCount = runtime.prompts.length;
for (let attempt = 0; attempt < 2; attempt += 1) {
await expect(session.startTurn("after crash")).rejects.toThrow(
"Pi process exited. Reload agent before sending another message.",
);
}
expect(runtime.prompts).toHaveLength(promptCount);
} finally {
await session.close();
}
},
);
test("fails an autonomous turn when the Pi process exits before settlement", async () => {
const { pi, events } = await createSession();
const fakeSession = pi.latestSession();
@@ -1223,6 +1223,7 @@ export class PiRpcAgentSession implements AgentSession {
private readonly logger: Logger;
private readonly usagePoller: PiUsagePoller;
private closed = false;
private runtimeDead = false;
private readonly closeController = new AbortController();
private readonly pendingExtensionHydrations = new Set<Promise<void>>();
private pendingOutput: Promise<void> | null = null;
@@ -1305,6 +1306,9 @@ export class PiRpcAgentSession implements AgentSession {
}
async startTurn(prompt: AgentPromptInput, options?: AgentRunOptions): Promise<StartTurnResult> {
if (this.runtimeDead) {
throw new Error("Pi process exited. Reload agent before sending another message.");
}
if (this.activeTurnId) {
throw new Error("A Pi turn is already active");
}
@@ -2190,6 +2194,7 @@ export class PiRpcAgentSession implements AgentSession {
}
private handleProcessExit(error: string): void {
this.runtimeDead = true;
this.rejectAllExtensionResults(new Error(error));
this.interruptingTurn = null;
if (!this.activeTurnId && !this.activeTurnStarted) {