/** * Session State Persistence Module * * Saves session metadata on shutdown and restores sessions on startup. * This compensates for PTY sessions not persisting like tmux sessions do. * * @module session-state-persistence */ import type { ISessionBackend, SessionOptions } from './session-backend.interface.js'; import { RuntimeType } from '../../constants.js'; /** * Metadata about a persisted session. */ export interface PersistedSessionInfo { /** Unique session name */ name: string; /** Working directory for the session */ cwd: string; /** Command to run (e.g., 'claude', 'gemini') */ command: string; /** Command arguments */ args: string[]; /** Runtime type for the agent */ runtimeType: RuntimeType; /** Agent role (e.g., 'orchestrator', 'dev', 'qa') */ role?: string; /** Team ID if part of a team */ teamId?: string; /** Member ID if this is a team member session */ memberId?: string; /** Environment variables */ env?: Record; /** Claude session ID for resuming conversations on restart */ claudeSessionId?: string; } /** * Structure of the persisted state file. */ export interface PersistedState { /** Schema version */ version: number; /** ISO timestamp when state was saved */ savedAt: string; /** Array of session metadata */ sessions: PersistedSessionInfo[]; } /** * Session State Persistence Service * * Manages saving and restoring session state for PTY-based sessions. * Sessions are saved when the backend shuts down and restored on startup. * * @example * ```typescript * const persistence = new SessionStatePersistence(); * * // Register a session for persistence * persistence.registerSession('my-session', options, 'claude-code', 'dev'); * * // Save state on shutdown * await persistence.saveState(backend); * * // Restore state on startup * await persistence.restoreState(backend); * ``` */ export declare class SessionStatePersistence { /** Path to the state file */ private readonly filePath; /** Map of session name to metadata for persistence */ private sessionMetadata; /** Set of session names that were restored from the state file on startup */ private restoredSessionNames; /** Logger instance */ private logger; /** * Create a new SessionStatePersistence instance. * * @param filePath - Optional custom path for the state file */ constructor(filePath?: string); /** * Register a session for persistence tracking. * * @param name - Session name * @param options - Session options used to create the session * @param runtimeType - Type of runtime (claude-code, gemini-cli, codex-cli) * @param role - Optional role for the agent * @param teamId - Optional team ID * @param memberId - Optional member ID for team member sessions */ registerSession(name: string, options: SessionOptions, runtimeType: RuntimeType, role?: string, teamId?: string, memberId?: string): void; /** * Remove a session from persistence tracking. * * @param name - Session name to unregister */ unregisterSession(name: string): void; /** * File that remembers stopped sessions' last conversation ids. * * @returns Absolute path next to the state file */ lastConversationsFile(): string; /** * Check if a session is registered for persistence. * * @param name - Session name to check * @returns true if the session is registered */ isSessionRegistered(name: string): boolean; /** * Get metadata for a registered session. * * @param name - Session name * @returns Session metadata or undefined if not registered */ getSessionMetadata(name: string): PersistedSessionInfo | undefined; /** * Update the Claude session ID for a registered session. * Called when an agent registers via MCP and reports its session ID. * * @param name - Session name * @param claudeSessionId - The Claude conversation/session ID */ updateSessionId(name: string, claudeSessionId: string): void; /** * Forget the stored conversation id so the next launch starts a fresh * conversation instead of resuming this one. Used when the running * conversation was replaced (e.g. Claude Code `/clear`) and the new id * could not be learned. * * @param name - Session name */ clearSessionId(name: string): void; /** * Get the Claude session ID for a registered session. * * @param name - Session name * @returns Claude session ID or undefined */ getSessionId(name: string): string | undefined; /** * Check if a session was restored from the state file on startup. * * Sessions loaded during restoreState() are tracked separately from * newly created sessions. This allows the resume logic to determine * whether an agent was running before the backend restart. * * @param name - Session name to check * @returns true if the session was restored from persisted state */ isRestoredSession(name: string): boolean; /** * Get all registered session names. * * @returns Array of registered session names */ getRegisteredSessions(): string[]; /** * Get all registered session metadata as a Map. * * @returns Map of session name to metadata */ getRegisteredSessionsMap(): Map; /** * Build the current persisted state object from in-memory metadata. * * @returns PersistedState snapshot */ private buildState; /** * Auto-save current session metadata to disk. * Called after register/unregister/updateSessionId to keep the state file * in sync with in-memory metadata at all times. This ensures the state file * is always up-to-date even if the process is killed before graceful shutdown. */ private autoSave; /** * Save current session state to disk. * * Saves all sessions registered in sessionMetadata, which is the source * of truth for what should persist. * * @param backend - Session backend (kept for backward compatibility, no longer used) * @returns Number of sessions saved */ saveState(backend: ISessionBackend): Promise; /** * Restore sessions from saved state. * * For Claude sessions, uses --resume flag to restore conversation context. * * @param backend - Session backend to create sessions in * @returns Number of sessions successfully restored */ restoreState(backend: ISessionBackend): Promise; /** * Get the arguments to use when restoring a session. * * The persisted command is the shell (e.g. /bin/zsh), not the Claude binary. * Claude Code is launched inside the shell by initializeAgentWithRegistration, * so we must NOT add --resume to the shell args — it would cause * "/bin/zsh: no such option: resume". * * @param sessionInfo - Session metadata * @returns Arguments array for session creation */ private getRestoreArgs; /** * Clear the saved state file. */ clearState(): Promise; /** * Clear both in-memory metadata and the state file on disk. * Used when the user dismisses the session resume popup. */ clearStateAndMetadata(): Promise; /** * Forget some sessions (the ones the owner declined to resume) while * keeping the others' metadata — and with it their conversation ids. * Dismissing the resume popup used to wipe every session, including * agents the backend had already brought back, so their next restart * could not resume (2026-09-18). * * @param names - Sessions to forget */ forgetSessions(names: readonly string[]): Promise; /** * Clear all registered session metadata (in-memory only). */ clearMetadata(): void; /** * Get the path to the state file. * * @returns State file path */ getFilePath(): string; /** * Load state from file without restoring sessions. * * @returns Persisted state or null if not found/invalid */ loadState(): Promise; } /** * Get the singleton SessionStatePersistence instance. * * @returns SessionStatePersistence instance */ export declare function getSessionStatePersistence(): SessionStatePersistence; /** * Reset the singleton instance (for testing). */ export declare function resetSessionStatePersistence(): void; //# sourceMappingURL=session-state-persistence.d.ts.map