/**
 * Autonomous loop driver (issue #332).
 *
 * Re-injects an entrypoint each iteration until a stop condition is met. The
 * driver is the deterministic skeleton; the entrypoint inside stays dynamic (it
 * can spawn subagents freely). Every guard — `max_iterations`, `budget`, the
 * `until: signal` condition, SIGINT/SIGTERM — lives OUTSIDE the agent, so the
 * agent cannot vote past a kill-switch (the standard answer to runaway-loop and
 * runaway-cost failure modes; see docs/execution.md).
 *
 * Structure mirrors the teams supervisor (`runSupervisor` in teams/supervisor.ts):
 * a bounded for-loop with a hard cap, a SIGINT/SIGTERM trap that flips a stop
 * flag, a per-iteration guard check, an interval sleep, and a typed `stoppedBy`
 * union for the exit reason.
 *
 * Token accounting: the budget cap is a TOKEN hard-cap, enforced after each
 * turn from the usage events parsed off the agent's stream-json output. Token
 * extraction reuses `extractUsageEvents` from budget/enforce.ts (read-only
 * import) rather than re-implementing the per-provider parsing.
 */
import type { AgentId } from './types.js';
import type { ExecOptions } from './exec.js';
import { type Checkpoint } from './checkpoint.js';
/** Loop block config; see docs/execution.md. */
export interface LoopConfig {
    /** Stop condition. `signal` reads loop-signal.json; absence is fail-closed. */
    until?: 'signal';
    /** Hard cap on iterations. */
    maxIterations?: number;
    /** Token hard-cap, enforced outside the agent. */
    budget?: number;
    /** Delay between iterations: "0" back-to-back, "30m" paces. */
    interval?: string;
}
/** The loop-signal.json contract the entrypoint writes each iteration. */
export interface LoopSignal {
    continue: boolean;
    reason?: string;
}
/** Why the loop stopped. Mirrors the teams supervisor exit reasons. */
export type LoopStoppedBy = 'condition-met' | 'budget' | 'stalled' | 'max' | 'signal' | 'error';
/** Result of a loop run. */
export interface LoopResult {
    /** Iterations actually executed. */
    iterations: number;
    stoppedBy: LoopStoppedBy;
    elapsedMs: number;
    /** Cumulative tokens consumed across all iterations. */
    tokens: number;
    /** Last loop-signal read, if any. */
    lastSignal?: LoopSignal;
}
/** What a single iteration's run function returns. */
export interface IterationResult {
    exitCode: number;
    /** Tokens consumed this iteration (input + output + cache). */
    tokens: number;
}
/** Per-iteration run function — the injectable seam that makes the driver testable. */
export type RunIteration = (options: ExecOptions) => Promise<IterationResult>;
/** Context the driver needs that isn't part of ExecOptions. */
export interface LoopContext {
    runId: string;
    runDir: string;
    agent: AgentId;
    version?: string;
    /** Iteration to start at (1 for a fresh run, checkpoint.iteration+1 for a resume). */
    startIteration?: number;
    /** Tokens already consumed before this driver started (carried across a resume). */
    startTokens?: number;
    /**
     * On a resume, the killed run's LAST iteration session id. The first resumed
     * iteration `/continue`s from it to thread conversation memory forward.
     * Undefined on a fresh run (iteration 1 mints its own id, no prior to continue).
     */
    sessionId?: string;
}
/** Dependency seams for testing. */
export interface LoopDeps {
    /** Per-iteration runner. Defaults to a token-capturing spawn (defaultRunIteration). */
    runIteration?: RunIteration;
    /** Sleep function (ms). Defaults to setTimeout-backed. Injectable so tests don't wait. */
    sleep?: (ms: number) => Promise<void>;
    /** Checkpoint writer. Defaults to writeCheckpoint. */
    writeCheckpoint?: (c: Checkpoint) => void;
}
/** Path to a run's loop-signal.json. */
export declare function loopSignalPath(runDir: string): string;
/**
 * Build the prompt for iteration >= 2 so the agent CONTINUES the prior
 * iteration's conversation instead of starting fresh.
 *
 * This reuses the repo's established cross-process Claude-continuity mechanism —
 * the `/continue <id>` skill (see `buildFallbackPrompt` in exec.ts, which hands
 * a rate-limit successor `/continue ${prevSessionId}`). The skill loads the
 * prior transcript via `agents sessions <id>`, so continuity does NOT depend on
 * the provider's native session being "active"; it reads the transcript off
 * disk. That is why each loop iteration can safely pin a FRESH session id (the
 * `--session-id` flag CREATES a session — re-passing one errors "Session ID
 * already in use") while still threading the conversation forward via the
 * prior id.
 *
 * The original entrypoint is re-appended after the continue directive so the
 * agent both recalls the prior turn AND knows what to do this iteration.
 */
export declare function buildLoopContinuePrompt(prevSessionId: string, entrypoint: string): string;
/**
 * The universal (Tier-2) resume directive: a `/continue <id>` first message that
 * tells the agent to load the prior transcript via `agents sessions <id>` and
 * pick up. Works for ANY agent that ships the `/continue` command — the resume
 * path for agents without a native `--resume` (gemini, grok, opencode, …). An
 * optional follow-on prompt is appended after a blank line; omitted when empty so
 * a bare resume sends just the directive.
 */
export declare function buildContinuePrompt(sessionId: string, prompt?: string): string;
/**
 * Resolve a loop interval string to milliseconds. `"0"` is an explicit
 * back-to-back run (0ms). Any other string must parse via parseTimeout
 * (e.g. "30m", "1h"); an unparseable value (e.g. "30s", "5", "abc") is a
 * configuration error and must NOT silently coalesce to 0 (which would run the
 * loop full-speed on a typo). Throws on bad input; validate at config build
 * time (validateLoopInterval) so the error surfaces before the loop starts.
 */
export declare function parseLoopInterval(interval: string | undefined): number;
/**
 * Read and parse loop-signal.json. Returns null when the file is absent or
 * unparseable — the caller treats null as fail-closed (continue:false).
 */
export declare function readLoopSignal(runDir: string): LoopSignal | null;
/** Delete loop-signal.json so a stale signal never carries into the next iteration. */
export declare function clearLoopSignal(runDir: string): void;
/**
 * Default per-iteration runner: spawn the agent, tee stdout, and sum token usage
 * off the stream. This is a purpose-built token-capturing spawn for the loop's
 * budget guard, not a re-implementation of exec's fallback/budget machinery —
 * it reuses `buildExecCommand` / `buildExecEnv` (the canonical command/env
 * builders) and `extractUsageEvents` (the canonical stream parser). The agent
 * is forced to JSON/headless so the usage stream is parseable.
 */
export declare function defaultRunIteration(options: ExecOptions): Promise<IterationResult>;
/**
 * Run the autonomous loop. Returns when a guard trips, the until-condition is
 * met, the iteration cap is reached, or a signal arrives.
 *
 * stoppedBy semantics:
 *   - `condition-met` — until=signal and the signal said stop (continue:false
 *     OR the file was absent/corrupt → fail-closed).
 *   - `budget`        — cumulative tokens crossed the budget cap (checked after
 *     each turn, outside the agent).
 *   - `max`           — ran maxIterations iterations without any earlier stop.
 *   - `signal`        — SIGINT/SIGTERM arrived; checkpoint is written before exit.
 *   - `error`         — an iteration threw or exited non-zero.
 */
export declare function runLoop(execOptions: ExecOptions, loop: LoopConfig, ctx: LoopContext, deps?: LoopDeps): Promise<LoopResult>;
