diff --git a/backend/src/pi/pi-process-manager.ts b/backend/src/pi/pi-process-manager.ts index cb0daf15..c9701d58 100644 --- a/backend/src/pi/pi-process-manager.ts +++ b/backend/src/pi/pi-process-manager.ts @@ -47,7 +47,7 @@ export class PiProcessManager { async spawnFor( sessionId: string, - o: { provider?: string; model?: string; thinking?: string; author?: string }, + o: { provider?: string; model?: string; thinking?: string; author?: string; mode?: "new" | "resume" }, ): Promise { // Idempotent per session id: tear down any existing runtime for this id // first (before the cap check) so a resume/respawn neither leaks the old @@ -80,7 +80,10 @@ export class PiProcessManager { await rpc.request({ type: "set_thinking_level", level: thinking } as object & { type: string }); } - rpc.send({ type: "prompt", message: `/nuova-domanda "kickoff"` }); + const message = o.mode === "resume" + ? `/riprendi-sessione ${sessionId}` + : `/nuova-domanda "kickoff"`; + rpc.send({ type: "prompt", message }); return rt; } @@ -90,6 +93,7 @@ export class PiProcessManager { provider: manifest?.provider, model: manifest?.model, thinking: manifest?.thinking, + mode: "resume", }); } diff --git a/backend/test/pi-process-manager.test.ts b/backend/test/pi-process-manager.test.ts index fb53bbfb..b983d660 100644 --- a/backend/test/pi-process-manager.test.ts +++ b/backend/test/pi-process-manager.test.ts @@ -2,6 +2,7 @@ import { test, expect } from "vitest"; import { spawn } from "node:child_process"; import path from "node:path"; import { fileURLToPath } from "node:url"; +import { EventEmitter } from "node:events"; import { PiProcessManager } from "../src/pi/pi-process-manager.js"; import { loadConfig } from "../src/config.js"; @@ -68,3 +69,32 @@ test("oltre maxPiProcesses solleva errore", async () => { await expect(mgr.spawnFor("b", {})).rejects.toThrow(/max/i); mgr.teardown("a"); }); + +function recordingChild() { + const ch: any = new EventEmitter(); + ch.stdout = new EventEmitter(); + ch.stderr = new EventEmitter(); + ch._writes = [] as string[]; + ch.stdin = { write: (d: any) => { ch._writes.push(String(d)); return true; } }; + ch.kill = () => {}; + return ch; +} + +test("spawnFor resume mode sends /riprendi-sessione ", async () => { + const cfg = loadConfig({}); // no provider/model/thinking -> no rpc.request handshakes + const child = recordingChild(); + const mgr = new PiProcessManager(cfg, { spawnFn: () => child as any }); + await mgr.spawnFor("sid-9", { mode: "resume" }); + expect(child._writes.join("")).toContain("/riprendi-sessione sid-9"); + expect(child._writes.join("")).not.toContain("/nuova-domanda"); + mgr.teardown("sid-9"); +}); + +test("spawnFor default (new) mode sends /nuova-domanda", async () => { + const cfg = loadConfig({}); + const child = recordingChild(); + const mgr = new PiProcessManager(cfg, { spawnFn: () => child as any }); + await mgr.spawnFor("sid-10", {}); + expect(child._writes.join("")).toContain("/nuova-domanda"); + mgr.teardown("sid-10"); +});