import { EventEmitter } from "node:events"; import type { Span, TelemetryProvider, Trace } from "@skaile/workspaces/telemetry"; import type { AgentPromptInput, Capability, CredentialMint, CodexCredentialDelivery, CodexSubscriptionRefreshInput, CodexSubscriptionRefreshResult, FlowExecution, ModelTokenUsage, RenderInvokedEvent, TokenUsage } from "@skaile/workspaces/types"; import type { CodexServerRequestHandlers } from "./codex-app-server.js"; export type { AgentPromptInput } from "@skaile/workspaces/types"; import type { ModelEntry } from "./models.js"; /** * Test-only hook injected by the runner so a no-LLM driver (the echo driver's * scripted-flow mode) can read flow state and drive flow-adapter mutations * without an SDK/MCP round-trip. The runner wires this in `createActiveFlow` * from the active FlowAdapter + handle. * * This is an internal runner↔driver interface — NOT part of any wire * protocol or flow contract. Production drivers ignore it. * * @see packages/bridge/drivers/echo — scripted-flow mode * @since 3.6.0 */ export interface FlowTestDriverHook { /** Current flow execution snapshot, or `null` when no flow is active. */ getState(): FlowExecution | null; /** * Execute a flow-adapter operation (`start_node`, `request_approval`, * `complete_node`, `request_input`, `skip_node`, `fail_node`, …). Returns * the adapter's JSON string result (or rejects on an illegal transition). */ execute(op: string, args: Record): Promise; /** * Node IDs marked as mandatory approval gates. The scripted policy resolves * `auto` to `request_approval` for these even in autonomous mode, so hosts * can drive a mandatory gate deterministically. Optional — absent hooks * behave as if no node is mandatory. */ mandatoryGates?(): string[]; } /** * Minimal LLM tool descriptor used by the capability dispatch path. Mirrors * the runner's `LLMTool` shape so drivers can consume registry output without * importing `@skaile/workspaces/runner` (which would create a circular dep). * * @category Capabilities * @since 2.0.0 * @docLink packages/bridge/concepts#bridge-capability-tool */ export interface BridgeCapabilityTool { /** Capability name; used as the LLM-visible tool name. */ name: string; /** Human/LLM-readable description. */ description: string; /** JSON Schema for the tool's input parameters. */ parameters: Record; } /** * Render-cap event emission callback used by the bridge when the LLM invokes * a capability that carries a `render` spec. The runner provides the actual * sink (transport.send / sendEvent); the bridge only emits. * * @category Capabilities * @since 2.0.0 * @docLink packages/bridge/concepts#bridge-render-emit */ export type BridgeRenderEmit = (event: RenderInvokedEvent) => void; /** * Text-event emission callback for render-capability fallbacks. When a * `RenderCapability` carries `render.fallback` and the LLM invokes it, the * bridge substitutes `{{prop}}` placeholders (top-level keys of `props`, * optional dotted-path traversal `{{user.name}}`) and pushes the rendered * string through this callback so clients without a render layer still see * a textual representation. No-op when `fallback` is absent. * * @category Capabilities * @since 2.0.0 * @docLink packages/bridge/concepts#bridge-text-emit */ export type BridgeTextEmit = (text: string) => void; /** * Bundle of capability dispatch hooks. Registered into the driver via * `AgentConfig.capabilities`; the driver routes registered LLM tool calls * back into `invoke()` instead of using its native dispatch path. * * The runner constructs this from a `CapabilityRegistry` instance and threads * it through `createAgentSession()`. Drivers that don't yet implement * capability dispatch ignore the field — legacy v1 paths stay intact. * * @category Capabilities * @since 2.0.0 * @docLink packages/bridge/concepts#bridge-capability-hooks */ export interface BridgeCapabilityHooks { /** Build the LLM tool descriptor list from the registry. Called per turn so registration changes take effect. */ composeTools(): BridgeCapabilityTool[]; /** Resolve a wire-format Capability descriptor by name. Used by the bridge to inspect `fireAndForget` / `render`. */ resolve(name: string): Capability | null; /** Validate input + dispatch through the registry. Logging is wired by the registry. */ invoke(name: string, input: unknown, signal?: AbortSignal): Promise; /** Subscribe to registry changes; callback carries no credentials or tool payloads. */ onToolsChanged?(listener: () => void): () => void; /** Emit a render-invoked event. The runner forwards via the WebSocket transport. */ emitRender?: BridgeRenderEmit; /** Emit a text fallback for render capabilities with `render.fallback`. */ emitText?: BridgeTextEmit; /** Session id passed through to the runner's per-handler logger. */ sessionId: string; } /** * Static metadata for a registered agent driver backend. * * Returned by {@link listDrivers} and exposed on every {@link AgentDriver} instance * via `driverInfo`. * * @docLink packages/bridge/concepts#driver-info */ export interface DriverInfo { /** Stable machine identifier used to look up the driver in the registry (e.g. `"omp"`, `"claude-sdk"`). */ id: string; /** Human-readable display name shown in UIs and logs. */ name: string; /** `true` when the driver can target any LLM provider; `false` for Anthropic-only drivers. */ modelAgnostic: boolean; /** * `true` when the driver supports mid-stream abort via `abort()` without killing the process. * Drivers that return `false` can only be stopped via `kill()`. */ supportsInBandAbort: boolean; } /** Closed, passive managed-MCP observations. Counts saturate at 512. */ export interface CodexNativeMcpDiagnostics { startupBeforeTurn: "unknown" | "starting" | "ready" | "failed" | "cancelled"; startupAtEnd: CodexNativeMcpDiagnostics["startupBeforeTurn"]; toolsListBeforeTurn: number; toolsListTotal: number; toolsCallTotal: number; nativeMcpToolItemCount: number; } /** Trusted session-local tool transport; fulfilled only after required tools are ready. */ export interface CodexToolTransport { mcpServers: Record>; /** Trusted runner server eligible for human native consent; must name its configured entry. */ approvalServerName?: string; onServerRequest?: CodexServerRequestHandlers; /** Revalidate the required live inventory before starting each turn. */ validate?(): Promise; /** Rebuild the native generation at idle when its advertised inventory is stale. */ needsReload?(): boolean; instructions?: string; /** Restrict model calls to the active turn and abort outstanding calls when it ends. */ beginTurn?(signal: AbortSignal): void; /** Trusted private server binding and passive request-handler counters; never performs I/O. */ diagnostics?: { serverName: string; requestCounts(): { toolsListTotal: number; toolsCallTotal: number; }; }; dispose(): Promise; } /** Explicit managed instance inputs; none are inferred from ambient developer state. */ export interface CodexManagedRuntimeOptions { executable: string; /** Optional launcher arguments, for the caller-owned executable wrapper. */ executableArgs?: string[]; /** Private local filesystem state; the host must fence old native processes before reuse. */ privateDirectory: string; /** Host-owned private mount containing every parent/child instance; denied to native tools. */ privateRoot: string; environment: Record; credential: CodexCredentialDelivery; instructionFiles?: string[]; skillsDirectory?: string; prepareTools(signal: AbortSignal): Promise; /** * Passive snapshots before/after turns, plus final local counts after successful transport closure. * No final snapshot is emitted on uncertain cleanup or a failed final counter read. Earlier snapshots * are not proof of completed cleanup. Sink failures never affect native execution or cleanup. */ onNativeMcpDiagnostics?(value: Readonly): void | Promise; refreshCredential?: (input: CodexSubscriptionRefreshInput, signal: AbortSignal) => Promise; requestTimeoutMs?: number; turnTimeoutMs?: number; /** Maximum native turns admitted by this instance; reserved for disposable one-turn children. */ nativeTurnBudget?: 1; /** Human interaction deadline, bounded separately from ordinary RPCs. */ interactionTimeoutMs?: number; } /** * Codex-specific tuning options passed through `DriverOptions.codex`. * * @remarks These are forwarded verbatim to the Codex driver and have no effect on other drivers. * @docLink packages/bridge/concepts#codex-driver-options */ export interface CodexDriverOptions { /** Omission retains the standalone SDK path; managed mode always requires App Server inputs. */ authSource?: "local" | "managed"; managedRuntime?: CodexManagedRuntimeOptions; /** Controls whether Codex may auto-apply edits without user approval. */ approvalPolicy?: "never" | "on-request" | "on-failure" | "untrusted"; /** Filesystem sandbox level for the Codex process. */ sandboxMode?: "read-only" | "workspace-write" | "danger-full-access"; /** Reasoning budget passed to the model. */ reasoningEffort?: "minimal" | "low" | "medium" | "high" | "xhigh"; /** When `true`, the Codex sandbox is allowed to make outbound network requests. */ networkAccessEnabled?: boolean; /** Extra directories made accessible to the Codex sandbox in addition to `cwd`. */ additionalDirectories?: string[]; } /** * Driver-specific configuration bag. * * Fields are keyed by driver ID so that callers can pass driver-specific options * through the common {@link AgentConfig} without breaking other drivers. * * @docLink packages/bridge/concepts#driver-options */ export interface DriverOptions { /** Codex-specific options. Ignored by all other drivers. */ codex?: CodexDriverOptions; } /** * What the driver tells the host about a turn blocked by an upstream usage * limit. Everything but `configId` is best-effort: the SDK reports the limit * window only on its structured telemetry, not on the error result text. * * @see AgentConfig.onLimitBlocked * @category Configuration * @since 3.9.0 */ export interface LimitBlockedContext { /** The AI provider config the blocked turn ran on (`""` when unmediated). */ configId: string; /** Upstream limit window (`five_hour`, `seven_day`, …) when reported. */ limitType?: string; /** Epoch seconds at which the limit window resets, when reported. */ resetsAt?: number; /** Fraction of the limit window consumed (0–1) when reported. */ utilization?: number; } /** * The host's verdict on a limit-blocked turn. `switched: true` is a promise * that a different seat's credential is already on disk — the driver acts on it * by respawning the CLI and replaying the prompt, so returning it without * having rewritten the credential file would just replay onto the dead seat. * * @see AgentConfig.onLimitBlocked * @category Configuration * @since 3.9.0 */ export interface LimitFailoverResult { /** Whether the session was repointed at a different AI provider config. */ switched: boolean; /** The config id now in effect. Informational — used for logs. */ configId?: string; /** * Why no switch happened: `same-config`, `standalone`, * `platform-omitted-config-id` (the mint succeeded but named no seat, so a * re-resolution is indistinguishable from none), or a `CredentialMint` * failure code. Diagnostic only — the driver branches on `switched`. */ reason?: string; } /** * One observed AI-provider rejection, reported to the host for per-seat health * attribution. Fire-and-forget — see {@link AgentConfig.onProviderResponse}. * * @category Configuration * @since 3.9.0 */ export interface ProviderResponseObservation { /** 401 — the credential was rejected. 429 — an upstream rate/session limit. */ status: 401 | 429; /** The AI provider config the observation is attributed to, when known. */ configId?: string; /** Upstream limit window (`five_hour`, `seven_day`, …) when reported. */ limitType?: string; /** Fraction of the limit window consumed (0–1) when reported. */ utilization?: number; /** Epoch seconds at which the limit window resets, when reported. */ resetsAt?: number; } /** * Configuration passed to a driver when it is created via {@link createDriver}. * * All fields are shared across drivers; driver-specific behaviour is documented * per field. Fields that are silently ignored by a driver are marked accordingly. * * @docLink packages/bridge/concepts#agent-config */ export interface AgentConfig { /** Absolute path to the working directory the agent operates in. */ cwd: string; /** * LLM provider name (e.g. `"anthropic"`, `"openai"`, `"google"`). * Combined with `model` as `provider/model` for omp's `--model` flag. * Ignored by claude-sdk (always Anthropic). */ provider?: string; /** * LLM model identifier (e.g. `"claude-sonnet-4-5"`, `"gpt-4o"`). * For omp: passed as `--model [provider/]model`. * For claude-sdk: passed as the `model` query option. */ model?: string; /** * Provider API keys keyed by provider name (e.g. `{ anthropic: "sk-..." }`). * Drivers inject the relevant key into the environment or SDK options. */ apiKeys?: Record; /** * Additional environment variables merged into the child process environment. * For omp: merged with `process.env` before spawn. For claude-sdk: accessed * via `env.ANTHROPIC_API_KEY` as an alternative to `apiKeys.anthropic`. */ env?: Record; /** * AI cloud transport for claude-sdk: `default | bedrock | vertex | azure | * gateway`. Absent (or `"default"`) preserves today's behavior — the * provider's native API. Typed as `string` for forward compatibility; the * driver falls back to `default` on unrecognized values (with a warning). * Ignored by all other drivers. * * @since 1.3.0 */ cloud?: string; /** * Non-secret transport settings for the selected `cloud` (camelCase — the * skaile.yaml `cloud_config` wire shape is snake_case and converted once at * the settings/serve boundary). Ignored when `cloud` is absent/default. * * @since 1.3.0 */ cloudConfig?: { region?: string; projectId?: string; resource?: string; baseUrl?: string; }; /** * Resolved credential env bundle for the selected `cloud` — runner-provided * (platform `session_init` secrets, or runner-materialized file paths such * as the Vertex service-account file). Values are secrets: never logged. * * @since 1.3.0 */ cloudSecrets?: Record; /** Inline system prompt — written to .omp/system.md and passed via --append-system-prompt (omp driver) */ systemPrompt?: string; /** Path to project .omp/ directory (PI_CODING_AGENT_DIR for omp driver) */ agentDir?: string; /** SSH key path — injected as GIT_SSH_COMMAND */ sshKeyPath?: string; /** Pre-assign a UUID as the session ID for a new session (claude-sdk: passed as sessionId option) */ sessionId?: string; /** Resume this specific past session by UUID instead of starting fresh (claude-sdk: passed as resume option) */ resumeSessionId?: string; /** Maximum agentic turns per query (claude-sdk: passed as maxTurns option) */ maxTurns?: number; /** Agent name — selects a deployed sub-agent definition as the main agent identity. * claude-sdk: passed as `agent` option to query() → reads .claude/agents/.md natively. * omp: ignored (omp uses agentDir/PI_CODING_AGENT_DIR instead). */ agentName?: string; /** In-process SDK MCP servers for custom tool injection (Claude SDK only, ignored by other drivers) */ mcpServers?: Record; /** Tool restrictions from agent.yaml — applied to the main agent session */ tools?: { /** Tool names that the agent is allowed to invoke. An empty array means no restriction. */ allowed?: string[]; /** Tool names that the agent is explicitly forbidden from invoking. */ denied?: string[]; }; /** Thinking mode for Claude models: adaptive (Claude decides), enabled (always think), disabled (no thinking). */ thinking?: "adaptive" | "enabled" | "disabled"; /** Reasoning effort level for Claude models. */ effort?: "low" | "medium" | "high" | "max"; /** Driver-specific configuration bag. */ driverOptions?: DriverOptions; /** * Protocol v2 capability dispatch hooks. When present, the driver uses the * registry as the source of LLM tool definitions and routes invocations * through `capabilities.invoke()`. Absent → legacy v1 path (existing * mcpServers / native dispatch). * * @since 2.0.0 */ capabilities?: BridgeCapabilityHooks; /** * The platform's `AIProviderConfig.id` for the AI credential currently * provisioned into this driver. The runner reads this off `AgentConfig` * during 401 mediation and passes it as `configId` in * `request_access_token { kind: 'ai-credentials' }`. The driver itself * does not consume this field — only the runner uses it. * * Set by the platform agent-gateway in `ConfigureCommandV2` for mediated * sessions. Standalone runners (CLI / forge / Claude plugin) leave it * undefined; the runner falls back to surfacing the auth error to the * user without mediating. * * Spec: `_devlog/specs/2026-05-07-unified-credential-mediation.md` * § "Wire `aiProviderConfigId` end-to-end". * * @since 3.3.0 */ aiProviderConfigId?: string; /** * Optional callback invoked by the driver when the underlying agent * surfaces an `authentication_error`. In Protocol v3 the runner mediates * the refresh via the `host.refresh_credential` capability and returns a * typed {@link CredentialMint}. The driver inspects the discriminator: * * - `mint.ok === true`: a fresh credential is now provisioned. The * driver re-attempts the in-flight prompt once. * - `mint.ok === false`: refresh failed (`code` carries a stable reason * such as `revoked`, `not-configured`, `provider-error`, * `backend-error`). The driver surfaces the original `AuthError` to * the caller. * * `args.configId` carries the platform `AIProviderConfig.id` the driver * was provisioned with (mirrors {@link AgentConfig.aiProviderConfigId}). * The runner uses it to scope the refresh to the correct AI credential. * * When omitted, the driver throws `AuthError` immediately as before * (standalone CLI / forge mode — there is no platform mediator to ask). * * Centralising auth-retry inside the driver means every consumer of * `driver.prompt(...)` (serve handler, compaction orchestrator, flow * orchestrator, …) gets self-healing for free — there is no per-call-site * wrapping. * * Spec: `_devlog/specs/2026-05-10-deterministic-session-bootstrap.md` * § "host.refresh_credential capability". * * @since 3.4.0 */ onAuthError?: (args: { configId: string; }) => Promise; /** * Optional callback invoked by the driver when a turn is blocked by an * upstream **usage limit** rather than a bad credential — the subscription * five-hour / seven-day ceiling, or any `rate_limit` the classifier * recognises. Called at most once per logical turn, and only after the * blocked attempt has already failed, so it never runs mid-turn. * * The runner implements it by asking the platform to re-resolve the seat: * * - `switched: true` — the session was repointed at a DIFFERENT AI * provider config and `.credentials.json` was rewritten. The driver * drops its query (forcing a fresh CLI spawn, which is what clears a * resident CLI's cached "blocked until " state — a hot file * rewrite alone does not) and replays the prompt once. * - `switched: false` — no alternative seat was available. The driver * surfaces the original limit error unchanged. * * Left unset by standalone runners (CLI / forge / Claude plugin): there is * no platform mediator to re-resolve a seat, so the limit error surfaces * exactly as it does today. * * Companion to {@link onAuthError}: that one covers 401s, this one covers * the rejection path, which previously reached no failover at all * (skaile-ai/workspaces#567, skaile-ai/platform#2976). * * @since 3.9.0 */ onLimitBlocked?: (ctx: LimitBlockedContext) => Promise; /** * Optional fire-and-forget hook invoked when the driver observes a 401 or a * 429 / subscription-limit rejection from the AI provider. The runner * forwards it to the platform as a `provider_response_seen` event so the * owning `AIProviderConfig`'s live health counters can be bumped — the cron * health probe only samples auth, never capacity * (skaile-ai/workspaces#368, skaile-ai/platform#3539). * * Never awaited and never retried; the driver swallows a throwing * implementation so telemetry can never fail a turn. * * @since 3.9.0 */ onProviderResponse?: (observation: ProviderResponseObservation) => void; /** * Test-only flow driver hook. When present, the echo driver's scripted-flow * mode uses it to advance flow nodes deterministically (no LLM). The runner * injects it in `createActiveFlow`; every other driver ignores it. * * @see FlowTestDriverHook * @since 3.6.0 */ flowTestDriver?: FlowTestDriverHook; } /** * A single message in an agent conversation. * * Emitted as part of {@link AgentEvent} variants (`message_start`, `message_update`, `message_end`). * * @docLink packages/bridge/concepts#agent-message */ export interface AgentMessage { /** Originator of the message. */ role: "user" | "assistant" | "tool"; /** Message body — either a plain string or a list of typed content blocks. */ content: string | ContentBlock[]; /** Tool invocations made by the assistant in this message. */ toolCalls?: ToolCall[]; /** For `role === "tool"`: the name of the tool whose result this message carries. */ toolName?: string; /** For `role === "tool"`: `true` when the tool returned an error. */ isError?: boolean; /** Optional structured payload for tool results. */ data?: unknown; } /** * A typed block within an {@link AgentMessage}'s content array. * * Mirrors the Anthropic content block schema but is driver-agnostic. * * @docLink packages/bridge/concepts#content-block */ export interface ContentBlock { /** Block discriminant. */ type: "text" | "tool_use" | "tool_result" | "thinking"; /** Present for `text` and `thinking` blocks. */ text?: string; /** Tool use / tool result correlation ID. */ id?: string; /** Tool name (present on `tool_use` blocks). */ name?: string; /** Tool input arguments (present on `tool_use` blocks). */ input?: any; /** Serialised tool result content (present on `tool_result` blocks). */ content?: string; } /** * A single tool invocation made by the agent within a message. * * @docLink packages/bridge/concepts#tool-call */ export interface ToolCall { /** Correlation ID used to match the call with its result. */ id: string; /** Name of the tool being invoked. */ name: string; /** Arguments passed to the tool. */ input: any; } /** * Error classification for agent failures. * * Used to determine whether a failure is retryable and to surface actionable * hints to the user. See `bridge/CLAUDE.md` for the full category-to-behaviour matrix. * * @see AgentError * @docLink packages/bridge/concepts#error-category */ export type ErrorCategory = "auth" | "account" | "rate_limit" | "model" | "network" | "config" | "process" | "validation" | "unknown"; /** * Structured error payload emitted inside the `error` {@link AgentEvent}. * * Consumers should inspect `retryable` before deciding to surface a retry button, * and `hint` to provide an actionable message to the user. * * @docLink packages/bridge/concepts#agent-error */ export interface AgentError { /** Human-readable error description. */ message: string; /** Coarse failure classification used for retry logic and telemetry. */ category: ErrorCategory; /** HTTP status code if the error originated from an API response. */ statusCode?: number; /** `true` when the caller may safely retry the same operation. */ retryable: boolean; /** Short actionable advice suitable for display in the UI. */ hint?: string; } /** * Metadata for a slash command exposed by the active agent runtime. * * @docLink packages/bridge/concepts#slash-command-info */ export interface SlashCommandInfo { /** Command name without the leading slash (e.g. `"compact"`). */ name: string; /** One-line description shown in command pickers. */ description: string; /** Placeholder text describing what argument the command expects. */ argumentHint?: string; } /** * Discriminated union of all events emitted by a driver on the `'agent-event'` channel. * * @remarks * Consumers listen via `driver.on('agent-event', (event: AgentEvent) => ...)`. * The `[k: string]: any` index signature on most variants allows drivers to attach * driver-specific fields (e.g. `_textDelta`) without breaking the union. * * @docLink packages/bridge/concepts#agent-event */ export type AgentEvent = { type: "message_start"; message: AgentMessage; [k: string]: any; } | { type: "message_update"; message: AgentMessage; [k: string]: any; } | { type: "message_end"; message: AgentMessage; [k: string]: any; } | { type: "turn_end"; toolResults?: AgentMessage[]; [k: string]: any; } | { type: "agent_end"; [k: string]: any; } | { type: "result"; subtype: string; summary?: string; costUsd?: number; /** Per-turn token usage when the driver tracks it. Added in 3.1.0. */ tokens?: TokenUsage; /** Per-turn usage keyed by model, subagent calls included. Added in 3.11.0. */ modelUsage?: Record; /** Prompt size of the turn's last main-thread API call. Added in 3.11.0. */ contextTokens?: number; /** Context window of the main model, in tokens. Added in 3.11.0. */ contextWindow?: number; errors?: string[]; [k: string]: any; } | { type: "error"; error: string; detail?: AgentError; fatal?: boolean; /** Usage of a failed provider turn, when reported. */ tokens?: TokenUsage; /** Failed-turn usage keyed by the actual model. */ modelUsage?: Record; contextTokens?: number; contextWindow?: number; [k: string]: any; } | { type: "tool_call"; name?: string; tool?: { name: string; }; [k: string]: any; } | { type: "tool_execution_end"; toolName?: string; [k: string]: any; } | { type: "commands_available"; commands: SlashCommandInfo[]; } | { type: "session_info"; driverSessionId: string; sessionFile?: string; } | { type: "ui_render"; [k: string]: any; } | { type: "ui_render_update"; [k: string]: any; } | { type: "ui_clear"; [k: string]: any; } | { type: "resume_failed"; resumeSessionId: string; reason: "signature_mismatch" | "model_mismatch" | "jsonl_lost" | "jsonl_poisoned" | "native_state_unavailable"; [k: string]: any; }; /** * Abstract base class for agent driver backends. * * Wraps an LLM coding agent behind a single `prompt()` interface. * All drivers emit `'agent-event'` with {@link AgentEvent} payloads. * * @remarks * Subclasses must implement `start`, `prompt`, `abort`, `kill`, and `isRunning`. * Implementations should never emit an event name other than `'agent-event'` as * the public streaming channel — internal events (`'ready'`, `'exit'`, etc.) are * driver-private. * * @example * ```ts * const driver = createDriver('omp', config); * driver.on('agent-event', (event) => console.log(event.type)); * await driver.start(); * await driver.prompt('Refactor the auth module'); * driver.kill(); * ``` * * @docLink packages/bridge/concepts#agent-driver */ export declare abstract class AgentDriver extends EventEmitter { /** Static metadata describing this driver's capabilities. */ abstract readonly driverInfo: DriverInfo; /** * Initialises the driver backend — spawns the child process (omp) or loads * the SDK module (claude-sdk). Resolves when the backend is ready to accept * prompts. Idempotent: calling `start()` on an already-running driver is a no-op. * * @throws {Error} When the backend binary is missing or the SDK cannot be loaded. */ abstract start(): Promise; /** * Sends a user message to the agent and resolves when the agent's turn completes * (i.e. after the `agent_end` event has been emitted). * * @param message - Plain-text user prompt to send to the agent. * @throws {Error} When the driver is not running or the underlying backend reports a fatal error. */ abstract prompt(message: string): Promise; /** Preserve text-only callers; drivers must explicitly support attachments. */ promptInput(input: AgentPromptInput): Promise; /** Managed replies never fall back to a new prompt after a request has settled. */ get acceptsReplyAsPrompt(): boolean; /** * Sends an in-band abort signal to the agent, requesting it to stop the current * turn without terminating the process. The driver remains usable after `abort()`. * * @remarks Only meaningful when `driverInfo.supportsInBandAbort` is `true`. * For other drivers, prefer `kill()` followed by creating a new driver instance. */ abstract abort(): Promise; /** * Terminates the agent backend immediately (SIGTERM for subprocess drivers, * `close()` for in-process drivers). The driver instance must not be reused * after `kill()`. */ abstract kill(): void; /** Stop the driver and await owned process/state cleanup when the backend supports it. */ dispose(): Promise; /** `true` when the backend is alive and able to accept new prompts. */ abstract get isRunning(): boolean; /** Optional provider-native session/thread identifier for resume support. */ get runtimeSessionId(): string | undefined; /** Slash commands discovered from the agent runtime. Override in drivers that support introspection. */ getSlashCommands(): SlashCommandInfo[]; /** * `true` while the driver is paused on an interactive question and waiting * for the user's answer rather than a new prompt. The runner routes a * `reply` command to {@link answerQuestion} instead of {@link prompt} when * this returns `true`. Drivers without interactive-question support return * `false` (the default), so a `reply` falls back to `prompt`. */ hasPendingQuestion(): boolean; /** * Deliver the user's answer to the question the driver is currently paused * on. This resumes the in-flight turn — it does NOT start a new one. When a * single tool call asks several questions, the driver resolves the underlying * call only once every sub-question has an answer. * * @param answer - The user's reply text (an option label, free text, or for * multi-select a comma-joined list). * @param question - The sub-question this answers, verbatim from the * `question` event that asked it. Supply it whenever the host lets the user * answer out of order — without it the answer fills the next unanswered * slot in order, which silently mismatches a non-sequential reply. * @returns `true` when the answer was consumed by a pending question; `false` * when no question was pending (caller should treat the reply as a prompt). */ answerQuestion(answer: string, question?: string, requestId?: string): boolean; /** * Live-update driver configuration. The new values take effect on the next * prompt() call. Only model/thinking/effort are reconfigurable mid-session. * * Subclasses that store config locally should override this to apply the * patch to their own config field. The base implementation is a no-op. */ reconfigure(patch: Partial>): void; /** * Returns the model identifier currently configured on this driver. * Subclasses that store config locally should override this. */ getModel(): string | undefined; /** * Returns the token usage from the most recent completed turn. * Returns `null` if usage data is unavailable (e.g. driver doesn't track it). * * Widened in 3.1.0 to return the full `TokenUsage` shape (input / output / * cache-read / cache-creation / reasoning). All fields are optional so * drivers can populate just what their provider reports. Existing callers * that read only `inputTokens` / `outputTokens` keep working — those fields * are still present and carry the same semantics as before. */ getTokenUsage(): TokenUsage | null; /** * Prompt tokens occupying the context window as of the most recent API * request — i.e. how full the context actually is right now. * * Deliberately NOT the same number as {@link getTokenUsage}, whose usage is * cumulative over every API call in the turn and therefore overstates the * context by 10-40x on any multi-tool turn. Callers measuring context fill * (compaction thresholds) must prefer this; callers billing a turn * (usage ledgers, the `finished` event) must keep using `getTokenUsage()`. * * Returns `null` both when the driver cannot report a per-call figure at all * and when it can but has no reading yet. Falling back to `getTokenUsage()` * is safe in either case only because a driver clears the two in lockstep: no * reading means no API call landed, so there is no cumulative usage to * overstate. A driver implementing this must preserve that invariant. */ getContextTokens(): number | null; /** * Returns the model's context window size in tokens. * Returns `null` if unknown. */ getContextWindow(): number | null; /** * Returns the list of models available to this driver without starting a session. * Drivers that can enumerate their own models override this method. * Returns an empty array by default. */ listModels(): Promise; /** * Closes the current conversation session and prepares for a fresh start. * The next `prompt()` call will begin a new session without prior history. * The driver remains usable after `resetSession()`. */ abstract resetSession(): Promise; protected _telemetry?: TelemetryProvider; protected _activeTrace?: Trace; protected _activeTurnSpan?: Span; private _turnStartTime?; /** * Attach a telemetry provider and active trace to this driver. * Called by the runner after creating the driver but before start(). */ setTelemetry(provider: TelemetryProvider, trace: Trace): void; /** * Begin a turn span. Called by the runner before prompt(). * Pass a parent span (e.g. the turn span from the orchestrator) for nesting. * Returns the span so the runner can annotate it further. */ beginTurnSpan(parent?: Trace | Span, attrs?: Record): Span | undefined; /** * End the current turn span with generation data. * Called by the runner after agent_end. Model and provider are passed * explicitly — the driver base class does not access config directly. */ endTurnSpan(result?: { model?: string; provider?: string; inputTokens?: number; outputTokens?: number; stopReason?: string; error?: string; }): void; } /** * Factory function signature registered in the driver registry. * * @param config - Configuration for the new driver instance. * @returns A freshly constructed (not yet started) {@link AgentDriver}. * @docLink packages/bridge/concepts#driver-factory */ export type DriverFactory = (config: AgentConfig) => AgentDriver; //# sourceMappingURL=types.d.ts.map