models.json,regen.ts,index.ts: bake glm-5.3, notice unbaked ids
41 unmodified lines42434445464748495046474849505152535452535455565758596061626353 unmodified lines41 unmodified lines```
That asks the endpoint for `/models` (what the key is entitled to) and models.dev formetadata (context window, output limit, reasoning, image input), then writes the file.Non-chat entitlements (audio, image generation) are skipped. Ids the `alibaba-token-plan`entry has not caught up with are filled from another models.dev provider that lists thesame id; freezing that choice in a committed file is the point, because resolving it livemade `deepseek-v4.1-flash` gain and lose image input between runs depending on whichprovider models.dev happened to list first.Non-chat entitlements (audio, image generation) are skipped, and the ids it refused arerecorded in `skipped`. Ids the `alibaba-token-plan` entry has not caught up with are filledfrom another Alibaba catalog listing the same id (`alibaba-cn` and the `-cn` variantsdescribe the same models) and never from an unrelated provider, where a shared id is a namecollision: `auto` is a route on this gateway and model-oracle-ai's model elsewhere, so thatentry would hand the route someone else's 1M context and image input. Freezing the choice ina committed file is the point, because resolving it live made `deepseek-v4.1-flash` gain andlose image input between runs depending on which provider models.dev happened to list first.
At startup the extension still asks `/models` for entitlement only, and hides models thekey cannot call. The list is cached in `~/.prime/agent/alibaba-entitlement.json`, so aslow or failed call reuses the last known list.At startup the extension still asks `/models` for entitlement only, and hides models the keycannot call. The list is cached in `~/.prime/agent/alibaba-entitlement.json`, so a slow orfailed call reuses the last known list. Entitlement can only hide, never add, so an id theaccount gains after the last bake is invisible until `regen.ts` runs again; a session thatfinds one notifies with the ids and says to regenerate, rather than dropping them in silence.
## Thinking
| family | switch | depth | levels offered |53 unmodified lines44 unmodified lines45464748495051525354106 unmodified linesExpand all1611621631641651661671681691701711721731741751761771784 unmodified lines44 unmodified linesexport interface BakedCatalog { generatedAt: string; baseUrl: string; models: BakedModel[]; /** entitled ids the bake considered and refused */ skipped: string[];}
type Family = "qwen" | "deepseek" | "zai" | "other";
106 unmodified linesExpand all }, ...(m.reasoning && THINKING_LEVELS[familyOf(m.id)] ? { thinkingLevelMap: THINKING_LEVELS[familyOf(m.id)] } : {}), }));
// entitlement can only hide, never add, so an id gained after the last bake stays // invisible until regen.ts runs const known = new Set([...catalog.models.map((m) => m.id), ...catalog.skipped]); const unbaked = entitled === undefined ? [] : [...entitled].filter((id) => !known.has(id)).sort(); pi.on("session_start", (_event, ctx) => { if (unbaked.length > 0) { ctx.ui.notify(`alibaba: entitled but not in models.json: ${unbaked.join(", ")} - run npx tsx regen.ts`, "warning"); } });
pi.registerProvider(PROVIDER, { name: "Alibaba Model Studio (token plan)", baseUrl, apiKey: API_KEY_FALLBACK,4 unmodified lines122345622 unmodified lines293031323333343536375 unmodified lines43444546474849505152535455565758596045 unmodified lines106107108109110111112113114115116117118{ "generatedAt": "2026-09-15T00:18:25.774Z", "generatedAt": "2026-09-22T03:18:28.206Z", "baseUrl": "https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1", "models": [ { "id": "deepseek-v4-flash-0731",22 unmodified lines "input": [ "text", "image" ], "contextWindow": 1050000, "contextWindow": 1000000, "maxTokens": 384000 }, { "id": "glm-5.2",5 unmodified lines "contextWindow": 1000000, "maxTokens": 131072 }, { "id": "glm-5.3", "name": "GLM-5.3", "reasoning": true, "input": [ "text" ], "contextWindow": 1000000, "maxTokens": 131072 }, { "id": "qwen3.6-flash", "name": "Qwen3.6 Flash", "reasoning": true, "input": [45 unmodified lines ], "contextWindow": 1000000, "maxTokens": 131072 } ], "skipped": [ "auto", "qwen-audio-3.0-realtime-plus", "qwen-audio-3.0-tts-plus", "wan2.7-image", "wan2.7-image-pro" ]}42 unmodified lines434445464748474849505152525354555613 unmodified lines7071727374757576777879804 unmodified lines42 unmodified lines accountModelIds(baseUrl, key), fetch(CATALOG_URL).then((r) => r.json() as Promise<Record<string, { models?: Record<string, DevModel> }>>), ]);
// The token-plan entry describes this deployment; other providers are a fallback for // ids it has not caught up with yet, preferred in order of how much they know. // other alibaba catalogs describe the same models, an unrelated one just shares the id: // `auto` is a route here and model-oracle-ai's model there const own = payload[CATALOG_PROVIDER]?.models ?? {}; const elsewhere = new Map<string, DevModel[]>(); for (const [provider, entry] of Object.entries(payload)) { if (provider === CATALOG_PROVIDER) continue; if (provider === CATALOG_PROVIDER || !provider.startsWith("alibaba")) continue; for (const [id, model] of Object.entries(entry.models ?? {})) { elsewhere.set(id, [...(elsewhere.get(id) ?? []), model]); } }13 unmodified lines } models.push(toBaked(id, model)); } models.sort((a, b) => a.id.localeCompare(b.id)); skipped.sort();
const catalog: BakedCatalog = { generatedAt: new Date().toISOString(), baseUrl, models }; const catalog: BakedCatalog = { generatedAt: new Date().toISOString(), baseUrl, models, skipped }; writeFileSync(CATALOG, `${JSON.stringify(catalog, null, "\t")}\n`); for (const m of models) { console.log(`${m.id}\t${m.contextWindow}\t${m.maxTokens}\t${m.reasoning ? "thinking" : "-"}\t${m.input.join("+")}`); }4 unmodified lines