/**
 * Interactive fleet-wide session browser — the human front-end of `agents sessions`.
 *
 * One canonical filter state (device / agent / project / window / running / teams),
 * driven by single-key hotkeys, re-pulling live over the same fleet fan-out the flag
 * surface uses. The identical state is expressible as flags (the agent front-end);
 * `y` / `--print-cmd` round-trips a hand-built view into a copy-pasteable command.
 *
 * Built on {@link dynamicPicker}; every data source (discover, fleet, live index,
 * preview, resume dispatch) is reused from the existing sessions plumbing.
 */
import { type SessionMeta } from '../lib/session/types.js';
import type { ActiveSession } from '../lib/session/active.js';
import { type LiveStatusFilter } from './sessions.js';
/**
 * The single canonical filter state. Every field has a flag equivalent, so the
 * same view is reachable interactively (hotkeys) or from the command line (flags).
 */
export interface BrowserFilter {
    /** running-only — the `R` key / `--active`. */
    running: boolean;
    /** include team-spawned sessions — the `C` key / `--teams`. */
    teams: boolean;
    /** filter to one agent, or all — the `A` key / `-a`. */
    agent?: string;
    /** filter to one machine, or all — the `D` key / `--device`. */
    device?: string;
    /** filter to one team's lineage, or all — the `T` key / `--in-team`. */
    team?: string;
    /** bookmarked-only — the `b` key / `--bookmarks`. */
    bookmarks: boolean;
    /** this-repo subtree vs every directory — the `P` key / `--all`. */
    projectScope: 'repo' | 'all';
    /** time window (undefined = all time) — the `W` key / `--since`. */
    window?: string;
    /** individual live states; an OR-union and, like the CLI flags, running-only. */
    statuses: LiveStatusFilter[];
    /** named project scope, independent of the repo/all hotkey. */
    project?: string;
    /** upper time bound from --until. */
    until?: string;
    /** retain only routine-origin sessions, optionally narrowed to one routine. */
    routine: boolean | string;
    /** indexed resource filters shared by the flag and focus surfaces. */
    skill?: string;
    plugin?: string;
    /** discovery cap. */
    limit: number;
    /** include unmanaged native-home transcripts. */
    unmanaged: boolean;
    /** candidate order shared with --sort. */
    sort: 'timestamp' | 'cost' | 'duration';
}
/**
 * Complete a seed into the filter the picker actually runs on.
 *
 * Every optional field of {@link BrowserFilter} has to be named here, because the
 * seed is copied field-by-field and an omission is silent: the field is optional,
 * so the compiler says nothing and the browser just opens without that filter.
 * `team` was dropped exactly this way, which made `--in-team` a no-op on the
 * interactive path while the scope half of the same seed still applied — so the
 * view opened wide and all-time and looked like the flag had worked.
 *
 * Exported so a test can assert the FILTER, not just the seed; testing the seed
 * alone cannot see this class of bug.
 */
export declare function buildInitialFilter(initial: Partial<BrowserFilter>): BrowserFilter;
/** Return the next value in `[undefined, ...options]`, wrapping. */
export declare function cycle(current: string | undefined, options: string[]): string | undefined;
export declare function cycleWindow(current: string | undefined): string | undefined;
/**
 * Cheap client-side match for the `S` search — a plain substring test over a
 * row's visible fields. Deliberately NOT the FTS `filterSessionsByQuery`: that
 * runs a content-index scan per call, which is fine once over a pool but a
 * CPU sink when a picker calls it per-row on every keystroke.
 */
export declare function sessionMatchesQuery(s: SessionMeta, query: string): boolean;
/**
 * The canonical `ag sessions …` command for a filter state (+ optional search) —
 * the agent-facing twin of the interactive view. Shared by the `y` hotkey and
 * `--print-cmd`.
 */
export declare function browserFilterToArgv(f: BrowserFilter, query?: string): string[];
/** Normalize a `--device`/`--device` token (`alias`, `user@host`, `host.domain`) to
 * the canonical machine id the rows carry in `.machine`, so a flag seed matches
 * (the `d` hotkey already cycles canonical ids). Mirrors sessions.ts `hostToken`. */
export declare function normalizeDeviceSeed(host: string | undefined): string | undefined;
/**
 * The initial filter for the `--active` browser: fleet-wide (matches the static
 * `renderActiveSessions`, which has no project scoping — the `p` hotkey narrows to
 * this repo), running-only, with the device seed normalized and `--since` seeding
 * the window. Pure, so the routing call site is unit-testable.
 */
