@agentproto/model-catalog 0.2.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.
Files changed (87) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +37 -0
  3. package/dist/access/index.d.ts +30 -0
  4. package/dist/access/index.mjs +4 -0
  5. package/dist/access/index.mjs.map +1 -0
  6. package/dist/audio/index.d.ts +84 -0
  7. package/dist/audio/index.mjs +3 -0
  8. package/dist/audio/index.mjs.map +1 -0
  9. package/dist/audio-BzVF_wke.d.ts +92 -0
  10. package/dist/base-BNHcMifc.d.ts +109 -0
  11. package/dist/byok/index.d.ts +33 -0
  12. package/dist/byok/index.mjs +3 -0
  13. package/dist/byok/index.mjs.map +1 -0
  14. package/dist/catalog-aQHd559b.d.ts +594 -0
  15. package/dist/chunk-2LWYZOWB.mjs +643 -0
  16. package/dist/chunk-2LWYZOWB.mjs.map +1 -0
  17. package/dist/chunk-CINXNTU3.mjs +134 -0
  18. package/dist/chunk-CINXNTU3.mjs.map +1 -0
  19. package/dist/chunk-F7OEHYUI.mjs +187 -0
  20. package/dist/chunk-F7OEHYUI.mjs.map +1 -0
  21. package/dist/chunk-GH7PB5FB.mjs +183 -0
  22. package/dist/chunk-GH7PB5FB.mjs.map +1 -0
  23. package/dist/chunk-H6JUTF3U.mjs +33 -0
  24. package/dist/chunk-H6JUTF3U.mjs.map +1 -0
  25. package/dist/chunk-HEDDSOZF.mjs +47 -0
  26. package/dist/chunk-HEDDSOZF.mjs.map +1 -0
  27. package/dist/chunk-HRRG2RZO.mjs +116 -0
  28. package/dist/chunk-HRRG2RZO.mjs.map +1 -0
  29. package/dist/chunk-IJZ4XPO2.mjs +606 -0
  30. package/dist/chunk-IJZ4XPO2.mjs.map +1 -0
  31. package/dist/chunk-IORIAFK2.mjs +227 -0
  32. package/dist/chunk-IORIAFK2.mjs.map +1 -0
  33. package/dist/chunk-OHAMH3ZE.mjs +2629 -0
  34. package/dist/chunk-OHAMH3ZE.mjs.map +1 -0
  35. package/dist/chunk-QHELXPSL.mjs +85 -0
  36. package/dist/chunk-QHELXPSL.mjs.map +1 -0
  37. package/dist/chunk-SGJR6ZQH.mjs +1230 -0
  38. package/dist/chunk-SGJR6ZQH.mjs.map +1 -0
  39. package/dist/chunk-WRUWNKDB.mjs +521 -0
  40. package/dist/chunk-WRUWNKDB.mjs.map +1 -0
  41. package/dist/chunk-WTHHXXIK.mjs +366 -0
  42. package/dist/chunk-WTHHXXIK.mjs.map +1 -0
  43. package/dist/chunk-XFDXTFV3.mjs +28 -0
  44. package/dist/chunk-XFDXTFV3.mjs.map +1 -0
  45. package/dist/cost/index.d.ts +114 -0
  46. package/dist/cost/index.mjs +13 -0
  47. package/dist/cost/index.mjs.map +1 -0
  48. package/dist/curation/index.d.ts +98 -0
  49. package/dist/curation/index.mjs +13 -0
  50. package/dist/curation/index.mjs.map +1 -0
  51. package/dist/evaluate-DisH3H7u.d.ts +84 -0
  52. package/dist/image/index.d.ts +79 -0
  53. package/dist/image/index.mjs +3 -0
  54. package/dist/image/index.mjs.map +1 -0
  55. package/dist/index-BICjPG8H.d.ts +132 -0
  56. package/dist/index-D8nL3fPZ.d.ts +171 -0
  57. package/dist/index.d.ts +94 -0
  58. package/dist/index.mjs +33 -0
  59. package/dist/index.mjs.map +1 -0
  60. package/dist/llm/index.d.ts +48 -0
  61. package/dist/llm/index.mjs +4 -0
  62. package/dist/llm/index.mjs.map +1 -0
  63. package/dist/overlay/index.d.ts +67 -0
  64. package/dist/overlay/index.mjs +3 -0
  65. package/dist/overlay/index.mjs.map +1 -0
  66. package/dist/picker/index.d.ts +11 -0
  67. package/dist/picker/index.mjs +26 -0
  68. package/dist/picker/index.mjs.map +1 -0
  69. package/dist/pricing/index.d.ts +106 -0
  70. package/dist/pricing/index.mjs +3 -0
  71. package/dist/pricing/index.mjs.map +1 -0
  72. package/dist/providers/index.d.ts +78 -0
  73. package/dist/providers/index.mjs +237 -0
  74. package/dist/providers/index.mjs.map +1 -0
  75. package/dist/schema/index.d.ts +627 -0
  76. package/dist/schema/index.mjs +4 -0
  77. package/dist/schema/index.mjs.map +1 -0
  78. package/dist/schema/voice.d.ts +66 -0
  79. package/dist/schema/voice.mjs +3 -0
  80. package/dist/schema/voice.mjs.map +1 -0
  81. package/dist/video/index.d.ts +78 -0
  82. package/dist/video/index.mjs +3 -0
  83. package/dist/video/index.mjs.map +1 -0
  84. package/dist/voice/index.d.ts +1300 -0
  85. package/dist/voice/index.mjs +4 -0
  86. package/dist/voice/index.mjs.map +1 -0
  87. package/package.json +137 -0
