@voltro/ai 0.74.0 → 0.76.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -5,6 +5,8 @@ import { DataStore } from '@voltro/database';
5
5
  import { defaultSettingsMiddleware } from 'ai';
6
6
  import { Effect } from 'effect';
7
7
  import { EmbeddingModelV4 } from '@ai-sdk/provider';
8
+ import { Experimental_RealtimeModelV4 } from '@ai-sdk/provider';
9
+ import { Experimental_RealtimeModelV4SessionConfig } from '@ai-sdk/provider';
8
10
  import { Experimental_VideoModelV4 } from '@ai-sdk/provider';
9
11
  import { Experimental_VideoModelV4File } from '@ai-sdk/provider';
10
12
  import { extractReasoningMiddleware } from 'ai';
@@ -16,6 +18,7 @@ import { LanguageModelV4 } from '@ai-sdk/provider';
16
18
  import { LanguageModelV4Source } from '@ai-sdk/provider';
17
19
  import { ModelMessage } from 'ai';
18
20
  import { PluginMediaPersistCapability } from '@voltro/protocol';
21
+ import { PluginMediaPersistMetadata } from '@voltro/protocol';
19
22
  import { QueryDescriptor } from '@voltro/database';
20
23
  import { QueryProcedureDescriptor } from '@voltro/protocol';
21
24
  import { Runtime } from 'effect';
@@ -25,9 +28,13 @@ import { SemanticCacheShape } from '@voltro/cache';
25
28
  import { simulateStreamingMiddleware } from 'ai';
26
29
  import { SpeechModelV4 } from '@ai-sdk/provider';
27
30
  import { Stream } from 'effect';
31
+ import { Subject } from '@voltro/protocol';
28
32
  import { Table } from '@voltro/database';
29
33
  import { ToolSet } from 'ai';
30
34
  import { TranscriptionModelV4 } from '@ai-sdk/provider';
35
+ import { VoidIfEmpty } from 'effect/Types';
36
+ import { WebSocketGatewayRoute } from '@voltro/protocol';
37
+ import { YieldableError } from 'effect/Cause';
31
38
 
32
39
  /** Count of in-flight streams (diagnostics / leak detection). */
33
40
  export declare const activeStreamCount: () => number;
@@ -302,6 +309,8 @@ export declare const AI_INFERENCES_TABLE = "_voltro_ai_inferences";
302
309
 
303
310
  export declare const AI_USAGE_TABLE = "_voltro_ai_usage";
304
311
 
312
+ export declare type AiBudgetError = AiBudgetExceeded | AiBudgetInvalidInput | AiBudgetReservationConflict;
313
+
305
314
  /** A refused reservation, not evidence of a provider charge. */
306
315
  export declare class AiBudgetExceeded extends AiBudgetExceeded_base {
307
316
  }
@@ -315,6 +324,17 @@ declare const AiBudgetExceeded_base: Schema.TaggedErrorClass<AiBudgetExceeded, "
315
324
  attemptedUsd: typeof Schema.Number;
316
325
  }>;
317
326
 
327
+ /** Invalid caller configuration, without echoing the supplied value. */
328
+ export declare class AiBudgetInvalidInput extends AiBudgetInvalidInput_base {
329
+ }
330
+
331
+ declare const AiBudgetInvalidInput_base: Schema.TaggedErrorClass<AiBudgetInvalidInput, "AiBudgetInvalidInput", {
332
+ readonly _tag: Schema.tag<"AiBudgetInvalidInput">;
333
+ } & {
334
+ field: Schema.Literal<["limitUsd", "addUsd", "since", "reservationId", "tenantId"]>;
335
+ message: typeof Schema.String;
336
+ }>;
337
+
318
338
  export declare interface AiBudgetReservation {
319
339
  readonly reservationId: string;
320
340
  readonly tenantId: string | null;
@@ -322,6 +342,17 @@ export declare interface AiBudgetReservation {
322
342
  readonly status: 'reserved' | 'replayed';
323
343
  }
324
344
 
345
+ /** A durable reservation identity cannot be rebound or spent after release. */
346
+ export declare class AiBudgetReservationConflict extends AiBudgetReservationConflict_base {
347
+ }
348
+
349
+ declare const AiBudgetReservationConflict_base: Schema.TaggedErrorClass<AiBudgetReservationConflict, "AiBudgetReservationConflict", {
350
+ readonly _tag: Schema.tag<"AiBudgetReservationConflict">;
351
+ } & {
352
+ reason: Schema.Literal<["identity-bound", "already-released"]>;
353
+ message: typeof Schema.String;
354
+ }>;
355
+
325
356
  /** Receipt and release tombstone. A receipt and its counter change commit together. */
326
357
  export declare const aiBudgetReservationsTable: Table<typeof AI_BUDGET_RESERVATIONS_TABLE, Record<string, ColumnDefinition<unknown>>, true>;
327
358
 
@@ -405,6 +436,29 @@ declare const AiError_base: Schema.TaggedErrorClass<AiError, "AiError", {
405
436
  }>>;
406
437
  }>;
