import type { QueuedMessage } from "../ws/protocol.js";
/**
 * Tightened orphan-cleanup gate. Returns true when an apparently-orphaned
 * session must NOT be archived because its worktree directory still exists
 * AND its agent JSONL has been written within the last 24h. Caller is
 * `SessionManager`'s boot orphan sweep — leave the session live; the user
 * can archive manually from the UI if it really is dead.
 *
 * `now` is injectable for testability.
 */
export declare function shouldKeepDespiteOrphan(ps: {
    worktreePath?: string;
    agentSessionFile?: string;
}, now?: number): boolean;
/** Persisted metadata for a single gateway session */
export interface PersistedSession {
    id: string;
    title: string;
    cwd: string;
    /** The agent's .jsonl session file path — needed to resume */
    agentSessionFile: string;
    createdAt: number;
    lastActivity: number;
    /** Epoch ms when the user last viewed this session. 0 / undefined = never read. */
    lastReadAt?: number;
    /** Optional goal this session belongs to */
    goalId?: string;
    /** Whether the agent was actively streaming when the server last knew about it */
    wasStreaming?: boolean;
    /** Epoch ms when the current streaming turn started (survives server restarts) */
    streamingStartedAt?: number;
    /** If this session is a delegate, the parent session ID */
    delegateOf?: string;
    /** First-class parent session ID for visible child sessions (not delegate lifecycle). */
    parentSessionId?: string;
    /** Kind discriminator for first-class child sessions, e.g. "pr-walkthrough". */
    childKind?: string;
    /** Whether the session should be treated as read-only by clients/tools. */
    readOnly?: boolean;
    /** PR walkthrough job metadata for session-hosted walkthrough children. */
    walkthroughJobId?: string;
    walkthroughChangesetId?: string;
    walkthroughTargetKey?: string;
    /** Explicit session-scoped tool allowlist captured at creation. Undefined means derive from role/default policy. */
    allowedTools?: string[];
    /** Which project this session belongs to */
    projectId?: string;
    /** Role in a team goal (e.g., 'coder', 'reviewer', 'tester') */
    role?: string;
    /** The team goal this agent belongs to */
    teamGoalId?: string;
    /** Session ID of the team lead that spawned this agent */
    teamLeadSessionId?: string;
    /** Path to the git worktree for this session */
    worktreePath?: string;
    /** Assistant type: "goal" | "role" | "tool" */
    assistantType?: string;
    /** @deprecated Use assistantType instead */
    goalAssistant?: boolean;
    /** @deprecated Use assistantType instead */
    roleAssistant?: boolean;
    /** @deprecated Use assistantType instead */
    toolAssistant?: boolean;
    /** Task ID this session is working on */
    taskId?: string;
    /** Staff agent ID this session belongs to */
    staffId?: string;
    /** Pixel-art accessory ID for the Bobbit sprite overlay */
    accessory?: string;
    /** Whether this session has a live HTML preview panel */
    preview?: boolean;
    /** Persisted prompt queue */
    messageQueue?: QueuedMessage[];
    /** Server-side draft storage, keyed by draft type (e.g. "prompt", "goal", "role") */
    drafts?: Record<string, unknown>;
    /** Goal ID this session is re-attempting (for goal assistant sessions) */
    reattemptGoalId?: string;
    /** Whether this session is archived (soft-deleted) */
    archived?: boolean;
    /** Epoch ms when this session was archived */
    archivedAt?: number;
    /** Whether this is an automated non-interactive session (e.g. verification reviewer) */
    nonInteractive?: boolean;
    /** Repository path (preserved from goal for worktree cleanup) */
    repoPath?: string;
    /** Branch name (preserved for worktree cleanup) */
    branch?: string;
    /** Model provider (e.g. "anthropic") — persisted so archived sessions can display model info */
    modelProvider?: string;
    /** Model ID (e.g. "claude-sonnet-4-20250514") — persisted so archived sessions can display model info */
    modelId?: string;
    /** Image generation model provider for this session, if overridden from the default. */
    imageModelProvider?: string;
    /** Image generation model ID for this session, if overridden from the default. */
    imageModelId?: string;
    /** Whether this session runs inside a Docker sandbox container */
    sandboxed?: boolean;
    /** Per-repo worktree paths (multi-repo only). Single-repo uses flat worktreePath. */
    repoWorktrees?: Record<string, string>;
}
/**
 * Subset of `PersistedSession` fields that `SessionStore.update()` is
 * permitted to mutate after creation. `id`, `createdAt`, `drafts`, and
 * other identity-shaped fields are intentionally excluded.
 */
export type UpdatableSessionFields = Pick<PersistedSession, "title" | "lastActivity" | "lastReadAt" | "agentSessionFile" | "goalId" | "wasStreaming" | "streamingStartedAt" | "delegateOf" | "parentSessionId" | "childKind" | "readOnly" | "walkthroughJobId" | "walkthroughChangesetId" | "walkthroughTargetKey" | "role" | "teamGoalId" | "teamLeadSessionId" | "worktreePath" | "assistantType" | "goalAssistant" | "roleAssistant" | "toolAssistant" | "taskId" | "staffId" | "accessory" | "preview" | "messageQueue" | "archived" | "archivedAt" | "repoPath" | "branch" | "nonInteractive" | "cwd" | "reattemptGoalId" | "modelProvider" | "modelId" | "imageModelProvider" | "imageModelId" | "sandboxed" | "projectId" | "repoWorktrees">;
/**
 * Simple JSON file store for gateway session metadata.
 * Allows sessions to survive server restarts.
 */
