import type { ImageContent, Message, MessageAttribution, ServiceTierByFamily, TextContent } from "@oh-my-pi/pi-ai";
import { ArtifactManager } from "./artifacts.js";
import { type BlobPutOptions, type BlobPutResult } from "./blob-store.js";
import { type BashExecutionMessage, type CustomMessage, type FileMentionMessage, type HookMessage, type PythonExecutionMessage } from "./messages.js";
import { type BuildSessionContextOptions, type SessionContext } from "./session-context.js";
import { type NewSessionOptions, type SessionEntry, type SessionHeader, type SessionTitleSource, type SessionTreeNode, type UsageStatistics } from "./session-entries.js";
import { type SessionInfo } from "./session-listing.js";
import { type SessionStorage } from "./session-storage.js";
export type ReadonlySessionManager = Pick<SessionManager, "getCwd" | "getSessionDir" | "getSessionId" | "getSessionFile" | "getSessionName" | "getArtifactsDir" | "getArtifactManager" | "allocateArtifactPath" | "saveArtifact" | "getArtifactPath" | "getLeafId" | "getLeafEntry" | "getEntry" | "getLabel" | "getBranch" | "getHeader" | "getEntries" | "getTree" | "getUsageStatistics" | "putBlob" | "putBlobSync">;
interface SessionManagerStateSnapshot {
    cwd: string;
    sessionDir: string;
    sessionId: string;
    sessionName: string | undefined;
    titleSource: SessionTitleSource | undefined;
    sessionFile: string | undefined;
    titleUpdatedAt: string;
    hasTitleSlot: boolean;
    onDisk: boolean;
    needsRewrite: boolean;
    draftOnlySessionCleanupArmed: boolean;
    header: SessionHeader;
    entries: SessionEntry[];
}
/**
 * Stores and navigates an append-only conversation journal.
 *
 * A session is a JSONL file: one header line followed by entries. Entries form a
 * tree by `(id, parentId)`, and the mutable leaf pointer selects which path is
 * active for future appends and for LLM context construction.
 *
 * Durability is software-crash safe but not power-loss safe: appends are handed
 * to the OS synchronously in-body (so an entry survives an OOM/SIGKILL the
 * instant `appendMessage` returns) but never `fsync`'d. Full-file rewrites go
 * through the storage layer's atomic temp-write+rename so a crash mid-rewrite
 * cannot truncate the prior good file.
 */