407
438
 
439
+ /**
440
+ * The one way a CAUGHT provider failure becomes an `AiError`.
441
+ *
442
+ * Every call site used to write `new AiError({ message: String(e.message) })`,
443
+ * which kept the top message and dropped `cause`. For a network failure that
444
+ * leaves `fetch failed` with nothing under it — no `ECONNREFUSED`, no TLS code —
445
+ * so the one fact that says what to fix was gone. Two sites were worse and
446
+ * dropped the provider's message too (`structured provider call failed`).
447
+ *
448
+ * Two things carry the cause now, deliberately split by audience:
449
+ * - the CODES of the cause chain (`ECONNREFUSED`, `CERT_HAS_EXPIRED`, …) join
450
+ * the message. Codes only: a nested message can carry an internal host or
451
+ * address, and `message` is a schema field — it is encoded, and it reaches a
452
+ * browser whenever a procedure declares `AiError` in its error union;
453
+ * - the raw failure stays on the instance as `cause`, NOT enumerable, so a
454
+ * server log or `error.cause` has the whole chain and the encoder never
455
+ * sees it.
456
+ */
457
+ export declare const aiGenerationError: (cause: unknown, options?: {
458
+ readonly prefix?: string;
459
+ readonly evidence?: AiCallEvidence;
460
+ }) => AiError;
461
+
408
462
  /**
409
463
  * One offloaded model call.
410
464
  *
@@ -469,6 +523,18 @@ export declare interface AiUsage {
469
523
  readonly totalTokens?: number | undefined;
470
524
  }
471
525
 
526
+ /** The usage ledger could not be written — the call it describes is not booked. */
527
+ export declare class AiUsageLedgerError extends AiUsageLedgerError_base<{
528
+ readonly receiptKey: string;
529
+ readonly cause: unknown;
530
+ }> {
531
+ get message(): string;
532
+ }
533
+
534
+ declare const AiUsageLedgerError_base: new <A extends Record<string, any> = {}>(args: VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => YieldableError & {
535
+ readonly _tag: "AiUsageLedgerError";
536
+ } & Readonly<A>;
537
+
472
538
  /**
473
539
  * Price a usage tally and write a ledger row. Returns the computed
474
540
  * `CostBreakdown` so the caller can surface the cost without a re-query.
@@ -476,6 +542,40 @@ export declare interface AiUsage {
476
542
  /** What `recordAiUsage` did with the receipt: wrote it, or found it already written. */
477
543
  export declare type AiUsageReceipt = 'recorded' | 'replayed';
478
544
 
545
+ /**
546
+ * A receipt was recorded again with DIFFERENT values than the row already
547
+ * written under it.
548
+ *
549
+ * A replay is the same paid call recorded twice, so its values are the same by
550
+ * definition. Different values under one receipt mean one of them is wrong — a
551
+ * second call billed under the first one's identity, or a caller whose receipt
552
+ * key is not the identity it thinks it is. Before this existed the second
553
+ * recording answered `replayed` and handed back the values it was GIVEN, so the
554
+ * caller saw a charge that was never booked and no sign of the disagreement.
555
+ */
556
+ export declare class AiUsageReceiptConflict extends AiUsageReceiptConflict_base<{
557
+ readonly receiptKey: string;
558
+ readonly recorded: AiUsageReceiptFacts;
559
+ readonly attempted: AiUsageReceiptFacts;
560
+ }> {
561
+ get message(): string;
562
+ }
563
+
564
+ declare const AiUsageReceiptConflict_base: new <A extends Record<string, any> = {}>(args: VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => YieldableError & {
565
+ readonly _tag: "AiUsageReceiptConflict";
566
+ } & Readonly<A>;
567
+
568
+ /** The facts of one usage row that a replay must agree with. */
569
+ export declare interface AiUsageReceiptFacts {
570
+ readonly tenantId: string | null;
571
+ readonly provider: string;
572
+ readonly model: string;
573
+ readonly operation: string;
574
+ readonly inputTokens: number;
575
+ readonly outputTokens: number;
576
+ readonly costMicroUsd: number;
577
+ }
578
+
479
579
  export declare interface AiUsageRecordInput {
480
580
  readonly tenantId?: string | null;
481
581
  readonly provider: ProviderName | string;
@@ -690,6 +790,9 @@ export declare const buildCopilotSystemPrompt: (schema: CopilotSchema) => string
690
790
  * `execute` tools ignore it. */
691
791
  export declare const buildSdkTools: (tools: Record<string, AnyTool>, ctx?: ToolContext) => ToolSet;
692
792
 
793
+ /** The names the built-in generators resolve without a registration. */
794
+ export declare const BUILTIN_MEDIA_PROVIDERS: ReadonlyArray<string>;
795
+
693
796
  /** The names `generateSpeech` resolves without a registration. */
694
797
  export declare const BUILTIN_SPEECH_PROVIDERS: ReadonlyArray<string>;
695
798
 
@@ -725,6 +828,9 @@ export declare const capabilitiesOf: (raw: RawGatewayModel) => ModelCapabilities
725
828
  /** Clear the global middleware stack. */
726
829
  export declare const clearAiMiddleware: () => void;
727
830
 
831
+ /** Test seam: forget every registration. */
832
+ export declare const clearMediaTaskProviders: () => void;
833
+
728
834
  /** Test seam: forget every registration. */
729
835
  export declare const clearSpeechProviders: () => void;
730
836
 
@@ -1068,6 +1174,14 @@ export declare interface DefineEvalInput {
1068
1174
  */
1069
1175
  export declare const definePrompt: (definition: PromptDefinition) => DefinedPrompt;
1070
1176
 
1177
+ /**
1178
+ * Declare an authenticated, server-owned voice/text relay as a *.ws.ts route.
1179
+ * This uses the gateway listener, not the physical RPC subscription socket.
1180
+ * Credentials, provider configuration and raw provider events stay server-side.
1181
+ * No reconnection or replay is attempted: a new connection is a new paid session.
1182
+ */
1183
+ export declare const defineRealtimeSession: (options: RealtimeSessionOptions) => WebSocketGatewayRoute;
1184
+
1071
1185
  export declare const defineTool: <A, I, O>(def: {
1072
1186
  readonly name: string;
1073
1187
  readonly description: string;
@@ -1865,6 +1979,10 @@ export declare interface InferenceDispatchOptions {
1865
1979
  readonly maxAttempts?: number;
1866
1980
  readonly baseDelayMs?: number;
1867
1981
  readonly maxDelayMs?: number;
1982
+ /** Between polls of a task-provider media job. Default {@link DEFAULT_MEDIA_TASK_POLL_MS}. */
1983
+ readonly taskPollMs?: number;
1984
+ /** A task still pending this long after submission is failed. Default {@link DEFAULT_MEDIA_TASK_TIMEOUT_MS}. */
1985
+ readonly taskTimeoutMs?: number;
1868
1986
  }
1869
1987
 
1870
1988
  export declare interface InferenceDispatchResult {
@@ -1876,6 +1994,9 @@ export declare interface InferenceDispatchResult {
1876
1994
  readonly failed: number;
1877
1995
  readonly retried: number;
1878
1996
  readonly reclaimed: number;
1997
+ /** Task-provider media jobs asked once and found still pending — released
1998
+ * back to the queue for a later claim, without a lease held across the job. */
1999
+ readonly polled: number;
1879
2000
  readonly failures: ReadonlyArray<{
1880
2001
  readonly id: string;
1881
2002
  readonly detail: string;
@@ -2245,6 +2366,105 @@ export declare interface McpTransport {
2245
2366
  readonly close: () => Effect.Effect<void>;
2246
2367
  }
2247
2368
 
2369
+ /** One artifact a finished task produced — a URL the dispatcher re-hosts, or
2370
+ * bytes (raw or base64) it stores. Same shape the built-in generators hand
2371
+ * over, so both paths persist identically. */
2372
+ export declare interface MediaTaskArtifact {
2373
+ readonly url?: string;
2374
+ readonly data?: Uint8Array | string;
2375
+ readonly mediaType: string;
2376
+ }
2377
+
2378
+ /**
2379
+ * The provider's handle for one submitted task.
2380
+ *
2381
+ * Stored on the queue row VERBATIM as JSON and handed back to `poll`, so it
2382
+ * may carry whatever the provider needs to find the task again — a request
2383
+ * id, the endpoint it was submitted to, a region. It must survive
2384
+ * `JSON.stringify`: a handle that does not is refused before it is stored,
2385
+ * because a handle that came back different would poll a task that does not
2386
+ * exist.
2387
+ */
2388
+ export declare type MediaTaskHandle = Readonly<Record<string, unknown>>;
2389
+
2390
+ export declare interface MediaTaskProvider {
2391
+ /** Submit the job; return the provider's handle as soon as it has one. */
2392
+ readonly submit: (request: MediaTaskSubmitRequest) => Effect.Effect<MediaTaskHandle, AiError>;
2393
+ /** Ask the provider about one handle. Called once per dispatcher claim. */
2394
+ readonly poll: (handle: MediaTaskHandle, request: MediaTaskSubmitRequest) => Effect.Effect<MediaTaskStatus, AiError>;
2395
+ /**
2396
+ * The provider de-duplicates on `idempotencyKey`: a second `submit` with the
2397
+ * same key returns the SAME task's handle rather than starting a second job.
2398
+ *
2399
+ * This closes the one window the handle cannot: a worker lost between the
2400
+ * provider accepting the job and the handle reaching the row. Without this
2401
+ * flag such a row is resumed with `AiOffloadError.outcome === 'unknown'` —
2402
+ * the job may have run and been charged for, and nothing can find it. With
2403
+ * it, the reclaiming worker submits again under the same key, receives the
2404
+ * same handle, and polls it.
2405
+ *
2406
+ * Say it only when the provider guarantees it. A `true` over a provider that
2407
+ * ignores the key turns the lost-worker case into a second paid job — the
2408
+ * exact outcome the `unknown` report exists to avoid.
2409
+ */
2410
+ readonly idempotentSubmit?: boolean;
2411
+ }
2412
+
2413
+ /** The task provider registered under `name`, or `undefined`. */
2414
+ export declare const mediaTaskProvider: (name: string) => MediaTaskProvider | undefined;
2415
+
2416
+ export declare type MediaTaskStatus = {
2417
+ readonly status: 'pending';
2418
+ /** How long to wait before asking again, when the provider says. Bounded
2419
+ * below by the dispatcher's own poll interval. */
2420
+ readonly retryAfterMs?: number;
2421
+ } | {
2422
+ readonly status: 'succeeded';
2423
+ readonly artifacts: ReadonlyArray<MediaTaskArtifact>;
2424
+ /** What the task consumed, in the provider's unit — seconds of video,
2425
+ * characters of text, or tokens. Recorded in the usage ledger under
2426
+ * the job's row and attempt. */
2427
+ readonly usage?: MediaUsage | MeteredUsage;
2428
+ readonly providerMetadata?: unknown;
2429
+ } | {
2430
+ readonly status: 'failed';
2431
+ /** The provider's own message. It is what the parked run is told. */
2432
+ readonly message: string;
2433
+ /**
2434
+ * Whether a fresh submission could succeed. Default `false`: a task the
2435
+ * provider ran and failed is a paid job, and re-submitting it is a second
2436
+ * charge, not a retry. Say `true` for a transient failure the provider
2437
+ * itself names as such (capacity, an upstream outage) — then the job is
2438
+ * re-submitted under the next attempt, with the dispatcher's backoff.
2439
+ */
2440
+ readonly retryable?: boolean;
2441
+ };
2442
+
2443
+ /**
2444
+ * What the dispatcher hands `submit`.
2445
+ *
2446
+ * `idempotencyKey` is derived from the queue row and the attempt — the same
2447
+ * two things the artifacts are stored under — so it is stable across a lost
2448
+ * worker and different for a deliberate re-submission. Forward it to the
2449
+ * provider when the provider takes one: see `MediaTaskProvider.idempotentSubmit`
2450
+ * for what that buys.
2451
+ */
2452
+ export declare interface MediaTaskSubmitRequest {
2453
+ readonly modality: 'image' | 'video' | 'speech';
2454
+ readonly prompt: string;
2455
+ readonly model?: string;
2456
+ /** The job's `params`, as declared on `mediaStep`. */
2457
+ readonly params: Readonly<Record<string, unknown>>;
2458
+ /** URL inputs, as declared on `mediaStep`. Never bytes. */
2459
+ readonly inputs: Readonly<Record<string, unknown>>;
2460
+ readonly providerOptions?: AiProviderOptions;
2461
+ readonly tenantId: string | null;
2462
+ /** `<queue row id>#<attempt>`. */
2463
+ readonly idempotencyKey: string;
2464
+ /** Aborted on Effect interruption; pass to provider I/O. */
2465
+ readonly abortSignal?: AbortSignal;
2466
+ }
2467
+
2248
2468
  /** Token tally, mirroring `GenerateTextResult['usage']`. Only `generateImage`
2249
2469
  * reports one (the speech + video model specs carry no usage), which is why
2250
2470
  * it's optional on every media result. */
@@ -2425,7 +2645,7 @@ export declare interface ModelDataPolicy {
2425
2645
  readonly noTraining: PolicyCoverage;
2426
2646
  }
2427
2647
 
2428
- export declare type ModelModality = 'language' | 'embedding' | 'image' | 'video' | 'realtime' | 'reranking' | 'speech' | 'transcription' | 'unknown';
2648
+ export declare type ModelModality = 'language' | 'embedding' | 'image' | 'video' | 'realtime' | 'reranking' | 'speech' | 'transcription' | 'evaluation' | 'unknown';
2429
2649
 
2430
2650
  export declare interface ModelPrice {
2431
2651
  /** USD per 1,000,000 input (prompt) tokens. */
@@ -2739,6 +2959,9 @@ export declare interface QueuedInference {
2739
2959
  readonly transcription: Record<string, unknown> | null;
2740
2960
  /** The media job for a `media` call. */
2741
2961
  readonly media: QueuedMediaJob | null;
2962
+ /** The stored provider handle of a task-provider media job, or `null`. */
2963
+ readonly taskHandle: Record<string, unknown> | null;
2964
+ readonly taskSubmittedAt: Date | null;
2742
2965
  readonly attempts: number;
2743
2966
  readonly fence: number;
2744
2967
  }
@@ -2748,6 +2971,8 @@ export declare interface QueuedMediaJob {
2748
2971
  readonly modality: 'image' | 'video' | 'speech';
2749
2972
  readonly params: Record<string, unknown>;
2750
2973
  readonly inputs: Record<string, unknown>;
2974
+ /** Owner, visibility and tags for the stored artifacts — see `PluginMediaPersistMetadata`. */
2975
+ readonly persist?: PluginMediaPersistMetadata;
2751
2976
  }
2752
2977
 
2753
2978
  /** What a media job resumes its run with: hosted artifacts, never bytes. */
@@ -2816,9 +3041,122 @@ export declare interface ReadRecorder<S extends QueryableStore> {
2816
3041
  reset(): void;
2817
3042
  }
2818
3043
 
3044
+ /** Browser-safe input allowlist. Session configuration and provider credentials
3045
+ * belong to the server, never to events received from the browser. */
3046
+ export declare const RealtimeClientEvent: Schema.Union<[Schema.Struct<{
3047
+ type: Schema.Literal<["input-audio-append"]>;
3048
+ audio: Schema.filter<typeof Schema.String>;
3049
+ }>, Schema.Struct<{
3050
+ type: Schema.Literal<["input-audio-commit", "input-audio-clear", "response-create", "response-cancel"]>;
3051
+ }>, Schema.Struct<{
3052
+ type: Schema.Literal<["conversation-item-create"]>;
3053
+ item: Schema.Struct<{
3054
+ type: Schema.Literal<["text-message"]>;
3055
+ role: Schema.Literal<["user"]>;
3056
+ text: typeof Schema.String;
3057
+ }>;
3058
+ }>, Schema.Struct<{
3059
+ type: Schema.Literal<["conversation-item-truncate"]>;
3060
+ itemId: typeof Schema.String;
3061
+ contentIndex: Schema.filter<Schema.filter<Schema.filter<typeof Schema.Number>>>;
3062
+ audioEndMs: Schema.filter<Schema.filter<typeof Schema.Number>>;
3063
+ }>]>;
3064
+
3065
+ export declare type RealtimeClientEvent = typeof RealtimeClientEvent.Type;
3066
+
3067
+ /** Safe, normalized server events. No raw provider payload, client secret,
3068
+ * arbitrary conversation item or provider error message crosses this surface. */
3069
+ export declare const RealtimeServerEvent: Schema.Union<[Schema.Struct<{
3070
+ type: Schema.Literal<["ready"]>;
3071
+ }>, Schema.Struct<{
3072
+ type: Schema.Literal<["session-created"]>;
3073
+ sessionId: Schema.optional<typeof Schema.String>;
3074
+ }>, Schema.Struct<{
3075
+ type: Schema.Literal<["session-updated"]>;
3076
+ }>, Schema.Struct<{
3077
+ type: Schema.Literal<["speech-started", "speech-stopped"]>;
3078
+ itemId: Schema.optional<typeof Schema.String>;
3079
+ }>, Schema.Struct<{
3080
+ type: Schema.Literal<["audio-committed"]>;
3081
+ itemId: Schema.optional<typeof Schema.String>;
3082
+ previousItemId: Schema.optional<typeof Schema.String>;
3083
+ }>, Schema.Struct<{
3084
+ itemId: typeof Schema.String;
3085
+ type: Schema.Literal<["conversation-item-added"]>;
3086
+ }>, Schema.Struct<{
3087
+ transcript: typeof Schema.String;
3088
+ itemId: typeof Schema.String;
3089
+ type: Schema.Literal<["input-transcription-completed"]>;
3090
+ }>, Schema.Struct<{
3091
+ type: Schema.Literal<["response-created"]>;
3092
+ responseId: typeof Schema.String;
3093
+ }>, Schema.Struct<{
3094
+ type: Schema.Literal<["response-done"]>;
3095
+ responseId: typeof Schema.String;
3096
+ status: typeof Schema.String;
3097
+ }>, Schema.Struct<{
3098
+ itemId: typeof Schema.String;
3099
+ responseId: typeof Schema.String;
3100
+ type: Schema.Literal<["output-item-added", "output-item-done", "content-part-added", "content-part-done", "audio-done"]>;
3101
+ }>, Schema.Struct<{
3102
+ delta: Schema.filter<typeof Schema.String>;
3103
+ itemId: typeof Schema.String;
3104
+ responseId: typeof Schema.String;
3105
+ type: Schema.Literal<["audio-delta"]>;
3106
+ }>, Schema.Struct<{
3107
+ delta: typeof Schema.String;
3108
+ itemId: typeof Schema.String;
3109
+ responseId: typeof Schema.String;
3110
+ type: Schema.Literal<["text-delta", "audio-transcript-delta"]>;
3111
+ }>, Schema.Struct<{
3112
+ text: Schema.optional<typeof Schema.String>;
3113
+ itemId: typeof Schema.String;
3114
+ responseId: typeof Schema.String;
3115
+ type: Schema.Literal<["text-done"]>;
3116
+ }>, Schema.Struct<{
3117
+ transcript: Schema.optional<typeof Schema.String>;
3118
+ itemId: typeof Schema.String;
3119
+ responseId: typeof Schema.String;
3120
+ type: Schema.Literal<["audio-transcript-done"]>;
3121
+ }>]>;
3122
+
3123
+ export declare type RealtimeServerEvent = typeof RealtimeServerEvent.Type;
3124
+
3125
+ export declare interface RealtimeSessionOptions {
3126
+ readonly path: `/${string}`;
3127
+ readonly model: Experimental_RealtimeModelV4;
3128
+ /** Runs before minting/opening. Authentication alone does not authorize AI spend. */
3129
+ readonly authorize: (subject: Subject, model: {
3130
+ readonly provider: string;
3131
+ readonly modelId: string;
3132
+ }) => Effect.Effect<boolean, unknown>;
3133
+ /** Server-owned configuration. Tool execution is not supported by this relay. */
3134
+ readonly sessionConfig: Omit<Experimental_RealtimeModelV4SessionConfig, 'tools'>;
3135
+ readonly limits?: {
3136
+ readonly maxMessageBytes?: number;
3137
+ readonly maxPendingBytes?: number;
3138
+ readonly maxPendingMessages?: number;
3139
+ readonly setupTimeoutMs?: number;
3140
+ readonly maxDurationMs?: number;
3141
+ };
3142
+ }
3143
+
2819
3144
  export declare const recordAiUsage: (store: DataStore, record: AiUsageRecordInput) => Effect.Effect<CostBreakdown & {
2820
3145
  readonly receipt: AiUsageReceipt;
2821
- }>;
3146
+ }, AiUsageReceiptConflict | AiUsageLedgerError>;
3147
+
3148
+ /**
3149
+ * `recordAiUsage` for a call that has ALREADY been paid for, where failing the
3150
+ * caller would lose a finished answer over a ledger row. Never fails — and never
3151
+ * silent: a ledger failure or a receipt conflict is logged at error level with
3152
+ * everything needed to reconcile it by hand.
3153
+ *
3154
+ * The framework's three callers each swallowed the failure outright
3155
+ * (`.catch(() => {})`, `catchAllCause(() => Effect.void)` twice). "Do not fail
3156
+ * the call" was right; "leave no trace" was not — an unbooked charge that
3157
+ * nothing reports is found only when the invoice disagrees with the ledger.
3158
+ */
3159
+ export declare const recordAiUsageBestEffort: (store: DataStore, record: AiUsageRecordInput) => Effect.Effect<void>;
2822
3160
 
2823
3161
  /**
2824
3162
  * Register a prompt version, idempotently, and return it.
@@ -2876,6 +3214,18 @@ export declare const redisResumableStreamStore: (redis: ResumableRedis, opts?: {
2876
3214
  */
2877
3215
  export declare const refreshMcpToolset: (server: McpServer, policy: McpToolPolicy) => Effect.Effect<McpToolset, McpError>;
2878
3216
 
3217
+ /**
3218
+ * The provider config `mediaStep` accepts for a registered task provider —
3219
+ * named, not enumerated, exactly like `RegisteredSpeechConfig`.
3220
+ */
3221
+ export declare interface RegisteredMediaTaskConfig {
3222
+ readonly name: string;
3223
+ readonly model?: string;
3224
+ }
3225
+
3226
+ /** Every registered name. */
3227
+ export declare const registeredMediaTaskProviders: () => ReadonlyArray<string>;
3228
+
2879
3229
  /**
2880
3230
  * Synthesize speech audio from text. Returns the audio bytes + media type and
2881
3231
  * provider metadata; provider failures surface as `AiError`.
@@ -2898,6 +3248,29 @@ export declare interface RegisteredSpeechConfig {
2898
3248
  /** Every registered name — what a picker asks for. */
2899
3249
  export declare const registeredSpeechProviders: () => ReadonlyArray<string>;
2900
3250
 
3251
+ /**
3252
+ * Register an external-task media provider under a name.
3253
+ *
3254
+ * The name then works in `mediaStep({ provider: { name, model } })`: the
3255
+ * dispatcher submits through the provider, keeps the handle on the queue row,
3256
+ * polls it once per claim, and resumes the run with the hosted artifacts.
3257
+ * Taking over a built-in name needs `{ replaceBuiltin: true }`.
3258
+ */
3259
+ export declare const registerMediaTaskProvider: (name: string, provider: MediaTaskProvider, options?: RegisterMediaTaskProviderOptions) => void;
3260
+
3261
+ export declare interface RegisterMediaTaskProviderOptions {
3262
+ /**
3263
+ * Take over a BUILT-IN name (`openai`, `gateway`, `anthropic`, `mock`).
3264
+ *
3265
+ * A registration under a built-in name routes every `mediaStep` that names
3266
+ * it through the task contract instead of the generator. Refused unless the
3267
+ * intent is in the call, for the reason the speech registry gives: a silent
3268
+ * shadow makes the same config mean different things depending on which
3269
+ * module loaded first.
3270
+ */
3271
+ readonly replaceBuiltin?: boolean;
3272
+ }
3273
+
2901
3274
  /**
2902
3275
  * Register a speech provider under a name.
2903
3276
  *
@@ -2942,7 +3315,7 @@ export declare const registerStream: (streamId: string) => AbortController;
2942
3315
  export declare const releaseAiBudget: (store: DataStore, opts: {
2943
3316
  readonly reservationId: string;
2944
3317
  readonly tenantId: string | null;
2945
- }) => Effect.Effect<"released" | "already-released" | "not-found">;
3318
+ }) => Effect.Effect<"released" | "already-released" | "not-found", AiBudgetInvalidInput>;
2946
3319
 
2947
3320
  export declare const releasedAtOf: (raw: RawGatewayModel) => string | undefined;
2948
3321
 
@@ -2962,7 +3335,8 @@ export declare interface RenderedPrompt extends PromptStamp {
2962
3335
  export declare const replacesBuiltinSpeechProvider: (name: string) => boolean;
2963
3336
 
2964
3337
  /** Reserve before provider I/O. Reuse reservationId only for the same logical
2965
- * spend, tenant, amount and window. Zero is check-only and returns null.
3338
+ * spend, tenant, amount and window. Zero checks for strictly positive headroom
3339
+ * and returns null; it never creates a free reservation.
2966
3340
  * Call outside another transaction; the receipt and counter need one commit.
2967
3341
  * A provider failure does not automatically release possibly billed spend. */
2968
3342
  export declare const requireAiBudget: (store: DataStore, opts: {
@@ -2971,7 +3345,7 @@ export declare const requireAiBudget: (store: DataStore, opts: {
2971
3345
  readonly addUsd?: number;
2972
3346
  readonly since?: Date;
2973
3347
  readonly reservationId?: string;
2974
- }) => Effect.Effect<AiBudgetReservation | null, AiBudgetExceeded>;
3348
+ }) => Effect.Effect<AiBudgetReservation | null, AiBudgetError>;
2975
3349
 
2976
3350
  /**
2977
3351
  * Rerank `documents` by relevance to `query`, highest score first. Returns
@@ -3037,6 +3411,9 @@ export declare const resolveMcpBounds: (overrides: Partial<McpBounds> | undefine
3037
3411
 
3038
3412
  export declare const resolveModel: (config: ProviderConfig, middleware?: AiMiddleware | ReadonlyArray<AiMiddleware>) => LanguageModelV4;
3039
3413
 
3414
+ /** Resolve supported server-owned provider configuration to the SDK codec. */
3415
+ export declare const resolveRealtimeModel: (config: ProviderConfig) => Experimental_RealtimeModelV4;
3416
+
3040
3417
  export declare const resolveSpeechModel: (config: ProviderConfig) => SpeechModelV4;
3041
3418
 
3042
3419
  /** Resolve the system prompt for a given decoded input. */
@@ -3678,6 +4055,20 @@ export declare const streamTextWithFallback: (options: StreamTextOptions) => Str
3678
4055
  */
3679
4056
  export declare const streamTextWithRetry: (options: StreamTextOptions, retry?: StreamRetryOptions) => Stream.Stream<AgentEvent>;
3680
4057
 
4058
+ /** Live partial transcription, not container decoding or a durable workflow journal.
4059
+ * No automatic retry: replaying audio can duplicate transcript segments and charges.
4060
+ * Raw provider chunks, headers and private metadata are never emitted. */
4061
+ export declare const streamTranscribe: (options: StreamTranscribeOptions) => Stream.Stream<TranscriptionStreamEvent, TranscriptionStreamError>;
4062
+
4063
+ export declare interface StreamTranscribeOptions {
4064
+ /** Single-use raw audio source. Scope interruption cancels it and the provider. */
4065
+ readonly audio: ReadableStream<Uint8Array>;
4066
+ readonly inputAudioFormat: TranscriptionAudioFormat;
4067
+ readonly provider?: ProviderConfig;
4068
+ readonly providerOptions?: AiProviderOptions;
4069
+ readonly signal?: AbortSignal;
4070
+ }
4071
+
3681
4072
  /** A synthesized tool's metadata (the discovery/inventory view). */
3682
4073
  export declare interface SynthesizedTool {
3683
4074
  readonly name: string;
@@ -3738,7 +4129,7 @@ export declare interface ToolContext {
3738
4129
  readonly runtime?: Runtime.Runtime<unknown>;
3739
4130
  /** SDK tool-call identity for this execution, not a durable application idempotency key. */
3740
4131
  readonly invocationId?: string;
3741
- /** The SDK's cancellation signal; Promise handlers must pass it to their I/O. */
4132
+ /** Cancellation from either the caller's turn or SDK invocation; Promise handlers must pass it to their I/O. */
3742
4133
  readonly abortSignal?: AbortSignal;
3743
4134
  }
3744
4135
 
@@ -3818,6 +4209,13 @@ export declare interface TranscribeOptions {
3818
4209
  readonly providerOptions?: AiProviderOptions;
3819
4210
  }
3820
4211
 
4212
+ /** Raw audio only. Container files must be decoded before entering this stream. */
4213
+ export declare interface TranscriptionAudioFormat {
4214
+ readonly type: 'audio/pcm' | 'audio/pcmu' | 'audio/pcma';
4215
+ /** Required for PCM; other formats may declare their provider-supported rate. */
4216
+ readonly rate?: number;
4217
+ }
4218
+
3821
4219
  export declare interface TranscriptionResult {
3822
4220
  readonly text: string;
3823
4221
  readonly language?: string;
@@ -3868,6 +4266,50 @@ export declare type TranscriptionSource =
3868
4266
  readonly mediaType: string;
3869
4267
  };
3870
4268
 
4269
+ /** Browser-safe error contract for a declared transcription stream. */
4270
+ export declare class TranscriptionStreamError extends TranscriptionStreamError_base {
4271
+ }
4272
+
4273
+ declare const TranscriptionStreamError_base: Schema.TaggedErrorClass<TranscriptionStreamError, "TranscriptionStreamError", {
4274
+ readonly _tag: Schema.tag<"TranscriptionStreamError">;
4275
+ } & {
4276
+ reason: Schema.Literal<["input", "unsupported", "provider", "cancelled"]>;
4277
+ message: typeof Schema.String;
4278
+ }>;
4279
+
4280
+ /** Partial text is replaceable, delta text is append-only, and final text seals a segment. */
4281
+ export declare const TranscriptionStreamEvent: Schema.Union<[Schema.Struct<{
4282
+ delta: typeof Schema.String;
4283
+ id: Schema.optional<typeof Schema.String>;
4284
+ type: Schema.Literal<["transcript-delta"]>;
4285
+ }>, Schema.Struct<{
4286
+ text: typeof Schema.String;
4287
+ startSecond: Schema.optional<typeof Schema.Number>;
4288
+ durationInSeconds: Schema.optional<typeof Schema.Number>;
4289
+ channelIndex: Schema.optional<typeof Schema.Number>;
4290
+ id: Schema.optional<typeof Schema.String>;
4291
+ type: Schema.Literal<["transcript-partial"]>;
4292
+ }>, Schema.Struct<{
4293
+ text: typeof Schema.String;
4294
+ startSecond: Schema.optional<typeof Schema.Number>;
4295
+ endSecond: Schema.optional<typeof Schema.Number>;
4296
+ channelIndex: Schema.optional<typeof Schema.Number>;
4297
+ id: Schema.optional<typeof Schema.String>;
4298
+ type: Schema.Literal<["transcript-final"]>;
4299
+ }>, Schema.Struct<{
4300
+ type: Schema.Literal<["finish"]>;
4301
+ text: typeof Schema.String;
4302
+ segments: Schema.Array$<Schema.Struct<{
4303
+ text: typeof Schema.String;
4304
+ start: typeof Schema.Number;
4305
+ end: typeof Schema.Number;
4306
+ }>>;
4307
+ language: Schema.optional<typeof Schema.String>;
4308
+ durationSec: Schema.optional<typeof Schema.Number>;
4309
+ }>]>;
4310
+
4311
+ export declare type TranscriptionStreamEvent = typeof TranscriptionStreamEvent.Type;
4312
+
3871
4313
  /** How much timestamp detail to ask for. Providers charge for it. */
3872
4314
  export declare type TranscriptionTimestamps = 'none' | 'segment' | 'word';
3873
4315