@broberg/ai-sdk 0.10.5 → 0.11.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
@@ -1501,6 +1501,16 @@ declare const aiConfigSchema: z.ZodObject<{
1501
1501
  perCallUsd?: number | undefined;
1502
1502
  rollingUsd?: number | undefined;
1503
1503
  }>>;
1504
+ availability: z.ZodOptional<z.ZodObject<{
1505
+ autoResolve: z.ZodOptional<z.ZodBoolean>;
1506
+ fallback: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodArray<z.ZodString, "many">]>>;
1507
+ }, "strip", z.ZodTypeAny, {
1508
+ fallback?: string | string[] | undefined;
1509
+ autoResolve?: boolean | undefined;
1510
+ }, {
1511
+ fallback?: string | string[] | undefined;
1512
+ autoResolve?: boolean | undefined;
1513
+ }>>;
1504
1514
  }, "strip", z.ZodTypeAny, {
1505
1515
  defaults?: Partial<Record<"fast" | "smart" | "powerful" | "cheap" | "vision" | "video" | "embedding", {
1506
1516
  provider: string;
@@ -1513,6 +1523,10 @@ declare const aiConfigSchema: z.ZodObject<{
1513
1523
  perCallUsd?: number | undefined;
1514
1524
  rollingUsd?: number | undefined;
1515
1525
  } | undefined;
1526
+ availability?: {
1527
+ fallback?: string | string[] | undefined;
1528
+ autoResolve?: boolean | undefined;
1529
+ } | undefined;
1516
1530
  }, {
1517
1531
  defaults?: Partial<Record<"fast" | "smart" | "powerful" | "cheap" | "vision" | "video" | "embedding", {
1518
1532
  provider: string;
@@ -1525,6 +1539,10 @@ declare const aiConfigSchema: z.ZodObject<{
1525
1539
  perCallUsd?: number | undefined;
1526
1540
  rollingUsd?: number | undefined;
1527
1541
  } | undefined;
1542
+ availability?: {
1543
+ fallback?: string | string[] | undefined;
1544
+ autoResolve?: boolean | undefined;
1545
+ } | undefined;
1528
1546
  }>;
1529
1547
  type ChatInput = z.infer<typeof chatInputSchema>;
1530
1548
  type VisionInput = z.infer<typeof visionInputSchema>;
@@ -1696,8 +1714,8 @@ declare const falStubAdapter: ProviderAdapter;
1696
1714
  * wires the live adapters. */
1697
1715
  declare const stubProviders: Record<string, ProviderAdapter>;
1698
1716
 
1699
- declare const VERSION: "0.10.5";
1700
- declare const SDK_TAG: "@broberg/ai-sdk@0.10.5";
1717
+ declare const VERSION: "0.11.0";
1718
+ declare const SDK_TAG: "@broberg/ai-sdk@0.11.0";
1701
1719
 
1702
1720
  /** Built-in defaults. Every entry is overridable via AiConfig.defaults or a
1703
1721
  * per-call override. Model IDs are current at scaffold time; callers pin their
@@ -1713,6 +1731,104 @@ declare const DEFAULT_TIER_MAP: Record<Tier, TierSpec>;
1713
1731
  */
1714
1732
  declare function resolveTier(tier: Tier, override?: Partial<TierSpec>, configMap?: Partial<Record<Tier, TierSpec>>): TierSpec;
1715
1733
 
1734
+ type AvailabilityStatus = "available" | "suspended" | "unknown";
1735
+ /** Where a model's current availability came from: the curated default seed,
1736
+ * or a live provider refresh (Anthropic GET /v1/models). */
1737
+ type AvailabilitySource = "default" | "refresh";
1738
+ /** One row of the shared status read — what a UI model-picker renders. */
1739
+ interface ModelStatus {
1740
+ /** Canonical provider model id, e.g. "claude-fable-5". */
1741
+ id: string;
1742
+ /** Short/tier alias, e.g. "fable" (the first registered alias). */
1743
+ alias?: string;
1744
+ /** "anthropic" | "openai" | "gemini" | "mistral" | … */
1745
+ provider: string;
1746
+ available: boolean;
1747
+ status: AvailabilityStatus;
1748
+ /** Friendly reason, e.g. "suspended — US export-control directive (2026-06-12)". */
1749
+ note?: string;
1750
+ source: AvailabilitySource;
1751
+ }
1752
+ /** Result of resolveModel — the spawn / call path consumes this synchronously. */
1753
+ interface ResolveResult {
1754
+ /** True when the requested model itself is available. */
1755
+ ok: boolean;
1756
+ /** The id to actually use: `requested` when ok, else the first available fallback. */
1757
+ model: string;
1758
+ /** What the caller asked for (id or alias, normalized to the canonical id). */
1759
+ requested: string;
1760
+ provider?: string;
1761
+ /** True when `model` differs from `requested` because we fell back. */
1762
+ fellBack: boolean;
1763
+ status: AvailabilityStatus;
1764
+ /** Why it degraded / why it is unavailable. */
1765
+ reason?: string;
1766
+ }
1767
+ /** Thrown by resolveModel when the requested model is unavailable, no usable
1768
+ * fallback exists, and the caller passed `throwIfUnavailable`. Callers flag on
1769
+ * `.code === "model_unavailable"`. */
1770
+ declare class ModelUnavailableError extends Error {
1771
+ readonly code = "model_unavailable";
1772
+ readonly requested: string;
1773
+ readonly provider?: string;
1774
+ readonly note?: string;
1775
+ constructor(requested: string, note?: string, provider?: string);
1776
+ }
1777
+
1778
+ interface ResolveOptions {
1779
+ /** One id/alias or an ordered chain to try when `requested` is unavailable. */
1780
+ fallback?: string | string[];
1781
+ /** Scope hint (passed through to the result); does not gate lookup. */
1782
+ provider?: string;
1783
+ /** Throw ModelUnavailableError instead of returning ok:false when there is no
1784
+ * usable fallback. For callers that want to flag rather than degrade. */
1785
+ throwIfUnavailable?: boolean;
1786
+ }
1787
+ /** The shared status read — UI pickers grey out `available:false` rows. */
1788
+ declare function listModels(opts?: {
1789
+ provider?: string;
1790
+ }): ModelStatus[];
1791
+ /**
1792
+ * Resolve a requested model (id or alias) to one that is actually usable.
1793
+ * Synchronous + offline by contract (cardmem #4842) — reads the registry only.
1794
+ *
1795
+ * - Available → pass through ({ ok:true, fellBack:false }).
1796
+ * - Unavailable + a fallback that IS available → swap ({ ok:false, fellBack:true }).
1797
+ * - Unavailable + no usable fallback → throw (throwIfUnavailable) or return ok:false.
1798
+ * - Unknown id → treated available (never block a model we do not track).
1799
+ */
1800
+ declare function resolveModel(requested: string, opts?: ResolveOptions): ResolveResult;
1801
+
1802
+ interface RefreshOptions {
1803
+ /** Only "anthropic" is wired in v1 (where the incident hit). */
1804
+ provider?: "anthropic";
1805
+ /** Injectable for tests. Defaults to global fetch. */
1806
+ fetch?: typeof fetch;
1807
+ /** Defaults to process.env.ANTHROPIC_API_KEY. */
1808
+ apiKey?: string;
1809
+ /** Min ms between live fetches for a provider (default 1h). */
1810
+ ttlMs?: number;
1811
+ /** Injectable clock for deterministic TTL tests. Defaults to Date.now(). */
1812
+ now?: number;
1813
+ }
1814
+ interface RefreshResult {
1815
+ refreshed: boolean;
1816
+ checked: number;
1817
+ markedUnavailable: string[];
1818
+ }
1819
+ /** Reset the TTL bookkeeping. For tests. */
1820
+ declare function resetRefreshState(): void;
1821
+ /**
1822
+ * Reconcile tracked models against the provider's live list. For Anthropic:
1823
+ * GET /v1/models — any tracked anthropic id NOT in the live set is marked
1824
+ * unavailable; ids present are (re)marked available. TTL-cached; a fetch within
1825
+ * the window is a no-op. Returns { refreshed:false } on any error or missing key.
1826
+ */
1827
+ declare function refreshAvailability(opts?: RefreshOptions): Promise<RefreshResult>;
1828
+
1829
+ /** Reset the overlay back to the curated defaults. For tests. */
1830
+ declare function resetRegistry(): void;
1831
+
1716
1832
  /**
1717
1833
  * Cost in USD for a call. cache-read/creation tokens are priced separately when
1718
1834
  * the pricing entry defines rates for them; otherwise they fall back to the
@@ -1890,4 +2006,4 @@ interface StreamTransportRequest extends TransportRequest {
1890
2006
  */
1891
2007
  declare function streamTransport(req: StreamTransportRequest): AsyncIterable<string>;
1892
2008
 
1893
- export { type AiClient, type AiConfig, type BatchJob, type BatchRequestItem, type BatchResultItem, type BudgetConfig, BudgetExceededError, BudgetGuard, type BudgetStore, type CallOptions, type Capability, type ChatInput, type ChatRequest, type ChatResult, type ChatStreamEvent, type ClassifyInput, type ClassifyResult, type ContentPart, type Contracts, type CostSink, type CostSummary, DEFAULT_TIER_MAP, type DesignInput, type DesignResult, type DialogueRequest, type DialogueTurn, type DiscordSinkConfig, ELEVENLABS_DANISH_VOICES, type EmbeddingInput, type EmbeddingRequest, type EmbeddingResult, type ExtractInput, type ExtractResult, type FalAdapterConfig, type HttpResponse, type ImageInput, type ImageRequest, type ImageResult, type LoraWeight, type Message, type MockupInput, type MockupResult, type ModerationInput, type ModerationItem, type ModerationRequest, type ModerationResult, type OcrInput, type OcrPage, type OcrRequest, type OcrResult, type OpenAICompatibleConfig, type PodcastInput, type PodcastResult, type PricingEntry, type ProviderAdapter, type RerankInput, type RerankResult, type Role, SDK_TAG, type SqliteBudgetStoreConfig, type SqliteSinkConfig, StreamHttpError, type SubprocessResponse, type Tier, type TierSpec, type Tool, type ToolCall, type TrainStyleInput, type TrainStyleRequest, type TrainStyleResult, type TranscribeInput, type TranscribeRequest, type TranscribeResult, type TranslateInput, type TranslateResult, type Transport, type TransportRequest, type TransportResponse, type TtsInput, type TtsRequest, type UpmetricsSinkConfig, type Usage, VERSION, type VideoInput, type VisionInput, aiConfigSchema, anthropicAdapter, anthropicApiAdapter, anthropicSubprocessAdapter, chatInputSchema, computeCost, createAI, deepinfraAdapter, defaultProviders, discordSink, elevenlabsAdapter, embeddingInputSchema, falAdapter, falStubAdapter, freshUsage, fromProviderToolCall, geminiAdapter, getCostSummary, getPrice, httpTransport, imageInputSchema, makeContracts, makeOpenAICompatibleAdapter, messageSchema, mistralAdapter, multiSink, noopSink, openaiAdapter, openaiStubAdapter, openrouterAdapter, parseClaudeCliJson, parseJsonLoose, resolveTier, resolveVoice, sqliteBudgetStore, sqliteSink, streamTransport, stubProviders, subprocessTransport, tierSpecSchema, toProviderTools, toolSchema, translateInputSchema, upmetricsSink, visionInputSchema };
2009
+ export { type AiClient, type AiConfig, type AvailabilitySource, type AvailabilityStatus, type BatchJob, type BatchRequestItem, type BatchResultItem, type BudgetConfig, BudgetExceededError, BudgetGuard, type BudgetStore, type CallOptions, type Capability, type ChatInput, type ChatRequest, type ChatResult, type ChatStreamEvent, type ClassifyInput, type ClassifyResult, type ContentPart, type Contracts, type CostSink, type CostSummary, DEFAULT_TIER_MAP, type DesignInput, type DesignResult, type DialogueRequest, type DialogueTurn, type DiscordSinkConfig, ELEVENLABS_DANISH_VOICES, type EmbeddingInput, type EmbeddingRequest, type EmbeddingResult, type ExtractInput, type ExtractResult, type FalAdapterConfig, type HttpResponse, type ImageInput, type ImageRequest, type ImageResult, type LoraWeight, type Message, type MockupInput, type MockupResult, type ModelStatus, ModelUnavailableError, type ModerationInput, type ModerationItem, type ModerationRequest, type ModerationResult, type OcrInput, type OcrPage, type OcrRequest, type OcrResult, type OpenAICompatibleConfig, type PodcastInput, type PodcastResult, type PricingEntry, type ProviderAdapter, type RefreshOptions, type RefreshResult, type RerankInput, type RerankResult, type ResolveOptions, type ResolveResult, type Role, SDK_TAG, type SqliteBudgetStoreConfig, type SqliteSinkConfig, StreamHttpError, type SubprocessResponse, type Tier, type TierSpec, type Tool, type ToolCall, type TrainStyleInput, type TrainStyleRequest, type TrainStyleResult, type TranscribeInput, type TranscribeRequest, type TranscribeResult, type TranslateInput, type TranslateResult, type Transport, type TransportRequest, type TransportResponse, type TtsInput, type TtsRequest, type UpmetricsSinkConfig, type Usage, VERSION, type VideoInput, type VisionInput, aiConfigSchema, anthropicAdapter, anthropicApiAdapter, anthropicSubprocessAdapter, chatInputSchema, computeCost, createAI, deepinfraAdapter, defaultProviders, discordSink, elevenlabsAdapter, embeddingInputSchema, falAdapter, falStubAdapter, freshUsage, fromProviderToolCall, geminiAdapter, getCostSummary, getPrice, httpTransport, imageInputSchema, listModels, makeContracts, makeOpenAICompatibleAdapter, messageSchema, mistralAdapter, multiSink, noopSink, openaiAdapter, openaiStubAdapter, openrouterAdapter, parseClaudeCliJson, parseJsonLoose, refreshAvailability, resetRefreshState, resetRegistry, resolveModel, resolveTier, resolveVoice, sqliteBudgetStore, sqliteSink, streamTransport, stubProviders, subprocessTransport, tierSpecSchema, toProviderTools, toolSchema, translateInputSchema, upmetricsSink, visionInputSchema };
package/dist/index.js CHANGED
@@ -14,6 +14,138 @@ function resolveTier(tier, override, configMap) {
14
14
  return { ...base, ...override };
15
15
  }
16
16
 
17
+ // src/availability/registry.ts
18
+ var SUSPENDED_FABLE_MYTHOS = "suspended \u2014 US export-control directive (2026-06-12)";
19
+ var DEFAULTS = [
20
+ // ── Anthropic ────────────────────────────────────────────────────────────
21
+ { id: "claude-haiku-4-5", aliases: ["haiku", "fast"], provider: "anthropic", available: true, status: "available", source: "default" },
22
+ { id: "claude-sonnet-4-6", aliases: ["sonnet", "smart"], provider: "anthropic", available: true, status: "available", source: "default" },
23
+ { id: "claude-opus-4-8", aliases: ["opus", "powerful"], provider: "anthropic", available: true, status: "available", source: "default" },
24
+ { id: "claude-fable-5", aliases: ["fable"], provider: "anthropic", available: false, status: "suspended", note: SUSPENDED_FABLE_MYTHOS, source: "default" },
25
+ { id: "claude-mythos-5", aliases: ["mythos"], provider: "anthropic", available: false, status: "suspended", note: SUSPENDED_FABLE_MYTHOS, source: "default" },
26
+ // ── Gemini ───────────────────────────────────────────────────────────────
27
+ { id: "gemini-2.5-flash", aliases: ["gemini-flash"], provider: "gemini", available: true, status: "available", source: "default" },
28
+ { id: "gemini-2.5-flash-lite", aliases: ["gemini-flash-lite", "video"], provider: "gemini", available: true, status: "available", source: "default" },
29
+ // ── OpenAI ───────────────────────────────────────────────────────────────
30
+ { id: "text-embedding-3-small", aliases: ["embedding"], provider: "openai", available: true, status: "available", source: "default" },
31
+ // ── Mistral (EU / GDPR) ──────────────────────────────────────────────────
32
+ { id: "mistral-large-latest", aliases: ["mistral-large"], provider: "mistral", available: true, status: "available", source: "default" },
33
+ { id: "mistral-small-latest", aliases: ["mistral-small"], provider: "mistral", available: true, status: "available", source: "default" }
34
+ ];
35
+ var OVERLAY = /* @__PURE__ */ new Map();
36
+ var ALIAS_INDEX = /* @__PURE__ */ new Map();
37
+ function seed() {
38
+ OVERLAY = new Map(DEFAULTS.map((e) => [e.id, { ...e, aliases: [...e.aliases] }]));
39
+ ALIAS_INDEX = /* @__PURE__ */ new Map();
40
+ for (const e of DEFAULTS) for (const a of e.aliases) ALIAS_INDEX.set(a, e.id);
41
+ }
42
+ seed();
43
+ function resetRegistry() {
44
+ seed();
45
+ }
46
+ function canonicalId(requested) {
47
+ if (OVERLAY.has(requested)) return requested;
48
+ return ALIAS_INDEX.get(requested) ?? null;
49
+ }
50
+ function getEntry(requested) {
51
+ const id = canonicalId(requested);
52
+ return id ? OVERLAY.get(id) : void 0;
53
+ }
54
+ function allEntries(provider) {
55
+ const rows = [];
56
+ for (const e of OVERLAY.values()) {
57
+ if (provider && e.provider !== provider) continue;
58
+ rows.push({
59
+ id: e.id,
60
+ alias: e.aliases[0],
61
+ provider: e.provider,
62
+ available: e.available,
63
+ status: e.status,
64
+ note: e.note,
65
+ source: e.source
66
+ });
67
+ }
68
+ return rows;
69
+ }
70
+ function providerIds(provider) {
71
+ return [...OVERLAY.values()].filter((e) => e.provider === provider).map((e) => e.id);
72
+ }
73
+ function setAvailability(id, available, note) {
74
+ const e = OVERLAY.get(id);
75
+ if (!e) return;
76
+ e.available = available;
77
+ e.status = available ? "available" : "suspended";
78
+ e.source = "refresh";
79
+ if (note !== void 0) e.note = note;
80
+ else if (available) e.note = void 0;
81
+ }
82
+
83
+ // src/availability/types.ts
84
+ var ModelUnavailableError = class extends Error {
85
+ code = "model_unavailable";
86
+ requested;
87
+ provider;
88
+ note;
89
+ constructor(requested, note, provider) {
90
+ super(`model "${requested}" is unavailable${note ? ` (${note})` : ""}`);
91
+ this.name = "ModelUnavailableError";
92
+ this.requested = requested;
93
+ this.note = note;
94
+ this.provider = provider;
95
+ }
96
+ };
97
+
98
+ // src/availability/resolve.ts
99
+ function listModels(opts = {}) {
100
+ return allEntries(opts.provider);
101
+ }
102
+ function isAvailable(requested) {
103
+ const e = getEntry(requested);
104
+ return e ? e.available : true;
105
+ }
106
+ function resolveModel(requested, opts = {}) {
107
+ const id = canonicalId(requested) ?? requested;
108
+ const entry = getEntry(requested);
109
+ const provider = opts.provider ?? entry?.provider;
110
+ if (isAvailable(requested)) {
111
+ return {
112
+ ok: true,
113
+ model: id,
114
+ requested: id,
115
+ provider,
116
+ fellBack: false,
117
+ status: entry?.status ?? "unknown"
118
+ };
119
+ }
120
+ const chain = opts.fallback === void 0 ? [] : Array.isArray(opts.fallback) ? opts.fallback : [opts.fallback];
121
+ for (const fb of chain) {
122
+ if (isAvailable(fb)) {
123
+ const fbId = canonicalId(fb) ?? fb;
124
+ return {
125
+ ok: false,
126
+ model: fbId,
127
+ requested: id,
128
+ provider,
129
+ fellBack: true,
130
+ status: entry?.status ?? "suspended",
131
+ reason: entry?.note ?? `${id} is unavailable`
132
+ };
133
+ }
134
+ }
135
+ if (opts.throwIfUnavailable) {
136
+ throw new ModelUnavailableError(id, entry?.note, provider);
137
+ }
138
+ return {
139
+ ok: false,
140
+ model: id,
141
+ requested: id,
142
+ provider,
143
+ fellBack: false,
144
+ status: entry?.status ?? "suspended",
145
+ reason: entry?.note ?? `${id} is unavailable`
146
+ };
147
+ }
148
+
17
149
  // src/transport/http.ts
18
150
  async function httpTransport(req) {
19
151
  if (!req.http) {
@@ -1930,13 +2062,18 @@ var budgetSchema = z.object({
1930
2062
  perCallUsd: z.number().positive().optional(),
1931
2063
  rollingUsd: z.number().positive().optional()
1932
2064
  });
2065
+ var availabilitySchema = z.object({
2066
+ autoResolve: z.boolean().optional(),
2067
+ fallback: z.union([z.string(), z.array(z.string())]).optional()
2068
+ });
1933
2069
  var aiConfigSchema = z.object({
1934
2070
  defaults: z.record(tierSchema, tierSpecSchema).optional(),
1935
2071
  // Functions can't be deeply validated — z.custom asserts the TS type and
1936
2072
  // passes the value through untouched.
1937
2073
  providers: z.record(z.string(), z.custom()).optional(),
1938
2074
  costSink: z.custom().optional(),
1939
- budget: budgetSchema.optional()
2075
+ budget: budgetSchema.optional(),
2076
+ availability: availabilitySchema.optional()
1940
2077
  });
1941
2078
 
1942
2079
  // src/client.ts
@@ -2009,9 +2146,14 @@ function createAI(config = {}) {
2009
2146
  msgs.push({ role: "user", content: input.prompt ?? "" });
2010
2147
  return msgs;
2011
2148
  }
2149
+ function applyAvailability(spec) {
2150
+ if (!cfg.availability?.autoResolve) return spec;
2151
+ const r = resolveModel(spec.model, { fallback: cfg.availability.fallback, provider: spec.provider });
2152
+ return r.fellBack ? { ...spec, model: r.model } : spec;
2153
+ }
2012
2154
  async function runCapability(opts) {
2013
2155
  const routes = [
2014
- opts.primary,
2156
+ applyAvailability(opts.primary),
2015
2157
  ...(opts.fallback ?? []).map(
2016
2158
  (f) => typeof f === "string" ? resolveTier(f, void 0, cfg.defaults) : f
2017
2159
  )
@@ -2470,8 +2612,53 @@ var stubProviders = {
2470
2612
  };
2471
2613
 
2472
2614
  // src/version.ts
2473
- var VERSION = "0.10.5";
2474
- var SDK_TAG = "@broberg/ai-sdk@0.10.5";
2615
+ var VERSION = "0.11.0";
2616
+ var SDK_TAG = "@broberg/ai-sdk@0.11.0";
2617
+
2618
+ // src/availability/refresh.ts
2619
+ var NOT_REFRESHED = { refreshed: false, checked: 0, markedUnavailable: [] };
2620
+ var DEFAULT_TTL_MS = 60 * 60 * 1e3;
2621
+ var ANTHROPIC_MODELS_URL = "https://api.anthropic.com/v1/models";
2622
+ var lastRefreshAt = /* @__PURE__ */ new Map();
2623
+ function resetRefreshState() {
2624
+ lastRefreshAt.clear();
2625
+ }
2626
+ async function refreshAvailability(opts = {}) {
2627
+ const provider = opts.provider ?? "anthropic";
2628
+ if (provider !== "anthropic") return NOT_REFRESHED;
2629
+ const ttl = opts.ttlMs ?? DEFAULT_TTL_MS;
2630
+ const now = opts.now ?? Date.now();
2631
+ const last = lastRefreshAt.get(provider);
2632
+ if (last !== void 0 && now - last < ttl) return NOT_REFRESHED;
2633
+ const apiKey = opts.apiKey ?? process.env.ANTHROPIC_API_KEY;
2634
+ if (!apiKey) return NOT_REFRESHED;
2635
+ const f = opts.fetch ?? fetch;
2636
+ let liveIds;
2637
+ try {
2638
+ const res = await f(ANTHROPIC_MODELS_URL, {
2639
+ headers: { "x-api-key": apiKey, "anthropic-version": "2023-06-01", accept: "application/json" }
2640
+ });
2641
+ if (!res.ok) return NOT_REFRESHED;
2642
+ const json = await res.json();
2643
+ liveIds = new Set((json.data ?? []).map((m) => m.id).filter((id) => typeof id === "string"));
2644
+ } catch {
2645
+ return NOT_REFRESHED;
2646
+ }
2647
+ if (liveIds.size === 0) return NOT_REFRESHED;
2648
+ const tracked = providerIds(provider);
2649
+ const markedUnavailable = [];
2650
+ for (const id of tracked) {
2651
+ const live = liveIds.has(id);
2652
+ if (live) {
2653
+ setAvailability(id, true);
2654
+ } else {
2655
+ setAvailability(id, false, "not in provider model list (live refresh)");
2656
+ markedUnavailable.push(id);
2657
+ }
2658
+ }
2659
+ lastRefreshAt.set(provider, now);
2660
+ return { refreshed: true, checked: tracked.length, markedUnavailable };
2661
+ }
2475
2662
 
2476
2663
  // src/cost/budget-store.ts
2477
2664
  function sqliteBudgetStore(config) {
@@ -2704,6 +2891,7 @@ export {
2704
2891
  BudgetGuard,
2705
2892
  DEFAULT_TIER_MAP,
2706
2893
  ELEVENLABS_DANISH_VOICES,
2894
+ ModelUnavailableError,
2707
2895
  SDK_TAG,
2708
2896
  StreamHttpError,
2709
2897
  VERSION,
@@ -2728,6 +2916,7 @@ export {
2728
2916
  getPrice,
2729
2917
  httpTransport,
2730
2918
  imageInputSchema,
2919
+ listModels,
2731
2920
  makeContracts,
2732
2921
  makeOpenAICompatibleAdapter,
2733
2922
  messageSchema,
@@ -2739,6 +2928,10 @@ export {
2739
2928
  openrouterAdapter,
2740
2929
  parseClaudeCliJson,
2741
2930
  parseJsonLoose,
2931
+ refreshAvailability,
2932
+ resetRefreshState,
2933
+ resetRegistry,
2934
+ resolveModel,
2742
2935
  resolveTier,
2743
2936
  resolveVoice,
2744
2937
  sqliteBudgetStore,