import { AgentProfile, AgentEvalError, AnalystFinding, KnowledgeReadinessReport, RunRecord, ControlEvalResult, KnowledgeRequirement } from '@tangle-network/agent-eval';
export { AgentEvalError, AgentEvalErrorCode, ConfigError, ControlBudget, ControlDecision, ControlEvalResult, ControlRunResult, ControlStep, DataAcquisitionPlan, JudgeError, KnowledgeReadinessReport, KnowledgeRequirement, NotFoundError, RunRecord, ValidationError } from '@tangle-network/agent-eval';
import { f as AgentBackendInput, O as OpenAIChatTool, g as OpenAIChatToolChoice, h as OpenAIChatResponseFormat, i as AgentExecutionBackend, j as AgentBackendContext, a as RuntimeStreamEvent, K as KnowledgeReadinessDecision, k as RunAgentTaskOptions, l as AgentTaskRunResult, m as RunAgentTaskStreamOptions, n as AgentRuntimeEvent, o as AgentTaskStatus, p as RuntimeSessionStore, q as RuntimeSession, R as RuntimeHooks } from './types-ESeMOj94.js';
export { r as AgentAdapter, s as AgentKnowledgeProvider, t as AgentRuntimeEventSink, u as AgentTaskContext, v as AgentTaskSpec, B as BackendErrorDetail, w as RuntimeDecisionEvidenceRef, x as RuntimeDecisionKind, y as RuntimeDecisionPoint, z as RuntimeHookContext, C as RuntimeHookErrorContext, D as RuntimeHookEvent, F as RuntimeHookPhase, G as RuntimeHookTarget, H as RuntimeRunHandle, J as RuntimeRunPersistenceAdapter, M as RuntimeRunRow, N as composeRuntimeHooks, P as defineRuntimeHooks, Q as notifyRuntimeDecisionPoint, T as notifyRuntimeHookEvent, U as startRuntimeRun } from './types-ESeMOj94.js';
import { Scenario, ProfileDispatchFn } from '@tangle-network/agent-eval/campaign';
import { C as CandidateGenerator } from './mcp-serve-verifier-FL7-ZEb_.js';
export { A as AgenticGeneratorOptions, I as ImprovementDriverOptions, M as McpServeSpec, V as Verifier, a as VerifyResult, b as agenticGenerator, c as commandVerifier, i as improvementDriver, m as mcpServeVerifier } from './mcp-serve-verifier-FL7-ZEb_.js';
import { Scenario as Scenario$1, SurfaceProposer, JudgeConfig, MutableSurface, DispatchContext, SelfImproveBudget, SelfImproveLlm, SelfImproveResult } from '@tangle-network/agent-eval/contract';
import { AgentProfile as AgentProfile$1 } from '@tangle-network/agent-interface';
import { S as SurfaceImprovementEdit } from './improvement-adapter-CDR8QNVM.js';
import { I as ImprovementAdapter } from './types-BC3bZpH0.js';
export { D as DELEGATED_LOOP_MODES, a as DelegatedLoopMode, b as DelegatedLoopRegistry, c as DelegatedLoopResult, d as DelegatedLoopRunner, L as LoopRunnerCliArgs, e as LoopRunnerCliResult, R as ResearchLoopResult, f as ResearchLoopRunnerOptions, g as RunDelegatedLoopOptions, V as VetoedFact, W as WorktreeLoopRunnerOptions, h as auditLoopRunner, i as isDelegatedLoopMode, p as parseLoopRunnerArgv, r as researchLoopRunner, j as runDelegatedLoop, k as runLoopRunnerCli, s as selfImproveLoopRunner, w as worktreeLoopRunner } from './loop-runner-bin-hQDUL8Ld.js';
export { m as mcpToolsForRuntimeMcp, a as mcpToolsForRuntimeMcpSubset } from './openai-tools-BnrOmGjN.js';
export { E as EvalRunEvent, a as EvalRunGeneration, b as EvalRunsExportConfig, c as EvalRunsExportResult, I as INTELLIGENCE_WIRE_VERSION, L as LoopSpanNode, d as OtelAttribute, e as OtelExportConfig, O as OtelExporter, f as OtelSpan, g as buildLoopOtelSpans, h as buildLoopSpanNodes, i as createOtelExporter, j as exportEvalRuns, l as loopEventToOtelSpan } from './otel-export-BKmNwiCb.js';
import '@tangle-network/sandbox';
import './local-harness-sI0S_XNA.js';
import 'node:child_process';
import './kb-gate-CwHO0vz6.js';
import './types-Driepl87.js';
import './worktree-fanout-D6xR2CIA.js';
import './worktree-harness-CX_McRLp.js';

/**
 *
 * Backend factories for `runAgentTaskStream`. Three shapes ship in core:
 *
 *  - `createIterableBackend` — wrap any custom async iterable into a backend
 *  - `createSandboxPromptBackend` — sandbox / sidecar `streamPrompt` clients
 *  - `createOpenAICompatibleBackend` — OpenAI-style chat completions endpoints
 *
 * Adapters stay thin: domain repos own auth, model selection, and the concrete
 * tool surface. The factories handle session creation, stream normalization,
 * and graceful end-of-stream signalling.
 *
 * @stable
 */

/** Wrap any custom async-iterable stream into a typed `AgentExecutionBackend`. @stable */
declare function createIterableBackend<TInput extends AgentBackendInput>(options: {
    kind: string;
    start?: AgentExecutionBackend<TInput>['start'];
    resume?: AgentExecutionBackend<TInput>['resume'];
    stream: AgentExecutionBackend<TInput>['stream'];
    stop?: AgentExecutionBackend<TInput>['stop'];
}): AgentExecutionBackend<TInput>;
/** Build an `AgentExecutionBackend` backed by a sandbox/sidecar `streamPrompt` call. @stable */
declare function createSandboxPromptBackend<TBox, TInput extends AgentBackendInput = AgentBackendInput>(options: {
    kind?: string;
    getBox(input: TInput, context: Omit<AgentBackendContext, 'session'>): Promise<TBox> | TBox;
    streamPrompt(box: TBox, message: string, context: AgentBackendContext): AsyncIterable<unknown>;
    mapEvent?: (event: unknown, context: AgentBackendContext) => RuntimeStreamEvent | undefined;
    getSessionId?: (box: TBox, input: TInput) => string | undefined;
}): AgentExecutionBackend<TInput>;
/** @stable */
/**
 * Retry policy for transient transport errors (rate limits, upstream
 * timeouts). Defaults to 5 attempts with exponential backoff starting at
 * 1s, ±25% jitter, capped at 30s. Set `maxAttempts: 1` to disable retries.
 *
 * Retried status codes:
 *   - 408 Request Timeout
 *   - 425 Too Early
 *   - 429 Too Many Requests
 *   - 500 / 502 / 503 / 504 — upstream transient failures
 *
 * Hard failures (401, 403, 4xx other than the above) propagate immediately.
 */
interface BackendRetryPolicy {
    /** Total attempts including the first try. Default 5. */
    maxAttempts?: number;
    /** Initial backoff in ms before the second attempt. Default 1000. */
    initialBackoffMs?: number;
    /** Hard ceiling on backoff in ms. Default 30000. */
    maxBackoffMs?: number;
    /** Jitter fraction in [0, 1]. Default 0.25 (±25%). */
    jitter?: number;
    /** Status codes that trigger a retry. Default: 408, 425, 429, 500, 502, 503, 504. */
    retryStatuses?: ReadonlyArray<number>;
    /**
     * Per-attempt wall-clock deadline in ms. If a single fetch attempt does
     * not return headers within this window the attempt is aborted and
     * retried. Default 120000 (2 min). Without this a hung upstream blocks
     * the attempt indefinitely — observed in production as a 15-minute
     * `fetch failed` that burned an entire eval persona. Set to 0 to disable.
     */
    requestTimeoutMs?: number;
}
/**
 *
 * OpenAI-compat streaming backend. Routes `runAgentTaskStream` through any
 * `POST /chat/completions` endpoint that speaks OpenAI's SSE protocol —
 * Tangle Router, OpenAI direct, OpenRouter, Groq, DeepSeek, Together. The
 * router also fronts Anthropic models in Anthropic-native SSE shape; this
 * backend handles both.
 *
 * ### Tool calls
 *
 * Pass `tools` (and optionally `toolChoice`) to forward an OpenAI Chat
 * Completions `tools[]` array on every request. Streamed `tool_call` chunks
 * are buffered until the model finalizes them (either `finish_reason:
 * 'tool_calls'` for OpenAI shape or a `content_block_stop` for Anthropic
 * `tool_use` blocks proxied through the router), then emitted as a single
 * `tool_call` RuntimeStreamEvent with the assembled `args`.
 *
 * The backend does NOT execute tools — it surfaces calls for the caller's
 * own dispatcher (typically the product's MCP / sandbox runtime) to fulfill
 * and feed back as a subsequent `messages` turn. This keeps the transport
 * thin and lets the agent host own tool dispatch policy.
 *
 * ### Fail-loud errors
 *
 * Non-success HTTP responses (4xx/5xx) and exhausted retry budgets throw
 * `BackendTransportError` from inside the `stream()` generator. The runtime
 * catches the throw, yields a `backend_error` with a typed `error` field
 * (`kind`, `status`, truncated `body`) and a terminal `final` event with
 * `status: 'failed'` carrying the same detail. Consumers MUST map
 * `final.error` onto their `RunRecord.error` — silently treating an empty
 * `finalText` as "agent produced nothing" hides credit exhaustion, auth
 * failure, and upstream outages.
 *
 * @stable
 */
declare function createOpenAICompatibleBackend<TInput extends AgentBackendInput = AgentBackendInput>(options: {
    apiKey: string;
    baseUrl: string;
    model: string;
    kind?: string;
    /**
     * OpenAI Chat Completions `tools[]` definitions surfaced to the model on
     * every request. Omit to send a tool-free request (existing behavior).
     * The runtime makes no assumption about the dispatcher — calls stream out
     * as `tool_call` events and the caller is responsible for executing them
     * and feeding `tool_result` messages back on a follow-up turn.
     */
    tools?: ReadonlyArray<OpenAIChatTool>;
    /**
     * OpenAI Chat Completions `tool_choice`. Default `undefined` (request
     * omits the field; provider falls back to its own default — typically
     * `'auto'`).
     */
    toolChoice?: OpenAIChatToolChoice;
    /**
     * OpenAI Chat Completions `response_format`. Omit for provider default text.
     */
    responseFormat?: OpenAIChatResponseFormat;
    fetchImpl?: typeof fetch;
    retry?: BackendRetryPolicy;
}): AgentExecutionBackend<TInput>;

