fix(resume): recover cleanly after Pi exits

This commit is contained in:
User
2026-07-14 21:35:50 +02:00
parent 7e1d72c9a4
commit df1e7dea9c
13 changed files with 214 additions and 17 deletions
+2 -1
View File
@@ -20,6 +20,7 @@ export interface BuildAppDeps {
listModels?: ListModelsFn;
getSettings?: () => Settings;
readiness?: ReadinessManager;
hub?: SseHub;
}
export function buildApp(config: AppConfig, deps?: BuildAppDeps): FastifyInstance {
@@ -39,7 +40,7 @@ export function buildApp(config: AppConfig, deps?: BuildAppDeps): FastifyInstanc
dataRoot: config.dataRoot,
});
const mgr = deps?.mgr ?? new PiProcessManager(config, deps?.spawnFn ? { spawnFn: deps.spawnFn } : undefined);
const hub = new SseHub();
const hub = deps?.hub ?? new SseHub();
const readiness = deps?.readiness ?? new ReadinessManager(
tht as ThtRunner,
Math.round(config.ollamaEnsureTimeoutMs / 1000),
+4 -1
View File
@@ -33,7 +33,7 @@ export class SessionBridge {
m.message?.role === "assistant" &&
m.message?.stopReason === "error"
) {
this.state = "failed";
this.markFailed();
this.fan({
type: "info",
level: "error",
@@ -69,6 +69,9 @@ export class SessionBridge {
beginTurn(): void { this.state = "running"; }
/** Record a backend-detected failure; callers own any sanitized client message. */
markFailed(): void { this.state = "failed"; }
onClientEvent(cb: (e: ClientEvent) => void): void { this.cbs.add(cb); }
/** Eventi generati dal backend stesso (es. exit inatteso del child Pi), non da Pi. */
+1
View File
@@ -103,6 +103,7 @@ export class PiProcessManager {
child.on("exit", (code) => {
console.error(`[pi:${sessionId}] exited code=${code ?? "?"} mapped=${this.runtimes.get(sessionId) === rt}`);
if (this.runtimes.get(sessionId) === rt) {
rt.bridge.markFailed();
this.runtimes.delete(sessionId);
rt.bridge.emitClientEvent({
type: "info",
+2 -2
View File
@@ -106,8 +106,6 @@ export function sessionRoutes(
if (state === "running" || state === "waiting") {
return reply.code(200).send({ id, alreadyActive: true });
}
d.mgr.teardown(id);
d.hub.clear(id);
}
const manifest = (await d.tht.sessionShow(id, d.getSettings().workspace)) as { status?: string; archived?: boolean } | null;
if (manifest?.status === "finalized" || manifest?.archived) {
@@ -124,6 +122,8 @@ export function sessionRoutes(
author: getUser(req).id,
mode: "resume" as const,
};
if (existing) d.mgr.teardown(id);
d.hub.clear(id);
await d.tht.reopenSession(id, settings.workspace);
const rt = d.mgr.createFor(id, options);
bindRuntime(id, rt);