import { captureProcessStartTime } from '../platform/index.js';
import { AgentType } from './parsers.js';
/**
 * Compute the Lowest Common Ancestor (LCA) of multiple file paths.
 * Returns the deepest common directory shared by all paths.
 * Returns null if paths is empty or paths have no common ancestor (different roots).
 */
export declare function computePathLCA(paths: string[]): string | null;
/** Lifecycle status of a teammate process. */
export declare enum AgentStatus {
    PENDING = "pending",// staged with unresolved --after deps
    RUNNING = "running",
    COMPLETED = "completed",
    FAILED = "failed",
    STOPPED = "stopped"
}
/**
 * The statuses a teammate can never leave — its process has run and finished
 * (or been stopped). Everything else (pending, running) is still live work.
 *
 * This is the ONLY set that retention (cleanupOldAgents) may reap: a `pending`
 * teammate has not launched yet and a `running` one is doing work, so deleting
 * either is data loss. Treating "not running" as "completed" was the RUSH-2356
 * bug — it swept live `pending` `--after` teammates past the 50-record cap.
 */
export declare const TERMINAL_STATUSES: ReadonlySet<AgentStatus>;
/** True when a teammate has reached a terminal (completed/failed/stopped) status. */
export declare function isTerminalStatus(status: AgentStatus): boolean;
export type TeammateFailureStage = 'placement' | 'spawn' | 'execution' | 'dependency' | 'cloud';
/** Durable evidence observed at a concrete teammate lifecycle boundary. */
export interface TeammateFailure {
    stage: TeammateFailureStage;
    code: string;
    message: string;
    exit_code: number | null;
    retryable: boolean;
    observed_at: string;
}
/**
 * One remote teammate's liveness, resolved by a single host probe. Three states,
 * kept distinct on purpose (RUSH-2366):
 *   - alive=true                                → process still running.
 *   - exitFilePresent=true                      → the `.exit` sentinel exists;
 *     `exit` is its (possibly empty, mid-write) contents.
 *   - alive=false && !exitFilePresent  ("GONE") → the process is gone AND the
 *     wrapper never recorded a sentinel — it was killed / the box died. There is
 *     no exit code coming, so this MUST resolve terminal instead of "running
 *     forever". Collapsing GONE into the empty-`.exit` case is exactly the bug
 *     that left a dead `--device` teammate RUNNING indefinitely.
 */
export interface RemoteLivenessSnapshot {
    alive: boolean;
    exit: string | null;
    exitFilePresent: boolean;
}
/**
 * The per-teammate shell that emits `<id> <ALIVE|EXITED|GONE> <codeOrEmpty>`.
 * Shared by the batched prefetch (many teammates, one round-trip) and the
 * direct single-teammate probe, so both classify liveness identically.
 * `exitFile` is interpolated UNQUOTED so `$HOME` in the dispatch path expands on
 * the remote shell (shellQuote would defeat the `[ -f ]` test).
 */
export declare function remoteLivenessSnippet(id: string, exitFile: string, pid: number): string;
/** Parse one `<STATE> <codeOrEmpty>` reading into a snapshot. */
export declare function parseRemoteLivenessState(state: string, code: string | undefined): RemoteLivenessSnapshot;
/** Task type label for Software Factory workflows. Drives planner fan-out. Optional — teammates without a task_type work exactly as before. */
export type TaskType = 'plan' | 'implement' | 'test' | 'review' | 'bugfix' | 'docs';
export declare const VALID_TASK_TYPES: readonly TaskType[];
export type { AgentType } from './parsers.js';
/**
 * Wrap a teammate argv in a POSIX shell command that runs it and then records
 * the real exit code to `exitCodePath`. `echo $?` captures the status of the
 * preceding command, so the sentinel reflects the underlying CLI's exit code,
 * not the shell's. Single source of truth shared by launchProcess() and its
 * test. See reapProcess() for how the sentinel is consumed.
 */
export declare function buildSentinelCommand(cmd: string[], exitCodePath: string): string;
/**
 * Env for a locally-spawned teammate. Freezes ONE actor for the whole spawn
 * tree: actorEnv(resolveActor()) stamps AGENTS_ACTOR* onto the child env, so the
 * teammate's inner `agents run` reads it via inheritedActor and short-circuits
 * computeActor instead of re-resolving — every teammate under one orchestrator
 * shares the orchestrator's single frozen actor (see actor.ts). Precedence:
 * process env < actor < the teammate's --env overrides, so an explicit override
 * still wins. Single source of truth shared by launchProcess() and its test.
 */
