@broberg/ai-sdk 0.49.3 → 0.50.1

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.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/routing/tier-map.ts","../src/availability/registry.ts","../src/availability/types.ts","../src/availability/resolve.ts"],"sourcesContent":["// Tier routing: a named Tier resolves to a concrete (provider, model, transport).\n// Precedence is per-call override > client config map > built-in defaults.\nimport type { Tier, TierSpec } from \"../types.js\";\n\n/** Built-in defaults. Every entry is overridable via AiConfig.defaults or a\n * per-call override.\n *\n * F030 — Anthropic API phase-out: `ANTHROPIC_API_KEY` was globally removed, so the\n * default cloud route may NOT hit Anthropic Console. `fast`/`smart`/`powerful`/\n * `vision` now default to **Mistral EU** (Paris-hosted, Schrems II-safe — so every\n * default text/vision call is GDPR-safe by default). Claude stays reachable as a\n * NON-default quality fallback for non-PII via `override:{provider:\"openrouter\",\n * model:\"anthropic/claude-…\"}`. DeepSeek (CN) is the opt-in non-PII secondary\n * (`provider:\"deepseek\"`), never a default. Magistral (reasoning) / mistral-large\n * for vision are per-call overrides, not defaults (don't pay the premium on all). */\nexport const DEFAULT_TIER_MAP: Record<Tier, TierSpec> = {\n fast: { provider: \"mistral\", model: \"mistral-small-latest\", transport: \"http\" },\n smart: { provider: \"mistral\", model: \"mistral-large-latest\", transport: \"http\" },\n powerful: { provider: \"mistral\", model: \"mistral-large-latest\", transport: \"http\" },\n cheap: { provider: \"mistral\", model: \"mistral-small-latest\", transport: \"http\" },\n // Vision: small-latest (vision-capable, cheap EU) is the default; override to\n // mistral-large-latest for demanding image/spatial/composition work.\n // F041 — bumped from mistral-small on Christian's ask, and the choice is MEASURED,\n // not assumed from price. On a fine-discrimination test (an 8x10 grid where one cell\n // differs only in its blue channel, 190->150) across 9 cases:\n // mistral-medium-latest 4/9 <- best\n // mistral-small-latest 1/6 + 0/3 = 1/9\n // mistral-large-latest 0/9 <- WORSE than small, despite costing 5x more\n // Large ties small on easy colour blocks (4/4 each) and collapses on subtle ones,\n // so \"bigger is better at vision\" does not hold in Mistral's lineup. Nobody is good\n // at this task; medium is simply the only one that sees anything.\n vision: { provider: \"mistral\", model: \"mistral-medium-latest\", transport: \"http\" },\n // Native video understanding — Gemini leads; flash-lite is the cheap default (F019).\n // NOT Anthropic → out of the F030 phase-out (its own EU epic if/when needed).\n video: { provider: \"gemini\", model: \"gemini-2.5-flash-lite\", transport: \"http\" },\n // NOT Anthropic → out of F030 (EU-embedding migration is its own future epic).\n embedding: { provider: \"openai\", model: \"text-embedding-3-small\", transport: \"http\" },\n};\n\n/**\n * Resolve a Tier to a concrete TierSpec.\n *\n * Merge order (later wins): DEFAULT_TIER_MAP < configMap < override.\n * - `configMap` is the client-level AiConfig.defaults (per-tier full specs).\n * - `override` is a per-call Partial<TierSpec> — only the fields it sets win.\n */\n/** Refuse an override that names a different provider without naming a model (F043.2).\n *\n * Exported and called at EVERY spec merge, not only inside resolveTier. The first\n * version guarded only the six capabilities that route via a tier; image, animate,\n * trainStyle, ocr, moderate, podcast, tts, transcribe and batch merge the override\n * themselves and skipped it entirely — so `ai.image({override:{provider:\"fal\"}})`\n * still posted BFL's flux-2-pro to fal, the exact misleading upstream error this was\n * written to kill. A guard at six of fifteen call sites is a guard you cannot rely on.\n *\n * We REFUSE rather than re-resolve: choosing a model for the new provider would be\n * the SDK making a price decision on the caller's behalf, visible only on the bill.\n *\n * `knownProviders` (when given) lets an UNREGISTERED provider through — that is a\n * typo, and \"no provider adapter registered\" is the useful thing to say about it. */\nexport function assertOverrideProvider(\n base: { provider: string; model: string },\n override: Partial<TierSpec> | undefined,\n label: string,\n knownProviders?: readonly string[],\n): void {\n if (!override?.provider || override.model !== undefined) return;\n if (override.provider === base.provider) return;\n if (knownProviders !== undefined && !knownProviders.includes(override.provider)) return;\n throw new Error(\n `createAI: override sets provider \"${override.provider}\", but \"${label}\" resolves to model ` +\n `\"${base.model}\", which belongs to \"${base.provider}\". Set a model too, e.g. ` +\n `override: { provider: \"${override.provider}\", model: \"<a ${override.provider} model>\" }.`,\n );\n}\n\nexport function resolveTier(\n tier: Tier,\n override?: Partial<TierSpec>,\n configMap?: Partial<Record<Tier, TierSpec>>,\n knownProviders?: readonly string[],\n): TierSpec {\n const base = configMap?.[tier] ?? DEFAULT_TIER_MAP[tier];\n assertOverrideProvider(base, override, tier, knownProviders);\n return { ...base, ...override };\n}\n","// F022 — the model-availability registry: the ONE source both resolveModel()\n// (spawn / call path) and listModels() (UI picker) read. A curated default seed\n// (works offline — the durable floor) plus a mutable overlay that\n// refreshAvailability() updates from the live provider list.\n//\n// Scope note: this is a LIVENESS view (is this id alive right now?), not the\n// rich capability/price inventory (that is F017 src/catalogue). We only track\n// ids we want to assert status on; anything not here is fail-open (treated\n// available) so we never block a model we simply do not track.\nimport { DEFAULT_TIER_MAP } from \"../routing/tier-map.js\";\nimport type { AvailabilityStatus, AvailabilitySource, ModelStatus } from \"./types.js\";\n\n/** Internal registry row. `aliases[0]` surfaces as ModelStatus.alias. */\nexport interface RegistryEntry {\n id: string;\n aliases: string[];\n provider: string;\n available: boolean;\n status: AvailabilityStatus;\n note?: string;\n source: AvailabilitySource;\n}\n\nconst SUSPENDED_FABLE_MYTHOS = \"suspended — US export-control directive (2026-06-12)\";\n\n/** Curated defaults. Aliases here are MODEL-IDENTITY names only (short names for\n * the model itself). TIER aliases — fast/smart/powerful/cheap/vision/video/\n * embedding — are NOT written here: they are derived from DEFAULT_TIER_MAP below,\n * because hand-maintaining them in two places is exactly how they drifted.\n *\n * They HAD drifted, and it crossed the EU border: this list said `smart` was\n * claude-sonnet-4-6 (Anthropic, US) for the ~3 months after F030 pointed the\n * default tiers at Mistral EU, so resolveModel('smart') named a US model while\n * ai.chat({tier:'smart'}) called an EU one. Anyone using resolveModel to SHOW or\n * DECIDE where data goes — the obvious use — got the wrong answer. Measured by\n * fd-sundhed in 0.21.1, still true in 0.28.0, fixed here by deleting the second\n * list rather than correcting it. */\nconst DEFAULTS: RegistryEntry[] = [\n // ── Anthropic ────────────────────────────────────────────────────────────\n { id: \"claude-haiku-4-5\", aliases: [\"haiku\"], provider: \"anthropic\", available: true, status: \"available\", source: \"default\" },\n { id: \"claude-sonnet-4-6\", aliases: [\"sonnet\"], provider: \"anthropic\", available: true, status: \"available\", source: \"default\" },\n { id: \"claude-opus-4-8\", aliases: [\"opus\"], provider: \"anthropic\", available: true, status: \"available\", source: \"default\" },\n { id: \"claude-fable-5\", aliases: [\"fable\"], provider: \"anthropic\", available: true, status: \"available\", source: \"default\" },\n { id: \"claude-mythos-5\", aliases: [\"mythos\"], provider: \"anthropic\", available: false, status: \"suspended\", note: SUSPENDED_FABLE_MYTHOS, source: \"default\" },\n // ── Gemini ───────────────────────────────────────────────────────────────\n { id: \"gemini-2.5-flash\", aliases: [\"gemini-flash\"], provider: \"gemini\", available: true, status: \"available\", source: \"default\" },\n { id: \"gemini-2.5-flash-lite\", aliases: [\"gemini-flash-lite\"], provider: \"gemini\", available: true, status: \"available\", source: \"default\" },\n // ── OpenAI ───────────────────────────────────────────────────────────────\n { id: \"text-embedding-3-small\", aliases: [], provider: \"openai\", available: true, status: \"available\", source: \"default\" },\n // ── DeepSeek (CN-hosted — NOT GDPR-safe; non-PII workloads only) ─────────\n //\n // F057.2. These were missing while pricing.ts carried OFFICIAL rates for them\n // and providers/deepseek.ts shipped an adapter (F030.2) — so our own CLAUDE.md\n // instruction (\"if you are GATING, pass requireKnown:true\") refused a route we\n // built, priced and wrote an F-number for. Two tables disagreed, and the gate\n // is the one that decides. Reported by pitch (ref 28546) via components.\n //\n // The note carries the sunset because the registry is what a picker renders, and\n // a row that says only \"available\" would hide it. Both ids were documented to\n // deprecate 2026-07-24 in favour of `deepseek-v4-flash` — we have NOT verified\n // against a live key (no DEEPSEEK_API_KEY here), so this is the curated seed's\n // usual standing: a default, not a measurement. A live refresh may overrule it.\n //\n // `deepseek-v4-flash` on the DIRECT api is deliberately NOT here: it has no\n // price in pricing.ts (measured — getPrice(\"deepseek\",\"deepseek-v4-flash\") is\n // undefined), so a row would pass the gate for a route that silently bills\n // nothing. That is the same green-direction failure this card exists to remove,\n // and it needs a rate from a real source, not a guess. See the plan-doc.\n { id: \"deepseek-chat\", aliases: [], provider: \"deepseek\", available: true, status: \"available\", note: \"direct api.deepseek.com; documented to deprecate 2026-07-24 in favour of deepseek-v4-flash — not live-verified\", source: \"default\" },\n { id: \"deepseek-reasoner\", aliases: [], provider: \"deepseek\", available: true, status: \"available\", note: \"direct api.deepseek.com (thinking); documented to deprecate 2026-07-24 in favour of deepseek-v4-flash — not live-verified\", source: \"default\" },\n // ── TypeSafe (US-hosted — NOT for personal data) ─────────────────────────\n //\n // F066. Two rows, not an alias. TypeSafe's \"jev-latest\" points at jev-1.13.0 TODAY and\n // moves on their next release; writing it here as an alias of 1.13.0 would make this\n // registry assert a mapping we would have to remember to undo. As separate rows, both\n // pass a requireKnown gate and neither claims to be the other.\n { id: \"jev-1.13.0\", aliases: [\"jev\"], provider: \"typesafe\", available: true, status: \"available\", note: \"TypeSafe Jev, pinned version. US-hosted — not for personal data. Decision model: use ai.judge, not ai.chat\", source: \"default\" },\n { id: \"jev-latest\", aliases: [], provider: \"typesafe\", available: true, status: \"available\", note: \"MOVING alias (jev-1.13.0 as of 2026-10-01). Pin jev-1.13.0 if you tuned thresholds on confidence. US-hosted — not for personal data\", source: \"default\" },\n // ── Mistral (EU / GDPR) ──────────────────────────────────────────────────\n { id: \"mistral-large-latest\", aliases: [\"mistral-large\"], provider: \"mistral\", available: true, status: \"available\", source: \"default\" },\n { id: \"mistral-medium-latest\", aliases: [\"mistral-medium\"], provider: \"mistral\", available: true, status: \"available\", source: \"default\" },\n { id: \"mistral-small-latest\", aliases: [\"mistral-small\"], provider: \"mistral\", available: true, status: \"available\", source: \"default\" },\n];\n\n/** Attach every tier name as an alias of the model that tier ACTUALLY calls.\n * One source: DEFAULT_TIER_MAP decides, the registry follows. A tier whose model\n * is not in the registry is left unaliased on purpose — resolveModel then reports\n * status \"unknown\" (fail-open) instead of inventing a row, and the drift test\n * catches it. */\nexport const TIER_ALIAS_CONFLICTS: string[] = [];\n\nfunction applyTierAliases(entries: RegistryEntry[]): RegistryEntry[] {\n for (const [tier, spec] of Object.entries(DEFAULT_TIER_MAP)) {\n const owner = entries.find((e) => e.id === spec.model && e.provider === spec.provider);\n // A tier name hand-written on any OTHER row is the drift bug returning. It\n // would resolve correctly TODAY only because seed() lets the last write win\n // and the rows happen to be ordered favourably — reorder the array and the\n // tier silently points at the wrong provider again. Record it so a test can\n // fail on it, then strip it so runtime is right regardless.\n for (const e of entries) {\n if (e !== owner && e.aliases.includes(tier)) {\n TIER_ALIAS_CONFLICTS.push(`${tier} is hand-declared on ${e.id} (${e.provider}) but tier ${tier} calls ${spec.model} (${spec.provider})`);\n e.aliases = e.aliases.filter((a) => a !== tier);\n }\n }\n if (owner && !owner.aliases.includes(tier)) owner.aliases.push(tier);\n }\n return entries;\n}\napplyTierAliases(DEFAULTS);\n\n/** The live overlay, keyed by canonical id. Seeded from DEFAULTS (deep-copied so\n * resetting is clean). refreshAvailability() mutates this; resolve/listModels\n * read it synchronously. */\nlet OVERLAY = new Map<string, RegistryEntry>();\n/** alias → canonical id, rebuilt whenever the overlay is seeded. */\nlet ALIAS_INDEX = new Map<string, string>();\n\nfunction seed(): void {\n OVERLAY = new Map(DEFAULTS.map((e) => [e.id, { ...e, aliases: [...e.aliases] }]));\n ALIAS_INDEX = new Map();\n for (const e of DEFAULTS) for (const a of e.aliases) ALIAS_INDEX.set(a, e.id);\n}\nseed();\n\n/** Reset the overlay back to the curated defaults. For tests. */\nexport function resetRegistry(): void {\n seed();\n}\n\n/** Canonical id for a model id OR alias; null when we track neither. */\nexport function canonicalId(requested: string): string | null {\n if (OVERLAY.has(requested)) return requested;\n return ALIAS_INDEX.get(requested) ?? null;\n}\n\n/** The current entry for an id/alias, or undefined when untracked (fail-open). */\nexport function getEntry(requested: string): RegistryEntry | undefined {\n const id = canonicalId(requested);\n return id ? OVERLAY.get(id) : undefined;\n}\n\n/** All tracked entries (optionally provider-scoped), as a public ModelStatus[]. */\nexport function allEntries(provider?: string): ModelStatus[] {\n const rows: ModelStatus[] = [];\n for (const e of OVERLAY.values()) {\n if (provider && e.provider !== provider) continue;\n rows.push({\n id: e.id,\n alias: e.aliases[0],\n provider: e.provider,\n available: e.available,\n status: e.status,\n note: e.note,\n source: e.source,\n });\n }\n return rows;\n}\n\n/** Provider-scoped canonical ids (for refresh reconciliation). */\nexport function providerIds(provider: string): string[] {\n return [...OVERLAY.values()].filter((e) => e.provider === provider).map((e) => e.id);\n}\n\n/** Mark a tracked id available/suspended from a live refresh. No-op if untracked. */\nexport function setAvailability(id: string, available: boolean, note?: string): void {\n const e = OVERLAY.get(id);\n if (!e) return;\n e.available = available;\n e.status = available ? \"available\" : \"suspended\";\n e.source = \"refresh\";\n if (note !== undefined) e.note = note;\n else if (available) e.note = undefined;\n}\n","// F022 — Model Availability Harness. Public types for the availability layer:\n// the shared status read (ModelStatus), the resolve result, and the structured\n// error a caller can flag on. The registry is the one source both the spawn /\n// call path (resolveModel) and UI pickers (listModels) read.\n\nexport type AvailabilityStatus = \"available\" | \"suspended\" | \"unknown\";\n\n/** Where a model's current availability came from: the curated default seed,\n * or a live provider refresh (Anthropic GET /v1/models). */\nexport type AvailabilitySource = \"default\" | \"refresh\";\n\n/** One row of the shared status read — what a UI model-picker renders. */\nexport interface ModelStatus {\n /** Canonical provider model id, e.g. \"claude-fable-5\". */\n id: string;\n /** Short/tier alias, e.g. \"fable\" (the first registered alias). */\n alias?: string;\n /** \"anthropic\" | \"openai\" | \"gemini\" | \"mistral\" | … */\n provider: string;\n available: boolean;\n status: AvailabilityStatus;\n /** Friendly reason, e.g. \"suspended — US export-control directive (2026-06-12)\". */\n note?: string;\n source: AvailabilitySource;\n}\n\n/** Result of resolveModel — the spawn / call path consumes this synchronously. */\nexport interface ResolveResult {\n /** True when the requested model itself is available. */\n ok: boolean;\n /** The id to actually use: `requested` when ok, else the first available fallback. */\n model: string;\n /** What the caller asked for (id or alias, normalized to the canonical id). */\n requested: string;\n provider?: string;\n /** True when `model` differs from `requested` because we fell back. */\n fellBack: boolean;\n status: AvailabilityStatus;\n /** Why it degraded / why it is unavailable. */\n reason?: string;\n /** The registry row's caveat, surfaced on the SUCCESS path too (F057.2).\n *\n * `reason` only ever appears when a model is unavailable, so a caveat written\n * on an AVAILABLE row — \"documented to deprecate 2026-07-24\", \"preview\", a\n * known rate limit — was reachable through listModels() and invisible to the\n * one caller who most needs it: the gate. A consumer passing requireKnown got\n * a clean green with the warning sitting one call away, unread. Carrying it\n * here costs an optional field and makes `ok:true` able to say \"yes, but\". */\n note?: string;\n}\n\n/** Thrown by resolveModel when the requested model is unavailable, no usable\n * fallback exists, and the caller passed `throwIfUnavailable`. Callers flag on\n * `.code === \"model_unavailable\"`. */\nexport class ModelUnavailableError extends Error {\n readonly code = \"model_unavailable\";\n readonly requested: string;\n readonly provider?: string;\n readonly note?: string;\n constructor(requested: string, note?: string, provider?: string) {\n super(`model \"${requested}\" is unavailable${note ? ` (${note})` : \"\"}`);\n this.name = \"ModelUnavailableError\";\n this.requested = requested;\n this.note = note;\n this.provider = provider;\n }\n}\n","// F022 — the synchronous, zero-I/O resolve + status read. This is the spawn /\n// call hot path (buddy's launcher calls resolveModel per spawn, cardmem #4842):\n// it MUST never await and never touch the network. Freshness comes only from a\n// prior async refreshAvailability(); resolve just reads the in-memory registry.\nimport { allEntries, canonicalId, getEntry } from \"./registry.js\";\nimport { ModelUnavailableError } from \"./types.js\";\nimport type { ModelStatus, ResolveResult } from \"./types.js\";\n\nexport interface ResolveOptions {\n /** One id/alias or an ordered chain to try when `requested` is unavailable. */\n fallback?: string | string[];\n /** Scope hint (passed through to the result); does not gate lookup. */\n provider?: string;\n /** Throw ModelUnavailableError instead of returning ok:false when there is no\n * usable fallback. For callers that want to flag rather than degrade. */\n throwIfUnavailable?: boolean;\n /** Treat an id we do not track as UNUSABLE instead of fail-open.\n *\n * Default is fail-open: we never block a model we simply do not track. That is\n * right for liveness, and wrong for a caller who is GATING — cms measured the\n * consequence: resolveModel(\"cheap\") answered {ok:true, model:\"cheap\"}, so a\n * consumer following our own instruction to gate on `ok` passed the gate and\n * then sent the literal string \"cheap\" to a provider as a model id. A\n * success-shaped non-answer is worse than an error, because an error gets\n * handled and a shape does not.\n *\n * Set this when you need \"a model you actually know about\". Off by default so\n * no existing caller changes behaviour. */\n requireKnown?: boolean;\n}\n\n/** The shared status read — UI pickers grey out `available:false` rows. */\nexport function listModels(opts: { provider?: string } = {}): ModelStatus[] {\n return allEntries(opts.provider);\n}\n\n/** Is this id/alias currently usable? Untracked ids are fail-open (true) unless\n * the caller asked for requireKnown. */\nfunction isAvailable(requested: string, requireKnown = false): boolean {\n const e = getEntry(requested);\n if (!e) return !requireKnown; // fail-open on unknown, unless gating\n return e.available;\n}\n\n/**\n * Resolve a requested model (id or alias) to one that is actually usable.\n * Synchronous + offline by contract (cardmem #4842) — reads the registry only.\n *\n * - Available → pass through ({ ok:true, fellBack:false }).\n * - Unavailable + a fallback that IS available → swap ({ ok:false, fellBack:true }).\n * - Unavailable + no usable fallback → throw (throwIfUnavailable) or return ok:false.\n * - Unknown id → treated available (never block a model we do not track).\n */\nexport function resolveModel(requested: string, opts: ResolveOptions = {}): ResolveResult {\n const id = canonicalId(requested) ?? requested;\n const entry = getEntry(requested);\n const provider = opts.provider ?? entry?.provider;\n\n if (isAvailable(requested, opts.requireKnown)) {\n return {\n ok: true,\n model: id,\n requested: id,\n provider,\n fellBack: false,\n status: entry?.status ?? \"unknown\",\n // A green gate that hides a known caveat is the failure this whole card is\n // about, one level up: the answer is shaped like \"fine\" and the warning is\n // somewhere else. If the row has a note, the caller gets it with the yes.\n ...(entry?.note ? { note: entry.note } : {}),\n };\n }\n\n // Requested is suspended — walk the fallback chain for the first available one.\n const chain = opts.fallback === undefined ? [] : Array.isArray(opts.fallback) ? opts.fallback : [opts.fallback];\n for (const fb of chain) {\n if (isAvailable(fb, opts.requireKnown)) {\n const fbId = canonicalId(fb) ?? fb;\n return {\n ok: false,\n model: fbId,\n requested: id,\n provider,\n fellBack: true,\n status: entry?.status ?? \"suspended\",\n reason: entry?.note ?? `${id} is unavailable`,\n };\n }\n }\n\n // No usable fallback.\n const unknownAndGating = !entry && opts.requireKnown;\n const reason = unknownAndGating\n ? `${id} is not a model this registry knows — requireKnown was set, so it is not assumed usable`\n : (entry?.note ?? `${id} is unavailable`);\n if (opts.throwIfUnavailable) {\n throw new ModelUnavailableError(id, reason, provider);\n }\n return {\n ok: false,\n model: id,\n requested: id,\n provider,\n fellBack: false,\n status: entry?.status ?? \"unknown\",\n reason,\n };\n}\n"],"mappings":"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","names":[]}
package/dist/index.d.ts CHANGED
@@ -155,7 +155,7 @@ interface TierSpec {
155
155
  transport: Transport;
156
156
  }
