import type { SessionEvent, SessionMeta, TodoProgress } from '../lib/session/types.js';
import { fetchPeerPreviewDigest } from '../lib/session/remote-list.js';
import { classifyFileChanges, changeCounts, toolHistogram, detectTestResult } from '../lib/session/digest.js';
import type { FileChange } from '../lib/session/digest.js';
import { extractArtifacts, extractHooks, extractLinks, extractSkills } from '../lib/session/highlights.js';
/** A session whose transcript FILE is on another machine: its `filePath` is on
 * that peer's disk, so the preview can't parse it locally — it fetches the
 * peer's digest over SSH and shows metadata + a "resume there" note until that
 * lands. `_remote` is an explicit signal; a peer `machine` plus no readable
 * local file covers host-dispatch index rows and live-registry rows that were
 * synthesized outside the fan-out. A locally-readable synced mirror stays
 * local even when its recorded owner is another machine.
 *
 * This is the READ rule and only the read rule. Whether a session may be
 * RESUMED here is a different question with a different answer — a mirror is
 * readable but not resumable — and `sessionOwnerDevice`
 * (lib/session/resume-owner.ts) is the single place that answers it (RUSH-2022). */
export declare function transcriptOnPeerOf(session: SessionMeta): string | undefined;
/**
 * Compact checklist tally for list rows and previews (RUSH-2045).
 * Example: `✓6/8 · A5 wiring runner`. Empty string when there is no list.
 * Consumes `SessionMeta.todos` / `ActiveSession.todos` as populated by the
 * state engine — does not re-parse transcripts.
 */
export declare function formatTodoCompact(todos?: Pick<TodoProgress, 'done' | 'total' | 'activeForm'> | null): string;
/**
 * Best-effort GitHub repo URL from a checkout path shaped like
 * `…/github.com/<owner>/<repo>/…`. Used to make the project name clickable
 * when no Linear project URL is available.
 */
export declare function githubRepoUrlFromCwd(cwd?: string): string | undefined;
export interface PickedSession {
    session: SessionMeta;
    action: 'resume' | 'view';
}
export interface SessionPickerConfig {
    message: string;
    /** Dim hint line shown under the header (filters/flags tip). */
    subtitle?: string;
    sessions: SessionMeta[];
    filter: (query: string) => SessionMeta[];
    labelFor: (s: SessionMeta, query: string) => string;
    pageSize?: number;
    initialSearch?: string;
    /** Verb shown on the Enter key in the footer (default 'resume'). */
    enterHint?: string;
    /** Lines the caller printed above the prompt (hidden-session footer). */
    linesAbovePrompt?: number;
}
export declare function setRemotePreviewRepaint(repaint?: () => void): void;
export declare function setPeerDigestFetcherForTest(fetcher?: typeof fetchPeerPreviewDigest): void;
export declare function clearRemoteDigestCacheForTest(): void;
/**
 * Validate + scrub a peer-supplied preview digest before any of it reaches this
 * terminal. Peer JSON is untrusted at this boundary — the same rule
 * `sanitizeMeta` applies to fan-out rows — so every string is stripped of
 * terminal escapes and every list re-shaped field by field. Anything that
 * doesn't look like the v1 digest is rejected, so a version-skewed peer
 * degrades to the metadata card instead of a corrupted pane.
 */
export declare function sanitizeRemoteDigest(raw: unknown): SessionPreviewDigest | undefined;
export declare function clearPreviewMemoryCacheForTest(): void;
export declare function loadSessionPreviewDigest(session: SessionMeta): {
    digest?: SessionPreviewDigest;
    events: SessionEvent[];
    error?: string;
};
/** Build a cached multi-line preview string for display in the session picker. */
export declare function buildPreview(session: SessionMeta): string;
/**
 * Body lines available from SessionMeta alone (no transcript parse) — used for
 * remote / unindexed sessions so checklist progress still surfaces when the
 * parser teammate (or a prior scan) has populated `session.todos`.
 */
/**
 * The session's place in a team, from whichever end it sits at: the orchestrator
 * that ran `agents teams create` (from the scan-derived `spawnedTeam`), or a
 * teammate (from its `meta.json`, via `classifyTeamSession`). Empty for a session
 * with no team involvement, which is the overwhelming majority.
 *
 * Deliberately no live teammate counts: tallying a team means reading every
 * record under the teams-agents dir, which runs to thousands of files on a busy
 * machine — far too much for a pane that repaints as the cursor moves. The line
 * names the command that does report them instead.
 */
