import { ControlEvalResult, KnowledgeRequirement, ControlBudget, KnowledgeReadinessReport, ControlStep, ControlDecision, UserQuestion, DataAcquisitionPlan, ControlRunResult, RunRecord, TraceStore, DefaultVerdict } from '@tangle-network/agent-eval';
import { CreateSandboxOptions, SandboxInstance, SandboxEvent, AgentProfile } from '@tangle-network/sandbox';

/**
 *
 * Core task, session, adapter, and stream-event types for the runtime.
 *
 * This module owns the public shape of every cross-cutting record (`TaskSpec`,
 * `RuntimeSession`, `RuntimeStreamEvent`). Everything else in the runtime
 * imports from here so type-level changes ripple in one place.
 *
 * @stable
 */

/** @stable */
interface AgentTaskSpec {
    id: string;
    intent: string;
    /** Domain is metadata, not an architectural boundary: tax, legal, gtm, creative, blueprint, redteam, etc. */
    domain?: string;
    inputs?: Record<string, unknown>;
    requiredKnowledge?: KnowledgeRequirement[];
    budget?: Partial<ControlBudget>;
    metadata?: Record<string, unknown>;
}
/** @stable */
interface AgentKnowledgeProvider {
    buildReadiness?(task: AgentTaskSpec): Promise<KnowledgeReadinessReport> | KnowledgeReadinessReport;
    answerQuestions?(questions: UserQuestion[], task: AgentTaskSpec): Promise<Record<string, string>> | Record<string, string>;
    executeAcquisitionPlans?(plans: DataAcquisitionPlan[], task: AgentTaskSpec): Promise<string[]> | string[];
    refreshReadiness?(input: {
        task: AgentTaskSpec;
        previous: KnowledgeReadinessReport;
        userAnswers: Record<string, string>;
        acquiredEvidenceIds: string[];
    }): Promise<KnowledgeReadinessReport> | KnowledgeReadinessReport;
}
/** @stable */
interface AgentTaskContext<TState, TAction, TActionResult, TEval extends ControlEvalResult = ControlEvalResult> {
    task: AgentTaskSpec;
    knowledge: KnowledgeReadinessReport;
    state: TState;
    evals: TEval[];
    history: ControlStep<TState, TAction, TActionResult, TEval>[];
    budget: ControlBudget;
    stepIndex: number;
    wallMs: number;
    spentCostUsd: number;
    remainingCostUsd?: number;
    abortSignal: AbortSignal;
}
/** @stable */
interface AgentAdapter<TState, TAction, TActionResult, TEval extends ControlEvalResult = ControlEvalResult> {
    observe(ctx: {
        task: AgentTaskSpec;
        knowledge: KnowledgeReadinessReport;
        history: ControlStep<TState, TAction, TActionResult, TEval>[];
        abortSignal: AbortSignal;
    }): Promise<TState> | TState;
    validate(ctx: {
        task: AgentTaskSpec;
        knowledge: KnowledgeReadinessReport;
        state: TState;
        history: ControlStep<TState, TAction, TActionResult, TEval>[];
        abortSignal: AbortSignal;
    }): Promise<TEval[]> | TEval[];
    decide(ctx: AgentTaskContext<TState, TAction, TActionResult, TEval>): Promise<ControlDecision<TAction>> | ControlDecision<TAction>;
    act(action: TAction, ctx: AgentTaskContext<TState, TAction, TActionResult, TEval>): Promise<TActionResult> | TActionResult;
    shouldStop?(ctx: AgentTaskContext<TState, TAction, TActionResult, TEval>): Promise<{
        stop: boolean;
        pass: boolean;
        reason: string;
        score?: number;
    }> | {
        stop: boolean;
        pass: boolean;
        reason: string;
        score?: number;
    };
    onKnowledgeBlocked?(ctx: {
        task: AgentTaskSpec;
        knowledge: KnowledgeReadinessReport;
        questions: UserQuestion[];
        acquisitionPlans: DataAcquisitionPlan[];
    }): Promise<ControlDecision<TAction>> | ControlDecision<TAction>;
    getActionCostUsd?(ctx: {
        action: TAction;
        result: TActionResult;
        task: AgentTaskSpec;
        state: TState;
        evals: TEval[];
        history: ControlStep<TState, TAction, TActionResult, TEval>[];
    }): number | undefined;
    projectRunRecords?(result: ControlRunResult<TState, TAction, TActionResult, TEval>, task: AgentTaskSpec): RunRecord[];
}
/** @stable */
type AgentTaskStatus = 'completed' | 'blocked' | 'failed' | 'aborted';
/** @stable */
type AgentRuntimeEvent<TState = unknown, TAction = unknown, TActionResult = unknown, TEval extends ControlEvalResult = ControlEvalResult> = {
    type: 'task_start';
    task: AgentTaskSpec;
} | {
    type: 'readiness_start';
    task: AgentTaskSpec;
} | {
    type: 'readiness_end';
    task: AgentTaskSpec;
    knowledge: KnowledgeReadinessReport;
} | {
    type: 'questions_start';
    task: AgentTaskSpec;
    questions: UserQuestion[];
} | {
    type: 'questions_end';
    task: AgentTaskSpec;
    questions: UserQuestion[];
    userAnswers: Record<string, string>;
} | {
    type: 'acquisition_start';
    task: AgentTaskSpec;
    acquisitionPlans: DataAcquisitionPlan[];
} | {
    type: 'acquisition_end';
    task: AgentTaskSpec;
    acquisitionPlans: DataAcquisitionPlan[];
    acquiredEvidenceIds: string[];
} | {
    type: 'control_start';
    task: AgentTaskSpec;
    knowledge: KnowledgeReadinessReport;
} | {
    type: 'control_step';
    task: AgentTaskSpec;
    step: ControlStep<TState, TAction, TActionResult, TEval>;
} | {
    type: 'control_end';
    task: AgentTaskSpec;
    control: ControlRunResult<TState, TAction, TActionResult, TEval>;
} | {
    type: 'task_end';
    task: AgentTaskSpec;
    status: AgentTaskStatus;
    reason: string;
};
/** @stable */
type AgentRuntimeEventSink<TState = unknown, TAction = unknown, TActionResult = unknown, TEval extends ControlEvalResult = ControlEvalResult> = (event: AgentRuntimeEvent<TState, TAction, TActionResult, TEval>) => Promise<void> | void;
/**
 *
 * Typed transport / backend failure detail. Carried on `backend_error` and
 * `final` events when the backend's stream throws or the upstream HTTP call
 * returns a non-success status. Lets consumers (a) distinguish "stream
 * completed with no text" from "stream never reached the model" and
 * (b) reconstruct the precise upstream signal (status + truncated body) when
 * building a `RunRecord.error`.
 *
 * `body` is truncated to 2 KiB by the backend so an HTML error page from a
 * misconfigured proxy never bloats event payloads or logs. Consumers needing
 * the full body should inspect the underlying `BackendTransportError.body`
 * via a custom `mapEvent` or backend wrapper.
 *
 * @stable
 */
