// scenarios/resilience.mjs — daemon crash (SIGKILL of the go process group) // and reboot on the SAME sqlite file: every persisted event survives, sessions // come back listed offline, and seq continues past the persisted high-water. import { agentClient, agentEvent, agentHello, rest, sessionSnapshot, sid, waitUntil } from "../lib.mjs"; export async function run(ctx) { const { r, base, token, agentUrl, state } = ctx; const id = sid("e2e-res"); const marker = `persist-me-${sid("m")}`; const snapshot = sessionSnapshot(id); const agent = await agentClient(agentUrl, token); const w1 = await agentHello(agent, snapshot); r.check("pre-restart handshake", w1?.type === "welcome"); agentEvent(agent, id, 1, "message_end", { message: { role: "user", id: "mr1", text: marker, thinking: null, toolCalls: [], toolCallId: null }, }); agentEvent(agent, id, 2, "tool_execution_start", { toolCallId: "tc9", toolName: "bash", args: { command: "true" } }); agentEvent(agent, id, 3, "agent_settled", {}); const flushed = await waitUntil( async () => (await rest(base, token, `/api/sessions/${id}/events?after=0`)).json?.length === 3, ); r.check("events flushed to sqlite before crash", flushed); agent.close(); let restarted = true; let restartDetail = ""; try { await ctx.restartDaemon(); } catch (err) { restarted = false; restartDetail = String(err?.message ?? err); } r.check("daemon restarted on same DB after SIGKILL", restarted, restartDetail); const sessions = (await rest(base, token, "/api/sessions")).json ?? []; const row = sessions.find((s) => s.id === id); r.check( "persisted session listed with online=false after restart", row !== undefined && row.online === false, JSON.stringify(row ?? null), ); r.check( "pre-restart sessions from other scenarios also listed offline", sessions.some((s) => s.id === state.lifecycle?.id && s.online === false) && sessions.every((s) => s.online === false), JSON.stringify(sessions.map((s) => [s.id, s.online])), ); const events = (await rest(base, token, `/api/sessions/${id}/events?after=0`)).json ?? []; r.check( "all 3 events survive the crash", JSON.stringify(events.map((e) => e.seq)) === "[1,2,3]", JSON.stringify(events.map((e) => e.seq)), ); r.check( "event payload intact (message text marker)", events[0]?.message?.text === marker, JSON.stringify(events[0] ?? null), ); const tail = (await rest(base, token, `/api/sessions/${id}/events?after=2`)).json ?? []; r.check("after=2 still only seq 3 after restart", JSON.stringify(tail.map((e) => e.seq)) === "[3]"); // Reconnect after restart: the persisted high-water must be honoured. const agent2 = await agentClient(agentUrl, token); const w2 = await agentHello(agent2, snapshot); r.check("post-restart welcome lastSeq=3", w2?.lastSeq === 3, `got ${w2?.lastSeq}`); agentEvent(agent2, id, 4, "message_end", { message: { role: "user", id: "mr2", text: "after restart", thinking: null, toolCalls: [], toolCallId: null }, }); const continued = await waitUntil( async () => (await rest(base, token, `/api/sessions/${id}/events?after=3`)).json?.length === 1, ); r.check("seq continues past persisted high-water after restart", continued); agent2.close(); }