/**
 * The "what did this session leave running" segments of the Doing line.
 *
 * Prefers a DERIVED count (fresh, from the transcript the caller just parsed)
 * and falls back to the PERSISTED column, which is what lets a remote/unindexed
 * row — rendered from `SessionMeta` alone, with no events — show fan-out at all.
 * That fallback is the whole reason the counts are columns (RUSH-3091/3095).
 *
 * A count of 0 or undefined renders NOTHING. Both are real states — "scanned,
 * none found" and "this harness cannot report it" — and a literal "0 background
 * shells" would assert "nothing is running" for a harness that simply does not
 * record them.
 *
 * Wording is "left behind", not "running": a transcript records a start and
 * never a death (see extractBackgroundShells).
 */
export declare function formatFanOut(session: SessionMeta, derived?: {
    subAgentCount?: number;
    backgroundShellCount?: number;
}): string[];
export declare function formatTeamLineage(session: SessionMeta): string;
/**
 * The three timing facts the header reports: when the session was created, when
 * it was last active, and how long it ran. Reads the parsed transcript when
 * there is one and otherwise the indexed `SessionMeta`, so a remote or
 * unindexed session — which has no local transcript to parse — still reports
 * them instead of silently dropping the whole line.
 *
 * `lastActive` and `lasted` are omitted for a session whose whole life was under
 * a minute: there they just restate `created`.
 */
export declare function extractTiming(session: Pick<SessionMeta, 'timestamp' | 'lastActivity' | 'durationMs'>, events: SessionEvent[]): {
    createdAgo?: string;
    lastActiveAgo?: string;
    duration?: string;
};
export interface SessionPreviewDigest {
    schemaVersion: 1;
    firstUser: string;
    lastAssistant: string;
    filesRead: number;
    toolCalls: number;
    planFile: string;
    todos?: TodoProgress;
    subAgentCount: number;
    /** undefined when the harness records no background-shell concept. */
    backgroundShellCount?: number;
    toolTags: string[];
    changes: ReturnType<typeof changeCounts>;
    /**
     * The per-file source paths behind `changes`. `changes` keeps the roll-up
     * counts every existing consumer already reads; `changedFiles` carries the
     * real path + op the CLI computed at scan time (via classifyFileChanges) and
     * used to discard — kept so a consumer that renders a per-file diff list (the
     * AGI EXT Fleet detail panel, PHNX-2973) has the paths, not just the totals.
     * Capped so a session that rewrote thousands of files can't bloat the cached
     * digest / JSON payload; the `changes` counts stay the true totals.
     */
    changedFiles: FileChange[];
    dirs: string[];
    repos: string[];
    artifacts: ReturnType<typeof extractArtifacts>;
    skills: ReturnType<typeof extractSkills>;
    plugins: string[];
    hooks: ReturnType<typeof extractHooks>;
    links: ReturnType<typeof extractLinks>;
    errorCount: number;
    firstError?: string;
    toolHistogram: ReturnType<typeof toolHistogram>;
    test: ReturnType<typeof detectTestResult>;
}
/** Fold a harness-normalized event stream into the stable preview data model. */
export declare function buildSessionPreviewDigest(events: SessionEvent[], session: SessionMeta): SessionPreviewDigest;
/**
 * Unique directories the session touched, compact and human-readable.
 * Prefer `session.recentDirectoriesTouched` — the scan records it on the row, so
 * it is present for remote rows whose transcript we can't parse here — otherwise
 * derive from the file-change + tool paths already available.
 */
export declare function directoriesTouched(session: SessionMeta, events: SessionEvent[], changes: ReturnType<typeof classifyFileChanges>): string[];
/** Relativize a file path to its parent dir, short enough for one preview line. */
export declare function relativizeDir(filePath: string, cwd?: string): string | undefined;
export declare function renderLastResponse(content: string, maxLines?: number, width?: number): string[];
/** Show an interactive session picker and return the selected session with its action (resume or view). */
export declare function sessionPicker(config: SessionPickerConfig): Promise<PickedSession | null>;