@@ -0,0 +1,183 @@
1
+ import { modelHasTag } from './chunk-WTHHXXIK.mjs';
2
+ import { getMergedOverlay } from './chunk-H6JUTF3U.mjs';
3
+ import { resolvePricing, resolveAlias, LLM_PRICING_CATALOG } from './chunk-OHAMH3ZE.mjs';
4
+ import { IMAGE_MODEL_CATALOG } from './chunk-2LWYZOWB.mjs';
5
+ import { VIDEO_MODEL_CATALOG } from './chunk-IJZ4XPO2.mjs';
6
+ import { AUDIO_MODEL_CATALOG } from './chunk-WRUWNKDB.mjs';
7
+ import { VOICE_CATALOG } from './chunk-SGJR6ZQH.mjs';
8
+
9
+ /**
10
+ * @agentproto/model-catalog v0.1.0
11
+ * Unified model catalog: LLM, image, video, audio entries + cost dispatcher + picker.
12
+ * Zod-only OSS core; @agstudio/model-catalog layers billing/access/BYOK on top.
13
+ */
14
+
15
+ // src/registry/index.ts
16
+ function followAlias(id, ov) {
17
+ let cur = id;
18
+ const seen = /* @__PURE__ */ new Set();
19
+ for (; ; ) {
20
+ const next = ov.aliases[cur];
21
+ if (next === void 0 || seen.has(cur)) break;
22
+ seen.add(cur);
23
+ cur = next;
24
+ }
25
+ return cur;
26
+ }
27
+ function overlayVoice(ov, key) {
28
+ return ov.voice.find(
29
+ (v) => v.catalogId === key || v.providerVoiceId === key || (v.aliases ?? []).includes(key)
30
+ );
31
+ }
32
+ var VOICE_INDEX = new Map([
33
+ ...VOICE_CATALOG.map((v) => [v.providerVoiceId, v]),
34
+ ...VOICE_CATALOG.map((v) => [v.catalogId, v]),
35
+ ...VOICE_CATALOG.flatMap(
36
+ (v) => (v.aliases ?? []).map((a) => [a, v])
37
+ )
38
+ ]);
39
+ function getModel(id) {
40
+ const ov = getMergedOverlay();
41
+ const key = followAlias(id, ov);
42
+ if (ov.image[key]) return { kind: "image", id, def: ov.image[key] };
43
+ if (ov.video[key]) return { kind: "video", id, def: ov.video[key] };
44
+ if (ov.audio[key]) return { kind: "audio", id, def: ov.audio[key] };
45
+ const ovVoice = overlayVoice(ov, key);
46
+ if (ovVoice) return { kind: "voice", id, voice: ovVoice };
47
+ if (ov.llm[key]) return { kind: "llm", id, canonicalId: key, pricing: ov.llm[key] };
48
+ const imageDef = IMAGE_MODEL_CATALOG[key];
49
+ if (imageDef) return { kind: "image", id, def: imageDef };
50
+ const videoDef = VIDEO_MODEL_CATALOG[key];
51
+ if (videoDef) return { kind: "video", id, def: videoDef };
52
+ const audioDef = AUDIO_MODEL_CATALOG[key];
53
+ if (audioDef) return { kind: "audio", id, def: audioDef };
54
+ const voice = VOICE_INDEX.get(key);
55
+ if (voice) return { kind: "voice", id, voice };
56
+ const llmPricing = resolvePricing(key);
57
+ if (llmPricing) {
58
+ const canonicalId = resolveAlias(key);
59
+ return { kind: "llm", id, canonicalId, pricing: llmPricing };
60
+ }
61
+ return void 0;
62
+ }
63
+ function isAgentVisible(id) {
64
+ const m = getModel(id);
65
+ if (!m) return false;
66
+ switch (m.kind) {
67
+ case "image":
68
+ return m.def.agentVisible;
69
+ case "video":
70
+ return m.def.agentVisible;
71
+ case "audio":
72
+ return m.def.agentVisible;
73
+ case "llm":
74
+ return id in LLM_PRICING_CATALOG || resolveAlias(id) in LLM_PRICING_CATALOG;
75
+ case "voice":
76
+ return true;
77
+ }
78
+ }
79
+ function listModels(filter = {}) {
80
+ const providerSet = filter.provider || filter.providers ? /* @__PURE__ */ new Set([
81
+ ...filter.provider ? [filter.provider] : [],
82
+ ...filter.providers ?? []
83
+ ]) : void 0;
84
+ const providerMatches = (provider) => !providerSet || provider !== void 0 && providerSet.has(provider);
85
+ const ov = getMergedOverlay();
86
+ const out = [];
87
+ const seen = { image: /* @__PURE__ */ new Set(), video: /* @__PURE__ */ new Set(), audio: /* @__PURE__ */ new Set(), voice: /* @__PURE__ */ new Set(), llm: /* @__PURE__ */ new Set() };
88
+ if (!filter.kind || filter.kind === "image") {
89
+ for (const [id, def] of Object.entries(ov.image)) {
90
+ if (!providerMatches(def.provider)) continue;
91
+ if (filter.agentVisibleOnly && !def.agentVisible) continue;
92
+ seen.image.add(id);
93
+ out.push({ kind: "image", id, def });
94
+ }
95
+ for (const [id, def] of Object.entries(IMAGE_MODEL_CATALOG)) {
96
+ if (seen.image.has(id) || !providerMatches(def.provider)) continue;
97
+ if (filter.agentVisibleOnly && !def.agentVisible) continue;
98
+ out.push({ kind: "image", id, def });
99
+ }
100
+ }
101
+ if (!filter.kind || filter.kind === "video") {
102
+ for (const [id, def] of Object.entries(ov.video)) {
103
+ if (!providerMatches(def.provider)) continue;
104
+ if (filter.agentVisibleOnly && !def.agentVisible) continue;
105
+ seen.video.add(id);
106
+ out.push({ kind: "video", id, def });
107
+ }
108
+ for (const [id, def] of Object.entries(VIDEO_MODEL_CATALOG)) {
109
+ if (seen.video.has(id) || !providerMatches(def.provider)) continue;
110
+ if (filter.agentVisibleOnly && !def.agentVisible) continue;
111
+ out.push({ kind: "video", id, def });
112
+ }
113
+ }
114
+ if (!filter.kind || filter.kind === "audio") {
115
+ for (const [id, def] of Object.entries(ov.audio)) {
116
+ if (!providerMatches(def.provider)) continue;
117
+ if (filter.agentVisibleOnly && !def.agentVisible) continue;
118
+ seen.audio.add(id);
119
+ out.push({ kind: "audio", id, def });
120
+ }
121
+ for (const [id, def] of Object.entries(AUDIO_MODEL_CATALOG)) {
122
+ if (seen.audio.has(id) || !providerMatches(def.provider)) continue;
123
+ if (filter.agentVisibleOnly && !def.agentVisible) continue;
124
+ out.push({ kind: "audio", id, def });
125
+ }
126
+ }
127
+ if (!filter.kind || filter.kind === "voice") {
128
+ for (const v of ov.voice) {
129
+ if (!providerMatches(v.provider)) continue;
130
+ seen.voice.add(v.catalogId);
131
+ out.push({ kind: "voice", id: v.catalogId, voice: v });
132
+ }
133
+ for (const v of VOICE_CATALOG) {
134
+ if (seen.voice.has(v.catalogId) || !providerMatches(v.provider)) continue;
135
+ out.push({ kind: "voice", id: v.catalogId, voice: v });
136
+ }
137
+ }
138
+ if (!filter.kind || filter.kind === "llm") {
139
+ for (const [id, pricing] of Object.entries(ov.llm)) {
140
+ if (!providerMatches(pricing.provider)) continue;
141
+ seen.llm.add(id);
142
+ out.push({ kind: "llm", id, canonicalId: id, pricing });
143
+ }
144
+ for (const [id, pricing] of Object.entries(LLM_PRICING_CATALOG)) {
145
+ if (seen.llm.has(id) || !providerMatches(pricing.provider)) continue;
146
+ out.push({ kind: "llm", id, canonicalId: id, pricing });
147
+ }
148
+ }
149
+ const required = [...filter.capabilities ?? [], ...filter.tags ?? []];
150
+ if (required.length === 0) return out;
151
+ return out.filter((model) => required.every((tag) => modelHasTag(model, tag)));
152
+ }
153
+ function getModelsByProvider(provider) {
154
+ return listModels({ provider });
155
+ }
156
+ function getStaticModelProvider(model) {
157
+ switch (model.kind) {
158
+ case "image":
159
+ case "video":
160
+ case "audio":
161
+ return model.def.provider;
162
+ case "voice":
163
+ return model.voice.provider;
164
+ case "llm":
165
+ return void 0;
166
+ }
167
+ }
168
+ function resolveAlias2(id) {
169
+ const ov = getMergedOverlay();
170
+ const key = followAlias(id, ov);
171
+ if (ov.image[key] || ov.video[key] || ov.audio[key]) return key;
172
+ if (overlayVoice(ov, key)) return key;
173
+ if (ov.llm[key]) return key;
174
+ if (IMAGE_MODEL_CATALOG[key]) return key;
175
+ if (VIDEO_MODEL_CATALOG[key]) return key;
176
+ if (AUDIO_MODEL_CATALOG[key]) return key;
177
+ if (VOICE_INDEX.has(key)) return key;
178
+ return resolveAlias(key);
179
+ }
180
+
181
+ export { getModel, getModelsByProvider, getStaticModelProvider, isAgentVisible, listModels, resolveAlias2 as resolveAlias };
182
+ //# sourceMappingURL=chunk-GH7PB5FB.mjs.map
183
+ //# sourceMappingURL=chunk-GH7PB5FB.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/registry/index.ts"],"names":["resolveAlias"],"mappings":"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","file":"chunk-GH7PB5FB.mjs","sourcesContent":["/**\n * Cross-kind registry. Resolves a model id against every catalog (LLM,\n * image, video, audio) and returns a discriminated `ResolvedModel`.\n *\n * v1 returns the legacy per-kind data shapes verbatim — image/video keep\n * `*ModelDefinition`, LLM keeps `LLMPricing`, audio surfaces voices.\n * Callers (cost dispatcher, access evaluator) match on `.kind` and dig\n * into the per-kind payload as needed.\n */\n\nimport {\n LLM_PRICING_CATALOG,\n resolveAlias as resolveLlmAlias,\n resolvePricing,\n type LLMPricing,\n} from \"../llm/index.js\"\nimport {\n IMAGE_MODEL_CATALOG,\n type ImageModelDefinition,\n} from \"../image/index.js\"\nimport {\n VIDEO_MODEL_CATALOG,\n type VideoModelDefinition,\n} from \"../video/index.js\"\nimport {\n AUDIO_MODEL_CATALOG,\n type AudioModelDefinition,\n} from \"../audio/index.js\"\nimport { VOICE_CATALOG, type CatalogVoice } from \"../voice/index.js\"\nimport { modelHasTag } from \"../enrichment/index.js\"\nimport { getMergedOverlay, type MergedOverlay } from \"../overlay/index.js\"\n\n/** Follow consumer-overlay alias hops to a concrete id (cycle-guarded).\n * Ids with no overlay alias pass through unchanged. */\nfunction followAlias(id: string, ov: MergedOverlay): string {\n let cur = id\n const seen = new Set<string>()\n for (;;) {\n const next = ov.aliases[cur]\n if (next === undefined || seen.has(cur)) break\n seen.add(cur)\n cur = next\n }\n return cur\n}\n\n/** Find an overlay voice by catalogId / providerVoiceId / alias. */\nfunction overlayVoice(ov: MergedOverlay, key: string): CatalogVoice | undefined {\n return ov.voice.find(\n v =>\n v.catalogId === key ||\n v.providerVoiceId === key ||\n (v.aliases ?? []).includes(key),\n )\n}\n\n/**\n * `audio` resolves to either an audio MODEL (priced TTS/STT) or a\n * voice (downstream metadata). Most call sites care about the model\n * (pricing, capabilities); a few (voice pickers) want the voice. They\n * share the resolver but discriminate on the inner payload.\n */\nexport type ResolvedModel =\n | { kind: \"llm\"; id: string; canonicalId: string; pricing: LLMPricing }\n | { kind: \"image\"; id: string; def: ImageModelDefinition }\n | { kind: \"video\"; id: string; def: VideoModelDefinition }\n | { kind: \"audio\"; id: string; def: AudioModelDefinition }\n | { kind: \"voice\"; id: string; voice: CatalogVoice }\n\n/**\n * Every kind the registry can resolve. Broader than the catalog\n * `ModelKind` (llm/image/video/audio) because voices are a first-class\n * registry kind but not a priced model family. Derived from\n * `ResolvedModel` so it can never drift.\n */\nexport type CatalogKind = ResolvedModel[\"kind\"]\n\n// Voices resolve by canonical id (\"minimax-french-female-anchor\"), native\n// provider id (\"French_FemaleAnchor\"), or a consumer-overlay alias — index\n// all three so new slugs, native ids, and persisted operator rows all hit.\nconst VOICE_INDEX: Map<string, CatalogVoice> = new Map([\n ...VOICE_CATALOG.map(v => [v.providerVoiceId, v] as const),\n ...VOICE_CATALOG.map(v => [v.catalogId, v] as const),\n ...VOICE_CATALOG.flatMap((v: CatalogVoice) =>\n (v.aliases ?? []).map(a => [a, v] as const)\n ),\n])\n\n/**\n * Resolves a model id across every catalog.\n *\n * Lookup order: image → video → audio → LLM (last because LLM has the\n * partial-prefix fallback that can match other strings if probed first).\n *\n * Returns `undefined` for unknown ids — callers decide whether to throw\n * or fall back. LLM uses fallback pricing inside its own dispatcher\n * (preserves existing behavior at the orchestrator).\n */\nexport function getModel(id: string): ResolvedModel | undefined {\n // Consumer overlays first: resolve any overlay alias to a concrete id,\n // then let overlay entries override/extend the generated catalogs. The\n // returned `id` echoes what the caller asked for (alias-preserving), as\n // the legacy voice-alias path already did.\n const ov = getMergedOverlay()\n const key = followAlias(id, ov)\n\n if (ov.image[key]) return { kind: \"image\", id, def: ov.image[key]! }\n if (ov.video[key]) return { kind: \"video\", id, def: ov.video[key]! }\n if (ov.audio[key]) return { kind: \"audio\", id, def: ov.audio[key]! }\n const ovVoice = overlayVoice(ov, key)\n if (ovVoice) return { kind: \"voice\", id, voice: ovVoice }\n if (ov.llm[key]) return { kind: \"llm\", id, canonicalId: key, pricing: ov.llm[key]! }\n\n const imageDef = IMAGE_MODEL_CATALOG[key]\n if (imageDef) return { kind: \"image\", id, def: imageDef }\n\n const videoDef = VIDEO_MODEL_CATALOG[key]\n if (videoDef) return { kind: \"video\", id, def: videoDef }\n\n // Audio MODEL (priced TTS/STT) — checked before voice lookup so an\n // id that exists in both maps lands on the priced entry.\n const audioDef = AUDIO_MODEL_CATALOG[key]\n if (audioDef) return { kind: \"audio\", id, def: audioDef }\n\n const voice = VOICE_INDEX.get(key)\n if (voice) return { kind: \"voice\", id, voice }\n\n const llmPricing = resolvePricing(key)\n if (llmPricing) {\n const canonicalId = resolveLlmAlias(key)\n return { kind: \"llm\", id, canonicalId, pricing: llmPricing }\n }\n\n return undefined\n}\n\n/**\n * Returns true iff the id is exposed to agents.\n *\n * - LLM: every entry in `LLM_PRICING_CATALOG` is considered visible\n * (legacy convention; refine in v2 with per-entry `agentVisible`).\n * - Image / Video: respects the `agentVisible` flag.\n * - Audio voices: all voices are visible (catalog itself is curated).\n */\nexport function isAgentVisible(id: string): boolean {\n const m = getModel(id)\n if (!m) return false\n switch (m.kind) {\n case \"image\":\n return m.def.agentVisible\n case \"video\":\n return m.def.agentVisible\n case \"audio\":\n return m.def.agentVisible\n case \"llm\":\n return (\n id in LLM_PRICING_CATALOG || resolveLlmAlias(id) in LLM_PRICING_CATALOG\n )\n case \"voice\":\n return true\n }\n}\n\nexport interface ListModelsFilter {\n kind?: CatalogKind\n /** Single provider (back-compat). Folded into `providers` internally. */\n provider?: string\n /** Provider OR-set — a model matches if its provider is any of these. */\n providers?: string[]\n /**\n * Capability tags a model must carry (ALL of them). Capabilities are\n * emitted as tags by enrichment — audio `tts`/`stt`/`s2s`, image\n * `edit`/`upscale`, video `i2v`/`t2v` — so this filters on the same\n * vocabulary `listModels({capabilities:[\"s2s\"]})` would expect.\n */\n capabilities?: string[]\n /** Arbitrary tags a model must carry (ALL of them). */\n tags?: string[]\n agentVisibleOnly?: boolean\n}\n\n/**\n * Cross-kind listing. Useful for catalog browsers and capability search.\n * Returns `ResolvedModel[]` (discriminated by kind).\n *\n * Provider is an OR-set (`providers` ∪ the singular `provider`); capability\n * and tag filters are AND-sets (a model must carry every listed value).\n * Composed: `listModels({ kind:\"audio\", capabilities:[\"s2s\"],\n * providers:[\"gemini-live\",\"openai-realtime\"] })` → all s2s models from\n * Gemini OR OpenAI.\n */\nexport function listModels(filter: ListModelsFilter = {}): ResolvedModel[] {\n const providerSet =\n filter.provider || filter.providers\n ? new Set([\n ...(filter.provider ? [filter.provider] : []),\n ...(filter.providers ?? []),\n ])\n : undefined\n const providerMatches = (provider: string | undefined): boolean =>\n !providerSet || (provider !== undefined && providerSet.has(provider))\n\n const ov = getMergedOverlay()\n const out: ResolvedModel[] = []\n // Overlay entries override core ones with the same id, so collect ids the\n // overlay has already supplied per kind and skip the core duplicate.\n const seen = { image: new Set<string>(), video: new Set<string>(), audio: new Set<string>(), voice: new Set<string>(), llm: new Set<string>() }\n\n if (!filter.kind || filter.kind === \"image\") {\n for (const [id, def] of Object.entries(ov.image)) {\n if (!providerMatches(def.provider)) continue\n if (filter.agentVisibleOnly && !def.agentVisible) continue\n seen.image.add(id)\n out.push({ kind: \"image\", id, def })\n }\n for (const [id, def] of Object.entries(IMAGE_MODEL_CATALOG)) {\n if (seen.image.has(id) || !providerMatches(def.provider)) continue\n if (filter.agentVisibleOnly && !def.agentVisible) continue\n out.push({ kind: \"image\", id, def })\n }\n }\n if (!filter.kind || filter.kind === \"video\") {\n for (const [id, def] of Object.entries(ov.video)) {\n if (!providerMatches(def.provider)) continue\n if (filter.agentVisibleOnly && !def.agentVisible) continue\n seen.video.add(id)\n out.push({ kind: \"video\", id, def })\n }\n for (const [id, def] of Object.entries(VIDEO_MODEL_CATALOG)) {\n if (seen.video.has(id) || !providerMatches(def.provider)) continue\n if (filter.agentVisibleOnly && !def.agentVisible) continue\n out.push({ kind: \"video\", id, def })\n }\n }\n if (!filter.kind || filter.kind === \"audio\") {\n for (const [id, def] of Object.entries(ov.audio)) {\n if (!providerMatches(def.provider)) continue\n if (filter.agentVisibleOnly && !def.agentVisible) continue\n seen.audio.add(id)\n out.push({ kind: \"audio\", id, def })\n }\n for (const [id, def] of Object.entries(AUDIO_MODEL_CATALOG)) {\n if (seen.audio.has(id) || !providerMatches(def.provider)) continue\n if (filter.agentVisibleOnly && !def.agentVisible) continue\n out.push({ kind: \"audio\", id, def })\n }\n }\n if (!filter.kind || filter.kind === \"voice\") {\n for (const v of ov.voice) {\n if (!providerMatches(v.provider)) continue\n seen.voice.add(v.catalogId)\n out.push({ kind: \"voice\", id: v.catalogId, voice: v })\n }\n for (const v of VOICE_CATALOG) {\n if (seen.voice.has(v.catalogId) || !providerMatches(v.provider)) continue\n out.push({ kind: \"voice\", id: v.catalogId, voice: v })\n }\n }\n if (!filter.kind || filter.kind === \"llm\") {\n for (const [id, pricing] of Object.entries(ov.llm)) {\n if (!providerMatches(pricing.provider)) continue\n seen.llm.add(id)\n out.push({ kind: \"llm\", id, canonicalId: id, pricing })\n }\n for (const [id, pricing] of Object.entries(LLM_PRICING_CATALOG)) {\n if (seen.llm.has(id) || !providerMatches(pricing.provider)) continue\n out.push({ kind: \"llm\", id, canonicalId: id, pricing })\n }\n }\n\n // Capability + tag filters are AND-sets, applied over the enrichment tag\n // vocabulary (capabilities are emitted as tags). Skipped entirely when\n // unset so the common path stays allocation-free.\n const required = [...(filter.capabilities ?? []), ...(filter.tags ?? [])]\n if (required.length === 0) return out\n return out.filter(model => required.every(tag => modelHasTag(model, tag)))\n}\n\n/**\n * Provider-centric view — every model a provider offers across all kinds\n * (llm / image / video / audio incl. tts/stt/s2s). This is the \"provider\n * catalog\" (openai, google, anthropic, replicate, openrouter, …): a\n * derived query over the kind-organized catalogs, NOT a parallel store.\n */\nexport function getModelsByProvider(provider: string): ResolvedModel[] {\n return listModels({ provider })\n}\n\n/**\n * Returns the platform a model's adapter is **statically pinned** to,\n * if known. ONLY meaningful for image / video / audio — those adapters\n * are constructed per-platform (Replicate adapter, MiniMax adapter…),\n * so the model's `def.provider` IS the runtime connector.\n *\n * For LLM, returns `undefined` — the runtime connector is determined\n * at call time by `pickLlmConnector(connectors, parseModelId(id).provider)`\n * against the guild's installed connectors, NOT by the model id alone.\n * The same `anthropic/claude-sonnet-4-5` can route through OpenRouter\n * OR direct Anthropic depending on which connectors the guild has\n * installed. Recording call sites MUST pass the actual `connectorUsed`\n * they invoked; do not infer from id.\n *\n * Use case: image/video workflow steps fetch `model.def.provider`\n * directly (no ambiguity). LLM call sites use the runtime resolver.\n */\nexport function getStaticModelProvider(\n model: ResolvedModel\n): string | undefined {\n switch (model.kind) {\n case \"image\":\n case \"video\":\n case \"audio\":\n return model.def.provider\n case \"voice\":\n return model.voice.provider\n case \"llm\":\n // Runtime routing — caller knows, we don't.\n return undefined\n }\n}\n\n/**\n * Resolves an id to its canonical catalog key across kinds. For ids that\n * have no alias, returns the id unchanged. Used by callers that want to\n * normalize before persisting (e.g., `byok_usage` rows).\n */\nexport function resolveAlias(id: string): string {\n const ov = getMergedOverlay()\n const key = followAlias(id, ov)\n if (ov.image[key] || ov.video[key] || ov.audio[key]) return key\n if (overlayVoice(ov, key)) return key\n if (ov.llm[key]) return key\n if (IMAGE_MODEL_CATALOG[key]) return key\n if (VIDEO_MODEL_CATALOG[key]) return key\n if (AUDIO_MODEL_CATALOG[key]) return key\n if (VOICE_INDEX.has(key)) return key\n return resolveLlmAlias(key)\n}\n"]}
@@ -0,0 +1,33 @@
1
+ /**
2
+ * @agentproto/model-catalog v0.1.0
3
+ * Unified model catalog: LLM, image, video, audio entries + cost dispatcher + picker.
4
+ * Zod-only OSS core; @agstudio/model-catalog layers billing/access/BYOK on top.
5
+ */
6
+
7
+ // src/overlay/index.ts
8
+ function emptyMerged() {
9
+ return { aliases: {}, llm: {}, image: {}, video: {}, audio: {}, voice: [] };
10
+ }
11
+ var MERGED = emptyMerged();
12
+ function registerCatalogOverlay(overlay) {
13
+ if (overlay.aliases) Object.assign(MERGED.aliases, overlay.aliases);
14
+ if (overlay.llm) Object.assign(MERGED.llm, overlay.llm);
15
+ if (overlay.image) Object.assign(MERGED.image, overlay.image);
16
+ if (overlay.video) Object.assign(MERGED.video, overlay.video);
17
+ if (overlay.audio) Object.assign(MERGED.audio, overlay.audio);
18
+ if (overlay.voice) {
19
+ const byId = new Map(MERGED.voice.map((v) => [v.catalogId, v]));
20
+ for (const v of overlay.voice) byId.set(v.catalogId, v);
21
+ MERGED.voice = [...byId.values()];
22
+ }
23
+ }
24
+ function getMergedOverlay() {
25
+ return MERGED;
26
+ }
27
+ function clearCatalogOverlays() {
28
+ MERGED = emptyMerged();
29
+ }
30
+
31
+ export { clearCatalogOverlays, getMergedOverlay, registerCatalogOverlay };
32
+ //# sourceMappingURL=chunk-H6JUTF3U.mjs.map
33
+ //# sourceMappingURL=chunk-H6JUTF3U.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/overlay/index.ts"],"names":[],"mappings":";;;;;;;AAyDA,SAAS,WAAA,GAA6B;AACpC,EAAA,OAAO,EAAE,OAAA,EAAS,IAAI,GAAA,EAAK,IAAI,KAAA,EAAO,EAAC,EAAG,KAAA,EAAO,EAAC,EAAG,KAAA,EAAO,EAAC,EAAG,KAAA,EAAO,EAAC,EAAE;AAC5E;AAEA,IAAI,SAAwB,WAAA,EAAY;AAIjC,SAAS,uBAAuB,OAAA,EAA+B;AACpE,EAAA,IAAI,QAAQ,OAAA,EAAS,MAAA,CAAO,OAAO,MAAA,CAAO,OAAA,EAAS,QAAQ,OAAO,CAAA;AAClE,EAAA,IAAI,QAAQ,GAAA,EAAK,MAAA,CAAO,OAAO,MAAA,CAAO,GAAA,EAAK,QAAQ,GAAG,CAAA;AACtD,EAAA,IAAI,QAAQ,KAAA,EAAO,MAAA,CAAO,OAAO,MAAA,CAAO,KAAA,EAAO,QAAQ,KAAK,CAAA;AAC5D,EAAA,IAAI,QAAQ,KAAA,EAAO,MAAA,CAAO,OAAO,MAAA,CAAO,KAAA,EAAO,QAAQ,KAAK,CAAA;AAC5D,EAAA,IAAI,QAAQ,KAAA,EAAO,MAAA,CAAO,OAAO,MAAA,CAAO,KAAA,EAAO,QAAQ,KAAK,CAAA;AAC5D,EAAA,IAAI,QAAQ,KAAA,EAAO;AAEjB,IAAA,MAAM,IAAA,GAAO,IAAI,GAAA,CAAI,MAAA,CAAO,KAAA,CAAM,GAAA,CAAI,CAAA,CAAA,KAAK,CAAC,CAAA,CAAE,SAAA,EAAW,CAAC,CAAC,CAAC,CAAA;AAC5D,IAAA,KAAA,MAAW,KAAK,OAAA,CAAQ,KAAA,OAAY,GAAA,CAAI,CAAA,CAAE,WAAW,CAAC,CAAA;AACtD,IAAA,MAAA,CAAO,KAAA,GAAQ,CAAC,GAAG,IAAA,CAAK,QAAQ,CAAA;AAAA,EAClC;AACF;AAGO,SAAS,gBAAA,GAAkC;AAChD,EAAA,OAAO,MAAA;AACT;AAGO,SAAS,oBAAA,GAA6B;AAC3C,EAAA,MAAA,GAAS,WAAA,EAAY;AACvB","file":"chunk-H6JUTF3U.mjs","sourcesContent":["/**\n * Consumer catalog overlays.\n *\n * The OSS core ships only generated, provider-native data. Anything\n * app-specific — legacy alias slugs persisted in an app's DB (e.g. a product's\n * legacy voice ids), a private fine-tune, a renamed model — is registered\n * by the consumer at boot as an **overlay**, never patched into the core data.\n *\n * import { registerCatalogOverlay } from \"@agentproto/model-catalog/overlay\"\n * registerCatalogOverlay({\n * aliases: { \"legacy-voice-1\": \"minimax-french-female-anchor\" },\n * image: { \"my-finetune\": { …ImageModelDefinition } },\n * })\n *\n * The registry (`getModel` / `resolveAlias` / `listModels`) consults the merged\n * overlay first, then falls through to the generated catalogs. Registration is\n * additive and idempotent-by-key (a later overlay overrides an earlier one for\n * the same id). `clearCatalogOverlays()` resets — used by tests.\n */\n\nimport type { LLMPricing } from \"../llm/index.js\"\nimport type { ImageModelDefinition } from \"../image/index.js\"\nimport type { VideoModelDefinition } from \"../video/index.js\"\nimport type { AudioModelDefinition } from \"../audio/index.js\"\nimport type { CatalogVoice } from \"../voice/index.js\"\n\nexport interface CatalogOverlay {\n /**\n * Extra alias → canonical-id mappings, any kind. Resolution is followed\n * (chains collapse), so `{ \"legacy-voice-1\": \"minimax-…\" }` makes `getModel`,\n * `resolveAlias`, and `isAgentVisible` all treat `legacy-voice-1` as the minimax\n * voice. The target should be a real id in the core catalog or this overlay.\n */\n aliases?: Record<string, string>\n /** Extra or overriding LLM pricing entries, keyed by model id. */\n llm?: Record<string, LLMPricing>\n /** Extra or overriding image model entries, keyed by model id. */\n image?: Record<string, ImageModelDefinition>\n /** Extra or overriding video model entries, keyed by model id. */\n video?: Record<string, VideoModelDefinition>\n /** Extra or overriding audio model entries, keyed by model id. */\n audio?: Record<string, AudioModelDefinition>\n /** Extra voices (matched by catalogId / providerVoiceId / their aliases). */\n voice?: CatalogVoice[]\n}\n\n/** The merged, registered overlay state. Same shape as a single overlay but\n * with all registrations folded in (last-write-wins per key). */\nexport interface MergedOverlay {\n aliases: Record<string, string>\n llm: Record<string, LLMPricing>\n image: Record<string, ImageModelDefinition>\n video: Record<string, VideoModelDefinition>\n audio: Record<string, AudioModelDefinition>\n voice: CatalogVoice[]\n}\n\nfunction emptyMerged(): MergedOverlay {\n return { aliases: {}, llm: {}, image: {}, video: {}, audio: {}, voice: [] }\n}\n\nlet MERGED: MergedOverlay = emptyMerged()\n\n/** Register a consumer overlay. Folds into the merged state; later\n * registrations override earlier ones for the same id. */\nexport function registerCatalogOverlay(overlay: CatalogOverlay): void {\n if (overlay.aliases) Object.assign(MERGED.aliases, overlay.aliases)\n if (overlay.llm) Object.assign(MERGED.llm, overlay.llm)\n if (overlay.image) Object.assign(MERGED.image, overlay.image)\n if (overlay.video) Object.assign(MERGED.video, overlay.video)\n if (overlay.audio) Object.assign(MERGED.audio, overlay.audio)\n if (overlay.voice) {\n // Replace-by-catalogId so re-registering a voice overrides cleanly.\n const byId = new Map(MERGED.voice.map(v => [v.catalogId, v]))\n for (const v of overlay.voice) byId.set(v.catalogId, v)\n MERGED.voice = [...byId.values()]\n }\n}\n\n/** The current merged overlay. Read-only snapshot for the registry. */\nexport function getMergedOverlay(): MergedOverlay {\n return MERGED\n}\n\n/** Reset all overlays (tests / re-init). */\nexport function clearCatalogOverlays(): void {\n MERGED = emptyMerged()\n}\n"]}
@@ -0,0 +1,47 @@
1
+ import { z } from 'zod';
2
+
3
+ /**
4
+ * @agentproto/model-catalog v0.1.0
5
+ * Unified model catalog: LLM, image, video, audio entries + cost dispatcher + picker.
6
+ * Zod-only OSS core; @agstudio/model-catalog layers billing/access/BYOK on top.
7
+ */
8
+
9
+ var CatalogVoiceProviderSchema = z.enum([
10
+ "minimax",
11
+ "elevenlabs",
12
+ "openai-realtime",
13
+ "gemini-live"
14
+ ]);
15
+ var CatalogVoiceSchema = z.object({
16
+ /** Stable provider-native slug (kept across renames). */
17
+ catalogId: z.string(),
18
+ /** Native provider id: "French_FemaleAnchor", "alloy", "Aoede", … */
19
+ providerVoiceId: z.string(),
20
+ provider: CatalogVoiceProviderSchema,
21
+ /** User-facing natural name: "Élise", "Camille", … */
22
+ label: z.string(),
23
+ description: z.string(),
24
+ gender: z.enum(["female", "male", "neutral"]),
25
+ /** Best language: "fr", "en", … */
26
+ primaryLanguage: z.string(),
27
+ /** Every language the voice can speak. */
28
+ supportedLanguages: z.array(z.string()).readonly(),
29
+ /** Storage path for an audio sample, when one exists. */
30
+ samplePath: z.string().optional(),
31
+ /**
32
+ * Legacy / alternate ids that also resolve to this voice (e.g. the old
33
+ * an app-side alias a renamed voice used to carry). Keeps persisted
34
+ * `operator.voiceId` rows + saved references working across renames.
35
+ */
36
+ aliases: z.array(z.string()).readonly().optional(),
37
+ /** Provider-native age bucket (surfaced by some adapters). */
38
+ age: z.enum(["child", "young", "adult", "senior"]).optional(),
39
+ /** Provider-native quality descriptor ("premium", "standard", …). */
40
+ quality: z.string().optional(),
41
+ /** Show in the voice-picker UI. */
42
+ featured: z.boolean().optional()
43
+ });
44
+
45
+ export { CatalogVoiceProviderSchema, CatalogVoiceSchema };
46
+ //# sourceMappingURL=chunk-HEDDSOZF.mjs.map
47
+ //# sourceMappingURL=chunk-HEDDSOZF.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/schema/voice.ts"],"names":[],"mappings":";;;;;;;;AAiBO,IAAM,0BAAA,GAA6B,EAAE,IAAA,CAAK;AAAA,EAC/C,SAAA;AAAA,EACA,YAAA;AAAA,EACA,iBAAA;AAAA,EACA;AACF,CAAC;AAaM,IAAM,kBAAA,GAAqB,EAAE,MAAA,CAAO;AAAA;AAAA,EAEzC,SAAA,EAAW,EAAE,MAAA,EAAO;AAAA;AAAA,EAEpB,eAAA,EAAiB,EAAE,MAAA,EAAO;AAAA,EAC1B,QAAA,EAAU,0BAAA;AAAA;AAAA,EAEV,KAAA,EAAO,EAAE,MAAA,EAAO;AAAA,EAChB,WAAA,EAAa,EAAE,MAAA,EAAO;AAAA,EACtB,QAAQ,CAAA,CAAE,IAAA,CAAK,CAAC,QAAA,EAAU,MAAA,EAAQ,SAAS,CAAC,CAAA;AAAA;AAAA,EAE5C,eAAA,EAAiB,EAAE,MAAA,EAAO;AAAA;AAAA,EAE1B,oBAAoB,CAAA,CAAE,KAAA,CAAM,EAAE,MAAA,EAAQ,EAAE,QAAA,EAAS;AAAA;AAAA,EAEjD,UAAA,EAAY,CAAA,CAAE,MAAA,EAAO,CAAE,QAAA,EAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAMhC,OAAA,EAAS,EAAE,KAAA,CAAM,CAAA,CAAE,QAAQ,CAAA,CAAE,QAAA,EAAS,CAAE,QAAA,EAAS;AAAA;AAAA,EAEjD,GAAA,EAAK,CAAA,CAAE,IAAA,CAAK,CAAC,OAAA,EAAS,SAAS,OAAA,EAAS,QAAQ,CAAC,CAAA,CAAE,QAAA,EAAS;AAAA;AAAA,EAE5D,OAAA,EAAS,CAAA,CAAE,MAAA,EAAO,CAAE,QAAA,EAAS;AAAA;AAAA,EAE7B,QAAA,EAAU,CAAA,CAAE,OAAA,EAAQ,CAAE,QAAA;AACxB,CAAC","file":"chunk-HEDDSOZF.mjs","sourcesContent":["import { z } from \"zod\"\n\n/**\n * TTS / realtime providers a catalog voice routes to. Each voice belongs\n * to exactly one — the voice service picks the right adapter on dispatch.\n *\n * - `elevenlabs` — HTTP TTS streaming (sequential engine)\n * - `minimax` — HTTP TTS streaming (sequential engine)\n * - `openai-realtime` — bidirectional realtime WS (s2s engine). Voice id\n * is the OpenAI voice name (`alloy`, `marin`, …).\n * - `gemini-live` — Google Live API (s2s engine). Voice id is the\n * named speaker (`Aoede`, `Charon`, …).\n *\n * Note: these are the voice-ENGINE provider tags, distinct from the\n * company provider tags audio MODELS carry (`google`/`openai`). A voice's\n * `gemini-live` ↔ the audio model `google/gemini-live` are the same engine.\n */\nexport const CatalogVoiceProviderSchema = z.enum([\n \"minimax\",\n \"elevenlabs\",\n \"openai-realtime\",\n \"gemini-live\",\n])\nexport type CatalogVoiceProvider = z.infer<typeof CatalogVoiceProviderSchema>\n\n/**\n * A curated, provider-neutral voice. Carries both the selection metadata\n * the picker shows (label, description, featured, samplePath) and the\n * provider-native descriptors an adapter may surface (age, quality) — one\n * entry serves both the UI and the execution adapter.\n *\n * `catalogId` is the stable provider-native slug saved on\n * `operator.voiceId` rows; renaming the `label` is pure UX and never\n * orphans a saved reference.\n */\nexport const CatalogVoiceSchema = z.object({\n /** Stable provider-native slug (kept across renames). */\n catalogId: z.string(),\n /** Native provider id: \"French_FemaleAnchor\", \"alloy\", \"Aoede\", … */\n providerVoiceId: z.string(),\n provider: CatalogVoiceProviderSchema,\n /** User-facing natural name: \"Élise\", \"Camille\", … */\n label: z.string(),\n description: z.string(),\n gender: z.enum([\"female\", \"male\", \"neutral\"]),\n /** Best language: \"fr\", \"en\", … */\n primaryLanguage: z.string(),\n /** Every language the voice can speak. */\n supportedLanguages: z.array(z.string()).readonly(),\n /** Storage path for an audio sample, when one exists. */\n samplePath: z.string().optional(),\n /**\n * Legacy / alternate ids that also resolve to this voice (e.g. the old\n * an app-side alias a renamed voice used to carry). Keeps persisted\n * `operator.voiceId` rows + saved references working across renames.\n */\n aliases: z.array(z.string()).readonly().optional(),\n /** Provider-native age bucket (surfaced by some adapters). */\n age: z.enum([\"child\", \"young\", \"adult\", \"senior\"]).optional(),\n /** Provider-native quality descriptor (\"premium\", \"standard\", …). */\n quality: z.string().optional(),\n /** Show in the voice-picker UI. */\n featured: z.boolean().optional(),\n})\nexport type CatalogVoice = z.infer<typeof CatalogVoiceSchema>\n"]}
@@ -0,0 +1,116 @@
1
+ import { modelMatchesPriceTier, modelHasTag } from './chunk-WTHHXXIK.mjs';
2
+
3
+ /**
4
+ * @agentproto/model-catalog v0.1.0
5
+ * Unified model catalog: LLM, image, video, audio entries + cost dispatcher + picker.
6
+ * Zod-only OSS core; @agstudio/model-catalog layers billing/access/BYOK on top.
7
+ */
8
+
9
+ // src/access/catalog-defaults.ts
10
+ function evaluateCatalogDefaults(model) {
11
+ switch (model.kind) {
12
+ case "image":
13
+ return model.def.agentVisible ? { allowed: true, reason: "catalog-default:agent-visible" } : { allowed: false, reason: "catalog-default:agent-hidden" };
14
+ case "video":
15
+ return model.def.agentVisible ? { allowed: true, reason: "catalog-default:agent-visible" } : { allowed: false, reason: "catalog-default:agent-hidden" };
16
+ case "llm":
17
+ return { allowed: true, reason: "catalog-default:llm-curated" };
18
+ case "audio":
19
+ return model.def.agentVisible ? { allowed: true, reason: "catalog-default:agent-visible" } : { allowed: false, reason: "catalog-default:agent-hidden" };
20
+ case "voice":
21
+ return { allowed: true, reason: "catalog-default:voice-curated" };
22
+ }
23
+ }
24
+
25
+ // src/access/evaluate.ts
26
+ var NON_MODEL_SPECIFICITY = {
27
+ tag: 4,
28
+ provider: 3,
29
+ priceTier: 2,
30
+ kind: 1
31
+ };
32
+ var NSFW_TAG_BLOCK_REASONS = /* @__PURE__ */ new Set(["nsfw", "compliance"]);
33
+ function ruleMatches(rule, model) {
34
+ const target = rule.target;
35
+ switch (target.kind) {
36
+ case "model":
37
+ return target.id === model.id;
38
+ case "kind":
39
+ return target.value === model.kind;
40
+ case "provider": {
41
+ switch (model.kind) {
42
+ case "image":
43
+ return model.def.provider === target.value;
44
+ case "video":
45
+ return model.def.provider === target.value;
46
+ case "audio":
47
+ return model.def.provider === target.value;
48
+ case "voice":
49
+ return model.voice.provider === target.value;
50
+ case "llm":
51
+ return model.canonicalId.startsWith(`${target.value}/`) || model.canonicalId.includes(`/${target.value}/`);
52
+ }
53
+ }
54
+ case "tag":
55
+ return modelHasTag(model, target.value);
56
+ case "priceTier":
57
+ return modelMatchesPriceTier(model, target.value);
58
+ }
59
+ }
60
+ function evaluateAccess(input) {
61
+ const { model, appScope, rules = [], byokActive = false } = input;
62
+ if (appScope && appScope.kinds.length > 0 && model.kind !== "voice" && !appScope.kinds.includes(model.kind)) {
63
+ return {
64
+ allowed: false,
65
+ reason: `app-scope:kind-not-allowed:${model.kind}`
66
+ };
67
+ }
68
+ const modelMatches = rules.filter(
69
+ (r) => r.target.kind === "model" && r.target.id === model.id
70
+ );
71
+ if (modelMatches.length > 0) {
72
+ const winner = modelMatches.find((r) => r.effect === "block") ?? modelMatches[0];
73
+ return {
74
+ allowed: winner.effect === "allow",
75
+ reason: `rule:model:${winner.effect}${winner.reason ? `:${winner.reason}` : ""}`
76
+ };
77
+ }
78
+ const matchedByBand = /* @__PURE__ */ new Map();
79
+ for (const rule of rules) {
80
+ const k = rule.target.kind;
81
+ if (k === "model") continue;
82
+ if (!ruleMatches(rule, model)) continue;
83
+ if (rule.effect === "block") {
84
+ if (byokActive && rule.target.kind === "priceTier" && rule.target.value === "premium" && !NSFW_TAG_BLOCK_REASONS.has(rule.reason ?? "")) {
85
+ continue;
86
+ }
87
+ }
88
+ const band = NON_MODEL_SPECIFICITY[k];
89
+ const list = matchedByBand.get(band) ?? [];
90
+ list.push(rule);
91
+ matchedByBand.set(band, list);
92
+ }
93
+ if (matchedByBand.size > 0) {
94
+ const bands = Array.from(matchedByBand.keys()).sort((a, b) => b - a);
95
+ for (const band of bands) {
96
+ const bandRules = matchedByBand.get(band);
97
+ const blocked = bandRules.find((r) => r.effect === "block");
98
+ const allowed = bandRules.find((r) => r.effect === "allow");
99
+ const winner = blocked ?? allowed;
100
+ if (!winner) continue;
101
+ const t = winner.target;
102
+ const targetStr = t.kind === "model" ? `model:${t.id}` : `${t.kind}:${t.value}`;
103
+ const tail = winner.reason ? `:${winner.reason}` : "";
104
+ return {
105
+ allowed: winner.effect === "allow",
106
+ reason: `rule:${targetStr}:${winner.effect}${tail}`
107
+ };
108
+ }
109
+ }
110
+ const fallback = evaluateCatalogDefaults(model);
111
+ return { allowed: fallback.allowed, reason: fallback.reason };
112
+ }
113
+
114
+ export { evaluateAccess, evaluateCatalogDefaults };
115
+ //# sourceMappingURL=chunk-HRRG2RZO.mjs.map
116
+ //# sourceMappingURL=chunk-HRRG2RZO.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/access/catalog-defaults.ts","../src/access/evaluate.ts"],"names":[],"mappings":"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","file":"chunk-HRRG2RZO.mjs","sourcesContent":["/**\n * Catalog-flag defaults — the layer that runs before any workspace\n * override. Mirrors the access-control plan:\n * - Default-allow: `lifecycle = stable && agentVisible = true`\n * - Default-block: `lifecycle = preview` or `lifecycle = deprecated`\n * - Default-block: any model with `agentVisible = false`\n *\n * Legacy catalog data (image/video) carries `agentVisible` but not\n * `lifecycle` — until v2 normalization, lifecycle defaults to \"stable\"\n * for every entry. LLM and audio entries default to allow (curated).\n */\n\nimport type { ResolvedModel } from \"../registry/index.js\"\n\nexport interface CatalogDefaultDecision {\n allowed: boolean\n reason: string\n}\n\nexport function evaluateCatalogDefaults(\n model: ResolvedModel\n): CatalogDefaultDecision {\n switch (model.kind) {\n case \"image\":\n return model.def.agentVisible\n ? { allowed: true, reason: \"catalog-default:agent-visible\" }\n : { allowed: false, reason: \"catalog-default:agent-hidden\" }\n case \"video\":\n return model.def.agentVisible\n ? { allowed: true, reason: \"catalog-default:agent-visible\" }\n : { allowed: false, reason: \"catalog-default:agent-hidden\" }\n case \"llm\":\n // LLM entries are curated — every catalog entry is visible.\n return { allowed: true, reason: \"catalog-default:llm-curated\" }\n case \"audio\":\n return model.def.agentVisible\n ? { allowed: true, reason: \"catalog-default:agent-visible\" }\n : { allowed: false, reason: \"catalog-default:agent-hidden\" }\n case \"voice\":\n // Voice metadata catalog is curated.\n return { allowed: true, reason: \"catalog-default:voice-curated\" }\n }\n}\n","/**\n * Access evaluator — resolves whether a workspace can use a given model.\n *\n * Resolution order:\n * 1. App scope — if the app opts out of the model's kind, deny.\n * 2. Workspace explicit `model:<id>` rule — terminal allow or block.\n * 3. Workspace tag / provider / priceTier / kind rules — most-specific\n * band wins. Within a band, block wins. Specificity ladder:\n * tag (4) > provider (3) > priceTier (2) > kind (1).\n * Rationale: an explicit `allow tag:image:generate` should beat a\n * blanket `block kind:llm`. The tag rule is the more specific\n * intent and reflects the operator's allowlist.\n * 4. Catalog defaults — `lifecycle=stable && agentVisible=true` → allow.\n *\n * BYOK relaxation: when `byokActive`, `priceTier=premium` blocks are\n * bypassed (BYOK users pay providers directly). NSFW / compliance\n * blocks NEVER bypass.\n */\n\nimport type { ResolvedModel } from \"../registry/index.js\"\nimport { modelHasTag, modelMatchesPriceTier } from \"../enrichment/index.js\"\nimport { evaluateCatalogDefaults } from \"./catalog-defaults.js\"\nimport type { AccessRule, AppScope } from \"./types.js\"\n\n/**\n * Specificity of non-model rule targets. Higher = more specific.\n * Most-specific band wins; within a band, block wins.\n *\n * Mirrors the scope specificity logic in\n * `packages/core/src/domain/model-access/types.ts:79`.\n */\nconst NON_MODEL_SPECIFICITY: Record<\n \"tag\" | \"provider\" | \"priceTier\" | \"kind\",\n number\n> = {\n tag: 4,\n provider: 3,\n priceTier: 2,\n kind: 1,\n}\n\nexport interface AccessEvalInput {\n model: ResolvedModel\n appScope?: AppScope\n /** Workspace overrides; v1 callers pass `[]`. */\n rules?: AccessRule[]\n byokActive?: boolean\n}\n\nexport interface AccessDecision {\n allowed: boolean\n /** Stable token for logs/metrics. */\n reason: string\n}\n\nconst NSFW_TAG_BLOCK_REASONS = new Set([\"nsfw\", \"compliance\"])\n\n/**\n * Returns true iff the rule matches the resolved model.\n *\n * Tag and priceTier matching go through the enrichment layer\n * (`@agentproto/model-catalog/enrichment`) — explicit per-model\n * enrichment wins, heuristic fallback fills the rest.\n */\nfunction ruleMatches(rule: AccessRule, model: ResolvedModel): boolean {\n const target = rule.target\n switch (target.kind) {\n case \"model\":\n return target.id === model.id\n case \"kind\":\n return target.value === model.kind\n case \"provider\": {\n switch (model.kind) {\n case \"image\":\n return model.def.provider === target.value\n case \"video\":\n return model.def.provider === target.value\n case \"audio\":\n return model.def.provider === target.value\n case \"voice\":\n return model.voice.provider === target.value\n case \"llm\":\n // No provider field on LLM entries — best-effort by canonical id\n // structure. All LLM ids follow `<vendor>/…` or\n // `<router>/<vendor>/…` conventions, so check for an exact\n // prefix segment or an interior `/<vendor>/` segment.\n // NOTE: avoid plain `.includes(target.value)` — it would\n // false-positive on any id that merely *contains* the substring\n // (e.g. \"not-google/…\" matching provider \"google\").\n // A `<router>/<vendor>/…` id (e.g. `openrouter/anthropic/…`) can\n // match BOTH a `router` and a `vendor` provider rule — that is\n // intentional; band resolution (most-specific wins) reconciles it.\n return (\n model.canonicalId.startsWith(`${target.value}/`) ||\n model.canonicalId.includes(`/${target.value}/`)\n )\n }\n }\n case \"tag\":\n return modelHasTag(model, target.value)\n case \"priceTier\":\n return modelMatchesPriceTier(model, target.value)\n }\n}\n\nexport function evaluateAccess(input: AccessEvalInput): AccessDecision {\n const { model, appScope, rules = [], byokActive = false } = input\n\n // 1. App scope. `voice` rows are metadata, not callable models — they\n // bypass kind-scope checks (gating happens on the audio MODEL the\n // voice plays through).\n if (\n appScope &&\n appScope.kinds.length > 0 &&\n model.kind !== \"voice\" &&\n !appScope.kinds.includes(model.kind)\n ) {\n return {\n allowed: false,\n reason: `app-scope:kind-not-allowed:${model.kind}`,\n }\n }\n\n // 2. Explicit per-model rules. Most specific band — terminal if any\n // matches. Within the band, block wins over allow (so a workspace\n // `block model:X` beats a tier-policy `allow model:X` regardless of\n // the order they appear in the merged rule list).\n const modelMatches = rules.filter(\n r =>\n r.target.kind === \"model\" && (r.target as { id: string }).id === model.id\n )\n if (modelMatches.length > 0) {\n const winner =\n modelMatches.find(r => r.effect === \"block\") ?? modelMatches[0]!\n return {\n allowed: winner.effect === \"allow\",\n reason: `rule:model:${winner.effect}${winner.reason ? `:${winner.reason}` : \"\"}`,\n }\n }\n\n // 3. Tag / provider / priceTier / kind rules. Most-specific band wins;\n // within a band, block wins. BYOK relaxation drops priceTier=premium\n // blocks before specificity grouping.\n const matchedByBand = new Map<number, AccessRule[]>()\n for (const rule of rules) {\n const k = rule.target.kind\n if (k === \"model\") continue\n if (!ruleMatches(rule, model)) continue\n if (rule.effect === \"block\") {\n // BYOK relaxation — same condition as before the refactor.\n if (\n byokActive &&\n rule.target.kind === \"priceTier\" &&\n rule.target.value === \"premium\" &&\n !NSFW_TAG_BLOCK_REASONS.has(rule.reason ?? \"\")\n ) {\n continue\n }\n }\n const band = NON_MODEL_SPECIFICITY[k]\n const list = matchedByBand.get(band) ?? []\n list.push(rule)\n matchedByBand.set(band, list)\n }\n\n if (matchedByBand.size > 0) {\n // Walk bands from most-specific to least-specific. The first band\n // with any matching rule decides — block wins within the band.\n const bands = Array.from(matchedByBand.keys()).sort((a, b) => b - a)\n for (const band of bands) {\n const bandRules = matchedByBand.get(band)!\n const blocked = bandRules.find(r => r.effect === \"block\")\n const allowed = bandRules.find(r => r.effect === \"allow\")\n const winner = blocked ?? allowed\n if (!winner) continue\n const t = winner.target\n const targetStr =\n t.kind === \"model\"\n ? `model:${(t as { id: string }).id}`\n : `${t.kind}:${(t as { value: string }).value}`\n const tail = winner.reason ? `:${winner.reason}` : \"\"\n return {\n allowed: winner.effect === \"allow\",\n reason: `rule:${targetStr}:${winner.effect}${tail}`,\n }\n }\n }\n\n // 4. Catalog defaults.\n const fallback = evaluateCatalogDefaults(model)\n return { allowed: fallback.allowed, reason: fallback.reason }\n}\n"]}