/**
 *
 * Per-call resilience policy for participant backends: deadline, retry with
 * backoff, and a circuit breaker. Each policy is applied *around* a single
 * turn's backend invocation, not across the whole conversation — the
 * conversation-level credit cap and `maxTurns` bound the broader run.
 *
 * Deadlines abort the underlying backend stream via `AbortSignal` linkage so
 * the OpenAI/SDK clients tear down their HTTP request cleanly instead of
 * leaking sockets. Retries replay the same logical turn (same `turnId`) so
 * any caching gateway can dedupe. Circuit breakers are *per participant*: A's
 * failures don't open B's breaker.
 *
 * @stable
 */
/** Pure judgment of whether an error is worth retrying. Defaults: TimeoutError, AbortError, fetch-level network errors. */
type RetryableErrorPredicate = (err: unknown) => boolean;
/** Backoff between attempts. Constant ms, or `(attempt: 1-indexed) => ms`. */
type RetryBackoff = number | ((attempt: number) => number);
/** Circuit-breaker tuning. `failuresToOpen` consecutive failures opens it; closed only after `cooldownMs`. */
interface CircuitBreakerConfig {
    failuresToOpen: number;
    cooldownMs: number;
}
interface BackendCallPolicy {
    /** Per-attempt wall clock limit. Exceeding fires an AbortSignal and is treated as a retryable failure. */
    perAttemptDeadlineMs?: number;
    /** Number of retries after the first attempt; total attempts = 1 + maxRetries. Default 0. */
    maxRetries?: number;
    /** Backoff between attempts. Default 250ms with jitter. */
    retryBackoffMs?: RetryBackoff;
    /** Custom retry classifier. Defaults to {@link defaultIsRetryable}. */
    isRetryable?: RetryableErrorPredicate;
    /** Circuit breaker that opens after N consecutive failures per participant. */
    circuitBreaker?: CircuitBreakerConfig;
}
/** Thrown when the circuit breaker is open for a participant and no retry is allowed yet. */
declare class CircuitOpenError extends Error {
    constructor(participant: string, retryAfterMs: number);
}
/** Thrown when a backend call exceeds its per-attempt deadline. */
declare class DeadlineExceededError extends Error {
    constructor(deadlineMs: number);
}
/**
 * Default retryable classification — network/timeout class errors. Errors
 * a model deliberately throws (validation, refusal, 4xx) are not retried;
 * those represent real outcomes, not transient infrastructure faults.
 */
declare const defaultIsRetryable: RetryableErrorPredicate;
/** Live circuit-breaker state — one instance per (participant, conversation run). */
declare class CircuitBreakerState {
    private readonly config;
    private consecutiveFailures;
    private openedAt;
    constructor(config: CircuitBreakerConfig | undefined);
    /**
     * Check whether the next call is allowed. Throws `CircuitOpenError` when
     * the breaker is open and the cooldown hasn't elapsed.
     */
    preflight(participant: string, now?: number): void;
    recordSuccess(): void;
    recordFailure(now?: number): void;
}
/**
 * Build a per-attempt AbortSignal linked to the parent signal AND fired when
 * the deadline elapses. The returned `dispose()` MUST be called in a
 * `finally` (clears the timer, detaches the listener) so we don't leak.
 *
 * When the deadline fires, the signal's `reason` is a `DeadlineExceededError`
 * — callers can detect timeout-vs-cancel by reading `signal.reason` after
 * the underlying operation throws.
 */
declare function makePerAttemptSignal(parentSignal: AbortSignal | undefined, deadlineMs: number | undefined): {
    signal: AbortSignal;
    dispose: () => void;
    getDeadlineError(): DeadlineExceededError | undefined;
};
/** Compute the delay before the next attempt. Default: 250ms exponential with jitter. */
declare function computeBackoff(spec: RetryBackoff | undefined, attempt: number): number;
/** Resolve after `ms` milliseconds — used for retry backoff in conversation call policy. */
declare function sleep(ms: number): Promise<void>;

/**
 *
 * Cross-gateway forwarding headers — the wire-level contract that makes
 * agent-to-agent communication composable across organizational boundaries.
 * Every header here is read on inbound and re-emitted on outbound, so a chain
 * `caller → A's gateway → A's runtime → B's gateway → B's runtime` ends with
 * B billing the original user, the depth counter monotonically incremented,
 * and the run/turn correlation IDs preserved end-to-end.
 *
 * The actual depth refusal (HTTP 413 at MAX_DEPTH) is enforced by
 * `agent-gateway`'s middleware; this module owns the names + the propagation
 * rules so both sides agree.
 *
 * Full protocol: `docs/agent-bus-protocol.md`.
 *
 * @stable
 */
/** Standard names — lowercased so Headers maps interop on every runtime. */
declare const FORWARD_HEADERS: {
    /** Forwarded original-user identity (`Bearer sk-tan-<user>`); downstream gateways bill against this. */
    readonly authorization: "x-tangle-forwarded-authorization";
    /** Monotonically incremented on every gateway hop. Refused at MAX_DEPTH. */
    readonly depth: "x-tangle-forwarded-depth";
    /** Top-level conversation run identifier, propagated through every nested call. */
    readonly runId: "x-tangle-runid";
    /** This call's turn within the run; deterministic + stable across retries. */
    readonly turnId: "x-tangle-turnid";
    /** When the call is *inside* another turn (recursion), the parent turn's id. */
    readonly parentTurnId: "x-tangle-parent-turnid";
    /** Logical conversation peer label at the sending side, for trace stitching. */
    readonly speaker: "x-tangle-speaker";
};
type ForwardHeaderName = (typeof FORWARD_HEADERS)[keyof typeof FORWARD_HEADERS];
/** Hard cap on chained gateway hops; refused beyond this. Default keeps recursion bounded. */
declare const DEFAULT_MAX_DEPTH = 4;
/**
 * Read the depth counter off an inbound request. Missing → 0 (caller is the
 * origin). Non-integer → throws — silent coercion would let a bad caller
 * reset depth and bypass the limit.
 */
declare function readDepth(headers: Readonly<Record<string, string | string[] | undefined>>): number;
/**
 * Refuse further forwarding when the inbound depth has reached the limit.
 * Callers (the gateway middleware) translate the boolean to an HTTP 413.
 */
declare function isDepthExceeded(inboundDepth: number, max?: number): boolean;
/**
 * Build the headers to emit on an outbound participant call, given the
 * conversation's propagation context. Depth is incremented from the inbound
 * value; runId / turnId / speaker stamp the current hop; the user's
 * `Authorization` is preserved verbatim so the downstream gateway bills the
 * right wallet.
 */
declare function buildForwardHeaders(input: {
    inboundDepth: number;
    forwardedAuthorization?: string;
    runId: string;
    turnId: string;
    parentTurnId?: string;
    speaker: string;
}): Record<string, string>;
/**
 * Header bag carried through `AgentBackendContext.propagatedHeaders` so
 * backends that opt in can merge them into their outbound HTTP requests.
 * Distinct from `buildForwardHeaders` so callers can attach extra
 * non-protocol headers (e.g. tracing) without colliding.
 */
type PropagatedHeaders = Readonly<Record<string, string>>;

/**
 *
 * Durable conversation transcript — survives a driver process crash mid-run.
 * The runner journals every committed turn before yielding `turn_end`, so a
 * resumed run replays the same `runId` against the same journal and picks up
 * from the first un-recorded turn. Combined with the deterministic
 * `turnId(runId, index, speaker)`, a retried turn collides with the prior
 * attempt's id and any caching gateway can dedupe.
 *
 * The interface is small enough that a Cloudflare D1 / R2 / postgres adapter
 * is ~30 lines. The in-memory adapter is the default for tests and scratch.
 * The file adapter (JSONL on disk) is the default-durable choice when no
 * upstream store is wired.
 *
 * @stable
 */

interface ConversationJournalEntry {
    runId: string;
    startedAt: string;
    /** Set when the run reaches a terminal state. */
    halted?: HaltReason;
    endedAt?: string;
    turns: ConversationTurn[];
}
interface ConversationJournal {
    /**
     * Load any prior state for `runId`. Returns `undefined` for a fresh run.
     * Implementations MUST NOT mutate the returned object — the runner clones
     * before continuing — but the runtime treats absence and emptiness
     * identically, so a journal with zero turns is equivalent to "fresh."
     */
    loadRun(runId: string): Promise<ConversationJournalEntry | undefined>;
    /**
     * Initialise journal state for a fresh run. Called once per run, before any
     * `appendTurn`. Idempotent: calling with an existing runId is a no-op if
     * the entry already exists with the same `startedAt`.
     */
    beginRun(runId: string, startedAt: string): Promise<void>;
    /**
     * Append a committed turn. The runner only calls this AFTER the turn's
     * backend stream completed and the credit total has been updated, so an
     * appended turn is observed-committed and never speculative.
     */
    appendTurn(runId: string, turn: ConversationTurn): Promise<void>;
    /**
     * Record the run's terminal halt reason + end time. Once called, the run
     * is observed-final; subsequent `loadRun` returns the same halt.
     */
    recordHalt(runId: string, halt: HaltReason, endedAt: string): Promise<void>;
}
/** In-memory `ConversationJournal` — suitable for testing and single-process runs. */
declare class InMemoryConversationJournal implements ConversationJournal {
    private readonly entries;
    loadRun(runId: string): Promise<ConversationJournalEntry | undefined>;
    beginRun(runId: string, startedAt: string): Promise<void>;
    appendTurn(runId: string, turn: ConversationTurn): Promise<void>;
    recordHalt(runId: string, halt: HaltReason, endedAt: string): Promise<void>;
}
/**
 * JSONL on disk. One line per record; first line is the `begin`, subsequent
 * lines are `turn` records, terminal line is `halt`. Replays the whole file
 * on `loadRun` — cheap for the conversation sizes this is designed for
 * (thousands of turns, not millions). For huge runs, plug in a real DB
 * adapter; the interface is small.
 *
 * Each `appendTurn` / `recordHalt` calls `fsync` after the write so a
 * process crash between writes never loses an acknowledged turn.
 */
declare class FileConversationJournal implements ConversationJournal {
    private readonly path;
    constructor(path: string);
    loadRun(runId: string): Promise<ConversationJournalEntry | undefined>;
    beginRun(runId: string, startedAt: string): Promise<void>;
    appendTurn(runId: string, turn: ConversationTurn): Promise<void>;
    recordHalt(runId: string, halt: HaltReason, endedAt: string): Promise<void>;
    private appendRecord;
}

/**
 *
 * Public types for multi-agent conversations. A `Conversation` is two-or-more
 * participants taking turns through their own `AgentExecutionBackend`s, driven
 * by a `ConversationPolicy` (turn order, halting, hard credit ceiling).
 *
 * Each participant's backend can resolve to any reachable endpoint —
 * in-process iterable, local cli-bridge, sandbox, router, or a remote
 * agent-gateway — so the same `runConversation` call drives same-machine,
 * same-cloud, and cross-cloud orchestration without code change.
 *
 * @stable
 */

