Something went wrong. Try again.
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566/** * runtime-resolve — pure helpers that resolve the `SessionRuntimeInfo` to render for a * selected session (saga vr7kgx.2 / vr7kgx.7). * * The web product keeps TWO runtime projections per session: * • the **summary's runtime projection** (`SessionSummary.runtime`) — sanitized, durable, * and LIVE: `session_runtime_changed` updates its `status`/`runtime_state`/`usage` * authoritatively as the runtime transitions (stopped→running→…). It carries no static * model/provider/mode detail. * • the **cached inspect record** (`runtimeBySession[id]`, from `session.status`) — a * point-in-time snapshot rich in static fields (model/provider/mode/title/created_at), * refreshed only on discrete UI events (select/swap/pin/fork/create/stop/kill/resume). * * `resolveRuntime` overlays the live lifecycle fields (`status`/`runtime_state`) + `usage` * from the summary over the cached inspect record, so the rendered runtime never shows a * stale `runtime_state`. Without that overlay the compact "Relaunch runtime" affordance * (and the runtime panel's "Resume runtime" button) would keep showing for a runtime that * has since become active — the relaunch-while-active bug. The cached record still owns the * static fields it alone carries. * * Boundary: pure, no I/O, no browser globals. Imports only TYPES from `@/contracts/*`. */import type { SessionSummary, SessionSummaryRuntime } from '@/contracts/session-summary';import type { SessionRuntimeInfo } from '@/contracts/session-runtime';
/** Synthesize a minimal `SessionRuntimeInfo` from a summary so the runtime panel always * has something to render before an inspect lands; `inspectSessionRuntime` enriches it. * Reads the LIVE summary projection, so a summary that already reports an active runtime * synthesizes an active one (never a stale "stopped"). */export function synthesizeRuntime(summary: SessionSummary): SessionRuntimeInfo { return { session_id: summary.session_id, cwd: summary.cwd, status: summary.runtime?.status ?? 'idle', runtime_state: summary.runtime?.runtime_state ?? 'stopped', created_at: summary.created_at, ...(summary.runtime?.usage !== undefined ? { usage: summary.runtime.usage } : {}), };}
/** Resolve the runtime to render for the selected session. Prefers the cached inspect * record (static model/provider/mode) and overlays the LIVE lifecycle fields * (`status`/`runtime_state`) + `usage` from the summary's runtime projection, so a runtime * that transitioned stopped→running since the last inspect no longer reads "stopped". * Falls back to `synthesizeRuntime` when no inspect has run yet. Pure. */export function resolveRuntime( id: string, summary: SessionSummary | null | undefined, runtimes: Record<string, SessionRuntimeInfo>,): SessionRuntimeInfo | null { if (summary === undefined || summary === null) return null; const cached = runtimes[id]; if (cached === undefined) return synthesizeRuntime(summary); const live = summary.runtime; if (live === undefined) return cached; return { ...cached, status: live.status, runtime_state: live.runtime_state, ...(live.usage !== undefined ? { usage: live.usage } : {}), };}
/** Type re-export for callers that build the live projection inline. */export type { SessionSummaryRuntime };