interface BackendErrorDetail {
    /**
     * `'transport'` — upstream HTTP / network failure with optional status code.
     * `'backend'` — the backend's `stream()` generator threw for a non-transport
     * reason (e.g. a custom adapter error, sandbox crash).
     */
    kind: 'transport' | 'backend';
    message: string;
    /** Upstream HTTP status when known. `0` for connection / abort errors. */
    status?: number;
    /** Truncated response body (≤2 KiB). Diagnostic only — never machine-parsed. */
    body?: string;
}
/**
 *
 * OpenAI Chat Completions tool descriptor. The shape mirrors the
 * `/v1/chat/completions` `tools[]` parameter so callers can pass tool
 * definitions through `createOpenAICompatibleBackend({ tools })` without any
 * runtime translation. The router proxies this shape verbatim to Anthropic
 * (translated server-side), DeepSeek, Groq, OpenAI, and Gemini — every model
 * that the eval surface targets.
 *
 * Callers that build their tool list from MCP servers should run a one-shot
 * MCP `tools/list` at config time and project the result into this shape. The
 * runtime intentionally does NOT depend on `@modelcontextprotocol/sdk` —
 * keeping the backend transport thin lets domain repos own MCP plumbing.
 *
 * @stable
 */
interface OpenAIChatTool {
    type: 'function';
    function: {
        name: string;
        description?: string;
        parameters?: Record<string, unknown>;
    };
}
/**
 *
 * `tool_choice` parameter for OpenAI-compat chat. Same shape as the OpenAI
 * spec: `'auto'` (default — model decides), `'none'` (disable tool calling
 * for this turn), `'required'` (force a tool call), or a specific function
 * pin `{ type: 'function', function: { name } }`.
 *
 * @stable
 */
type OpenAIChatToolChoice = 'auto' | 'none' | 'required' | {
    type: 'function';
    function: {
        name: string;
    };
};
/**
 *
 * `response_format` parameter for OpenAI-compatible chat endpoints. Use
 * `json_object` when the caller needs syntactically valid JSON, or
 * `json_schema` when the upstream provider supports schema-constrained JSON.
 *
 * @stable
 */