/** @stable */
interface ConversationParticipant {
    /**
     * Stable name used as the speaker label in the transcript. Must be unique
     * within a `Conversation`.
     */
    name: string;
    /**
     * Backend that runs this participant's turn. Reuses the existing
     * `AgentExecutionBackend` contract from `runAgentTaskStream`, so any
     * registered backend (iterable, sandbox, OpenAI-compatible) works without
     * adaptation.
     */
    backend: AgentExecutionBackend;
    /**
     * Optional human label for traces / dashboards. Distinct from `name`, which
     * is the addressing key.
     */
    label?: string;
    /**
     * Optional per-participant override of the conversation's default
     * `callPolicy`. Use to tighten the deadline or raise the retry budget for
     * a participant known to be slow or flaky.
     */
    callPolicy?: BackendCallPolicy;
    /**
     * Who pays for THIS participant's outbound calls?
     *
     * - `'forward-user'` (default) — propagate the caller's
     *   `X-Tangle-Forwarded-Authorization` so the downstream gateway bills the
     *   original user. Right for pass-through agents that aggregate/route
     *   without taking economic risk.
     * - `'agent-owned'` — DO NOT forward the user's auth; the participant's
     *   backend uses its own credentials (typically a sk-tan-AGENT or x402
     *   wallet baked into the backend at construction). Downstream charges
     *   land on the agent, not the user. Right for resold-bundle agents that
     *   take margin between their inbound price and their sub-agent costs.
     * - `(state) => AuthSource` — per-turn / per-condition decision, e.g. base
     *   sub-services are agent-owned but premium add-ons forward the user.
     *
     * The agent's own credentials live on the backend (set at construction
     * time, e.g. `createOpenAICompatibleBackend({ apiKey })`); this field is
     * purely about *whether to also forward the user's identity downstream*.
     */
    authSource?: AuthSource;
}
/** @stable */
type AuthSource = 'forward-user' | 'agent-owned' | ((state: ConversationDriveState) => 'forward-user' | 'agent-owned');
/** @stable */
type TurnOrder = 'alternate' | 'round-robin' | ((state: ConversationDriveState) => number);
/** @stable */
interface ConversationDriveState {
    transcript: readonly ConversationTurn[];
    turnIndex: number;
    spentCreditsCents: number;
}
/** @stable */
interface HaltContext extends ConversationDriveState {
    lastTurn: ConversationTurn;
}
/** @stable */
interface HaltSignal {
    halted: true;
    reason: string;
}
/** @stable */
type HaltPredicate = (ctx: HaltContext) => boolean | HaltSignal | Promise<boolean | HaltSignal>;
/** @stable */
type HaltReason = {
    kind: 'max_turns';
    turns: number;
} | {
    kind: 'max_credits';
    spentCents: number;
    capCents: number;
} | {
    kind: 'predicate';
    reason: string;
} | {
    kind: 'abort';
} | {
    kind: 'participant_error';
    participant: string;
    message: string;
};
/** @stable */
interface ConversationPolicy {
    /** Hard cap on speaker-turns. Each call into a participant's backend counts as 1. */
    maxTurns: number;
    /**
     * Hard cap on aggregate credit spend across all participants, in cents.
     * Computed by summing `llm_call.costUsd` from every participant's stream.
     * Unset (`undefined`) means no credit ceiling — the run is bounded only by
     * `maxTurns` and `haltOn`.
     */
    maxCreditsCents?: number;
    /**
     * Speaker selection. Defaults to `'alternate'` for two-participant
     * conversations and `'round-robin'` for any other arity.
     */
    turnOrder?: TurnOrder;
    /**
     * Optional convergence / content-based halt. Called after every turn ends;
     * returning truthy stops the loop with `{ kind: 'predicate', ... }`.
     */
    haltOn?: HaltPredicate;
    /**
     * Default per-turn resilience policy applied to every participant call
     * (deadline, retries, circuit breaker). Individual participants may
     * override via `ConversationParticipant.callPolicy`.
     */
    defaultCallPolicy?: BackendCallPolicy;
}
/** @stable */
interface ConversationTurn {
    index: number;
    speaker: string;
    /**
     * Deterministic turn identifier — stable across retries of the same logical
     * turn so caching gateways and trace backends can dedupe. Shape:
     * `${runId}.t${index}.${speakerSlug}`.
     */
    turnId: string;
    text: string;
    /**
     * Aggregated backend usage for this turn alone. Populated from any
     * `llm_call` stream events the backend emitted; `undefined` when the
     * backend reports no usage.
     */
    usage?: {
        tokensIn?: number;
        tokensOut?: number;
        costUsd?: number;
        latencyMs?: number;
        model?: string;
    };
    /**
     * Number of attempts that ran before this turn committed. `1` is the
     * common case; higher means the call policy retried after transient
     * failures.
     */
    attempts: number;
    startedAt: string;
    endedAt: string;
}
/** @stable */
interface Conversation {
    participants: readonly ConversationParticipant[];
    policy: ConversationPolicy;
}
/** @stable */
interface RunConversationOptions {
    /** First message kicking off the conversation. Routes to the first speaker. */
    seed: string;
    /**
     * Optional run identifier for cross-participant trace correlation. Auto-
     * generated when omitted. Reusing a runId against the same `journal`
     * resumes the prior run — the runner replays the persisted transcript and
     * continues from the first un-recorded turn.
     */
    runId?: string;
    /** Cancellation signal — aborts mid-stream and halts with `{ kind: 'abort' }`. */
    signal?: AbortSignal;
    /**
     * Event sink for per-turn micro-events. Distinct from the result transcript:
     * the sink fires for every text-delta, every turn-start/end, and the
     * conversation-start/end markers. Used to drive SSE / dashboard updates
     * without waiting for the conversation to finish.
     */
    onEvent?: (event: ConversationStreamEvent) => void | Promise<void>;
    /**
     * Optional durable transcript. When set, the runner persists every
     * committed turn before yielding `turn_end`. Reusing the same `runId`
     * against the same journal resumes from the last committed turn — so a
     * driver process crash mid-run loses zero acknowledged turns.
     */
    journal?: ConversationJournal;
    /**
     * Headers to forward verbatim to every participant backend call (gateway
     * propagation: `X-Tangle-Forwarded-Authorization`, run/turn correlation,
     * depth counter). Backends opt in by reading `propagatedHeaders` from
     * their `AgentBackendContext`; backends that ignore the field still work.
     */
    propagatedHeaders?: PropagatedHeaders;
    /**
     * Inbound depth at the point this driver was invoked. The runner
     * increments it on every outbound participant call; gateways refuse at
     * `DEFAULT_MAX_DEPTH`. Default 0 (origin caller).
     */
    inboundDepth?: number;
    /**
     * Parent turn id when this conversation is *inside* another turn (i.e. the
     * driver is itself a participant via `createConversationBackend`). The
     * runner stamps each outbound call with this as `X-Tangle-Parent-TurnId`
     * so trace stitching survives nested orchestration.
     */
    parentTurnId?: string;
}
/** @stable */
interface ConversationResult {
    runId: string;
    transcript: ConversationTurn[];
    turns: number;
    spentCreditsCents: number;
    halted: HaltReason;
    durationMs: number;
    startedAt: string;
    endedAt: string;
}
/** @stable */
type ConversationStreamEvent = {
    type: 'conversation_start';
    runId: string;
    participants: readonly string[];
    seed: string;
    timestamp: string;
} | {
    type: 'conversation_resumed';
    runId: string;
    participants: readonly string[];
    transcript: readonly ConversationTurn[];
    timestamp: string;
} | {
    type: 'turn_start';
    runId: string;
    index: number;
    speaker: string;
    turnId: string;
    attempt: number;
    timestamp: string;
} | {
    type: 'turn_text_delta';
    runId: string;
    index: number;
    speaker: string;
    turnId: string;
    text: string;
    timestamp?: string;
} | {
    type: 'turn_retry';
    runId: string;
    index: number;
    speaker: string;
    turnId: string;
    attempt: number;
    reason: string;
    timestamp: string;
} | {
    type: 'turn_end';
    runId: string;
    turn: ConversationTurn;
    timestamp: string;
} | {
    type: 'conversation_end';
    runId: string;
    result: ConversationResult;
    timestamp: string;
};

/**
 *
 * Wrap a `Conversation` so it satisfies `AgentExecutionBackend`. The result is
 * an addressable "single agent" whose internal behavior is an N-party
 * orchestrated conversation — the recursion primitive that lets a swarm be a
 * participant inside another swarm, or be published behind a single
 * agent-gateway endpoint.
 *
 * Stream events from inner participants are NOT forwarded verbatim. Outer
 * callers see one `text_delta` per inner turn (the turn's full text), tagged
 * with `[speaker] ` prefix so the outer transcript stays attributable. The
 * conversation's `conversation_end` halt reason rides on a `final` event.
 *
 * @stable
 */

declare function createConversationBackend(options: {
    conversation: Conversation;
    /** Optional backend kind label. Defaults to `'conversation'`. */
    kind?: string;
}): AgentExecutionBackend;

/**
 *
 * Declarative constructor for a multi-agent `Conversation`. Validates inputs
 * fail-loud at definition time (duplicate participant names, alternate order
 * with ≠2 participants, non-positive `maxTurns`) so misconfiguration is caught
 * before `runConversation` is called and not buried inside a streaming run.
 *
 * @stable
 */

declare function defineConversation(input: {
    participants: ConversationParticipant[];
    policy: ConversationPolicy;
}): Conversation;

/**
 *
 * Durable conversation journal backed by any SQL store. Adapter-agnostic by
 * design: callers wire a `SqlAdapter` against their driver of choice (D1,
 * postgres, sqlite, libSQL…) and the same journal implementation persists
 * conversation runs durably across process restarts. The schema is two
 * tables — runs (latest state) + events (append-only log) — so a partial
 * crash in the middle of a turn leaves an unambiguous "last committed turn"
 * to resume from.
 *
 * Why not bake in a specific driver? agent-runtime ships against multiple
 * runtimes (Cloudflare Workers, Node, Bun, Deno) and consumers' fleets have
 * already standardized on one of D1 / postgres / sqlite / libSQL. Adapter
 * indirection costs ~5 lines per driver in the consumer's code and keeps the
 * SDK free of native deps.
 *
 * @example D1 (Cloudflare Workers)
 *   import { SqlConversationJournal, d1ToSqlAdapter } from '@tangle-network/agent-runtime'
 *   const journal = new SqlConversationJournal(d1ToSqlAdapter(env.DB))
 *   await journal.migrate()                     // once at deploy
 *   await runConversation(conv, { seed, journal, runId: 'run_abc' })
 *
 * @example node-postgres
 *   import { Pool } from 'pg'
 *   const pool = new Pool({ connectionString: process.env.DATABASE_URL })
 *   const pg: SqlAdapter = {
 *     exec: async (sql, params = []) => {
 *       const r = await pool.query(sql, params as never)
 *       return { rowsAffected: r.rowCount ?? 0 }
 *     },
 *     query: async (sql, params = []) => (await pool.query(sql, params as never)).rows,
 *   }
 *   const journal = new SqlConversationJournal(pg)
 *   await journal.migrate()
 *
 * @stable
 */

