import type { AgentDeps } from '../agent-deps.js';
import type { AgentDepsFactory } from '../agent-deps-factory.js';
import { type ChatQueueService } from '../chat-queue-service.js';
import type { HumanReply } from '../elicitation.js';
import type { PriceCatalogOptions } from '../pricing/boot-pricing.js';
import type { AgentStore } from '../spi/agent-store.js';
import type { AgentPricingStore } from '../spi/pricing-store.js';
import type { TokenStreamSink } from '../spi/token-stream-sink.js';
import type { AiToolCtx } from '../spi/tool.js';
import type { AgentUiComponent } from '../stream-events.js';
import type { ToolPresentation } from '../tool-presentation.js';
import type { ToolRegistry } from '../tool-registry.js';
import type { Actor, AgentRunInput, MessageUsage } from '../types.js';
import type { OpenCodeHost, OpenCodeSessionStore } from './host.js';
import { type Milestone, type OpenCodeUsage, type PendingAsk, type StepCost, type StepModel, type TurnOutcome } from './turn.js';
/**
 * The app's tools, served to OpenCode sessions over the engine's own MCP endpoint
 * (`POST <agent path>/opencode/mcp`, mounted by the engine).
 */
export interface OpenCodeToolsOptions {
    /**
     * The URL the OpenCode server calls that endpoint at, passed to it verbatim (no default). It is
     * resolved from where OpenCode runs, not from the app: e.g. `http://127.0.0.1:3333/agent/opencode/mcp`
     * on the same machine, the app's Compose service or Kubernetes Service name otherwise.
     * `/agent` is the agent's route prefix (`path`).
     */
    url: string;
    /** The MCP server's name in OpenCode; its tools are `<server>.<tool>` / `<server>_<tool>`. Default `'agora'`. */
    server?: string;
    /**
     * The secret the endpoint's bearer tokens are signed with. Default: derived from the app's
     * `APP_KEY`; without one, a random per-process secret (which only works with one process).
     */
    secret?: string;
    /**
     * How long a token OpenCode is given stays valid. A token only ever reaches a turn of its own
     * actor that is running on the session that calls, and it is re-issued on the turns of a session
     * once it is half-way through. Default 7 days.
     */
    ttlMs?: number;
}
export interface OpenCodeEngineSettings {
    /** The id a run gets (e.g. a tenant prefix your durable store partitions by). Default: a random UUID. */
    runId?: (input: AgentRunInput) => string;
    /**
     * `openCodeDurable()` only: how each turn's workflow run starts — tags, search attributes, a
     * concurrency quota (`WorkflowEngine.start` options) — and what a refused start becomes (e.g. a
     * concurrency limit → a 429 for the person sending).
     */
    durable?: {
        start?: (input: AgentRunInput, runId: string) => Record<string, unknown> | Promise<Record<string, unknown>>;
        startError?: (error: unknown, input: AgentRunInput) => unknown;
    };
    /** How many earlier messages a NEW session is told about (a thread whose session was lost). Default 20. */
    historyMessages?: number;
    /** The instructions key the agent's own prompt is put under. Default `'agora.system'`. */
    systemKey?: string;
    /** Serve the app's tools to the sessions. Omit → the session has the host's tools only. */
    tools?: OpenCodeToolsOptions;
    /** Where skills are written, relative to the session's directory. Default `'.opencode/skills'`. */
    skillsDir?: string;
    /**
     * How long `observe` waits for a milestone before failing the turn. Default 30 minutes. A turn
     * parked on a person is not observing: this bounds one stretch of OpenCode working on its own.
     */
    turnTimeoutMs?: number;
    /**
     * Whose figure a model call is recorded at. OpenCode prices each call itself, off its model
     * catalog (the `cost` its config declares, else models.dev's list price):
     *
     * - `'reported'` (default) — OpenCode's figure when it has one (`cost_source` `'provider'`); a call
     *   OpenCode priced at 0 (no price for the model) is estimated from the library's price instead
     *   (`priceCatalog`, the pricing store, the built-in GovCloud Bedrock table, models.dev;
     *   `cost_source` `'estimate'`).
     * - `'estimate'` — the library's price whenever it has one, OpenCode's figure otherwise. For
     *   models OpenCode prices at a list price that is not yours (Bedrock in GovCloud, a negotiated
     *   rate) when you would rather not declare `cost` in OpenCode's config.
     */
    cost?: 'reported' | 'estimate';
}
/** A thread's session, as the steps of a turn pass it along (and a durable run journals it). */
export interface SessionHandle {
    sessionId: string;
    serverKey: string;
    bootId?: string;
    directory?: string;
}
/** What the engine's MCP endpoint tells the turns about the call it is serving. */
export interface OpenCodeToolCall {
    actor: Actor;
    /** The OpenCode server the endpoint's token was issued for. */
    serverKey: string;
    /** The MCP request id, so two calls' unnamed components stay apart. */
    requestId: string;
    /** The call's `_meta` — OpenCode names its session there (`ai.opencode/sessionID`). */
    meta: Readonly<Record<string, unknown>> | undefined;
}
/** The turn a tool call over MCP belongs to — see {@link OpenCodeTurns.callContext}. */
export interface OpenCodeCallContext {
    input: AgentRunInput;
    runId: string;
    ctx: Pick<AiToolCtx, 'threadId' | 'runId' | 'emitUi'> & {
        agentName?: string;
    };
}
/** The context the engine's runners and endpoint are built from. */
export interface OpenCodeTurnsDeps {
    host: OpenCodeHost;
    sessions: OpenCodeSessionStore;
    settings: OpenCodeEngineSettings;
    factory: AgentDepsFactory;
    store: AgentStore;
    sink: TokenStreamSink;
    registry: ToolRegistry;
    queue?: ChatQueueService;
    /** Mints the bearer token a session's tools endpoint is registered with (`settings.tools`). */
    mintToolsToken?: (actor: Actor, serverKey: string) => string;
    /** Where a call OpenCode did not price gets its price from (`pricingStore`, `priceCatalog`). */
    pricingStore?: AgentPricingStore;
    priceCatalog?: PriceCatalogOptions | false;
}
/**
 * The steps of an OpenCode turn — `begin` (thread, session, instructions), `prompt`, `observe`
 * (until a person is asked something or the execution ends), `reply`, `settle` — so an in-memory
 * runner can run them in a row and a durable one can checkpoint each. A step may run in a process
 * that did not run the one before (a durable run resumed elsewhere): the live turn is rebuilt from
 * the session, catching up on what OpenCode asked while nobody listened.
 */