type OpenAIChatResponseFormat = {
    type: 'text';
} | {
    type: 'json_object';
} | {
    type: 'json_schema';
    json_schema: Record<string, unknown>;
};
/** @stable */
type RuntimeStreamEvent = {
    type: 'task_start';
    task: AgentTaskSpec;
    timestamp: string;
} | {
    type: 'readiness_start';
    task: AgentTaskSpec;
    timestamp: string;
} | {
    type: 'readiness_end';
    task: AgentTaskSpec;
    knowledge: KnowledgeReadinessReport;
    decision: KnowledgeReadinessDecision;
    timestamp: string;
} | {
    type: 'questions_start';
    task: AgentTaskSpec;
    questions: UserQuestion[];
    timestamp: string;
} | {
    type: 'questions_end';
    task: AgentTaskSpec;
    questions: UserQuestion[];
    userAnswers: Record<string, string>;
    timestamp: string;
} | {
    type: 'acquisition_start';
    task: AgentTaskSpec;
    acquisitionPlans: DataAcquisitionPlan[];
    timestamp: string;
} | {
    type: 'acquisition_end';
    task: AgentTaskSpec;
    acquisitionPlans: DataAcquisitionPlan[];
    acquiredEvidenceIds: string[];
    timestamp: string;
} | {
    type: 'session_created';
    task: AgentTaskSpec;
    session: RuntimeSession;
    timestamp: string;
} | {
    type: 'session_resumed';
    task: AgentTaskSpec;
    session: RuntimeSession;
    timestamp: string;
} | {
    type: 'backend_start';
    task: AgentTaskSpec;
    session: RuntimeSession;
    backend: string;
    timestamp: string;
} | {
    type: 'text_delta';
    task?: AgentTaskSpec;
    session?: RuntimeSession;
    text: string;
    timestamp?: string;
} | {
    type: 'reasoning_delta';
    task?: AgentTaskSpec;
    session?: RuntimeSession;
    text: string;
    timestamp?: string;
} | {
    type: 'tool_call';
    task?: AgentTaskSpec;
    session?: RuntimeSession;
    toolName: string;
    toolCallId?: string;
    args?: unknown;
    timestamp?: string;
} | {
    type: 'tool_result';
    task?: AgentTaskSpec;
    session?: RuntimeSession;
    toolName: string;
    toolCallId?: string;
    result?: unknown;
    timestamp?: string;
} | {
    type: 'llm_call';
    task?: AgentTaskSpec;
    session?: RuntimeSession;
    model: string;
    tokensIn?: number;
    tokensOut?: number;
    costUsd?: number;
    latencyMs?: number;
    finishReason?: string;
    timestamp?: string;
} | {
    type: 'artifact';
    task?: AgentTaskSpec;
    session?: RuntimeSession;
    artifactId: string;
    name?: string;
    mimeType?: string;
    uri?: string;
    content?: string;
    metadata?: Record<string, unknown>;
    timestamp?: string;
} | {
    type: 'proposal_created';
    task?: AgentTaskSpec;
    session?: RuntimeSession;
    proposalId: string;
    title: string;
    status?: 'pending' | 'approved' | 'rejected';
    content?: string;
    timestamp?: string;
} | {
    type: 'backend_error';
    task: AgentTaskSpec;
    session?: RuntimeSession;
    backend: string;
    message: string;
    recoverable: boolean;
    /**
     * Typed transport diagnostic. Present when the upstream returned a
     * non-success HTTP status or every retry attempt threw. Consumers MUST
     * surface this onto their `RunRecord.error` — silently treating a
     * `backend_error` as "no output" hides credit exhaustion, auth failure,
     * and upstream outages from operators.
     *  - `kind: 'transport'` — HTTP / network failure with optional `status`
     *    + truncated response `body`.
     *  - `kind: 'backend'` — the backend's `stream()` generator threw for a
     *    reason that isn't a recognized transport failure.
     */
    error?: BackendErrorDetail;
    timestamp: string;
} | {
    type: 'backend_end';
    task: AgentTaskSpec;
    session: RuntimeSession;
    backend: string;
    timestamp: string;
} | {
    type: 'task_end';
    task: AgentTaskSpec;
    status: AgentTaskStatus;
    reason: string;
    timestamp: string;
} | {
    type: 'final';
    task: AgentTaskSpec;
    session?: RuntimeSession;
    status: AgentTaskStatus;
    reason: string;
    text?: string;
    metadata?: Record<string, unknown>;
    /**
     * Typed terminal-error diagnostic. Mirrors the `backend_error.error`
     * shape so a consumer that only listens for `final` still receives a
     * loud, structured failure when the backend never produced output. Only
     * set when `status !== 'completed'`. Consumers building a `RunRecord`
     * MUST map this to `RunRecord.error` rather than recording silent
     * `error: null` with empty `finalText`.
     */
    error?: BackendErrorDetail;
    timestamp: string;
};
/** @stable */
interface RuntimeSession {
    id: string;
    backend: string;
    status: 'active' | 'completed' | 'failed' | 'aborted';
    resumeToken?: string;
    createdAt: string;
    updatedAt: string;
    metadata?: Record<string, unknown>;
}
/** @stable */
interface RuntimeSessionStore {
    get(sessionId: string): Promise<RuntimeSession | undefined> | RuntimeSession | undefined;
    put(session: RuntimeSession): Promise<void> | void;
    appendEvent?(sessionId: string, event: RuntimeStreamEvent): Promise<void> | void;
    listEvents?(sessionId: string): Promise<RuntimeStreamEvent[]> | RuntimeStreamEvent[];
}
/** @stable */
interface AgentBackendInput {
    task: AgentTaskSpec;
    message?: string;
    messages?: Array<{
        role: string;
        content: string;
    }>;
    inputs?: Record<string, unknown>;
}
/** @stable */
interface AgentBackendContext {
    task: AgentTaskSpec;
    knowledge: KnowledgeReadinessReport;
    session: RuntimeSession;
    signal?: AbortSignal;
    /**
     * Conversation/run identifier when this call is part of a multi-agent run.
     * Backends should stamp it into any trace/log emission so cross-participant
     * events correlate. Absent when the call is a stand-alone `runAgentTask`.
     */
    runId?: string;
    /**
     * Deterministic turn id for this single call. Stable across retries of the
     * same logical turn so a caching gateway / idempotent backend can dedupe.
     */
    turnId?: string;
    /**
     * If this call is itself nested inside a higher-order conversation
     * (recursion via `createConversationBackend`), the enclosing turn's id.
     * Used for trace stitching across nested orchestration.
     */
    parentTurnId?: string;
    /**
     * Headers to forward verbatim to any outbound HTTP the backend issues:
     * `X-Tangle-Forwarded-Authorization`, `X-Tangle-Forwarded-Depth`,
     * run/turn correlation. Backends that issue HTTP MUST merge these into
     * the outbound request; backends that don't issue HTTP may ignore them.
     */
    propagatedHeaders?: Readonly<Record<string, string>>;
}
/** @stable */
interface AgentExecutionBackend<TInput extends AgentBackendInput = AgentBackendInput> {
    kind: string;
    start?(input: TInput, context: Omit<AgentBackendContext, 'session'> & {
        requestedSessionId?: string;
    }): Promise<RuntimeSession> | RuntimeSession;
    resume?(session: RuntimeSession, input: TInput, context: Omit<AgentBackendContext, 'session'>): Promise<RuntimeSession> | RuntimeSession;
    stream(input: TInput, context: AgentBackendContext): AsyncIterable<RuntimeStreamEvent>;
    stop?(session: RuntimeSession, reason: string): Promise<void> | void;
}
/** @stable */
interface RunAgentTaskStreamOptions<TInput extends AgentBackendInput = AgentBackendInput> {
    task: AgentTaskSpec;
    backend: AgentExecutionBackend<TInput>;
    input?: Omit<TInput, 'task'>;
    knowledge?: AgentKnowledgeProvider;
    sessionStore?: RuntimeSessionStore;
    sessionId?: string;
    resume?: boolean;
    signal?: AbortSignal;
    minimumReadinessScore?: number;
}
/** @stable */
interface RunAgentTaskOptions<TState, TAction, TActionResult, TEval extends ControlEvalResult = ControlEvalResult> {
    task: AgentTaskSpec;
    adapter: AgentAdapter<TState, TAction, TActionResult, TEval>;
    knowledge?: AgentKnowledgeProvider;
    onEvent?: AgentRuntimeEventSink<TState, TAction, TActionResult, TEval>;
    store?: TraceStore;
    signal?: AbortSignal;
    scenarioId?: string;
    projectId?: string;
    variantId?: string;
    minimumReadinessScore?: number;
}
/** @stable */
interface AgentTaskRunResult<TState, TAction, TActionResult, TEval extends ControlEvalResult = ControlEvalResult> {
    task: AgentTaskSpec;
    status: AgentTaskStatus;
    knowledge: KnowledgeReadinessReport;
    questions: UserQuestion[];
    acquisitionPlans: DataAcquisitionPlan[];
    userAnswers: Record<string, string>;
    acquiredEvidenceIds: string[];
    control: ControlRunResult<TState, TAction, TActionResult, TEval>;
    runRecords: RunRecord[];
}
/** @stable */
interface KnowledgeReadinessDecision {
    passed: boolean;
    status: 'ready' | 'blocked' | 'caveat';
    reason: string;
    readinessScore: number;
    recommendedAction: KnowledgeReadinessReport['recommendedAction'];
    severity: KnowledgeReadinessReport['severity'];
    blockingGapIds: string[];
    nonBlockingGapIds: string[];
}

/**
 *
 * Runtime hook contracts. Hooks are execution-scoped observers, not part of an
 * `AgentProfile`: profiles stay portable agent recipes; hooks attach to the
 * loop or product harness that is running the profile.
 *
 * @experimental
 */
type RuntimeHookPhase = 'before' | 'after' | 'error' | 'event';
type RuntimeHookTarget = 'agent.run' | 'agent.turn' | 'agent.tool_call' | 'agent.spawn' | 'agent.child' | 'agent.plan' | 'agent.decision' | (string & {});
type RuntimeDecisionKind = 'continue' | 'verify' | 'ask' | 'retry' | 'stop' | 'memory-write' | 'memory-read' | 'tool-select' | 'skill-select' | 'workflow-select' | 'surface-promote' | (string & {});
interface RuntimeHookEvent<Payload = unknown> {
    id: string;
    runId: string;
    scenarioId?: string;
    target: RuntimeHookTarget;
    phase: RuntimeHookPhase;
    timestamp: number;
    stepIndex?: number;
    parentId?: string;
    payload?: Payload;
    metadata?: Record<string, unknown>;
}
interface RuntimeHookContext {
    signal?: AbortSignal;
}
interface RuntimeDecisionEvidenceRef {
    source: string;
    id: string;
    detail?: string;
    metadata?: Record<string, unknown>;
}
interface RuntimeDecisionPoint {
    id: string;
    runId: string;
    scenarioId?: string;
    stepIndex: number;
    kind: RuntimeDecisionKind;
    candidateActions: string[];
    context?: string;
    evidence: RuntimeDecisionEvidenceRef[];
    metadata?: Record<string, unknown>;
}
interface RuntimeHookErrorContext {
    hook: 'onEvent' | 'onDecisionPoint';
    eventId?: string;
    target?: RuntimeHookTarget;
    phase?: RuntimeHookPhase;
    decisionId?: string;
    decisionKind?: RuntimeDecisionKind;
}
/**
 * The observation seam attached to a running loop (never to the portable genome).
 * Implement the optional hooks to receive lifecycle events, semantic decision points,
 * and hook errors. Author with {@link defineRuntimeHooks} for inference, and attach N
 * observers at once with {@link composeRuntimeHooks} — there is ONE event stream, not a
 * callback-prop zoo.
 */