157
157
  /** High-level capability a call exercises. Mirrors the capability layer (F5). */
158
- type Capability = "chat" | "vision" | "video" | "translate" | "image" | "animate" | "embedding" | "transcribe" | "ocr" | "moderation" | "podcast" | "tts" | "trainStyle" | "mockup" | "design" | "extract" | "classify" | "rerank";
158
+ type Capability = "chat" | "vision" | "video" | "translate" | "image" | "animate" | "embedding" | "transcribe" | "ocr" | "moderation" | "podcast" | "tts" | "trainStyle" | "mockup" | "design" | "extract" | "classify" | "rerank" | "judge";
159
159
  type Role = "system" | "user" | "assistant" | "tool";
160
160
  /** A piece of message content. Text everywhere; image parts feed vision. */
161
161
  type ContentPart = {
@@ -528,6 +528,68 @@ interface ModerationResult {
528
528
  results: ModerationItem[];
529
529
  usage: Usage;
530
530
  }
531
+ /** TypeSafe's EntryType: plain text OR JSON structure. Instructions, choice option
532
+ * descriptions, score levels and noul criteria all accept it — and TypeSafe's own
533
+ * headline patterns (walking a taxonomy, a JSON rubric that sharpens the boundary
534
+ * between options) depend on it. Measured live 2026-10-01: structured instructions and
535
+ * object-valued choice options → 200. */
536
+ type JudgeEntry = string | null | unknown[] | {
537
+ [key: string]: unknown;
538
+ };
539
+ type JudgeQuestion =
540
+ /** `criteria` is an OBJECT with `true`/`false`, not a string — measured live: a string
541
+ * is a 422 from TypeSafe. The first version of this type had it as a string, which
542
+ * let through exactly the shape the server refuses. */
543
+ {
544
+ type: "noul";
545
+ instructions: JudgeEntry;
546
+ criteria?: {
547
+ true?: JudgeEntry;
548
+ false?: JudgeEntry;
549
+ };
550
+ } | {
551
+ type: "choice";
552
+ instructions: JudgeEntry;
553
+ criteria: Record<string, JudgeEntry>;
554
+ } | {
555
+ type: "score";
556
+ instructions: JudgeEntry;
557
+ criteria: JudgeEntry[];
558
+ };
559
+ interface JudgeNoulAnswer {
560
+ type: "noul";
561
+ /** Probability 0–1 that the answer is yes. */
562
+ noul: number;
563
+ }
564
+ interface JudgeChoiceAnswer {
565
+ type: "choice";
566
+ /** The key of the most probable option. ALWAYS one of yours — Jev cannot answer
567
+ * "none of these". Read `confidence` to decide whether to act on it. */
568
+ choice: string;
569
+ probabilities: Record<string, number>;
570
+ /** How peaked the distribution is, 0–1. NOT the same as the winning probability. */
571
+ confidence: number;
572
+ }
573
+ interface JudgeScoreAnswer {
574
+ type: "score";
575
+ /** Probability-weighted level; can fall BETWEEN levels (1.5). */
576
+ score: number;
577
+ probabilities: Record<string, number>;
578
+ /** Level number → the level as you DESCRIBED it. If you passed structured levels,
579
+ * the legend echoes structure, not text — measured live: {"0":{"level":"minor"}}. */
580
+ legend: Record<string, JudgeEntry>;
581
+ confidence: number;
582
+ }
583
+ type JudgeAnswer = JudgeNoulAnswer | JudgeChoiceAnswer | JudgeScoreAnswer;
584
+ interface JudgeRequest {
585
+ state: string | string[] | Record<string, unknown>;
586
+ questions: Record<string, JudgeQuestion>;
587
+ spec: TierSpec;
588
+ }
589
+ interface JudgeResult {
590
+ answers: Record<string, JudgeAnswer>;
591
+ usage: Usage;
592
+ }
531
593
  interface DialogueTurn {
532
594
  text: string;
533
595
  voiceId: string;
@@ -643,6 +705,8 @@ interface ProviderAdapter {
643
705
  transcribe?(req: TranscribeRequest): Promise<TranscribeResult>;
644
706
  ocr?(req: OcrRequest): Promise<OcrResult>;
645
707
  moderate?(req: ModerationRequest): Promise<ModerationResult>;
708
+ /** F066 — typed decisions with calibrated probabilities (TypeSafe Jev). */
709
+ judge?(req: JudgeRequest): Promise<JudgeResult>;
646
710
  /** Multi-voice dialogue → one audio episode (F020). ElevenLabs. */
647
711
  dialogue?(req: DialogueRequest): Promise<PodcastResult>;
648
712
  /** Single-voice TTS (F020.4) → audio. ElevenLabs. */
@@ -1937,6 +2001,182 @@ declare const moderationInputSchema: z.ZodObject<{
1937
2001
  })[] | undefined;
1938
2002
  labels?: Record<string, string> | undefined;
1939
2003
  }>;
2004
+ declare const judgeInputSchema: z.ZodObject<{
2005
+ tier: z.ZodOptional<z.ZodEnum<["fast", "smart", "powerful", "cheap", "vision", "video", "embedding"]>>;
2006
+ override: z.ZodOptional<z.ZodObject<{
2007
+ provider: z.ZodOptional<z.ZodString>;
2008
+ model: z.ZodOptional<z.ZodString>;
2009
+ transport: z.ZodOptional<z.ZodEnum<["http", "subprocess"]>>;
2010
+ }, "strip", z.ZodTypeAny, {
2011
+ provider?: string | undefined;
2012
+ model?: string | undefined;
2013
+ transport?: "http" | "subprocess" | undefined;
2014
+ }, {
2015
+ provider?: string | undefined;
2016
+ model?: string | undefined;
2017
+ transport?: "http" | "subprocess" | undefined;
2018
+ }>>;
2019
+ fallback: z.ZodOptional<z.ZodArray<z.ZodUnion<[z.ZodEnum<["fast", "smart", "powerful", "cheap", "vision", "video", "embedding"]>, z.ZodObject<{
2020
+ provider: z.ZodString;
2021
+ model: z.ZodString;
2022
+ transport: z.ZodEnum<["http", "subprocess"]>;
2023
+ }, "strip", z.ZodTypeAny, {
2024
+ provider: string;
2025
+ model: string;
2026
+ transport: "http" | "subprocess";
2027
+ }, {
2028
+ provider: string;
2029
+ model: string;
2030
+ transport: "http" | "subprocess";
2031
+ }>]>, "many">>;
2032
+ purpose: z.ZodOptional<z.ZodString>;
2033
+ labels: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
2034
+ state: z.ZodUnion<[z.ZodString, z.ZodArray<z.ZodString, "many">, z.ZodRecord<z.ZodString, z.ZodUnknown>]>;
2035
+ questions: z.ZodEffects<z.ZodRecord<z.ZodString, z.ZodDiscriminatedUnion<"type", [z.ZodObject<{
2036
+ type: z.ZodLiteral<"noul">;
2037
+ instructions: z.ZodUnion<[z.ZodString, z.ZodNull, z.ZodArray<z.ZodUnknown, "many">, z.ZodRecord<z.ZodString, z.ZodUnknown>]>;
2038
+ criteria: z.ZodOptional<z.ZodObject<{
2039
+ true: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodNull, z.ZodArray<z.ZodUnknown, "many">, z.ZodRecord<z.ZodString, z.ZodUnknown>]>>;
2040
+ false: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodNull, z.ZodArray<z.ZodUnknown, "many">, z.ZodRecord<z.ZodString, z.ZodUnknown>]>>;
2041
+ }, "strict", z.ZodTypeAny, {
2042
+ true?: string | unknown[] | Record<string, unknown> | null | undefined;
2043
+ false?: string | unknown[] | Record<string, unknown> | null | undefined;
2044
+ }, {
2045
+ true?: string | unknown[] | Record<string, unknown> | null | undefined;
2046
+ false?: string | unknown[] | Record<string, unknown> | null | undefined;
2047
+ }>>;
2048
+ }, "strip", z.ZodTypeAny, {
2049
+ type: "noul";
2050
+ instructions: string | unknown[] | Record<string, unknown> | null;
2051
+ criteria?: {
2052
+ true?: string | unknown[] | Record<string, unknown> | null | undefined;
2053
+ false?: string | unknown[] | Record<string, unknown> | null | undefined;
2054
+ } | undefined;
2055
+ }, {
2056
+ type: "noul";
2057
+ instructions: string | unknown[] | Record<string, unknown> | null;
2058
+ criteria?: {
2059
+ true?: string | unknown[] | Record<string, unknown> | null | undefined;
2060
+ false?: string | unknown[] | Record<string, unknown> | null | undefined;
2061
+ } | undefined;
2062
+ }>, z.ZodObject<{
2063
+ type: z.ZodLiteral<"choice">;
2064
+ instructions: z.ZodUnion<[z.ZodString, z.ZodNull, z.ZodArray<z.ZodUnknown, "many">, z.ZodRecord<z.ZodString, z.ZodUnknown>]>;
2065
+ criteria: z.ZodEffects<z.ZodRecord<z.ZodString, z.ZodUnion<[z.ZodString, z.ZodNull, z.ZodArray<z.ZodUnknown, "many">, z.ZodRecord<z.ZodString, z.ZodUnknown>]>>, Record<string, string | unknown[] | Record<string, unknown> | null>, Record<string, string | unknown[] | Record<string, unknown> | null>>;
2066
+ }, "strip", z.ZodTypeAny, {
2067
+ type: "choice";
2068
+ instructions: string | unknown[] | Record<string, unknown> | null;
2069
+ criteria: Record<string, string | unknown[] | Record<string, unknown> | null>;
2070
+ }, {
2071
+ type: "choice";
2072
+ instructions: string | unknown[] | Record<string, unknown> | null;
2073
+ criteria: Record<string, string | unknown[] | Record<string, unknown> | null>;
2074
+ }>, z.ZodObject<{
2075
+ type: z.ZodLiteral<"score">;
2076
+ instructions: z.ZodUnion<[z.ZodString, z.ZodNull, z.ZodArray<z.ZodUnknown, "many">, z.ZodRecord<z.ZodString, z.ZodUnknown>]>;
2077
+ criteria: z.ZodArray<z.ZodUnion<[z.ZodString, z.ZodNull, z.ZodArray<z.ZodUnknown, "many">, z.ZodRecord<z.ZodString, z.ZodUnknown>]>, "many">;
2078
+ }, "strip", z.ZodTypeAny, {
2079
+ type: "score";
2080
+ instructions: string | unknown[] | Record<string, unknown> | null;
2081
+ criteria: (string | unknown[] | Record<string, unknown> | null)[];
2082
+ }, {
2083
+ type: "score";
2084
+ instructions: string | unknown[] | Record<string, unknown> | null;
2085
+ criteria: (string | unknown[] | Record<string, unknown> | null)[];
2086
+ }>]>>, Record<string, {
2087
+ type: "noul";
2088
+ instructions: string | unknown[] | Record<string, unknown> | null;
2089
+ criteria?: {
2090
+ true?: string | unknown[] | Record<string, unknown> | null | undefined;
2091
+ false?: string | unknown[] | Record<string, unknown> | null | undefined;
2092
+ } | undefined;
2093
+ } | {
2094
+ type: "choice";
2095
+ instructions: string | unknown[] | Record<string, unknown> | null;
2096
+ criteria: Record<string, string | unknown[] | Record<string, unknown> | null>;
2097
+ } | {
2098
+ type: "score";
2099
+ instructions: string | unknown[] | Record<string, unknown> | null;
2100
+ criteria: (string | unknown[] | Record<string, unknown> | null)[];
2101
+ }>, Record<string, {
2102
+ type: "noul";
2103
+ instructions: string | unknown[] | Record<string, unknown> | null;
2104
+ criteria?: {
2105
+ true?: string | unknown[] | Record<string, unknown> | null | undefined;
2106
+ false?: string | unknown[] | Record<string, unknown> | null | undefined;
2107
+ } | undefined;
2108
+ } | {
2109
+ type: "choice";
2110
+ instructions: string | unknown[] | Record<string, unknown> | null;
2111
+ criteria: Record<string, string | unknown[] | Record<string, unknown> | null>;
2112
+ } | {
2113
+ type: "score";
2114
+ instructions: string | unknown[] | Record<string, unknown> | null;
2115
+ criteria: (string | unknown[] | Record<string, unknown> | null)[];
2116
+ }>>;
2117
+ }, "strip", z.ZodTypeAny, {
2118
+ state: string | string[] | Record<string, unknown>;
2119
+ questions: Record<string, {
2120
+ type: "noul";
2121
+ instructions: string | unknown[] | Record<string, unknown> | null;
2122
+ criteria?: {
2123
+ true?: string | unknown[] | Record<string, unknown> | null | undefined;
2124
+ false?: string | unknown[] | Record<string, unknown> | null | undefined;
2125
+ } | undefined;
2126
+ } | {
2127
+ type: "choice";
2128
+ instructions: string | unknown[] | Record<string, unknown> | null;
2129
+ criteria: Record<string, string | unknown[] | Record<string, unknown> | null>;
2130
+ } | {
2131
+ type: "score";
2132
+ instructions: string | unknown[] | Record<string, unknown> | null;
2133
+ criteria: (string | unknown[] | Record<string, unknown> | null)[];
2134
+ }>;
2135
+ tier?: "fast" | "smart" | "powerful" | "cheap" | "vision" | "video" | "embedding" | undefined;
2136
+ purpose?: string | undefined;
2137
+ override?: {
2138
+ provider?: string | undefined;
2139
+ model?: string | undefined;
2140
+ transport?: "http" | "subprocess" | undefined;
2141
+ } | undefined;
2142
+ fallback?: ("fast" | "smart" | "powerful" | "cheap" | "vision" | "video" | "embedding" | {
2143
+ provider: string;
2144
+ model: string;
2145
+ transport: "http" | "subprocess";
2146
+ })[] | undefined;
2147
+ labels?: Record<string, string> | undefined;
2148
+ }, {
2149
+ state: string | string[] | Record<string, unknown>;
2150
+ questions: Record<string, {
2151
+ type: "noul";
2152
+ instructions: string | unknown[] | Record<string, unknown> | null;
2153
+ criteria?: {
2154
+ true?: string | unknown[] | Record<string, unknown> | null | undefined;
2155
+ false?: string | unknown[] | Record<string, unknown> | null | undefined;
2156
+ } | undefined;
2157
+ } | {
2158
+ type: "choice";
2159
+ instructions: string | unknown[] | Record<string, unknown> | null;
2160
+ criteria: Record<string, string | unknown[] | Record<string, unknown> | null>;
2161
+ } | {
2162
+ type: "score";
2163
+ instructions: string | unknown[] | Record<string, unknown> | null;
2164
+ criteria: (string | unknown[] | Record<string, unknown> | null)[];
2165
+ }>;
2166
+ tier?: "fast" | "smart" | "powerful" | "cheap" | "vision" | "video" | "embedding" | undefined;
2167
+ purpose?: string | undefined;
2168
+ override?: {
2169
+ provider?: string | undefined;
2170
+ model?: string | undefined;
2171
+ transport?: "http" | "subprocess" | undefined;
2172
+ } | undefined;
2173
+ fallback?: ("fast" | "smart" | "powerful" | "cheap" | "vision" | "video" | "embedding" | {
2174
+ provider: string;
2175
+ model: string;
2176
+ transport: "http" | "subprocess";
2177
+ })[] | undefined;
2178
+ labels?: Record<string, string> | undefined;
2179
+ }>;
1940
2180
  declare const podcastInputSchema: z.ZodObject<{
1941
2181
  tier: z.ZodOptional<z.ZodEnum<["fast", "smart", "powerful", "cheap", "vision", "video", "embedding"]>>;
1942
2182
  override: z.ZodOptional<z.ZodObject<{
@@ -2272,6 +2512,7 @@ type EmbeddingInput = z.infer<typeof embeddingInputSchema>;
2272
2512
  type TranscribeInput = z.infer<typeof transcribeInputSchema>;
2273
2513
  type OcrInput = z.infer<typeof ocrInputSchema>;
2274
2514
  type ModerationInput = z.infer<typeof moderationInputSchema>;
2515
+ type JudgeInput = z.infer<typeof judgeInputSchema>;
2275
2516
  type PodcastInput = z.infer<typeof podcastInputSchema>;
2276
2517
  type TtsInput = z.infer<typeof ttsInputSchema>;
2277
2518
  type AiConfig = z.infer<typeof aiConfigSchema>;
@@ -2302,6 +2543,9 @@ interface AiClient {
2302
2543
  ocr(input: OcrInput): Promise<OcrResult>;
2303
2544
  /** Moderation (F016.4) — classify text against safety categories. Mistral. */
2304
2545
  moderate(input: ModerationInput): Promise<ModerationResult>;
2546
+ /** Judge (F066) — typed yes/no, choice and score decisions with calibrated
2547
+ * probabilities. TypeSafe Jev. US-hosted: NOT for personal data. */
2548
+ judge(input: JudgeInput): Promise<JudgeResult>;
2305
2549
  /** Podcast (F020) — a finished manuscript → one multi-voice audio episode. ElevenLabs. */
2306
2550
  podcast(input: PodcastInput): Promise<PodcastResult>;
2307
2551
  /** Single-voice TTS (F020.4) — text → audio. `voice` = curated name or voiceId. ElevenLabs. */
@@ -2509,6 +2753,12 @@ declare function deeplAdapter(config?: {
2509
2753
  pricePer1kChars?: number;
2510
2754
  }): ProviderAdapter;
2511
2755
 
2756
+ declare function typesafeAdapter(config?: {
2757
+ apiKey?: string;
2758
+ baseUrl?: string;
2759
+ fetch?: typeof fetch;
2760
+ }): ProviderAdapter;
2761
+
2512
2762
  interface FalAdapterConfig {
2513
2763
  apiKey?: string;
2514
2764
  /** "sync" (default — fal.run, fast models) or "queue" (queue.fal.run, polled). */
@@ -2604,8 +2854,8 @@ declare const falStubAdapter: ProviderAdapter;
2604
2854
  * wires the live adapters. */
2605
2855
  declare const stubProviders: Record<string, ProviderAdapter>;
2606
2856
 
2607
- declare const VERSION: "0.49.3";
2608
- declare const SDK_TAG: "@broberg/ai-sdk@0.49.3";
2857
+ declare const VERSION: "0.50.1";
2858
+ declare const SDK_TAG: "@broberg/ai-sdk@0.50.1";
2609
2859
 
2610
2860
  /** Built-in defaults. Every entry is overridable via AiConfig.defaults or a
2611
2861
  * per-call override.
@@ -3029,6 +3279,7 @@ declare const DEFAULT_BASE_URLS: {
3029
3279
  readonly mistral: "https://api.mistral.ai/v1";
3030
3280
  readonly openai: "https://api.openai.com/v1";
3031
3281
  readonly openrouter: "https://openrouter.ai/api/v1";
3282
+ readonly typesafe: "https://api.typesafe.ai/v1";
3032
3283
  };
3033
3284
  type FixedHostProvider = keyof typeof DEFAULT_BASE_URLS;
3034
3285
  /** `bfl` is DELIBERATELY ABSENT. Its adapter hard-pins `https://api.eu.bfl.ai` in a local
@@ -3115,4 +3366,4 @@ interface StreamTransportRequest extends TransportRequest {
3115
3366
  */
3116
3367
  declare function streamTransport(req: StreamTransportRequest): AsyncIterable<string>;
3117
3368
 
3118
- export { AZURE_DANISH_VOICES, AZURE_DANISH_VOICE_LIST, type AiClient, type AiConfig, type AlignedWordTimings, type AzureVoiceInfo, type AzureWordBoundary, type BatchJob, type BatchRequestItem, type BatchResultItem, type BflAdapterConfig, type BflCredits, type BudgetConfig, BudgetExceededError, BudgetGuard, type BudgetStore, CONFIG_DERIVED_HOSTS, type CallOptions, type Capability, type ChatInput, type ChatRequest, type ChatResult, type ChatStreamEvent, type CheckVoiceOptions, type ClassifyInput, type ClassifyResult, type ContentPart, type Contracts, type CostQuery, type CostSink, type CostSummary, type CostSummaryQuery, type CostTimeseriesQuery, DEFAULT_BASE_URLS, 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 FixedHostProvider, type HttpResponse, type ImageInput, type ImageRequest, type ImageResult, LOCALLY_PINNED_HOSTS, 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 RefreshOptions, type RefreshResult, type Region, type RerankInput, type RerankResult, type Role, type RouteForecast, 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 UpmetricsCostClientConfig, UpmetricsCostError, type UpmetricsCostRow, type UpmetricsCostSummary, type UpmetricsCostTimeseries, type UpmetricsSink, type UpmetricsSinkConfig, type UpmetricsSinkStats, type Usage, VERSION, type VideoInput, type VisionInput, type VoiceInfo, type VoiceProvider, type VoiceResolveResult, type VoiceStatus, VoiceUnavailableError, type WordTiming, aiConfigSchema, alignWordTimings, anthropicAdapter, anthropicApiAdapter, anthropicSubprocessAdapter, azureAdapter, bflAdapter, bflCredits, chatInputSchema, checkVoice, classifyRegionName, computeCost, createAI, deepinfraAdapter, deeplAdapter, deepseekAdapter, defaultBaseUrl, defaultProviders, discordSink, elevenlabsAdapter, embeddingInputSchema, falAdapter, falStubAdapter, freshUsage, fromProviderToolCall, geminiAdapter, getCostSummary, getPrice, httpTransport, imageInputSchema, listAzureDanishVoices, listVoices, makeContracts, makeOpenAICompatibleAdapter, matchLabel, messageSchema, mistralAdapter, mistralStubAdapter, multiSink, noopSink, openaiAdapter, openaiStubAdapter, openrouterAdapter, parseClaudeCliJson, parseJsonLoose, refreshAvailability, regionOfHost, regionOfProvider, requestyAdapter, resetRefreshState, resetRegistry, resolveAzureVoice, resolveTier, resolveVoice, sqliteBudgetStore, sqliteSink, streamTransport, stubProviders, subprocessTransport, tierSpecSchema, toProviderTools, toolSchema, translateInputSchema, upmetricsCostClient, upmetricsSink, usdFromMicro, vertexAdapter, visionInputSchema, wouldProviderRouteTo, wouldRouteTo };
3369
+ export { AZURE_DANISH_VOICES, AZURE_DANISH_VOICE_LIST, type AiClient, type AiConfig, type AlignedWordTimings, type AzureVoiceInfo, type AzureWordBoundary, type BatchJob, type BatchRequestItem, type BatchResultItem, type BflAdapterConfig, type BflCredits, type BudgetConfig, BudgetExceededError, BudgetGuard, type BudgetStore, CONFIG_DERIVED_HOSTS, type CallOptions, type Capability, type ChatInput, type ChatRequest, type ChatResult, type ChatStreamEvent, type CheckVoiceOptions, type ClassifyInput, type ClassifyResult, type ContentPart, type Contracts, type CostQuery, type CostSink, type CostSummary, type CostSummaryQuery, type CostTimeseriesQuery, DEFAULT_BASE_URLS, 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 FixedHostProvider, type HttpResponse, type ImageInput, type ImageRequest, type ImageResult, type JudgeAnswer, type JudgeChoiceAnswer, type JudgeInput, type JudgeNoulAnswer, type JudgeQuestion, type JudgeResult, type JudgeScoreAnswer, LOCALLY_PINNED_HOSTS, 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 RefreshOptions, type RefreshResult, type Region, type RerankInput, type RerankResult, type Role, type RouteForecast, 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 UpmetricsCostClientConfig, UpmetricsCostError, type UpmetricsCostRow, type UpmetricsCostSummary, type UpmetricsCostTimeseries, type UpmetricsSink, type UpmetricsSinkConfig, type UpmetricsSinkStats, type Usage, VERSION, type VideoInput, type VisionInput, type VoiceInfo, type VoiceProvider, type VoiceResolveResult, type VoiceStatus, VoiceUnavailableError, type WordTiming, aiConfigSchema, alignWordTimings, anthropicAdapter, anthropicApiAdapter, anthropicSubprocessAdapter, azureAdapter, bflAdapter, bflCredits, chatInputSchema, checkVoice, classifyRegionName, computeCost, createAI, deepinfraAdapter, deeplAdapter, deepseekAdapter, defaultBaseUrl, defaultProviders, discordSink, elevenlabsAdapter, embeddingInputSchema, falAdapter, falStubAdapter, freshUsage, fromProviderToolCall, geminiAdapter, getCostSummary, getPrice, httpTransport, imageInputSchema, listAzureDanishVoices, listVoices, makeContracts, makeOpenAICompatibleAdapter, matchLabel, messageSchema, mistralAdapter, mistralStubAdapter, multiSink, noopSink, openaiAdapter, openaiStubAdapter, openrouterAdapter, parseClaudeCliJson, parseJsonLoose, refreshAvailability, regionOfHost, regionOfProvider, requestyAdapter, resetRefreshState, resetRegistry, resolveAzureVoice, resolveTier, resolveVoice, sqliteBudgetStore, sqliteSink, streamTransport, stubProviders, subprocessTransport, tierSpecSchema, toProviderTools, toolSchema, translateInputSchema, typesafeAdapter, upmetricsCostClient, upmetricsSink, usdFromMicro, vertexAdapter, visionInputSchema, wouldProviderRouteTo, wouldRouteTo };
package/dist/index.js CHANGED
@@ -8,7 +8,7 @@ import {
8
8
  resolveModel,
9
9
  resolveTier,
10
10
  setAvailability
11
- } from "./chunk-SWV5YIEE.js";
11
+ } from "./chunk-XDBN6QJ4.js";
12
12
  import {
13
13
  DEFAULT_CLIP_SEC,
14
14
  MEDIA_PRICING_CHECKED_AT,
@@ -18,7 +18,7 @@ import {
18
18
  listMediaPrices,
19
19
  pricingFreshness,
20
20
  pricingGeneratedAt
21
- } from "./chunk-4SIHCELN.js";
21
+ } from "./chunk-DCNONKOW.js";
22
22
 
23
23
  // src/cost/default-hosts.ts
24
24
  var DEFAULT_BASE_URLS = {
@@ -29,7 +29,9 @@ var DEFAULT_BASE_URLS = {
29
29
  gemini: "https://generativelanguage.googleapis.com/v1beta",
30
30
  mistral: "https://api.mistral.ai/v1",
31
31
  openai: "https://api.openai.com/v1",
32
- openrouter: "https://openrouter.ai/api/v1"
32
+ openrouter: "https://openrouter.ai/api/v1",
33
+ // F066. US-hosted per the TypeSafe privacy policy — see region.ts.
34
+ typesafe: "https://api.typesafe.ai/v1"
33
35
  };
34
36
  var LOCALLY_PINNED_HOSTS = {
35
37
  bfl: "https://api.eu.bfl.ai"
@@ -269,6 +271,11 @@ var HOST_REGION = {
269
271
  "fal.run": "us",
270
272
  "queue.fal.run": "us",
271
273
  "api.deepseek.com": "cn",
274
+ // F066 — TypeSafe (Jev). "The Services are hosted in the United States" (their privacy
275
+ // policy, read 2026-10-01). MEASURED TRAP: a live call answered through Cloudflare's
276
+ // COPENHAGEN edge (cf-ray …-CPH). That is where the TLS terminated, not where the data
277
+ // is processed — a host behind a CDN cannot have its residency read off the edge.
278
+ "api.typesafe.ai": "us",
272
279
  // Aggregators: the host is theirs, the upstream is not ours to know.
273
280
  "openrouter.ai": "unknown",
274
281
  "router.requesty.ai": "unknown"
@@ -2303,6 +2310,71 @@ function deeplAdapter(config = {}) {
2303
2310
  return { name: "deepl", translate };
2304
2311
  }
2305
2312
 
2313
+ // src/providers/typesafe.ts
2314
+ function typesafeAdapter(config = {}) {
2315
+ const fetchImpl = config.fetch ?? fetch;
2316
+ const base = (config.baseUrl ?? DEFAULT_BASE_URLS.typesafe).replace(/\/$/, "");
2317
+ function key() {
2318
+ const k = config.apiKey ?? process.env.TYPESAFE_API_KEY;
2319
+ if (!k) throw new Error("typesafe adapter: API key not set (env TYPESAFE_API_KEY)");
2320
+ return k;
2321
+ }
2322
+ function mapAnswer(id, q, a) {
2323
+ if (!a) throw new Error(`typesafe judge: no answer returned for question "${id}"`);
2324
+ const num = (v, field) => {
2325
+ if (typeof v !== "number") throw new Error(`typesafe judge: "${id}" answer has no numeric ${field}`);
2326
+ return v;
2327
+ };
2328
+ if (q.type === "noul") return { type: "noul", noul: num(a.noul, "noul") };
2329
+ if (q.type === "choice") {
2330
+ if (typeof a.choice !== "string") throw new Error(`typesafe judge: "${id}" answer has no choice`);
2331
+ return {
2332
+ type: "choice",
2333
+ choice: a.choice,
2334
+ probabilities: a.probabilities ?? {},
2335
+ confidence: num(a.confidence, "confidence")
2336
+ };
2337
+ }
2338
+ return {
2339
+ type: "score",
2340
+ score: num(a.score, "score"),
2341
+ probabilities: a.probabilities ?? {},
2342
+ legend: a.legend ?? {},
2343
+ confidence: num(a.confidence, "confidence")
2344
+ };
2345
+ }
2346
+ async function judge(req) {
2347
+ const apiKey = key();
2348
+ const res = await fetchImpl(`${base}/systemone`, {
2349
+ method: "POST",
2350
+ headers: { "content-type": "application/json", authorization: `Bearer ${apiKey}` },
2351
+ body: JSON.stringify({ model: req.spec.model, state: req.state, questions: req.questions })
2352
+ });
2353
+ if (!res.ok) {
2354
+ const errBody = await res.text().catch(() => "");
2355
+ throw new Error(`typesafe judge ${res.status}: ${errBody.slice(0, 300)}`);
2356
+ }
2357
+ const data = await res.json();
2358
+ const answers = {};
2359
+ for (const [id, q] of Object.entries(req.questions)) {
2360
+ answers[id] = mapAnswer(id, q, data.answers?.[id]);
2361
+ }
2362
+ return {
2363
+ answers,
2364
+ usage: freshUsage({
2365
+ provider: "typesafe",
2366
+ model: data.model ?? req.spec.model,
2367
+ region: regionOfHost(base),
2368
+ transport: "http",
2369
+ capability: "judge",
2370
+ inputTokens: data.usage?.input_tokens ?? 0,
2371
+ outputTokens: data.usage?.output_tokens ?? 0
2372
+ })
2373
+ };
2374
+ }
2375
+ return { name: "typesafe", judge };
2376
+ }
2377
+
2306
2378
  // src/providers/fal.ts
2307
2379
  import { deflateRawSync, crc32 } from "zlib";
2308
2380
  var sleep = (ms) => new Promise((r) => setTimeout(r, ms));
@@ -2743,6 +2815,7 @@ var defaultProviders = {
2743
2815
  azure: azureAdapter(),
2744
2816
  vertex: vertexAdapter(),
2745
2817
  deepl: deeplAdapter(),
2818
+ typesafe: typesafeAdapter(),
2746
2819
  fal: falAdapter(),
2747
2820
  bfl: bflAdapter()
2748
2821
  };
@@ -3340,6 +3413,37 @@ var moderationInputSchema = z.object({
3340
3413
  input: z.union([z.string(), z.array(z.string())]),
3341
3414
  ...callOptions
3342
3415
  });
3416
+ var judgeEntrySchema = z.union([z.string().min(1), z.null(), z.array(z.unknown()), z.record(z.unknown())]);
3417
+ var judgeQuestionSchema = z.discriminatedUnion("type", [
3418
+ z.object({
3419
+ type: z.literal("noul"),
3420
+ instructions: judgeEntrySchema,
3421
+ // An OBJECT with true/false. strict(): a typo like {yes: …} is refused here instead
3422
+ // of being forwarded and 422'd after a paid round-trip.
3423
+ criteria: z.object({ true: judgeEntrySchema.optional(), false: judgeEntrySchema.optional() }).strict().optional()
3424
+ }),
3425
+ z.object({
3426
+ type: z.literal("choice"),
3427
+ instructions: judgeEntrySchema,
3428
+ // A choice with fewer than two options is not a choice.
3429
+ criteria: z.record(z.string(), judgeEntrySchema).refine((o) => Object.keys(o).length >= 2, {
3430
+ message: "a choice needs at least 2 options in criteria"
3431
+ })
3432
+ }),
3433
+ z.object({
3434
+ type: z.literal("score"),
3435
+ instructions: judgeEntrySchema,
3436
+ // TypeSafe: a rubric of 2–10 ordered levels.
3437
+ criteria: z.array(judgeEntrySchema).min(2).max(10)
3438
+ })
3439
+ ]);
3440
+ var judgeInputSchema = z.object({
3441
+ state: z.union([z.string().min(1), z.array(z.string()), z.record(z.unknown())]),
3442
+ questions: z.record(z.string(), judgeQuestionSchema).refine((q) => Object.keys(q).length > 0, {
3443
+ message: "judge needs at least one question"
3444
+ }),
3445
+ ...callOptions
3446
+ });
3343
3447
  var pronunciationSchema = z.object({
3344
3448
  word: z.string(),
3345
3449
  alias: z.string().optional(),
@@ -3410,8 +3514,8 @@ var aiConfigSchema = z.object({
3410
3514
  import { randomUUID } from "crypto";
3411
3515
 
3412
3516
  // src/version.ts
3413
- var VERSION = "0.49.3";
3414
- var SDK_TAG = "@broberg/ai-sdk@0.49.3";
3517
+ var VERSION = "0.50.1";
3518
+ var SDK_TAG = "@broberg/ai-sdk@0.50.1";
3415
3519
 
3416
3520
  // src/cost/sinks/upmetrics.ts
3417
3521
  function isRetryableStatus(status) {
@@ -3644,6 +3748,7 @@ var DEFAULT_BFL_REFERENCE_SPEC = {
3644
3748
  };
3645
3749
  var DEFAULT_OCR_SPEC = { provider: "mistral", model: "mistral-ocr-latest", transport: "http" };
3646
3750
  var DEFAULT_MODERATION_SPEC = { provider: "mistral", model: "mistral-moderation-latest", transport: "http" };
3751
+ var DEFAULT_JUDGE_SPEC = { provider: "typesafe", model: "jev-latest", transport: "http" };
3647
3752
  var DEFAULT_PODCAST_SPEC = { provider: "elevenlabs", model: "eleven_v3", transport: "http" };
3648
3753
  var DEFAULT_TTS_SPEC = { provider: "elevenlabs", model: "eleven_multilingual_v2", transport: "http" };
3649
3754
  var DEFAULT_BATCH_SPEC = { provider: "mistral", model: "mistral-small-latest", transport: "http" };
@@ -4033,6 +4138,25 @@ function createAI(config = {}) {
4033
4138
  }
4034
4139
  });
4035
4140
  },
4141
+ async judge(input) {
4142
+ input = judgeInputSchema.parse(input);
4143
+ const stateText = typeof input.state === "string" ? input.state : JSON.stringify(input.state);
4144
+ return runCapability({
4145
+ primary: withOverride(DEFAULT_JUDGE_SPEC, input.override, "judge"),
4146
+ fallback: input.fallback,
4147
+ capability: "judge",
4148
+ purpose: input.purpose,
4149
+ labels: input.labels,
4150
+ estIn: estTokens(stateText) + estTokens(JSON.stringify(input.questions)),
4151
+ estOut: 0,
4152
+ // output tokens are free on Jev
4153
+ invoke: async (spec) => {
4154
+ const adapter = pickProvider(spec.provider);
4155
+ if (!adapter.judge) throw new Error(`createAI: provider "${spec.provider}" does not support judge`);
4156
+ return adapter.judge({ state: input.state, questions: input.questions, spec });
4157
+ }
4158
+ });
4159
+ },
4036
4160
  async podcast(input) {
4037
4161
  input = podcastInputSchema.parse(input);
4038
4162
  const inputs = input.script.map((turn) => {
@@ -4651,6 +4775,7 @@ export {
4651
4775
  toProviderTools,
4652
4776
  toolSchema,
4653
4777
  translateInputSchema,
4778
+ typesafeAdapter,
4654
4779
  upmetricsCostClient,
4655
4780
  upmetricsSink,
4656
4781
  usdFromMicro,