/**
 * Minimal SQL driver shape. Implementations forward to whichever client the
 * deployment already uses; agent-runtime takes no opinion on which.
 *
 * Parameter placeholders MUST be `?` (positional). All adapters listed in the
 * file header accept this convention.
 */
interface SqlAdapter {
    /** Execute a write statement (INSERT/UPDATE/DELETE/DDL). */
    exec(sql: string, params?: readonly unknown[]): Promise<{
        rowsAffected: number;
    }>;
    /** Execute a read statement (SELECT). Returns rows as plain objects. */
    query<TRow = Record<string, unknown>>(sql: string, params?: readonly unknown[]): Promise<TRow[]>;
}
/**
 * Adapt a Cloudflare D1 binding to the SqlAdapter shape. Lives here so D1
 * consumers don't have to write the wrapper themselves; the runtime never
 * imports `@cloudflare/workers-types` directly (peer-style typing).
 */
declare function d1ToSqlAdapter(db: D1DatabaseLike): SqlAdapter;
/**
 * Structural type matching the surface of `D1Database` we depend on, so the
 * SDK never imports `@cloudflare/workers-types`. Consumers pass their real
 * `D1Database` from `env.DB` and TS structural compatibility lines it up.
 */
interface D1DatabaseLike {
    prepare(sql: string): D1StmtLike;
}
interface D1StmtLike {
    bind(...params: unknown[]): D1StmtLike;
    run(): Promise<unknown>;
    all<TRow = unknown>(): Promise<{
        results?: TRow[];
    }>;
}
/**
 * SQL-backed ConversationJournal. Two tables — runs (one row per runId, holds
 * start/halt timestamps + halt reason) and turns (one row per committed turn,
 * payload is the ConversationTurn JSON). Replays the turns table on
 * `loadRun` and writes append-only per `appendTurn`.
 */
declare class SqlConversationJournal implements ConversationJournal {
    private readonly db;
    private readonly table;
    /**
     * @param db    SQL adapter (D1, postgres, sqlite, libSQL — all work)
     * @param table Table-name prefix; the journal creates `${table}_runs` and
     *              `${table}_turns`. Lets multiple journals share a database
     *              without colliding (e.g. one per product surface).
     */
    constructor(db: SqlAdapter, table?: string);
    /**
     * Create the journal's tables if absent. Idempotent. Call once at deploy
     * (or at app boot) — running on every request is harmless but adds latency.
     */
    migrate(): Promise<void>;
    loadRun(runId: string): Promise<ConversationJournalEntry | undefined>;
    beginRun(runId: string, startedAt: string): Promise<void>;
    appendTurn(runId: string, turn: ConversationTurn): Promise<void>;
    recordHalt(runId: string, halt: HaltReason, endedAt: string): Promise<void>;
}

/**
 *
 * Conversation orchestrator. Drives N participants in turn through their own
 * `AgentExecutionBackend`s, aggregating per-turn text + usage, enforcing
 * `maxTurns` / `maxCreditsCents` / `haltOn`, and emitting per-event stream
 * markers so callers can plumb the run through SSE without buffering.
 *
 * `runConversation` returns the full result; `runConversationStream` returns
 * an `AsyncIterable<ConversationStreamEvent>` for callers that want to
 * forward events as they arrive. Both share one driving loop.
 *
 * Distributed-systems primitives layered on top of the loop:
 *   - **Idempotent turn ids** — `turnId(runId, index, speaker)` stays stable
 *     across retries so caching gateways can dedupe.
 *   - **Durable journal** — optional `ConversationJournal` persists every
 *     committed turn; reusing a runId against the same journal resumes
 *     transparently from the last committed turn.
 *   - **Per-turn call policy** — deadline, retry-with-backoff, and a
 *     per-participant circuit breaker. Retries replay the same logical turn
 *     (same `turnId`); the retry loop lives inside the outer generator so
 *     deltas yield naturally without cross-coroutine buffering.
 *   - **Header propagation** — run/turn/depth headers (+ forwarded user
 *     authorization) stamped onto every outbound backend call so downstream
 *     gateways can bill the right user and enforce `X-Tangle-Forwarded-Depth`.
 *
 * Credit cap is enforced *between turns*, not mid-stream: a turn that
 * overshoots the cap completes, the cap then halts the conversation before
 * the next turn.
 *
 * @stable
 */

declare function runConversation(conversation: Conversation, options: RunConversationOptions): Promise<ConversationResult>;
/** Streaming conversation orchestrator: drives N participants in turn through their own backends, enforcing `maxTurns` / `maxCreditsCents` / `haltOn`, yielding per-event stream markers. */
declare function runConversationStream(conversation: Conversation, options: RunConversationOptions): AsyncIterable<ConversationStreamEvent>;

/**
 * `runPersonaConversation` — the persona loop runner: run a WORKER `AgentProfile`
 * (the agent under test) as a multi-round conversation driven by a PERSONA (the
 * simulated user), over the persistent conversation transcript.
 *
 * It is profiles-vs-profiles: the persona is itself a driver `AgentProfile` (an
 * LLM role-playing the user from its facts) — `runConversation` runs the two
 * against each other. Scripted persona turns are kept as a deterministic
 * fast-path. Only the WORKER is metered (it is the side under test); the
 * persona-driver is the test harness, not billed against the agent.
 *
 * `runPersonaDispatch` wraps the runner as a `ProfileDispatchFn` so it drops
 * straight into `runProfileMatrix({ dispatch })` — the same loop serves a single
 * cell and the whole matrix, replacing the per-agent hand-rolled
 * `dispatchWithSurface` bridges.
 */

/** A persona that drives the conversation: either a full driver `AgentProfile`
 *  (an LLM user-sim) or a deterministic script of user turns (the fast-path). */
type PersonaDriver = {
    kind: 'profile';
    profile: AgentProfile;
} | {
    kind: 'scripted';
    turns: string[];
};
interface RunPersonaConversationOptions {
    /** The agent under test. Metered; its rendered prompt leads its turns. */
    worker: AgentProfile;
    /** The simulated user driving the dialogue. */
    persona: PersonaDriver;
    /** Turn an `AgentProfile` into a runnable backend (router / sandbox / fake).
     *  Applied to the worker and to a `profile`-kind persona. */
    backendFor: (profile: AgentProfile, role: 'worker' | 'persona') => AgentExecutionBackend;
    /** Render a profile's system prompt — prepended to that profile's messages. */
    systemPromptOf: (profile: AgentProfile) => string;
    /** Speaker-turn cap. Default for a scripted persona = `2 * turns.length`
     *  (worker answers each user turn). REQUIRED for a `profile` persona. */
    maxTurns?: number;
    /** Kickoff message routed to the first speaker (the persona). Default 'Begin.' */
    seed?: string;
    /** Content-based "until satisfied" halt, called after every turn. `maxTurns` is the
     *  hard ceiling; this is the early stop (the persona declares the goal met / unreachable). */
    haltOn?: HaltPredicate;
    signal?: AbortSignal;
    /** Worker participant / transcript speaker label. Default 'agent'. */
    workerName?: string;
}
interface PersonaConversationResult {
    transcript: ConversationTurn[];
    turns: number;
    halted: HaltReason;
    /** Worker-only spend (the side under test). */
    costUsd: number;
    tokensIn: number;
    tokensOut: number;
}
/**
 * Run one worker profile against one persona as a multi-round conversation.
 * The persona leads (participant 0): it speaks, the worker answers, repeat,
 * until `maxTurns`. Returns the persistent transcript + worker-only usage.
 */
declare function runPersonaConversation(opts: RunPersonaConversationOptions): Promise<PersonaConversationResult>;
interface RunPersonaConfig<TScenario extends Scenario, TArtifact> {
    /** Turn an `AgentProfile` into a runnable backend (router / sandbox / fake). */
    backendFor: (profile: AgentProfile, role: 'worker' | 'persona') => AgentExecutionBackend;
    /** Render a profile's system prompt. */
    systemPromptOf: (profile: AgentProfile) => string;
    /** The persona driving each scenario — a driver profile or scripted turns. */
    personaOf: (scenario: TScenario) => PersonaDriver;
    /** Build the scored artifact from the finished transcript. */
    artifactOf: (transcript: ConversationTurn[], scenario: TScenario) => TArtifact;
    /** Speaker-turn cap (required when a persona is profile-driven). */
    maxTurns?: (scenario: TScenario) => number;
    seed?: (scenario: TScenario) => string;
    workerName?: string;
}
/**
 * Wrap {@link runPersonaConversation} as a `ProfileDispatchFn` for
 * `runProfileMatrix`: the profile axis is the worker-under-test, the scenario
 * axis is the persona, and the runner is the cell. Meters the worker through
 * `ctx.cost` so the matrix's backend-integrity guard sees real usage.
 */
declare function runPersonaDispatch<TScenario extends Scenario, TArtifact>(config: RunPersonaConfig<TScenario, TArtifact>): ProfileDispatchFn<TScenario, TArtifact>;

/**
 *
 * Deterministic turn identifier. Stable across retries of the same logical
 * turn so backends (and any caching gateway in between) can dedupe on it.
 * A retry triggered by a network blip or deadline timeout MUST produce the
 * same `turn_id`; only the underlying attempt count differs.
 *
 * Shape: `${runId}.t${index}.${speakerSlug}` — readable in logs, sortable by
 * turn index, attributable to a speaker. Slugify keeps the speaker portion
 * URL-safe so it can ride in HTTP headers without escaping.
 *
 * @stable
 */
declare function turnId(runId: string, index: number, speaker: string): string;
/**
 * Reduce a speaker name to ASCII alphanumerics + dashes. Preserves enough
 * substance to read in a log line; collisions between speakers within a
 * single Conversation are prevented by `defineConversation`'s
 * unique-name check, so the slug only needs to be deterministic, not unique.
 */
declare function slugifySpeaker(speaker: string): string;