interface RuntimeHooks {
    /**
     * General before/after/event hook. Use this for telemetry, memory capture,
     * policy wrapping, child lifecycle observers, or product-specific extension
     * points.
     */
    onEvent?: (event: RuntimeHookEvent, context: RuntimeHookContext) => void | Promise<void>;
    /**
     * Semantic decision hook. Belief-state evaluation consumes this, but runtime
     * code should keep emitting ordinary lifecycle events as the base layer.
     */
    onDecisionPoint?: (point: RuntimeDecisionPoint, context: RuntimeHookContext) => void | Promise<void>;
    onHookError?: (error: Error, context: RuntimeHookErrorContext) => void | Promise<void>;
}
/** Identity helper that types a {@link RuntimeHooks} literal so the fields are inferred. */
declare function defineRuntimeHooks(hooks: RuntimeHooks): RuntimeHooks;
/**
 * Merge several {@link RuntimeHooks} into one. Falsy entries are dropped (so you can
 * pass `flag && hooks`), and every observer's `onEvent`/`onDecisionPoint` fires for each
 * event. Use this to attach N observers to a loop instead of a second event bus.
 */
declare function composeRuntimeHooks(...entries: Array<RuntimeHooks | undefined | null | false>): RuntimeHooks;
/** Fire `hooks.onEvent`, swallowing sync throws and surfacing async failures to `onError`. */
declare function notifyRuntimeHookEvent(hooks: RuntimeHooks | undefined, event: RuntimeHookEvent, context?: RuntimeHookContext): void;
/** Fire `hooks.onDecisionPoint`, swallowing sync throws and surfacing async failures to `onError`. */
declare function notifyRuntimeDecisionPoint(hooks: RuntimeHooks | undefined, point: RuntimeDecisionPoint, context?: RuntimeHookContext): void;

/**
 *
 * Production-run lifecycle: record what the agent did on behalf of a customer,
 * what it cost, and how it ended.
 *
 * Three concerns live in this module:
 *
 *  1. **Lifecycle state machine** — `running` -> `completed | failed | cancelled`,
 *     enforced by `RuntimeRunStateError`. Completion is idempotent for the same
 *     status (a second `complete()` call is a no-op so retries / cleanup paths
 *     don't double-fire side effects). A different terminal status is a state
 *     error.
 *
 *  2. **Cost ledger** — every `llm_call` event the handle observes contributes
 *     `tokensIn`, `tokensOut`, `costUsd`, and bumps `llmCalls`. Wall time is
 *     measured from `startRuntimeRun()` to `complete()`. Surface via
 *     `handle.cost()` for cost-per-task dashboards.
 *
 *  3. **Persistence adapter** — `RuntimeRunPersistenceAdapter` is the seam
 *     consumers plug in to write a `RuntimeRunRow` to their D1 / postgres /
 *     KV store. The adapter receives a sanitized row shape; no telemetry
 *     payload bytes flow through it unless the consumer opts in via
 *     `RuntimeRunOptions.telemetryEvents`.
 *
 * @stable
 */

/** @stable */
type RuntimeRunStatus = 'running' | 'completed' | 'failed' | 'cancelled';
/** @stable */
interface RuntimeRunCost {
    /** Cumulative input tokens across every observed `llm_call` event. */
    tokensIn: number;
    /** Cumulative output tokens across every observed `llm_call` event. */
    tokensOut: number;
    /** Sum of `costUsd` from every observed `llm_call` event. */
    costUsd: number;
    /** Wall time from `startRuntimeRun()` to `complete()` (or `now()` if not yet completed). */
    wallMs: number;
    /** Count of `llm_call` events observed during the run. */
    llmCalls: number;
}
/** @stable */
interface RuntimeRunCompleteInput {
    status: Exclude<RuntimeRunStatus, 'running'>;
    resultSummary?: string;
    /** Optional explicit cost override; if omitted, the accumulated ledger is used. */
    cost?: Partial<RuntimeRunCost>;
    /** Stable error message when `status === 'failed'`. */
    error?: string;
    /** Additional adapter-specific fields merged into the persisted row. */
    metadata?: Record<string, unknown>;
}
/** @stable */
interface RuntimeRunRow {
    /** Stable runtime-side identifier. Adapters may translate to their own primary key. */
    id: string;
    workspaceId: string;
    sessionId?: string;
    agentId?: string;
    domain?: string;
    taskId: string;
    scenarioId?: string;
    status: RuntimeRunStatus;
    resultSummary?: string;
    error?: string;
    cost: RuntimeRunCost;
    startedAt: string;
    completedAt?: string;
    metadata?: Record<string, unknown>;
}
/** @stable */
interface RuntimeRunPersistenceAdapter {
    /**
     * Called once when `handle.persist()` runs. Implementations write `row` to
     * their durable store (D1, postgres, KV) and return whatever the consumer
     * wants the caller to see (often the storage-side row id). Errors thrown
     * here propagate out of `persist()` so the caller can decide whether to
     * retry or log-and-continue.
     */
    upsert(row: RuntimeRunRow): Promise<void> | void;
}
/** @stable */
interface RuntimeRunOptions {
    workspaceId: string;
    sessionId?: string;
    agentId?: string;
    taskSpec: AgentTaskSpec;
    scenarioId?: string;
    /** Optional persistence adapter; if omitted, `persist()` is a no-op. */
    adapter?: RuntimeRunPersistenceAdapter;
    /** Override the row id; default = `${taskSpec.id}:${random suffix}`. */
    id?: string;
    /** Override the clock; default = `Date.now()`. Useful for deterministic tests. */
    now?: () => number;
}
/** @stable */
interface RuntimeRunHandle {
    /** Stable id assigned at start. */
    readonly id: string;
    readonly workspaceId: string;
    readonly sessionId: string | undefined;
    readonly taskSpec: AgentTaskSpec;
    readonly status: RuntimeRunStatus;
    /**
     * Observe a single `RuntimeStreamEvent`. The handle ignores non-cost events
     * (text deltas, tool calls) silently so consumers can pipe the whole stream
     * through `handle.observe`. `llm_call` events update the ledger.
     */
    observe(event: RuntimeStreamEvent): void;
    /** Snapshot of the current cost ledger. Safe to call at any time. */
    cost(): RuntimeRunCost;
    /**
     * Transition to a terminal state. Idempotent for the same status; throws
     * `RuntimeRunStateError` for a different terminal status (state machines
     * don't time-travel).
     */
    complete(input: RuntimeRunCompleteInput): void;
    /** Build the current row without writing it. Useful for tests + dry runs. */
    toRow(metadata?: Record<string, unknown>): RuntimeRunRow;
    /**
     * Persist the current row via the configured adapter. Must be called after
     * `complete()`. Idempotent for the same terminal state (the adapter sees
     * the same row on retry).
     */
    persist(metadata?: Record<string, unknown>): Promise<void>;
}
/**
 *
 * Construct a runtime-run handle. The returned handle is mutable across its
 * lifetime; consumers should not share it across requests.
 *
 * @stable
 */