export declare function buildTeammateSpawnEnv(envOverrides: Record<string, string> | null): NodeJS.ProcessEnv;
/**
 * Re-exported from `platform/process.ts`, which owns the one implementation.
 *
 * This module used to carry its own near-identical copy, and that copy had no
 * Windows branch — it fell through to `ps`, which does not exist there, so
 * `captureProcessStartTime` always returned null and the pid-reuse guard at
 * `stop()` was silently inert on Windows. That is exactly how `agents teams stop`
 * ends up SIGKILLing an unrelated process group once the OS recycles a pid.
 */
export { captureProcessStartTime };
/**
 * Reasoning-intensity knob. Passed through to `agents run --effort`, which
 * translates it into per-agent reasoning flags (claude --effort, codex
 * model_reasoning_effort override). Mode (plan/edit/full) is a separate knob.
 */
export type EffortLevel = 'low' | 'medium' | 'high' | 'xhigh' | 'max' | 'auto';
/**
 * Append {@link TEAMMATE_PR_POLICY} to a teammate prompt for every WRITE-capable
 * mode (all but plan, which is read-only and opens no PR). Exported so the CLOUD
 * dispatch path (`cloudDispatchOptions` in `commands/teams.ts`) applies the SAME
 * boundary this file's `buildRunArgv` applies to LOCAL and REMOTE teammates. A
 * cloud teammate is the case that needs it MOST: it runs in the provider's
 * sandbox, not the shared local version home, so it never inherits the
 * `merge-guard.sh` PreToolUse hook — the prompt policy is then its ONLY
 * self-merge layer. Routing every dispatch surface through one helper keeps that
 * parity from drifting (PHNX-3236).
 */
export declare function withTeammatePrPolicy(prompt: string, mode: string): string;
export declare const VALID_MODES: readonly ["plan", "edit", "auto", "skip", "full"];
type Mode = 'plan' | 'edit' | 'auto' | 'skip';
/** Resolve a mode string to a validated Mode, falling back to the given default. */
export declare function resolveMode(requestedMode: string | null | undefined, defaultMode?: Mode): Mode;
/**
 * Check whether the CLI binary for a given agent type is installed.
 * Returns [available, pathOrError].
 *
 * The agents-managed shims dir (`~/.agents/.cache/shims`) is the canonical
 * install location, so a shim there means installed regardless of the caller's
 * PATH. Non-interactive callers — the menu-bar helper, cron, CI — run with a
 * minimal launchd PATH that omits the shims dir; a bare PATH lookup false-flags
 * every shim-based CLI as "not installed". Check the shim first, PATH second
 * (for CLIs the user installed outside agents-cli).
 */
export declare function checkCliAvailable(agentType: AgentType): [boolean, string | null];
/** Check availability of all known agent CLIs. Returns a map of agent type to install status. */
export declare function checkAllClis(): Record<string, {
    installed: boolean;
    path: string | null;
    error: string | null;
}>;
/**
 * Advisory sign-in probe for a teammate's agent. Reads the account-global login
 * (no `home` → active config) via `getAccountInfo`. Deliberately best-effort:
 * sign-in detection is UNRELIABLE for opaque-credential agents (Kimi/Antigravity
 * store an OAuth/JWT with no email claim) and for keychain-probed agents, so a
 * `false` here is often a false negative. Callers must WARN and continue — never
 * block a team on this result. Never throws (returns false on any error).
 */
export declare function checkCliSignedIn(agentType: AgentType): Promise<boolean>;
/** Advisory sign-in status for a `teams doctor` row. */
export interface SignInAdvisory {
    /** true / false from the probe, or null when the agent isn't installed. */
    signedIn: boolean | null;
    /** Whether the agent is currently a running teammate. */
    running: boolean;
}
/**
 * Resolve the advisory sign-in status shown by `teams doctor`. An agent that is
 * currently RUNNING in a team is live proof it works, so it overrides a
 * (frequently false-negative) sign-in probe — doctor must never report a
 * working agent as logged out. Not installed → `signedIn: null` (nothing to
 * probe). Never flips the authoritative installed/ready column.
 */
