@broberg/ai-sdk 0.10.4 → 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
@@ -715,9 +715,13 @@ declare const visionInputSchema: z.ZodObject<{
715
715
  image: z.ZodUnion<[z.ZodString, z.ZodType<Uint8Array<ArrayBuffer>, z.ZodTypeDef, Uint8Array<ArrayBuffer>>]>;
716
716
  prompt: z.ZodString;
717
717
  mimeType: z.ZodOptional<z.ZodString>;
718
+ /** System instruction — drives instruction-following far better than stuffing
719
+ * rules into `prompt` for instruction-heavy vision tasks (e.g. a JSON critic). */
720
+ system: z.ZodOptional<z.ZodString>;
718
721
  }, "strip", z.ZodTypeAny, {
719
722
  image: string | Uint8Array<ArrayBuffer>;
720
723
  prompt: string;
724
+ system?: string | undefined;
721
725
  mimeType?: string | undefined;
722
726
  purpose?: string | undefined;
723
727
  tier?: "fast" | "smart" | "powerful" | "cheap" | "vision" | "video" | "embedding" | undefined;
@@ -735,6 +739,7 @@ declare const visionInputSchema: z.ZodObject<{
735
739
  }, {
736
740
  image: string | Uint8Array<ArrayBuffer>;
737
741
  prompt: string;
742
+ system?: string | undefined;
738
743
  mimeType?: string | undefined;
739
744
  purpose?: string | undefined;
740
745
  tier?: "fast" | "smart" | "powerful" | "cheap" | "vision" | "video" | "embedding" | undefined;
@@ -783,9 +788,12 @@ declare const videoInputSchema: z.ZodObject<{
783
788
  video: z.ZodUnion<[z.ZodString, z.ZodType<Uint8Array<ArrayBuffer>, z.ZodTypeDef, Uint8Array<ArrayBuffer>>]>;
784
789
  prompt: z.ZodString;
785
790
  mimeType: z.ZodOptional<z.ZodString>;
791
+ /** System instruction — same instruction-following benefit as on vision. */
792
+ system: z.ZodOptional<z.ZodString>;
786
793
  }, "strip", z.ZodTypeAny, {
787
794
  video: string | Uint8Array<ArrayBuffer>;
788
795
  prompt: string;
796
+ system?: string | undefined;
789
797
  mimeType?: string | undefined;
790
798
  purpose?: string | undefined;
791
799
  tier?: "fast" | "smart" | "powerful" | "cheap" | "vision" | "video" | "embedding" | undefined;
@@ -803,6 +811,7 @@ declare const videoInputSchema: z.ZodObject<{
803
811
  }, {
804
812
  video: string | Uint8Array<ArrayBuffer>;
805
813
  prompt: string;
814
+ system?: string | undefined;
806
815
  mimeType?: string | undefined;
807
816
  purpose?: string | undefined;
808
817
  tier?: "fast" | "smart" | "powerful" | "cheap" | "vision" | "video" | "embedding" | undefined;
@@ -1492,6 +1501,16 @@ declare const aiConfigSchema: z.ZodObject<{
1492
1501
  perCallUsd?: number | undefined;
1493
1502
  rollingUsd?: number | undefined;
1494
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
+ }>>;
1495
1514
  }, "strip", z.ZodTypeAny, {
1496
1515
  defaults?: Partial<Record<"fast" | "smart" | "powerful" | "cheap" | "vision" | "video" | "embedding", {
1497
1516
  provider: string;
@@ -1504,6 +1523,10 @@ declare const aiConfigSchema: z.ZodObject<{
1504
1523
  perCallUsd?: number | undefined;
1505
1524
  rollingUsd?: number | undefined;
1506
1525
  } | undefined;
1526
+ availability?: {
1527
+ fallback?: string | string[] | undefined;
1528
+ autoResolve?: boolean | undefined;
1529
+ } | undefined;
1507
1530
  }, {
1508
1531
  defaults?: Partial<Record<"fast" | "smart" | "powerful" | "cheap" | "vision" | "video" | "embedding", {
1509
1532
  provider: string;
@@ -1516,6 +1539,10 @@ declare const aiConfigSchema: z.ZodObject<{
1516
1539
  perCallUsd?: number | undefined;
1517
1540
  rollingUsd?: number | undefined;
1518
1541
  } | undefined;
1542
+ availability?: {
1543
+ fallback?: string | string[] | undefined;
1544
+ autoResolve?: boolean | undefined;
1545
+ } | undefined;
1519
1546
  }>;
1520
1547
  type ChatInput = z.infer<typeof chatInputSchema>;
1521
1548
  type VisionInput = z.infer<typeof visionInputSchema>;
@@ -1687,8 +1714,8 @@ declare const falStubAdapter: ProviderAdapter;
1687
1714
  * wires the live adapters. */
1688
1715
  declare const stubProviders: Record<string, ProviderAdapter>;
1689
1716
 
1690
- declare const VERSION: "0.10.4";
1691
- declare const SDK_TAG: "@broberg/ai-sdk@0.10.4";
1717
+ declare const VERSION: "0.11.0";
1718
+ declare const SDK_TAG: "@broberg/ai-sdk@0.11.0";
1692
1719
 
1693
1720
  /** Built-in defaults. Every entry is overridable via AiConfig.defaults or a
1694
1721
  * per-call override. Model IDs are current at scaffold time; callers pin their
@@ -1704,6 +1731,104 @@ declare const DEFAULT_TIER_MAP: Record<Tier, TierSpec>;
1704
1731
  */
1705
1732
  declare function resolveTier(tier: Tier, override?: Partial<TierSpec>, configMap?: Partial<Record<Tier, TierSpec>>): TierSpec;
1706
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
+
1707
1832
  /**
1708
1833
  * Cost in USD for a call. cache-read/creation tokens are priced separately when
1709
1834
  * the pricing entry defines rates for them; otherwise they fall back to the
@@ -1881,4 +2006,4 @@ interface StreamTransportRequest extends TransportRequest {
1881
2006
  */
1882
2007
  declare function streamTransport(req: StreamTransportRequest): AsyncIterable<string>;
1883
2008
 
1884
- 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) {
@@ -638,7 +770,7 @@ function makeOpenAICompatibleAdapter(config) {
638
770
  }
639
771
  const data = res.json;
640
772
  const msg = data.choices?.[0]?.message;
641
- const text = msg?.content ?? "";
773
+ const text = contentToText(msg?.content);
642
774
  const toolCalls = msg?.tool_calls?.map(
643
775
  (tc) => fromProviderToolCall(tc, "openai")
644
776
  );
@@ -745,6 +877,17 @@ function makeOpenAICompatibleAdapter(config) {
745
877
  vision: chat
746
878
  };
747
879
  }
880
+ function contentToText(content) {
881
+ if (typeof content === "string") return content;
882
+ if (Array.isArray(content)) {
883
+ return content.map((p) => {
884
+ if (typeof p === "string") return p;
885
+ const t = p?.text;
886
+ return typeof t === "string" ? t : "";
887
+ }).join("");
888
+ }
889
+ return "";
890
+ }
748
891
  function mapFinishReason(reason) {
749
892
  switch (reason) {
750
893
  case "tool_calls":
@@ -1618,29 +1761,31 @@ var BudgetGuard = class {
1618
1761
  // src/capabilities/vision.ts
1619
1762
  var VISION_DEFAULT_TIER = "vision";
1620
1763
  function buildVisionMessages(input) {
1621
- return [
1622
- {
1623
- role: "user",
1624
- content: [
1625
- { type: "text", text: input.prompt },
1626
- { type: "image", image: input.image, mimeType: input.mimeType }
1627
- ]
1628
- }
1629
- ];
1764
+ const messages = [];
1765
+ if (input.system) messages.push({ role: "system", content: input.system });
1766
+ messages.push({
1767
+ role: "user",
1768
+ content: [
1769
+ { type: "text", text: input.prompt },
1770
+ { type: "image", image: input.image, mimeType: input.mimeType }
1771
+ ]
1772
+ });
1773
+ return messages;
1630
1774
  }
1631
1775
 
1632
1776
  // src/capabilities/video.ts
1633
1777
  var VIDEO_DEFAULT_TIER = "video";
1634
1778
  function buildVideoMessages(input) {
1635
- return [
1636
- {
1637
- role: "user",
1638
- content: [
1639
- { type: "video", video: input.video, mimeType: input.mimeType },
1640
- { type: "text", text: input.prompt }
1641
- ]
1642
- }
1643
- ];
1779
+ const messages = [];
1780
+ if (input.system) messages.push({ role: "system", content: input.system });
1781
+ messages.push({
1782
+ role: "user",
1783
+ content: [
1784
+ { type: "video", video: input.video, mimeType: input.mimeType },
1785
+ { type: "text", text: input.prompt }
1786
+ ]
1787
+ });
1788
+ return messages;
1644
1789
  }
1645
1790
 
1646
1791
  // src/capabilities/translate.ts
@@ -1834,12 +1979,17 @@ var visionInputSchema = z.object({
1834
1979
  image: z.union([z.string(), z.instanceof(Uint8Array)]),
1835
1980
  prompt: z.string(),
1836
1981
  mimeType: z.string().optional(),
1982
+ /** System instruction — drives instruction-following far better than stuffing
1983
+ * rules into `prompt` for instruction-heavy vision tasks (e.g. a JSON critic). */
1984
+ system: z.string().optional(),
1837
1985
  ...callOptions
1838
1986
  });
1839
1987
  var videoInputSchema = z.object({
1840
1988
  video: z.union([z.string(), z.instanceof(Uint8Array)]),
1841
1989
  prompt: z.string(),
1842
1990
  mimeType: z.string().optional(),
1991
+ /** System instruction — same instruction-following benefit as on vision. */
1992
+ system: z.string().optional(),
1843
1993
  ...callOptions
1844
1994
  });
1845
1995
  var translateInputSchema = z.object({
@@ -1912,13 +2062,18 @@ var budgetSchema = z.object({
1912
2062
  perCallUsd: z.number().positive().optional(),
1913
2063
  rollingUsd: z.number().positive().optional()
1914
2064
  });
2065
+ var availabilitySchema = z.object({
2066
+ autoResolve: z.boolean().optional(),
2067
+ fallback: z.union([z.string(), z.array(z.string())]).optional()
2068
+ });
1915
2069
  var aiConfigSchema = z.object({
1916
2070
  defaults: z.record(tierSchema, tierSpecSchema).optional(),
1917
2071
  // Functions can't be deeply validated — z.custom asserts the TS type and
1918
2072
  // passes the value through untouched.
1919
2073
  providers: z.record(z.string(), z.custom()).optional(),
1920
2074
  costSink: z.custom().optional(),
1921
- budget: budgetSchema.optional()
2075
+ budget: budgetSchema.optional(),
2076
+ availability: availabilitySchema.optional()
1922
2077
  });
1923
2078
 
1924
2079
  // src/client.ts
@@ -1991,9 +2146,14 @@ function createAI(config = {}) {
1991
2146
  msgs.push({ role: "user", content: input.prompt ?? "" });
1992
2147
  return msgs;
1993
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
+ }
1994
2154
  async function runCapability(opts) {
1995
2155
  const routes = [
1996
- opts.primary,
2156
+ applyAvailability(opts.primary),
1997
2157
  ...(opts.fallback ?? []).map(
1998
2158
  (f) => typeof f === "string" ? resolveTier(f, void 0, cfg.defaults) : f
1999
2159
  )
@@ -2452,8 +2612,53 @@ var stubProviders = {
2452
2612
  };
2453
2613
 
2454
2614
  // src/version.ts
2455
- var VERSION = "0.10.4";
2456
- var SDK_TAG = "@broberg/ai-sdk@0.10.4";
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
+ }
2457
2662
 
2458
2663
  // src/cost/budget-store.ts
2459
2664
  function sqliteBudgetStore(config) {
@@ -2686,6 +2891,7 @@ export {
2686
2891
  BudgetGuard,
2687
2892
  DEFAULT_TIER_MAP,
2688
2893
  ELEVENLABS_DANISH_VOICES,
2894
+ ModelUnavailableError,
2689
2895
  SDK_TAG,
2690
2896
  StreamHttpError,
2691
2897
  VERSION,
@@ -2710,6 +2916,7 @@ export {
2710
2916
  getPrice,
2711
2917
  httpTransport,
2712
2918
  imageInputSchema,
2919
+ listModels,
2713
2920
  makeContracts,
2714
2921
  makeOpenAICompatibleAdapter,
2715
2922
  messageSchema,
@@ -2721,6 +2928,10 @@ export {
2721
2928
  openrouterAdapter,
2722
2929
  parseClaudeCliJson,
2723
2930
  parseJsonLoose,
2931
+ refreshAvailability,
2932
+ resetRefreshState,
2933
+ resetRegistry,
2934
+ resolveModel,
2724
2935
  resolveTier,
2725
2936
  resolveVoice,
2726
2937
  sqliteBudgetStore,