declare function startRuntimeRun(options: RuntimeRunOptions): RuntimeRunHandle;

/**
 *
 * Driven-loop substrate — type surface.
 *
 * The loop kernel orchestrates around the sandbox SDK; it does not invent
 * its own notion of "what an agent is". Each iteration is a sandbox-SDK
 * `streamPrompt` call against an `AgentProfile`. The kernel owns iteration
 * accounting, concurrency, abort propagation, cost aggregation, and trace
 * emission; the driver owns topology (plan + decide); the validator owns
 * output scoring; the output adapter owns event-stream → typed-output decode.
 *
 * @experimental
 */

/** @experimental */
interface ValidationCtx {
    /** Iteration index this output came from (0-based). */
    iteration: number;
    /**
     * Live sandbox for this iteration. Validators that need execution-grounded
     * evidence can inspect files or run commands here instead of forcing callers
     * to bypass the loop kernel with raw Sandbox SDK orchestration.
     */
    box?: SandboxInstance;
    /** Cooperative cancellation channel. */
    signal: AbortSignal;
    /**
     * Optional trace emitter. When set, validator implementations that make
     * LLM calls (e.g. an LLM-judge reviewer) emit spans into it.
     * The kernel passes `ctx.traceEmitter` from `ExecCtx` when available.
     */
    traceEmitter?: LoopTraceEmitter;
}
/** @experimental */
interface Validator<Output, Verdict = DefaultVerdict> {
    validate(output: Output, ctx: ValidationCtx): Promise<Verdict>;
}
/**
 * Sandbox-SDK-shaped agent specification.
 *
 * The kernel uses `profile` to instantiate a sandbox per iteration, formats
 * `task` into a prompt via `taskToPrompt`, and merges `sandboxOverrides` into
 * the `CreateSandboxOptions` it passes to `client.create`. Heterogeneous
 * fanout supplies multiple `AgentRunSpec`s and the kernel round-robins
 * through them when the driver plans N tasks.
 *
 * @experimental
 */
interface AgentRunSpec<Task> {
    /** Sandbox SDK profile — what kind of agent runs the task. */
    profile: AgentProfile;
    /** Task → prompt formatter. Pure and deterministic. */
    taskToPrompt: (task: Task) => string;
    /**
     * Optional pre-prompt sandbox provisioner. Runs after the sandbox is acquired
     * and before the first prompt is streamed into that box. Use this for
     * domain-agnostic setup such as repo snapshots, benchmark fixtures, policy
     * files, or seed datasets. The hook is part of the runtime surface so loop
     * consumers do not hand-roll Sandbox SDK orchestration just to prepare a
     * workspace before the agent sees it.
     *
     * `ctx.recordMount` records what was placed into the box so the run carries a
     * provenance manifest (`LoopResult.provenance.mounts`). It is optional and
     * provenance-only — the kernel never reads box contents and attaches no
     * meaning to the entries; not calling it simply leaves the manifest empty.
     */
    prepareBox?: (box: SandboxInstance, ctx: {
        signal: AbortSignal;
        recordMount: MountRecorder;
    }) => Promise<void> | void;
    /**
     * Per-spec stable name. Surfaced in trace events and the default winner
     * selector tiebreak. Falls back to `profile.name ?? 'agent'`.
     */
    name?: string;
    /**
     * Optional sandbox-SDK `CreateSandboxOptions` overrides merged on top of
     * the kernel's defaults. `backend.profile` is set to `profile` by the
     * kernel and cannot be overridden here — use `profile` itself for that.
     */
    sandboxOverrides?: Partial<Omit<CreateSandboxOptions, 'backend'>> & {
        backend?: Omit<NonNullable<CreateSandboxOptions['backend']>, 'profile'>;
    };
}
/**
 * Stream of `SandboxEvent`s → typed `Output`.
 *
 * Adapters are pure functions over the already-collected event array; they
 * do not receive the live AsyncIterable so they can be replayed against
 * persisted streams during tests / replays.
 *
 * @experimental
 */
interface OutputAdapter<Output> {
    parse(events: SandboxEvent[]): Output;
}
/** LLM token usage. Structurally matches agent-eval's `RunTokenUsage` /
 *  `CampaignTokenUsage` ({ input, output }) so a loop result maps straight
 *  onto `ctx.cost.observeTokens` in a `runProfileMatrix` dispatch — without
 *  which the backend-integrity guard reads the run as a stub. */
interface LoopTokenUsage {
    input: number;
    output: number;
}
/**
 * One mounted resource recorded during box preparation — a pure provenance
 * record of what the caller placed into a box before the agent saw it. The
 * kernel never reads box contents itself (it does not know what was mounted);
 * the caller, which owns the bytes inside `prepareBox`, supplies each entry via
 * `recordMount`. Carries no domain semantics — just where the resource landed,
 * its content fingerprint, its size, and where it came from — so a run is
 * auditable after the fact ("what exactly was this agent given?").
 *
 * @experimental
 */
interface MountManifestEntry {
    /** Destination path inside the box where the resource was placed. */
    path: string;
    /** Hex SHA-256 of the mounted bytes. The caller computes it from the bytes
     *  it wrote — the kernel does not hash box contents. */
    sha256: string;
    /** Size of the mounted resource in bytes. */
    bytes: number;
    /** Free-form origin of the resource (e.g. a repo ref, a corpus id, a local
     *  path, a URL). Provenance only — the kernel attaches no meaning to it. */
    source: string;
}
/**
 * A record of one candidate-selection decision: which iteration the selector
 * picked (or rejected) and why. Pure audit trail of the SELECTOR role — it
 * carries the selector's identity, the candidate's score, and an optional
 * human-readable reason, with no domain semantics. The kernel emits one receipt
 * per scored candidate at finalize so a run answers "why did THIS one win?".
 *
 * @experimental
 */
