vern-llm 2.4.1 → 2.5.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.cts CHANGED
@@ -7,7 +7,7 @@ type LLMErrorType = 'timeout' | 'api' | 'network' | 'parse' | 'validation' | 'in
7
7
  * e.g. `authentication`/`authorization` apply the same way regardless of
8
8
  * which type wraps them.
9
9
  */
10
- type LLMErrorCode = 'unknown_tool' | 'duplicate_tool_call_id' | 'tool_choice_none_violated' | 'unexpected_tool_calls' | 'unsupported_capability' | 'duplicate_tool_names' | 'unknown_tool_choice' | 'duplicate_tool_result_ids' | 'unknown_tool_result_ids' | 'missing_tool_results' | 'rate_limit_queue_full' | 'rate_limit_queue_timeout' | 'rate_limit_capacity_exceeded' | 'provider_rate_limited' | 'request_timeout' | 'idle_timeout' | 'authentication' | 'authorization' | 'not_found' | 'payload_too_large' | 'server_error' | 'empty_response' | 'connection_failed' | 'circuit_cooling_down' | 'circuit_trial_in_flight' | 'fallback_exhausted' | 'tool_arguments_parse_failed' | 'stream_frame_invalid';
10
+ type LLMErrorCode = 'unknown_tool' | 'duplicate_tool_call_id' | 'tool_choice_none_violated' | 'unexpected_tool_calls' | 'unsupported_capability' | 'duplicate_tool_names' | 'unknown_tool_choice' | 'duplicate_tool_result_ids' | 'unknown_tool_result_ids' | 'missing_tool_results' | 'middleware_threw' | 'rate_limit_queue_full' | 'rate_limit_queue_timeout' | 'rate_limit_capacity_exceeded' | 'provider_rate_limited' | 'request_timeout' | 'idle_timeout' | 'middleware_timeout' | 'deadline_exceeded' | 'authentication' | 'authorization' | 'not_found' | 'payload_too_large' | 'server_error' | 'empty_response' | 'connection_failed' | 'circuit_cooling_down' | 'circuit_trial_in_flight' | 'fallback_exhausted' | 'tool_arguments_parse_failed' | 'stream_frame_invalid';
11
11
  /**
12
12
  * Tool contract codes: a model or provider response defect, not a
13
13
  * transient provider fault. Deterministic on the wire request, so
@@ -187,9 +187,10 @@ declare class LLMError extends Error {
187
187
  * `nonRetryableStatus` list. False for `parse`/`validation`/
188
188
  * `invalid_params`/`aborted` types (the caller's own input, the model's
189
189
  * own response, or intentional cancellation, none of which are the
190
- * provider being unhealthy), the tool contract codes, and the local
191
- * rate limit codes. Subclasses (see `FallbackExhaustedError`) may
192
- * override this when `type` alone carries no retry signal.
190
+ * provider being unhealthy), the tool contract codes, the local
191
+ * rate limit codes, and the middleware timeout code.
192
+ * Subclasses (see `FallbackExhaustedError`) may override this when `type`
193
+ * alone carries no retry signal.
193
194
  */
194
195
  get retryable(): boolean;
