import { CDPClient } from './cdp.js';
import { type DeviceProbe } from './resolve-target.js';
import { type ResolvedDomainSkill } from './domain-skills.js';
import { type Task, type TabInfo, type ProfileStatus, type BrowserType, type HistoricalTask, type ReapResult, type ProfileName, type ConnectionKey } from './types.js';
import { type ReapOptions } from './hygiene.js';
import { type RefOpts, type RefNode } from './refs.js';
import type { TargetFilter } from './types.js';
export type UploadMode = 'auto' | 'input' | 'drop' | 'chooser';
export declare function resolveScreenshotOutputPath(outputPath: string | undefined, automaticPath: string): string;
/**
 * Parse a `targetFilter` string into its kind + value, or return `null`
 * when the input is missing or malformed. Filter syntax:
 *   - `url:<substring>`   — picks the first page target whose URL contains the substring
 *   - `title:<substring>` — picks the first page target whose title contains the substring
 *
 * The match is case-insensitive on both sides because Electron apps
 * frequently lowercase or title-case their target metadata in unpredictable ways.
 */
export declare function parseTargetFilter(filter: string | undefined): TargetFilter | null;
/**
 * Choose the CDP page target that represents the visible UI.
 *
 * Order:
 *   1. If `filter` is set and parseable, narrow to page targets matching it
 *      (case-insensitive substring). Among matches, prefer one that is not in
 *      `INVISIBLE_URL_PATTERNS` — this is the tiebreaker that makes a coarse
 *      filter like `url:https://www.canva.com/` skip the background service
 *      (`https://www.canva.com/_desktop-background-service` *also* matches the
 *      substring). If every match is invisible, return the first match so the
 *      caller still gets something rather than silently falling through.
 *      An explicit filter that finds *no* match returns `undefined` — callers
 *      should surface this as an error rather than create an orphan window.
 *   2. If `filter` is unset (or unparseable), apply the skip-invisible heuristic
 *      across all page targets.
 *   3. As a last resort, return the first page target.
 */
export declare function pickWindowTarget<T extends {
    type: string;
    url?: string;
    title?: string;
}>(targets: T[], filter: string | undefined): T | undefined;
/**
 * Parse a `--since`/`--until` value. Accepts ISO-8601 absolute timestamps
 * or relative offsets like `30s`, `5m`, `2h`, `1d`.
 */
export declare function parseSinceUntil(s: string): Date;
export declare function readNewestMatchingRemoteFileCommand(dir: string, prefix: string, tailLines: number): string;
export declare function readNewestMatchingFile(dir: string, prefix: string, tailLines: number): string;
interface ProfileConnection {
    cdp: CDPClient;
    port: number;
    pid: number;
    electron?: boolean;
    /**
     * The profile's declared browser family. Load-bearing for Arc: Arc answers
     * `Browser.getVersion` and DOES expose CDP page targets it honors `Page.navigate`
     * on, but CRASHES on `Target.createTarget` (verified live, PR #2778), so any
     * tab-CREATING path must refuse and drive an EXISTING tab instead rather than
     * crash the user's Arc. See `createPageTarget` / `pickReusableTargetWithoutCreate`.
     */
    browserType?: BrowserType;
    /** Raw `url:<v>` / `title:<v>` filter copied from the profile config. */
    targetFilter?: string;
    /**
     * The runtime key this connection is registered under in `this.connections`
     * (`<profile>@<endpoint>`, plus `.<fork>` for an Electron fork; a bare name
     * for a legacy pre-composite dir). Also keys the profile's runtime dir, so
     * downloads and session captures land under `.cache/browser/<key>/`.
     */
    key?: ConnectionKey;
    /** The bare, user-facing profile name this connection belongs to. */
    profile?: ProfileName;
    forkedFrom?: ConnectionKey;
    tasks: Map<string, Task>;
    windowId?: string;
    targetCache?: {
        targets: TargetInfo[];
        ts: number;
    };
    sessionCache: Map<string, string>;
    /**
     * Connection-specific teardown (e.g. killing the SSH tunnel for an ssh://
     * profile). Must be called whenever the connection is removed from
     * `this.connections`, otherwise the tunnel leaks across daemon restarts
     * and hijacks future `cdp://127.0.0.1:N` profiles on the same local port.
     */
    cleanup?: () => void;
}
/** Join error lines so callers get a next command, not a dead-end message. */
export declare function actionable(...lines: string[]): string;
/**
 * Arc DOES expose CDP page targets and honors `Page.navigate` on the ones the
 * user already has open (measured live against Arc: 33 targets, PR #2786), so
 * agents browser drives an attached Arc by reusing existing tabs. What it CANNOT
 * survive is `Target.createTarget` — opening a brand-new tab crashes the user's
 * Arc window (verified live, PR #2778). Tab creation is the one CDP-only op that
 * fails clearly here (PHNX-2399): every tab-creating path throws this actionable
 * error instead of crashing Arc, and steers the caller to reuse an existing
 * tab/Space (via `--target-filter`) or to a Chromium-family browser for new tabs.
 */