export declare function resolveSignInAdvisory(installed: boolean, running: boolean, probeSignedIn: boolean): SignInAdvisory;
/** One row of `agents teams doctor --json` output. */
export interface TeamsDoctorEntry {
    installed: boolean;
    path: string | null;
    error: string | null;
    signedIn: boolean | null;
    running: boolean;
}
/**
 * Collect the same data `agents teams doctor` prints: per-agent install status,
 * launch health, and advisory sign-in state. Kept in one place so `agents doctor
 * --devices` can run it locally or compare it against remote JSON without
 * duplicating the probe logic.
 */
export declare function collectTeamsDoctorData(): Promise<Record<string, TeamsDoctorEntry>>;
/** Resolve and cache the base directory where teammate process data is stored. */
export declare function getAgentsDir(): Promise<string>;
/**
 * Represents a single teammate process within a team.
 *
 * Tracks process metadata (PID, status, timestamps), reads incremental
 * stdout events, persists state to disk as meta.json, and can be
 * reconstituted from disk via loadFromDisk().
 */
export declare class AgentProcess {
    agentId: string;
    taskName: string;
    agentType: AgentType;
    prompt: string;
    cwd: string | null;
    workspaceDir: string | null;
    mode: Mode;
    pid: number | null;
    startTime: string | null;
    status: AgentStatus;
    startedAt: Date;
    completedAt: Date | null;
    parentSessionId: string | null;
    actor: string | null;
    cloudSessionId: string | null;
    cloudProvider: string | null;
    prUrl: string | null;
    version: string | null;
    remoteSessionId: string | null;
    name: string | null;
    after: string[];
    effort: EffortLevel | null;
    model: string | null;
    profileName: string | null;
    envOverrides: Record<string, string> | null;
    taskType: TaskType | null;
    cloudRepo: string | null;
    cloudBranch: string | null;
    worktreeName: string | null;
    worktreePath: string | null;
    project: string | null;
    hostName: string | null;
    hostTarget: string | null;
    hostIdentityFile: string | null;
    repoPath: string | null;
    remotePid: number | null;
    remoteLog: string | null;
    remoteExit: string | null;
    failure: TeammateFailure | null;
    remoteLogOffset: number;
    remotePollSnapshot: RemoteLivenessSnapshot | null;
    private eventsCache;
    private lastReadPos;
    private baseDir;
    constructor(agentId: string, taskName: string, agentType: AgentType, prompt: string, cwd?: string | null, mode?: Mode, pid?: number | null, status?: AgentStatus, startedAt?: Date, completedAt?: Date | null, baseDir?: string | null, parentSessionId?: string | null, workspaceDir?: string | null, cloudSessionId?: string | null, cloudProvider?: string | null, prUrl?: string | null, version?: string | null, remoteSessionId?: string | null, name?: string | null, after?: string[], effort?: EffortLevel | null, model?: string | null, envOverrides?: Record<string, string> | null, taskType?: TaskType | null, cloudRepo?: string | null, cloudBranch?: string | null, worktreeName?: string | null, worktreePath?: string | null, profileName?: string | null);
    get isEditMode(): boolean;
    getAgentDir(): Promise<string>;
    /**
     * Dump the subset of state the Ledger sync hook needs. Keeps sync.ts
     * free of any teams-internal imports.
     */
    toSnapshot(): Promise<{
        agent_id: string;
        team_id: string;
        teammate_name: string | null;
        agent_type: string;
        task_type: string | null;
        status: string;
        started_at: string;
        completed_at: string | null;
        after: string[];
        cloud_provider: string | null;
        cloud_session_id: string | null;
        cloud_repo: string | null;
        cloud_branch: string | null;
        failure: TeammateFailure | null;
        agent_dir: string;
        cwd: string | null;
    }>;
    getStdoutPath(): Promise<string>;
    getMetaPath(): Promise<string>;
    /**
     * Path to the exit-code sentinel. The launcher wraps the teammate command in
     * a shell that writes the underlying CLI's `$?` here once it exits. Detached
     * teammates can't be wait()ed on by the parent, so this file is the only
     * durable record of the real exit status — see reapProcess().
     */
    getExitCodePath(): Promise<string>;
    toDict(): any;
    duration(): string | null;
    get events(): any[];
    /**
     * Return the latest timestamp we have seen in the agent's events.
     * Falls back to null when none are available.
     */
    private getLatestEventTime;
    /**
     * For a distributed (remote-host) teammate, pull NEW bytes of the host's log
     * into the LOCAL mirror the parser consumes, advance the remote offset, and
     * resolve terminal status from the remote `.exit` sentinel. Runs BEFORE the
     * local read in readNewEvents(), so the existing stream-json parse path then
     * runs unchanged over the freshly-mirrored bytes.
     *
     * Uses a per-wave batched snapshot (remotePollSnapshot) when the supervisor's
     * one-ssh-per-host pre-pass populated it; otherwise falls back to its own
     * round-trips so a bare `teams status`/`teams logs` is still correct.
     *
     * Only polls a teammate that is plausibly still RUNNING (RUSH-2118). Once a
     * remote teammate reaches a terminal status, the poll that resolved it already
     * mirrored the final log bytes and read the `.exit` sentinel in this SAME
     * function (delta pulled before the exit check below) — the underlying process
     * is gone and can never write more, so there is nothing left to fetch. Without
     * this guard every finished remote teammate still cost one ssh round-trip on
     * EVERY `--active`/`listAll` poll forever, which is what made `agents sessions
     * --active --local` take ~4.3s on a box with 30 completed teammates.
     */
    private syncRemoteMirror;
    /**
     * One-shot direct liveness probe for a single remote teammate — the fallback
     * used outside a batched supervisor wave (a bare `teams status`, `mgr.get()`
     * for `teams resume`). Returns null on a transient ssh failure so the caller
     * leaves the teammate RUNNING rather than reaping it on a dropped connection.
     */
    private probeRemoteLiveness;
    /** Reset the local stdout cursor for a newly truncated resume log. */
    resetLogReadPosition(): number;
    /** Restore the cursor when a resume transaction puts the prior log back. */
    restoreLogReadPosition(position: number): void;
    /**
     * @param opts.skipRemote A `--local` caller (RUSH-2118): never dial a
     *   remote-host teammate, not even a still-RUNNING one — report its
     *   last-persisted meta.json state as-is. A local-only query is by definition
     *   this-machine-only, so it must not issue an ssh round-trip at all.
     */
    readNewEvents(opts?: {
        skipRemote?: boolean;
    }): Promise<void>;
    /**
     * Truncate the local mirror to its trailing REMOTE_MIRROR_MAX_BYTES and reset
     * lastReadPos to the new (smaller) size so the parser doesn't re-read the kept
     * tail. Only trims when over the cap — a normal-length log is untouched.
     */
    private capMirrorToTail;
    /** Cap on the in-memory event backlog kept per remote teammate. */
    private static readonly REMOTE_EVENTS_MAX;
    /**
     * Drop the oldest cached events for a remote teammate once past the cap. The
     * status path only needs recent events (last N messages, recentToolCalls,
     * terminal status) and the getDelta cursor filters by timestamp, so a bounded
     * recent window preserves the digest while bounding the heap. Terminal status
     * is already latched onto `this.status`, so trimming can't lose it.
     */
    private capEventsCache;
    saveMeta(): Promise<void>;
    /**
     * Rename an unreadable meta.json out of the way so it stops silently
     * masquerading as "no record" (RUSH-2429). Before saveMeta() wrote atomically,
     * a process killed mid-write left a truncated, unparseable meta.json that
     * loadFromDisk() returned null for -- indistinguishable from ENOENT -- so
     * retention (loadExistingAgents/rescanFromDisk) never reaped it and
     * isWorktreeClaimed() (which reads meta.json directly, not through this
     * method) failed CLOSED on it forever: it scans every record and answers
     * "claimed" for every worktree name in every team the first time it cannot
     * read one. Quarantining removes meta.json so the NEXT read of this record
     * sees ENOENT (genuinely absent) instead of "unreadable" -- that is what lets
     * isWorktreeClaimed's fail-closed guard recover once the corrupt record is
     * gone, without weakening the guard itself for a record that is still
     * present-but-unreadable at decision time.
     *
     * Best-effort: if the rename itself fails (e.g. EACCES on the directory),
     * the record is left in place and the fail-closed guard keeps protecting
     * worktree removal -- quarantine only ever ADDS a recovery path.
     */
    private static quarantineCorruptMeta;
    static loadFromDisk(agentId: string, baseDir?: string | null): Promise<AgentProcess | null>;
    isProcessAlive(): boolean;
    /**
     * Read just the persisted status + completion time from meta.json, without
     * reconstructing the whole teammate. Returns null when there is no readable
     * record on disk. Used to detect that ANOTHER process (a `teams stop`, a
     * sibling supervisor) has already moved this teammate to a terminal status.
     */
    private readDiskStatus;
    /**
     * If this in-memory teammate is still non-terminal but disk already shows a
     * terminal status, adopt the disk state. Returns true when it did.
     *
     * This is the guard against the stale-manager race (RUSH-2366): a long-lived
     * supervisor holding a teammate as `running` must never re-persist that stale
     * `running` over a `stopped`/`failed`/`completed` another process just wrote
     * (e.g. an explicit `teams stop` in a separate CLI invocation). A terminal
     * status is a one-way latch, so disk-terminal always wins over memory-running.
     */
    private adoptDiskTerminalIfNewer;
    /**
     * @param opts.skipRemote A `--local` caller (RUSH-2118): a distributed
     *   teammate is never dialed — its in-memory state (already loaded from
     *   meta.json) stands as-is, no ssh, no re-save.
     */
    updateStatusFromProcess(opts?: {
        skipRemote?: boolean;
    }): Promise<void>;
    /**
     * Recover the teammate's exit status after its process is gone.
     *
     * The teammate is spawned detached + unref()'d (see launchProcess), so the
     * parent never gets the child's exit code from the OS. Instead the launcher
     * wraps the command in a shell that records `$?` to the exit-code sentinel.
     * This reads that file:
     *   - still alive            -> null (no verdict yet)
     *   - sentinel present       -> the real exit code (0 = success)
     *   - sentinel absent        -> 1 (the shell was killed before it could write
     *                                  it, e.g. SIGKILL on timeout/stop — a real
     *                                  failure)
     *
     * Returning a real code (not a hardcoded 1) is what lets agents whose stream
     * never emits a parsed terminal event — kimi, antigravity, droid — be marked
     * completed on success instead of falsely failed.
     */
    private reapProcess;
}
/**
 * Manages the full lifecycle of teammate agent processes.
 *
 * Handles spawning (with DAG dependency resolution), status polling,
 * stopping, and automatic cleanup of old agents. Maintains an in-memory
 * cache backed by on-disk meta.json files.
 */