export declare function activeBrowserSeed(opts: {
    teams?: boolean;
    agent?: string;
    host?: string[];
    since?: string;
    all?: boolean;
    bookmarks?: boolean;
    routine?: boolean | string;
}): Partial<BrowserFilter>;
/**
 * The initial filter for the bare interactive listing: current-repo subtree by
 * default (matches the static overview's cwd scoping). `--all` sets every
 * non-status filter to its "all" value — every directory (project) AND all-time
 * (window) — so one flag maxes the view; `--since` still overrides the window and
 * `-a`/`--device` still narrow their axis.
 */
export declare function bareBrowserSeed(opts: {
    teams?: boolean;
    agent?: string;
    all?: boolean;
    since?: string;
    host?: string[];
    inTeam?: string;
    bookmarks?: boolean;
    routine?: boolean | string;
}): Partial<BrowserFilter>;
/** Build the static peer query. Keeping it pure pins the per-device alias
 * contract: latest/oldest travel unresolved and are resolved by each peer. */
export declare function remotePoolArgs(f: BrowserFilter, fixedFilters: boolean): string[];
/**
 * A live session's stable row key: its session id when the agent reported one,
 * else a per-machine handle so an id-less live session (a just-booted harness, a
 * cloud task) still gets exactly one row instead of being dropped. Cloud rows key
 * on the task id because they have no pid — keying them all on the machine alone
 * would collapse two cloud tasks into one row, the same silent-drop this whole
 * change exists to remove.
 */
export declare function liveRowKey(a: ActiveSession, self: string): string;
/** Index live sessions by {@link liveRowKey} — the join key between the live scan
 *  and the transcript pool. Id-carrying rows key on the session id, so a live row
 *  and its transcript row collapse to one. */
export declare function indexLiveRows(rows: ActiveSession[], self: string): Map<string, ActiveSession>;
/**
 * Project a live session onto the picker's row shape, for a session the transcript
 * pool doesn't carry — a peer's session, a transcript outside the current window,
 * or an agent that has not written one yet. `filePath` is the live transcript path
 * when the scan resolved one and empty otherwise, which is what
 * {@link handlePickedSession} keys "there is nothing to open yet" off.
 */
export declare function liveSessionToMeta(a: ActiveSession, self: string): SessionMeta;
/**
 * Fold the live scan INTO the transcript pool. The running filter used to be a
 * pure intersection (`pool ∩ live`), which meant a session had to already be in
 * the pool to be shown as running — so every session on another machine, and
 * every local one outside the pool's window, was invisible in the browser while
 * `--active --json` listed it. Live sessions the pool lacks are appended as their
 * own rows, then the whole set is grouped local-machine-first.
 */
export declare function mergeLiveIntoPool(rows: SessionMeta[], live: Map<string, ActiveSession>, self: string, includeUnindexed?: boolean): SessionMeta[];
/**
 * Whether to render the host-program column. It belongs to the running view
 * only, so this gates on the FILTER — not on the live index being populated.
 * `liveCache` outlives a toggle of the `r` hotkey, so testing `live` alone would
 * keep the column after running is turned back off, widening a plain transcript
 * listing that has no live rows to explain it.
 */
export declare function shouldShowHostColumn(f: BrowserFilter, live: Map<string, ActiveSession> | null, rows: SessionMeta[]): boolean;
/** Apply the cheap in-memory filters (agent / device / project / running / bookmarks). */
export declare function applyFilters(rows: SessionMeta[], live: Map<string, ActiveSession>, f: BrowserFilter, self: string, bookmarks: Set<string>): SessionMeta[];
/** One-shot form of the browser's canonical candidate pipeline. Focus uses this
 * instead of maintaining a second discovery/filter implementation. */
export declare function collectSessionCandidates(initial: Partial<BrowserFilter>, opts?: {
    local?: boolean;
    hosts?: string[];
    includeLive?: boolean;
}): Promise<{
    sessions: SessionMeta[];
    liveById: Map<string, ActiveSession>;
    self: string;
    unreachable: string[];
}>;
/**
 * Launch the interactive session browser. `initial` seeds the filter (e.g.
 * `{ running: true }` for `--active`). Resolves after the user resumes a session
 * or cancels — the picked row is dispatched through the shared resume/focus path.
 */
export declare function runSessionBrowser(initial?: Partial<BrowserFilter>, opts?: {
    local?: boolean;
    hosts?: string[];
}): Promise<void>;
