/**
 * TopicResumeMap — Persistent mapping from Telegram topic IDs to Claude session UUIDs.
 *
 * Before killing an idle interactive session, the system persists the Claude
 * session UUID so it can be resumed when the next message arrives on that topic.
 * This avoids cold-starting sessions (rebuilding context from topic history)
 * and provides seamless conversational continuity.
 *
 * Storage: {stateDir}/topic-resume-map.json
 * Entries auto-prune after 24 hours.
 */
/** §8 provenance tag — gates the none-loss claim (TOPIC-PROFILE-SPEC §7/§8). */
export type ResumeProvenance = 'hook' | 'mtime-fallback';
export interface ResumeEntry {
    uuid: string;
    savedAt: string;
    sessionName: string;
    /**
     * §8 framework tag. Untagged legacy entries are grandfathered as
     * 'claude-code' (provably safe — this map has only ever captured Claude
     * JSONL UUIDs) and tagged lazily on the next write.
     */
    framework?: string;
    /** Untagged legacy entries grandfather as 'hook' (same provable basis). */
    provenance?: ResumeProvenance;
    /**
     * §8 — "remove" means PARK, not delete: parked entries are ignored by
     * resolution (get() returns null) but recoverable by the §10.4 breaker
     * revert / §10.3 undo via unpark(). Holds the parking reason.
     */
    parked?: string | null;
}
/**
 * §8 resume-writer gate (TOPIC-PROFILE-SPEC round-3 adversarial): EVERY
 * writer — the beforeSessionKill listener, the 60s heartbeat
 * (refreshResumeMappings), the 8s post-spawn proactive save, and the
 * shutdown/refresh-route saves — funnels through save()/refreshResumeMappings,
 * so a single gate at this chokepoint covers all four structurally. The gate
 * refuses a write for a topic whose resolved profile framework is not
 * claude-code, or whose topic is under an active mid-framework-switch
 * suppression marker. Without it, the heartbeat would re-poison the map
 * within a minute of a framework switch.
 */
export type ResumeWriteGate = (topicId: number) => {
    allowed: boolean;
    reason?: string;
};
export declare class TopicResumeMap {
    private filePath;
    private projectDir;
    private tmuxPath;
    private writeGate;
    constructor(stateDir: string, projectDir: string, tmuxPath?: string);
    /**
     * Compute the Claude Code project directory name for this project.
     * Claude Code hashes the project path by replacing '/' with '-' and
     * stripping dots — e.g. /Users/foo/.bar/baz → -Users-foo--bar-baz
     */
    private claudeProjectDirName;
    /**
     * Get the full path to this project's Claude JSONL directory.
     */
    private claudeProjectJsonlDir;
    /**
     * Discover the Claude session UUID from the most recent JSONL file
     * in THIS project's .claude/projects/ directory.
     *
     * Scoped to the current project to avoid cross-project UUID contamination.
     */
    findClaudeSessionUuid(): string | null;
    /**
     * Find the Claude session UUID for a specific tmux session.
     *
     * Only uses the authoritative claudeSessionId from hook events.
     * The mtime-based heuristic was removed because it causes cross-topic
     * contamination when multiple sessions are active — it always picks
     * the most recent JSONL file regardless of which session it belongs to.
     */
    findUuidForSession(tmuxSession: string, claudeSessionId?: string): string | null;
    /**
     * Install the §8 profile write-gate (TOPIC-PROFILE-SPEC). All writers are
     * gated at this single chokepoint — the heartbeat, the kill listener, the
     * post-spawn save and the shutdown saves all land in save()/
     * refreshResumeMappings. No gate installed ⇒ today's behavior (ungated).
     */
    setWriteGate(gate: ResumeWriteGate | null): void;
    private gateAllows;
    /**
     * Persist a resume mapping before killing an idle session.
     * Entries are framework-tagged ('claude-code' — this map only ever holds
     * Claude JSONL UUIDs) and provenance-tagged (§8). A refused (gated) write
     * is a silent no-op — the gate's caller owns disclosure.
     */
    save(topicId: number, uuid: string, sessionName: string, provenance?: ResumeProvenance): void;
    /**
     * Look up a resume UUID for a topic. Returns null if not found,
     * expired, or the JSONL file no longer exists.
     */
    get(topicId: number): string | null;
    /**
     * §8 last-line guard: the spawn path REFUSES a resume id whose framework
     * tag mismatches the resolved framework, falling to CONTINUATION with
     * disclosure. Untagged legacy entries grandfather as 'claude-code'.
     */
    getForFramework(topicId: number, resolvedFramework: string): string | null;
    /**
     * §8 provenance read — the none-loss rows require HOOK provenance; an
     * mtime-fallback-only entry classifies as CONTINUATION-class loss.
     * Untagged legacy entries grandfather as 'hook' (provably safe).
     */
    getProvenance(topicId: number): ResumeProvenance | null;
    /** Raw entry (parked included) for the §8 park/un-park machinery. */
    getEntryRaw(topicId: number): ResumeEntry | null;
    /** §8 — "remove" means PARK, not delete (deletion destroys the cheap recovery). */
    park(topicId: number, reason: string): void;
    /** §10.4 revert / §10.3 undo — un-park the entry. Returns true when un-parked. */
    unpark(topicId: number): boolean;
    /**
     * Remove an entry after successful resume (prevents stale reuse).
     */
    remove(topicId: number): void;
    /**
     * Proactive resume heartbeat: update the topic→UUID mapping for all active
     * topic-linked sessions. Called periodically (e.g., every 60s).
     *
     * Uses authoritative Claude session IDs from hook events when available.
     * Only falls back to mtime-based JSONL scanning when there's exactly one
     * active session (no cross-topic contamination risk).
     *
     * @param topicSessions - Map of topicId → { sessionName, claudeSessionId? }
     */
    refreshResumeMappings(topicSessions: Map<number, {
        sessionName: string;
        claudeSessionId?: string;
    }>): void;
    private load;
    /**
     * Check if a JSONL file exists for the given UUID in this project's directory.
     * Public alias for use by the resume heartbeat (Slack channel resume writes).
     */
    jsonlExistsPublic(uuid: string): boolean;
    /**
     * Check if a JSONL file exists for the given UUID in this project's directory.
     */
    private jsonlExists;
}
//# sourceMappingURL=TopicResumeMap.d.ts.map