/** * Realtime Voice Transport Types * * A realtime voice session is a SEPARATE transport axis from * `complete()` / `streamCompletion()` — a long-lived, bidirectional, stateful * duplex connection where audio flows in and out continuously, the user can * barge in mid-utterance, VAD segments turns server-side, and usage accrues * over the whole session rather than at one response boundary. Forcing this * into `complete()` would corrupt the streaming contract (which MUST end with * exactly one `message_stop` chunk). So realtime voice lives here, off the chat * path, exactly like image/video are separate axes. * * These types are consumed by: * - `ILLMProvider.openRealtimeSession?()` (optional method, default-throws in * `BaseLLMAdapter` — mirrors `uploadFile`), gated by the universal * `Capabilities.realtimeVoice` flag. * - `adapters/openai-realtime.ts` (the first per-vendor implementation). * - `@holoscript/holoscript-agent` CostGuard, which imports `AudioUsage` the * same way it already imports `TokenUsage` — the usage type lives with the * session that emits it; the pricer + pricing table live with CostGuard. * * SSOT: research/2026-07-10_realtime-adapter-implementation-plan-p0pp.md §1. * * @module @holoscript/llm-provider */ /** * Transport SELECTION knob. Server-side WebSocket is the testable default; * WebRTC targets browser/Quest; SIP targets telephony. This is a per-session * choice — a provider may support several. */ type RealtimeTransport = 'webrtc' | 'sip' | 'websocket'; /** * Provider-neutral config for opening a realtime voice session. Per-vendor * transport dialects (OpenAI's 3-way transport union + SIP + ephemeral secrets, * xAI's WebSocket-only + REST STT/TTS, Gemini's live-vs-tts mode split) live * SEGREGATED on `provider..realtimeVoice` — never flattened here. */ interface RealtimeSessionConfig { /** Realtime model id — NOT a chat model. e.g. 'gpt-realtime-2.1'. */ model: string; /** Transport SELECTION knob (server WebSocket is the testable default). */ transport: RealtimeTransport; /** Which modalities the session exchanges. */ modalities: Array<'audio' | 'text'>; /** System instructions (the "brain" prompt for the voice). */ instructions?: string; /** Named voice (vendor-specific values, e.g. 'marin', 'cedar'). */ voice?: string; /** Provider-neutral audio codec hints; adapters map to vendor formats. */ audioFormat?: { input?: string; output?: string; }; /** Turn/VAD policy (provider-neutral; adapters map to server_vad etc.). */ turnDetection?: { mode: 'server_vad' | 'semantic_vad' | 'manual'; thresholdMs?: number; }; /** Tools exposed inside the session (reuses the universal ToolSpec shape). */ tools?: ToolSpec[]; /** Which surface this session serves — drives ephemeral-secret scoping + CAEL. */ surface?: 'brittney' | 'hololand' | 'quest' | 'server' | string; /** Hard budget ceilings so a long duplex session can't run away. */ budget?: { maxDurationSeconds?: number; maxCostUsd?: number; }; /** SEGREGATED per-vendor transport dialect. Never flattened. */ provider?: ProviderExtensions; } /** * Short-lived credential a browser/Quest client uses instead of the master key. * Minted server-side, scoped per surface, short-TTL. The master API key must * never leave the server (see plan §1.3, §6.2). */ interface EphemeralSecret { value: string; /** ISO timestamp. */ expiresAt: string; surface: string; sessionId: string; } /** * Usage emitted by a realtime session. Audio is 3-dimensional (audio-in / * cached-audio-in / audio-out), unlike the text-only `TokenUsage` * ({promptTokens, completionTokens, totalTokens}) — so it needs a distinct * shape. Realtime sessions still bill text (transcripts, tool args), hence the * optional text dims. */ interface AudioUsage { audioInputTokens: number; cachedAudioInputTokens: number; audioOutputTokens: number; /** Realtime sessions still bill text (transcripts, tool args). */ textInputTokens?: number; textOutputTokens?: number; } /** * Server → client events over the session lifetime. Kept faithful to the vendor * event shapes so the mockable transport seam doesn't hide contract drift. */ type RealtimeServerEvent = { type: 'session.open'; sessionId: string; ephemeral?: EphemeralSecret; } | { type: 'audio.delta'; chunk: Uint8Array; } | { type: 'transcript.delta'; role: 'user' | 'assistant'; text: string; } | { type: 'turn.start'; } | { type: 'turn.end'; } | { type: 'tool.call'; id: string; name: string; input: unknown; } | { type: 'usage'; usage: AudioUsage; } | { type: 'error'; error: LLMProviderError; } | { type: 'session.close'; totalUsage: AudioUsage; durationSeconds: number; }; /** * The live duplex handle returned by `openRealtimeSession()`. Push audio/text * in, consume server events out, close to finalize cost. */ interface RealtimeSession { readonly sessionId: string; readonly transport: RealtimeTransport; /** Push mic audio frames in. */ sendAudio(chunk: Uint8Array): void; /** Inject a text turn (e.g. a system nudge or typed input). */ sendText(text: string): void; /** Commit the current input turn (manual VAD). */ commitTurn(): void; /** Barge-in: cancel the assistant's in-flight audio. */ interrupt(): void; /** Consume server events. */ events(): AsyncIterable; /** Tear down; resolves with final usage for cost finalization. */ close(): Promise<{ totalUsage: AudioUsage; durationSeconds: number; }>; } /** * Type guard narrowing a provider to one that implements `openRealtimeSession`. * Checks BOTH the universal capability flag (routing/swap) AND the concrete * method (some adapter inherits the default-throw). Gives call sites a clean * narrow before opening a voice session. */ declare function supportsRealtime(p: ILLMProvider): p is ILLMProvider & Required>; /** * Unified LLM Provider Types * * Provider-agnostic interfaces for working with OpenAI, Anthropic (Claude), * and Google Gemini from HoloScript scenes and AI agents. * * @module @holoscript/llm-provider * @version 1.0.0 */ type MessageRole = 'system' | 'user' | 'assistant'; /** * Content for a user message that's feeding tool results back to the model. * Caller emits one `tool_result` block per `tool_use` block in the prior * assistant response. Anthropic adapter recognizes this shape and forwards. */ interface ToolResultBlock { type: 'tool_result'; tool_use_id: string; /** * Tool output to feed back to the model. Anthropic's API also accepts an * `Array<{type:'text',text:string} | {type:'image',source:...}>` here, but * this codebase deliberately narrows to `string` because every producer * (holoscript-agent/src/tools.ts: okResult / errResult) emits string * content. If/when an image-returning tool is added, widen here AND update * the display formatter at types.ts:~164 plus runner.ts SHA-extraction. * * Revisit triggers (do not speculate-widen — wait for one of these): * 1. A vision-shaped tool is added to MESH_TOOLS (e.g. `screenshot`, * `read_media_file`) — the new producer would emit Array content. * 2. Paper 20 (Learned Scene Composition) acceptance criteria require a * headless mesh agent to ingest rendered frames. * 3. Anthropic deprecates string-form `tool_result.content` and requires * the array shape on the wire. * * Until one of those triggers fires: text-only is intentional. Routing rule * (global CLAUDE.md): multimodal verification → Gemini in Antigravity, not * a headless HoloScript mesh agent. */ content: string; /** Optional: mark the tool result as an error so the model retries / reroutes. */ is_error?: boolean; } interface LLMMessage { role: MessageRole; /** Either plain text (most messages) OR a structured content array. * - Assistant messages mid-tool-loop carry the assistant's prior * text + tool_use blocks so the model has its own turn in context. * - User messages mid-tool-loop carry tool_result blocks. * - Anthropic file references use provider-specific blocks so callers can * pass file_id references instead of base64 payloads. */ content: string | LLMContentBlock[]; } interface LLMSystemMessage { role: 'system'; content: string; } interface LLMUserMessage { role: 'user'; content: string; } interface LLMAssistantMessage { role: 'assistant'; content: string; } /** Anthropic Messages API — `output_config.effort` (adaptive thinking guidance). */ type AnthropicEffortLevel = 'low' | 'medium' | 'high' | 'xhigh' | 'max'; /** * `thinking` on the Anthropic Messages API. `adaptive` is the supported mode * on Opus 4.7; `enabled`+`budget_tokens` is for manual / legacy paths. */ type AnthropicThinkingParam = { type: 'adaptive'; display?: 'summarized' | 'omitted'; } | { type: 'disabled'; } | { type: 'enabled'; budget_tokens: number; display?: 'summarized' | 'omitted'; }; interface LLMCompletionRequest { /** The messages to send to the model */ messages: LLMMessage[]; /** Maximum tokens in the response */ maxTokens?: number; /** Temperature (0-2). Higher = more creative. Default: 0.7 */ temperature?: number; /** Top-P nucleus sampling. Default: 1 */ topP?: number; /** Stop sequences - model will stop generating before these tokens */ stop?: string[]; /** Whether to stream the response */ stream?: boolean; /** * Structured-output grammar, passed through verbatim on the OpenAI-compat * LOCAL path only (llama.cpp / HoloServe request field `grammar`): a GBNF * string for llama-server, or a registered grammar NAME for HoloServe's * sovereign constrained decoder (W.780 — e.g. "containment" | "deontic" | * "composition", advertised by its /health). Output is valid-by-construction; * cloud adapters ignore this field. */ grammar?: string; /** * HoloServe's opt-in consumer payload pipeline. Passed through only by the * local OpenAI-compatible adapter as request field `holo_pipeline`; cloud * adapters ignore it and the native Ollama path rejects it rather than * silently degrading to model generation. */ holoPipeline?: 'off' | 'broker-v1'; /** * Tools the model can call. When set, the response may contain `toolUses` * blocks that the caller must execute and re-feed via a follow-up request * containing assistantBlocks (the prior response) + tool_result messages. * Anthropic adapter passes these straight through to messages.stream. * * Accepts both generic JSONSchema-shaped tools (`ToolSpec`) and Anthropic * server-side tools like the advisor (`AnthropicAdvisorToolSpec`). When an * advisor tool is present, the Anthropic adapter injects the * `anthropic-beta: advisor-tool-2026-03-01` header. Adapters for other * providers strip provider-specific shapes (they fail the function-tool * contract). */ tools?: ToolSpecUnion[]; /** * Anthropic: passed to `messages.stream({ thinking })`. If omitted, Opus 4.6/4.7 * and Sonnet 4.5/4.6 get `{ type: 'adaptive', display: 'summarized' }` by default * unless you set `thinking: { type: 'disabled' }` to opt out of extended thinking. */ thinking?: AnthropicThinkingParam; /** * Anthropic: optional shorthand for `thinking.display` (merged when `thinking` * is present, or applied together with the adaptive default for supported models). */ thinkingDisplay?: 'summarized' | 'omitted'; /** * Anthropic: `output_config.effort` on the Messages API. `max` is only * meaningful on Opus family models; on other models the adapter may downgrade * to `high` to avoid 400s. */ effort?: AnthropicEffortLevel; /** * Provider-namespaced request extensions (segregated axis). Each adapter * validates only its own namespace and ignores others — same request * object can be re-routed to a fallback provider on retry without * stripping fields. See `ProviderExtensions`. * * Anthropic-specific fields above (`thinking`/`thinkingDisplay`/`effort`) * remain valid for backward-compat; `provider.anthropic.*` is the canonical * long-term home. Migration is brain-by-brain, not breaking. */ provider?: ProviderExtensions; } /** * Spec for a tool the model is allowed to call. Schema follows the Anthropic * tool-use shape (which also matches OpenAI function-calling JSONSchema). */ interface ToolSpec { name: string; description: string; input_schema: { type: 'object'; properties: Record; required?: string[]; }; } /** * Anthropic-defined server-side advisor tool (beta * `advisor-tool-2026-03-01`). Pairs a frontier "advisor" model (e.g. Opus 4.7) * with a cheaper executor in long-horizon HoloScript agentic loops + generated * agents — executor delegates expensive reasoning to advisor, then resumes. * * Distinct shape from `ToolSpec` (no JSON schema — `input` is a free-form * advisor sub-prompt). Including ANY tool of this shape in * `LLMCompletionRequest.tools` causes the Anthropic adapter to inject the * `anthropic-beta: advisor-tool-2026-03-01` header. Other adapters MUST * ignore the entry (it is not a callable function in their tool surface). * * See `docs/LLM_CAPABILITIES.md` § Anthropic → Built-in * server-side tools. */ interface AnthropicAdvisorToolSpec { /** Discriminator for the Anthropic advisor beta. */ type: 'advisor_20260301'; /** The advisor tool's name MUST be the literal `'advisor'` per Anthropic API. */ name: 'advisor'; /** * Model the advisor uses for sub-inference (e.g. `claude-opus-4-7`). * Caller decides which frontier model pairs with the executor. */ model: string; /** * Which caller surfaces are allowed to invoke the advisor. Default is * `['direct']` (the executor model calls advisor directly). Add * `'code_execution_20260120'` to let code-execution sandboxes call advisor * mid-script. Omit for direct-only. */ allowed_callers?: Array<'direct' | 'code_execution_20250825' | 'code_execution_20260120'>; } /** * Union of every tool shape the unified request accepts. Generic JSONSchema * tools (`ToolSpec`) pass through to every adapter. Provider-specific tool * shapes (currently only `AnthropicAdvisorToolSpec`) opt in to a beta header * on the matching adapter and are filtered out by non-matching adapters. * * Discriminate via the `type` field: any tool with `type: 'advisor_20260301'` * is the advisor; anything without `type` (or with a future provider-specific * discriminator) is a generic function tool. */ type ToolSpecUnion = ToolSpec | AnthropicAdvisorToolSpec; /** Type guard for the Anthropic advisor tool. */ declare function isAnthropicAdvisorTool(t: ToolSpecUnion): t is AnthropicAdvisorToolSpec; /** Generic JSONSchema function tool guard for non-Anthropic adapters. */ declare function isToolSpec(t: ToolSpecUnion): t is ToolSpec; /** * Filter a heterogenous tools list down to generic `ToolSpec` entries — drops * provider-specific shapes (currently only Anthropic's advisor) that other * adapters can't dispatch. Use this at adapter boundaries for OpenAI, * Gemini, Ollama, etc. so callers can opt into the advisor on Anthropic * without breaking fallback-provider retry on the same `LLMCompletionRequest`. */ declare function filterGenericTools(tools: ToolSpecUnion[] | undefined): ToolSpec[]; /** A tool call the model wants the caller to execute. */ interface ToolUseBlock { type: 'tool_use'; id: string; name: string; input: Record; } /** A text block from an assistant response (separate from tool_use blocks). */ interface TextBlock { type: 'text'; text: string; } interface CacheControlEphemeral { type: 'ephemeral'; ttl?: '5m' | '1h'; } interface AnthropicFileSource { type: 'file'; file_id: string; } interface AnthropicDocumentFileBlock { type: 'document'; source: AnthropicFileSource; title?: string; context?: string; citations?: { enabled: boolean; }; cache_control?: CacheControlEphemeral; } interface AnthropicImageFileBlock { type: 'image'; source: AnthropicFileSource; cache_control?: CacheControlEphemeral; } interface AnthropicContainerUploadBlock { type: 'container_upload'; file_id: string; cache_control?: CacheControlEphemeral; } type AnthropicFileContentBlock = AnthropicDocumentFileBlock | AnthropicImageFileBlock | AnthropicContainerUploadBlock; type AnthropicFileContentBlockType = AnthropicFileContentBlock['type']; interface AnthropicFileContentBlockOptions { title?: string; context?: string; citations?: { enabled: boolean; }; cacheControl?: CacheControlEphemeral; } /** * Helper for referencing an Anthropic Files API `file_id` in messages. * * `document` and `image` blocks let Claude read uploaded PDFs/text/images. * `container_upload` mounts arbitrary uploaded files for code execution. */ declare function anthropicFileContentBlock(fileId: string, type: AnthropicFileContentBlockType, options?: AnthropicFileContentBlockOptions): AnthropicFileContentBlock; type LLMContentBlock = TextBlock | ToolUseBlock | ToolResultBlock | AnthropicFileContentBlock; type AssistantContentBlock = TextBlock | ToolUseBlock; /** * Provider-agnostic stream chunk emitted by `ILLMProvider.streamCompletion`. * * Adapters MAP their native streaming surface (Anthropic SSE events, Ollama * NDJSON, OpenAI chat-completion stream, etc.) onto this discriminated union * so callers consume one shape regardless of provider. The chunk vocabulary * is intentionally minimal — text deltas, tool-use lifecycle, and a final * `message_stop` carrying the same `finishReason` + usage that * `LLMCompletionResponse` carries. Thinking/refusal/error chunks can be added * later without breaking existing consumers. * * Reading the stream: * for await (const chunk of provider.streamCompletion(req)) { * if (chunk.type === 'text_delta') sendToClient(chunk.text); * if (chunk.type === 'tool_use_end') queueTool(chunk.id, chunk.name, chunk.input); * if (chunk.type === 'message_stop') finalize(chunk.finishReason, chunk.usage); * } * * Tool-input deltas are emitted as PARTIAL JSON fragments (the exact text the * provider emitted, before any parse). The terminating `tool_use_end` chunk * carries the FULLY PARSED `input` object — callers should use `tool_use_end` * to dispatch, and use `tool_use_input_delta` only for live UX (e.g. showing * partial JSON in a spinner). */ type LLMStreamChunk = { type: 'text_delta'; text: string; } | { type: 'tool_use_start'; id: string; name: string; } | { type: 'tool_use_input_delta'; id: string; /** Provider-emitted partial JSON fragment. NOT cumulative — each delta * is the new fragment to append to prior fragments for the same id. */ partialJson: string; } | { type: 'tool_use_end'; id: string; /** Fully parsed tool input. Empty object if the model emitted no input. */ input: Record; } | { type: 'message_stop'; finishReason: LLMCompletionResponse['finishReason']; usage: TokenUsage; model: string; /** Provider-assigned request-id (see LLMCompletionResponse.requestId). */ requestId?: string; /** Raw response headers (see LLMCompletionResponse.responseHeaders). */ responseHeaders?: Record; }; /** * Coerce LLMMessage content to a plain string for adapters/surfaces that * intentionally flatten tool-use (chat-completion compatibility, gemini, * local-llm, bitnet, mock). Non-text blocks are flattened to a JSON-ish * summary so the message still carries SOME signal about what the model said. * Native tool-capable surfaces (Anthropic, OpenAI Responses) should pass * structured blocks through unchanged. */ declare function messageContentAsString(content: LLMMessage['content']): string; interface LLMCompletionResponse { /** The generated text content (concatenated text blocks only) */ content: string; /** Token usage statistics */ usage: TokenUsage; /** Which model produced this response */ model: string; /** Exact provider-reported model identity, or null when the provider omitted it. */ reportedModel?: string | null; /** The provider that handled this request */ provider: LLMProviderName; /** Finish reason — `tool_use` indicates toolUses must be executed and * re-fed via a follow-up request to continue the loop. * - `refusal` — model declined for safety reasons (Claude 4+ stop_reason * `refusal`); response body may not match the requested format. Caller * must NOT retry the same prompt verbatim. * - `context_window_exceeded` — model hit the CONTEXT WINDOW limit, not * the requested max_tokens (Claude 4.5+ stop_reason * `model_context_window_exceeded`). Caller should compact / split the * conversation, not just bump max_tokens. */ finishReason: 'stop' | 'length' | 'content_filter' | 'error' | 'tool_use' | 'refusal' | 'context_window_exceeded'; /** * Tool-use blocks the model wants the caller to execute. Empty when the * model didn't request tools. Caller should run each tool, then send a * follow-up request containing this assistant turn's full content blocks * (preserved in `assistantBlocks` for round-trip fidelity) plus a user * message with `tool_result` blocks for each tool use. */ toolUses?: ToolUseBlock[]; /** Full assistant content blocks (text + tool_use), in order, for the * follow-up tool_result message construction. */ assistantBlocks?: AssistantContentBlock[]; /** * Provider-assigned request identifier for observability, debugging, and * support escalations. * * - Anthropic: the `request-id` response header (via * `MessageStream.request_id` or `APIPromise.withResponse()`). * - OpenAI: the `x-request-id` response header. * - Other providers: the closest equivalent header/field, or `undefined` * if the provider doesn't expose one. * * Without this, debugging a failing request requires reconstructing the * timeline from logs alone — the provider's own support team needs the * request-id to look up server-side traces. Capturing it here makes every * LLMCompletionResponse self-describing for incident response. */ requestId?: string; /** * Raw response headers from the provider, keyed by header name. * * Captured alongside `requestId` so callers can extract rate-limit headers, * retry-after, provider-specific metadata, etc. without parsing the `raw` * response object. Only populated by adapters that expose it (currently * Anthropic via `MessageStream.response`). Keys are lowercased. * * Deliberately `Record` rather than `Headers` — this is * a plain-data snapshot, not a live browser/Header object. Adapters * convert at capture time. */ responseHeaders?: Record; /** * Inline moderation result from the provider. * * Populated by the OpenAI adapter when `req.provider.openai.moderation` was * set on the request — the response carries the moderation verdict alongside * the generation output in a single round-trip. * * Anthropic analog: built-in safety layer surfaces as `finishReason: * "content_filter"` (mapped from `stop_reason: "refusal"`). No separate * result object is emitted; `moderationResult` will be `undefined` on * Anthropic responses. Callers that need a unified HoloDoor gate should * check both `finishReason === "content_filter"` (Anthropic) and * `moderationResult?.flagged === true` (OpenAI) at the call site. * * See `InlineModerationResult` for the field contract. */ moderationResult?: InlineModerationResult; /** Raw response from the provider (for debugging) */ raw?: unknown; } interface TokenUsage { /** Tokens in the prompt/input */ promptTokens: number; /** Tokens in the completion/output */ completionTokens: number; /** Total tokens used */ totalTokens: number; /** False when compatibility zeroes are present because the provider omitted usage. */ reported?: boolean; } /** Per-call transport controls that must reach the underlying provider request. */ interface LLMRequestOptions { signal?: AbortSignal; } interface LLMFileUploadRequest { /** * Provider-native upload object. Anthropic SDK accepts Node ReadStream, * File/Blob-like values, or other Uploadable shapes. Kept `unknown` here so * the provider-neutral type does not force every adapter to depend on one * vendor SDK's upload type. */ file: unknown; /** Provider-namespaced upload extensions. */ provider?: ProviderExtensions; } interface LLMFileMetadata { id: string; type: 'file'; filename: string; mimeType: string; sizeBytes: number; createdAt: string; downloadable?: boolean; raw?: unknown; } interface HoloScriptGenerationRequest { /** Natural language description of the scene */ prompt: string; /** Optional system context for the model */ systemPrompt?: string; /** Maximum scene complexity (object count hint) */ maxObjects?: number; /** Target export format */ targetFormat?: 'holo' | 'hsplus' | 'hs'; /** Temperature override */ temperature?: number; } interface HoloScriptGenerationResponse { /** The generated HoloScript code */ code: string; /** Whether the generated code passed validation */ valid: boolean; /** Validation errors if any */ errors: string[]; /** The provider that generated this */ provider: LLMProviderName; /** Token usage */ usage: TokenUsage; /** Detected traits in generated code */ detectedTraits: string[]; } type LLMProviderName = 'openai' | 'anthropic' | 'gemini' | 'mock' | 'bitnet' | 'local-llm' | 'fleet' | 'openrouter' | 'xai' | 'brittney-cloud' | 'sovereign'; interface LLMProviderConfig { /** API key for authentication */ apiKey: string; /** Base URL override (for proxies or self-hosted endpoints) */ baseURL?: string; /** Request timeout in milliseconds. Default: 30000 */ timeoutMs?: number; /** Maximum retry attempts on rate limits. Default: 3 */ maxRetries?: number; /** Default model to use if not specified per-request */ defaultModel?: string; } type OpenAIApiSurface = 'responses' | 'chat-completions'; type OpenAIReasoningEffort = 'none' | 'minimal' | 'low' | 'medium' | 'high' | 'xhigh'; interface OpenAIProviderConfig extends LLMProviderConfig { /** OpenAI organization ID */ organization?: string; /** * OpenAI API surface. Defaults to `responses` so current models, hosted * tools, and function-call output items all use the modern wire contract. * Use `chat-completions` only for older OpenAI-compatible proxies. */ apiSurface?: OpenAIApiSurface; /** * Responses API reasoning effort. Leave unset for provider/model default. * Useful for GPT-5.x calls where HoloScript wants explicit cost/latency * control at adapter construction time. */ reasoningEffort?: OpenAIReasoningEffort; /** * Responses API persistence flag. Leave undefined to inherit OpenAI's * default for the account/project; set false for stateless private calls. */ store?: boolean; /** * Whether OpenAI Responses may emit multiple tool calls in one turn. * Defaults true to match agentic HoloScript workflows. */ parallelToolCalls?: boolean; } interface AnthropicProviderConfig extends LLMProviderConfig { /** Anthropic API version header. Default: '2023-06-01' */ apiVersion?: string; /** * Opt in to prompt caching on the system prompt + tools. * * When `true`, the adapter sends `system` in array form with a * `cache_control: {type: "ephemeral"}` breakpoint on the last system * block. Render order is `tools → system → messages`, so the breakpoint * caches BOTH tools AND system together — exactly the pattern an agent * runner needs (stable brain composition + stable tool list across * many ticks). * * Cost shape: cache writes cost ~1.25× input on the FIRST request with * a given prefix; subsequent reads cost ~0.1× input. For a stable * `system` ≥ the model's minimum cacheable prefix (Opus 4.7 / Opus 4.6 * / Haiku 4.5: 4096 tokens; Sonnet 4.6: 2048 tokens; Opus 4.8: 1024 tokens), break-even is * 2 requests with 5-min TTL. Below the minimum the request is sent * unchanged — no error, just no cache benefit. * * Default: `true`. Caching is the right default for almost every Claude * API call: agent runners get the full ~10× per-tick reduction; code-gen * paths with stable system prompts get the same; one-off calls below the * minimum prefix get neither benefit nor extra cost. Set to `false` only * when you have measured evidence that a particular caller's prompts are * pathological for caching (e.g. a hot path with varied above-minimum * prefixes that never repeat — paying 1.25× writes with zero reads). */ enablePromptCaching?: boolean; /** * Maximum total cache breakpoints per request (Anthropic API limit: 4). * * One breakpoint is always used for the system+tools prefix when * `enablePromptCaching` is true. The remaining budget is distributed * across message turns (assistant-turn boundaries), working backwards * from the most recent turn. This gives agent tool-loops (which can hit * 30+ iterations) intermediate cache hits within the 5-min TTL window, * preventing the "cache miss after ~15 blocks" cliff. * * Default: 4 (Anthropic's hard limit). Set lower (e.g. 2) to reserve * cache budget for the system prefix only, or in pathological cases * where mid-turn breakpoints add cost without reuse. */ maxCacheBreakpoints?: number; } interface GeminiProviderConfig extends LLMProviderConfig { /** Google Cloud project ID (for Vertex AI) */ projectId?: string; /** Google Cloud location (for Vertex AI). Default: 'us-central1' */ location?: string; } /** * Config for the real bitnet.cpp inference server. * No API key required — the server runs locally. */ interface BitNetProviderConfig extends Omit { /** API key — unused for local servers, defaults to empty string */ apiKey?: string; /** * Base URL of the bitnet.cpp server. * Default: http://localhost:8080 */ baseURL?: string; /** BitNet model ID (HuggingFace format). Default: 'microsoft/bitnet-b1.58-2B-4T' */ model?: string; } /** * Config for a generic local OpenAI-compatible inference server. * Works with llama.cpp, Ollama, LM Studio, or any compatible server. * No API key required — the server runs locally. */ interface LocalLLMProviderConfig extends Omit { /** API key — unused for local servers, defaults to empty string */ apiKey?: string; /** * Base URL of the local LLM server. * Default: http://localhost:8080 */ baseURL?: string; /** Model name to send in requests. Default: 'mistral-7b-instruct' */ model?: string; } /** * Config for the OpenRouter provider. * OpenRouter is an OpenAI-compatible API that routes to 200+ models. * The required HTTP-Referer and X-Title headers are set for attribution; * callers can override via referer/title config. */ interface OpenRouterProviderConfig extends LLMProviderConfig { /** * HTTP-Referer header for attribution. * OpenRouter requires this. Default: 'https://holoscript.net' */ referer?: string; /** * X-Title header for attribution. * OpenRouter requires this. Default: 'HoloScript' */ title?: string; } /** * Config for the xAI (Grok) provider. * xAI provides an OpenAI-compatible API at https://api.x.ai/v1. */ interface XAIProviderConfig extends LLMProviderConfig { } /** * Config for the Brittney Cloud provider. * Connects to HoloScript's first-party cloud inference gateway. */ interface BrittneyCloudProviderConfig extends LLMProviderConfig { /** * Inference tier. 'pro' routes to Kimi K2.5 when available; * 'standard' uses the preferred available provider. * Default: 'standard'. */ tier?: 'standard' | 'pro'; } /** * Unified interface that all LLM provider adapters must implement. */ interface ILLMProvider { /** The name of this provider */ readonly name: LLMProviderName; /** Available models for this provider */ readonly models: readonly string[]; /** Default model for HoloScript generation */ readonly defaultHoloScriptModel: string; /** * Capability manifest — drives capability-aware routing in the supervisor. * Adapters override `BaseLLMAdapter`'s `DEFAULT_CAPABILITIES` with their * actual declarations. Brain compositions declare `requires` / `prefers` * / `avoids` over capability keys; the router matches at session start. */ readonly capabilities: Capabilities; /** * Optional escape hatch — return the underlying typed SDK client for * advanced cases not covered by the universal/segregated split. Calling * `.native()` is allowed but flags routing-debt: a knob the abstraction * is missing. Each adapter narrows the return type via its own typed * override (e.g. AnthropicAdapter.native(): Anthropic). */ native?(): unknown; /** * Send a completion request to the provider. Adapters wrap the underlying * SDK/fetch call in `withRetry` (BaseLLMAdapter) so transient errors * (429, 5xx, network) retry with exponential backoff and Retry-After * honor before throwing. */ complete(request: LLMCompletionRequest, model?: string, options?: LLMRequestOptions): Promise; /** * Generate HoloScript code from a natural language description. Validates * the model output's structure (balanced braces, recognized object types) * and returns the validation result on the response. Transient-error retry * is handled inside `complete()` — this method calls it once and surfaces * any final error. */ generateHoloScript(request: HoloScriptGenerationRequest): Promise; /** * Check if the provider is correctly configured and reachable. */ healthCheck(): Promise<{ ok: boolean; latencyMs: number; error?: string; }>; /** * Stream a completion as an async iterable of provider-agnostic chunks * (`LLMStreamChunk`). Adapters that support native streaming (Anthropic * SSE, Ollama NDJSON, OpenAI chat-completion stream) translate native * events to this shape; adapters that don't yet have a streaming impl * fall back to the BaseLLMAdapter default (call `complete()`, then * yield the full text + tool-use blocks as a synthesized batch). * * Stream MUST end with exactly one `message_stop` chunk. Callers can * trust this and finalize state on `message_stop`. * * Adapters do NOT wrap streamCompletion in `withRetry` — partial-text * retries would re-emit prefix tokens and corrupt downstream state. * Pre-flight failures (auth, 429, request validation) throw before the * first chunk; mid-stream failures yield a final `message_stop` with * `finishReason: 'error'` and the partial state observed so far. */ streamCompletion(request: LLMCompletionRequest, model?: string): AsyncIterable; /** * Upload a reusable file reference on providers that support a Files API. * Adapters without native support inherit BaseLLMAdapter's explicit * unsupported-provider error. */ uploadFile(request: LLMFileUploadRequest): Promise; /** * Open a bidirectional realtime voice session. OPTIONAL — only providers * whose manifest declares `capabilities.realtimeVoice === true` implement it; * the router refuses to route a realtime brain to a provider that doesn't. * Adapters without support inherit BaseLLMAdapter's explicit * unsupported-provider throw (mirrors `uploadFile`). This is a SEPARATE * transport from `complete()` / `streamCompletion()` — it never touches the * chat path. Kept optional (like `native?`) so existing adapters compile * unchanged. Narrow with `supportsRealtime()` before calling. */ openRealtimeSession?(config: RealtimeSessionConfig): Promise; } interface LLMProviderRegistry { openai?: ILLMProvider; anthropic?: ILLMProvider; gemini?: ILLMProvider; bitnet?: ILLMProvider; 'local-llm'?: ILLMProvider; fleet?: ILLMProvider; openrouter?: ILLMProvider; xai?: ILLMProvider; 'brittney-cloud'?: ILLMProvider; } interface ProviderSelectionStrategy { /** Primary provider to use */ primary: LLMProviderName; /** Fallback provider if primary fails */ fallback?: LLMProviderName; /** Cost optimization: prefer cheapest available provider */ optimizeForCost?: boolean; /** Speed optimization: prefer fastest responding provider */ optimizeForSpeed?: boolean; } declare class LLMProviderError extends Error { readonly provider: LLMProviderName; readonly statusCode?: number | undefined; readonly retryable: boolean; constructor(message: string, provider: LLMProviderName, statusCode?: number | undefined, retryable?: boolean); } declare class LLMRateLimitError extends LLMProviderError { readonly retryAfterMs?: number | undefined; constructor(provider: LLMProviderName, retryAfterMs?: number | undefined); } declare class LLMAuthenticationError extends LLMProviderError { constructor(provider: LLMProviderName); } declare class LLMContextLengthError extends LLMProviderError { readonly tokenCount: number; constructor(provider: LLMProviderName, tokenCount: number); } declare class LLMCreditExhaustedError extends LLMProviderError { constructor(provider: LLMProviderName); } /** * Capability declaration for a provider. Drives capability-aware routing: * brain compositions declare `requires` / `prefers` / `avoids` over capability * keys; the router matches against each provider's manifest at session start. * * Mirrors the cross-provider matrix in * `docs/LLM_CAPABILITIES.md`. Add new fields here when the doc * grows a new row, and update each adapter's overridden manifest. * * Universal+segregated principle (founder ruling 2026-05-06): this is the * UNIVERSAL axis (the swap contract). Provider-specific superpowers go on * the SEGREGATED axis (`req.provider..*` — see `ProviderExtensions`). * Both first-class. Never flatten one into the other. */ interface Capabilities { /** Maximum input context window in tokens (0 = unknown / per-model). */ contextWindow: number; /** Maximum output tokens per response (0 = unknown / per-model). */ maxOutput: number; /** Per-million-token pricing in USD. Omit for $0 / variable / unknown. */ costPerMillion?: { input: number; output: number; }; streaming: boolean; tools: boolean; /** Programmatic function/tool calling surface (provider can return structured tool calls). */ programmaticToolCalling?: boolean; vision: boolean; /** >1568px long-edge images (e.g. Anthropic Opus 4.7 2576px). */ highResVision?: boolean; videoInput?: boolean; audioInput?: boolean; audioOutput?: boolean; /** Streaming TTS/audio chunks; distinct from normal text-token streaming. */ streamingSpeechGeneration?: boolean; imageGeneration?: boolean; /** Provider-family video generation, separate from normal text completion. */ videoGeneration?: boolean; /** Provider-family video editing or refinement, separate from normal text completion. */ videoEditing?: boolean; /** Still-image-to-video animation, separate from static image generation. */ imageAnimation?: boolean; /** Conversational media refinement loop, e.g. iterative video/image edits. */ conversationalMediaEditing?: boolean; /** Provider-visible reasoning artifacts/summaries; raw private CoT is not assumed. */ visibleReasoning?: boolean; /** Adjustable effort level (low/medium/high/xhigh/max). */ adjustableEffort?: boolean; /** Real-time web search (xAI Live Search, Gemini Grounding, OpenAI web tool). */ liveWebSearch?: boolean; /** Shell tool support; may be hosted or caller-executed depending on provider/runtime. */ hostedShell?: boolean; /** Server-side code execution sandbox. */ codeExecutionSandbox?: boolean; /** * GUI-automation surface: the model can drive a computer via screenshots + * click/type/scroll actions (Anthropic computer-use tool, OpenAI Responses * `computer_use`, Gemini Computer Use). UNIVERSAL routing axis — a brain * declares `requires: ["computerUse"]` (the router's satisfies() matches the * verbatim camelCase Capabilities key — NOT snake_case `computer_use`) and it * swaps to any computerUse-capable provider, giving HoloDoor ONE policy * chokepoint instead of three vendor-specific ones. The per-vendor action * DIALECTS (coordinate vs environment vs intent-based — they differ) live * segregated on `ProviderExtensions..computerUse`; do not flatten here. */ computerUse?: boolean; /** First-party file search / vector store — never source-of-truth (W.GOLD don't). */ fileSearchBuiltIn?: boolean; /** Server-side prompt caching (Anthropic cache_control, Gemini cached_content). */ promptCaching?: boolean; /** Caller can place explicit cache breakpoints or disable implicit cache selection. */ explicitPromptCacheControls?: boolean; /** Provider can persist reasoning state across stored Responses/conversation turns. */ persistedReasoning?: boolean; /** Per-loop token budget the model is aware of (Anthropic Task Budgets). */ perLoopBudget?: boolean; /** Server-side conversation compaction (Anthropic compact-2026-01-12). */ serverSideCompaction?: boolean; /** Hosted agentic loop (Anthropic Managed Agents, OpenAI Assistants/Agents). */ hostedAgenticLoop?: boolean; /** Persistent cross-session memory store (Anthropic Memory Stores, OpenAI vector store). */ persistentMemoryStore?: boolean; /** Strict JSON-schema-enforced structured outputs. */ structuredOutputs?: boolean; /** First-class embeddings endpoint. */ embeddings?: boolean; /** Batch API with discounted pricing (typically 50% off, 24h SLA). */ batchApi?: boolean; /** WebRTC/SIP/WebSocket realtime voice (OpenAI Realtime API). */ realtimeVoice?: boolean; /** Embedded chat UI components (OpenAI ChatKit). */ embeddedChatUI?: boolean; /** MCP-Apps iframe surface inside vendor's chat UI (OpenAI Apps SDK). */ appsIframeSurface?: boolean; /** This provider exposes its capabilities AS an MCP server (Codex MCP-server mode). */ mcpServerMode?: boolean; /** App worktrees / per-task git worktree lifecycle (Codex). */ appWorktrees?: boolean; /** First-party eval / prompt-optimizer pipeline (OpenAI Evals + Prompt Optimizer). */ evalsFirstParty?: boolean; /** Runs locally / offline (Ollama, Codex hardware-native). */ local?: boolean; /** $0 per-call inference cost (compute pre-paid via hardware). */ zeroMarginalInference?: boolean; /** Available via Amazon Bedrock. */ bedrockAvailable?: boolean; /** Available via Google Vertex AI. */ vertexAvailable?: boolean; /** Programmatic bearer-token API access (vs IDE-only / OAuth-only). */ bearerTokenAccess: boolean; } /** * Provider-namespaced request extensions. Each adapter validates ONLY its * own namespace and ignores others — the same `LLMCompletionRequest` can * be routed to a fallback provider on retry without stripping fields. * * This is the SEGREGATED axis: each provider's superpowers are addressable * without leakage. The UNIVERSAL axis is the top-level fields on * `LLMCompletionRequest` (messages, maxTokens, tools, stream, ...). * * See `docs/LLM_CAPABILITIES.md` § Universal axis vs Segregated axis. */ interface ProviderExtensions { anthropic?: AnthropicProviderExtensions; openai?: OpenAIProviderExtensions; codex?: CodexProviderExtensions; grok?: GrokProviderExtensions; gemini?: GeminiProviderExtensions; ollama?: OllamaProviderExtensions; copilot?: CopilotProviderExtensions; } /** * Anthropic-specific request extensions. Existing top-level * `thinking`/`thinkingDisplay`/`effort` fields on `LLMCompletionRequest` * remain valid for backward-compat; `provider.anthropic.*` is the canonical * long-term home. Migration is brain-by-brain, not breaking. */ interface AnthropicProviderExtensions { thinking?: AnthropicThinkingParam; thinkingDisplay?: 'summarized' | 'omitted'; effort?: AnthropicEffortLevel; /** Beta `task-budgets-2026-03-13` — Opus 4.7 per-loop token budget visible to model. */ taskBudget?: { type: 'tokens'; total: number; }; /** Beta `compact-2026-01-12` — server-side conversation compaction (4.6+). */ compaction?: { type: 'compact_20260112'; }; /** Opt-in beta headers (e.g. `managed-agents-2026-04-01`, `advisor-tool-2026-03-01`). */ betaHeaders?: string[]; /** * Anthropic `tool_choice` — controls whether/which tool the model must call. * `{ type: 'auto' }` default, `{ type: 'any' }` forces at least one tool, * `{ type: 'tool', name: 'fn' }` forces a specific tool. */ toolChoice?: { type: 'auto' | 'any' | 'none'; } | { type: 'tool'; name: string; }; /** * Anthropic computer-use tool (Opus 4.7/4.8). Segregated action dialect: * COORDINATE-based mouse/keyboard over a virtual display, selected by a * dated tool type (`computer_YYYYMMDD`). Kept distinct from the OpenAI/Gemini * dialects on purpose — the universal `Capabilities.computerUse` axis is for * swap, this is for exploit. */ computerUse?: { /** Dated tool type, e.g. 'computer_20250124'. */ toolVersion: string; displayWidthPx: number; displayHeightPx: number; /** X11 display number for multi-display setups. */ displayNumber?: number; }; } /** * Inline moderation request object accepted by OpenAI Responses/Chat Completions. * * When present, OpenAI runs content-moderation on the generation request and * returns a `moderationResult` alongside the response — eliminating a separate * POST /v1/moderations call for HoloDoor policy gates. * * Wire form: `{ "moderation": { "input": "all" | string[], "model": "omni-moderation-latest" } }` * Source: developers.openai.com/api/docs/changelog (verified 2026-06-08 A-020). */ interface InlineModerationRequest { /** * Which content to moderate. * - `"all"` — moderate all text/image inputs in the request. * - `string[]` — explicit item IDs to moderate (Responses API only). */ input: 'all' | string[]; /** * Moderation model to use. Defaults to `"omni-moderation-latest"` if omitted. */ model?: string; } /** * Inline moderation result returned by OpenAI alongside a generation response. * * Maps to the `moderation` field on the Responses API response object when * `moderationRequest` was set on the request. The `results` array contains one * entry per moderated item. * * Anthropic analog: built-in safety layer. When Claude stops for policy reasons * the `stop_reason` is `"refusal"` (mapped to `finishReason: "content_filter"` * in this SDK). No separate result object is emitted — the stop_reason IS the * moderation signal. Callers that need a unified gate should check both * `finishReason === "content_filter"` (Anthropic) and `moderationResult.flagged` * (OpenAI) at the call site. */ interface InlineModerationResult { /** True if any category was flagged by the moderation model. */ flagged: boolean; /** * Per-category flag map. Keys are moderation category names * (e.g. `"hate"`, `"self-harm"`, `"sexual"`, `"violence"`). */ categories: Record; /** * Per-category confidence scores (0–1). Present when the provider returns them. */ categoryScores?: Record; /** Raw provider result for categories this SDK hasn't typed yet. */ raw?: unknown; } interface OpenAIProviderExtensions { reasoningEffort?: OpenAIReasoningEffort; parallelToolCalls?: boolean; /** * OpenAI tool-choice mode. `required` forces at least one of the supplied * tools, while `none` disables tool calls and `auto` leaves the choice to * the model. With one supplied function plus `parallelToolCalls: false`, * `required` is the deterministic provider-native planner contract. */ toolChoice?: 'auto' | 'required' | 'none'; /** Responses API background mode — long-running task returns a token; poll for completion. */ background?: boolean; /** * Inline moderation request. When set, OpenAI runs content-moderation on * the generation request and returns a `moderationResult` on the response * — no separate POST /v1/moderations call needed. HoloDoor policy gates * use this to enforce content policy in one round-trip. * * Source: developers.openai.com/api/docs/changelog (verified 2026-06-08 A-020). */ moderation?: InlineModerationRequest; /** * GPT-5.6 explicit prompt-cache controls. Segregated OpenAI dialect: * callers can opt into explicit-only cache selection and mark content * blocks with `prompt_cache_breakpoint` when constructing provider-native * payloads. */ promptCache?: { mode?: 'auto' | 'explicit'; maxBreakpoints?: number; }; /** * Persist reasoning/conversation state with OpenAI Responses `store`. * Kept provider-scoped because other providers expose different persistence * and retention contracts. */ persistedReasoning?: boolean; /** Sora/GPT Image route hint; current text completion path ignores media route hints. */ videoEditing?: boolean; /** Sora route hint for still-image animation into video. */ imageAnimation?: boolean; /** Conversational Sora/GPT Image refinement hint for future media adapters. */ conversationalMediaEditing?: boolean; /** * OpenAI Responses `computer_use` tool. Segregated action dialect: * ENVIRONMENT-scoped screenshot + action loop (computer_call / * computer_call_output). Distinct from the Anthropic coordinate dialect and * the Gemini intent dialect. */ computerUse?: { environment: 'browser' | 'mac' | 'windows' | 'ubuntu'; displayWidth: number; displayHeight: number; }; /** * OpenAI Realtime voice transport dialect. Segregated per-vendor shape (do * NOT flatten — same rule as computerUse): OpenAI is the richest, carrying a * 3-way transport union + SIP telephony + ephemeral secrets. xAI (WebSocket * only + REST STT/TTS billed per-hour) and Gemini (live-vs-tts mode * discriminant) get their own distinct shapes on their own extension * interfaces in slice D. The universal `Capabilities.realtimeVoice` flag is * the swap axis; this is the exploit axis. */ realtimeVoice?: { transport: 'webrtc' | 'sip' | 'websocket'; /** Defaults: gpt-realtime-2.1 (full) / gpt-realtime-2.1-mini (cost). */ model: 'gpt-realtime-2.1' | 'gpt-realtime-2.1-mini' | string; /** Named voice, e.g. 'marin', 'cedar'. */ voice?: string; /** Browser/Quest surfaces mint a short-lived client secret server-side. */ ephemeralSecret?: { mint: boolean; ttlSeconds?: number; }; /** SIP telephony surface. */ sip?: { projectId: string; inbound?: boolean; outbound?: boolean; }; turnDetection?: { type: 'server_vad' | 'semantic_vad'; threshold?: number; }; audioFormat?: { input: 'pcm16' | 'g711_ulaw' | 'g711_alaw'; output: 'pcm16' | 'g711_ulaw' | 'g711_alaw'; }; }; } /** * Codex is a runtime, not an API surface — its extensions describe * execution-lane state (worktree path, AGENTS.md, hooks, MCP-server mode). * Slot is open; populated when the Codex execution lane is built. */ interface CodexProviderExtensions { } interface GrokProviderExtensions { /** xAI Live Search — real-time web + X-platform results. */ liveSearch?: boolean; /** Grok Imagine route hint; current chat completion path ignores media route hints. */ videoEditing?: boolean; /** Grok Imagine image-to-video route hint. */ imageAnimation?: boolean; /** Grok Imagine Agent Mode refinement hint for future media adapters. */ conversationalMediaEditing?: boolean; /** * xAI Voice Agent realtime transport dialect. Segregated per-vendor shape (do * NOT flatten — same rule as computerUse / OpenAI realtimeVoice). xAI's realtime * surface is genuinely DISTINCT from OpenAI's: a WebSocket-ONLY agent endpoint * (no WebRTC, no SIP, no ephemeral-secret mint) at `/v1/realtime`, with SEPARATE * REST STT/TTS endpoints, and a PER-HOUR billing model ($3/hr agent) rather than * OpenAI's per-token audio pricing. Pricing lives with CostGuard * (`XAI_REALTIME_PRICING_USD_PER_HOUR` + `defaultXaiRealtimePricer`, the * per-DURATION variant of the `RealtimePricer` union). * * Verified 2026-07-10 (docs/llm-capabilities/xai-grok.md § Voice Agent API): * `/v1/realtime` WebSocket + `grok-voice-think-fast-1.0` + `/v1/tts` ($15/1M * chars) + `/v1/stt` ($0.10/hr REST, $0.20/hr streaming). VERIFICATION GAP: the * exact per-message WIRE FRAMES for `/v1/realtime` are not published in that * doc — this is a typed INTENT surface (endpoints + model + transport + billing * model), NOT a byte-level wire spec. The frame schema + the session-opener * (`GrokRealtimeAdapter`) are a slice-E live-endpoint known-unknown; do not * invent frame fields here. */ realtimeVoice?: { /** * xAI has NO WebRTC/SIP surface — WebSocket only. Literal-typed to `'websocket'` * so the OpenAI transports (`'webrtc'` / `'sip'`) cannot leak onto this shape. */ transport: 'websocket'; /** Voice agent model, e.g. 'grok-voice-think-fast-1.0'. */ model: 'grok-voice-think-fast-1.0' | string; /** Realtime WebSocket endpoint. Default '/v1/realtime'. */ endpoint?: string; /** SPLIT REST speech-to-text endpoint (billed per-hour, not per-token). */ stt?: { endpoint: string; streaming?: boolean; }; /** SPLIT REST text-to-speech endpoint ($15 / 1M chars). */ tts?: { endpoint: string; }; }; } interface GeminiProviderExtensions { /** Search Grounding — first-party Google Search citations. */ grounding?: boolean; /** Reference to a previously-cached `cached_content` resource. */ cachedContent?: string; /** systemInstruction (split from top-level `system` if upstream call needs it). */ systemInstruction?: string; /** Gemini Omni/Interactions route hint; current generateContent path ignores media route hints. */ videoEditing?: boolean; /** Gemini Omni still-image animation route hint. */ imageAnimation?: boolean; /** Gemini Omni conversational media refinement route hint. */ conversationalMediaEditing?: boolean; /** * Gemini Computer Use tool (3.5 Flash, public preview 2026-06-24). Segregated * action dialect: INTENT-based predefined UI actions across environments — * NOT coordinate-driven like Anthropic/OpenAI. Supports restricting or * excluding specific predefined actions. */ computerUse?: { environment: 'browser' | 'mobile' | 'desktop'; /** Restrict to a subset of the predefined intent actions. */ enabledActions?: string[]; /** Exclude specific predefined actions (Gemini supports exclusion). */ excludedActions?: string[]; }; /** * Gemini realtime voice transport dialect. Segregated per-vendor shape (do NOT * flatten). Genuinely DISTINCT from OpenAI (no WebRTC/SIP transport union; no SIP; * no per-token audio pricing table here) and from xAI (no per-hour REST STT/TTS * split; has no top-level `transport` field at all): the discriminant is a `mode` * between the bidirectional **Live API** (`live`) and **streaming TTS-only** * (`tts`) — two different Google surfaces — plus `responseModalities` (Live * session config) and short-lived **ephemeral tokens** for client-side Live * connections. * * Verified 2026-07-10 (docs/llm-capabilities/google-gemini.md): Live API models + * streaming TTS `gemini-3.1-flash-tts-preview` (streamGenerateContent / * Interactions `stream:true`); `GEMINI_CAPABILITIES.streamingSpeechGeneration` is * already true. VERIFICATION GAP: the Live API native-audio model id and the * ephemeral-token mint wire are a moving preview surface — encoded as a typed * INTENT surface with a `string` model fallback; the precise wire fields + the * session-opener (`GeminiRealtimeAdapter`) are a slice-E known-unknown. */ realtimeVoice?: { /** Live API (bidirectional duplex) vs streaming TTS-only. THE discriminant — no `transport` union. */ mode: 'live' | 'tts'; /** e.g. 'gemini-3-flash-preview' (Live) or 'gemini-3.1-flash-tts-preview' (TTS). */ model: 'gemini-3-flash-preview' | 'gemini-3.1-flash-tts-preview' | string; /** Live API session output modalities. */ responseModalities?: Array<'AUDIO' | 'TEXT'>; /** Client-side Live connections mint a short-lived ephemeral token server-side. */ ephemeralToken?: { mint: boolean; ttlSeconds?: number; }; /** Prebuilt voice selection (Live/TTS voice config). */ voiceConfig?: { prebuiltVoice?: string; }; }; } interface OllamaProviderExtensions { /** Controls how long the model stays loaded after the call (e.g. '5m', '1h'). */ keepAlive?: string; /** Per-request context window override. */ numCtx?: number; /** GPU layer offload count. */ numGpu?: number; } /** * Copilot is an IDE surface, not a router-style provider — slot exists for * symmetry but is unlikely to be populated. See * `docs/LLM_CAPABILITIES.md` § Copilot for the architectural framing. */ interface CopilotProviderExtensions { } /** * Base LLM Adapter * * Abstract base class providing shared functionality for all LLM provider adapters. * Implements retry logic, HoloScript generation prompting, and response validation. * * @version 1.0.0 */ declare abstract class BaseLLMAdapter implements ILLMProvider { abstract readonly name: LLMProviderName; abstract readonly models: readonly string[]; abstract readonly defaultHoloScriptModel: string; /** * Capability manifest. Conservative default (DEFAULT_CAPABILITIES) so * existing adapters compile without change; each adapter overrides * with its actual declarations to participate in capability-aware * routing. See `Capabilities` in types.ts for the full field set. */ readonly capabilities: Capabilities; protected readonly config: Required; constructor(config: LLMProviderConfig); protected abstract getDefaultModel(): string; abstract complete(request: LLMCompletionRequest, model?: string, options?: LLMRequestOptions): Promise; uploadFile(_request: LLMFileUploadRequest): Promise; /** * Default `openRealtimeSession` — providers that don't support the realtime * voice transport axis inherit this explicit unsupported-provider throw * (mirror of `uploadFile`). Only adapters whose manifest declares * `capabilities.realtimeVoice === true` (e.g. OpenAIRealtimeAdapter) override * it. Realtime is a SEPARATE transport from complete()/streamCompletion(). */ openRealtimeSession(_config: RealtimeSessionConfig): Promise; /** * Default `streamCompletion` implementation: call `complete()`, then yield * the full response as a synthesized batch of stream chunks. * * Adapters that support NATIVE streaming (Anthropic, Ollama, OpenAI) * override this with a real translation of their provider's stream events * to `LLMStreamChunk`. Adapters that don't (Mock, BitNet, Gemini) inherit * this default — callers get the same chunk shape, just batched at the end * instead of token-by-token. * * Synthesis order: text chunks first (one `text_delta` carrying the full * concatenated text), then tool-use chunks (one `tool_use_start` + * `tool_use_end` per tool — no `tool_use_input_delta` since the input is * already fully parsed), finally `message_stop`. This preserves the * type-level invariant that `tool_use_end` carries fully-parsed input. */ streamCompletion(request: LLMCompletionRequest, model?: string): AsyncIterable; /** * Generate HoloScript code from a natural language description. * * Transient-error retry lives inside `complete()` (each adapter wraps its * call in `withRetry`). The outer retry loop that previously lived here * was multiplicative with the inner one (4 outer × 4 inner = 16 worst-case * calls on persistent rate-limits) without adding behavior the inner loop * doesn't already cover. */ generateHoloScript(request: HoloScriptGenerationRequest): Promise; /** * Health check - tests connectivity and authentication. */ healthCheck(): Promise<{ ok: boolean; latencyMs: number; error?: string; }>; /** * Probe an OpenAI-compatible local inference server (llama.cpp, Ollama, * LM Studio, bitnet.cpp). Tries `${baseURL}/health` first, falls back to * `${baseURL}/v1/models` if that 404s — different runtimes ship different * health endpoints. 5s timeout per probe. * * `formatError` brands the error string per adapter so the failure message * carries the right setup hint (e.g. local-llm says "Start with: llama-server * -m model.gguf"; bitnet says "Run: python run_inference.py --serve"). * * Local-server adapters (local-llm, bitnet) override the cloud-flavored * `healthCheck()` (which calls `complete()`) and delegate here instead — * pinging a tiny endpoint is much cheaper than a full chat round-trip. */ protected healthCheckLocalServer(baseURL: string, formatError: (baseURL: string, message: string) => string): Promise<{ ok: boolean; latencyMs: number; error?: string; }>; protected buildGenerationPrompt(description: string, format: string, maxObjects?: number): string; /** * Extract HoloScript code from LLM response, stripping markdown fences if present. */ protected extractHoloScriptCode(content: string): string; /** * Basic structural validation of generated HoloScript code. */ protected validateHoloScriptOutput(code: string): { valid: boolean; errors: string[]; }; protected sleep(ms: number): Promise; /** * Run an async operation with retry on transient errors. * * Retries `LLMProviderError` instances where `retryable=true` (e.g. * `LLMRateLimitError`, 5xx via `mapXError`) up to `this.config.maxRetries` * times. `LLMAuthenticationError`, `LLMContextLengthError`, and any * `LLMProviderError` with `retryable=false` (4xx other than 429) throw * immediately. Non-`LLMProviderError` exceptions (network errors, SDK * shapes the adapter didn't classify) get one retry then re-throw — they * could be transient or programmer errors, one retry covers the common * "first connection from cold worker" case without masking real bugs. * * Backoff is `2^attempt * 100ms + jitter`, capped at 8000ms. When the * caught error is `LLMRateLimitError` with `retryAfterMs`, that value is * used instead of the exponential backoff. * * Adapters call this around their SDK invocation: previously the SDK's * own retry was disabled (`maxRetries: 0` with the comment "We handle * retries ourselves") but no handler existed; this is that handler. */ protected withRetry(operation: () => Promise): Promise; /** * Create a zero-usage TokenUsage for mock/error cases. */ protected zeroUsage(): TokenUsage; } export { type MessageRole as $, type AnthropicProviderConfig as A, BaseLLMAdapter as B, type Capabilities as C, type GrokProviderExtensions as D, type EphemeralSecret as E, type InlineModerationRequest as F, type GeminiProviderConfig as G, type HoloScriptGenerationRequest as H, type ILLMProvider as I, type InlineModerationResult as J, type LLMAssistantMessage as K, type LLMProviderConfig as L, LLMAuthenticationError as M, type LLMContentBlock as N, type OpenAIProviderConfig as O, type ProviderSelectionStrategy as P, LLMContextLengthError as Q, type RealtimeSessionConfig as R, LLMCreditExhaustedError as S, type LLMFileMetadata as T, type LLMFileUploadRequest as U, type LLMMessage as V, LLMProviderError as W, type XAIProviderConfig as X, LLMRateLimitError as Y, type LLMSystemMessage as Z, type LLMUserMessage as _, type RealtimeSession as a, type OllamaProviderExtensions as a0, type OpenAIApiSurface as a1, type OpenAIProviderExtensions as a2, type OpenAIReasoningEffort as a3, type ProviderExtensions as a4, type RealtimeServerEvent as a5, type RealtimeTransport as a6, type TextBlock as a7, type TokenUsage as a8, type ToolResultBlock as a9, type ToolSpec as aa, type ToolSpecUnion as ab, type ToolUseBlock as ac, anthropicFileContentBlock as ad, filterGenericTools as ae, isAnthropicAdvisorTool as af, isToolSpec as ag, messageContentAsString as ah, supportsRealtime as ai, type LLMRequestOptions as aj, type LLMCompletionRequest as b, type LLMCompletionResponse as c, type LLMStreamChunk as d, type LLMProviderRegistry as e, type HoloScriptGenerationResponse as f, type LLMProviderName as g, type BitNetProviderConfig as h, type BrittneyCloudProviderConfig as i, type LocalLLMProviderConfig as j, type OpenRouterProviderConfig as k, type AnthropicAdvisorToolSpec as l, type AnthropicContainerUploadBlock as m, type AnthropicDocumentFileBlock as n, type AnthropicEffortLevel as o, type AnthropicFileContentBlock as p, type AnthropicFileContentBlockOptions as q, type AnthropicFileContentBlockType as r, type AnthropicFileSource as s, type AnthropicImageFileBlock as t, type AnthropicProviderExtensions as u, type AnthropicThinkingParam as v, type AssistantContentBlock as w, type AudioUsage as x, type CacheControlEphemeral as y, type GeminiProviderExtensions as z };