import type { ConversationCache } from "./conversation-cache"; import { type ManagedSessionRow, type ManagedSessionsRepository } from "./db/repositories/managed-sessions.repository"; import type { FeatureFlagValues } from "./feature-flags"; import type { LiveSessionManager } from "./live-session-manager"; import type { Logger } from "./logger"; import type { ResumeOutcome } from "./server"; import { type ReconcileVerdict } from "./services/sessions/reconcileSessions"; import type { SessionStore } from "./session-store"; import type { ManagedSession } from "./types"; /** * Everything the boot-time registry lifecycle reads from the server. Thunks * rather than values for anything bound after construction or replaceable at * runtime (`managedSessionsRepo` opens during listen(); tests swap `log` and * `resumeSession` on the server instance) — the same reason ScannerManagerDeps * passes `cache: () => ConversationCache | null`. */ export type SessionRegistryBootDeps = { log: () => Logger; ptyManager: () => LiveSessionManager; sessionStore: () => SessionStore; featureFlags: () => FeatureFlagValues; autoResumeOnBoot: () => boolean; managedSessionsRepo: () => ManagedSessionsRepository | null; cache: () => ConversationCache | null; streamerInstanceId: string; sessionVerdicts: Map; selfPtyEndedAt: Map; resumeSession: (opts: { sessionId: string; projectName?: string; branch?: string; }) => Promise; watchConversationFile: (sessionId: string, historyId?: string) => Promise; broadcastSessionList: () => void; resolveConversationTarget: (sessionId: string) => Promise<{ ok: false; reason: string; } | { ok: true; [key: string]: unknown; }>; }; /** * The durable session registry's boot-and-shutdown lifecycle: classifying what * previous runs left behind, seeding the session list from it, optionally * resuming, pruning retired rows, and mirroring spawns and shutdown back into * the registry. * * Extracted from StreamerServer so registry work stops editing the server file * (see docs/plans/2026-07-12-server-ts-split.md, PR 6). State stays on the * server: this class only reads it through `deps`. */ export declare class SessionRegistryBoot { private deps; constructor(deps: SessionRegistryBootDeps); private get log(); private get ptyManager(); private get sessionStore(); private get featureFlags(); private get autoResumeOnBoot(); private get managedSessionsRepo(); private get streamerInstanceId(); private get sessionVerdicts(); private get selfPtyEndedAt(); /** * Classify sessions left behind by previous streamer runs (C1 Phase 3a). * * Agents already outlive the streamer today on the crash and dev-takeover * paths, which exit without reaching ptyManager.dispose() — they are just * invisible when they do, because nothing recorded that they existed. This * turns those rows into an explicit verdict per session. * * Read-only with respect to processes: it probes and classifies, and never * signals anything. `orphaned` is a report, not a cleanup trigger. */ reconcilePreviousSessions(): Promise; /** * Drop finished sessions the registry has held long enough (plan Phase 4). * * The registry is authoritative and never rebuilt from the cache, so nothing * else would ever remove a row: without this it grows for the life of the * install, and every boot pays for rows about sessions from months ago. */ pruneTerminalSessions(): void; /** * Seed the session list with what previous runs left behind (persistence plan * Phase 1, gaps G1/G2/G8). * * Reconciliation classifies rows and stops there; a verdict is overlaid onto a * SessionResponse that already exists, and after a clean restart none does — * `SessionStore` starts empty. So the user's session did not become * `resumable`, it became *absent*. This is the half that puts it back. * * The seeded stubs hold no PTY and are never handed to `LiveSessionManager`, * so `reapIdleSessions` and `startGraceTimer` — both of which iterate * `ptyManager.listSessions()` — cannot observe them. A later resume calls * `sessionStore.addManaged` with the real session, which overwrites the stub * by id rather than duplicating it. */ rehydratePreviousSessions(verdicts: ReconcileVerdict[]): ManagedSessionRow[]; /** Resume only the recent sessions the user explicitly allowed us to start at boot. */ autoResumePreviousSessions(rows: ManagedSessionRow[]): Promise; /** * Pick a token guaranteed to appear in the spawned process's argv, for the * reconciler's pid-reuse guard. * * Claude always passes the session id (`--resume ` or `--session-id * `), so it is both present and unique. Codex only does on *resume* * (`codex resume `); a fresh Codex spawn is `codex --cd * --no-alt-screen` with no id at all, because the rollout id does not exist * until the CLI writes it. boundConversationId is what distinguishes the two: * it is set once that rollout has been discovered. */ spawnArgvToken(session: ManagedSession): string; /** Restore registry-only metadata after the host mirror has been adopted. */ refreshHostedSessionsFromRegistry(): void; /** * Mirror a freshly-spawned session into the durable registry (C1 Phase 2). * * Called at each addManaged() site rather than inside SessionStore, because * the store is a pure in-memory structure with no DB dependency and adding * one would drag persistence into every unit test that touches it. * * Best-effort by design: a failed registry write must never break session * start. Losing a row costs post-restart *visibility* for that session, which * is strictly better than refusing to run the agent at all. */ recordSessionSpawn(session: ManagedSession): void; /** * Stamp every live session as ended-by-shutdown before dispose() kills it. * * PTYManager.dispose() signals each child directly and fires no * onStatusChange, so the registry would otherwise keep rows sitting at * `running` forever and the next boot could not tell a deliberate restart * from a crash. Recording `shutdown` as the status source makes that * distinction explicit rather than inferred. * * Not a `completed_at` write for the agent's own work — the agent did not * finish, we stopped it — but the session is genuinely terminal, so it must * leave the reconciler's probe set. */ recordShutdownState(): void; } //# sourceMappingURL=session-registry-boot.d.ts.map