/**
 * Callback used to dispatch a cloud-backed teammate when its --after deps
 * resolve. Teams.ts registers one via setCloudDispatcher() at startup; the
 * MCP server path leaves it null (cloud teammates aren't dispatched from MCP).
 */
export type CloudDispatchFn = (agent: AgentProcess) => Promise<{
    cloudSessionId: string;
}>;
interface ResumeLogTransaction {
    agent: AgentProcess;
    stdoutPath: string;
    backupPath: string;
    hadOriginal: boolean;
    previousReadPos: number;
}
export declare function beginResumeLogTransaction(agent: AgentProcess): Promise<ResumeLogTransaction>;
export declare function commitResumeLogTransaction(transaction: ResumeLogTransaction): Promise<void>;
export declare function terminateSpawnedProcess(pid: number): Promise<void>;
export declare class AgentManager {
    private agents;
    private maxAgents;
    private agentsDir;
    private filterByCwd;
    private cleanupAgeDays;
    private defaultMode;
    private initPromise;
    private cloudDispatcher;
    /**
     * A `--local` caller (RUSH-2118): every poll this manager issues skips the
     * ssh round-trip for a distributed (remote-host) teammate, reporting its
     * last-persisted meta.json state instead. Set once at construction so the
     * INITIAL load in doInitialize()/loadExistingAgents() — which polls every
     * teammate before listRunning()/listAll() ever run — honors it too.
     */
    private localOnly;
    private constructorAgentsDir;
    /**
     * One-shot memo of the last `validateAddPreconditions` result, so the
     * command-layer pre-worktree call and spawn()'s own call don't each pay a
     * full `listAll()` status refresh (a round of SSH probes on a `--device`
     * team). Consumed by the first matching call — see that method.
     */
    private validatedAdd;
    constructor(maxAgents?: number, agentsDir?: string | null, defaultMode?: Mode | null, filterByCwd?: string | null, cleanupAgeDays?: number, localOnly?: boolean);
    private initialize;
    private doInitialize;
    getDefaultMode(): Mode;
    /**
     * Register the callback used to dispatch cloud-backed teammates when their
     * --after deps resolve. Called once at CLI startup by `agents teams`.
     */
    setCloudDispatcher(fn: CloudDispatchFn | null): void;
    registerAgent(agent: AgentProcess): void;
    private failAgent;
    private deferAgent;
    /**
     * Scan the agents dir for meta.json files not already in the in-memory
     * cache and load them. Needed when another process (e.g. a Planner
     * teammate running `agents teams add`) creates new teammates while this
     * manager is alive — the supervisor loop calls this each wave so
     * dynamically-added teammates get picked up.
     *
     * For a teammate ALREADY cached, refreshes it only when disk has latched it
     * terminal while the cache still holds it non-terminal — the case where
     * another process (e.g. `agents teams stop` in a separate CLI invocation)
     * moved it to `stopped`/`failed` and this long-lived manager would otherwise
     * never see it and re-persist a stale `running` (RUSH-2366). A still-live
     * cached teammate is left untouched; updateStatusFromProcess() owns that path.
     */
    rescanFromDisk(): Promise<number>;
    private loadExistingAgents;
    /**
     * Validate an add's name uniqueness and `--after` dependency graph, without
     * any side effects. Throws a user-facing error on: a duplicate name, `--after`
     * without `--name`, an unknown dependency, or a cycle. Returns the cleaned
     * (whitespace-filtered) `after` list.
     *
     * Extracted from spawn() so the command layer can run it BEFORE creating a
     * worktree — a rejected add must not leave an orphan `agents/<name>` branch
     * that then breaks the retry with `fatal: a branch ... already exists`
     * (RUSH-2356). spawn() calls it too, so validation lives in exactly one place.
     *
     * The result is cached for exactly ONE subsequent call with the same
     * arguments, which spawn() then consumes. `listByTask()` → `listAll()`
     * refreshes every sibling's status, and on a `--device` team that is a full
     * round of SSH liveness probes — running it twice per `teams add` would
     * double that cost for no gain, since the second pass reads the same snapshot
     * and cannot catch anything the first missed. The cache is single-use so any
     * later spawn (a `teams start --watch` supervisor launching staged teammates)
     * still validates against fresh state and still rejects a duplicate name.
     */
    validateAddPreconditions(taskName: string, name: string | null, after: string[]): Promise<string[]>;
    /**
     * Does any LIVE teammate — in any team — already own `worktreeName`?
     *
     * A RAW disk scan: no status probing, no cache, no `listAll()`. The caller is
     * the `teams add` failure path, where the manager's own status refresh can be
     * the very thing that threw (`cleanupOldAgents()` → `listAll()` →
     * `updateStatusFromProcess()` runs AFTER the staged record is saved), so a
     * check that re-entered that machinery would throw again and answer nothing.
     *
     * `teams add` asks this before removing a worktree, to tell an ORPHAN from
     * someone's live checkout (RUSH-2356). Two deliberate scoping choices:
     *
     * - **Any team, not just the one being added to.** Worktree names are global
     *   to the repo but records are per-team, so a same-named worktree owned by
     *   another team's teammate must also block the removal.
     * - **Non-terminal records only.** A completed/failed/stopped teammate's
     *   worktree was already cleaned up at `teams stop`, and its record lingers
     *   until retention reaps it — counting those would leave a genuine orphan
     *   branch stranded forever, which is the bug this all exists to fix.
     * - **Fails CLOSED.** This guards a `git worktree remove --force`, so the two
     *   errors are not symmetric: a false "claimed" strands an orphan branch that
     *   a human can delete, while a false "unclaimed" deletes a live agent's
     *   checkout and its uncommitted work. Only `ENOENT` proves absence — no
     *   agents dir means no records, and a record with no `meta.json` is not a
     *   record. Any other failure (EACCES, EIO, half-written or invalid JSON,
     *   a race with a writer) means we could not READ the records, which is not
     *   the same as there being none, so it answers `true`. This is deliberate
     *   asymmetry, not defensive coding: the caller acts destructively on `false`.
     */
    isWorktreeClaimed(worktreeName: string): Promise<boolean>;
    spawn(taskName: string, agentType: AgentType, prompt: string, cwd?: string | null, mode?: Mode | null, effort?: EffortLevel, parentSessionId?: string | null, workspaceDir?: string | null, version?: string | null, name?: string | null, after?: string[], model?: string | null, envOverrides?: Record<string, string> | null, taskType?: TaskType | null, cloudProvider?: string | null, cloudSessionId?: string | null, cloudRepo?: string | null, cloudBranch?: string | null, worktreeName?: string | null, worktreePath?: string | null, profileName?: string | null, hostName?: string | null, hostTarget?: string | null, repoPath?: string | null, project?: string | null): Promise<AgentProcess>;
    /**
     * Resume a STOPPED teammate (completed / failed / stopped) by re-entering its
     * own session with `message` as the next user turn. Re-launches through the
     * SAME backend the teammate first used (local process or remote host), reusing
     * its stored cwd / worktree / host / version / model / effort, and flips it
     * back to RUNNING so the team tracks it live again.
     *
     * The resume target is the teammate's underlying agent session id: for Claude
     * that IS its agent_id (unified identity, pinned via --session-id at first
     * launch); other harnesses only expose their session/thread id after their
     * first stream event, captured as `remoteSessionId`.
     *
     * Callers branch on status first — a RUNNING teammate is steered via its
     * mailbox, never re-launched — so this method assumes a non-running teammate.
     */
    resumeTeammate(agentId: string, message: string): Promise<AgentProcess>;
    /**
     * Actually spawn the OS process for a teammate. Extracted from spawn() so
     * staged teammates can be launched later by startReady().
     */
    private launchProcess;
    /**
     * Dispatch a distributed teammate onto its host over SSH — the remote-host
     * analog of launchProcess(). Symmetric to the cloud path: no local process; the
     * lifecycle lives on the host and is polled (isProcessAlive/readNewEvents over
     * SSH via the remote `.exit` sentinel + offset-tailed log).
     *
     * When the team uses worktrees (agent.worktreeName set), a git worktree is first
     * created ON THE HOST off the freshly-fetched default branch; the teammate runs
     * there. Otherwise it runs in the host repo path directly.
     */
    private launchRemoteProcess;
    /**
     * Resolve a scheduler-picked device to host placement fields on an unpinned
     * teammate at LAUNCH time (the same resolution `teams add --device` runs, minus
     * the fatal `die()` — a scheduling failure here is per-teammate, not per-add).
     * Sets hostName/hostTarget/repoPath + persists, so the subsequent
     * launchRemoteProcess dispatches over SSH. Mirrors the `add`-time pin path:
     * resolve device → reject Windows (POSIX-only) → ssh target → ensure the repo
     * is present on the host from the team's --repo (ensureRemoteRepo).
     */
    private resolveScheduledPlacement;
    /**
     * Place an UNPINNED, non-cloud teammate onto the team pool via the cascade
     * (least-loaded). A poolless team consumes the active worker allowlist and
     * fails loud when none exists; it never silently lands on the orchestrator.
     * A no-op only for a pinned teammate (hostName already set from `--device`) or
     * a cloud teammate. Shared by spawn()
     * (immediate add-launch) and startReady() (staged launch) so an unpinned pool
     * teammate schedules identically no matter how it was fired.
     */
    private maybeSchedulePlacement;
    /** Human label of a teammate's agent for the placement fail-loud message. */
    private placementAgentLabel;
    /**
     * One-ssh-per-host batched liveness/exit pre-pass for a team's remote teammates.
     * The supervisor calls this each wave BEFORE listByTask() so the per-teammate
     * isProcessAlive()/readNewEvents() consume a cached snapshot instead of each
     * issuing its own SSH handshake — avoiding N round-trips per wave at 10+ remote
     * teammates. Groups by hostTarget and, for each host, checks every teammate's
     * `.exit` + `kill -0` in a single ssh call over the shared ControlMaster socket.
     */
    prefetchRemoteStatus(taskName: string): Promise<void>;
    /**
     * Fire any pending teammates in the given team whose `after` deps have all
     * completed. Returns the list of teammates just launched. Repeatable:
     * call it once per DAG wave. Safe to call on teams with no pending work
     * (returns empty list).
     */
    startReady(taskName: string): Promise<AgentProcess[]>;
    /**
     * Build the argv to spawn for a teammate. Delegates to `agents run` so the
     * agent's CLI flags, version routing, mode handling (plan/edit/full), model
     * injection, and reasoning-intensity flags are owned by a single canonical
     * exec path (src/lib/exec.ts). The team runner just supplies prompt + mode
     * and reads stream-json events off stdout.
     */
    /**
     * Build the `agents run …` argv AFTER the `agents` binary — the flags + prompt
     * scaffolding shared by the LOCAL launch (buildCommand, which prefixes
     * process.execPath + the agents CLI path) and the REMOTE launch
     * (launchRemoteProcess, which prefixes `agents` on the host via dispatch). Kept
     * in one place so the PROMPT_SUFFIX / CLAUDE_PLAN_MODE_PREFIX scaffolding and the
     * flag set can never drift between the two backends.
     *
     * `cwd` is intentionally NOT emitted here: the local path passes it as
     * `--cwd`/`--add-dir` (below), while the remote path `cd`s into the host cwd
     * before invoking `agents`. `sessionId` is likewise local-only (the remote run
     * mints its own session on the host).
     */
    private buildRunArgv;
    private buildCommand;
    get(agentId: string): Promise<AgentProcess | null>;
    /**
     * Resolve a teammate reference to a single agent_id within a team.
     * Accepts (in priority order):
     *   1. exact teammate name                ("alice")
     *   2. exact UUID                         ("b2438499-dc25-4a5e-9e02-9916012580b8")
     *   3. UUID prefix, if unique             ("b2438499")
     *
     * Returns:
     *  - { kind: 'ok', agentId }       when exactly one teammate matches
     *  - { kind: 'none' }              when nothing matches
     *  - { kind: 'ambiguous', matches } when the prefix matches multiple ids
     */
    resolveAgentIdInTask(taskName: string, ref: string): Promise<{
        kind: 'ok';
        agentId: string;
    } | {
        kind: 'none';
    } | {
        kind: 'ambiguous';
        matches: string[];
    }>;
    listAll(): Promise<AgentProcess[]>;
    listRunning(): Promise<AgentProcess[]>;
    /**
     * Teammates that have reached a terminal status (completed/failed/stopped) —
     * the ONLY records retention may reap. A `pending` teammate has not launched
     * and a `running` one is working, so neither is "completed"; classifying them
     * as such let cleanupOldAgents sweep live `pending` `--after` teammates past
     * the cap (RUSH-2356). Filter on `isTerminalStatus`, never `!== RUNNING`.
     */
    listCompleted(): Promise<AgentProcess[]>;
    listByTask(taskName: string): Promise<AgentProcess[]>;
    /**
     * Terminal removal of every teammate record for a team (RUSH-2450).
     *
     * `teams disband` used to delete log dirs and the registry entry, but left
     * the in-memory AgentProcess cache intact. A concurrent `teams start --watch`
     * supervisor then re-persisted those records via saveMeta, so a second
     * disband still found N logs to clear and `teams start` could re-launch
     * PENDING work that had already merged.
     *
     * This drops every matching record from the manager map AND removes its
     * durable state. With `keepLogs`, only `meta.json` is removed so the
     * teammate is no longer discoverable/startable while stdout/stderr logs
     * remain for postmortem.
     *
     * Returns the agent_ids that were purged.
     */
    purgeByTask(taskName: string, opts?: {
        keepLogs?: boolean;
    }): Promise<string[]>;
    listByParentSession(parentSessionId: string): Promise<AgentProcess[]>;
    stopByTask(taskName: string): Promise<{
        stopped: string[];
        alreadyStopped: string[];
    }>;
    stop(agentId: string): Promise<boolean>;
    private cleanupOldAgents;
}
