/**
 * Dispatch a headless `agents …` command onto a host over SSH.
 *
 * The command is launched detached (`nohup … &`) writing combined output to a
 * remote log and its exit code to a sibling `.exit` file, so progress survives a
 * dropped connection (followed via offset-tail in progress.ts). This is the
 * offload win: the process/thread/file fan-out happens on the host, not the
 * laptop. `agents run` uses it; `agents teams start --watch --device` reuses the
 * same core so a remote team supervisor keeps running after you disconnect.
 */
import type { Host } from './types.js';
import { type HostTask } from './tasks.js';
import { type HostCredentials } from './credentials.js';
import { deriveMirroredCwd, homeRemainder, remoteCdPrefix } from '../project-root.js';
export { deriveMirroredCwd, homeRemainder, remoteCdPrefix };
/**
 * Diagnostic helper for RUSH-2441: log the requested agent and initial remote
 * `agents run` argv without changing normal command output.
 */
export declare function logForwardedArgs(kind: string, agent: string, version: string | undefined, args: string[], hasPrompt: boolean): void;
/**
 * Merge the resolved actor's provenance env UNDER a caller-supplied env, so every
 * remote `agents …` invocation forwards `AGENTS_ACTOR*` / `GIT_*` across the SSH
 * hop. Without it the remote re-resolves the actor from the ORIGINATING box's
 * `SSH_CONNECTION` (the wrong IP) and mis-credits the run to the shared machine or
 * `UNRESOLVED@<host>` (RUSH-2028). A caller-supplied value wins on any key
 * collision, mirroring `buildExecEnv`'s `...options.env` precedence (exec.ts).
 */
export declare function withActorEnv(env?: Record<string, string>): Record<string, string>;
/**
 * The shell-export prelude prepended to EVERY remote `agents run` dispatch —
 * actor provenance plus, for a `run auto` dispatch, the chain-hop guard
 * (RUN_AUTO_HOST_RESOLVED_ENV): this dispatch already IS the affinity pick, so
 * the remote CLI must not re-run host affinity and hop to a third host. The
 * guard MUST be a shell export (landing in the remote CLI's own process.env,
 * which `runAutoDefaultsToAffinity` reads) — a forwarded `--env` flag would
 * only reach the spawned agent's env and the remote `run auto` would re-pick.
 * Shared by the interactive (runInteractiveOnHost) and detached
 * (launchDetached) paths so both behave identically.
 *
 * `extra` carries the markers that are true of ONE path rather than both — the
 * interactive dispatch adds REMOTE_INTERACTIVE_ENV, which the remote CLI reads
 * to know its stdio is an ssh link (reconnect target, `--no-follow` pane
 * requirement). It goes through this builder rather than being concatenated on
 * at the call site so every remote env marker is exported the same way, in one
 * place.
 */
export declare function remoteRunShellPrelude(agent: string, extra?: Record<string, string>): string;
/**
 * Launch a detached login-shell command in its own Unix session/process group.
 *
 * Node is already a hard requirement for a host that can run `agents`. Its
 * `detached: true` contract calls setsid(2) on Unix, unlike `nohup ... &` under
 * a non-interactive shell where the background wrapper can remain in the SSH
 * shell's process group. Returning the group leader PID makes `kill(-pid)` a
 * reliable whole-tree operation for both normal stops and rollback cleanup.
 */
export declare function buildDetachedLaunchCommand(inner: string): string;
/** Build the detached PowerShell launch protocol used by Windows SSH hosts. */
export declare function buildWindowsDetachedLaunchCommand(opts: {
    forwardedArgs: string[];
    remoteCwd?: string;
    mirrorCwd?: boolean;
    remoteLog: string;
    remoteExit: string;
    env: Record<string, string>;
}): string;
export interface DispatchResult {
    task: HostTask;
    /** Exit code when followed; undefined when detached (--no-follow). */
    exitCode?: number;
}
/** Terminate a detached dispatch that its caller could not persist locally. */
export declare function terminateDispatchedTask(task: HostTask): void;
/**
 * Build the remote shell used by {@link stopDispatchedTask}. Exported for
 * unit tests — the keep-log / no-clobber contract lives in this script.
 *
 * Protocol (printed to stdout for the local caller):
 * - `SIGNALED`  — process group was live; SIGTERM/KILL applied; wrote 143
 * - `ALREADY` + code — group gone; adopted existing `.exit` (never overwrite)
 * - `GONE` — group gone and no `.exit`; write 143 as the local stop outcome
 * Exit 1 if the group is still alive after TERM/KILL (can't stop it).
 */