/**
 * `handleChatTurn` — framework-neutral chat-turn HTTP orchestrator.
 * Owns the NDJSON `ChatStreamEvent` line protocol, the `session.run.*`
 * lifecycle vocabulary, and the persist / post-process / trace-flush
 * hook order. Returns a `ReadableStream` body the product hands to its
 * platform `Response`.
 *
 * Execution durability is the substrate's concern: `box.streamPrompt`
 * auto-reconnects in-call; cross-process reconnect via `X-Execution-ID`
 * is the product's job. The producer this engine wraps already speaks
 * that protocol — the engine just frames the events.
 *
 * Hooks (`ChatTurnHooks`):
 *   - `produce`                  — build the backend event stream
 *   - `persistAssistantMessage`  — write the assistant turn to the product DB
 *   - `onTurnComplete?`          — post-process (proposals, citations, …)
 *   - `onEvent?`                 — per-event side channel (e.g. DO broadcast)
 *   - `transformFinalText?`      — pre-persist transform (e.g. PII redact)
 *   - `traceFlush?`              — handed to waitUntil so OTLP export lands
 *
 * Framework neutrality: takes already-resolved values (`identity` tuple,
 * a `waitUntil`), never a `Request` or a `Context`. The product's thin
 * route adapter does auth + parse + access-control, then calls
 * `handleChatTurn(...)` and returns `result.body` as its platform `Response`.
 */
/** The NDJSON line protocol every product chat client already speaks. */
interface ChatStreamEvent {
    type: string;
    data?: Record<string, unknown>;
}
/** Identity of a chat turn. `tenantId` is the workspace id for workspace-
 *  scoped products and the user id for session-scoped products. */
interface ChatTurnIdentity {
    tenantId: string;
    /** Thread / session id. */
    sessionId: string;
    userId: string;
    /** Monotonic 0-based turn index within the session. */
    turnIndex: number;
}
/** The live side of a turn — what the product's `produce` hook returns. */
interface ChatTurnProducer<TEvent extends ChatStreamEvent = ChatStreamEvent> {
    /** The turn's event stream. Forwarded verbatim to the caller. */
    stream: AsyncGenerator<TEvent, void, unknown>;
    /** The turn's final assistant text. Read once, after `stream` drains. */
    finalText(): string;
}
interface ChatTurnHooks {
    /** Build the backend stream. The engine forwards events verbatim and
     *  reads `finalText()` once the stream drains. */
    produce(): ChatTurnProducer;
    /** Persist the assistant message to the product's own store. Called
     *  once, after drain, with the assembled (transform-applied) text. */
    persistAssistantMessage(input: {
        identity: ChatTurnIdentity;
        finalText: string;
    }): Promise<void>;
    /** Optional post-processing (proposals, citations, credit metering …).
     *  Errors are swallowed + logged — post-process must never fail a turn
     *  that already streamed successfully. */
    onTurnComplete?(input: {
        identity: ChatTurnIdentity;
        finalText: string;
    }): Promise<void>;
    /** Optional per-event side channel (e.g. DO broadcast). Runs for every
     *  emitted event, lifecycle envelope included. Errors swallowed — a
     *  broadcast failure must not break the chat stream. */
    onEvent?(event: ChatStreamEvent): void | Promise<void>;
    /** Optional pre-persist transform of the final text (e.g. PII
     *  redaction). Affects only what is persisted; the live stream is
     *  never altered. */
    transformFinalText?(text: string): string | Promise<string>;
    /** Optional trace flush — resolves when OTLP export completes. Handed
     *  to `waitUntil` so the worker isolate stays alive for the POST. */
    traceFlush?(): Promise<void>;
}
interface RunChatTurnInput {
    identity: ChatTurnIdentity;
    hooks: ChatTurnHooks;
    /** Worker liveness hook. When omitted, trace flush is awaited inline
     *  before the stream closes. */
    waitUntil?: (p: Promise<unknown>) => void;
    /** Structured logger for swallowed hook errors. Defaults to
     *  `console.error` so failures surface without product wiring. */
    log?: (message: string, meta?: Record<string, unknown>) => void;
}
interface ChatTurnResult {
    /** NDJSON body — return this as the platform `Response` body. */
    body: ReadableStream<Uint8Array>;
    /** Content type for the response. */
    contentType: 'application/x-ndjson';
}
/**
 * Run one chat turn. Returns immediately with a `ReadableStream` body;
 * the turn executes as the body is pulled. Never rejects — backend
 * failures surface as `error` + `session.run.failed` events.
 */
declare function handleChatTurn(input: RunChatTurnInput): ChatTurnResult;

/**
 * Derive a stable executionId from the run identity. The same
 * `(projectId, sessionId, turnIndex)` tuple yields the same id — so a
 * client retry of the same turn lands on the same substrate execution
 * and the orchestrator's buffer replays instead of starting a second
 * prompt.
 *
 * Format is readable, not hashed: operators grepping orchestrator logs
 * for `gtm-agent:thread-abc:3` find the run without translating an
 * opaque id. Substrate executionIds are not a secrecy boundary.
 *
 * Wire integration:
 *   - Sandbox PromptOptions accepts `executionId` and `lastEventId`.
 *     Products pass this id to make cross-process reconnect land on the
 *     same substrate execution instead of spawning a duplicate run.
 */
declare function deriveExecutionId(input: {
    projectId: string;
    sessionId: string;
    turnIndex: number;
}): string;

/**
 *
 * Error taxonomy for `@tangle-network/agent-runtime`.
 *
 * Public contract: every error this package throws as part of its consumer-
 * facing API either extends `AgentEvalError` (re-exported here for ergonomic
 * `instanceof` checks at the runtime boundary) or extends one of the
 * runtime-specific subclasses below.
 *
 * Internal invariant guards (`throw new Error('this should never happen')`)
 * remain plain `Error` — they are programmer-mistake assertions, not
 * consumer-catchable contract failures.
 *
 * Subclassing strategy: where a runtime-specific failure maps cleanly to an
 * agent-eval code (validation, config, not_found), we re-use the agent-eval
 * subclass. Runtime-only failure modes (session resume against the wrong
 * backend, backend transport errors) get fresh subclasses that still carry an
 * `AgentEvalErrorCode` so cross-package handlers can pattern-match without
 * importing the runtime.
 *
 * @stable
 */

/**
 *
 * A backend transport call (HTTP, gRPC, sidecar IPC) failed with a non-success
 * status. Distinct from `JudgeError` (which is structural / unrecoverable)
 * because backend failures are sometimes retryable and consumers may want to
 * branch on the upstream status code.
 *
 * @stable
 */
declare class BackendTransportError extends AgentEvalError {
    readonly backend: string;
    readonly status?: number;
    /**
     * Truncated upstream response body (≤2 KiB) when available. Diagnostic
     * only — surfaces in `backend_error.error.body` and `final.error.body`
     * so operators can see "free_tier_limit", "invalid_api_key", etc. without
     * cracking the log line open.
     */
    readonly body?: string;
    constructor(backend: string, message: string, options?: {
        cause?: unknown;
        status?: number;
        body?: string;
    });
}
/**
 *
 * A runtime-run lifecycle method was called in an order the state machine does
 * not allow: `persist()` before `complete()`, `complete()` twice, etc.
 *
 * @stable
 */
declare class RuntimeRunStateError extends AgentEvalError {
    constructor(message: string, options?: {
        cause?: unknown;
    });
}
/**
 *
 * The dynamic-loop planner returned an unusable topology move — the LLM emitted
 * no parseable envelope, an unknown `kind`, or a structurally-invalid move
 * (e.g. a fanout with zero tasks). This is a structural failure of the
 * agent-authored topology, not a config mistake: the planner ran but its output
 * cannot drive the kernel. Carries `validation` so cross-package handlers can
 * pattern-match without importing the runtime. Fail loud — never substitute a
 * default move, or the loop silently runs a topology nobody chose.
 *
 * @stable
 */
declare class PlannerError extends AgentEvalError {
    constructor(message: string, options?: {
        cause?: unknown;
    });
}

/**
 * Build-prompt starting points for the two buildable artifact types. There is
 * NO `toolGenerator`/`mcpGenerator` wrapper — the factory is `agenticGenerator`
 * + a verifier (docs/artifact-lifecycle-frontier.md), so a tool or an MCP
 * server is built by composing the pieces directly:
 *
 *   // a tool:
 *   agenticGenerator({ buildPrompt: toolBuildPrompt, verify: commandVerifier('pnpm', ['test']) })
 *   // an MCP server:
 *   agenticGenerator({ buildPrompt: mcpBuildPrompt, verify: mcpServeVerifier({ command: 'node', args: ['server.mjs'] }) })
 *
 * These are the only type-specific bit (the phrasing that points the agent at a
 * tool vs. an MCP); the worktree, resume-on-failure loop, and improvement-loop
 * wrapper are shared. MCP is the load-bearing target — it is how a harness
 * acquires tools; raw tools matter where we control the loader.
 */

type FindingsArg = {
    report: unknown;
    findings: AnalystFinding[];
};
/** Build the starting instruction for a coder agent tasked with implementing a new tool. */
declare function toolBuildPrompt(args: FindingsArg): string;
/** Build the starting instruction for a coder agent tasked with implementing a new MCP server. */
declare function mcpBuildPrompt(args: FindingsArg): string;

/**
 *
 * `improve` — the ONE public, surface-pluggable RSI verb.
 *
 * A thin facade over agent-eval's `selfImprove` (the held-out-gated closed
 * loop). It removes the two things a caller otherwise has to know to drive the
 * loop by hand: WHICH `MutableSurface` of the profile is being optimized, and
 * WHICH `SurfaceProposer` mutates that surface. You name a `surface`; the
 * facade picks the matching default proposer, extracts the baseline surface from
 * the profile, runs `selfImprove`, and (on a ship verdict) writes the promoted
 * winner back into the corresponding profile field.
 *
 *   - `surface: 'prompt'` → `gepaProposer` mutates `profile.prompt.systemPrompt`.
 *   - `surface: 'skills'` → `skillOptProposer` mutates a skills document string.
 *   - `surface` ∈ {`tools`, `mcp`, `hooks`, `code`} → no zero-config default
 *     proposer exists (a code/config proposer needs caller-supplied wiring — a
 *     worktree repo root, a candidate generator, a serializer). The facade
 *     requires an explicit `opts.generator` for these and throws a `ConfigError`
 *     otherwise. This is a designed boundary, not a missing default: there is
 *     no safe value the facade could invent for those seams.
 *
 * Everything else (`scenarios`, `judge`, `agent`, `budget`, `llm`) passes
 * straight through to `selfImprove`.
 *
 * @experimental
 */

/** The agent-profile lever `improve` optimizes. Mirrors the AgentProfile-law
 *  profile levers; `code` is the implementation-tier surface. */
