@namzu/zen 2.4.0 → 2.5.2

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 (45) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/README.md +30 -11
  3. package/dist/catalogue/catalogue.d.ts +106 -0
  4. package/dist/catalogue/catalogue.d.ts.map +1 -0
  5. package/dist/catalogue/catalogue.js +325 -0
  6. package/dist/catalogue/catalogue.js.map +1 -0
  7. package/dist/catalogue/derive.d.ts +203 -0
  8. package/dist/catalogue/derive.d.ts.map +1 -0
  9. package/dist/catalogue/derive.js +485 -0
  10. package/dist/catalogue/derive.js.map +1 -0
  11. package/dist/catalogue/fetch.d.ts +63 -0
  12. package/dist/catalogue/fetch.d.ts.map +1 -0
  13. package/dist/catalogue/fetch.js +180 -0
  14. package/dist/catalogue/fetch.js.map +1 -0
  15. package/dist/catalogue/index.d.ts +17 -0
  16. package/dist/catalogue/index.d.ts.map +1 -0
  17. package/dist/catalogue/index.js +17 -0
  18. package/dist/catalogue/index.js.map +1 -0
  19. package/dist/client.d.ts +5 -1
  20. package/dist/client.d.ts.map +1 -1
  21. package/dist/client.js +59 -8
  22. package/dist/client.js.map +1 -1
  23. package/dist/index.d.ts +2 -1
  24. package/dist/index.d.ts.map +1 -1
  25. package/dist/index.js +1 -1
  26. package/dist/index.js.map +1 -1
  27. package/dist/models.d.ts +6 -0
  28. package/dist/models.d.ts.map +1 -1
  29. package/dist/models.js +49 -9
  30. package/dist/models.js.map +1 -1
  31. package/dist/prompt.d.ts.map +1 -1
  32. package/dist/prompt.js +7 -0
  33. package/dist/prompt.js.map +1 -1
  34. package/dist/types.d.ts +11 -1
  35. package/dist/types.d.ts.map +1 -1
  36. package/package.json +6 -2
  37. package/src/catalogue/catalogue.ts +462 -0
  38. package/src/catalogue/derive.ts +638 -0
  39. package/src/catalogue/fetch.ts +229 -0
  40. package/src/catalogue/index.ts +37 -0
  41. package/src/client.ts +68 -9
  42. package/src/index.ts +2 -1
  43. package/src/models.ts +50 -9
  44. package/src/prompt.ts +7 -0
  45. package/src/types.ts +11 -1