export declare function buildStopRemoteCommand(pid: number, remoteExit: string): string;
export declare function buildWindowsStopRemoteCommand(pid: number, remoteExit: string): string;
/**
 * Stop a running host task from the origin machine (`agents devices stop <id>`).
 *
 * Unlike {@link terminateDispatchedTask} (rollback cleanup after a failed
 * persist), this keeps the remote log so `agents logs <id>` still works,
 * writes a terminal `.exit` marker only when we actually stopped a live group
 * (or no code existed), and never clobbers a real completed-run exit code.
 */
export declare function stopDispatchedTask(task: HostTask): HostTask;
export interface DispatchOptions {
    agent: string;
    prompt: string;
    /** Explicit agent version pin (e.g. "2.1.207") to forward as `agent@version`. */
    version?: string;
    /** Run strategy (e.g. "balanced") — the remote picks among ITS signed-in accounts. */
    strategy?: string;
    /** Named provider account — the remote resolves it against ITS signed-in versions. */
    account?: string;
    balanced?: boolean;
    fallback?: string;
    mode?: string;
    model?: string;
    /** Reasoning effort — forwarded unless 'auto' (the remote default). */
    effort?: string;
    /** `--env k=v` pairs, forwarded verbatim (the remote CLI parses them). */
    env?: string[];
    /** `--add-dir` grants, already made remote-portable. */
    addDir?: string[];
    /** Agent wall-clock cap, forwarded as `--timeout <duration>` for the REMOTE to enforce. */
    timeout?: string;
    /** Loop family — the loop driver runs on the host. */
    loop?: boolean;
    maxIterations?: string;
    budget?: string;
    until?: string;
    interval?: string;
    /** Remote emits ndjson into its log; the local follow streams it verbatim. */
    json?: boolean;
    verbose?: boolean;
    /** Skip the budget-confirm prompt — a detached remote run can't answer one. */
    yes?: boolean;
    /** Route through the Agent Client Protocol. */
    acp?: boolean;
    /** False forwards --no-auto-secrets (workflow secrets resolve on the REMOTE keychain). */
    autoSecrets?: boolean;
    /** Native-CLI passthrough (everything after `--`), appended last. */
    passthroughArgs?: string[];
    remoteCwd?: string;
    /** `remoteCwd` was derived from the local cwd — mirror it, don't fail on it. */
    mirrorCwd?: boolean;
    /**
     * Force the remote run's NEW session to use this exact id (Claude only, via
     * `agents run --session-id`). Captured on the task record so the run is
     * resumable by id. Mutually exclusive with `resume`.
     */
    sessionId?: string;
    /**
     * Durable `--name <slug>` handle, forwarded to the remote `agents run` and
     * recorded on the local task so `agents logs <name>` / `agents devices ps` resolve it.
     */
    name?: string;
    /** Resume an existing session on the host by id (via `agents run --resume`). */
    resume?: string;
    /**
     * Forward `--emit-session-id` so the remote run prints its resolved session id
     * as a stdout sentinel (hosts/session-marker.ts). The launcher parses that id
     * out of the followed log and stamps it on the task — the join that maps a
     * remote-created session back home for agents that don't take `--session-id`.
     */
    emitSessionId?: boolean;
    /** Stream progress and block until completion (default true). */
    follow?: boolean;
    timeoutMs?: number;
    /** Copy runtime credentials to the host before the run and shred them after. */
    copyCreds?: HostCredentials;
}
/**
 * Build the remote `agents run …` argv for a host dispatch. Pure so the
 * session-id / resume flag wiring is unit-testable without an SSH round-trip.
 * `--session-id` and `--resume` are mutually exclusive (the CLI rejects both);
 * resume wins when — defensively — both are set.
 *
 * Every field here is classified 'forward' in RUN_OPTION_FORWARDING
 * (remote-cmd.ts) — keep the two in lockstep; run-forwarding.test.ts asserts
 * the table side.
 */