export declare class SessionManager {
    #private;
    /**
     * Collab replication tap: invoked for every appended entry with the
     * in-memory (pre-blob-externalization) entry, so inline images survive.
     */
    onEntryAppended?: (entry: SessionEntry) => void;
    private constructor();
    /** Puts a binary blob into the blob store and returns the blob reference. */
    putBlob(data: Buffer, options?: BlobPutOptions): Promise<BlobPutResult>;
    /** Synchronous variant of {@link putBlob} for rebuild-only render paths. */
    putBlobSync(data: Buffer, options?: BlobPutOptions): BlobPutResult;
    captureState(): SessionManagerStateSnapshot;
    restoreState(snapshot: SessionManagerStateSnapshot): void;
    /** Switch to a different session file (resume / branch). */
    setSessionFile(sessionFile: string): Promise<void>;
    /** Start a new session. Drains and closes any existing writer first. */
    newSession(options?: NewSessionOptions): Promise<string | undefined>;
    /** Delete a session file and its artifact directory. ENOENT is treated as success. */
    dropSession(sessionPath: string): Promise<void>;
    /**
     * Fork the current session into a new file with the same entries.
     * @returns the old and new session file paths, or undefined when not persisting.
     */
    fork(): Promise<{
        oldSessionFile: string;
        newSessionFile: string;
    } | undefined>;
    /**
     * Move the session to a new working directory: relocate the session file and
     * artifacts on disk, update internal references, and rewrite the header cwd.
     */
    moveTo(newCwd: string, targetSessionDir?: string): Promise<void>;
    /**
     * Force the session onto disk even with no assistant message yet (ACP
     * session/new must create a discoverable file immediately).
     */
    ensureOnDisk(): Promise<void>;
    /** Flush pending writes. Call before switching sessions or on shutdown. */
    flush(): Promise<void>;
    /**
     * Synchronously makes the current append-only session durable. Avoid rewriting
     * an already-current file: large restored sessions can contain GiB of compacted
     * history, and Ctrl+C must not rebuild the whole JSONL string just to flush.
     */
    flushSync(): void;
    /** Flush, then close the append writer. */
    close(): Promise<void>;
    getCwd(): string;
    getUsageStatistics(): UsageStatistics;
    /**
     * Open a new per-turn budget window: snapshot the cumulative output baseline,
     * reset the eval-subagent counter, and set the (optional) ceiling.
     */
    beginTurnBudget(total: number | null, hard: boolean): void;
    recordEvalSubagentOutput(output: number): void;
    getTurnBudget(): {
        total: number | null;
        spent: number;
        hard: boolean;
    };
    getSessionDir(): string;
    getSessionId(): string;
    getSessionFile(): string | undefined;
    getArtifactsDir(): string | null;
    adoptArtifactManager(manager: ArtifactManager): void;
    getArtifactManager(): ArtifactManager | null;
    allocateArtifactPath(toolType: string): Promise<{
        id?: string;
        path?: string;
    }>;
    saveArtifact(content: string, toolType: string): Promise<string | undefined>;
    getArtifactPath(id: string): Promise<string | null>;
    saveDraft(text: string): Promise<void>;
    consumeDraft(): Promise<string | null>;
    /** The source that set the session name: "user" (manual/RPC) or "auto" (generated title). */
    get titleSource(): SessionTitleSource | undefined;
    getSessionName(): string | undefined;
    onSessionNameChanged(cb: () => void): () => void;
    /**
     * Set the session display name.
     * @param source "user" for explicit renames; "auto" for generated titles.
     *   Auto titles are ignored once the user has set a name.
     */
    setSessionName(name: string, source?: SessionTitleSource, trigger?: string): Promise<boolean>;
    /**
     * Append a foreign (host-authored) entry verbatim, preserving its
     * `id`/`parentId`. Used by collab guests to mirror the host session.
     */
    ingestReplicatedEntry(entry: SessionEntry): void;
    /**
     * Snapshot the session for collab replication: the live header plus a deep
     * copy of every entry (the host mutates entries in place on rewrite paths, so
     * guests must not share references).
     */
    snapshotForReplication(): {
        header: SessionHeader;
        entries: SessionEntry[];
    };
    /**
     * Append a message as a child of the current leaf, then advance the leaf.
     * CompactionSummaryMessage / BranchSummaryMessage are rejected here — they are
     * top-level entries via appendCompaction()/branchWithSummary().
     */
    appendMessage(message: Message | CustomMessage | HookMessage | BashExecutionMessage | PythonExecutionMessage | FileMentionMessage): string;
    /** Append a thinking level change as child of current leaf, then advance leaf. Returns entry id. */
    appendThinkingLevelChange(thinkingLevel?: string, configured?: string): string;
    appendServiceTierChange(serviceTier: ServiceTierByFamily | null): string;
    appendModeChange(mode: string, data?: Record<string, unknown>): string;
    /**
     * Append a model change as a child of the current leaf, then advance the leaf.
     * @param model Model in "provider/modelId" format
     * @param role Optional role (default: "default")
     */
    appendModelChange(model: string, role?: string): string;
    appendSessionInit(init: {
        systemPrompt: string;
        task: string;
        tools: string[];
        outputSchema?: unknown;
        spawns?: string;
        readSummarize?: boolean;
    }): string;
    appendCompaction<T = unknown>(summary: string, shortSummary: string | undefined, firstKeptEntryId: string, tokensBefore: number, details?: T, fromExtension?: boolean, preserveData?: Record<string, unknown>): string;
    appendCustomEntry(customType: string, data?: unknown): string;
    /**
     * Rewrite the session file after in-place entry updates (e.g. pruning old tool
     * outputs). Use sparingly.
     */
    rewriteEntries(): Promise<void>;
    /**
     * Append a custom message entry (for extensions) that participates in LLM context.
     * @param customType Hook identifier for filtering on reload
     * @param content Message content (string or TextContent/ImageContent array)
     * @param display Whether to show in TUI (true = styled display, false = hidden)
     * @param details Optional extension-specific metadata (not sent to LLM)
     * @param attribution Who initiated this message for billing/attribution semantics
     */
    appendCustomMessageEntry<T = unknown>(customType: string | undefined, content: string | (TextContent | ImageContent)[] | undefined, display: boolean | undefined, details?: T, attribution?: MessageAttribution | undefined): string;
    /**
     * Append an MCP tool selection entry recording the discovery-selected MCP tools.
     */
    appendMCPToolSelection(selectedToolNames: string[]): string;
    /** Append a TTSR injection entry recording which rules were injected. */
    appendTtsrInjection(ruleNames: string[]): string;
    /** All unique TTSR rule names injected on the current branch (root → leaf). */
    getInjectedTtsrRules(): string[];
    getLeafId(): string | null;
    getLeafEntry(): SessionEntry | undefined;
    /**
     * The most recent model role on the current branch, or undefined when no
     * model change has been recorded.
     */
    getLastModelChangeRole(): string | undefined;
    getEntry(id: string): SessionEntry | undefined;
    /** All direct children of an entry. */
    getChildren(parentId: string): SessionEntry[];
    getLabel(id: string): string | undefined;
    /**
     * Set or clear a label on an entry. Pass undefined/empty to clear.
     */
    appendLabelChange(targetId: string, label: string | undefined): string;
    /**
     * Walk from an entry to root, returning entries in path order. Includes all
     * entry types; use buildSessionContext() for the resolved LLM messages.
     */
    getBranch(fromId?: string): SessionEntry[];
    /**
     * Build the session context (LLM messages), or — with `{ transcript: true }` —
     * the full-history display transcript, from the current leaf path.
     */
    buildSessionContext(options?: BuildSessionContextOptions): SessionContext;
    /** Strip stale OpenAI Responses assistant replay metadata from loaded entries. */
    sanitizeLoadedOpenAIResponsesReplayMetadata(): boolean;
    getHeader(): SessionHeader | null;
    /** All session entries (excludes header). Returns a shallow copy. */
    getEntries(): SessionEntry[];
    /**
     * The session as a tree. A well-formed session has exactly one root; orphaned
     * entries (broken parent chain) are returned as roots too.
     */
    getTree(): SessionTreeNode[];
    /**
     * Move the leaf to an earlier entry so the next append forms a new branch.
     * Existing entries are never modified or deleted.
     */
    branch(branchFromId: string): void;
    /** Reset the leaf to null so the next append creates a new root entry. */
    resetLeaf(): void;
    /** Like branch(), but also records a branch_summary of the abandoned path. */
    branchWithSummary(branchFromId: string | null, summary: string, details?: unknown, fromExtension?: boolean): string;
    /**
     * Create a new session file containing only the path from root to `leafId`.
     * Returns the new file path, or undefined when not persisting.
     */
    createBranchedSession(leafId: string): string | undefined;
    /** Resolve the canonical default session directory for a cwd. */
    static getDefaultSessionDir(cwd: string, agentDir?: string, storage?: SessionStorage): string;
    /**
     * Create a new session.
     * @param cwd Working directory (stored in the session header)
     * @param sessionDir Optional session directory; defaults to the cwd-derived dir.
     */
    static create(cwd: string, sessionDir?: string, storage?: SessionStorage): SessionManager;
    /**
     * Create a fresh empty session file in the default session directory for
     * `cwd`, writing only the session header. The returned path can be passed to
     * `setSessionFile` / `AgentSession.switchSession` when a caller explicitly
     * needs a brand-new persisted session at a cwd-derived path.
     */
    static createEmptySessionFile(cwd: string, storage?: SessionStorage): string;
    /**
     * Fork a session into the current project directory: copy history from another
     * session file while creating a fresh session file in this sessionDir.
     *
     * `options.sessionFile` pins the new session's file path (default: an
     * auto-named `<timestamp>_<id>.jsonl` in `sessionDir`). Callers that register
     * the fork as a named agent (e.g. `/tan`) pass `<agentId>.jsonl` so the
     * persisted-subagent scan keys the agent by the same id the live ref uses.
     */
    static forkFrom(sourcePath: string, cwd: string, sessionDir?: string, storage?: SessionStorage, options?: {
        suppressBreadcrumb?: boolean;
        sessionFile?: string;
    }): Promise<SessionManager>;
    /**
     * Open a specific session file.
     * @param sessionDir Optional dir for /new or /branch; defaults to the file's parent.
     * @param options.initialCwd Cwd to use when the file is empty or missing.
     */
    static open(filePath: string, sessionDir?: string, storage?: SessionStorage, options?: {
        initialCwd?: string;
        suppressBreadcrumb?: boolean;
    }): Promise<SessionManager>;
    /**
     * Lock-free peek for cold subagent revival: returns the recorded working
     * directory (session header) and the latest `session_init` contract (system
     * prompt / tools / output schema) WITHOUT taking the single-writer lock that
     * {@link open} acquires — the caller re-opens for the actual revive. Returns
     * null when the file can't be read; `init` is null for files written before
     * `session_init` was recorded (no faithful contract to rebuild from).
     */
    static peekSessionInit(filePath: string, storage?: SessionStorage): Promise<{
        cwd: string;
        init: {
            systemPrompt: string;
            task: string;
            tools: string[];
            outputSchema?: unknown;
            spawns?: string;
            readSummarize?: boolean;
        } | null;
    } | null>;
    /** Continue the most recent session, or create a new one if none exists. */
    static continueRecent(cwd: string, sessionDir?: string, storage?: SessionStorage): Promise<SessionManager>;
    /** Create an in-memory session (no file persistence). */
    static inMemory(cwd?: string, storage?: SessionStorage): SessionManager;
    /**
     * List sessions for a project directory.
     * @param sessionDir Optional dir; defaults to the cwd-derived dir.
     */
    static list(cwd: string, sessionDir?: string, storage?: SessionStorage): Promise<SessionInfo[]>;
    /** List all sessions across all project directories. */
    static listAll(storage?: SessionStorage): Promise<SessionInfo[]>;
}
/**
 * If the current session was created by `/move` and contains no real
 * user/assistant messages, delete it so empty move sessions don't accumulate.
 */
export declare function cleanupEmptyMoveSession(sessionManager: SessionManager, movedFromEmptySessionFile: string | undefined): Promise<void>;
export {};