export declare function arcNotDrivableError(profileName?: string): Error;
/** Derive a human label from an explicit title or a navigated URL. */
export declare function deriveTaskLabel(opts: {
    title?: string;
    url?: string;
    existing?: string;
}): string;
type TargetInfo = {
    targetId: string;
    url?: string;
    title?: string;
};
/** Describes a ref that was re-resolved from a drifted integer via its cached descriptor. */
export interface HealInfo {
    from: number;
    to: number;
    role: string;
    name: string;
}
/**
 * Resolve the identity stamped on a task at start: WHO (`owner`) and WHICH run
 * (`launchId`). The forwarded values come from the caller's own CLI process and
 * are authoritative — the browser daemon is shared and long-lived, so resolving
 * the actor daemon-side (the RUSH-2020 bug) mis-attributes every task to the
 * daemon's owner. `resolveLocalActor` is consulted ONLY when no actor was
 * forwarded (a CLI that predates the field, mid-rollout) — never to override a
 * forwarded one.
 */
export declare function resolveTaskIdentity(forwarded: {
    actor?: string;
    launchId?: string;
    sessionId?: string;
}, resolveLocalActor: () => string): {
    owner: string;
    launchId?: string;
    sessionId?: string;
};
export declare class BrowserService {
    private static readonly SOURCE_PREFIX;
    /**
     * Live browsers, keyed by {@link ConnectionKey} — NOT by profile name. The
     * branded key type is what makes `connections.get(someProfileName)` (the
     * RUSH-2709 miss that made `status --profile <name>` return empty for a
     * running `<name>@<device>`) fail to compile. Keys are `<profile>@<device>`
     * (legacy leftover `<profile>@endpoint-N` dirs are still recognized).
     */
    private connections;
    private forkingProfiles;
    private consoleLogs;
    private pageErrors;
    private networkRequests;
    private pendingDownloads;
    private enabledSessions;
    /** Profile -> when a `touchTask` last persisted tasks.json for it. */
    private lastTouchPersist;
    /** Coalescing window for the `touchTask` write. See {@link touchTask}. */
    private static readonly TOUCH_PERSIST_INTERVAL_MS;
    start(profileName: string, opts?: {
        taskName?: string;
        url?: string;
        endpointName?: string;
        skipDomainSkill?: boolean;
        /** Always open a new tab, skipping the abandoned-task reclaim. */
        fresh?: boolean;
        /** Caller identity, forwarded from the CLI (see IPCRequest.actor/launchId). */
        actor?: string;
        launchId?: string;
        /** Calling agent session, forwarded from the CLI (see IPCRequest.sessionId). */
        sessionId?: string;
        /** Whether the CALLER was dispatched here by a fleet `--device` hop. */
        fleetRemote?: boolean;
        /** Explicit human label (`--title`). */
        title?: string;
        /** Injected reachability probe — production uses ssh; tests pass a fake. */
        probe?: DeviceProbe;
    }): Promise<{
        task: string;
        name: string;
        tabId?: string;
        windowId?: string;
        /** BARE profile name — what the caller asked for, never the runtime key. */
        profile: ProfileName;
        /** Runtime key the task actually landed on (`<profile>@<device>`). */
        key: ConnectionKey;
        /** Device the daemon connected to. */
        device: string;
        /** Set when the daemon picked a remote declaring device. */
        picked?: string;
        skill?: ResolvedDomainSkill;
    }>;
    /**
     * Reclaim a live page already showing `url` from an ABANDONED task, instead
     * of opening a second copy of the same page (RUSH-2622). Returns its CDP
     * targetId, or undefined when there is nothing safe to reclaim.
     *
     * "Safe" is narrow on purpose, because both of the wider readings close a tab
     * somebody is using:
     *
     *   - An UNOWNED matching page is NOT taken. No task claims it, which most
     *     often means the user opened it themselves — and adopting it would make
     *     this task's `done` close the user's tab.
     *   - A page owned by a LIVE task is NOT taken. Stealing it would leave that
     *     agent's `screenshot`/`click` throwing "No tabs open for this task", its
     *     `navigate` silently opening the duplicate this feature exists to
     *     prevent, and any in-flight recording truncated when the new owner calls
     *     `done`.
     *
     * What is left is exactly the pile-up case: a page held by a task whose owner
     * is provably gone. Liveness uses the reaper's own predicate
     * (`taskOwnerIsGone`), so "dead" has one definition in this codebase rather
     * than two that can drift. A task the reaper cannot prove dead keeps its tab
     * and is closed later by the idle rule instead.
     *
     * Reclaiming transfers rather than shares: two tasks holding one targetId
     * means the first `done` closes the other's tab. `start --fresh` skips this
     * path entirely.
     *
     * URLs are compared canonically (`new URL(...).href`), so a requested
     * `https://example.com` matches the `https://example.com/` Chrome reports. A
     * page that has since redirected elsewhere simply does not match, and the
     * caller opens a tab as before.
     */
    /** Is this CDP target already registered to any task on the connection? */
    private targetIsClaimed;
    /**
     * The CDP targets a task may CLOSE — its own tabs minus any it merely
     * borrowed. A borrowed tab existed before the task did (see `Task.borrowedTabs`),
     * so closing it would remove something the task never opened.
     */
    private closeableTargetIds;
    /**
     * A page target that `navigate` may take over on a browser which cannot create
     * tabs (Arc). Deliberately narrow: only a tab ALREADY showing the requested URL
     * -- re-showing that document there is what the caller asked for -- or an empty
     * new-tab page. A target any task already owns is left alone. Note the honest
     * limit: reusing a matching tab still RELOADS it, discarding scroll position and
     * unsaved form state. Arc cannot open a tab at all, so some intrusion is
     * unavoidable to show the document; a reload is the smallest one, and the tab is
     * borrowed rather than owned so it is never closed.
     */
    private pickReusableTargetWithoutCreate;
    private adoptTabShowing;
    /**
     * Every live task across every connected profile — the reaper's input
     * (`hygiene.ts`). A read-only view: mutating a returned `Task` mutates the
     * daemon's live state, so callers only read it and act through `stop`.
     */
    listTasks(): Array<{
        profile: ConnectionKey;
        task: Task;
    }>;
    /**
     * Close tasks whose owning agent session is gone, or that have sat untouched
     * past the idle window. The entry point the daemon's periodic tick and
     * `agents browser prune` both call; the policy lives in `hygiene.ts`.
     */
    reapAbandoned(opts?: ReapOptions): Promise<ReapResult>;
    stop(taskName: string): Promise<{
        ok: boolean;
        profile?: string;
    }>;
    done(taskName: string): Promise<{
        ok: boolean;
        profile?: string;
    }>;
    stopProfile(profileRef: ProfileName | ConnectionKey, opts?: {
        fleetRemote?: boolean;
        actor?: string;
    }): Promise<void>;
    navigate(taskId: string, url: string, profileRef?: ProfileName | ConnectionKey): Promise<{
        tabId: string;
        url: string;
        created: boolean;
    }>;
    tabAdd(taskId: string, url: string, profileRef?: ProfileName | ConnectionKey): Promise<{
        tabId: string;
        url: string;
    }>;
    tabFocus(taskId: string, tabHint: string): Promise<{
        tabId: string;
    }>;
    tabList(taskId: string): Promise<Array<{
        id: string;
        url: string;
        title: string;
        current: boolean;
    }>>;
    private resolveTabHint;
    private resolveCurrentTab;
    private getCdpTargetId;
    tabs(taskId?: string, profileRef?: ProfileName | ConnectionKey): Promise<TabInfo[]>;
    tabClose(taskId: string, tabHint?: string): Promise<void>;
    evaluate(taskId: string, tabHint: string | undefined, expression: string): Promise<unknown>;
    screenshot(taskId: string, tabHint?: string, outputPath?: string, quality?: 'compressed' | 'raw'): Promise<{
        path: string;
        bytes: number;
        width: number;
        height: number;
    }>;
    /**
     * Export the current tab as a PDF via CDP `Page.printToPDF`. Reuses the
     * screenshot session + tab resolution so `--tab`, path sandboxing, and the
     * auto-path (`sessions/<task>/<ts>.pdf`) all behave identically to
     * `screenshot`. `printBackground: true` matches Chrome's default print flow
     * — without it, dark-mode pages render on a blank sheet.
     */
    printToPdf(taskId: string, tabHint?: string, outputPath?: string): Promise<{
        path: string;
        bytes: number;
    }>;
    private recordings;
    recordStart(taskId: string, tabHint?: string, opts?: {
        fps?: number;
        duration?: number;
        maxMb?: number;
    }): Promise<{
        path: string;
        fps: number;
        durationCapSec: number;
        maxMb: number;
    }>;
    recordStop(taskId: string, reason?: 'manual' | 'duration-cap' | 'size-cap'): Promise<{
        path: string;
        bytes: number;
        durationMs: number;
        reason: string;
    }>;
    recordStatus(taskId: string): Promise<{
        recording: boolean;
        path?: string;
        elapsedMs?: number;
    }>;
    refs(taskId: string, tabHint?: string, opts?: RefOpts): Promise<{
        refs: string;
        nodeMap: Map<number, RefNode>;
    }>;
    /** Record the last ref listing (descriptors + opts) for a tab into state. */
    private cacheRefDescriptors;
    /**
     * Re-resolve a caller-supplied ref against a freshly-built node map, healing
     * it back to the right element when the integer ref has drifted since the
     * cached `refs` listing. Shared by `click` and `type` so both self-heal
     * identically. Returns the ref to act on plus, when a heal occurred, the
     * {@link HealInfo} to surface. Throws when the cached element is gone.
     */
    private resolveHealedRef;
    click(taskId: string, ref: number, tabHint?: string): Promise<{
        healed?: HealInfo;
    }>;
    /**
     * Click raw viewport coordinates, bypassing ref resolution entirely. Backs
     * `browser click --at X,Y` — the escape hatch when the accessibility tree
     * exposes no usable ref (canvas apps, custom-drawn UI) and the caller has
     * located the target from a screenshot.
     */
    clickAt(taskId: string, x: number, y: number, tabHint?: string): Promise<void>;
    type(taskId: string, ref: number, text: string, tabHint?: string, clear?: boolean): Promise<void>;
    press(taskId: string, key: string, tabHint?: string): Promise<void>;
    hover(taskId: string, ref: number, tabHint?: string): Promise<void>;
    scroll(taskId: string, deltaX: number, deltaY: number, atX?: number, atY?: number, tabHint?: string): Promise<void>;
    upload(taskId: string, files: string[], options: {
        ref?: number;
        trigger?: number;
        mode?: UploadMode;
        tabHint?: string;
        timeout?: number;
    }): Promise<{
        mode: 'input' | 'drop' | 'chooser';
    }>;
    stageUpload(source: string): {
        path: string;
    };
    /**
     * Status for one profile (bare name) or every live profile.
     *
     * `profileName` is a {@link ProfileName} — a BARE name, never a runtime key.
     * Every candidate is selected by the one rule ({@link keyBelongsToProfile}),
     * and a scoped query that finds no live connection falls through to disk, so
     * `status --profile comet-local` reports the LIVE `comet-local@<device>`
     * (and leftover `comet-local@endpoint-0` dirs) instead of the empty list
     * it used to return (RUSH-2709).
     */
    status(profileRef?: ProfileName | ConnectionKey): Promise<ProfileStatus[]>;
    /**
     * Rebuild a profile's status from its runtime dirs when no live connection is
     * registered — a daemon that never connected this profile, or a browser
     * started by an earlier daemon.
     *
     * Takes a BARE profile name and walks every runtime key that belongs to it
     * ({@link listProfileCacheDirs}), including the legacy pre-composite dir named
     * exactly the profile.
     */
    private reconcileFromDisk;
    private profileStatusFromDisk;
    setViewport(taskId: string, width: number, height: number, options?: {
        mobile?: boolean;
        deviceScaleFactor?: number;
        tabHint?: string;
    }): Promise<void>;
    setDevice(taskId: string, deviceName: string, tabHint?: string): Promise<void>;
    private enableRuntimeForSession;
    getConsoleLogs(taskId: string, options?: {
        level?: string;
        clear?: boolean;
        tabHint?: string;
    }): Promise<import('./types.js').ConsoleEntry[]>;
    getErrors(taskId: string, options?: {
        clear?: boolean;
        tabHint?: string;
    }): Promise<import('./types.js').ErrorEntry[]>;
    private enableNetworkForSession;
    getNetworkRequests(taskId: string, options?: {
        filter?: string;
        clear?: boolean;
        tabHint?: string;
    }): Promise<import('./types.js').NetworkRequest[]>;
    getResponseBody(taskId: string, urlPattern: string, options?: {
        timeout?: number;
        maxChars?: number;
        tabHint?: string;
    }): Promise<string>;
    getAppLogs(taskId: string, opts: {
        lines?: number;
        level?: string;
        filter?: string;
        message?: string;
        source?: string;
        since?: string;
        until?: string;
    }): Promise<any[]>;
    wait(taskId: string, type: 'time' | 'selector' | 'url' | 'function' | 'load', value: string | number, options?: {
        timeout?: number;
        tabHint?: string;
    }): Promise<void>;
    /**
     * Point the browser's default download destination at the profile's downloads
     * dir, browser-global, at connect time. Without this a download the agent never
     * explicitly routed (`browser download --path`) falls to Chromium's own default
     * — for an attached user browser, wherever that browser was last configured,
     * which is how downloads used to escape into random locations. Sent on the root
     * session (no sessionId) so every current and future tab inherits it. Best
     * effort: a remote CDP endpoint that doesn't expose the Browser domain must not
     * fail the whole connect.
     */
    private applyDefaultDownloadBehavior;
    setDownloadPath(taskId: string, downloadPath?: string, tabHint?: string): Promise<string>;
    waitForDownload(taskId: string, timeout?: number): Promise<string>;
    private findTaskBySession;
    shutdown(): Promise<void>;
    private findAvailableFork;
    private forkElectronProfile;
    /**
     * Connect to a profile at a specific endpoint preset. The caller has
     * already resolved the endpoint and built the `effectiveProfile` with
     * the per-endpoint binary/targetFilter overrides applied; we just use it.
     *
     * `key` is the runtime key (`<profile>@<device>`) and is what every on-disk
     * lookup uses. It is a SEPARATE argument because `effectiveProfile.name`
     * stays the bare, user-facing name (RUSH-2709).
     */
    private connectProfile;
    private connectEndpoint;
    private enableDomains;
    /**
     * The single path that opens a new CDP page/window target. It exists so the
     * Arc guard lives in exactly one place: `Target.createTarget` crashes Arc
     * (verified, PR #2778), so an Arc connection throws a clear, actionable error
     * here instead of every call site re-checking. Every drivable Chromium-family
     * browser goes straight through.
     */
    private createPageTarget;
    private getOrCreateWindow;
    private hasTaskNamed;
    /** Map key for a task on a connection (tasks are keyed by `name`). */
    private taskMapKey;
    /** Find a task by map key, id, or name on one connection. */
    private lookupTaskOnConn;
    private maybeUpdateLabelFromUrl;
    /**
     * Resolve a task for a page/close verb.
     *
     *   - explicit `task` handle → look up (with disk rehydrate on RAM miss)
     *   - no handle, exactly one live task for this caller → use it
     *   - no handle, multiple for this caller → error listing them + `--task`
     *   - no handle, zero for this caller, createIfMissing → start a new task
     *   - no handle, zero, !createIfMissing → return null (caller reports "nothing to close")
     */
    resolveOrCreateTask(opts: {
        /** Whether the CALLER was dispatched here by a fleet `--device` hop. */
        fleetRemote?: boolean;
        task?: string;
        profile?: string;
        actor?: string;
        launchId?: string;
        sessionId?: string;
        createIfMissing: boolean;
        title?: string;
        url?: string;
    }): Promise<{
        conn: ProfileConnection;
        task: Task;
        key: ConnectionKey;
        created: boolean;
        picked?: string;
        device?: string;
    } | null>;
    private listTasksForCaller;
    private currentUrlHint;
    /**
     * Mark a task as active, so the reaper's idle window measures time since the
     * last real use rather than time since `start` (RUSH-2622).
     *
     * Called from `findTask` — the single funnel every task-scoped operation
     * resolves through — rather than from each of the ~26 call sites, so a new
     * action added later cannot forget to stamp it.
     *
     * The in-memory stamp is what the reaper reads: it runs inside the same
     * daemon that owns these `Task` objects. The write to tasks.json exists only
     * so a daemon RESTART does not inherit a stale stamp and reap a task an agent
     * has been clicking through for the last half hour — `click`, `type`,
     * `evaluate`, and `screenshot` never call `saveTaskState` on their own, so
     * without this the on-disk stamp would sit at the last navigation. It is
     * coalesced to at most one write per profile per minute so a screenshot loop
     * does not become a write loop; the resulting on-disk stamp trails by at most
     * a minute, well inside the 30-minute idle window.
     */
    private touchTask;
    private findTask;
    /**
     * Soft-attach to an already-running browser for a runtime dir.
     *
     * Never launches a browser and never clears pid/port files on failure —
     * `connectProfile` does both, which made `status()` wipe disk state when
     * CDP was unreachable (CI / post-reaper) and then return an empty list.
     * Returns null when nothing is speaking CDP so callers can fall through
     * to disk reconcile.
     */
    private attachRunningProfile;
    /**
     * Reconnect every profile runtime dir that still has a live browser and
     * tasks.json entries. Used before identity-based resolution so a daemon
     * restart does not make the caller's tasks invisible.
     */
    /**
     * Register a freshly-rehydrated connection, unless a concurrent rehydrate won
     * the race and already registered one for this key. `attachRunningProfile`
     * awaits an ssh-tunnel spawn / CDP connect, so two commands landing right
     * after a daemon restart can each build a connection for the same key; the
     * loser must be released instead of silently overwriting the winner (whose
     * `cleanup` would then never run).
     *
     * The subtlety is the SSH tunnel: when the loser's `connectSSH` landed after
     * the winner's tunnel had already bound the local port, `isOwnTunnel` makes it
     * **reuse** that same OS process (`drivers/ssh.ts`) — so both connections carry
     * the same `pid`/`port`. Calling the loser's `cleanup()` then does
     * `tunnel.kill()` on the SHARED process and breaks the winner's live CDP. So
     * when the loser shares the winner's tunnel we close ONLY the loser's own CDP
     * socket and leave the tunnel to the winner; only a loser that spawned its own
     * distinct tunnel gets the full `cleanup()` (else it would leak). Returns the
     * winning connection to use.
     */
    private registerRehydratedConnection;
    private rehydrateAllFromDisk;
    /**
     * On a RAM miss, scan profile runtime dirs for a tasks.json entry matching
     * `taskId`, soft-attach when CDP is still up, and register the connection.
     * Does not launch browsers or clear runtime files.
     */
    private rehydrateTaskFromDisk;
    private getTabsForTask;
    /** Live status for ONE registered connection, addressed by its runtime key. */
    private getProfileStatus;
    private getTarget;
    private getSessionId;
    private invalidateTargetCache;
    private saveTaskState;
    private loadTaskState;
    private saveToHistory;
    /**
     * The cached connection for `key`, or undefined — evicting it first if the
     * browser died underneath us.
     *
     * A browser killed externally (Cmd-Q, crash, a user clearing the profile by
     * hand) leaves a closed WebSocket in the map. Without this the next
     * `cdp.send` throws "CDP connection not open" and there is no recovery short
     * of killing the daemon.
     */
    private reuseHealthyConnection;
    /**
     * Reuse a live connection for this profile: the new `<name>@<device>` key
     * first, then any leftover `<name>@endpoint-N` key still in the map so a
     * browser started before the key collapse is not abandoned.
     */
    private reuseProfileConnection;
    /** Connect a profile at `target`, register it under `key`, and arm downloads. */
    private openConnection;
    /**
     * Open a tab for a HUMAN to read, bound to no task.
     *
     * That is the entire difference from {@link start}, and it is the point. The
     * abandoned-task reaper closes a task's tabs when the calling session dies or
     * after the idle window, but it "deliberately never touches a tab that is not
     * in `task.tabs`" (hygiene.ts) — so a viewer tab must not be in one, or the
     * artifact the user is reading vanishes when the agent that rendered it exits.
     *
     * Consequently `stop`/`done` do not close it either, which is correct: it is
     * the user's tab now.
     */
    showUrl(profileName: ProfileName, url: string, opts?: {
        endpointName?: string;
        fleetRemote?: boolean;
        actor?: string;
        probe?: DeviceProbe;
    }): Promise<{
        profile: ProfileName;
        key: ConnectionKey;
        tabId: string;
        device: string;
        picked?: string;
    }>;
    getHistory(limit?: number): Promise<HistoricalTask[]>;
}
export {};