195
196
  /**
@@ -303,8 +304,303 @@ declare class TieredCacheAdapter<T = unknown> implements CacheAdapter<T> {
303
304
  }
304
305
 
305
306
  //#endregion
306
- //#region src/circuitBreaker.d.ts
307
+ //#region src/types/events.d.ts
307
308
  //# sourceMappingURL=cache.d.ts.map
309
+ /**
310
+ * Reports what happened during a call. Fire and forget, mirroring
311
+ * `onUsage`: the return value is never read and a throwing handler cannot
312
+ * change what the call does, only what gets reported about it.
313
+ */
314
+ type VernLLMEvent = {
315
+ kind: 'retry';
316
+ requestId: string;
317
+ provider: string;
318
+ /** The model actually resolved for this call (honors a per-call `model` override). */
319
+ model: string;
320
+ /** The 1-based retry ordinal (the 1st retry is `1`, not the overall attempt count). */
321
+ attempt: number;
322
+ maxRetries: number;
323
+ delayMs: number;
324
+ retryAfterHonored: boolean;
325
+ error: LLMError;
326
+ } | {
327
+ kind: 'circuit_state';
328
+ provider: string;
329
+ /**
330
+ * The model of the call that triggered this specific transition
331
+ * (whatever was passed to the `assertClosed`/`recordSuccess`/
332
+ * `recordFailure` call that caused it), not a property of the
333
+ * circuit itself: the breaker still counts failures across every
334
+ * model together, so a threshold crossing can be the sum of
335
+ * several different models' failures even though only the
336
+ * triggering call's `model` is reported here.
337
+ */
338
+ model: string;
339
+ from: CircuitState;
340
+ to: CircuitState;
341
+ consecutiveFailures: number;
342
+ } | {
343
+ kind: 'fallback';
344
+ requestId: string;
345
+ /** Provider name of the target that just failed. */
346
+ from: string;
347
+ /** Provider name of the target about to be tried next. */
348
+ to: string;
349
+ /** `-1` for the primary target, otherwise the index into `fallback`. */
350
+ fromIndex: number;
351
+ toIndex: number;
352
+ /** The normalized error that caused `from` to be abandoned. */
353
+ error: LLMError;
354
+ /** Time spent on `from`, including its own retries, before giving up. */
355
+ elapsedMs: number;
356
+ } | {
357
+ kind: 'rate_limited';
358
+ requestId: string;
359
+ provider: string;
360
+ /** The model actually resolved for this call (honors a per-call `model` override). */
361
+ model: string;
362
+ /** How long this attempt sat queued for capacity before it was let through. */
363
+ waitedMs: number;
364
+ /** Which configured bucket was blocking this attempt just before it cleared. */
365
+ reason: 'concurrency' | 'rpm' | 'tpm';
366
+ } | {
367
+ kind: 'middleware';
368
+ requestId: string;
369
+ /** This middleware's `name`, or its array position if unnamed. */
370
+ middleware: string;
371
+ hook: 'transform' | 'wrap_short_circuit' | 'enabled_skip';
372
+ /** For `hook: 'transform'` only: which top-level fields the merged patch touched. */
373
+ patchedFields?: string[];
374
+ };
375
+ type OnEvent = (event: VernLLMEvent) => void;
376
+
377
+ //#endregion
378
+ //#region src/types/middleware.d.ts
379
+ //# sourceMappingURL=events.d.ts.map
380
+ /** Capabilities of the target a middleware hook is currently looking at. */
381
+ interface MiddlewareCapabilities {
382
+ /**
383
+ * Whether this target honors `response_format: { type: 'json_object' }`
384
+ * as a real constraint. Mirrors `LLMClient.supportsJsonObjectMode`.
385
+ * `false` for `fromAnthropic` and `fromBedrock`.
386
+ */
387
+ supportsJsonObjectMode: boolean;
388
+ }
389
+ /**
390
+ * A typed reference to one slot in `ctx.state`. Create one with
391
+ * `createStateKey`, export it, and import the same reference wherever
392
+ * another middleware needs to read or write the same value. There's no
393
+ * string key anywhere in this path, so a typo becomes a missing import
394
+ * or an undefined variable, a compile error, instead of a silently
395
+ * created new property.
396
+ */
397
+ interface MiddlewareStateKey<T> {
398
+ readonly debugName: string;
399
+ /**
400
+ * Never set at runtime; exists purely so `T` is actually used
401
+ * somewhere in this interface's shape (a phantom type), which is what
402
+ * lets `MiddlewareStateBag.get`/`set` infer the right type for a given
403
+ * key instead of two `MiddlewareStateKey<string>` and
404
+ * `MiddlewareStateKey<number>` keys being structurally identical.
405
+ */
406
+ readonly __phantom?: T;
407
+ }
408
+ /** Creates a new, distinct `MiddlewareStateKey`. `debugName` is used only in log lines and the `'middleware'` event; it never affects equality. */
409
+ declare function createStateKey<T>(debugName: string): MiddlewareStateKey<T>;
410
+ /**
411
+ * Typed, per-logical-call storage two middleware can deliberately share a
412
+ * value through (a span ID one sets, another reads). Backed by a plain
413
+ * `Map` internally, created once per logical call and never read or
414
+ * written by VernLLM itself.
415
+ */
416
+ interface MiddlewareStateBag {
417
+ get<T>(key: MiddlewareStateKey<T>): T | undefined;
418
+ set<T>(key: MiddlewareStateKey<T>, value: T): void;
419
+ }
420
+ /** A plain, `Map`-backed `MiddlewareStateBag`. */
421
+ declare function createMiddlewareStateBag(): MiddlewareStateBag;
422
+ /** Fields every `MiddlewareContext` variant carries, regardless of `stage`. */
423
+ interface MiddlewareContextBase {
424
+ requestId: string;
425
+ /** Capabilities of the target this stage's identity fields describe. */
426
+ capabilities: MiddlewareCapabilities;
427
+ signal?: AbortSignal;
428
+ /** Shared, collision-proof state for two middleware to deliberately coordinate through. See `MiddlewareStateBag`. */
429
+ state: MiddlewareStateBag;
430
+ /** Simple, string-keyed scratch space, pre-namespaced to this one middleware so two middleware can never collide here even by accident. */
431
+ own: Record<string, unknown>;
432
+ }
433
+ /**
434
+ * The `ctx` `transform` receives, and every attempt-scoped event context
435
+ * (`'retry'`, `'fallback'`, `'circuit_state'`, `'middleware'`). Built once
436
+ * a specific target has actually been selected for this attempt, so every
437
+ * field describes the real target, not a placeholder.
438
+ */
439
+ interface AttemptContext extends MiddlewareContextBase {
440
+ stage: 'attempt';
441
+ /** The target this attempt is actually dispatched to. */
442
+ requestedProvider: string;
443
+ requestedModel: string;
444
+ isFallbackAttempt: boolean;
445
+ /**
446
+ * The real, current attempt number for this dispatch.
447
+ *
448
+ * Exception: on a `'circuit_state'` event triggered by a pre-dispatch
449
+ * check (`assertClosed`, before any attempt has been made), this is
450
+ * `1` regardless of which attempt is about to run, since no attempt
451
+ * exists yet to report. Every other `'circuit_state'` event, and every
452
+ * other attempt-scoped event, reports the real attempt number.
453
+ */
454
+ attempt: number;
455
+ }
456
+ /**
457
+ * The `ctx` `wrap` receives before `next()` resolves (and `onError`'s own
458
+ * `ctx`, built the same way under the hood). Built once, before any
459
+ * fallback target is chosen, so it only ever describes the primary
460
+ * target. There is no real "requested" target yet, and no attempt count,
461
+ * fallback flag, or per-attempt capability to report. Read `next()`'s
462
+ * resolved `CallResult.meta` once you need to know what actually
463
+ * happened.
464
+ */
465
+ interface PreDispatchContext extends MiddlewareContextBase {
466
+ stage: 'pre-dispatch';
467
+ /** The primary target only, not necessarily who ends up answering. */
468
+ primaryProvider: string;
469
+ primaryModel: string;
470
+ }
471
+ /**
472
+ * `enabled` and `onEvent` are called from both stages (gating/observing
473
+ * `transform` as well as `wrap`), so they receive this union and must
474
+ * narrow on `ctx.stage` before reading stage-specific fields.
475
+ * `transform` and `wrap` themselves receive the single variant that's
476
+ * always accurate for them (`AttemptContext`/`PreDispatchContext`
477
+ * respectively). See `VernLLMMiddleware`.
478
+ */
479
+ type MiddlewareContext = AttemptContext | PreDispatchContext;
480
+ /** The `response_format` shape `RequestBuilder` can put on the wire. */
481
+ type WireResponseFormat = {
482
+ type: 'json_object';
483
+ } | {
484
+ type: 'json_schema';
485
+ json_schema: {
486
+ name: string;
487
+ schema: Record<string, unknown>;
488
+ strict?: boolean;
489
+ description?: string;
490
+ };
491
+ };
492
+ /** A tool as it appears on the wire, OpenAI's `function`-wrapped shape. */
493
+ interface WireTool {
494
+ type: 'function';
495
+ function: {
496
+ name: string;
497
+ description: string;
498
+ parameters: Record<string, unknown>;
499
+ };
500
+ }
501
+ /**
502
+ * The wire-shaped request `RequestBuilder.build()` produces for one call
503
+ * attempt, before dispatch. Read only inside `transform`; return a patch
504
+ * of the fields you want to change instead of the whole object.
505
+ */
506
+ interface WireCallRequest {
507
+ model: string;
508
+ temperature?: number;
509
+ max_tokens: number;
510
+ response_format?: WireResponseFormat;
511
+ reasoning_effort?: 'minimal' | 'low' | 'medium' | 'high';
512
+ budget_tokens?: number;
513
+ tools?: WireTool[];
514
+ tool_choice?: WireToolChoice;
515
+ messages: WireMessage[];
516
+ }
517
+ /**
518
+ * What `transform` returns: a patch merged onto the request that
519
+ * `RequestBuilder.build()` (plus every earlier middleware's own patch)
520
+ * already produced, not a replacement for it. `model` and
521
+ * `response_format` can't be expressed here at all, since everything
522
+ * downstream that attributes a call to a target keys off the values
523
+ * `RequestBuilder` already resolved for those two fields, not off
524
+ * whatever ends up on the wire request. `messages`/`tools` are joined by
525
+ * a separate `add*` field, appended rather than replaced, so two
526
+ * independently written middleware can each add to the list without one
527
+ * silently clobbering what the other already added.
528
+ */
529
+ interface WireCallRequestPatch {
530
+ temperature?: number;
531
+ max_tokens?: number;
532
+ reasoning_effort?: 'minimal' | 'low' | 'medium' | 'high';
533
+ budget_tokens?: number;
534
+ tool_choice?: WireToolChoice;
535
+ /** Replaces the whole message list. Prefer `addMessages` unless a full replace is genuinely the intent. */
536
+ messages?: WireMessage[];
537
+ /** Appended after whatever earlier middleware already added. Never clobbers a prior addition. */
538
+ addMessages?: WireMessage[];
539
+ /** Replaces the whole tool list. Prefer `addTools`, same reasoning as `messages`/`addMessages`. */
540
+ tools?: WireTool[];
541
+ /** Appended after whatever earlier middleware already added. Never clobbers a prior addition. */
542
+ addTools?: WireTool[];
543
+ }
544
+ /**
545
+ * The settled outcome of one logical call, passed to `wrap`'s `next()`.
546
+ * `meta` is populated once a target has actually answered, for both
547
+ * streaming and non-streaming calls (`undefined` only on a cache hit,
548
+ * where nothing was actually spent).
549
+ */
550
+ interface CallResult<T = unknown> {
551
+ value: T;
552
+ meta?: CallMeta;
553
+ }
554
+ /**
555
+ * One entry in `VernLLMOptions.middleware`. All four hooks are optional;
556
+ * an entry that sets none of them is inert. See the middleware docs for
557
+ * how `transform`, `wrap`, `onEvent`, and `enabled` compose across
558
+ * several entries.
559
+ */
560
+ interface VernLLMMiddleware {
561
+ /** Used in log lines and the `'middleware'` event. Defaults to this entry's array position when omitted. */
562
+ name?: string;
563
+ /** Sort key for composition order, ascending, ties broken by array order. See the middleware docs for what "lower runs first" means for `wrap`. */
564
+ priority?: number;
565
+ /**
566
+ * Boolean for a static on/off switch, or a predicate evaluated per
567
+ * call. A throwing, rejecting, or timed-out predicate is logged and
568
+ * treated as `false` for that call.
569
+ */
570
+ enabled?: boolean | ((ctx: MiddlewareContext) => boolean | Promise<boolean>);
571
+ /** Per-middleware override of the instance-level `middlewareTimeoutMs`, applied to this entry's `transform` and function `enabled`. `<= 0` means unbounded (no timer at all). */
572
+ timeoutMs?: number;
573
+ /** Transforms the outgoing wire request for one attempt. Runs once per attempt, including retries. `ctx` is always accurate to the real target for this attempt. */
574
+ transform?: (request: Readonly<WireCallRequest>, ctx: AttemptContext) => WireCallRequestPatch | Promise<WireCallRequestPatch>;
575
+ /**
576
+ * Wraps one whole logical call, exactly once, regardless of how many
577
+ * retries or fallback targets ran underneath it. `ctx` is built once,
578
+ * before any fallback target is chosen, so it only describes the
579
+ * primary target. There is no `requestedProvider`/`isFallbackAttempt`/
580
+ * `attempt` to read here. Read `next()`'s resolved `CallResult.meta`
581
+ * for what actually happened.
582
+ */
583
+ wrap?: (request: Readonly<WireCallRequest>, next: () => Promise<CallResult>, ctx: PreDispatchContext) => Promise<CallResult>;
584
+ /** Observes the same events reported on `VernLLMOptions.onEvent`, filtered by this middleware's own `enabled`. Called from both stages; narrow on `ctx.stage` before reading stage-specific fields. */
585
+ onEvent?: (event: VernLLMEvent, ctx: MiddlewareContext) => void;
586
+ }
587
+
588
+ //#endregion
589
+ //#region src/circuitBreaker.d.ts
590
+ //# sourceMappingURL=middleware.d.ts.map
591
+ /** The call this mutation happened as part of, forwarded to `onStateChange` untouched. `CircuitBreaker` never inspects it. */
592
+ interface CircuitBreakerCallContext {
593
+ requestId: string;
594
+ state: MiddlewareStateBag;
595
+ signal?: AbortSignal;
596
+ /**
597
+ * The real, current attempt number for this dispatch, when the call
598
+ * site actually has one in scope (i.e. after a dispatch was made or
599
+ * failed). Omitted for calls that happen before any attempt exists,
600
+ * like `assertClosed`'s pre-dispatch check.
601
+ */
602
+ attempt?: number;
603
+ }
308
604
  interface CircuitBreakerOptions {
309
605
  /** Consecutive failures before the circuit opens, default 5 */
310
606
  threshold?: number;
@@ -323,7 +619,8 @@ interface CircuitBreakerOptions {
323
619
  * here. With `isolateByModel` on, it's exact: each model has its own
324
620
  * counter, so the transition really was caused solely by that model.
325
621
  */
326
- onStateChange?: (from: CircuitState, to: CircuitState, consecutiveFailures: number, model?: string) => void;
622
+ onStateChange?: (from: CircuitState, to: CircuitState, consecutiveFailures: number, model?: string, /** See `CircuitBreakerCallContext`. */
623
+ context?: CircuitBreakerCallContext) => void;
327
624
  /**
328
625
  * Track a separate circuit per resolved model instead of one shared
329
626
  * circuit for the whole instance. A failure on one model then never
@@ -367,9 +664,9 @@ declare class CircuitBreaker {
367
664
  * elapsed, or half-open with no trial currently running), this call
368
665
  * becomes that trial
369
666
  */
370
- assertClosed(model?: string): void;
371
- recordSuccess(model?: string): void;
372
- recordFailure(model?: string): void;
667
+ assertClosed(model?: string, context?: CircuitBreakerCallContext): void;
668
+ recordSuccess(model?: string, context?: CircuitBreakerCallContext): void;
669
+ recordFailure(model?: string, context?: CircuitBreakerCallContext): void;
373
670
  /**
374
671
  * With `isolateByModel` off (the default), `model` is ignored and the
375
672
  * one shared circuit's state is returned, unchanged from every version
@@ -385,7 +682,7 @@ declare class CircuitBreaker {
385
682
  * threshold-crossing failure would, and clears any in-flight half-open
386
683
  * trial since it no longer applies once the circuit is (re)opened.
387
684
  */
388
- open(model?: string): void;
685
+ open(model?: string, context?: CircuitBreakerCallContext): void;
389
686
  /**
390
687
  * Manually closes the circuit and resets its failure count, e.g. once a
391
688
  * provider is confirmed healthy again without waiting out the cooldown.
@@ -393,7 +690,7 @@ declare class CircuitBreaker {
393
690
  * per-model bucket under `isolateByModel`, once idle) but without
394
691
  * requiring an actual successful call first.
395
692
  */
396
- close(model?: string): void;
693
+ close(model?: string, context?: CircuitBreakerCallContext): void;
397
694
  }
398
695
 
399
696
  //#endregion
@@ -616,6 +913,19 @@ declare class FallbackExhaustedError extends LLMError {
616
913
  }
617
914
  /** Narrows `err` to {@link FallbackExhaustedError}, for direct access to its `attempts` (`provider`/`model` per failed target) without a manual `instanceof` check. */
618
915
  declare function isFallbackExhaustedError(err: unknown): err is FallbackExhaustedError;
916
+ /**
917
+ * Creates an empty ref box to pass as `CallParams['meta']`, so a caller can
918
+ * read the `CallMeta` written by `call()` on the same line as the result
919
+ * instead of pre-declaring a `{ current?: CallMeta }` by hand.
920
+ *
921
+ * @example
922
+ * const meta = metaRef();
923
+ * const result = await vern.call({ userContent: '...', meta });
924
+ * meta.current?.provider;
925
+ */
926
+ declare function metaRef(): {
927
+ current?: CallMeta;
928
+ };
619
929
 
620
930
  //#endregion
621
931
  //#region src/types/schema.d.ts
@@ -658,8 +968,8 @@ interface JsonSchemaSpec {
658
968
  * itself. VernLLM transports this to the provider and parses what comes
659
969
  * back; it never executes anything.
660
970
  */
661
- interface ToolDefinition {
662
- name: string;
971
+ interface ToolDefinition<Name extends string = string, Args = unknown> {
972
+ name: Name;
663
973
  description: string;
664
974
  /** JSON Schema for the tool's input. */
665
975
  parameters: Record<string, unknown>;
@@ -671,16 +981,45 @@ interface ToolDefinition {
671
981
  * requiring a JSON Schema validator (e.g. ajv) as a new dependency.
672
982
  * Failed validation throws `LLMError('validation')`. If omitted, VernLLM
673
983
  * parses arguments as JSON but does not validate them further.
984
+ *
985
+ * When set, `Args` (and therefore `Name`) flow into the `ToolCall`s
986
+ * returned by `call()`/`cachedCall()`, provided the tool was declared
987
+ * with `defineTool()` or otherwise has a literal `name`; see
988
+ * `defineTool()` below for why a plain object literal often doesn't.
674
989
  */
675
- argumentsSchema?: SchemaLike<unknown>;
990
+ argumentsSchema?: SchemaLike<Args>;
676
991
  }
677
- /** A single tool invocation requested by the model. */
678
- interface ToolCall {
992
+ /**
993
+ * Preserves a tool definition's literal `name` (and its `argumentsSchema`'s
994
+ * inferred `Args`) so it can discriminate a `ToolCall` union later.
995
+ *
996
+ * A plain object literal like `{ name: 'get_weather', ... }` widens `name`
997
+ * to `string` unless annotated `as const`, which silently defeats
998
+ * `ToolCall` narrowing the moment a second tool is added to the same
999
+ * `tools: [...]` array (single-tool arrays still narrow fine even without
1000
+ * this, since there's nothing to discriminate against but that stops
1001
+ * being true as soon as a second tool shows up). Wrapping the same object
1002
+ * in `defineTool()` preserves the literal `name` type without requiring
1003
+ * `as const` at every call site.
1004
+ */
1005
+ declare function defineTool<const Name extends string, Args = unknown>(tool: ToolDefinition<Name, Args>): ToolDefinition<Name, Args>;
1006
+ /** Maps a single `ToolDefinition` to its matching `ToolCall` shape. */
1007
+ type ToolCallFor<T> = T extends ToolDefinition<infer N, infer A> ? {
679
1008
  id: string;
680
- name: string;
681
- /** Parsed JSON arguments (and validated, if `argumentsSchema` was set). */
682
- arguments: unknown;
683
- }
1009
+ name: N;
1010
+ arguments: A;
1011
+ } : never;
1012
+ /**
1013
+ * A single tool invocation requested by the model.
1014
+ *
1015
+ * When `Tools` is a literal tuple (e.g. inferred from `tools: [getWeather,
1016
+ * cancelOrder]` at a `call()`/`cachedCall()` site), this is a discriminated
1017
+ * union keyed by `name`. Checking `call.name === 'get_weather'` narrows
1018
+ * `call.arguments` to that tool's `Args` with no cast needed. Without a
1019
+ * literal `Tools` (the default), this collapses back to today's
1020
+ * `{ id: string; name: string; arguments: unknown }`.
1021
+ */
1022
+ type ToolCall<Tools extends readonly ToolDefinition[] = ToolDefinition[]> = ToolCallFor<Tools[number]>;
684
1023
  /** The application's result of executing a `ToolCall`, sent back to the model. */
685
1024
  interface ToolResult {
686
1025
  toolCallId: string;
@@ -699,13 +1038,33 @@ interface ContentResult<T> {
699
1038
  content: T;
700
1039
  }
701
1040
  /** `call()` result when `tools` was set and the model requested one or more tools. */
702
- interface ToolCallResult {
1041
+ interface ToolCallResult<Tools extends readonly ToolDefinition[] = ToolDefinition[]> {
703
1042
  type: 'tool_calls';
704
- toolCalls: ToolCall[];
1043
+ toolCalls: ToolCall<Tools>[];
705
1044
  /** Any text the model produced alongside the tool request, if present. */
706
1045
  content?: string;
707
1046
  }
708
- type CallWithToolsResult<T> = ContentResult<T> | ToolCallResult;
1047
+ type CallWithToolsResult<T, Tools extends readonly ToolDefinition[] = ToolDefinition[]> = ContentResult<T> | ToolCallResult<Tools>;
1048
+ /**
1049
+ * Pulls `Tools` out of a `result` value's own static type, when that type
1050
+ * is already `ContentResult<T> | ToolCallResult<Tools>` (or a superset
1051
+ * including it) rather than plain `unknown`. `Extract` isolates just the
1052
+ * `ToolCallResult<...>` member(s) of a union before inferring, so this
1053
+ * degrades to the default cleanly when `result` doesn't carry a
1054
+ * `ToolCallResult` shape at all (e.g. `unknown`, or a `call()` result that
1055
+ * TypeScript could only type as plain `T`, see `isToolCallResult`'s docs).
1056
+ */
1057
+ type ExtractTools<R> = Extract<R, ToolCallResult<ToolDefinition[]>> extends ToolCallResult<infer Tools> ? Tools : ToolDefinition[];
1058
+ /**
1059
+ * Resolves the `Tools` `isToolCallResult` actually narrows with: the
1060
+ * explicit `Tools` type argument if one was given, otherwise whatever
1061
+ * `ExtractTools` can infer from `R` (the `result` argument's own type).
1062
+ * `Tools` defaults to `never` as an "unset" sentinel, not a real tools
1063
+ * list, so this can tell "caller passed nothing" apart from "caller
1064
+ * genuinely passed `never`" (which would be an unusual thing to write on
1065
+ * purpose, and isn't a meaningful `Tools` value regardless).
1066
+ */
1067
+ type ResolvedTools<Tools, R> = [Tools] extends [never] ? ExtractTools<R> : Tools;
709
1068
  /**
710
1069
  * Runtime-safe check for whether a `call()` result is a `tool_calls`
711
1070
  * result. Prefer this over relying on TypeScript's static narrowing
@@ -714,8 +1073,42 @@ type CallWithToolsResult<T> = ContentResult<T> | ToolCallResult;
714
1073
  * that case TS may have typed the result as plain `T` even though it's
715
1074
  * actually a `CallWithToolsResult<T>` at runtime, and this check works
716
1075
  * either way.
1076
+ *
1077
+ * Generic over `Tools`, same as `ToolCallResult` itself, so narrowing a
1078
+ * conditional-tools result (`tools: someCondition ? [myTool] : undefined`)
1079
+ * through this check doesn't erase the per-tool `arguments` typing that
1080
+ * `ConditionalToolCallParams` already captured. `Tools` is inferred
1081
+ * automatically from `result`'s own static type whenever that's already
1082
+ * `T | CallWithToolsResult<T, Tools>` (which it is whenever `call()`'s
1083
+ * overload resolution succeeded, the common case), no type argument
1084
+ * needed:
1085
+ *
1086
+ * ```ts
1087
+ * const tools = someCondition ? [myTool] : undefined;
1088
+ * const result = await llm.call({ userContent: '...', tools });
1089
+ * if (isToolCallResult(result)) {
1090
+ * // result.toolCalls[number].arguments is typed per tool, inferred
1091
+ * // automatically, not `unknown`
1092
+ * }
1093
+ * ```
1094
+ *
1095
+ * If `result`'s static type is plain `unknown` (or otherwise doesn't
1096
+ * carry `Tools`, e.g. a variable annotated `: CallParams<T>` upstream
1097
+ * widened it away, see the overload note), there's nothing to infer from
1098
+ * and this falls back to the default `ToolCallResult` with `arguments:
1099
+ * unknown`, same as before `isToolCallResult` became generic. Pass
1100
+ * `Tools` explicitly as the first type argument to override the inferred
1101
+ * (or defaulted) type in either case; the second type argument (`R`, the
1102
+ * `result` value's own type) is always inferred from the argument itself
1103
+ * and should not be set manually:
1104
+ *
1105
+ * ```ts
1106
+ * if (isToolCallResult<typeof tools>(result)) {
1107
+ * // arguments typed per tool via the explicit override
1108
+ * }
1109
+ * ```
717
1110
  */
718
- declare function isToolCallResult(result: unknown): result is ToolCallResult;
1111
+ declare function isToolCallResult<Tools extends readonly ToolDefinition[] | undefined = never, R = unknown>(result: R): result is R & ToolCallResult<NonNullable<ResolvedTools<Tools, R>>>;
719
1112
  /** What the model should do about tools on a given call. */
720
1113
  type ToolChoice = 'auto' | 'none' | 'required' | {
721
1114
  name: string;
@@ -723,7 +1116,6 @@ type ToolChoice = 'auto' | 'none' | 'required' | {
723
1116
 
724
1117
  //#endregion
725
1118
  //#region src/types/usage.d.ts
726
- //# sourceMappingURL=tools.d.ts.map
727
1119
  type ReserveUsage = (params: {
728
1120
  coalesced: boolean;
729
1121
  signal?: AbortSignal;
@@ -846,13 +1238,21 @@ interface ImageBlock {
846
1238
  }
847
1239
  /** A single segment of multimodal `userContent`. */
848
1240
  type ContentBlock = TextBlock | ImageBlock;
849
- interface CallParams<T = unknown> extends UsageHooks {
1241
+ /**
1242
+ * Every field of a call request except the `reserveUsage`/`refundUsage`
1243
+ * hooks from `UsageHooks`. `CallParams` is this plus `UsageHooks`; the
1244
+ * `Cached*` param types below are call sites that want the request shape
1245
+ * without those two hooks (usage is metered once, at the `cachedCall`
1246
+ * level, not per-request), and use this directly instead of re-deriving
1247
+ * it with `Omit<CallParams<T>, 'reserveUsage' | 'refundUsage'>` each time.
1248
+ */
1249
+ interface LLMRequestShape<T = unknown, Tools extends readonly ToolDefinition[] = ToolDefinition[]> {
850
1250
  systemPrompt?: string;
851
1251
  /** Current user message, as text or multimodal content blocks. */
852
1252
  userContent: string | ContentBlock[];
853
1253
  /**
854
1254
  * Previous conversation turns. Must alternate roles; tool turns must follow
855
- * assistant tool calls. Invalid history throws LLMError('validation').
1255
+ * assistant tool calls. Invalid history throws LLMError('invalid_params').
856
1256
  */
857
1257
  history?: ConversationTurn[];
858
1258
  /**
@@ -865,6 +1265,20 @@ interface CallParams<T = unknown> extends UsageHooks {
865
1265
  maxTokens?: number;
866
1266
  requestId?: string;
867
1267
  signal?: AbortSignal;
1268
+ /**
1269
+ * Total time budget in ms for this whole call, across every retry and
1270
+ * every fallback target. Unlike timeoutMs, which resets on each attempt,
1271
+ * this is a single clock starting when call is invoked. The call is
1272
+ * aborted once this elapses, even mid retry or mid fallback, the same
1273
+ * way an aborted signal is today. Omit for no overall deadline, only
1274
+ * the existing per attempt timeoutMs applies.
1275
+ *
1276
+ * Only bounds getting to a final result: choosing a target, retrying,
1277
+ * and opening a stream. It does not extend to the time spent reading a
1278
+ * stream after it has opened. Use chunkIdleTimeoutMs for gaps between
1279
+ * chunks once a stream is open.
1280
+ */
1281
+ deadlineMs?: number;
868
1282
  /**
869
1283
  * Per-call override for the instance's `chunkIdleTimeoutMs` (max gap
870
1284
  * between stream chunks once opened). Only applies when `stream: true`.
@@ -911,8 +1325,12 @@ interface CallParams<T = unknown> extends UsageHooks {
911
1325
  * Tools the model may call. When set, `call()` returns a
912
1326
  * `CallWithToolsResult<T>` union instead of `T` directly. Combining with
913
1327
  * `jsonSchema` is provider-dependent; see the Tool Calling docs.
1328
+ *
1329
+ * Passed as a literal array (or via `defineTool()`-wrapped entries, see
1330
+ * `types/tools.ts`), this also drives the `Tools` type parameter, which
1331
+ * narrows `CallWithToolsResult`'s `toolCalls[number].arguments` per tool.
914
1332
  */
915
- tools?: ToolDefinition[];
1333
+ tools?: Tools;
916
1334
  /** Defaults to `'auto'` when `tools` is set. */
917
1335
  toolChoice?: ToolChoice;
918
1336
  /**
@@ -933,15 +1351,24 @@ interface CallParams<T = unknown> extends UsageHooks {
933
1351
  * Optional out-parameter for provider identity. Pass `{}` (or any object
934
1352
  * with a mutable `current` property) and `call()` writes a `CallMeta`
935
1353
  * into `meta.current` before returning, alongside whatever `onUsage`
936
- * already reports. Ignored for `stream: true`, since `call()` returns
937
- * before the outcome (and so the target that answered) is known; read
938
- * `TokenUsage.provider`/`usedFallback` from `onUsage` for streaming
939
- * calls instead.
1354
+ * already reports. This includes `stream: true`: the target is chosen
1355
+ * once the stream opens, which is also the point `call()` itself
1356
+ * returns `{ chunks, finalResult }`, so `meta.current` is already set
1357
+ * by then. `TokenUsage.provider`/`usedFallback` from `onUsage` reports
1358
+ * the same information asynchronously, for both streaming and
1359
+ * non-streaming calls.
1360
+ *
1361
+ * `meta.current` is only written once execution actually reaches and
1362
+ * selects a provider target. A `wrap` middleware that short-circuits
1363
+ * without calling `next()` never reaches that point, so `meta.current`
1364
+ * is left untouched; if the same holder object is reused across calls,
1365
+ * it can still hold a prior call's target.
940
1366
  */
941
1367
  meta?: {
942
1368
  current?: CallMeta;
943
1369
  };
944
1370
  }
1371
+ interface CallParams<T = unknown, Tools extends readonly ToolDefinition[] = ToolDefinition[]> extends LLMRequestShape<T, Tools>, UsageHooks {}
945
1372
  /**
946
1373
  * A `CallParams` variant where tool calling is explicitly enabled.
947
1374
  *
@@ -949,22 +1376,33 @@ interface CallParams<T = unknown> extends UsageHooks {
949
1376
  * tool-aware `call()` overload and return `CallWithToolsResult<T>` instead
950
1377
  * of the normal `T` response type.
951
1378
  */
952
- type ToolEnabledCallParams<T> = CallParams<T> & {
953
- tools: NonNullable<CallParams<T>['tools']>;
1379
+ type ToolEnabledCallParams<T, Tools extends readonly ToolDefinition[] = ToolDefinition[]> = CallParams<T, Tools> & {
1380
+ tools: NonNullable<CallParams<T, Tools>['tools']>;
954
1381
  };
955
1382
  /**
956
1383
  * A `CallParams` variant for tools set conditionally, e.g. `tools:
957
1384
  * someCondition ? [myTool] : undefined`. Selects the `call()` overload
958
- * returning the honest union `T | CallWithToolsResult<T>` instead of
1385
+ * returning the honest union `T | CallWithToolsResult<T, Tools>` instead of
959
1386
  * falling through to plain `T` (which is what happened before this type
960
1387
  * existed, since `ToolDefinition[] | undefined` matched neither
961
1388
  * `ToolEnabledCallParams` nor `ToolsDisabledCallParams`). Forces an
962
1389
  * `isToolCallResult()` check before treating the result as plain
963
1390
  * content. Omitting `tools` entirely still resolves to plain `T`, since
964
1391
  * tools genuinely cannot have run there.
965
- */
966
- type ConditionalToolCallParams<T> = CallParams<T> & {
967
- tools: ToolDefinition[] | undefined;
1392
+ *
1393
+ * `Tools` still can't reliably infer a literal tuple here the way
1394
+ * `ToolEnabledCallParams` does for an inline array (a ternary/variable
1395
+ * expression doesn't carry the same `const`-literal preservation), so
1396
+ * getting typed `arguments` out of a conditional-tools result also needs
1397
+ * an explicit `Tools` type argument on `isToolCallResult<Tools>()` when
1398
+ * narrowing, see its docs.
1399
+ */
1400
+ type ConditionalToolCallParams<T, Tools extends readonly ToolDefinition[] = ToolDefinition[]> = CallParams<T, Tools> & {
1401
+ tools: Tools | undefined;
1402
+ };
1403
+ /** Conditional tool-call parameters whose non-tool result is plain text. */
1404
+ type ConditionalStringToolCallParams<Tools extends readonly ToolDefinition[] = ToolDefinition[]> = ConditionalToolCallParams<string, Tools> & {
1405
+ jsonMode: false;
968
1406
  };
969
1407
  /**
970
1408
  * A `CallParams` variant where tools are offered but the model is barred
@@ -976,8 +1414,8 @@ type ConditionalToolCallParams<T> = CallParams<T> & {
976
1414
  * on the wrapper object silently produces `"[object Object]"` instead of
977
1415
  * throwing. The type itself rules that shape out.
978
1416
  */
979
- type ToolsDisabledCallParams<T> = CallParams<T> & {
980
- tools: NonNullable<CallParams<T>['tools']>;
1417
+ type ToolsDisabledCallParams<T, Tools extends readonly ToolDefinition[] = ToolDefinition[]> = CallParams<T, Tools> & {
1418
+ tools: NonNullable<CallParams<T, Tools>['tools']>;
981
1419
  toolChoice: 'none';
982
1420
  };
983
1421
  /**
@@ -1022,7 +1460,7 @@ interface CachedCallInput extends UsageHooks {
1022
1460
  * the caching docs for why.
1023
1461
  */
1024
1462
  type CachedCallParams<T> = CachedCallInput & {
1025
- call: Omit<CallParams<T>, 'reserveUsage' | 'refundUsage'>;
1463
+ call: LLMRequestShape<T>;
1026
1464
  };
1027
1465
  /**
1028
1466
  * Parameters for a cached LLM call with tool calling enabled.
@@ -1034,8 +1472,10 @@ type CachedCallParams<T> = CachedCallInput & {
1034
1472
  * See `CachedCallParams` for why `reserveUsage`/`refundUsage` are omitted
1035
1473
  * from `call`'s type here too.
1036
1474
  */
1037
- type CachedToolCallParams<T> = CachedCallInput & {
1038
- call: Omit<ToolEnabledCallParams<T>, 'reserveUsage' | 'refundUsage'>;
1475
+ type CachedToolCallParams<T, Tools extends readonly ToolDefinition[] = ToolDefinition[]> = CachedCallInput & {
1476
+ call: LLMRequestShape<T, Tools> & {
1477
+ tools: NonNullable<LLMRequestShape<T, Tools>['tools']>;
1478
+ };
1039
1479
  };
1040
1480
  /**
1041
1481
  * Parameters for a cached LLM call with `call.tools` set conditionally.
@@ -1043,22 +1483,36 @@ type CachedToolCallParams<T> = CachedCallInput & {
1043
1483
  * `T | CallWithToolsResult<T>` instead of narrowing to plain `T`. See
1044
1484
  * `ConditionalToolCallParams` for why this overload exists.
1045
1485
  */
1046
- type CachedConditionalToolCallParams<T> = CachedCallInput & {
1047
- call: Omit<ConditionalToolCallParams<T>, 'reserveUsage' | 'refundUsage'>;
1486
+ type CachedConditionalToolCallParams<T, Tools extends readonly ToolDefinition[] = ToolDefinition[]> = CachedCallInput & {
1487
+ call: LLMRequestShape<T, Tools> & {
1488
+ tools: Tools | undefined;
1489
+ };
1490
+ };
1491
+ /** Cached conditional tool-call parameters whose non-tool result is plain text. */
1492
+ type CachedConditionalStringToolCallParams<Tools extends readonly ToolDefinition[] = ToolDefinition[]> = CachedConditionalToolCallParams<string, Tools> & {
1493
+ call: {
1494
+ jsonMode: false;
1495
+ };
1048
1496
  };
1049
1497
  /**
1050
1498
  * Parameters for a cached LLM call with `jsonMode: false`. Selects the
1051
1499
  * `cachedCall()` overload that returns a plain `string`.
1052
1500
  */
1053
1501
  type CachedJsonModeDisabledCallParams = CachedCallInput & {
1054
- call: Omit<JsonModeDisabledCallParams, 'reserveUsage' | 'refundUsage'>;
1502
+ call: Omit<LLMRequestShape<unknown>, 'jsonSchema'> & {
1503
+ jsonMode: false;
1504
+ jsonSchema?: never;
1505
+ };
1055
1506
  };
1056
1507
  /**
1057
1508
  * Parameters for a cached LLM call with `jsonMode: true` and no `schema`.
1058
1509
  * Selects the `cachedCall()` overload that returns a `JsonValue`.
1059
1510
  */
1060
1511
  type CachedJsonModeEnabledCallParams = CachedCallInput & {
1061
- call: Omit<JsonModeEnabledCallParams, 'reserveUsage' | 'refundUsage'>;
1512
+ call: Omit<LLMRequestShape<JsonValue>, 'schema'> & {
1513
+ jsonMode: true;
1514
+ schema?: never;
1515
+ };
1062
1516
  };
1063
1517
 
1064
1518
  //#endregion
@@ -1104,9 +1558,11 @@ interface StreamCallResult<R> {
1104
1558
  * select the streaming `call()` overload and return `StreamCallResult<...>`
1105
1559
  * instead of the normal, single-shot response type.
1106
1560
  */
1107
- type StreamEnabledCallParams<T> = CallParams<T> & {
1561
+ type StreamEnabledCallParams<T, Tools extends readonly ToolDefinition[] = ToolDefinition[]> = CallParams<T, Tools> & {
1108
1562
  stream: true;
1109
1563
  };
1564
+ /** Streaming conditional tool-call parameters whose non-tool result is text. */
1565
+ type StreamConditionalStringToolCallParams<Tools extends readonly ToolDefinition[] = ToolDefinition[]> = StreamEnabledCallParams<string, Tools> & ConditionalStringToolCallParams<Tools>;
1110
1566
  /**
1111
1567
  * `StreamEnabledCallParams` with `jsonMode: false`. Selects the streaming
1112
1568
  * `call()` overload whose `finalResult` resolves to a plain `string`.
@@ -1177,10 +1633,12 @@ type WireStreamChunk = {
1177
1633
  * like).
1178
1634
  *
1179
1635
  * `reserveUsage`/`refundUsage` are omitted from `call`'s type; see
1180
- * `CachedCallParams` for why they belong at the top level here too.
1636
+ * `CachedCallParams` for why they belong at the top level here too.
1181
1637
  */
1182
1638
  type CachedStreamCallParams<T> = CachedCallInput & {
1183
- call: Omit<StreamEnabledCallParams<T>, 'reserveUsage' | 'refundUsage'>;
1639
+ call: LLMRequestShape<T> & {
1640
+ stream: true;
1641
+ };
1184
1642
  };
1185
1643
  /**
1186
1644
  * Parameters for a cached, streaming LLM call with tool calling enabled.
@@ -1189,8 +1647,11 @@ type CachedStreamCallParams<T> = CachedCallInput & {
1189
1647
  * `CachedToolCallParams<T>`, with the same live-chunks-on-miss,
1190
1648
  * replayed-chunks-on-hit behavior as `CachedStreamCallParams<T>`.
1191
1649
  */
1192
- type CachedStreamToolCallParams<T> = CachedCallInput & {
1193
- call: Omit<StreamEnabledCallParams<T> & ToolEnabledCallParams<T>, 'reserveUsage' | 'refundUsage'>;
1650
+ type CachedStreamToolCallParams<T, Tools extends readonly ToolDefinition[] = ToolDefinition[]> = CachedCallInput & {
1651
+ call: LLMRequestShape<T, Tools> & {
1652
+ stream: true;
1653
+ tools: NonNullable<LLMRequestShape<T, Tools>['tools']>;
1654
+ };
1194
1655
  };
1195
1656
  /**
1196
1657
  * Parameters for a cached, streaming LLM call with `call.tools` set
@@ -1199,8 +1660,17 @@ type CachedStreamToolCallParams<T> = CachedCallInput & {
1199
1660
  * `T | CallWithToolsResult<T>` instead of narrowing to plain `T`. See
1200
1661
  * `ConditionalToolCallParams` for why this overload exists.
1201
1662
  */
1202
- type CachedStreamConditionalToolCallParams<T> = CachedCallInput & {
1203
- call: Omit<StreamEnabledCallParams<T> & ConditionalToolCallParams<T>, 'reserveUsage' | 'refundUsage'>;
1663
+ type CachedStreamConditionalToolCallParams<T, Tools extends readonly ToolDefinition[] = ToolDefinition[]> = CachedCallInput & {
1664
+ call: LLMRequestShape<T, Tools> & {
1665
+ stream: true;
1666
+ tools: Tools | undefined;
1667
+ };
1668
+ };
1669
+ /** Cached streaming conditional tool-call parameters whose non-tool result is text. */
1670
+ type CachedStreamConditionalStringToolCallParams<Tools extends readonly ToolDefinition[] = ToolDefinition[]> = CachedStreamConditionalToolCallParams<string, Tools> & {
1671
+ call: {
1672
+ jsonMode: false;
1673
+ };
1204
1674
  };
1205
1675
  /**
1206
1676
  * Parameters for a cached, streaming LLM call with `jsonMode: false`.
@@ -1208,7 +1678,11 @@ type CachedStreamConditionalToolCallParams<T> = CachedCallInput & {
1208
1678
  * cached value (on a hit) is a plain `string`.
1209
1679
  */
1210
1680
  type CachedStreamJsonModeDisabledCallParams = CachedCallInput & {
1211
- call: Omit<StreamJsonModeDisabledCallParams, 'reserveUsage' | 'refundUsage'>;
1681
+ call: Omit<LLMRequestShape<unknown>, 'jsonSchema'> & {
1682
+ stream: true;
1683
+ jsonMode: false;
1684
+ jsonSchema?: never;
1685
+ };
1212
1686
  };
1213
1687
  /**
1214
1688
  * Parameters for a cached, streaming LLM call with `jsonMode: true` and no
@@ -1216,7 +1690,11 @@ type CachedStreamJsonModeDisabledCallParams = CachedCallInput & {
1216
1690
  * miss) or cached value (on a hit) is a `JsonValue`.
1217
1691
  */
1218
1692
  type CachedStreamJsonModeEnabledCallParams = CachedCallInput & {
1219
- call: Omit<StreamJsonModeEnabledCallParams, 'reserveUsage' | 'refundUsage'>;
1693
+ call: Omit<LLMRequestShape<JsonValue>, 'schema'> & {
1694
+ stream: true;
1695
+ jsonMode: true;
1696
+ schema?: never;
1697
+ };
1220
1698
  };
1221
1699
 
1222
1700
  //#endregion
@@ -1372,72 +1850,9 @@ declare class ConsoleLogger implements Logger {
1372
1850
  error(message: string, meta?: Record<string, unknown>): void;
1373
1851
  }
1374
1852
 
1375
- //#endregion
1376
- //#region src/types/events.d.ts
1377
- //# sourceMappingURL=logger.d.ts.map
1378
- /**
1379
- * Reports what happened during a call. Fire and forget, mirroring
1380
- * `onUsage`: the return value is never read and a throwing handler cannot
1381
- * change what the call does, only what gets reported about it.
1382
- */
1383
- type VernLLMEvent = {
1384
- kind: 'retry';
1385
- requestId: string;
1386
- provider: string;
1387
- /** The model actually resolved for this call (honors a per-call `model` override). */
1388
- model: string;
1389
- /** The 1-based retry ordinal (the 1st retry is `1`, not the overall attempt count). */
1390
- attempt: number;
1391
- maxRetries: number;
1392
- delayMs: number;
1393
- retryAfterHonored: boolean;
1394
- error: LLMError;
1395
- } | {
1396
- kind: 'circuit_state';
1397
- provider: string;
1398
- /**
1399
- * The model of the call that triggered this specific transition
1400
- * (whatever was passed to the `assertClosed`/`recordSuccess`/
1401
- * `recordFailure` call that caused it), not a property of the
1402
- * circuit itself: the breaker still counts failures across every
1403
- * model together, so a threshold crossing can be the sum of
1404
- * several different models' failures even though only the
1405
- * triggering call's `model` is reported here.
1406
- */
1407
- model: string;
1408
- from: CircuitState;
1409
- to: CircuitState;
1410
- consecutiveFailures: number;
1411
- } | {
1412
- kind: 'fallback';
1413
- requestId: string;
1414
- /** Provider name of the target that just failed. */
1415
- from: string;
1416
- /** Provider name of the target about to be tried next. */
1417
- to: string;
1418
- /** `-1` for the primary target, otherwise the index into `fallback`. */
1419
- fromIndex: number;
1420
- toIndex: number;
1421
- /** The normalized error that caused `from` to be abandoned. */
1422
- error: LLMError;
1423
- /** Time spent on `from`, including its own retries, before giving up. */
1424
- elapsedMs: number;
1425
- } | {
1426
- kind: 'rate_limited';
1427
- requestId: string;
1428
- provider: string;
1429
- /** The model actually resolved for this call (honors a per-call `model` override). */
1430
- model: string;
1431
- /** How long this attempt sat queued for capacity before it was let through. */
1432
- waitedMs: number;
1433
- /** Which configured bucket was blocking this attempt just before it cleared. */
1434
- reason: 'concurrency' | 'rpm' | 'tpm';
1435
- };
1436
- type OnEvent = (event: VernLLMEvent) => void;
1437
-
1438
1853
  //#endregion
1439
1854
  //#region src/types/options.d.ts
1440
- //# sourceMappingURL=events.d.ts.map
1855
+ //# sourceMappingURL=logger.d.ts.map
1441
1856
  interface VernLLMOptions {
1442
1857
  client: LLMClient;
1443
1858
  model: string;
@@ -1569,11 +1984,61 @@ interface VernLLMOptions {
1569
1984
  * and moves on for everything else.
1570
1985
  */
1571
1986
  fallbackOn?: FallbackOn;
1987
+ /**
1988
+ * Transforms outgoing requests and/or wraps whole logical calls,
1989
+ * without touching retry, circuit breaker, or fallback internals.
1990
+ * Defaults to an empty array. See `VernLLMMiddleware` for the four
1991
+ * available hooks (`transform`, `wrap`, `onEvent`, `enabled`).
1992
+ */
1993
+ middleware?: VernLLMMiddleware[];
1994
+ /**
1995
+ * Bounds `transform` and a function `enabled`, the same way every
1996
+ * other blocking operation in the package is already bounded.
1997
+ * Overridable per middleware via that entry's own `timeoutMs`.
1998
+ * `<= 0` means unbounded (no timer at all). Default 5000.
1999
+ */
2000
+ middlewareTimeoutMs?: number;
1572
2001
  }
1573
2002
 
1574
2003
  //#endregion
1575
- //#region src/vernLLM.d.ts
2004
+ //#region src/types/createMiddleware.d.ts
1576
2005
  //# sourceMappingURL=options.d.ts.map
2006
+ /**
2007
+ * `VernLLMMiddleware` plus `onError`, a convenience for the common "I
2008
+ * only care about failures" case. Everything else is passed through to
2009
+ * the resulting `VernLLMMiddleware` unchanged; setting `wrap` directly
2010
+ * alongside `onError` is an error, since `onError` builds its own `wrap`
2011
+ * under the hood, and building it around a `wrap` you also supplied
2012
+ * would silently drop one of the two.
2013
+ */
2014
+ type CreateMiddlewareOptions = Omit<VernLLMMiddleware, 'wrap'> & {
2015
+ wrap?: undefined;
2016
+ /**
2017
+ * Called with this call's terminal error, if it fails: the same error
2018
+ * `wrap`'s own `next()` would reject with. Never called on success,
2019
+ * and never called for a failure some *other* middleware's `wrap`
2020
+ * already swallowed by short-circuiting with its own `CallResult`.
2021
+ * The original error is always rethrown afterward, `onError` only
2022
+ * observes it, exactly like `onUsage`/`onEvent` elsewhere: a throwing
2023
+ * `onError` is discarded (not logged, this helper has no `Logger` of
2024
+ * its own to log through) and otherwise has no effect on the call.
2025
+ * `ctx` is `wrap`'s own pre-dispatch context (`onError` builds a `wrap`
2026
+ * under the hood), so it only describes the primary target.
2027
+ */
2028
+ onError?: (error: LLMError, ctx: PreDispatchContext) => void | Promise<void>;
2029
+ };
2030
+ /**
2031
+ * Builds a `VernLLMMiddleware` entry. Plain pass-through when `onError`
2032
+ * is omitted; when it's set, wraps it in a `wrap` that calls `next()`,
2033
+ * reports `onError` on a rejection, and always rethrows the original
2034
+ * error afterward, so `onError` never changes what the call itself
2035
+ * returns or throws, only what gets observed about it.
2036
+ */
2037
+ declare function createMiddleware(options: CreateMiddlewareOptions): VernLLMMiddleware;
2038
+
2039
+ //#endregion
2040
+ //#region src/vernLLM.d.ts
2041
+ //# sourceMappingURL=createMiddleware.d.ts.map
1577
2042
  /**
1578
2043
  * A LLM call framework for resilience, observability and control. This is VernLLM!
1579
2044
  *
@@ -1587,21 +2052,37 @@ declare class VernLLM {
1587
2052
  /**
1588
2053
  * One `CallExecutor` per provider target: index 0 is the primary,
1589
2054
  * everything after it is a `fallback` target, in the order declared.
1590
- * Each owns its own request building, retry/timeout, circuit breaker,
1591
- * and rate limiter. `call()` walks this array in `runFallbackChain`,
1592
- * moving to the next entry only when `fallbackOn` says to.
2055
+ * Walked by `runFallbackChain`, moving to the next entry only when
2056
+ * `fallbackOn` says to.
1593
2057
  */
1594
2058
  private readonly executors;
1595
2059
  /** Decides whether a failed target is followed by the next one or the chain stops. See `VernLLMOptions['fallbackOn']`. */
1596
2060
  private readonly fallbackOn;
1597
2061
  /** Reports a `'fallback'` event when the chain moves to the next target. Shared `onEvent` plumbing, same as every executor's. */
1598
2062
  private readonly reportEvent;
2063
+ /** Owns cache reads/writes and in-flight coalescing for `cachedCall()`. Only calls back into `this.call()` as an opaque function. */
2064
+ private readonly cacheOrchestrator;
2065
+ /** See `VernLLMOptions.middleware`. Sorted once here by `priority`, ascending, ties broken by original array order. */
2066
+ private readonly middleware;
2067
+ /** See `VernLLMOptions.middlewareTimeoutMs`. Bounds `transform` and a function `enabled`; `wrap` itself is never bounded by this. */
2068
+ private readonly middlewareTimeoutMs;
1599
2069
  /**
1600
- * Owns cache key resolution, cache reads/writes, and in-flight
1601
- * coalescing for `cachedCall()`. Independent of `executor`: it only
1602
- * ever calls back into `this.call()` as an opaque function.
2070
+ * Maps `cachedCall()`'s inner `this.call(...)` params to its own
2071
+ * `middlewareState`, so that call's own `runOperation` skips wrapping
2072
+ * again and reuses the same state bag `wrap` just ran with (so a
2073
+ * value `wrap` sets is visible to `transform`, same as a direct
2074
+ * call). Keyed by object identity, not `requestId`, since two
2075
+ * concurrent `cachedCall()`s can share an explicit `requestId`.
1603
2076
  */
1604
- private readonly cacheOrchestrator;
2077
+ private readonly cachedCallInnerParams;
2078
+ /**
2079
+ * Shares one `CallMeta` holder across every `cachedCall()` in flight
2080
+ * for the same resolved cache key, so a joining invocation (never
2081
+ * calls `call()` itself) reports the trigger's real metadata instead
2082
+ * of `undefined`. A true cache hit never creates an entry, so it
2083
+ * still reports no metadata correctly.
2084
+ */
2085
+ private readonly cachedCallMeta;
1605
2086
  /**
1606
2087
  * @param options Client, model, and tunables. Defaults: `maxRetries` 1,
1607
2088
  * `timeoutMs` 25000, `baseDelayMs` 500, `defaultMaxTokens` 1000,
@@ -1611,70 +2092,35 @@ declare class VernLLM {
1611
2092
  constructor(options: VernLLMOptions);
1612
2093
  /** Logs a failed refundUsage attempt via the configured logger. */
1613
2094
  private logRefundError;
1614
- /**
1615
- * Walks `this.executors` in order, running `attempt` against each until
1616
- * one succeeds or every target has failed. `run` on a lone target
1617
- * (no `fallback` configured) throws exactly what it throws today: the
1618
- * loop's single iteration path is unchanged from pre-fallback behavior.
1619
- *
1620
- * For streaming, `attempt` is `executor.runStream`, whose own retries
1621
- * only cover *opening* the stream (see `CallExecutor.runStream`). A
1622
- * mid-stream failure surfaces through `finalResult` after this function
1623
- * has already returned, so it's never seen here and never falls over,
1624
- * per the streaming limitation: splicing a second model's output into a
1625
- * response the consumer has already partially rendered would corrupt
1626
- * it.
1627
- */
1628
- private runFallbackChain;
2095
+ /** Everything `executeLogicalCall`/`executeLogicalStreamCall` (in `logicalCall.ts`) need from this instance, gathered once so `call()` doesn't rebuild it per invocation. */
2096
+ private get logicalCallDependencies();
2097
+ /** Everything `runOperation` (in `runOperation.ts`) needs from this instance, gathered once so `call()`/`cachedCall()` don't rebuild it per invocation. */
2098
+ private get runOperationDependencies();
1629
2099
  /**
1630
2100
  * Makes a single logical LLM call, retrying on failure per the configured
1631
2101
  * policy. Fails fast if the breaker is open or the signal is already
1632
- * aborted. Rejects with a normalized LLMError on exhausted retries.
1633
- *
1634
- * When `tools` is set, returns a `CallWithToolsResult<T>` instead of `T`:
1635
- * `{ type: 'content', content }` or `{ type: 'tool_calls', toolCalls,
1636
- * content? }`. VernLLM never executes tools; run them yourself and
1637
- * continue via `history` (see `ConversationTurn`). Mutually exclusive
1638
- * with `jsonSchema`/`schema`.
2102
+ * aborted. Rejects with a normalized `LLMError` on exhausted retries.
1639
2103
  *
1640
- * TypeScript picks the tools-aware overload (`CallWithToolsResult<T>`)
1641
- * when `tools` is a literal array on `params`, and the conditional-tools
1642
- * overload (`T | CallWithToolsResult<T>`, see `ConditionalToolCallParams`)
1643
- * when `tools` is present but statically `ToolDefinition[] | undefined`,
1644
- * e.g. `const tools = condition ? [myTool] : undefined`. Either way, use
1645
- * `isToolCallResult()` to narrow the result once `tools` isn't a literal
1646
- * array: TypeScript's static type can't know from the `ConditionalToolCallParams`
1647
- * shape alone whether tools actually ran on a given call. Only omitting
1648
- * `tools` entirely resolves to the plain `T` overload, since then tools
1649
- * genuinely cannot have run. See the Tool Calling docs for details.
1650
- *
1651
- * The same static-vs-dynamic caveat applies to `stream`: TypeScript only
1652
- * selects the streaming overload (returning `StreamCallResult<...>`) when
1653
- * `stream: true` is statically present on `params`. A `stream` value set
1654
- * conditionally on a plain `CallParams<T>` still resolves to `Promise<T>`
1655
- * (or `Promise<CallWithToolsResult<T>>`) at the type level even though
1656
- * the actual runtime result is the `{ chunks, finalResult }` streaming
1657
- * shape whenever `stream` evaluates to `true`, callers doing this should
1658
- * narrow/cast accordingly rather than relying on the static return type.
2104
+ * Supports `tools`, `stream`, and JSON mode/schema, in any combination.
2105
+ * See the Tool Calling and Streaming docs for return-shape details and
2106
+ * the TypeScript overloads that select between them.
1659
2107
  *
1660
2108
  * @param params System/user content plus per-call overrides. See `CallParams`.
1661
- * @returns Without `tools` or `stream`: the parsed response, or raw
1662
- * string if `jsonMode` is false. With `tools`: a `CallWithToolsResult<T>`,
1663
- * narrowed to `ContentResult<T>` when `toolChoice: 'none'` is set, since
1664
- * the model is then structurally barred from returning a `tool_calls`
1665
- * result. With `stream: true` (statically): a `{ chunks, finalResult }`
1666
- * `StreamCallResult`, `finalResult` resolving to whichever of the above
1667
- * shapes applies once the stream completes. See `StreamCallResult`.
2109
+ * @returns The parsed response (or raw string if `jsonMode` is false), a
2110
+ * `CallWithToolsResult<T>` when `tools` is set, or a `{ chunks,
2111
+ * finalResult }` `StreamCallResult` when `stream: true`. See `StreamCallResult`.
1668
2112
  */
1669
2113
  call<T = unknown>(params: StreamEnabledCallParams<T> & ToolsDisabledCallParams<T>): Promise<StreamCallResult<ContentResult<T>>>;
1670
- call<T = unknown>(params: StreamEnabledCallParams<T> & ToolEnabledCallParams<T>): Promise<StreamCallResult<CallWithToolsResult<T>>>;
1671
- call<T = unknown>(params: StreamEnabledCallParams<T> & ConditionalToolCallParams<T>): Promise<StreamCallResult<T | CallWithToolsResult<T>>>;
2114
+ call<T = unknown, const Tools extends readonly ToolDefinition[] = ToolDefinition[]>(params: StreamEnabledCallParams<T, Tools> & ToolEnabledCallParams<T, Tools>): Promise<StreamCallResult<CallWithToolsResult<T, Tools>>>;
2115
+ call<const Tools extends readonly ToolDefinition[] = ToolDefinition[]>(params: StreamConditionalStringToolCallParams<Tools>): Promise<StreamCallResult<string | CallWithToolsResult<string, Tools>>>;
2116
+ call<T = unknown, const Tools extends readonly ToolDefinition[] = ToolDefinition[]>(params: StreamEnabledCallParams<T, Tools> & ConditionalToolCallParams<T, Tools>): Promise<StreamCallResult<T | CallWithToolsResult<T, Tools>>>;
1672
2117
  call(params: StreamJsonModeDisabledCallParams): Promise<StreamCallResult<string>>;
1673
2118
  call(params: StreamJsonModeEnabledCallParams): Promise<StreamCallResult<JsonValue>>;
1674
2119
  call<T = unknown>(params: StreamEnabledCallParams<T>): Promise<StreamCallResult<T>>;
1675
2120
  call<T = unknown>(params: ToolsDisabledCallParams<T>): Promise<ContentResult<T>>;
1676
- call<T = unknown>(params: ToolEnabledCallParams<T>): Promise<CallWithToolsResult<T>>;
1677
- call<T = unknown>(params: ConditionalToolCallParams<T>): Promise<T | CallWithToolsResult<T>>;
2121
+ call<T = unknown, const Tools extends readonly ToolDefinition[] = ToolDefinition[]>(params: ToolEnabledCallParams<T, Tools>): Promise<CallWithToolsResult<T, Tools>>;
2122
+ call<const Tools extends readonly ToolDefinition[] = ToolDefinition[]>(params: ConditionalStringToolCallParams<Tools>): Promise<string | CallWithToolsResult<string, Tools>>;
2123
+ call<T = unknown, const Tools extends readonly ToolDefinition[] = ToolDefinition[]>(params: ConditionalToolCallParams<T, Tools>): Promise<T | CallWithToolsResult<T, Tools>>;
1678
2124
  call(params: JsonModeDisabledCallParams): Promise<string>;
1679
2125
  call(params: JsonModeEnabledCallParams): Promise<JsonValue>;
1680
2126
  call<T = unknown>(params: CallParams<T>): Promise<T>;
@@ -1696,39 +2142,30 @@ declare class VernLLM {
1696
2142
  deleteCache(key: string): Promise<void>;
1697
2143
  /**
1698
2144
  * Cache wrapper composing `call` + caching, so cached LLM calls
1699
- * automatically get retry/timeout/circuit-breaker behavior. `reserveUsage`/
1700
- * `refundUsage` are read from the top-level params only. Concurrent misses
1701
- * for the same `cacheKey` share a single in-flight call, avoiding cache
1702
- * stampedes. Supports `stream: true` and `tools` in any combination.
1703
- *
1704
- * When `call.tools` is set, this caches the whole `CallWithToolsResult`,
1705
- * including `tool_calls` results, not just final answers. Whether
1706
- * that's appropriate depends on the tool: caching "the model decided to
1707
- * call get_weather" is usually fine to reuse briefly, but caching a
1708
- * decision made under permissions or account state that can change
1709
- * between calls is not. Use a short `ttl` or a separate `cacheKey` for
1710
- * such tools if this distinction matters.
2145
+ * automatically get retry/timeout/circuit-breaker behavior. Concurrent
2146
+ * misses for the same `cacheKey` share a single in-flight call, avoiding
2147
+ * cache stampedes. Supports `stream: true` and `tools` in any combination.
1711
2148
  *
1712
- * There is no public way to cache an arbitrary non-LLM function through
1713
- * `VernLLM`. This method always composes with `call()`. For
1714
- * general-purpose caching unrelated to an LLM call, use a dedicated
1715
- * caching library at the application level instead.
2149
+ * When `call.tools` is set, this caches the whole result including any
2150
+ * `tool_calls` decision, not just final answers; use a short `ttl` or a
2151
+ * separate `cacheKey` if a tool's result shouldn't be reused across calls.
1716
2152
  *
1717
- * @param params `cacheKey`, `ttl`, and optional
2153
+ * @param params `cacheKey`, `ttl`, optional
1718
2154
  * `reserveUsage`/`refundUsage`/`signal`, plus `call`, the `CallParams`
1719
- * (optionally with `tools` and/or `stream`) to pass through to
1720
- * `this.call(...)`. The top-level `signal` governs the cached operation
1721
- * and its usage hooks only; to also abort the underlying provider
1722
- * request, set `signal` inside `call`.
2155
+ * to pass through to `this.call(...)`. The top-level `signal` governs
2156
+ * the cached operation and its usage hooks only; to also abort the
2157
+ * underlying provider request, set `signal` inside `call`.
1723
2158
  * @returns The cached value on a hit, or the freshly-called result on a miss.
1724
2159
  */
1725
- cachedCall<T>(params: CachedStreamToolCallParams<T>): Promise<StreamCallResult<CallWithToolsResult<T>>>;
1726
- cachedCall<T>(params: CachedStreamConditionalToolCallParams<T>): Promise<StreamCallResult<T | CallWithToolsResult<T>>>;
2160
+ cachedCall<T, const Tools extends readonly ToolDefinition[] = ToolDefinition[]>(params: CachedStreamToolCallParams<T, Tools>): Promise<StreamCallResult<CallWithToolsResult<T, Tools>>>;
2161
+ cachedCall<const Tools extends readonly ToolDefinition[] = ToolDefinition[]>(params: CachedStreamConditionalStringToolCallParams<Tools>): Promise<StreamCallResult<string | CallWithToolsResult<string, Tools>>>;
2162
+ cachedCall<T, const Tools extends readonly ToolDefinition[] = ToolDefinition[]>(params: CachedStreamConditionalToolCallParams<T, Tools>): Promise<StreamCallResult<T | CallWithToolsResult<T, Tools>>>;
1727
2163
  cachedCall(params: CachedStreamJsonModeDisabledCallParams): Promise<StreamCallResult<string>>;
1728
2164
  cachedCall(params: CachedStreamJsonModeEnabledCallParams): Promise<StreamCallResult<JsonValue>>;
1729
2165
  cachedCall<T>(params: CachedStreamCallParams<T>): Promise<StreamCallResult<T>>;
1730
- cachedCall<T>(params: CachedToolCallParams<T>): Promise<CallWithToolsResult<T>>;
1731
- cachedCall<T>(params: CachedConditionalToolCallParams<T>): Promise<T | CallWithToolsResult<T>>;
2166
+ cachedCall<T, const Tools extends readonly ToolDefinition[] = ToolDefinition[]>(params: CachedToolCallParams<T, Tools>): Promise<CallWithToolsResult<T, Tools>>;
2167
+ cachedCall<const Tools extends readonly ToolDefinition[] = ToolDefinition[]>(params: CachedConditionalStringToolCallParams<Tools>): Promise<string | CallWithToolsResult<string, Tools>>;
2168
+ cachedCall<T, const Tools extends readonly ToolDefinition[] = ToolDefinition[]>(params: CachedConditionalToolCallParams<T, Tools>): Promise<T | CallWithToolsResult<T, Tools>>;
1732
2169
  cachedCall(params: CachedJsonModeDisabledCallParams): Promise<string>;
1733
2170
  cachedCall(params: CachedJsonModeEnabledCallParams): Promise<JsonValue>;
1734
2171
  cachedCall<T>(params: CachedCallParams<T>): Promise<T>;
@@ -1764,11 +2201,11 @@ declare class VernLLM {
1764
2201
  * @throws {RangeError} If `target.index` names no target.
1765
2202
  */
1766
2203
  closeCircuit(target?: CircuitTarget): void;
1767
- /** Resolves a target index so every circuit-breaker method agrees on what counts as valid. */
1768
- private resolveExecutor;
1769
- /** Warns when `model` can't do anything on this target, so it's never silently ignored. */
1770
- private warnIfModelUnsupported;
1771
2204
  }
2205
+
2206
+ //#endregion
2207
+ //#region src/paramsHelpers.d.ts
2208
+ //# sourceMappingURL=vernLLM.d.ts.map
1772
2209
  /**
1773
2210
  * Identity function preserving `params`'s own precise type, unlike a `:
1774
2211
  * CallParams<T>` annotation, which would widen `tools` away and break the
@@ -1807,7 +2244,7 @@ declare function defineCachedCallParams<P extends CachedCallParams<unknown>>(par
1807
2244
 
1808
2245
  //#endregion
1809
2246
  //#region src/adapters/internal/sse.d.ts
1810
- //# sourceMappingURL=vernLLM.d.ts.map
2247
+ //# sourceMappingURL=paramsHelpers.d.ts.map
1811
2248
  /**
1812
2249
  * Parses a Server-Sent-Events byte/text stream into the JSON payload of
1813
2250
  * each `data:` frame, in arrival order. Generic over transport: works with
@@ -1868,8 +2305,8 @@ type SupportedImageMimeType = (typeof SUPPORTED_IMAGE_MIME_TYPES)[number];
1868
2305
 
1869
2306
  /**
1870
2307
  * A static allow-list or predicate naming which models support native,
1871
- * schema-constrained output as its own request field Anthropic's
1872
- * `output_config.format`, Bedrock's `outputConfig.textFormat` separate
2308
+ * schema-constrained output as its own request field. Anthropic's
2309
+ * `output_config.format` and Bedrock's `outputConfig.textFormat` separate
1873
2310
  * from `tools`/`tool_choice`, so it can be combined with real,
1874
2311
  * caller-supplied `tools` in the same request.
1875
2312
  *
@@ -1877,13 +2314,13 @@ type SupportedImageMimeType = (typeof SUPPORTED_IMAGE_MIME_TYPES)[number];
1877
2314
  * Anthropic's and Bedrock's call to make, not this package's, and it
1878
2315
  * changes over time; hardcoding a guessed list would risk silently
1879
2316
  * routing a request onto a field a given model doesn't actually support,
1880
- * trading a clear `LLMError('validation')` for a confusing error from the
1881
- * provider instead. So this is opt-in: pass the model IDs you've verified
1882
- * against the provider's own docs (or a predicate). Left unset, no model
1883
- * is treated as native-capable, `jsonSchema` keeps using the older
1884
- * forced-single-tool-call emulation, and `tools` + `jsonSchema` together
1885
- * is rejected, exactly this package's behavior before native support was
1886
- * added.
2317
+ * trading a clear `LLMError('invalid_params')` with
2318
+ * `code: 'unsupported_capability'` for a confusing error from the provider
2319
+ * instead. So this is opt-in: pass the model IDs you've verified against the
2320
+ * provider's own docs (or a predicate). Left unset, no model is treated as
2321
+ * native-capable, `jsonSchema` keeps using the older forced-single-tool-call
2322
+ * emulation, and combining it with `tools` throws the coded capability error,
2323
+ * exactly this package's behavior before native support was added.
1887
2324
  */
1888
2325
  type ModelCapabilityOverride = string[] | ((model: string) => boolean);
1889
2326
 
@@ -2083,7 +2520,8 @@ interface AnthropicAdapterOptions {
2083
2520
  * forwarded when set, and `tool_choice` forces the model to call it. This
2084
2521
  * legacy path cannot be combined with real `tools` (both would need the
2085
2522
  * same `tools`/`tool_choice` field), and a call that tries throws
2086
- * `LLMError('validation')` before reaching the API. Provider-constrained
2523
+ * `LLMError('invalid_params')` with `code: 'unsupported_capability'` and
2524
+ * `issues: { capability: 'tools_with_json_schema' }` before reaching the API. Provider-constrained
2087
2525
  * schema matching applies only when `strict: true` is forwarded and
2088
2526
  * supported.
2089
2527
  *
@@ -2115,11 +2553,13 @@ type GeminiPart = {
2115
2553
  };
2116
2554
  } | {
2117
2555
  functionCall: {
2556
+ id?: string;
2118
2557
  name: string;
2119
2558
  args: Record<string, unknown>;
2120
2559
  };
2121
2560
  } | {
2122
2561
  functionResponse: {
2562
+ id?: string;
2123
2563
  name: string;
2124
2564
  response: Record<string, unknown>;
2125
2565
  };
@@ -2204,6 +2644,7 @@ interface GeminiClient {
2204
2644
  parts?: Array<{
2205
2645
  text?: string;
2206
2646
  functionCall?: {
2647
+ id?: string;
2207
2648
  name?: string;
2208
2649
  args?: unknown;
2209
2650
  };
@@ -2231,6 +2672,7 @@ interface GeminiClient {
2231
2672
  parts?: Array<{
2232
2673
  text?: string;
2233
2674
  functionCall?: {
2675
+ id?: string;
2234
2676
  name?: string;
2235
2677
  args?: unknown;
2236
2678
  };
@@ -2540,7 +2982,7 @@ type BedrockConverseStreamEvent = {
2540
2982
  interface BedrockAdapterOptions {
2541
2983
  /**
2542
2984
  * Optional preflight check for tool-use support, needed whenever a
2543
- * `jsonSchema` call ends up sending Converse `toolConfig` either the
2985
+ * `jsonSchema` call ends up sending Converse `toolConfig`, either the
2544
2986
  * legacy forced-single-tool-call emulation, or real `tools` sent
2545
2987
  * alongside native structured output (`outputConfig`). VernLLM never
2546
2988
  * guesses capability from a failed call's error message (AWS's error
@@ -2631,7 +3073,8 @@ interface AwsSendClient {
2631
3073
  * the schema, description, and strictness settings, and `toolChoice`
2632
3074
  * forces the model to call it. This legacy path cannot be combined with
2633
3075
  * real `tools` (both would need the same `toolConfig`), and a call that
2634
- * tries throws `LLMError('validation')` before reaching the API.
3076
+ * tries throws `LLMError('invalid_params')` with `code: 'unsupported_capability'`
3077
+ * and `issues: { capability: 'tools_with_json_schema' }` before reaching the API.
2635
3078
  * Provider-constrained schema matching applies only when `strict: true` is
2636
3079
  * forwarded and supported. Native tool support varies by model family;
2637
3080
  * pass `toolUseSupportedModels` to preflight-check it (see
@@ -2994,5 +3437,5 @@ declare const from01AI: typeof fromOpenAICompatible;
2994
3437
  //#endregion
2995
3438
  //# sourceMappingURL=openaiCompatible.d.ts.map
2996
3439
 
2997
- export { AnthropicClient, AssistantContent, BedrockConverseClient, CacheAdapter, CachedCallParams, CachedConditionalToolCallParams, CachedJsonModeDisabledCallParams, CachedJsonModeEnabledCallParams, CachedStreamCallParams, CachedStreamConditionalToolCallParams, CachedStreamJsonModeDisabledCallParams, CachedStreamJsonModeEnabledCallParams, CachedStreamToolCallParams, CachedToolCallParams, CallMeta, CallParams, CallWithToolsResult, CircuitBreaker, CircuitBreakerOptions, CircuitState, CircuitTarget, ConditionalToolCallParams, ConsoleLogger, ContentBlock, ContentResult, ConversationTurn, DuplicateToolNamesIssue, FallbackAttempt, FallbackExhaustedError, FallbackOn, FallbackTarget, FetchAdapterConfig, GeminiClient, HistoryToolResultIssue, ImageBlock, InMemoryCacheAdapter, JsonModeDisabledCallParams, JsonModeEnabledCallParams, JsonSchemaSpec, JsonValue, LLMClient, LLMError, LLMErrorCode, LLMErrorIssuesByCode, LLMErrorSnapshot, LLMErrorType, LLMRequestSnapshot, Logger, NormalizedCacheAdapter, OnEvent, OnUsage, RateLimitAcquireResult, RateLimitOptions, RateLimitReason, RateLimiter, RefundUsage, ReserveUsage, RetryAttempt, SSE_PING, SchemaLike, StreamCallResult, StreamChunk, StreamEnabledCallParams, StreamJsonModeDisabledCallParams, StreamJsonModeEnabledCallParams, TargetCircuitState, TextBlock, TieredCacheAdapter, TokenUsage, ToolCall, ToolCallResult, ToolChoice, ToolDefinition, ToolEnabledCallParams, ToolIssue, ToolResult, ToolsDisabledCallParams, UnknownToolChoiceIssue, UnsupportedCapabilityIssue, VernLLM, VernLLMEvent, VernLLMOptions, WireMessage, WireRequest, WireStreamChunk, WireToolCall, WireToolChoice, defaultEstimateTokens, defaultFallbackOn, defineCachedCallParams, defineCallParams, from01AI, fromAnthropic, fromAnyscale, fromAtlasCloud, fromBaseten, fromBedrock, fromCerebras, fromCloudflareWorkersAI, fromDeepInfra, fromDeepSeek, fromFeatherless, fromFetch, fromFireworks, fromFriendli, fromGemini, fromGroq, fromHyperbolic, fromInferenceNet, fromInfermatic, fromLMStudio, fromLambdaLabs, fromLepton, fromMiniMax, fromMistral, fromMoonshot, fromNebius, fromNovita, fromNvidiaNIM, fromOllama, fromOpenAI, fromOpenAICompatible, fromOpenRouter, fromParasail, fromPerplexity, fromSambaNova, fromSiliconFlow, fromSnowflakeCortex, fromStepFun, fromTogether, fromVLLM, fromVercelAIGateway, fromXAI, fromZhipu, hasIssues, isFallbackExhaustedError, isLLMError, isToolCallResult, parseSseStream };
3440
+ export { AnthropicClient, AssistantContent, AttemptContext, BedrockConverseClient, CacheAdapter, CachedCallParams, CachedConditionalToolCallParams, CachedJsonModeDisabledCallParams, CachedJsonModeEnabledCallParams, CachedStreamCallParams, CachedStreamConditionalToolCallParams, CachedStreamJsonModeDisabledCallParams, CachedStreamJsonModeEnabledCallParams, CachedStreamToolCallParams, CachedToolCallParams, CallMeta, CallParams, CallResult, CallWithToolsResult, CircuitBreaker, CircuitBreakerOptions, CircuitState, CircuitTarget, ConditionalToolCallParams, ConsoleLogger, ContentBlock, ContentResult, ConversationTurn, CreateMiddlewareOptions, DuplicateToolNamesIssue, FallbackAttempt, FallbackExhaustedError, FallbackOn, FallbackTarget, FetchAdapterConfig, GeminiClient, HistoryToolResultIssue, ImageBlock, InMemoryCacheAdapter, JsonModeDisabledCallParams, JsonModeEnabledCallParams, JsonSchemaSpec, JsonValue, LLMClient, LLMError, LLMErrorCode, LLMErrorIssuesByCode, LLMErrorSnapshot, LLMErrorType, LLMRequestShape, LLMRequestSnapshot, Logger, MiddlewareCapabilities, MiddlewareContext, MiddlewareContextBase, MiddlewareStateBag, MiddlewareStateKey, NormalizedCacheAdapter, OnEvent, OnUsage, PreDispatchContext, RateLimitAcquireResult, RateLimitOptions, RateLimitReason, RateLimiter, RefundUsage, ReserveUsage, RetryAttempt, SSE_PING, SchemaLike, StreamCallResult, StreamChunk, StreamEnabledCallParams, StreamJsonModeDisabledCallParams, StreamJsonModeEnabledCallParams, TargetCircuitState, TextBlock, TieredCacheAdapter, TokenUsage, ToolCall, ToolCallResult, ToolChoice, ToolDefinition, ToolEnabledCallParams, ToolIssue, ToolResult, ToolsDisabledCallParams, UnknownToolChoiceIssue, UnsupportedCapabilityIssue, VernLLM, VernLLMEvent, VernLLMMiddleware, VernLLMOptions, WireCallRequest, WireCallRequestPatch, WireMessage, WireRequest, WireResponseFormat, WireStreamChunk, WireTool, WireToolCall, WireToolChoice, createMiddleware, createMiddlewareStateBag, createStateKey, defaultEstimateTokens, defaultFallbackOn, defineCachedCallParams, defineCallParams, defineTool, from01AI, fromAnthropic, fromAnyscale, fromAtlasCloud, fromBaseten, fromBedrock, fromCerebras, fromCloudflareWorkersAI, fromDeepInfra, fromDeepSeek, fromFeatherless, fromFetch, fromFireworks, fromFriendli, fromGemini, fromGroq, fromHyperbolic, fromInferenceNet, fromInfermatic, fromLMStudio, fromLambdaLabs, fromLepton, fromMiniMax, fromMistral, fromMoonshot, fromNebius, fromNovita, fromNvidiaNIM, fromOllama, fromOpenAI, fromOpenAICompatible, fromOpenRouter, fromParasail, fromPerplexity, fromSambaNova, fromSiliconFlow, fromSnowflakeCortex, fromStepFun, fromTogether, fromVLLM, fromVercelAIGateway, fromXAI, fromZhipu, hasIssues, isFallbackExhaustedError, isLLMError, isToolCallResult, metaRef, parseSseStream };
2998
3441
  //# sourceMappingURL=index.d.cts.map