@@ -0,0 +1,485 @@
1
+ /**
2
+ * The derivation rules that turn Zen's and Go's upstream documents into a
3
+ * model roster.
4
+ *
5
+ * Pure: no network, no file system, no logger. `fetch.ts` reads the sources and
6
+ * `catalogue.ts` assembles a catalogue from them; the maintainer's generator
7
+ * (`scripts/generate-zen-models.mjs`) imports these same functions to render
8
+ * the bundled snapshot in `models.ts`, so the runtime refresh and the snapshot
9
+ * can never parse the same page two different ways.
10
+ *
11
+ * ## Where each field comes from
12
+ *
13
+ * Routes come from the service's own documentation page, as the pair
14
+ * (endpoint, AI SDK package) that page states per model. Both halves are read
15
+ * and required to agree, because a page that changed shape must stop the run
16
+ * rather than have one half guessed: the wire a model is served on is a routing
17
+ * fact, and the wrong one is a request to the wrong endpoint rather than a
18
+ * clean failure. A row may state its endpoint and no package, with the `-` the
19
+ * page's own tables use for a cell that has no value: that row is read and
20
+ * recorded with no wire, and `deriveService` reports it as a model it will not
21
+ * carry. Losing a column, or carrying a marker that is not `-`, is the page
22
+ * moving and stops the run.
23
+ *
24
+ * Prices come from the same page's per-1M-token table, at the base tier: the
25
+ * unqualified row when a model has one, the "(Off-Peak)" row for the Go
26
+ * DeepSeek entries that publish both, and the "(≤ n tokens)" row when a model
27
+ * is only published as a tier pair. A model with no price row at all must
28
+ * appear in that page's free-model list and is then zero — "no row" is never
29
+ * quietly read as free.
30
+ *
31
+ * Limits, modalities, tool support and effort levels come from the matching
32
+ * provider entry in models.dev. Its `npm` is deliberately NOT used as a route,
33
+ * in either place models.dev states one: the provider-level field names a
34
+ * single Chat-Completions package for both services, which would send the Go
35
+ * Qwen rows — documented on `/messages` — to `/chat/completions`, and the
36
+ * per-model `provider.npm` is stated for fewer than half the documented
37
+ * models, so it cannot route the rest.
38
+ *
39
+ * The two services' own `/models` answers carry `id`, `object`, `created` and
40
+ * `owned_by` and nothing else: they say a model EXISTS and nothing about how to
41
+ * call it. An id served there and documented nowhere cannot be carried, only
42
+ * reported.
43
+ */
44
+ /**
45
+ * A source that could not be read, or no longer has the shape these rules
46
+ * parse. Never a curation decision: nothing a caller does to the catalogue
47
+ * clears it, and a refresh that meets one must keep whatever it had before.
48
+ */
49
+ export class ZenCatalogueSourceError extends Error {
50
+ name = 'ZenCatalogueSourceError';
51
+ }
52
+ function unusable(message) {
53
+ throw new ZenCatalogueSourceError(message);
54
+ }
55
+ /**
56
+ * Characters a model name may not carry: controls (C0, DEL, C1), format
57
+ * characters (bidi overrides, zero-widths) and line or paragraph separators.
58
+ *
59
+ * A name is upstream text that a host prints as it is, in a terminal picker
60
+ * among other places. An escape sequence in it would be an OSC 52 clipboard
61
+ * write or a screen clear in every session that lists the model, and a bidi
62
+ * override would show one name while meaning another. No real name needs any
63
+ * of them, so a source whose name carries one is a source these rules refuse.
64
+ */
65
+ const UNDISPLAYABLE = /[\p{Cc}\p{Cf}\p{Zl}\p{Zp}]/u;
66
+ /** Whether `name` is safe to print as it is: no control, format or separator character. */
67
+ export function isDisplayableName(name) {
68
+ return !UNDISPLAYABLE.test(name);
69
+ }
70
+ function routeName(cell, page) {
71
+ const name = cell.trim();
72
+ if (!isDisplayableName(name)) {
73
+ unusable(`${page} has a route row whose model name, ${JSON.stringify(name)}, carries a control,\n format or separator character. A name is printed as it is wherever the model is\n listed, so the run stops rather than carry it.`);
74
+ }
75
+ return name;
76
+ }
77
+ /** A service, its documentation page, and the models.dev provider holding its limits. */
78
+ export const ZEN_SERVICES = Object.freeze([
79
+ { service: 'zen', page: 'zen.mdx', provider: 'opencode' },
80
+ { service: 'go', page: 'go.mdx', provider: 'opencode-go' },
81
+ ]);
82
+ /**
83
+ * Every (AI SDK package, endpoint) pair a page may state, and the protocol
84
+ * each is. `kind` says how the endpoint names the model: the first three put
85
+ * it in the body of a fixed path, Google puts it in the path itself.
86
+ */
87
+ export const ROUTES = Object.freeze([
88
+ { npm: '@ai-sdk/openai-compatible', path: '/chat/completions', protocol: 'chat', kind: 'path' },
89
+ { npm: '@ai-sdk/openai', path: '/responses', protocol: 'responses', kind: 'path' },
90
+ { npm: '@ai-sdk/anthropic', path: '/messages', protocol: 'messages', kind: 'path' },
91
+ { npm: '@ai-sdk/google', path: '/models/', protocol: 'google', kind: 'model' },
92
+ ]);
93
+ /** The Google route's tail: `models/<id>`, and nothing else. */
94
+ const MODEL_PATH = /^models\/[a-z0-9][a-z0-9.-]*$/;
95
+ /** The SDK's input modalities, and the models.dev names that map onto them. */
96
+ export const MODALITIES = Object.freeze({
97
+ text: 'text',
98
+ image: 'image',
99
+ pdf: 'document',
100
+ });
101
+ /** The service each base host serves, by the path prefix its endpoints carry. */
102
+ export const SERVICE_PREFIX = Object.freeze({
103
+ zen: '/zen/v1/',
104
+ go: '/zen/go/v1/',
105
+ });
106
+ /**
107
+ * One `/models` answer, as the set of ids it says the service serves.
108
+ *
109
+ * The shape is asserted rather than assumed, and an answer with no ids in it is
110
+ * refused: a service that returns nothing is a source that moved, and reading
111
+ * it as "nothing is served" would turn an outage into an empty roster.
112
+ */
113
+ export function parseServed(service, text) {
114
+ let body;
115
+ try {
116
+ body = JSON.parse(text);
117
+ }
118
+ catch (error) {
119
+ unusable(`The ${service} /models answer is not JSON: ${error instanceof Error ? error.message : error}`);
120
+ }
121
+ const data = body !== null && typeof body === 'object' ? body.data : undefined;
122
+ if (!Array.isArray(data)) {
123
+ unusable(`The ${service} /models answer has no \`data\` array. It is the field the served roster\n is read from, so a service that changed shape stops here.`);
124
+ }
125
+ const ids = new Set();
126
+ for (const item of data) {
127
+ const id = item !== null && typeof item === 'object' ? item.id : undefined;
128
+ if (typeof id !== 'string' || id.length === 0) {
129
+ unusable(`The ${service} /models answer has an entry with no string \`id\`. Every id it\n serves is checked against the catalogue, so an entry that cannot be read is\n an id nobody checked.`);
130
+ }
131
+ ids.add(id);
132
+ }
133
+ if (ids.size === 0) {
134
+ unusable(`The ${service} /models answer lists no models at all. That is a source that moved,\n not a service that serves nothing, and accepting it would empty the roster\n for as long as the outage lasted.`);
135
+ }
136
+ return ids;
137
+ }
138
+ /**
139
+ * Whether a line looks like a row of the route table.
140
+ *
141
+ * The account has to CLOSE, not merely to be attempted: a line shaped like a
142
+ * route row that the route pattern did not capture is a page these rules no
143
+ * longer read correctly, and dropping it silently is the defect the whole
144
+ * arrangement exists to remove. Two shapes count, both measured against the
145
+ * live pages:
146
+ *
147
+ * - two or more backticked cells, which is a row carrying both its endpoint
148
+ * and its package;
149
+ * - three or more cells with at least one backticked cell, which is a row that
150
+ * kept a value and lost a column: an empty package cell leaves four cells,
151
+ * and a row whose package COLUMN is gone leaves three.
152
+ *
153
+ * The backtick guard keeps the wider rule off table separators
154
+ * (`| --- | --- | --- | --- |` carries no code span), and a prose row that
155
+ * quotes a code span has two cells.
156
+ */
157
+ export function isRouteRowShaped(line) {
158
+ const ticked = [...line.matchAll(/`[^`]*`/g)].length;
159
+ const cells = line.split('|').length - 2;
160
+ return ticked >= 2 || (cells >= 3 && ticked >= 1);
161
+ }
162
+ /** A documentation page, split into the three tables the rules read. */
163
+ export function parsePage(text, { page, service }) {
164
+ const routes = [];
165
+ const prices = [];
166
+ const freeNames = [];
167
+ const unroutable = [];
168
+ // The free-model list is the only place a model is stated to be served at no
169
+ // charge, and it is a bullet list under its own heading. It is read as a
170
+ // block rather than by scanning for prose that looks like it, because the
171
+ // same page ends with per-model data-handling notes that are also bullets
172
+ // and also name models — reading those as free models would be a silent
173
+ // widening of anonymous access.
174
+ let inFreeSection = false;
175
+ for (const line of text.split('\n')) {
176
+ if (/^The free models:/.test(line)) {
177
+ inFreeSection = true;
178
+ continue;
179
+ }
180
+ if (inFreeSection) {
181
+ const bullet = line.match(/^-\s+(.+?)\s+is\s/);
182
+ if (bullet?.[1] !== undefined) {
183
+ freeNames.push(bullet[1].trim());
184
+ continue;
185
+ }
186
+ if (line.trim().length > 0 && !line.startsWith('-'))
187
+ inFreeSection = false;
188
+ }
189
+ const route = line.match(/^\|\s*(.+?)\s*\|\s*([a-z0-9][a-z0-9.\-]*)\s*\|\s*`([^`]+)`\s*\|\s*`([^`]+)`\s*\|$/);
190
+ if (route) {
191
+ routes.push({
192
+ name: routeName(route[1], page),
193
+ id: route[2],
194
+ endpoint: route[3],
195
+ npm: route[4],
196
+ });
197
+ continue;
198
+ }
199
+ // A route row may state NO wire: the package column carries the `-` the
200
+ // page's own tables use for a cell with no value. Narrow on purpose — the
201
+ // fourth cell has to be exactly that marker, and the name cell refuses a
202
+ // `|`, so a five-cell line whose extra cell sits in the middle cannot be
203
+ // read as a wireless route named after two cells.
204
+ const wireless = line.match(/^\|\s*([^|]+?)\s*\|\s*([a-z0-9][a-z0-9.\-]*)\s*\|\s*`([^`]+)`\s*\|\s*-\s*\|$/);
205
+ if (wireless) {
206
+ routes.push({
207
+ name: routeName(wireless[1], page),
208
+ id: wireless[2],
209
+ endpoint: wireless[3],
210
+ npm: undefined,
211
+ });
212
+ continue;
213
+ }
214
+ const price = line.match(/^\|\s*(.+?)\s*\|\s*([^|]*?)\s*\|\s*([^|]*?)\s*\|/);
215
+ if (price) {
216
+ // A price cell is a dollar figure or the word "Free". Nothing else is
217
+ // read, so a table whose columns mean something else cannot be picked
218
+ // up by being three cells wide.
219
+ const input = priceCell(price[2]);
220
+ const output = priceCell(price[3]);
221
+ if (input !== undefined && output !== undefined) {
222
+ prices.push({ name: price[1].trim(), input, output });
223
+ continue;
224
+ }
225
+ }
226
+ if (line.startsWith('|') && isRouteRowShaped(line))
227
+ unroutable.push(line.trim());
228
+ }
229
+ if (unroutable.length > 0) {
230
+ unusable(`${page} has ${unroutable.length} line(s) shaped like a route row that this script\n did not read as one. A model the page routes but this script does not capture is\n a model that silently disappears from the catalogue, so the run stops instead:\n${unroutable
231
+ .slice(0, 5)
232
+ .map((line) => ` ${line}`)
233
+ .join('\n')}${unroutable.length > 5 ? `\n … and ${unroutable.length - 5} more` : ''}`);
234
+ }
235
+ if (routes.length === 0)
236
+ unusable(`${page} has no route table.`);
237
+ const seen = new Set();
238
+ for (const route of routes) {
239
+ if (seen.has(route.id)) {
240
+ unusable(`${page} routes "${route.id}" on more than one row. Which row states its wire and\n its name is not something these rules can choose, so the run stops instead.`);
241
+ }
242
+ seen.add(route.id);
243
+ }
244
+ if (service === 'zen' && freeNames.length === 0) {
245
+ unusable(`${page} has no free-model list. Anonymous admission is stated there and is never\n inferred from a zero price, so an empty list is a page that moved, not a\n catalogue that serves nothing for free.`);
246
+ }
247
+ return { routes, prices, freeNames };
248
+ }
249
+ /**
250
+ * A price cell, or undefined when the cell is not a price.
251
+ *
252
+ * Go states its free models' rate as the word "Free" rather than as `$0.00`,
253
+ * which is a statement about the price and not a missing one, so it is read as
254
+ * zero.
255
+ */
256
+ export function priceCell(cell) {
257
+ // One figure with at most one decimal point. A looser pattern reads `$1.2.3`
258
+ // as `Number('1.2.3')`, which is NaN, and a NaN rate is not a price.
259
+ const dollars = /^\$([0-9]+(?:\.[0-9]+)?)$/.exec(cell.trim());
260
+ if (dollars)
261
+ return Number(dollars[1]);
262
+ return /^free$/i.test(cell.trim()) ? 0 : undefined;
263
+ }
264
+ /** Model names are matched across tables by their letters, digits and dots. */
265
+ export function nameKey(name) {
266
+ return name
267
+ .toLowerCase()
268
+ .replace(/\([^)]*\)/g, '')
269
+ .replace(/[^a-z0-9.]/g, '');
270
+ }
271
+ /**
272
+ * The base-tier price row for a model: the unqualified row, the "(≤ …)" row of
273
+ * a tier pair, or the "(Off-Peak)" row of a peak/off-peak pair.
274
+ */
275
+ export function priceRowFor(rows, name) {
276
+ const candidates = rows.filter((row) => nameKey(row.name) === nameKey(name));
277
+ if (candidates.length === 0)
278
+ return undefined;
279
+ return (candidates.find((row) => /off-peak/i.test(row.name)) ??
280
+ candidates.find((row) => !row.name.includes('(')) ??
281
+ candidates.find((row) => row.name.includes('≤')));
282
+ }
283
+ /**
284
+ * The protocol a (package, endpoint) pair is, refusing anything unrecognised.
285
+ *
286
+ * The pair is checked against the URL's own parts rather than by substring: a
287
+ * cell reading `https://evil.example/messages` contains the path a Messages
288
+ * route has, and a routing decision that accepts it is a routing decision made
289
+ * on the wrong string. Host, service prefix, path and package must all agree.
290
+ */
291
+ export function protocolFor({ id, endpoint, npm }, service) {
292
+ const refuse = (why) => unusable(`"${id}" is documented as ${npm} on ${endpoint}, ${why}. A model whose wire is not\n known cannot be routed, so the run stops rather than picking one.`);
293
+ if (npm === undefined) {
294
+ unusable(`"${id}" is documented on ${endpoint} with no AI SDK package stated, so no wire is\n known. A model whose wire is not known cannot be routed, so the run stops\n rather than picking one.`);
295
+ }
296
+ let url;
297
+ try {
298
+ url = new URL(endpoint);
299
+ }
300
+ catch {
301
+ return refuse('which is not a URL');
302
+ }
303
+ if (url.host !== 'opencode.ai')
304
+ refuse("whose host is not the service's");
305
+ const prefix = SERVICE_PREFIX[service];
306
+ if (prefix === undefined)
307
+ return refuse('on a service this script does not know');
308
+ if (!url.pathname.startsWith(prefix))
309
+ refuse(`which is not on the ${service} service's own path`);
310
+ const route = ROUTES.find((candidate) => candidate.npm === npm);
311
+ if (!route) {
312
+ return refuse(`with a package that is not one of the ${ROUTES.length} routes this driver implements`);
313
+ }
314
+ const tail = url.pathname.slice(prefix.length);
315
+ const matches = route.kind === 'path' ? tail === route.path.slice(1) : MODEL_PATH.test(tail);
316
+ if (!matches)
317
+ refuse(`which does not address ${route.path} on the ${service} service`);
318
+ return route.protocol;
319
+ }
320
+ function omissionKeys(omissions) {
321
+ return omissions instanceof Set ? omissions : new Set(Object.keys(omissions));
322
+ }
323
+ /**
324
+ * One service's carried models, plus everything either source names and the
325
+ * catalogue does not carry.
326
+ *
327
+ * `served` is the set of ids the service's own `/models` answer carries, and it
328
+ * is required rather than defaulted: a caller that forgot it would turn the
329
+ * served dimension off silently.
330
+ */
331
+ export function derive(service, parsed, providerKey, provider, omissions, served) {
332
+ const omitted = omissionKeys(omissions);
333
+ const freeIds = new Set();
334
+ for (const name of parsed.freeNames) {
335
+ const row = parsed.routes.find((candidate) => nameKey(candidate.name) === nameKey(name));
336
+ if (!row) {
337
+ unusable(`The free-model list names "${name}", which the route table does not. Anonymous\n admission is derived from that pairing, so the page has moved.`);
338
+ }
339
+ freeIds.add(row.id);
340
+ }
341
+ const documented = new Set(parsed.routes.map((row) => row.id));
342
+ const routeNames = new Set(parsed.routes.map((row) => nameKey(row.name)));
343
+ const orphanPrices = parsed.prices
344
+ .filter((row) => !routeNames.has(nameKey(row.name)))
345
+ .map((row) => row.name);
346
+ const models = [];
347
+ const undecided = [];
348
+ const incomplete = [];
349
+ // The rows actually put to models.dev: an omitted row is never asked about,
350
+ // and neither is one that states no wire — no answer there could make it
351
+ // carryable.
352
+ let considered = 0;
353
+ let silentOnTools = 0;
354
+ let modelsDevEntries = 0;
355
+ for (const row of parsed.routes) {
356
+ if (omitted.has(`${service}/${row.id}`))
357
+ continue;
358
+ if (row.npm === undefined) {
359
+ undecided.push([
360
+ `${service}/${row.id}`,
361
+ 'the page states no AI SDK package for it, so no wire is known',
362
+ ]);
363
+ continue;
364
+ }
365
+ considered += 1;
366
+ const metadata = provider.models[row.id];
367
+ if (!metadata || typeof metadata !== 'object') {
368
+ undecided.push([`${service}/${row.id}`, `models.dev has no \`${providerKey}\` entry`]);
369
+ continue;
370
+ }
371
+ if (metadata.tool_call === undefined)
372
+ silentOnTools += 1;
373
+ modelsDevEntries += 1;
374
+ const priceRow = priceRowFor(parsed.prices, row.name);
375
+ if (!priceRow && !freeIds.has(row.id)) {
376
+ undecided.push([`${service}/${row.id}`, 'the page neither prices it nor names it as free']);
377
+ continue;
378
+ }
379
+ const effort = (Array.isArray(metadata.reasoning_options) ? metadata.reasoning_options : []).find((option) => option?.type === 'effort');
380
+ const inputModalities = (Array.isArray(metadata.modalities?.input) ? metadata.modalities.input : [])
381
+ .map((modality) => MODALITIES[modality])
382
+ .filter((modality) => modality !== undefined);
383
+ const contextWindow = metadata.limit?.context;
384
+ const maxOutputTokens = metadata.limit?.output;
385
+ if (!isPositiveInteger(contextWindow) ||
386
+ !isPositiveInteger(maxOutputTokens) ||
387
+ inputModalities.length === 0) {
388
+ incomplete.push(`${service}/${row.id} (context ${contextWindow}, output ${maxOutputTokens}, ${inputModalities.length} input modalities)`);
389
+ continue;
390
+ }
391
+ // The page either prices the model or names it free; the guard above
392
+ // refused every model that is neither. So the zero written when there is
393
+ // no price row is a rate these rules KNOW, not a default.
394
+ const inputPrice = priceRow ? priceRow.input : 0;
395
+ const outputPrice = priceRow ? priceRow.output : 0;
396
+ models.push({
397
+ id: row.id,
398
+ name: row.name,
399
+ protocol: protocolFor(row, service),
400
+ contextWindow,
401
+ maxOutputTokens,
402
+ inputModalities,
403
+ inputPrice,
404
+ outputPrice,
405
+ supportsToolUse: metadata.tool_call === true,
406
+ effortLevels: effortValues(effort?.values, `${service}/${row.id}`),
407
+ // Anonymous admission is a Zen concept: the Go constructor refuses an
408
+ // absent key outright, so the flag would mean nothing on that service.
409
+ supportsAnonymousAccess: service === 'zen' && freeIds.has(row.id),
410
+ });
411
+ }
412
+ // Incomplete metadata is a source that moved, not a decision anybody makes:
413
+ // an entry that is there and has lost the fields is models.dev having
414
+ // changed shape, and every model reporting it at once is what a rename
415
+ // looks like.
416
+ if (incomplete.length > 0) {
417
+ const all = incomplete.length === considered;
418
+ unusable(`${incomplete.length}${all ? ' — every one put to models.dev —' : ` of ${considered}`} model(s) on the\n ${service} page have an entry in models.dev that is missing the fields the catalogue\n needs. An entry that exists and has lost its limits is models.dev having changed\n shape, not a curation decision, so this is reported as drift in the SOURCE:\n${incomplete
419
+ .slice(0, 8)
420
+ .map((line) => ` ${line}`)
421
+ .join('\n')}${incomplete.length > 8 ? `\n … and ${incomplete.length - 8} more` : ''}`);
422
+ }
423
+ // A `tool_call` missing from every entry at once is a rename, and it is the
424
+ // loss that fails QUIETLY: read as "no tools", it would rewrite the whole
425
+ // roster to `supportsToolUse: false`. One entry of many is a real answer.
426
+ if (modelsDevEntries > 0 && silentOnTools === modelsDevEntries) {
427
+ unusable(`All ${modelsDevEntries} models.dev entr${modelsDevEntries === 1 ? 'y' : 'ies'} for the ${service}\n provider omit \`tool_call\`. A field that vanished from every entry at once is models.dev\n having changed shape, not every model losing its tools, so this is reported as a\n SOURCE failure rather than written into the catalogue as \`supportsToolUse: false\`.`);
428
+ }
429
+ const servedUncurried = [...served]
430
+ .filter((id) => !documented.has(id) && !omitted.has(`${service}/${id}`))
431
+ .sort();
432
+ // An omission expires when upstream stops naming the model at all: neither
433
+ // its page nor the service that serves it.
434
+ const stale = [...omitted]
435
+ .filter((key) => key.startsWith(`${service}/`))
436
+ .map((key) => key.slice(service.length + 1))
437
+ .filter((id) => !documented.has(id) && !served.has(id));
438
+ return { models, undecided, stale, orphanPrices, servedUncurried };
439
+ }
440
+ const EFFORTS = new Set([
441
+ 'none',
442
+ 'minimal',
443
+ 'low',
444
+ 'medium',
445
+ 'high',
446
+ 'xhigh',
447
+ 'max',
448
+ 'ultra',
449
+ ]);
450
+ function effortValues(values, key) {
451
+ if (values === undefined)
452
+ return [];
453
+ if (!Array.isArray(values))
454
+ unusable(`models.dev states effort options for ${key} as a non-list.`);
455
+ for (const value of values) {
456
+ if (typeof value !== 'string' || !EFFORTS.has(value)) {
457
+ unusable(`models.dev states an effort level ${JSON.stringify(value)} for ${key}, which the SDK\n has no \`ReasoningEffort\` for. A level the driver cannot send is not carried by guessing.`);
458
+ }
459
+ }
460
+ return [...values];
461
+ }
462
+ function isPositiveInteger(value) {
463
+ return typeof value === 'number' && Number.isSafeInteger(value) && value > 0;
464
+ }
465
+ /**
466
+ * Refuse a roster that collapsed.
467
+ *
468
+ * A roster that came out empty, or that lost a fifth of what the baseline
469
+ * carries, is a source that moved far more often than a decision anybody made,
470
+ * and both look identical to a successful run from the inside.
471
+ */
472
+ export const ROSTER_FLOOR = 0.8;
473
+ export function checkRosterFloor(built, committed) {
474
+ for (const { service } of ZEN_SERVICES) {
475
+ const derived = built[service].length;
476
+ const carried = committed[service].length;
477
+ if (derived === 0) {
478
+ unusable(`The ${service} roster came out empty. Every documented model was rejected — a\n catalogue with nothing in it is not a result, and a review file that omits\n every model produces exactly this. Refusing rather than writing it.`);
479
+ }
480
+ if (carried > 0 && derived < carried * ROSTER_FLOOR) {
481
+ unusable(`The ${service} roster fell from ${carried} models to ${derived}, past the floor of\n ${ROSTER_FLOOR * 100}% of what is committed. A fall that size is far more likely to be a source\n that changed shape than a decision somebody made, so it is reported as one.\n If upstream really did withdraw that many models, change ROSTER_FLOOR in\n packages/providers/zen/src/catalogue/derive.ts and say so in the commit.`);
482
+ }
483
+ }
484
+ }
485
+ //# sourceMappingURL=derive.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"derive.js","sourceRoot":"","sources":["../../src/catalogue/derive.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Reading the Zen catalogue's sources over the network — the one module in
3
+ * this package that does, and only when a caller invokes it. Importing it,
4
+ * constructing a provider or reading the bundled snapshot fetches nothing.
5
+ */
6
+ import type { ZenService } from '../models.js';
7
+ import { type BuildZenCatalogueOptions, type ZenCatalogueResult, type ZenCatalogueSources } from './catalogue.js';
8
+ /** The branch of OpenCode's repository the two documentation pages are read from. */
9
+ export declare const ZEN_DOCS_REF = "dev";
10
+ export declare const ZEN_MODELS_DEV_URL = "https://models.dev/api.json";
11
+ /** The host both services' own `/models` endpoints hang off. */
12
+ export declare const ZEN_SERVICE_BASE = "https://opencode.ai";
13
+ /** Byte ceilings per source. A source past its ceiling is refused, never truncated. */
14
+ export interface ZenCatalogueLimits {
15
+ /** Each documentation page. Default 1 MiB (the live pages are ~32 KB). */
16
+ readonly pageBytes?: number;
17
+ /** models.dev's whole document. Default 32 MiB (measured 4.8 MB on 2026-09-22). */
18
+ readonly modelsDevBytes?: number;
19
+ /** Each `/models` answer. Default 1 MiB, the driver's own `listModels` ceiling. */
20
+ readonly servedBytes?: number;
21
+ }
22
+ export interface FetchZenCatalogueOptions {
23
+ /** Defaults to the global `fetch`. */
24
+ readonly fetch?: typeof fetch;
25
+ /** Aborts every outstanding read; the call then rejects with the signal's reason. */
26
+ readonly signal?: AbortSignal;
27
+ /** Deadline for each source read, in milliseconds. Default 15,000. */
28
+ readonly timeoutMs?: number;
29
+ /** Tries per source; a 404 is never retried. Default 1. */
30
+ readonly attempts?: number;
31
+ readonly limits?: ZenCatalogueLimits;
32
+ /** OpenCode branch for the two pages. Default `dev`. */
33
+ readonly docsRef?: string;
34
+ readonly modelsDevUrl?: string;
35
+ /** Host for the two `/models` answers. Default `https://opencode.ai`. */
36
+ readonly serviceBase?: string;
37
+ }
38
+ /** The models-list endpoint of one service. */
39
+ export declare function zenServedUrl(service: ZenService, serviceBase?: string): string;
40
+ /**
41
+ * One source, as text, inside its deadline and its byte ceiling.
42
+ *
43
+ * Redirects are refused: every source is a fixed URL that answers directly, and
44
+ * a source that starts redirecting has moved. A 404 is a branch or a path that
45
+ * moved and is not retried; a caller's abort is not retried either.
46
+ */
47
+ export declare function readZenSource(url: string, limit: number, options?: Pick<FetchZenCatalogueOptions, 'fetch' | 'signal' | 'timeoutMs' | 'attempts'>): Promise<string>;
48
+ /** The two `/models` answers, as text. */
49
+ export declare function fetchZenServedRosters(options?: FetchZenCatalogueOptions): Promise<Record<ZenService, string>>;
50
+ /**
51
+ * All five sources, as text: the two documentation pages, models.dev and the
52
+ * two `/models` answers. Rejects on the first source that cannot be read.
53
+ */
54
+ export declare function fetchZenCatalogueSources(options?: FetchZenCatalogueOptions): Promise<ZenCatalogueSources>;
55
+ /**
56
+ * Read every source and derive a catalogue from them, or reject.
57
+ *
58
+ * Opt-in by construction: nothing in this package calls it. A rejection is a
59
+ * `ZenCatalogueSourceError` for a source that could not be read or no longer
60
+ * parses, or the caller's abort reason; there is no partial result.
61
+ */
62
+ export declare function fetchZenCatalogue(options?: FetchZenCatalogueOptions & BuildZenCatalogueOptions): Promise<ZenCatalogueResult>;
63
+ //# sourceMappingURL=fetch.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fetch.d.ts","sourceRoot":"","sources":["../../src/catalogue/fetch.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AAC9C,OAAO,EACN,KAAK,wBAAwB,EAC7B,KAAK,kBAAkB,EACvB,KAAK,mBAAmB,EAExB,MAAM,gBAAgB,CAAA;AAGvB,qFAAqF;AACrF,eAAO,MAAM,YAAY,QAAQ,CAAA;AACjC,eAAO,MAAM,kBAAkB,gCAAgC,CAAA;AAC/D,gEAAgE;AAChE,eAAO,MAAM,gBAAgB,wBAAwB,CAAA;AAErD,uFAAuF;AACvF,MAAM,WAAW,kBAAkB;IAClC,0EAA0E;IAC1E,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAA;IAC3B,mFAAmF;IACnF,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAA;IAChC,mFAAmF;IACnF,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAC7B;AAED,MAAM,WAAW,wBAAwB;IACxC,sCAAsC;IACtC,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,KAAK,CAAA;IAC7B,qFAAqF;IACrF,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,CAAA;IAC7B,sEAAsE;IACtE,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAA;IAC3B,2DAA2D;IAC3D,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;IAC1B,QAAQ,CAAC,MAAM,CAAC,EAAE,kBAAkB,CAAA;IACpC,wDAAwD;IACxD,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAA;IACzB,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAA;IAC9B,yEAAyE;IACzE,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAC7B;AASD,+CAA+C;AAC/C,wBAAgB,YAAY,CAAC,OAAO,EAAE,UAAU,EAAE,WAAW,SAAmB,GAAG,MAAM,CAExF;AAgDD;;;;;;GAMG;AACH,wBAAsB,aAAa,CAClC,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,MAAM,EACb,OAAO,GAAE,IAAI,CAAC,wBAAwB,EAAE,OAAO,GAAG,QAAQ,GAAG,WAAW,GAAG,UAAU,CAAM,GACzF,OAAO,CAAC,MAAM,CAAC,CA+BjB;AAiBD,0CAA0C;AAC1C,wBAAsB,qBAAqB,CAC1C,OAAO,GAAE,wBAA6B,GACpC,OAAO,CAAC,MAAM,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC,CAOrC;AAED;;;GAGG;AACH,wBAAsB,wBAAwB,CAC7C,OAAO,GAAE,wBAA6B,GACpC,OAAO,CAAC,mBAAmB,CAAC,CA8B9B;AAED;;;;;;GAMG;AACH,wBAAsB,iBAAiB,CACtC,OAAO,GAAE,wBAAwB,GAAG,wBAA6B,GAC/D,OAAO,CAAC,kBAAkB,CAAC,CAI7B"}