interface SelectionReceipt {
    /** Iteration index this receipt is about. */
    candidateIndex: number;
    /** True for the iteration the selector chose as winner; false otherwise. */
    selected: boolean;
    /** The candidate's verdict score, when it has one. */
    score?: number;
    /** Why this candidate was (or was not) selected, when the selector states it. */
    reason?: string;
    /** Identity of the selector that produced this receipt — `'caller'` (an
     *  explicit `selectWinner`), `'driver'` (a driver-authored winner), or
     *  `'default'` (the kernel's best-valid-score argmax). */
    selector: 'caller' | 'driver' | 'default';
}
/**
 * Domain-free run provenance: a manifest of what was mounted into the run's
 * boxes and the receipts for how the winner was selected. Surfaced on
 * `LoopResult` purely for run auditability — nothing in the kernel branches on
 * it. Empty arrays when the caller recorded no mounts and there was no
 * candidate to select.
 *
 * @experimental
 */
interface RunProvenance {
    /** Every resource recorded via `prepareBox`'s `recordMount`, in record order. */
    mounts: MountManifestEntry[];
    /** One receipt per scored candidate at finalize, in iteration order. */
    selectionReceipts: SelectionReceipt[];
}
/**
 * Records a mounted resource into the run's provenance manifest. Passed to
 * `prepareBox` so the caller — which owns the bytes it writes into the box —
 * declares what it mounted without the kernel having to inspect box contents.
 *
 * @experimental
 */
type MountRecorder = (entry: MountManifestEntry) => void;
/** @experimental */
interface Iteration<Task, Output> {
    /** 0-based iteration index assigned by the kernel. */
    index: number;
    task: Task;
    /** Stable name of the `AgentRunSpec` that produced this iteration. */
    agentRunName: string;
    output?: Output;
    verdict?: DefaultVerdict;
    error?: Error;
    /** Raw sandbox event stream collected for this iteration. */
    events: SandboxEvent[];
    startedAt: number;
    endedAt: number;
    costUsd: number;
    /** Summed LLM token usage across every `llm_call` event in this iteration. */
    tokenUsage: LoopTokenUsage;
}
/** @experimental */
interface Driver<Task, Output, Decision> {
    /**
     * Stable identifier surfaced in trace events. Default `'driver'`.
     */
    readonly name?: string;
    /**
     * Tasks to issue this iteration. `[task]` → refine; N copies → fanout;
     * `[]` → no more work this round (kernel proceeds to `decide`).
     */
    plan(task: Task, history: ReadonlyArray<Iteration<Task, Output>>): Promise<Task[]>;
    /**
     * Inspect history and return the next state. The kernel terminates the
     * loop when `decide` returns a value listed in `isTerminalDecision`
     * (`'stop' | 'pick-winner' | 'fail' | 'done'`), when `maxIterations`
     * is hit, or when the abort signal fires.
     */
    decide(history: ReadonlyArray<Iteration<Task, Output>>): Decision | Promise<Decision>;
    /**
     * Optional: describe the move `plan()` just produced, for trace emission.
     * The kernel calls this immediately after `plan()` and emits the result in
     * the `loop.plan` event so a topology viewer can render the agent's chosen
     * move + rationale (not just the inferred fan-width). Drivers whose topology
     * is a pure function of count (refine/fanout-vote) omit it — the kernel
     * infers `moveKind` from the planned-task count. A driver that authors its
     * own topology returns its chosen move's kind + rationale here.
     */
    describePlan?(): LoopPlanDescription | undefined;
    /**
     * Optional: the driver AUTHORS the winner instead of the kernel's argmax. The
     * kernel consults this at finalize ONLY when the caller did not pass an explicit
     * `selectWinner` to runLoop. Return the driver-declared winner (e.g. from a
     * `select` topology move) or `undefined` to fall through to the default
     * (best-valid-score, earliest index). This is the SELECTOR role made
     * agent-authorable — the planner runs the selection, not the kernel.
     * @experimental
     */
    selectWinner?(history: ReadonlyArray<Iteration<Task, Output>>): LoopWinner<Task, Output> | undefined;
}
/** @experimental Driver-supplied description of the just-planned move. */
interface LoopPlanDescription {
    /** Topology move this round — e.g. `'refine' | 'fanout' | 'verify' | 'stop'`. */
    kind: string;
    /** Why the driver chose this move (the agent's rationale), when available. */
    rationale?: string;
    /**
     * Iteration index this round branches FROM, when the driver declares it.
     * Overrides the kernel's inferred branch point — lets a planner that
     * branches off a specific (non-winner) iteration emit faithful edge lineage.
     * Omit to keep the inferred (best-valid / latest) branch point.
     */
    parentIndex?: number;
}
/** @experimental */
interface LoopWinner<Task, Output> {
    task: Task;
    output: Output;
    verdict?: DefaultVerdict;
    iterationIndex: number;
    agentRunName: string;
}
/** @experimental */
interface LoopResult<Task, Output, Decision> {
    decision: Decision;
    iterations: Iteration<Task, Output>[];
    winner?: LoopWinner<Task, Output>;
    durationMs: number;
    /** Sum of every iteration's `costUsd`. */
    costUsd: number;
    /** Sum of every iteration's token usage. Forward to
     *  `ctx.cost.observeTokens` in a `runProfileMatrix` dispatch so the
     *  integrity guard sees real LLM activity. */
    tokenUsage: LoopTokenUsage;
    /** Domain-free run provenance for auditability: the mount manifest recorded
     *  during `prepareBox` and the selection receipts for how the winner was
     *  chosen. Always present; empty arrays when nothing was recorded. */
    provenance: RunProvenance;
}
/**
 * Minimal sandbox client surface the kernel calls. Satisfied structurally by
 * `new Sandbox({ apiKey, baseUrl })` — declared as a structural type so
 * tests can pass a stub without instantiating the SDK.
 *
 * `describePlacement` is optional. When present, the kernel calls it after
 * each `create()` so the `loop.iteration.dispatch` trace event carries fleet
 * coordinates (fleetId + machineId) instead of just the sibling sandboxId.
 * Fleet-aware adapters set this; the raw `Sandbox` SDK class does not, and
 * the kernel falls back to `{ placement: 'sibling', sandboxId: box.id }`.
 *
 * @experimental
 */