type ImproveSurface = 'prompt' | 'skills' | 'tools' | 'mcp' | 'hooks' | 'code';
interface ImproveOptions<TScenario extends Scenario$1, TArtifact> {
    /** Which profile lever to optimize. Default `'prompt'`. Selects the default
     *  generator + the baseline-surface extraction shape. */
    surface?: ImproveSurface;
    /** The `SurfaceProposer` that mutates the surface. When unset, the facade
     *  picks the default for `surface` (`gepaProposer` for prompt, `skillOptProposer`
     *  for skills); surfaces with no default REQUIRE this (fail-loud otherwise). */
    generator?: SurfaceProposer;
    /** Gate mode. `'holdout'` (default) runs the held-out promotion gate;
     *  `'none'` is a baseline-only run (`budget.generations = 0`). */
    gate?: 'holdout' | 'none';
    /** Scenarios to evaluate against. Passthrough to `selfImprove`. */
    scenarios: TScenario[];
    /** Judge that scores artifacts. Passthrough to `selfImprove`. */
    judge: JudgeConfig<TArtifact, TScenario>;
    /** The agent under improvement — same shape as `selfImprove.agent`: it takes
     *  the current surface + scenario + ctx and returns the artifact to judge. */
    agent: (surface: MutableSurface, scenario: TScenario, ctx: DispatchContext) => Promise<TArtifact>;
    /** Budget + loop shape. Passthrough; `gate: 'none'` forces `generations = 0`. */
    budget?: SelfImproveBudget;
    /** LLM config. Passthrough to `selfImprove` AND used to construct the default
     *  reflective proposer (`gepaProposer`/`skillOptProposer`) when `generator` is unset. */
    llm?: SelfImproveLlm;
    /** Restrict the run to this subset of models. When set, the reflection model
     *  (`llm.model`, or the default when unset) must be a member, or `improve()` throws
     *  a `ConfigError` before the generator is built. Unset = unrestricted. */
    allowedModels?: readonly string[];
}
interface ImproveResult<TScenario extends Scenario$1, TArtifact> {
    /** The profile after improvement: the winner surface applied back into the
     *  matching field when the gate shipped, else the input profile unchanged. */
    profile: AgentProfile$1;
    /** True when `gateDecision === 'ship'`. */
    shipped: boolean;
    /** Held-out lift (`winner − baseline` composite). */
    lift: number;
    /** The five-valued gate verdict from `selfImprove`. */
    gateDecision: SelfImproveResult<TScenario, TArtifact>['gateDecision'];
    /** Full `selfImprove` result for advanced inspection. */
    raw: SelfImproveResult<TScenario, TArtifact>;
}
/**
 * Run the held-out-gated self-improvement loop on ONE profile surface.
 *
 * @example Optimize the system prompt, default holdout gate:
 *
 *   const out = await improve(profile, findings, {
 *     surface: 'prompt',
 *     scenarios,
 *     judge,
 *     agent: (surface, scenario, ctx) => runAgent(surface, scenario, ctx.signal),
 *   })
 *   if (out.shipped) deploy(out.profile)
 */
declare function improve<TScenario extends Scenario$1, TArtifact>(profile: AgentProfile$1, findings: unknown[], opts: ImproveOptions<TScenario, TArtifact>): Promise<ImproveResult<TScenario, TArtifact>>;

/**
 *
 * `reflectiveGenerator` — the cheap, no-sandbox `CandidateGenerator`. It drafts
 * surface edits via the existing improvement adapter (`proposeFromFindings`,
 * one LLM patch per finding) and applies them as ONE coherent improvement into
 * the candidate worktree. `maxShots` is ignored — reflection is single-shot by
 * construction (the patches are already drafted).
 *
 * This is the `shots=1, sandbox=off` setting of the one improvement driver.
 * The `agenticGenerator` (a multi-shot verify-in-session loop) is the
 * `shots=N` setting — both plug into the same `improvementDriver`.
 *
 * @experimental
 */

interface ReflectiveGeneratorOptions {
    improvementAdapter: ImprovementAdapter<SurfaceImprovementEdit>;
}
/** Cheap no-sandbox `CandidateGenerator` (the `shots=1` setting): draft surface edits via the improvement adapter and apply them as one coherent candidate. */
declare function reflectiveGenerator(opts: ReflectiveGeneratorOptions): CandidateGenerator;

/**
 *
 * Chat-model resolution + catalog validation — the shared primitive every
 * product chat handler needs and was, until now, hand-rolling. Lifts the
 * router `/v1/models` fetch, the fail-closed id validation, and the
 * precedence resolver out of four near-identical per-repo copies.
 *
 * Policy-free by design: callers pass their own precedence order
 * (`resolveChatModel`) and their own known-good `allowlist`
 * (`validateChatModelId`), so each product keeps its resolution policy while
 * sharing the catalog fetch, the malformed-id guard, and the fail-closed
 * admission rule. No React, no `process.env` assumption — `env` is an
 * explicit narrow record so this runs unchanged in Node and in Workers.
 *
 * @stable
 */
/**
 * A model entry as returned by the Tangle Router `/v1/models` endpoint.
 * Intentionally minimal — only the fields resolution + validation read.
 */
interface ModelInfo {
    id: string;
    name?: string;
    description?: string;
    /** Provider slug, when the router exposes it (`provider` or `_provider`). */
    provider?: string;
    _provider?: string;
    architecture?: {
        modality?: string;
        input_modalities?: string[];
        output_modalities?: string[];
    };
}
/** Env keys the router base URL is resolved from. */
interface RouterEnv {
    TANGLE_ROUTER_URL?: string;
    TANGLE_ROUTER_BASE_URL?: string;
}
/** Default Tangle Router base URL used when no env override is set. */
declare const DEFAULT_ROUTER_BASE_URL = "https://router.tangle.tools";
/** Resolve the router base URL from env, normalised — no trailing `/v1` or `/`. */
declare function resolveRouterBaseUrl(env?: RouterEnv): string;
/**
 * Fetch the model catalog from the router's `/v1/models`. Throws on a non-2xx
 * response — callers decide whether to fail open (empty catalog) or closed.
 */
declare function getModels(routerBaseUrl?: string): Promise<ModelInfo[]>;
/** Trim a candidate model id; `undefined` for non-strings and blanks. */
declare function cleanModelId(value: unknown): string | undefined;
interface ChatModelCandidate {
    /** Stable label for telemetry — e.g. `request`, `workspace`, `env`. */
    source: string;
    model: string | undefined;
}
interface ResolvedChatModel {
    source: string;
    model: string;
}
/**
 * Resolve a chat model by precedence: the first candidate carrying a
 * non-blank model wins, else `fallback`. The caller owns the precedence
 * order, so each product keeps its own policy (request → workspace → env,
 * etc.) while the first-non-blank logic and the telemetry shape stay shared.
 */
declare function resolveChatModel(candidates: ChatModelCandidate[], fallback: ResolvedChatModel): ResolvedChatModel;
type ChatModelValidation = {
    succeeded: true;
    value: string;
} | {
    succeeded: false;
    error: string;
};
/**
 * Validate a caller-supplied chat-model id. Rejects non-strings, malformed
 * ids, and ids absent from both the caller's `allowlist` and the live router
 * catalog. Fails closed: when the catalog cannot be fetched, an unverifiable
 * id is rejected rather than admitted — a bad model never reaches the agent.
 */
declare function validateChatModelId(modelId: unknown, options?: {
    /**
     * Known-good ids that skip the catalog round trip — e.g. the product's
     * default model plus any env-configured ids.
     */
    allowlist?: string[];
    routerBaseUrl?: string;
    /** Injectable catalog loader — overridden in tests. */
    loadModels?: (routerBaseUrl: string) => Promise<ModelInfo[]>;
}): Promise<ChatModelValidation>;

/**
 *
 * Pure readiness-decision helper. Maps a `KnowledgeReadinessReport` from
 * `@tangle-network/agent-eval` to a three-state branch (`ready` / `blocked` /
 * `caveat`) the runtime, route handlers, and UI shells can all switch on.
 *
 * Default `minimumScore` of 0.7 mirrors the readiness scoring scale in
 * agent-eval; callers tightening or loosening this should keep it consistent
 * across all entry points for the same product so the UI / metrics agree on
 * what "caveat" means.
 *
 * @stable
 */

/**
 * Map a `KnowledgeReadinessReport` to a three-state branch (`ready` / `blocked` / `caveat`) the runtime, route handlers, and UI shells all switch on.
 *
 * @stable
 */
declare function decideKnowledgeReadiness(report: KnowledgeReadinessReport, options?: {
    minimumScore?: number;
}): KnowledgeReadinessDecision;

/**
 * The product-facing backend selector for `runChatThroughRuntime` /
 * `runAgentTaskStream`: one call turns a `--backend {router,tcloud,cli-bridge,
 * sandbox}` choice into the `AgentExecutionBackend` the chat leg runs on.
 *
 * It is the `AgentExecutionBackend` sibling of `resolveSandboxClient` (which
 * resolves the `SandboxClient` a `runLoop` drives). Both exist for the same
 * reason: every in-process eval product hand-rolled the identical
 * "`backend-name` → `createOpenAICompatibleBackend`" branch, and the copies
 * drift. This is the single generic resolver they share.
 *
 *   - `router` / `tcloud` / `cli-bridge` → OpenAI-compatible chat completions.
 *     All three speak `POST {baseUrl}/chat/completions` in OpenAI's SSE shape —
 *     the router (a.k.a. tcloud) IS that endpoint, and cli-bridge fronts a
 *     harness CLI behind the same protocol at its own `/v1`. They differ only
 *     in `baseUrl` / `apiKey` and the `kind` label a product wants on its
 *     traces. cli-bridge REQUIRES `model` in the request body, so it MUST route
 *     through `createOpenAICompatibleBackend` (which sends it), never a
 *     transport that drops the field.
 *   - `sandbox` → the caller's own domain backend. The sandbox variant carries
 *     product specifics (system prompt, workspace id, in-box D1 executor) that
 *     do NOT belong in the substrate, so the product passes a `sandboxBackend()`
 *     seam that this resolver simply invokes.
 *
 * This resolver is PURE backend selection. Product concerns — credit hard-cuts,
 * fetch-capture shims, D1 platform wiring — stay as product-side WRAPPERS
 * around the returned backend. The OpenAI-compat passthrough fields (`tools`,
 * `toolChoice`, `responseFormat`, `fetchImpl`, `retry`) are forwarded verbatim
 * so a product can advertise its app tools or install a capturing fetch without
 * re-opening the branch this consolidation closes.
 */