export declare class OpenCodeTurns {
    private readonly events;
    private readonly live;
    /** Actions approved per run, and the approvals the MCP endpoint already spent. */
    private readonly grants;
    private readonly spent;
    /** When the tools endpoint was last registered for a location (`server|boot|directory`). */
    private readonly toolsIssued;
    readonly host: OpenCodeHost;
    private readonly sessions;
    readonly settings: OpenCodeEngineSettings;
    private readonly factory;
    private readonly store;
    private readonly sink;
    readonly registry: ToolRegistry;
    private readonly queue;
    private readonly mintToolsToken;
    /** Starts the next queued message of the thread; set by the runner (it owns `start`). */
    startNext: ((next: AgentRunInput, runId: string) => Promise<unknown>) | undefined;
    constructor(deps: OpenCodeTurnsDeps);
    private readonly prices;
    /**
     * What a model call OpenCode made cost, by the same rule the library's loop records its own steps
     * at: a reported figure wins, else an estimate off the model's price — see
     * {@link OpenCodeEngineSettings.cost}.
     */
    priceStep(step: {
        model: StepModel | undefined;
        usage: MessageUsage;
        reportedCostUsd: number | undefined;
    }): Promise<StepCost>;
    /** The tools server's name in OpenCode. */
    get toolsServer(): string;
    /** The app tool an OpenCode permission `action` names, and its presentation — `undefined` for a built-in. */
    presentationOf(action: string): {
        name: string;
        presentation?: ToolPresentation;
    } | undefined;
    /** The agent's deps (prompt, approval policy, allow-list, skills, memory) for a turn. */
    depsFor(input: Pick<AgentRunInput, 'agentName'>): AgentDeps;
    /** The tools a turn may reach: the agent's allow-list, narrowed by its persona's. */
    allowedTools(input: Pick<AgentRunInput, 'agentName' | 'persona'>): string[] | undefined;
    /** Whether the memory provider writes, so the endpoint serves `remember`. */
    memoryWritable(input: Pick<AgentRunInput, 'agentName'>): boolean;
    /** The runs this process is following now (an updater drains on it). */
    liveRuns(): string[];
    /** Stop following every run, and close the event streams. */
    close(): void;
    /**
     * The turn a tool call over the engine's MCP endpoint belongs to, or `undefined` when it belongs
     * to none — which the endpoint refuses: it exists only to serve turns. OpenCode names its session in
     * every call's `_meta` (`ai.opencode/sessionID`); the call is that session's running turn's, and
     * only when the endpoint's caller is the person that turn runs for, on the server the token was
     * issued for. A session this process is not following is found through OpenCode (`session.get` →
     * the thread it was created for → the thread's running turn); its components still reach the
     * stream, as a message of their own.
     */
    callContext(call: OpenCodeToolCall): Promise<OpenCodeCallContext | undefined>;
    /**
     * Spend one approval of `tool` in `runId` — what the MCP endpoint asks before it runs an `action`.
     * OpenCode asks the person (or the policy answers) before it calls an action tool; the endpoint
     * runs it only against an approval this engine saw granted, so a caller that skipped OpenCode's
     * permission rules (anything that got hold of the token) cannot run an action unapproved. The
     * approval is the run's own: live in this process, else read off the run's persisted calls.
     */
    spendApproval(runId: string, threadId: string, tool: string): Promise<boolean>;
    /**
     * Push a component into the run an OpenCode session is serving, for the host's own trusted
     * callers (no caller check — the MCP endpoint checks its own). `false` when no run is live on
     * that session in this process.
     */
    pushToSession(sessionId: string, component: AgentUiComponent): Promise<boolean>;
    private uiPushed;
    private threadOfSession;
    private activeRun;
    /**
     * Open the run's row, persist the user message (or rewind the thread for a regenerate), find or
     * create the thread's session, and refresh what the session is told this turn.
     */
    begin(runId: string, input: AgentRunInput, durable?: boolean): Promise<SessionHandle>;
    private createSession;
    /** The user message, as the loop persists it (or, on a regenerate, the thread rewound to it). */
    private prepareThread;
    /** Rewind the session to before its last user message, so the regenerated answer replaces it. */
    private revertLastExchange;
    /** The agent's prompt, the memory block and the host's entries — refreshed every turn. */
    private refreshInstructions;
    /**
     * The agent's prompt as the loop resolves it: a persona's prompt stands in for the base (a
     * builder is handed the base as `basePrompt`), else the agent's `systemPrompt`.
     */
    private systemPrompt;
    private scopeContext;
    /** What is on file about the actor (`memory` in the config); writable when `remember` is served. */
    private memoryBlock;
    /**
     * The agent's skills (`skills` in the config) as `SKILL.md` files under the session's directory,
     * where OpenCode's own `skill` tool finds them. Returns the names written.
     */
    private writeSkills;
    /**
     * Permission rules for the app's tools: the MCP server allowed as a whole (OpenCode offers a
     * server's tools only when the server itself is allowed), then each `action` tool asked — the last
     * matching rule wins, so an action still lands on an approval card. Tools outside the agent's
     * allow-list, and the kinds only the loop serves, are denied.
     */
    private toolRules;
    /**
     * Register the tools endpoint at the session's location, with a bearer token for the turn's
     * actor. On a new session always; on a kept one when this process has not registered it yet or
     * its token is half-way through its life.
     */
    private addTools;
    /** Start listening to the session (so nothing it says is missed), then send the user message. */
    prompt(runId: string, input: AgentRunInput, handle: SessionHandle, spent?: OpenCodeUsage): Promise<void>;
    /**
     * Wait for the turn's next milestone. In a process that was not following the turn (a resumed
     * durable run), the turn is rebuilt and first catches up on requests OpenCode raised meanwhile;
     * `session.wait` is the safety net for a terminal event that was missed.
     *
     * `spent` is what the run spent before (the sum of the `usage` of its earlier milestones): a turn
     * rebuilt here starts from it, so the answer it persists carries the whole run's usage.
     */
    observe(runId: string, input: AgentRunInput, handle: SessionHandle, spent?: OpenCodeUsage): Promise<Milestone>;
    /**
     * Hand a person's answer back to OpenCode. When the server restarted while the turn waited, the
     * request is gone with it: a new session is opened, told what the person decided, and prompted
     * to go on.
     */
    reply(runId: string, input: AgentRunInput, handle: SessionHandle, ask: PendingAsk, reply: HumanReply, spent?: OpenCodeUsage): Promise<SessionHandle>;
    private continueAfterRestart;
    /** What a session spent so far (`session.get`), or nothing when OpenCode does not say. */
    private sessionUsage;
    /** Interrupt the thread's session (cancel). */
    interrupt(input: AgentRunInput): Promise<void>;
    private ensureLive;
    /** Stop following a run in this process. */
    drop(runId: string): void;
    /** A person was asked something: tell the host (cards in other channels, wake-ups). */
    private reached;
    /**
     * What the run produced, as the host's settle hooks see it. `usage` is what the run spent — the
     * runner's sum of its milestones' usage, which a resumed run reads back from its journal — else
     * what the turn this process follows saw.
     */
    private runResult;
    /**
     * The host's last word on the answer before the stream ends: components to append (a guardrail
     * notice), or, for a failed run, the error the person reads instead of OpenCode's.
     */
    private amend;
    /** Delivery, telemetry, spend: after the run settled. Errors are the host's, never the run's. */
    private settled;
    private forget;
    /**
     * What the run spent. The turn this process follows starts from the runner's sum of the earlier
     * milestones and adds every call it saw since, so it is never behind that sum — and ahead of it for
     * a run that failed or was cancelled between two milestones. Without one, the runner's sum.
     */
    private spentBy;
    /**
     * End the run's stream and bookkeeping for how its execution ended. `spent` is what the run spent
     * (the sum of its milestones' `usage`); omit → what the turn this process follows saw.
     */
    settle(runId: string, input: AgentRunInput, outcome: TurnOutcome, durationMs: number, spent?: OpenCodeUsage): Promise<void>;
    settleFailed(runId: string, input: AgentRunInput, error: string, durationMs?: number, spent?: OpenCodeUsage): Promise<void>;
    /** As `InlineAgentRunner`: hand the thread to its next queued message, and say so on the stream. */
    private handoff;
}
/** The `_meta` key OpenCode names its session with on every MCP call. */
export declare const SESSION_META_KEY = "ai.opencode/sessionID";
//# sourceMappingURL=turns.d.ts.map