interface SandboxClient {
    create(options?: CreateSandboxOptions): Promise<SandboxInstance>;
    describePlacement?(box: SandboxInstance): LoopSandboxPlacement;
    /**
     * Optional CRIU capability probe. When present and it resolves
     * `{ available: true }`, the loop's `lineage.fork` seam may checkpoint+fork a
     * parent box so a fanout's branches inherit a shared context prefix; absent or
     * `false`, the fanout degrades to independent fresh boxes. The kernel reads
     * this ONLY through the capability probe — it never branches on backend kind.
     * The raw `Sandbox` SDK class satisfies it; the loop's test fakes omit it
     * (⇒ `canFork = false`).
     * @experimental
     */
    criuStatus?(): Promise<{
        available: boolean;
        criuVersion?: string;
        reason?: string;
    }>;
}
/**
 * Opt-in box-lineage controls for `runLoop`. Default OFF — with both flags
 * unset the kernel's per-iteration behavior is byte-identical to acquiring a
 * fresh box, streaming once, and tearing it down. The independence of N fresh
 * boxes (e.g. `random@k`) is a compute-control invariant; these flags must
 * never apply to it. Enable them ONLY on a steered loop (refine / planner-driven
 * fanout) where reusing the parent's context is intended.
 *
 * Live-box footprint: the lineage keeps every box it starts or forks alive
 * across rounds so a later round can descend from it, and tears them down at
 * loop end. When the driver's branch point is kernel-inferred (no
 * `describePlan` — refine, fanout-vote), the kernel prunes boxes no future
 * round can reach after each round, so the live set tracks the active frontier.
 * When the driver authors its own branch point (`describePlan().parentIndex`),
 * it may descend from any prior
 * iteration, so no box is pruned and the live-box count rises to the total
 * iterations across all rounds. Size `forkFanout` runs accordingly (CRIU forks
 * are copy-on-write, but each is still a live box until loop end).
 *
 * @experimental
 */
interface LoopLineageOptions {
    /**
     * When true, a refine round (1 planned task) descending from a prior round
     * CONTINUES the parent iteration's session on the SAME box
     * (`streamPrompt({ sessionId })`) instead of acquiring a fresh box and
     * re-injecting prior context as prompt text. Round 0 (no parent) always
     * starts fresh. Usable on any single-task path, not just the refine driver.
     *
     * Requires a platform that honors a client-supplied `sessionId`. The lineage
     * mints the id and `continue` asserts the session is still live
     * (`box.session(id).status()`), failing loud if the platform dropped it — so a
     * non-honoring platform errors instead of silently running contextless turns.
     * Verify continuity against the live platform before enabling: the assertion
     * proves the session EXISTS server-side, not that prior turns replay into it.
     */
    sessionContinuity?: boolean;
    /**
     * When true AND the platform reports CRIU fork support, a fanout round (N
     * planned tasks) descending from a prior round FORKS the parent iteration's
     * checkpoint so all N branches inherit a shared context prefix. Without fork
     * support it degrades to N independent fresh boxes (same result, no prefix).
     * Round 0 always starts fresh. NEVER set this for a `random@k` control arm —
     * forking would couple the independent samples.
     *
     * A real fork inherits the parent's IMAGE/PROFILE: per-branch `AgentRunSpec`
     * profiles are honored only on the degraded fresh-box path, so a
     * heterogeneous-profile fanout silently homogenizes to the parent's profile
     * when fork is available. Use this for same-profile branching; for
     * different-per-branch profiles use the unforked fanout path.
     */
    forkFanout?: boolean;
    /**
     * Per-turn sandbox streaming mode. Default `'sse'` (live `streamPrompt` —
     * low-latency, full per-token trace; best for interactive chat). `'poll'`
     * fire-and-detaches via `dispatchPrompt` and awaits the terminal result by
     * status-polling, so a long, quiet in-box turn (clone + build + test) never
     * holds a live stream a proxy idle-timeout can drop mid-execution. Lower trace
     * fidelity (one terminal event), so it is opt-in — intended for BATCH eval
     * runs, which don't need live streaming and were losing long turns to the
     * idle-drop. Applies to the default fresh-box path too, not only when
     * `sessionContinuity`/`forkFanout` are on.
     */
    streaming?: 'sse' | 'poll';
}
/** @experimental */
interface LoopSandboxPlacement {
    kind: 'sibling' | 'fleet';
    sandboxId?: string;
    fleetId?: string;
    machineId?: string;
}
/** @experimental */
interface LoopTraceEmitter {
    emit(event: LoopTraceEvent): void | Promise<void>;
}
/** @experimental */
type LoopTraceEvent = {
    kind: 'loop.started';
    runId: string;
    timestamp: number;
    payload: LoopStartedPayload;
} | {
    kind: 'loop.plan';
    runId: string;
    timestamp: number;
    payload: LoopPlanPayload;
} | {
    kind: 'loop.iteration.started';
    runId: string;
    timestamp: number;
    payload: LoopIterationStartedPayload;
} | {
    kind: 'loop.iteration.dispatch';
    runId: string;
    timestamp: number;
    payload: LoopIterationDispatchPayload;
} | {
    kind: 'loop.iteration.ended';
    runId: string;
    timestamp: number;
    payload: LoopIterationEndedPayload;
} | {
    kind: 'loop.decision';
    runId: string;
    timestamp: number;
    payload: LoopDecisionPayload;
} | {
    kind: 'loop.ended';
    runId: string;
    timestamp: number;
    payload: LoopEndedPayload;
} | {
    kind: 'loop.teardown.failed';
    runId: string;
    timestamp: number;
    payload: LoopTeardownFailedPayload;
};
/** @experimental */
interface LoopStartedPayload {
    driver: string;
    agentRunNames: string[];
    maxIterations: number;
    maxConcurrency: number;
}
/**
 * Emitted once per `plan()` round, immediately after the driver plans. Carries
 * the topology move so a viewer renders WHAT the agent decided + WHY, not just
 * the inferred fan-width. `moveKind` is the driver's `describePlan().kind` when
 * provided, else inferred from `plannedCount` (0→stop, 1→refine, N→fanout).
 *
 * @experimental
 */
interface LoopPlanPayload {
    /** 0-based plan round (one per `plan()` call). */
    roundIndex: number;
    /** Tasks the driver issued this round. */
    plannedCount: number;
    /** Topology move — `'refine' | 'fanout' | 'verify' | 'stop'` etc. */
    moveKind: string;
    /** Driver rationale for the move, when available. */
    rationale?: string;
    /**
     * Iteration index this round branched FROM (the edge source). `undefined`
     * for round 0 (root). Kernel-inferred branch point — the best-valid (else
     * latest) iteration so far — unless a driver later declares it explicitly.
     */
    parentIndex?: number;
    /** Iteration indices this round dispatched (the edge targets). */
    childIndices: number[];
}
/** @experimental */
interface LoopIterationStartedPayload {
    iterationIndex: number;
    agentRunName: string;
    taskHash: string;
    /** Plan round (== `LoopPlanPayload.roundIndex`) this iteration belongs to. */
    groupId?: number;
    /** Iteration this one was planned from; `undefined` ⇒ root. */
    parentIndex?: number;
}
/**
 * Where the iteration's worker was placed. `sibling` = a fresh sandbox the
 * kernel created via `sandboxClient.create`. `fleet` = an existing machine in
 * a shared-workspace fleet — workers see the caller's filesystem and any diff
 * they write lands on it directly.
 *
 * @experimental
 */