/** The transport a chat backend runs on. */
type AgentBackendKind = 'router' | 'tcloud' | 'cli-bridge' | 'sandbox';
/**
 * OpenAI-compat passthrough forwarded to `createOpenAICompatibleBackend` for
 * the `router` / `tcloud` / `cli-bridge` kinds. Mirrors that factory's optional
 * inputs so a product keeps its tool advertising / capture-fetch without
 * re-implementing the backend branch.
 */
type OpenAICompatPassthrough = Pick<Parameters<typeof createOpenAICompatibleBackend>[0], 'tools' | 'toolChoice' | 'responseFormat' | 'fetchImpl' | 'retry'>;
interface ResolveAgentBackendOptions<TInput extends AgentBackendInput = AgentBackendInput> extends OpenAICompatPassthrough {
    /** The chat transport to resolve. */
    kind: AgentBackendKind;
    /**
     * Bearer credential for the OpenAI-compat kinds. Empty string is valid for a
     * loopback-anonymous cli-bridge; a `router`/`tcloud` route with an empty key
     * is a caller bug the product surfaces before calling in.
     */
    apiKey: string;
    /** Base URL for the OpenAI-compat kinds. cli-bridge's is its `/v1`. */
    baseUrl: string;
    /** Model id sent on every request. cli-bridge rejects a request without it. */
    model: string;
    /** `kind` label stamped on the resolved backend + its traces. Defaults to `kind`. */
    label?: string;
    /**
     * `sandbox` kind: the product's own domain backend. Required for that kind —
     * the substrate owns no product sandbox shape, so a `sandbox` resolution with
     * no seam is a caller bug, not a silent fallback.
     */
    sandboxBackend?: () => AgentExecutionBackend<TInput>;
}
/**
 * Resolve the `AgentExecutionBackend` for the chosen `kind`. Reuse this instead
 * of hand-rolling the `createOpenAICompatibleBackend` branch in each product.
 */
declare function resolveAgentBackend<TInput extends AgentBackendInput = AgentBackendInput>(opts: ResolveAgentBackendOptions<TInput>): AgentExecutionBackend<TInput>;

/**
 *
 * The two top-level entry points:
 *
 *  - `runAgentTask` — single-shot lifecycle for adapter-driven tasks.
 *  - `runAgentTaskStream` — streaming lifecycle that delegates execution to an
 *    `AgentExecutionBackend` (model API, sandbox, or custom iterable).
 *
 * Both gate the run on `KnowledgeReadinessReport` from `agent-eval`, emit the
 * same lifecycle event vocabulary (under different shapes — see `types.ts`),
 * and route session lifecycle through a pluggable `RuntimeSessionStore`.
 *
 * @stable
 */

/** Stamp cross-cutting defaults onto adapter-projected RunRecords without
 *  overriding anything the adapter set explicitly:
 *   - `scenarioId` — the run's scenario, when the record omits one.
 *   - `failureClass` — the control layer's failure classification promoted
 *     onto the canonical cross-agent key, but ONLY when it's a real taxonomy
 *     class. This is what lets the substrate aggregate failures across every
 *     agent in one vocabulary instead of per-agent ad-hoc strings. */
declare function applyRunRecordDefaults(records: RunRecord[], scenarioId: string, controlFailureClass: string | undefined): RunRecord[];

/**
 * Single-shot task lifecycle for adapter-driven tasks: readiness-gated, emits the runtime lifecycle event vocabulary, session-store pluggable.
 *
 * @stable
 */
declare function runAgentTask<TState, TAction, TActionResult, TEval extends ControlEvalResult = ControlEvalResult>(options: RunAgentTaskOptions<TState, TAction, TActionResult, TEval>): Promise<AgentTaskRunResult<TState, TAction, TActionResult, TEval>>;
/**
 * Streaming task lifecycle: delegates execution to an `AgentExecutionBackend` (model API, sandbox, or custom iterable) and yields lifecycle events as they happen.
 *
 * @stable
 */
declare function runAgentTaskStream<TInput extends AgentBackendInput = AgentBackendInput>(options: RunAgentTaskStreamOptions<TInput>): AsyncIterable<RuntimeStreamEvent>;

/**
 *
 * Sanitization for runtime telemetry. The rule: nothing user-controlled leaks
 * unless the caller opts in with a `RuntimeTelemetryOptions` flag. This is the
 * envelope that ends up in `agent_run.metadata.runtimeEvents` on every
 * consumer, so the default must be safe.
 *
 * @stable
 */

/** @stable */
interface RuntimeTelemetryOptions {
    /**
     * Include raw task inputs. Off by default because task inputs often contain
     * customer facts, credentials, source text, or internal IDs.
     */
    includeInputs?: boolean;
    /** Include requirement descriptions. Secret requirements are always redacted. */
    includeRequirementDescriptions?: boolean;
    /** Include evidence IDs. Off by default; counts are safer for shared reports. */
    includeEvidenceIds?: boolean;
    /** Include user answers from question preflight. Off by default. */
    includeUserAnswers?: boolean;
    /** Include action payloads and action results for control steps. Off by default. */
    includeControlPayloads?: boolean;
    /** Include task metadata. Off by default because metadata may carry IDs or policy internals. */
    includeMetadata?: boolean;
    /** Include eval detail/evidence strings. Off by default because validators may echo private input. */
    includeEvalDetails?: boolean;
}
/** @stable */
interface SanitizedKnowledgeRequirement {
    id: string;
    description?: string;
    requiredFor: string[];
    category: KnowledgeRequirement['category'];
    acquisitionMode: KnowledgeRequirement['acquisitionMode'];
    importance: KnowledgeRequirement['importance'];
    freshness: KnowledgeRequirement['freshness'];
    sensitivity: KnowledgeRequirement['sensitivity'];
    confidenceNeeded: number;
    currentConfidence: number;
    evidenceCount: number;
    evidenceIds?: string[];
    fallbackPolicy: KnowledgeRequirement['fallbackPolicy'];
}
/** @stable */
interface SanitizedKnowledgeReadinessReport {
    taskId: string;
    readinessScore: number;
    recommendedAction: KnowledgeReadinessReport['recommendedAction'];
    severity: KnowledgeReadinessReport['severity'];
    reason: string;
    blockingMissingRequirements: SanitizedKnowledgeRequirement[];
    nonBlockingGaps: SanitizedKnowledgeRequirement[];
    evidenceCount: number;
    evidenceIds?: string[];
    missingRequirementIds: string[];
}
/** Strip PII and large blobs from a `KnowledgeReadinessReport` for safe telemetry emission. @stable */
declare function sanitizeKnowledgeReadinessReport(report: KnowledgeReadinessReport, options?: RuntimeTelemetryOptions): SanitizedKnowledgeReadinessReport;
/** Reduce an `AgentRuntimeEvent` to a PII-safe, serializable plain object for telemetry. @stable */
declare function sanitizeAgentRuntimeEvent<TState, TAction, TActionResult, TEval extends ControlEvalResult>(event: AgentRuntimeEvent<TState, TAction, TActionResult, TEval>, options?: RuntimeTelemetryOptions): Record<string, unknown>;
/** Reduce a `RuntimeStreamEvent` to a PII-safe, serializable plain object for telemetry. @stable */
declare function sanitizeRuntimeStreamEvent(event: RuntimeStreamEvent, options?: RuntimeTelemetryOptions): Record<string, unknown>;
/** @stable */
interface RuntimeEventCollector<TState = unknown, TAction = unknown, TActionResult = unknown, TEval extends ControlEvalResult = ControlEvalResult> {
    onEvent: (event: AgentRuntimeEvent<TState, TAction, TActionResult, TEval>) => void;
    events: Array<Record<string, unknown>>;
}
/** @stable */
type RuntimeStreamEventSink = (event: RuntimeStreamEvent) => void;
/** @stable */
interface RuntimeStreamEventSummary {
    /** Total count of sanitized events collected. */
    eventCount: number;
    /** Count of events per `type`. Useful for log-line summaries. */
    eventCountsByType: Record<string, number>;
    /** First session id observed in a `session_created` / `session_resumed` event, if any. */
    firstSessionId?: string;
    /** Last `final` event's status, if a final event was observed. */
    finalStatus?: AgentTaskStatus;
    /** Last `final` event's reason, if a final event was observed. */
    finalReason?: string;
    /** Concatenated `text_delta.text` across the stream, even when payloads are redacted. */
    finalText: string;
}
/** @stable */
interface RuntimeStreamEventCollector {
    onEvent: RuntimeStreamEventSink;
    events: Array<Record<string, unknown>>;
    /** Snapshot of a small streaming-flavored summary derived from collected events. */
    summary(): RuntimeStreamEventSummary;
}
/** Build an in-memory collector that sanitizes and accumulates `AgentRuntimeEvent`s for inspection. @stable */
declare function createRuntimeEventCollector<TState = unknown, TAction = unknown, TActionResult = unknown, TEval extends ControlEvalResult = ControlEvalResult>(options?: RuntimeTelemetryOptions): RuntimeEventCollector<TState, TAction, TActionResult, TEval>;
/**
 *
 * Streaming-event counterpart of `createRuntimeEventCollector`. Pass each
 * event yielded by `runAgentTaskStream` through `onEvent` and read the
 * sanitized copies off `events`; the same `RuntimeTelemetryOptions` redaction
 * flags apply. Kept distinct from `createRuntimeEventCollector` because the
 * stream and non-stream event shapes overlap on `type` literals — dispatching
 * on `type` alone would misroute events.
 *
 * @stable
 */
declare function createRuntimeStreamEventCollector(options?: RuntimeTelemetryOptions): RuntimeStreamEventCollector;

/**
 *
 * Session helpers + an in-memory `RuntimeSessionStore` implementation suitable
 * for tests, scratch processes, and per-request scratch storage in serverless
 * runtimes. Durable stores (D1, postgres, Durable Objects) implement the same
 * interface from `./types`.
 *
 * @stable
 */

/** In-memory `RuntimeSessionStore` for single-process use and tests. @stable */
declare class InMemoryRuntimeSessionStore implements RuntimeSessionStore {
    private readonly sessions;
    private readonly events;
    get(sessionId: string): RuntimeSession | undefined;
    put(session: RuntimeSession): void;
    appendEvent(sessionId: string, event: RuntimeStreamEvent): void;
    listEvents(sessionId: string): RuntimeStreamEvent[];
}

/**
 *
 * Server-Sent Events serialization for runtime telemetry streams.
 *
 * Newline-safe by construction: any newline in `id` or `event` is collapsed to
 * a space (browsers terminate fields on newline), and multi-line `data`
 * payloads are split into one `data:` line per source line so JSON.stringify
 * output transports cleanly.
 *
 * @stable
 */