export declare class SessionStore {
    private readonly storeDir;
    private readonly storeFile;
    private sessions;
    private saveTimer;
    private static SAVE_DEBOUNCE_MS;
    private static BACKUP_COUNT;
    /** Monotonically increasing counter — bumped on every mutation. Resets to 0 on server restart. */
    private generation;
    /** Epoch read from disk on construction (or 0 for legacy/missing). */
    private loadedEpoch;
    /** Epoch we have successfully written to disk this process. */
    private writtenEpoch;
    /** One-shot latch: once tripped, no further saveNow() writes to disk. */
    private staleGuardTripped;
    constructor(stateDir: string);
    /** Normalise a single PersistedSession-shaped row read from disk (legacy field migration). */
    private seedFromArray;
    /** Backup-file path for index 1..N. */
    private bakPath;
    private load;
    /**
     * Synchronously read just `sessions.json` and return its `epoch`.
     * Returns 0 for legacy v1 array shape; -1 if the file is missing or
     * unparseable. Used by saveNow() to detect external rewrites.
     */
    private peekDiskEpoch;
    /** Rotate sessions.json → .bak.1 → .bak.2 → … → .bak.N. Best-effort. */
    private rotateBackups;
    /** True if the most recent saveNow() refused to write due to the stale-snapshot guard. */
    isStaleGuardTripped(): boolean;
    /** Epoch read from disk at construction. Test-visible. */
    getLoadedEpoch(): number;
    /** Epoch most recently written to disk this process. Test-visible. */
    getWrittenEpoch(): number;
    /** Write sessions to disk immediately (synchronous). */
    private saveNow;
    /**
     * Walk `agentSessionsRoot` for `*.jsonl` transcripts that are not referenced
     * by any persisted session (`agentSessionFile`) and whose mtime is newer
     * than the most recent `lastActivity` in the store. Useful as a divergence
     * signal after crash recovery — the agent CLI may have written transcripts
     * that never made it into the session-metadata index.
     *
     * Does NOT auto-import. Caps the returned `paths` at `maxPaths` (default 50)
     * and emits at most 20 `[session-store] WARN: orphaned transcript: …` log
     * lines.
     */
    scanOrphanedTranscripts(agentSessionsRoot: string, options?: {
        mostRecentLastActivity?: number;
        maxPaths?: number;
        maxLogLines?: number;
    }): {
        count: number;
        paths: string[];
    };
    private computeMostRecentLastActivity;
    /** Schedule a debounced save — coalesces rapid writes into one disk flush. */
    private save;
    /** Current generation counter — bumped on every mutation. */
    getGeneration(): number;
    /** Optional callback invoked after any session mutation (put/update/archive). */
    onIndexUpdate?: (session: PersistedSession) => void;
    put(session: PersistedSession): void;
    get(id: string): PersistedSession | undefined;
    remove(id: string): void;
    getAll(): PersistedSession[];
    /**
     * Fields whose persistence is required for the session to survive a hard
     * restart (kill -9, OS crash, container OOM). When any of these change we
     * flush synchronously instead of going through the 1s save debounce —
     * otherwise the gateway can advertise the session as `idle` to the API
     * before the recovery-critical disk write has landed, and a kill in that
     * window archives the session on next boot.
     *
     * Lower-frequency by nature, so synchronous writes are not a perf concern.
     * `lastActivity` / `lastReadAt` are intentionally excluded — they fire on
     * every event and benefit from coalescing.
     */
    private static RECOVERY_CRITICAL_FIELDS;
    /** Update a subset of fields for an existing session */
    update(id: string, updates: Partial<UpdatableSessionFields>): void;
    /** Get a draft for a session by type. */
    getDraft(sessionId: string, type: string): unknown | undefined;
    /** Set a draft for a session by type. Triggers debounced save. */
    setDraft(sessionId: string, type: string, data: unknown): boolean;
    /** Delete a draft for a session by type. Triggers debounced save. */
    deleteDraft(sessionId: string, type: string): boolean;
    /** Mark a session as archived. */
    archive(id: string): boolean;
    /** Get all archived sessions. */
    getArchived(): PersistedSession[];
    /**
     * Paginated listing of archived sessions, sorted by archivedAt DESC.
     * @param limit Max items per page
     * @param afterCursor archivedAt timestamp — return items with archivedAt < cursor
     */
    listArchivedSessionsPaginated(limit: number, afterCursor?: number): {
        sessions: PersistedSession[];
        total: number;
        hasMore: boolean;
        nextCursor?: number;
    };
    /** Get all live (non-archived) sessions. */
    getLive(): PersistedSession[];
    /** Permanently remove an archived session from the store. */
    purge(id: string): boolean;
    /** Flush any pending debounced save immediately (e.g. before shutdown). */
    flush(): void;
}
//# sourceMappingURL=session-store.d.ts.map