export declare function buildRunForwardedArgs(opts: DispatchOptions): string[];
export interface InteractiveDispatchOptions {
    agent: string;
    /** Explicit agent version pin (e.g. "2.1.207") to forward as `agent@version`. */
    version?: string;
    /** Preserve the trailing-@ account picker so selection happens on the execution host. */
    accountPicker?: boolean;
    /** Explicit run strategy (e.g. "balanced") to forward as `--strategy <strategy>`. */
    strategy?: string;
    /** Named provider account to resolve on the remote host. */
    account?: string;
    /** Optional prompt — forwarded only when the caller explicitly forced interactive mode. */
    prompt?: string;
    mode?: string;
    model?: string;
    /** Reasoning effort to forward as `--effort <effort>`. */
    effort?: string;
    /** Additional directories to grant, already made remote-portable. */
    addDir?: string[];
    /** Stream events as JSON lines. */
    json?: boolean;
    /** Show detailed execution logs. */
    verbose?: boolean;
    /** Kill the agent after this duration, forwarded as `--timeout <duration>`. */
    timeout?: string;
    /** Skip the interactive budget-confirm prompt. */
    yes?: boolean;
    /** Route through the Agent Client Protocol. */
    acp?: boolean;
    remoteCwd?: string;
    /** `remoteCwd` was derived from the local cwd — mirror it, don't fail on it. */
    mirrorCwd?: boolean;
    sessionId?: string;
    name?: string;
    resume?: string;
    passthroughArgs?: string[];
    raw?: boolean;
    /** Forward `--interactive` to the remote so a prompt-bearing run still starts the TUI. */
    forceInteractive?: boolean;
    /** `--env k=v` pairs, forwarded verbatim (the remote CLI parses them). */
    env?: string[];
    balanced?: boolean;
    fallback?: string;
    /** Copy runtime credentials to the host before the run and shred them after. */
    copyCreds?: HostCredentials;
}
/**
 * Build the remote `agents run …` argv for an INTERACTIVE host dispatch. The
 * remote agent sees a TTY, so we omit `--quiet`; the remote CLI will launch its
 * normal interactive TUI / tmux wrapper. A prompt is only included when the
 * caller explicitly forced interactive mode (otherwise the remote CLI would
 * infer headless from the prompt).
 */
export declare function buildInteractiveRunForwardedArgs(opts: InteractiveDispatchOptions): string[];
/**
 * Run an agent interactively on a host, forwarding the local TTY over SSH.
 * Returns the SSH exit code. The remote `agents` CLI is responsible for its own
 * tmux wrapping; the local machine is just the transport.
 */
export declare function runInteractiveOnHost(host: Host, opts: InteractiveDispatchOptions): Promise<number>;
/** Dispatch an `agents run <agent> "<prompt>"` onto a host (the `run --device` path). */
export declare function dispatchToHost(host: Host, opts: DispatchOptions): Promise<DispatchResult>;
export interface CommandDispatchOptions {
    /** `agents …` args (command name first), already stripped of routing flags. */
    forwardedArgs: string[];
    remoteCwd?: string;
    follow?: boolean;
    timeoutMs?: number;
}
/**
 * Dispatch an arbitrary long-running `agents <command>` onto a host detached —
 * used for `teams start --watch --device`, whose supervisor must outlive the SSH
 * connection. Reachability is assumed (the caller has already resolved the host);
 * a launch failure surfaces the remote stderr.
 */
export declare function dispatchAgentsCommand(host: Host, opts: CommandDispatchOptions): Promise<DispatchResult>;