/** @stable */
interface ServerSentEventOptions {
    event?: string;
    id?: string;
    retry?: number;
}
/** Serialize a `KnowledgeReadinessReport` as a Server-Sent Event string. @stable */
declare function readinessServerSentEvent(report: KnowledgeReadinessReport, options?: RuntimeTelemetryOptions & ServerSentEventOptions): string;
/** Serialize a `RuntimeStreamEvent` as a Server-Sent Event string. @stable */
declare function runtimeStreamServerSentEvent(event: RuntimeStreamEvent, options?: RuntimeTelemetryOptions & ServerSentEventOptions): string;

/**
 * Bounded turn-level tool-dispatch loop.
 *
 * `runAgentTaskStream` runs ONE model turn; `runLoop` orchestrates DELEGATED
 * multi-agent topologies (refine / fanout-vote). Neither is the everyday
 * interactive shape: a chat turn where the model may emit tool calls, each is
 * executed, the results are folded back, and the turn re-runs until the model
 * stops (or a turn cap). Every agent app hand-rolls that loop — this is it,
 * as a reusable primitive.
 *
 * Substrate-neutral by design: the caller supplies `streamTurn` (wrapping
 * whatever backend / `runAgentTaskStream` it uses) and `executeToolCall`
 * (routing to its executors). This module owns the LOOP; the caller owns the
 * model and the executors. `Raw` (streaming variant) is the caller's own
 * event type. The only imported contract is the runtime hook type: hooks are
 * execution-scoped observers, not part of the agent profile.
 */

interface ToolLoopCall {
    toolCallId?: string;
    toolName: string;
    args: Record<string, unknown>;
}
/** Outcome of one tool dispatch — structurally compatible with a hub/integration
 *  tool-outcome union, so callers can fold either through the loop. */
type ToolCallOutcome = {
    ok: true;
    result: unknown;
} | {
    ok: false;
    code: string;
    message: string;
    status?: number;
};
/** One OpenAI-shaped tool-call entry carried on an assistant message. */
interface ToolLoopAssistantToolCall {
    id: string;
    type: 'function';
    function: {
        name: string;
        arguments: string;
    };
}
/**
 * A message in the running conversation the loop sends to `streamTurn`.
 *
 * The base `{ role, content }` covers `system` / `user` / plain `assistant`
 * turns. Two optional fields carry the OpenAI function-calling contract so a
 * strict model (Claude, and any OpenAI-compatible provider that validates tool
 * history) reads its own tool use back instead of re-issuing the same call:
 *
 *   - an assistant turn that emitted tool calls carries `tool_calls`, and its
 *     `content` is `null` when the turn was tool-only;
 *   - each tool result is its own `{ role: 'tool', tool_call_id, content }`
 *     message keyed to the call that produced it.
 *
 * Widening is additive: a `streamTurn` that reads only `role` + `content` still
 * works; one that forwards the whole message to an OpenAI-compatible endpoint
 * now sends correct tool history.
 */
type ToolLoopMessage = {
    role: string;
    content: string | null;
    tool_calls?: ToolLoopAssistantToolCall[];
    tool_call_id?: string;
};
type ToolLoopEvent = {
    type: 'text';
    text: string;
} | {
    type: 'tool_call';
    call: ToolLoopCall;
} | {
    type: 'other';
    event: unknown;
};
/** Why the loop stopped. `completed` = model finished naturally; `stuck-loop` =
 *  ≥3 consecutive identical tool calls (same tool + args); `backstop` = hit the
 *  runaway-backstop cap (200 by default); `deadline` = wall-clock deadlineMs
 *  exceeded; `budget` = maxCostUsd exhausted. Non-`completed` stops are infra /
 *  resource outcomes — eval scoring must distinguish them from capability failure. */
type ToolLoopStopReason = 'completed' | 'stuck-loop' | 'backstop' | 'deadline' | 'budget';
interface ToolLoopResult {
    finalText: string;
    toolResults: Array<{
        call: ToolLoopCall;
        label: string;
        outcome: ToolCallOutcome;
    }>;
    turns: number;
    stopReason: ToolLoopStopReason;
    /** @deprecated Use `stopReason !== 'completed'` instead. */
    cappedOut: boolean;
}
interface RunToolLoopOptions {
    systemPrompt: string;
    userMessage: string;
    priorMessages?: ToolLoopMessage[];
    streamTurn: (messages: ToolLoopMessage[]) => AsyncIterable<ToolLoopEvent>;
    executeToolCall: (call: ToolLoopCall) => Promise<ToolCallOutcome>;
    isExecutableTool: (toolName: string) => boolean;
    /** Runaway-backstop cap. Default 200 — set far above any legitimate workflow.
     *  For per-workflow limits, use `maxCostUsd` or `deadlineMs` instead. */
    maxToolTurns?: number;
    /** Wall-clock deadline in ms since epoch (Date.now()-based). When exceeded the
     *  loop stops with stopReason `deadline`. */
    deadlineMs?: number;
    /** Maximum total cost in USD. Requires `costOf` to meter each tool call. */
    maxCostUsd?: number;
    /** Return the USD cost of one outcome. Required for `maxCostUsd` to work. */
    costOf?: (call: ToolLoopCall, outcome: ToolCallOutcome) => number;
    renderResult?: (label: string, outcome: ToolCallOutcome) => string;
    labelFor?: (call: ToolLoopCall) => string;
    runId?: string;
    scenarioId?: string;
    hooks?: RuntimeHooks;
}
/** Run the bounded tool loop and return the final text + every executed tool
 *  outcome. Awaitable — callers needing to stream events to a UI use
 *  {@link streamToolLoop}. */
declare function runToolLoop(opts: RunToolLoopOptions): Promise<ToolLoopResult>;
type StreamToolLoopYield<Raw> = {
    kind: 'event';
    event: Raw;
} | {
    kind: 'tool_result';
    toolName: string;
    toolCallId?: string;
    label: string;
    outcome: ToolCallOutcome;
} | {
    kind: 'capped';
    pending: number;
    stopReason: Exclude<ToolLoopStopReason, 'completed'>;
};
interface StreamToolLoopOptions<Raw> {
    systemPrompt: string;
    userMessage: string;
    priorMessages?: ToolLoopMessage[];
    streamTurn: (messages: ToolLoopMessage[]) => AsyncIterable<Raw>;
    extractText: (event: Raw) => string;
    extractToolCall: (event: Raw) => ToolLoopCall | null;
    isExecutableTool: (toolName: string) => boolean;
    executeToolCall: (call: ToolLoopCall) => Promise<ToolCallOutcome>;
    /** Runaway-backstop cap. Default 200 — set far above any legitimate workflow. */
    maxToolTurns?: number;
    /** Wall-clock deadline in ms since epoch (Date.now()-based). */
    deadlineMs?: number;
    /** Maximum total cost in USD. Requires `costOf` to meter each tool call. */
    maxCostUsd?: number;
    /** Return the USD cost of one outcome. Required for `maxCostUsd` to work. */
    costOf?: (call: ToolLoopCall, outcome: ToolCallOutcome) => number;
    renderResult?: (label: string, outcome: ToolCallOutcome) => string;
    labelFor?: (call: ToolLoopCall) => string;
    runId?: string;
    scenarioId?: string;
    hooks?: RuntimeHooks;
}
/** Streaming bounded tool loop: yields each raw turn event (the caller maps +
 *  telemetries + re-emits it) and each executed `tool_result`; emits one
 *  `capped` if it stops for any non-completed reason with calls still pending. */
declare function streamToolLoop<Raw>(opts: StreamToolLoopOptions<Raw>): AsyncGenerator<StreamToolLoopYield<Raw>, void, unknown>;

export { AgentBackendContext, AgentBackendInput, type AgentBackendKind, AgentExecutionBackend, AgentRuntimeEvent, AgentTaskRunResult, AgentTaskStatus, type AuthSource, type BackendCallPolicy, BackendTransportError, CandidateGenerator, type ChatStreamEvent, type ChatTurnHooks, type ChatTurnIdentity, type ChatTurnProducer, type ChatTurnResult, type CircuitBreakerConfig, CircuitBreakerState, CircuitOpenError, type Conversation, type ConversationDriveState, type ConversationJournal, type ConversationJournalEntry, type ConversationParticipant, type ConversationPolicy, type ConversationResult, type ConversationStreamEvent, type ConversationTurn, type D1DatabaseLike, type D1StmtLike, DEFAULT_MAX_DEPTH, DEFAULT_ROUTER_BASE_URL, DeadlineExceededError, FORWARD_HEADERS, FileConversationJournal, type ForwardHeaderName, type HaltContext, type HaltPredicate, type HaltReason, type HaltSignal, type ImproveOptions, type ImproveResult, type ImproveSurface, InMemoryConversationJournal, InMemoryRuntimeSessionStore, type ModelInfo, OpenAIChatResponseFormat, OpenAIChatTool, OpenAIChatToolChoice, type PersonaConversationResult, type PersonaDriver, PlannerError, type PropagatedHeaders, type ReflectiveGeneratorOptions, type ResolveAgentBackendOptions, type ResolvedChatModel, type RetryBackoff, type RetryableErrorPredicate, type RouterEnv, type RunChatTurnInput, type RunConversationOptions, type RunPersonaConfig, type RunPersonaConversationOptions, type RunToolLoopOptions, type RuntimeEventCollector, RuntimeHooks, RuntimeRunStateError, RuntimeSessionStore, RuntimeStreamEvent, type RuntimeStreamEventCollector, type RuntimeTelemetryOptions, type SanitizedKnowledgeReadinessReport, type SqlAdapter, SqlConversationJournal, type StreamToolLoopOptions, type StreamToolLoopYield, type ToolCallOutcome, type ToolLoopAssistantToolCall, type ToolLoopCall, type ToolLoopEvent, type ToolLoopMessage, type ToolLoopResult, type ToolLoopStopReason, type TurnOrder, applyRunRecordDefaults, buildForwardHeaders, cleanModelId, computeBackoff, createConversationBackend, createIterableBackend, createOpenAICompatibleBackend, createRuntimeEventCollector, createRuntimeStreamEventCollector, createSandboxPromptBackend, d1ToSqlAdapter, decideKnowledgeReadiness, defaultIsRetryable, defineConversation, deriveExecutionId, getModels, handleChatTurn, improve, isDepthExceeded, makePerAttemptSignal, mcpBuildPrompt, readDepth, readinessServerSentEvent, reflectiveGenerator, resolveAgentBackend, resolveChatModel, resolveRouterBaseUrl, runAgentTask, runAgentTaskStream, runConversation, runConversationStream, runPersonaConversation, runPersonaDispatch, runToolLoop, runtimeStreamServerSentEvent, sanitizeAgentRuntimeEvent, sanitizeKnowledgeReadinessReport, sanitizeRuntimeStreamEvent, sleep, slugifySpeaker, streamToolLoop, toolBuildPrompt, turnId, validateChatModelId };