interface LoopIterationDispatchPayload {
    iterationIndex: number;
    agentRunName: string;
    placement: 'sibling' | 'fleet';
    /** Set on every placement. Lets analyst loops correlate per-iteration logs. */
    sandboxId?: string;
    /** Set only when `placement === 'fleet'`. */
    fleetId?: string;
    /** Set only when `placement === 'fleet'`. */
    machineId?: string;
    /** Plan round this iteration belongs to. */
    groupId?: number;
    /** Iteration this one was planned from; `undefined` ⇒ root. */
    parentIndex?: number;
}
/** @experimental */
interface LoopIterationEndedPayload {
    iterationIndex: number;
    agentRunName: string;
    outputHash?: string;
    verdict?: DefaultVerdict;
    error?: string;
    costUsd: number;
    durationMs: number;
    /** Summed LLM token usage for this iteration — maps to gen_ai.usage.* on the
     *  branch span. Omitted when no `llm_call` events carried token counts. */
    tokenUsage?: LoopTokenUsage;
    /** Plan round this iteration belongs to. */
    groupId?: number;
    /** Iteration this one was planned from; `undefined` ⇒ root. */
    parentIndex?: number;
    /** Truncated string preview of the parsed output — for a viewer's drawer.
     *  Bounded to ~280 chars; never the full payload. */
    outputPreview?: string;
}
/** @experimental */
interface LoopDecisionPayload {
    decision: string;
    historyLength: number;
}
/** @experimental */
interface LoopEndedPayload {
    winnerIterationIndex?: number;
    totalCostUsd: number;
    durationMs: number;
    iterations: number;
}
/** Emitted when a box's `delete()` throws or times out during teardown — the
 *  loop swallows the failure (platform reaps on expiry) but surfaces it here so
 *  a real leak (e.g. mid-loop auth expiry) is observable. @experimental */
interface LoopTeardownFailedPayload {
    sandboxId?: string;
    /** `'timeout'` or the delete error message. */
    reason: string;
}
/**
 * Execution context for `runLoop`: the sandbox client the kernel creates boxes through, plus optional runtime hooks.
 *
 * @experimental
 */
interface ExecCtx {
    /** Sandbox SDK client — the kernel calls `.create()` per iteration. */
    sandboxClient: SandboxClient;
    /** Optional runtime hooks. Execution-scoped; never part of `AgentProfile`. */
    hooks?: RuntimeHooks;
    /** Optional trace emitter. When set, the kernel emits `loop.*` events. */
    traceEmitter?: LoopTraceEmitter;
    /**
     * Optional per-event tee. When set, the kernel forwards EVERY raw event from
     * each iteration's `streamPrompt` stream as it arrives, so a host can stream
     * the agent's live output (tokens, tool calls) token-by-token. The observer
     * receives a defensive copy of each event — mutating it cannot affect the
     * run's own cost accounting or output parsing. Called synchronously in the hot
     * stream loop and never awaited, so a slow or never-settling observer cannot
     * stall the stream; keep it cheap. An async observer is fire-and-forget: its
     * promise is not awaited, so events carry no ordering or backpressure
     * guarantees (the next event may be observed before a prior async observer
     * settles) — use it for side-effect telemetry, not sequential processing.
     * Both a synchronous throw and a rejected returned promise are caught +
     * ignored so the observer can never break the run — but prefer not to depend
     * on that.
     *
     * @experimental
     */
    onSandboxEvent?: (event: SandboxEvent, meta: {
        iterationIndex: number;
        agentRunName: string;
    }) => void | PromiseLike<void>;
    /**
     * Optional production-run handle. When set, every synthesized `llm_call`
     * the kernel infers from a sandbox event stream is forwarded via
     * `runHandle.observe` so per-run cost aggregates pick up loop spend.
     */
    runHandle?: RuntimeRunHandle;
    /** Cooperative cancellation signal. */
    signal?: AbortSignal;
    /**
     * Trace id for OTEL correlation. When set alongside `traceEmitter`, the
     * exporter uses this as the parent trace for all emitted spans. Typically
     * inherited from TRACE_ID env var in MCP subprocess mode.
     */
    traceId?: string;
    /**
     * Parent span id for OTEL correlation. Loop events become children of
     * this span. Typically inherited from PARENT_SPAN_ID env var.
     */
    parentSpanId?: string;
}

export { type LoopDecisionPayload as $, type AgentRunSpec as A, type BackendErrorDetail as B, type RuntimeHookErrorContext as C, type RuntimeHookEvent as D, type ExecCtx as E, type RuntimeHookPhase as F, type RuntimeHookTarget as G, type RuntimeRunHandle as H, type Iteration as I, type RuntimeRunPersistenceAdapter as J, type KnowledgeReadinessDecision as K, type LoopTokenUsage as L, type RuntimeRunRow as M, composeRuntimeHooks as N, type OpenAIChatTool as O, defineRuntimeHooks as P, notifyRuntimeDecisionPoint as Q, type RuntimeHooks as R, type SandboxClient as S, notifyRuntimeHookEvent as T, startRuntimeRun as U, type Validator as V, type Driver as W, type LoopWinner as X, type LoopLineageOptions as Y, type LoopResult as Z, type MountRecorder as _, type RuntimeStreamEvent as a, type LoopEndedPayload as a0, type LoopIterationDispatchPayload as a1, type LoopIterationEndedPayload as a2, type LoopIterationStartedPayload as a3, type LoopPlanDescription as a4, type LoopPlanPayload as a5, type LoopStartedPayload as a6, type LoopTeardownFailedPayload as a7, type MountManifestEntry as a8, type RunProvenance as a9, type SelectionReceipt as aa, type ValidationCtx as ab, type OutputAdapter as b, type LoopTraceEvent as c, type LoopSandboxPlacement as d, type LoopTraceEmitter as e, type AgentBackendInput as f, type OpenAIChatToolChoice as g, type OpenAIChatResponseFormat as h, type AgentExecutionBackend as i, type AgentBackendContext as j, type RunAgentTaskOptions as k, type AgentTaskRunResult as l, type RunAgentTaskStreamOptions as m, type AgentRuntimeEvent as n, type AgentTaskStatus as o, type RuntimeSessionStore as p, type RuntimeSession as q, type AgentAdapter as r, type AgentKnowledgeProvider as s, type AgentRuntimeEventSink as t, type AgentTaskContext as u, type AgentTaskSpec as v, type RuntimeDecisionEvidenceRef as w, type RuntimeDecisionKind as x, type RuntimeDecisionPoint as y, type RuntimeHookContext as z };
