@p4code/cli 0.6.51 → 0.6.52

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/bin.mjs CHANGED
@@ -263,7 +263,7 @@ const make$123 = () => {
263
263
  const layer$124 = Layer.sync(NetService, make$123);
264
264
  //#endregion
265
265
  //#region package.json
266
- var version$1 = "0.6.51";
266
+ var version$1 = "0.6.52";
267
267
  //#endregion
268
268
  //#region ../../packages/shared/src/serverInstance.ts
269
269
  /**
@@ -33511,7 +33511,7 @@ const ManifestFile = Schema$1.Struct({
33511
33511
  directories: Schema$1.Record(Schema$1.String, Schema$1.Array(ManifestEntry))
33512
33512
  });
33513
33513
  const ManifestFileFromJson = Schema$1.fromJsonString(ManifestFile);
33514
- const decodeManifest$1 = Schema$1.decodeUnknownEffect(ManifestFileFromJson);
33514
+ const decodeManifest = Schema$1.decodeUnknownEffect(ManifestFileFromJson);
33515
33515
  const encodeManifest = Schema$1.encodeEffect(ManifestFileFromJson);
33516
33516
  const EMPTY = {
33517
33517
  version: 1,
@@ -33528,7 +33528,7 @@ const EMPTY = {
33528
33528
  const readSkillManifest = Effect.fn("skillManifest.read")(function* (manifestPath) {
33529
33529
  const raw = yield* (yield* FileSystem.FileSystem).readFileString(manifestPath).pipe(Effect.orElseSucceed(() => void 0));
33530
33530
  if (raw === void 0) return EMPTY;
33531
- return yield* decodeManifest$1(raw).pipe(Effect.orElseSucceed(() => EMPTY));
33531
+ return yield* decodeManifest(raw).pipe(Effect.orElseSucceed(() => EMPTY));
33532
33532
  });
33533
33533
  const entriesFor = (manifest, skillsRoot) => manifest.directories[skillsRoot] ?? [];
33534
33534
  const withEntriesFor = (manifest, skillsRoot, entries) => ({
@@ -37146,7 +37146,7 @@ function collectForkedAttachmentCopies(threadId, rows) {
37146
37146
  }
37147
37147
  return copies;
37148
37148
  }
37149
- function isRecord$6(value) {
37149
+ function isRecord$7(value) {
37150
37150
  return value !== null && typeof value === "object" && !Array.isArray(value);
37151
37151
  }
37152
37152
  /**
@@ -37157,7 +37157,7 @@ function isRecord$6(value) {
37157
37157
  function buildForkedProviderBinding(threadId, source) {
37158
37158
  if (source === void 0) return { rejection: "no-binding" };
37159
37159
  if (!FORKABLE_PROVIDERS.has(source.provider)) return { rejection: "provider-cannot-fork" };
37160
- if (!isRecord$6(source.resumeCursor) || typeof source.resumeCursor.resume !== "string") return { rejection: "no-resume-cursor" };
37160
+ if (!isRecord$7(source.resumeCursor) || typeof source.resumeCursor.resume !== "string") return { rejection: "no-resume-cursor" };
37161
37161
  return { binding: {
37162
37162
  threadId,
37163
37163
  provider: source.provider,
@@ -62754,18 +62754,18 @@ function deriveToolActivityPresentation(input) {
62754
62754
  }
62755
62755
  //#endregion
62756
62756
  //#region src/provider/acp/AcpRuntimeModel.ts
62757
- function isRecord$5(value) {
62757
+ function isRecord$6(value) {
62758
62758
  return typeof value === "object" && value !== null && !Array.isArray(value);
62759
62759
  }
62760
62760
  function isSessionModelState(value) {
62761
- if (!isRecord$5(value) || typeof value.currentModelId !== "string") return false;
62761
+ if (!isRecord$6(value) || typeof value.currentModelId !== "string") return false;
62762
62762
  if (!Array.isArray(value.availableModels)) return false;
62763
- return value.availableModels.every((model) => isRecord$5(model) && typeof model.modelId === "string" && typeof model.name === "string" && (model.description === void 0 || model.description === null || typeof model.description === "string"));
62763
+ return value.availableModels.every((model) => isRecord$6(model) && typeof model.modelId === "string" && typeof model.name === "string" && (model.description === void 0 || model.description === null || typeof model.description === "string"));
62764
62764
  }
62765
62765
  function isSessionModeState(value) {
62766
- if (!isRecord$5(value) || typeof value.currentModeId !== "string") return false;
62766
+ if (!isRecord$6(value) || typeof value.currentModeId !== "string") return false;
62767
62767
  if (!Array.isArray(value.availableModes)) return false;
62768
- return value.availableModes.every((mode) => isRecord$5(mode) && typeof mode.id === "string" && typeof mode.name === "string" && (mode.description === void 0 || typeof mode.description === "string"));
62768
+ return value.availableModes.every((mode) => isRecord$6(mode) && typeof mode.id === "string" && typeof mode.name === "string" && (mode.description === void 0 || typeof mode.description === "string"));
62769
62769
  }
62770
62770
  function extractModelConfigId(sessionResponse) {
62771
62771
  const configOptions = sessionResponse.configOptions;
@@ -62841,7 +62841,7 @@ function extractCommandFromTitle(title) {
62841
62841
  return /`([^`]+)`/.exec(title)?.[1]?.trim() || void 0;
62842
62842
  }
62843
62843
  function extractToolCallCommand(rawInput, title) {
62844
- if (isRecord$5(rawInput)) {
62844
+ if (isRecord$6(rawInput)) {
62845
62845
  const directCommand = normalizeCommandValue(rawInput.command);
62846
62846
  if (directCommand) return directCommand;
62847
62847
  const executable = typeof rawInput.executable === "string" ? rawInput.executable.trim() : "";
@@ -62963,7 +62963,7 @@ function parsePermissionRequest(params) {
62963
62963
  }
62964
62964
  function sessionUpdateIsReplay(params) {
62965
62965
  const meta = params._meta;
62966
- return isRecord$5(meta) && meta.isReplay === true;
62966
+ return isRecord$6(meta) && meta.isReplay === true;
62967
62967
  }
62968
62968
  const waitForSessionLoadReplayIdle = (input) => Effect.gen(function* () {
62969
62969
  const pollInterval = Duration.millis(25);
@@ -62978,8 +62978,8 @@ const waitForSessionLoadReplayIdle = (input) => Effect.gen(function* () {
62978
62978
  });
62979
62979
  function syntheticLoadSessionResponseFromInitialize(initializeResult) {
62980
62980
  const meta = initializeResult._meta;
62981
- const modelState = isRecord$5(meta) ? meta.modelState : void 0;
62982
- const modeState = isRecord$5(meta) ? meta.modeState : void 0;
62981
+ const modelState = isRecord$6(meta) ? meta.modelState : void 0;
62982
+ const modeState = isRecord$6(meta) ? meta.modeState : void 0;
62983
62983
  const models = isSessionModelState(modelState) ? modelState : void 0;
62984
62984
  const modes = isSessionModeState(modeState) ? modeState : void 0;
62985
62985
  return {
@@ -67083,24 +67083,426 @@ function hasValidClaudeManifestAdapters(manifest) {
67083
67083
  return Object.values(catalog.profiles).every((profile) => Option.isSome(decodeClaudeProfileAdapter(profile.adapter ?? {}))) && catalog.models.every((model) => Option.isSome(decodeClaudeModelAdapter(model.adapter ?? {})));
67084
67084
  }
67085
67085
  //#endregion
67086
+ //#region src/usage/usageSourceHttp.ts
67087
+ const MAX_RESPONSE_BYTES$1 = 1024 * 1024;
67088
+ const LOOPBACK_HOSTS$1 = /* @__PURE__ */ new Set([
67089
+ "localhost",
67090
+ "127.0.0.1",
67091
+ "[::1]"
67092
+ ]);
67093
+ /** The management key stays on the configured origin, including redirects. */
67094
+ async function readManagementJson(config, path, body, signal) {
67095
+ const base = new URL(config.url);
67096
+ if (base.username || base.password || base.search || base.hash || base.protocol !== "https:" && !(base.protocol === "http:" && LOOPBACK_HOSTS$1.has(base.hostname))) throw new Error("Invalid usage source origin");
67097
+ if (path !== "auth-files" && path !== "api-call" && !/^model-definitions\/[a-z0-9-]+$/.test(path)) throw new Error("Unsupported management operation");
67098
+ const response = await fetch(new URL(`/v0/management/${path}`, base), {
67099
+ method: body === void 0 ? "GET" : "POST",
67100
+ redirect: "error",
67101
+ signal,
67102
+ headers: {
67103
+ authorization: `Bearer ${config.managementKey}`,
67104
+ "content-type": "application/json"
67105
+ },
67106
+ ...body === void 0 ? {} : { body: JSON.stringify(body) }
67107
+ });
67108
+ if (!response.ok || !response.body) {
67109
+ await response.body?.cancel();
67110
+ throw new UsageLimitSourceError({ detail: response.status === 401 || response.status === 403 ? `The usage source rejected its management key (HTTP ${response.status}). Update the management key in Manage usage sources; a provider API key cannot replace it.` : `The usage source management request failed (HTTP ${response.status}). Check the source URL and management API availability.` });
67111
+ }
67112
+ const reader = response.body.getReader();
67113
+ const chunks = [];
67114
+ let total = 0;
67115
+ try {
67116
+ for (;;) {
67117
+ const chunk = await reader.read();
67118
+ if (chunk.done) break;
67119
+ total += chunk.value.byteLength;
67120
+ if (total > MAX_RESPONSE_BYTES$1) throw new Error("Usage source response too large");
67121
+ chunks.push(chunk.value);
67122
+ }
67123
+ return JSON.parse(Buffer.concat(chunks).toString("utf8"));
67124
+ } finally {
67125
+ await reader.cancel().catch(() => void 0);
67126
+ reader.releaseLock();
67127
+ }
67128
+ }
67129
+ //#endregion
67130
+ //#region src/provider/externalModelCatalog.ts
67131
+ /**
67132
+ * External model list folded over the bundled manifest. Sources in order: the
67133
+ * user's CLIProxyAPI (CPA) usage source, models.dev, then OpenRouter. Each
67134
+ * provider comes from the first source that lists it.
67135
+ *
67136
+ * The external list decides which Anthropic, OpenAI and Google models exist,
67137
+ * their names, context sizes and which one is current in each family. The
67138
+ * bundled manifest keeps what only P4 Code knows: Claude Code option profiles
67139
+ * (ultracode, effort mapping), minimum CLI versions, aliases and Antigravity's
67140
+ * effort-suffixed slugs. A model the bundle does not know yet inherits the
67141
+ * newest bundled model of its family.
67142
+ */
67143
+ const MODELS_DEV_URL = "https://models.dev/api.json";
67144
+ const OPENROUTER_MODELS_URL = "https://openrouter.ai/api/v1/models";
67145
+ const ExternalProvider = Schema$1.Literals([
67146
+ "anthropic",
67147
+ "openai",
67148
+ "google"
67149
+ ]);
67150
+ const EXTERNAL_PROVIDERS = [
67151
+ "anthropic",
67152
+ "openai",
67153
+ "google"
67154
+ ];
67155
+ /** CPA `model-definitions` channels per provider. Its Google channels trail models.dev. */
67156
+ const CPA_CHANNELS = [
67157
+ {
67158
+ provider: "anthropic",
67159
+ channel: "claude"
67160
+ },
67161
+ {
67162
+ provider: "openai",
67163
+ channel: "codex-pro"
67164
+ },
67165
+ {
67166
+ provider: "openai",
67167
+ channel: "codex-plus"
67168
+ },
67169
+ {
67170
+ provider: "openai",
67171
+ channel: "codex-team"
67172
+ },
67173
+ {
67174
+ provider: "openai",
67175
+ channel: "codex-free"
67176
+ }
67177
+ ];
67178
+ const ExternalModel = Schema$1.Struct({
67179
+ provider: ExternalProvider,
67180
+ id: Schema$1.String,
67181
+ name: Schema$1.String,
67182
+ family: Schema$1.String,
67183
+ /** ISO date; the newest model of a family is its current one. */
67184
+ releaseDate: Schema$1.optional(Schema$1.String),
67185
+ contextTokens: Schema$1.optional(Schema$1.Number),
67186
+ /** Effort levels the model accepts, when the source lists them. */
67187
+ effort: Schema$1.optional(Schema$1.Array(Schema$1.String)),
67188
+ /** Whether a fast mode exists, when the source says. */
67189
+ fastMode: Schema$1.optional(Schema$1.Boolean)
67190
+ });
67191
+ const isRecord$5 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
67192
+ const stringOr = (value) => typeof value === "string" && value.trim() !== "" ? value.trim() : void 0;
67193
+ const numberOr = (value) => typeof value === "number" && Number.isFinite(value) && value > 0 ? value : void 0;
67194
+ /** Coding agents only run tool-calling models that answer in text; image, audio and TTS models share families with them. */
67195
+ const isTextOnly = (modalities) => Array.isArray(modalities) && modalities.length === 1 && modalities[0] === "text";
67196
+ /** Family of a model id with its version tokens removed: `claude-opus-5-5` is `claude-opus`. */
67197
+ function familyOf(id) {
67198
+ return id.split("-").filter((token) => !/^\d+(\.\d+)*$/.test(token)).join("-");
67199
+ }
67200
+ /** Parse one CPA `/v0/management/model-definitions/:channel` response. */
67201
+ function parseCpaModelDefinitions(json, provider) {
67202
+ if (!isRecord$5(json) || !Array.isArray(json.models)) return [];
67203
+ return json.models.flatMap((raw) => {
67204
+ if (!isRecord$5(raw)) return [];
67205
+ const id = stringOr(raw.id);
67206
+ const output = raw.supportedOutputModalities;
67207
+ if (!id || output !== void 0 && !isTextOnly(output)) return [];
67208
+ const created = numberOr(raw.created);
67209
+ const contextTokens = numberOr(raw.context_length) ?? numberOr(raw.inputTokenLimit);
67210
+ const levels = isRecord$5(raw.thinking) ? raw.thinking.levels : void 0;
67211
+ return [{
67212
+ provider,
67213
+ id,
67214
+ name: stringOr(raw.display_name) ?? id,
67215
+ family: familyOf(id),
67216
+ ...created ? { releaseDate: (/* @__PURE__ */ new Date(created * 1e3)).toISOString() } : {},
67217
+ ...contextTokens ? { contextTokens } : {},
67218
+ ...Array.isArray(levels) ? { effort: levels.filter((level) => typeof level === "string") } : {}
67219
+ }];
67220
+ });
67221
+ }
67222
+ /** Parse `https://models.dev/api.json`; null when it lists none of our providers. */
67223
+ function parseModelsDev(json) {
67224
+ if (!isRecord$5(json)) return null;
67225
+ const models = [];
67226
+ for (const provider of EXTERNAL_PROVIDERS) {
67227
+ const entry = json[provider];
67228
+ if (!isRecord$5(entry) || !isRecord$5(entry.models)) continue;
67229
+ for (const raw of Object.values(entry.models)) {
67230
+ if (!isRecord$5(raw)) continue;
67231
+ const id = stringOr(raw.id);
67232
+ const output = isRecord$5(raw.modalities) ? raw.modalities.output : void 0;
67233
+ if (!id || raw.tool_call !== true || !isTextOnly(output)) continue;
67234
+ const effort = Array.isArray(raw.reasoning_options) ? raw.reasoning_options.find((option) => isRecord$5(option) && option.type === "effort") : void 0;
67235
+ const effortValues = isRecord$5(effort) && Array.isArray(effort.values) ? effort.values.filter((value) => typeof value === "string") : void 0;
67236
+ const modes = isRecord$5(raw.experimental) ? raw.experimental.modes : void 0;
67237
+ const releaseDate = stringOr(raw.release_date);
67238
+ const contextTokens = isRecord$5(raw.limit) ? numberOr(raw.limit.context) : void 0;
67239
+ models.push({
67240
+ provider,
67241
+ id,
67242
+ name: stringOr(raw.name) ?? id,
67243
+ family: stringOr(raw.family) ?? familyOf(id),
67244
+ ...releaseDate ? { releaseDate } : {},
67245
+ ...contextTokens ? { contextTokens } : {},
67246
+ ...effortValues ? { effort: effortValues } : {},
67247
+ fastMode: isRecord$5(modes) && modes.fast !== void 0
67248
+ });
67249
+ }
67250
+ }
67251
+ return models.length > 0 ? models : null;
67252
+ }
67253
+ /** Parse `https://openrouter.ai/api/v1/models`; null when it lists none of our providers. */
67254
+ function parseOpenRouter(json) {
67255
+ if (!isRecord$5(json) || !Array.isArray(json.data)) return null;
67256
+ const models = [];
67257
+ for (const raw of json.data) {
67258
+ if (!isRecord$5(raw)) continue;
67259
+ const fullId = stringOr(raw.id);
67260
+ if (!fullId || fullId.includes(":")) continue;
67261
+ const output = isRecord$5(raw.architecture) ? raw.architecture.output_modalities : void 0;
67262
+ const parameters = Array.isArray(raw.supported_parameters) ? raw.supported_parameters : [];
67263
+ if (!isTextOnly(output) || !parameters.includes("tools")) continue;
67264
+ const [prefix, rawId] = fullId.split("/", 2);
67265
+ const provider = EXTERNAL_PROVIDERS.find((candidate) => candidate === prefix);
67266
+ if (!provider || !rawId) continue;
67267
+ const id = provider === "anthropic" ? rawId.replace(/(\d)\.(\d)/g, "$1-$2") : rawId;
67268
+ const created = numberOr(raw.created);
67269
+ const contextTokens = numberOr(raw.context_length);
67270
+ models.push({
67271
+ provider,
67272
+ id,
67273
+ name: (stringOr(raw.name) ?? id).replace(/^[^:]+:\s*/, ""),
67274
+ family: familyOf(id),
67275
+ ...created ? { releaseDate: (/* @__PURE__ */ new Date(created * 1e3)).toISOString() } : {},
67276
+ ...contextTokens ? { contextTokens } : {}
67277
+ });
67278
+ }
67279
+ return models.length > 0 ? models : null;
67280
+ }
67281
+ /** Providers the list has no models for. */
67282
+ function missingProviders(models) {
67283
+ return EXTERNAL_PROVIDERS.filter((provider) => !models.some((model) => model.provider === provider));
67284
+ }
67285
+ /** Add `next` for the providers `models` lacks; ids already present win. */
67286
+ function fillMissingProviders(models, next) {
67287
+ const missing = new Set(missingProviders(models));
67288
+ return [...models, ...next.filter((model) => missing.has(model.provider))];
67289
+ }
67290
+ const DATED_SNAPSHOT = /-\d{8}$/;
67291
+ const releaseMs = (model) => {
67292
+ const parsed = model?.releaseDate ? Date.parse(model.releaseDate) : NaN;
67293
+ return Number.isNaN(parsed) ? 0 : parsed;
67294
+ };
67295
+ /** A family the bundle never listed is retired once its newest release trails the provider's by this much. */
67296
+ const CURRENT_FAMILY_WINDOW_MS = 4320 * 60 * 60 * 1e3;
67297
+ /**
67298
+ * Ids that are current: the newest release of each family, and `-latest`
67299
+ * pointers. Families the bundle lists keep their newest model current however
67300
+ * long the next release takes; other families and `-latest` pointers must be
67301
+ * recent enough to still be shipping.
67302
+ */
67303
+ function currentIds(models, bundledFamilies) {
67304
+ const newest = /* @__PURE__ */ new Map();
67305
+ let providerNewest = 0;
67306
+ for (const model of models) {
67307
+ const released = releaseMs(model);
67308
+ newest.set(model.family, Math.max(newest.get(model.family) ?? 0, released));
67309
+ providerNewest = Math.max(providerNewest, released);
67310
+ }
67311
+ return new Set(models.filter((model) => {
67312
+ const released = releaseMs(model);
67313
+ const recent = providerNewest - released <= CURRENT_FAMILY_WINDOW_MS;
67314
+ if (model.id.endsWith("-latest")) return recent;
67315
+ return released === newest.get(model.family) && (recent || bundledFamilies.has(model.family));
67316
+ }).map((model) => model.id));
67317
+ }
67318
+ /** Effort values only Claude Code offers, with the API effort each one needs. */
67319
+ const CLAUDE_CODE_EFFORTS = {
67320
+ ultracode: "xhigh",
67321
+ ultrathink: null
67322
+ };
67323
+ /** Template profile narrowed to what the external entry says the model supports. */
67324
+ function deriveClaudeProfile(template, model) {
67325
+ const descriptors = (template?.capabilities?.optionDescriptors ?? []).flatMap((descriptor) => {
67326
+ if (descriptor.id === "fastMode" && model.fastMode === false) return [];
67327
+ if (descriptor.id !== "effort" || descriptor.type !== "select" || !model.effort) return [descriptor];
67328
+ const supported = new Set(model.effort);
67329
+ const options = descriptor.options.filter((option) => {
67330
+ const needs = CLAUDE_CODE_EFFORTS[option.id];
67331
+ return supported.has(option.id) || needs === null || needs !== void 0 && supported.has(needs);
67332
+ });
67333
+ return options.length > 0 ? [{
67334
+ ...descriptor,
67335
+ options
67336
+ }] : [];
67337
+ });
67338
+ const adapter = isRecord$5(template?.adapter) ? template.adapter : {};
67339
+ const claudeCode = isRecord$5(adapter.claudeCode) ? adapter.claudeCode : {};
67340
+ const hasContextPicker = descriptors.some((descriptor) => descriptor.id === "contextWindow");
67341
+ return {
67342
+ capabilities: { optionDescriptors: descriptors },
67343
+ adapter: {
67344
+ ...adapter,
67345
+ claudeCode: {
67346
+ ...claudeCode,
67347
+ ...!hasContextPicker && model.contextTokens ? { fixedContextWindowTokens: model.contextTokens } : {}
67348
+ }
67349
+ }
67350
+ };
67351
+ }
67352
+ /** `claude-opus-6-1` reads `opus-6.1` and `claude-opus-6.1`; `claude-opus-6` reads `opus-6`. */
67353
+ function shortAliases(slug) {
67354
+ const dotted = slug.replace(/-(\d+)-(\d+)$/, "-$1.$2");
67355
+ return [.../* @__PURE__ */ new Set([dotted.replace(/^claude-/, ""), dotted])].filter((alias) => alias !== slug);
67356
+ }
67357
+ function deriveClaudeCatalog(catalog, external) {
67358
+ const models = external.filter((model) => !DATED_SNAPSHOT.test(model.id));
67359
+ const current = currentIds(models, new Set(catalog.models.map((entry) => familyOf(entry.slug))));
67360
+ const byId = new Map(models.map((model) => [model.id, model]));
67361
+ const profiles = { ...catalog.profiles };
67362
+ const entries = catalog.models.map((entry) => {
67363
+ if (!byId.get(entry.slug)) return { ...entry };
67364
+ return {
67365
+ ...entry,
67366
+ status: current.has(entry.slug) ? "current" : "legacy"
67367
+ };
67368
+ });
67369
+ const bundledSlugs = new Set(catalog.models.map((entry) => entry.slug));
67370
+ for (const model of models) {
67371
+ if (bundledSlugs.has(model.id) || !current.has(model.id)) continue;
67372
+ const template = catalog.models.filter((entry) => familyOf(entry.slug) === model.family).toSorted((left, right) => releaseMs(byId.get(right.slug)) - releaseMs(byId.get(left.slug))).at(0);
67373
+ const profileKey = `models.dev:${model.id}`;
67374
+ profiles[profileKey] = deriveClaudeProfile(template?.profile ? catalog.profiles[template.profile] : void 0, model);
67375
+ const aliases = shortAliases(model.id);
67376
+ entries.push({
67377
+ slug: model.id,
67378
+ name: model.name,
67379
+ ...aliases.length > 0 ? { aliases } : {},
67380
+ status: "current",
67381
+ profile: profileKey,
67382
+ ...template?.adapter !== void 0 ? { adapter: template.adapter } : {}
67383
+ });
67384
+ }
67385
+ for (const entry of entries) {
67386
+ const familyAlias = familyOf(entry.slug).replace(/^claude-/, "");
67387
+ if (entry.status === "current" || !entry.aliases?.includes(familyAlias)) continue;
67388
+ const successor = entries.find((candidate) => candidate.status === "current" && familyOf(candidate.slug) === familyOf(entry.slug));
67389
+ if (!successor) continue;
67390
+ entry.aliases = entry.aliases.filter((alias) => alias !== familyAlias);
67391
+ successor.aliases = [familyAlias, ...successor.aliases ?? []];
67392
+ }
67393
+ return {
67394
+ catalog: {
67395
+ ...catalog,
67396
+ ...withCurrentDefault(catalog, entries),
67397
+ profiles,
67398
+ models: entries
67399
+ },
67400
+ current: entries.filter((entry) => entry.status === "current").map((entry) => entry.slug)
67401
+ };
67402
+ }
67403
+ /** Keep the chat default; when it went legacy, move it to its family's current model. */
67404
+ function withCurrentDefault(catalog, entries, matches = (candidate, previous) => familyOf(candidate.slug) === familyOf(previous.slug)) {
67405
+ const chat = catalog.defaults?.chat;
67406
+ const previous = entries.find((entry) => entry.slug === chat);
67407
+ if (!previous || previous.status === "current") return { defaults: catalog.defaults };
67408
+ const next = entries.find((entry) => entry.status === "current" && matches(entry, previous));
67409
+ return { defaults: {
67410
+ ...catalog.defaults,
67411
+ chat: next?.slug ?? chat
67412
+ } };
67413
+ }
67414
+ const ANTIGRAVITY_EFFORT_SUFFIX = /-(high|medium|low|minimal)$/;
67415
+ const effortLabel = (effort) => effort.charAt(0).toUpperCase() + effort.slice(1);
67416
+ /**
67417
+ * Antigravity slugs carry their effort (`gemini-3.8-flash-high`). Every current
67418
+ * Google model in a family the bundle already offers gets the same variants.
67419
+ */
67420
+ function deriveAntigravityCatalog(catalog, external) {
67421
+ const byId = new Map(external.map((model) => [model.id, model]));
67422
+ const baseOf = (slug) => slug.replace(ANTIGRAVITY_EFFORT_SUFFIX, "");
67423
+ const suffixes = [...new Set(catalog.models.flatMap((entry) => entry.slug.match(ANTIGRAVITY_EFFORT_SUFFIX)?.[1] ?? []))];
67424
+ const families = new Set(catalog.models.flatMap((entry) => byId.get(baseOf(entry.slug))?.family ?? []));
67425
+ const current = currentIds(external, families);
67426
+ const entries = catalog.models.map((entry) => {
67427
+ const known = byId.get(baseOf(entry.slug));
67428
+ if (!known) return entry;
67429
+ return {
67430
+ ...entry,
67431
+ status: current.has(known.id) ? "current" : "legacy"
67432
+ };
67433
+ });
67434
+ const slugs = new Set(entries.map((entry) => entry.slug));
67435
+ for (const model of external) {
67436
+ if (!families.has(model.family) || !current.has(model.id) || model.id.endsWith("-latest")) continue;
67437
+ for (const suffix of suffixes) {
67438
+ const slug = `${model.id}-${suffix}`;
67439
+ if (slugs.has(slug) || model.effort && !model.effort.includes(suffix)) continue;
67440
+ slugs.add(slug);
67441
+ entries.push({
67442
+ slug,
67443
+ name: `${model.name} (${effortLabel(suffix)})`,
67444
+ status: "current"
67445
+ });
67446
+ }
67447
+ }
67448
+ return {
67449
+ catalog: {
67450
+ ...catalog,
67451
+ ...withCurrentDefault(catalog, entries, (candidate, previous) => candidate.slug.match(ANTIGRAVITY_EFFORT_SUFFIX)?.[1] === previous.slug.match(ANTIGRAVITY_EFFORT_SUFFIX)?.[1]),
67452
+ models: entries
67453
+ },
67454
+ current: entries.filter((entry) => entry.status === "current").map((entry) => entry.slug)
67455
+ };
67456
+ }
67457
+ /** Codex discovers its own models; the manifest only says which are current. */
67458
+ function deriveCodexCurrent(bundledCurrent, external) {
67459
+ const byId = new Map(external.map((model) => [model.id, model]));
67460
+ const current = currentIds(external, new Set(bundledCurrent.map((slug) => byId.get(slug)?.family ?? familyOf(slug))));
67461
+ const known = new Set(byId.keys());
67462
+ return [.../* @__PURE__ */ new Set([...current, ...bundledCurrent.filter((slug) => !known.has(slug))])];
67463
+ }
67464
+ /** Fold the external model list over the bundled manifest. */
67465
+ function deriveModelManifest(bundled, external) {
67466
+ const forProvider = (provider) => external.filter((model) => model.provider === provider);
67467
+ const providers = { ...bundled.providers };
67468
+ const currentModels = { ...bundled.currentModels };
67469
+ const claude = bundled.providers?.claudeAgent;
67470
+ if (claude) {
67471
+ const derived = deriveClaudeCatalog(claude, forProvider("anthropic"));
67472
+ providers.claudeAgent = derived.catalog;
67473
+ currentModels.claudeAgent = derived.current;
67474
+ }
67475
+ const antigravity = bundled.providers?.antigravity;
67476
+ if (antigravity) {
67477
+ const derived = deriveAntigravityCatalog(antigravity, forProvider("google"));
67478
+ providers.antigravity = derived.catalog;
67479
+ currentModels.antigravity = derived.current;
67480
+ }
67481
+ currentModels.codex = deriveCodexCurrent(bundled.currentModels.codex ?? [], forProvider("openai"));
67482
+ return {
67483
+ ...bundled,
67484
+ currentModels,
67485
+ providers
67486
+ };
67487
+ }
67488
+ //#endregion
67086
67489
  //#region src/provider/model-manifest.json
67087
67490
  var model_manifest_default = {
67088
67491
  version: 1,
67089
- updatedAt: "2026-09-28T20:00:00Z",
67492
+ updatedAt: "2026-10-07T19:00:00Z",
67090
67493
  currentModels: {
67091
67494
  "codex": [
67092
67495
  "gpt-6-astra",
67093
- "gpt-5.6-luna",
67094
- "gpt-5.6-terra",
67095
- "gpt-5.6-sol",
67496
+ "gpt-6.1-sol",
67497
+ "gpt-6-luna",
67096
67498
  "gpt-daybreak-blue-latest",
67097
67499
  "gpt-daybreak-red-latest"
67098
67500
  ],
67099
67501
  "claudeAgent": [
67100
67502
  "claude-fable-5-1",
67101
- "claude-opus-5",
67503
+ "claude-opus-5-5",
67102
67504
  "claude-sonnet-5-5",
67103
- "claude-sonnet-5"
67505
+ "claude-haiku-5-5"
67104
67506
  ],
67105
67507
  "antigravity": [
67106
67508
  "gemini-3.8-flash-high",
@@ -67110,7 +67512,7 @@ var model_manifest_default = {
67110
67512
  },
67111
67513
  providers: {
67112
67514
  "claudeAgent": {
67113
- "defaults": { "chat": "claude-sonnet-5" },
67515
+ "defaults": { "chat": "claude-fable-5-1" },
67114
67516
  "profiles": {
67115
67517
  "fable-5": {
67116
67518
  "capabilities": { "optionDescriptors": [{
@@ -67124,13 +67526,60 @@ var model_manifest_default = {
67124
67526
  },
67125
67527
  {
67126
67528
  "id": "medium",
67127
- "label": "Medium"
67529
+ "label": "Medium",
67530
+ "isDefault": true
67128
67531
  },
67129
67532
  {
67130
67533
  "id": "high",
67131
- "label": "High",
67534
+ "label": "High"
67535
+ },
67536
+ {
67537
+ "id": "xhigh",
67538
+ "label": "Extra High"
67539
+ },
67540
+ {
67541
+ "id": "max",
67542
+ "label": "Max"
67543
+ },
67544
+ {
67545
+ "id": "ultracode",
67546
+ "label": "Ultracode",
67547
+ "description": "xhigh effort plus multi-agent workflow orchestration"
67548
+ },
67549
+ {
67550
+ "id": "ultrathink",
67551
+ "label": "Ultrathink"
67552
+ }
67553
+ ],
67554
+ "promptInjectedValues": ["ultrathink"]
67555
+ }] },
67556
+ "adapter": { "claudeCode": {
67557
+ "effortMap": {
67558
+ "ultracode": "xhigh",
67559
+ "ultrathink": null
67560
+ },
67561
+ "fixedContextWindowTokens": 1e6
67562
+ } }
67563
+ },
67564
+ "opus-5-5": {
67565
+ "capabilities": { "optionDescriptors": [{
67566
+ "id": "effort",
67567
+ "label": "Reasoning",
67568
+ "type": "select",
67569
+ "options": [
67570
+ {
67571
+ "id": "low",
67572
+ "label": "Low"
67573
+ },
67574
+ {
67575
+ "id": "medium",
67576
+ "label": "Medium",
67132
67577
  "isDefault": true
67133
67578
  },
67579
+ {
67580
+ "id": "high",
67581
+ "label": "High"
67582
+ },
67134
67583
  {
67135
67584
  "id": "xhigh",
67136
67585
  "label": "Extra High"
@@ -67151,99 +67600,67 @@ var model_manifest_default = {
67151
67600
  ],
67152
67601
  "promptInjectedValues": ["ultrathink"]
67153
67602
  }, {
67154
- "id": "contextWindow",
67155
- "label": "Context Window",
67156
- "type": "select",
67157
- "options": [{
67158
- "id": "200k",
67159
- "label": "200k"
67160
- }, {
67161
- "id": "1m",
67162
- "label": "1M",
67163
- "isDefault": true
67164
- }]
67603
+ "id": "fastMode",
67604
+ "label": "Fast Mode",
67605
+ "type": "boolean"
67165
67606
  }] },
67166
67607
  "adapter": { "claudeCode": {
67167
67608
  "effortMap": {
67168
67609
  "ultracode": "xhigh",
67169
67610
  "ultrathink": null
67170
67611
  },
67171
- "modelSuffixes": { "contextWindow": { "1m": "[1m]" } },
67172
- "contextWindowTokens": {
67173
- "200k": 2e5,
67174
- "1m": 1e6
67175
- }
67612
+ "fixedContextWindowTokens": 1e6
67176
67613
  } }
67177
67614
  },
67178
67615
  "opus-5": {
67179
- "capabilities": { "optionDescriptors": [
67180
- {
67181
- "id": "effort",
67182
- "label": "Reasoning",
67183
- "type": "select",
67184
- "options": [
67185
- {
67186
- "id": "low",
67187
- "label": "Low"
67188
- },
67189
- {
67190
- "id": "medium",
67191
- "label": "Medium"
67192
- },
67193
- {
67194
- "id": "high",
67195
- "label": "High",
67196
- "isDefault": true
67197
- },
67198
- {
67199
- "id": "xhigh",
67200
- "label": "Extra High"
67201
- },
67202
- {
67203
- "id": "max",
67204
- "label": "Max"
67205
- },
67206
- {
67207
- "id": "ultracode",
67208
- "label": "Ultracode",
67209
- "description": "xhigh effort plus multi-agent workflow orchestration"
67210
- },
67211
- {
67212
- "id": "ultrathink",
67213
- "label": "Ultrathink"
67214
- }
67215
- ],
67216
- "promptInjectedValues": ["ultrathink"]
67217
- },
67218
- {
67219
- "id": "fastMode",
67220
- "label": "Fast Mode",
67221
- "type": "boolean"
67222
- },
67223
- {
67224
- "id": "contextWindow",
67225
- "label": "Context Window",
67226
- "type": "select",
67227
- "options": [{
67228
- "id": "200k",
67229
- "label": "200k"
67230
- }, {
67231
- "id": "1m",
67232
- "label": "1M",
67616
+ "capabilities": { "optionDescriptors": [{
67617
+ "id": "effort",
67618
+ "label": "Reasoning",
67619
+ "type": "select",
67620
+ "options": [
67621
+ {
67622
+ "id": "low",
67623
+ "label": "Low"
67624
+ },
67625
+ {
67626
+ "id": "medium",
67627
+ "label": "Medium"
67628
+ },
67629
+ {
67630
+ "id": "high",
67631
+ "label": "High",
67233
67632
  "isDefault": true
67234
- }]
67235
- }
67236
- ] },
67633
+ },
67634
+ {
67635
+ "id": "xhigh",
67636
+ "label": "Extra High"
67637
+ },
67638
+ {
67639
+ "id": "max",
67640
+ "label": "Max"
67641
+ },
67642
+ {
67643
+ "id": "ultracode",
67644
+ "label": "Ultracode",
67645
+ "description": "xhigh effort plus multi-agent workflow orchestration"
67646
+ },
67647
+ {
67648
+ "id": "ultrathink",
67649
+ "label": "Ultrathink"
67650
+ }
67651
+ ],
67652
+ "promptInjectedValues": ["ultrathink"]
67653
+ }, {
67654
+ "id": "fastMode",
67655
+ "label": "Fast Mode",
67656
+ "type": "boolean"
67657
+ }] },
67237
67658
  "adapter": { "claudeCode": {
67238
67659
  "effortMap": {
67239
67660
  "ultracode": "xhigh",
67240
67661
  "ultrathink": null
67241
67662
  },
67242
- "modelSuffixes": { "contextWindow": { "1m": "[1m]" } },
67243
- "contextWindowTokens": {
67244
- "200k": 2e5,
67245
- "1m": 1e6
67246
- }
67663
+ "fixedContextWindowTokens": 1e6
67247
67664
  } }
67248
67665
  },
67249
67666
  "opus-4-8": {
@@ -67431,7 +67848,7 @@ var model_manifest_default = {
67431
67848
  "label": "Fast Mode",
67432
67849
  "type": "boolean"
67433
67850
  }] },
67434
- "adapter": { "claudeCode": {} }
67851
+ "adapter": { "claudeCode": { "fixedContextWindowTokens": 2e5 } }
67435
67852
  },
67436
67853
  "sonnet-5": {
67437
67854
  "capabilities": { "optionDescriptors": [{
@@ -67466,26 +67883,10 @@ var model_manifest_default = {
67466
67883
  }
67467
67884
  ],
67468
67885
  "promptInjectedValues": ["ultrathink"]
67469
- }, {
67470
- "id": "contextWindow",
67471
- "label": "Context Window",
67472
- "type": "select",
67473
- "options": [{
67474
- "id": "200k",
67475
- "label": "200k",
67476
- "isDefault": true
67477
- }, {
67478
- "id": "1m",
67479
- "label": "1M"
67480
- }]
67481
67886
  }] },
67482
67887
  "adapter": { "claudeCode": {
67483
67888
  "effortMap": { "ultrathink": null },
67484
- "modelSuffixes": { "contextWindow": { "1m": "[1m]" } },
67485
- "contextWindowTokens": {
67486
- "200k": 2e5,
67487
- "1m": 1e6
67488
- }
67889
+ "fixedContextWindowTokens": 1e6
67489
67890
  } }
67490
67891
  },
67491
67892
  "sonnet-4-6": {
@@ -67542,16 +67943,81 @@ var model_manifest_default = {
67542
67943
  }
67543
67944
  } }
67544
67945
  },
67946
+ "haiku-5-5": {
67947
+ "capabilities": { "optionDescriptors": [{
67948
+ "id": "effort",
67949
+ "label": "Reasoning",
67950
+ "type": "select",
67951
+ "options": [
67952
+ {
67953
+ "id": "low",
67954
+ "label": "Low"
67955
+ },
67956
+ {
67957
+ "id": "medium",
67958
+ "label": "Medium",
67959
+ "isDefault": true
67960
+ },
67961
+ {
67962
+ "id": "high",
67963
+ "label": "High"
67964
+ },
67965
+ {
67966
+ "id": "xhigh",
67967
+ "label": "Extra High"
67968
+ },
67969
+ {
67970
+ "id": "max",
67971
+ "label": "Max"
67972
+ },
67973
+ {
67974
+ "id": "ultrathink",
67975
+ "label": "Ultrathink"
67976
+ }
67977
+ ],
67978
+ "promptInjectedValues": ["ultrathink"]
67979
+ }] },
67980
+ "adapter": { "claudeCode": {
67981
+ "effortMap": { "ultrathink": null },
67982
+ "fixedContextWindowTokens": 1e6
67983
+ } }
67984
+ },
67545
67985
  "haiku-4-5": {
67546
67986
  "capabilities": { "optionDescriptors": [{
67547
67987
  "id": "thinking",
67548
67988
  "label": "Thinking",
67549
67989
  "type": "boolean"
67550
67990
  }] },
67551
- "adapter": { "claudeCode": {} }
67991
+ "adapter": { "claudeCode": { "fixedContextWindowTokens": 2e5 } }
67552
67992
  }
67553
67993
  },
67554
67994
  "models": [
67995
+ {
67996
+ "slug": "claude-opus-5-5",
67997
+ "name": "Claude Opus 5.5",
67998
+ "aliases": [
67999
+ "opus",
68000
+ "opus-5.5",
68001
+ "claude-opus-5.5"
68002
+ ],
68003
+ "status": "current",
68004
+ "badge": "new",
68005
+ "profile": "opus-5-5",
68006
+ "adapter": { "claudeCode": { "minVersion": "2.1.280" } }
68007
+ },
68008
+ {
68009
+ "slug": "claude-sonnet-5-5",
68010
+ "name": "Claude Sonnet 5.5",
68011
+ "aliases": [
68012
+ "sonnet",
68013
+ "sonnet-5.5",
68014
+ "claude-sonnet-5.5"
68015
+ ],
68016
+ "status": "current",
68017
+ "badge": "new",
68018
+ "profile": "sonnet-5",
68019
+ "adapter": { "claudeCode": { "minVersion": "2.1.284" } }
68020
+ },
67555
68021
  {
67556
68022
  "slug": "claude-fable-5-1",
67557
68023
  "name": "Claude Fable 5.1",
@@ -67561,7 +68027,6 @@ var model_manifest_default = {
67561
68027
  "claude-fable-5.1"
67562
68028
  ],
67563
68029
  "status": "current",
67564
- "badge": "new",
67565
68030
  "profile": "fable-5",
67566
68031
  "adapter": { "claudeCode": { "minVersion": "2.1.257" } }
67567
68032
  },
@@ -67576,12 +68041,11 @@ var model_manifest_default = {
67576
68041
  "slug": "claude-opus-5",
67577
68042
  "name": "Claude Opus 5",
67578
68043
  "aliases": [
67579
- "opus",
67580
68044
  "opus-5",
67581
68045
  "claude-opus-5.0",
67582
68046
  "claude-opus-5-0"
67583
68047
  ],
67584
- "status": "current",
68048
+ "status": "legacy",
67585
68049
  "profile": "opus-5",
67586
68050
  "adapter": { "claudeCode": { "minVersion": "2.1.219" } }
67587
68051
  },
@@ -67618,25 +68082,15 @@ var model_manifest_default = {
67618
68082
  "status": "legacy",
67619
68083
  "profile": "opus-4-5"
67620
68084
  },
67621
- {
67622
- "slug": "claude-sonnet-5-5",
67623
- "name": "Claude Sonnet 5.5",
67624
- "aliases": ["sonnet-5.5", "claude-sonnet-5.5"],
67625
- "status": "current",
67626
- "badge": "new",
67627
- "profile": "sonnet-5",
67628
- "adapter": { "claudeCode": { "minVersion": "2.1.284" } }
67629
- },
67630
68085
  {
67631
68086
  "slug": "claude-sonnet-5",
67632
68087
  "name": "Claude Sonnet 5",
67633
68088
  "aliases": [
67634
- "sonnet",
67635
68089
  "sonnet-5",
67636
68090
  "claude-sonnet-5.0",
67637
68091
  "claude-sonnet-5-0"
67638
68092
  ],
67639
- "status": "current",
68093
+ "status": "legacy",
67640
68094
  "profile": "sonnet-5"
67641
68095
  },
67642
68096
  {
@@ -67650,11 +68104,23 @@ var model_manifest_default = {
67650
68104
  "status": "legacy",
67651
68105
  "profile": "sonnet-4-6"
67652
68106
  },
68107
+ {
68108
+ "slug": "claude-haiku-5-5",
68109
+ "name": "Claude Haiku 5.5",
68110
+ "aliases": [
68111
+ "haiku",
68112
+ "haiku-5.5",
68113
+ "claude-haiku-5.5"
68114
+ ],
68115
+ "status": "current",
68116
+ "badge": "new",
68117
+ "profile": "haiku-5-5",
68118
+ "adapter": { "claudeCode": { "minVersion": "2.1.293" } }
68119
+ },
67653
68120
  {
67654
68121
  "slug": "claude-haiku-4-5",
67655
68122
  "name": "Claude Haiku 4.5",
67656
68123
  "aliases": [
67657
- "haiku",
67658
68124
  "haiku-4.5",
67659
68125
  "claude-haiku-4.5",
67660
68126
  "claude-haiku-4-5-20251001"
@@ -67690,27 +68156,27 @@ var model_manifest_default = {
67690
68156
  //#endregion
67691
68157
  //#region src/provider/ModelManifest.ts
67692
68158
  /**
67693
- * ModelManifest — remote provider-model metadata with a bundled offline
67694
- * fallback.
68159
+ * ModelManifest — provider-model metadata: the bundled manifest folded with
68160
+ * the external model list from models.dev (OpenRouter as fallback).
67695
68161
  *
67696
- * Provider catalogs and legacy classification live in `model-manifest.json`.
67697
- * The bundled copy ships with every release; at runtime the service refreshes
67698
- * it from the same file on `main`. Preference order is remote, then the last
67699
- * successful on-disk copy, then the bundle. A failed fetch never fails a
67700
- * provider check.
68162
+ * Provider catalogs and legacy classification live in `model-manifest.json`,
68163
+ * which ships with every release. At runtime the service fetches the external
68164
+ * list and derives the manifest from both (`externalModelCatalog.ts`).
68165
+ * Preference order is a fresh fetch, then the last successful list on disk,
68166
+ * then the bundle alone. A failed fetch never fails a provider check.
67701
68167
  *
67702
68168
  * Providers with authoritative discovery can use only the classification
67703
68169
  * overlay. Providers with static catalogs can resolve presentation and
67704
68170
  * capabilities from `providers`, then decode their own allowlisted adapter
67705
68171
  * payload separately.
67706
68172
  */
67707
- const MODEL_MANIFEST_URL = "https://raw.githubusercontent.com/pingdotgg/t3code/main/apps/server/src/provider/model-manifest.json";
67708
68173
  /** How long a fetched manifest stays fresh before the next probe re-fetches. */
67709
68174
  const MANIFEST_TTL_MS = 3600 * 1e3;
67710
68175
  /** Minimum gap between fetch attempts after a failure, so an offline server
67711
68176
  * does not pay a network timeout on every provider check. */
67712
68177
  const MANIFEST_RETRY_MS = 300 * 1e3;
67713
- const FETCH_TIMEOUT_MS = 1e4;
68178
+ /** models.dev serves about 0.5 MB compressed, so allow more than a small JSON. */
68179
+ const FETCH_TIMEOUT_MS = 2e4;
67714
68180
  const ManifestModelStatus = Schema$1.Literals(["current", "legacy"]);
67715
68181
  const ManifestModelProfile = Schema$1.Struct({
67716
68182
  capabilities: Schema$1.optional(ModelCapabilities),
@@ -67738,11 +68204,7 @@ const ManifestProviderCatalog = Schema$1.Struct({
67738
68204
  */
67739
68205
  const ModelManifestEnvelopeSchema = Schema$1.Struct({
67740
68206
  version: Schema$1.Literal(1),
67741
- /**
67742
- * ISO date of the last edit. A release bundles its manifest, and a disk
67743
- * cache of an older edit must not outrank it. Optional so older remote
67744
- * files still decode; they count as older than any dated bundle.
67745
- */
68207
+ /** ISO date of the last edit. */
67746
68208
  updatedAt: Schema$1.optional(Schema$1.String),
67747
68209
  currentModels: Schema$1.Record(Schema$1.String, Schema$1.Array(Schema$1.String)),
67748
68210
  providers: Schema$1.optional(Schema$1.Record(Schema$1.String, ManifestProviderCatalog))
@@ -67756,13 +68218,22 @@ const hasValidProviderCatalogReferences = (manifest) => Object.values(manifest.p
67756
68218
  }) && (catalog.defaults?.chat === void 0 || slugs.has(catalog.defaults.chat));
67757
68219
  });
67758
68220
  const ModelManifestSchema = ModelManifestEnvelopeSchema.pipe(Schema$1.check(Schema$1.makeFilter(hasValidProviderCatalogReferences, { expected: "unique model slugs and existing model and profile references" }), Schema$1.makeFilter(hasValidClaudeManifestAdapters, { expected: "valid Claude adapter metadata" })));
67759
- const decodeManifest = Schema$1.decodeUnknownEffect(ModelManifestSchema);
68221
+ const decodeManifestOption = Schema$1.decodeUnknownOption(ModelManifestSchema);
68222
+ /** Public sources after the user's CPA, in order. */
68223
+ const PUBLIC_SOURCES = [{
68224
+ url: MODELS_DEV_URL,
68225
+ parse: parseModelsDev
68226
+ }, {
68227
+ url: OPENROUTER_MODELS_URL,
68228
+ parse: parseOpenRouter
68229
+ }];
67760
68230
  const BUNDLED_MODEL_MANIFEST = Schema$1.decodeUnknownSync(ModelManifestSchema)(model_manifest_default);
67761
- /** Epoch millis of the manifest's `updatedAt`, or 0 when absent or unparsable. */
67762
- function manifestUpdatedAtMs(manifest) {
67763
- if (manifest.updatedAt === void 0) return 0;
67764
- const parsed = Date.parse(manifest.updatedAt);
67765
- return Number.isNaN(parsed) ? 0 : parsed;
68231
+ /**
68232
+ * The bundle folded with an external list. A derived manifest that fails the
68233
+ * catalog checks is dropped in favour of the bundle alone.
68234
+ */
68235
+ function manifestFromExternalModels(external) {
68236
+ return Option.getOrNull(decodeManifestOption(deriveModelManifest(BUNDLED_MODEL_MANIFEST, external)));
67766
68237
  }
67767
68238
  /** Resolve provider-neutral model presentation and capability data. */
67768
68239
  function resolveProviderCatalog(manifest, driverKind) {
@@ -67798,13 +68269,12 @@ function resolveProviderCatalog(manifest, driverKind) {
67798
68269
  defaults: { chat: catalog.defaults?.chat }
67799
68270
  };
67800
68271
  }
67801
- /** On-disk shape of the last successfully fetched manifest. */
68272
+ /** On-disk shape of the last successfully fetched external model list. */
67802
68273
  const ManifestCacheFile = Schema$1.Struct({
67803
68274
  fetchedAtMs: Schema$1.Number,
67804
- manifest: ModelManifestSchema
68275
+ models: Schema$1.Array(ExternalModel)
67805
68276
  });
67806
68277
  const decodeManifestCache = Schema$1.decodeUnknownEffect(Schema$1.fromJsonString(ManifestCacheFile));
67807
- /** Exported for tests that seed the disk cache. */
67808
68278
  const encodeManifestCache = Schema$1.encodeEffect(Schema$1.fromJsonString(ManifestCacheFile));
67809
68279
  /** True when the manifest classifies `slug` as legacy for `driverKind`. */
67810
68280
  function isLegacyModel(manifest, driverKind, slug) {
@@ -67885,7 +68355,7 @@ const make$76 = Effect.gen(function* () {
67885
68355
  const settingsService = yield* ServerSettingsService;
67886
68356
  const httpClient = yield* HttpClient.HttpClient;
67887
68357
  const serviceScope = yield* Effect.scope;
67888
- const cachePath = path.join(config.stateDir, "model-manifest.json");
68358
+ const cachePath = path.join(config.stateDir, "external-models.json");
67889
68359
  let manifest = BUNDLED_MODEL_MANIFEST;
67890
68360
  let fetchedAtMs = null;
67891
68361
  let lastAttemptMs = null;
@@ -67893,10 +68363,26 @@ const make$76 = Effect.gen(function* () {
67893
68363
  const ensureDiskCacheLoaded = yield* Effect.cached(Effect.gen(function* () {
67894
68364
  const fromDisk = yield* fileSystem.readFileString(cachePath).pipe(Effect.flatMap((raw) => decodeManifestCache(raw)), Effect.catchCause(() => Effect.succeed(null)));
67895
68365
  if (fromDisk === null) return;
67896
- if (manifestUpdatedAtMs(BUNDLED_MODEL_MANIFEST) > manifestUpdatedAtMs(fromDisk.manifest)) return;
67897
- manifest = fromDisk.manifest;
68366
+ const derived = manifestFromExternalModels(fromDisk.models);
68367
+ if (derived === null) return;
68368
+ manifest = derived;
67898
68369
  fetchedAtMs = fromDisk.fetchedAtMs;
67899
68370
  }));
68371
+ const fetchPublic = (source) => httpClient.get(source.url).pipe(Effect.flatMap(HttpClientResponse.filterStatusOk), Effect.flatMap((response) => response.json), Effect.map((json) => source.parse(json) ?? []), Effect.timeout(FETCH_TIMEOUT_MS), Effect.catchCause(() => Effect.succeed([])));
68372
+ /** Static model definitions from the first enabled CPA usage source; credentials do not matter. */
68373
+ const fetchCpa = (sources) => {
68374
+ const source = Object.values(sources).find((candidate) => candidate.enabled && candidate.managementKey !== "");
68375
+ if (!source) return Effect.succeed([]);
68376
+ return Effect.forEach(CPA_CHANNELS, ({ provider, channel }) => Effect.tryPromise((signal) => readManagementJson(source, `model-definitions/${channel}`, void 0, signal)).pipe(Effect.map((json) => parseCpaModelDefinitions(json, provider)), Effect.timeout(FETCH_TIMEOUT_MS), Effect.catchCause(() => Effect.succeed([]))), { concurrency: "unbounded" }).pipe(Effect.map((lists) => {
68377
+ const seen = /* @__PURE__ */ new Set();
68378
+ return lists.flat().filter((model) => {
68379
+ const key = `${model.provider}:${model.id}`;
68380
+ if (seen.has(key)) return false;
68381
+ seen.add(key);
68382
+ return true;
68383
+ });
68384
+ }));
68385
+ };
67900
68386
  const refresh = Effect.fn("ModelManifest.refresh")(function* () {
67901
68387
  yield* ensureDiskCacheLoaded;
67902
68388
  const now = yield* Clock.currentTimeMillis;
@@ -67906,13 +68392,18 @@ const make$76 = Effect.gen(function* () {
67906
68392
  const settings = yield* settingsService.getSettings.pipe(Effect.catchCause(() => Effect.succeed(null)));
67907
68393
  if (settings !== null && !settings.enableProviderUpdateChecks) return manifest;
67908
68394
  lastAttemptMs = now;
67909
- const fetched = yield* httpClient.get(MODEL_MANIFEST_URL).pipe(Effect.flatMap(HttpClientResponse.filterStatusOk), Effect.flatMap((response) => response.json), Effect.flatMap((json) => decodeManifest(json)), Effect.timeout(FETCH_TIMEOUT_MS), Effect.catchCause(() => Effect.succeed(null)));
68395
+ let models = settings === null ? [] : yield* fetchCpa(settings.usageLimitSources);
68396
+ for (const source of PUBLIC_SOURCES) {
68397
+ if (missingProviders(models).length === 0) break;
68398
+ models = fillMissingProviders(models, yield* fetchPublic(source));
68399
+ }
68400
+ const fetched = models.length === 0 ? null : manifestFromExternalModels(models);
67910
68401
  if (fetched === null) return manifest;
67911
68402
  manifest = fetched;
67912
68403
  fetchedAtMs = now;
67913
68404
  yield* encodeManifestCache({
67914
68405
  fetchedAtMs: now,
67915
- manifest: fetched
68406
+ models
67916
68407
  }).pipe(Effect.flatMap((serialized) => fileSystem.writeFileString(cachePath, serialized)), Effect.catchCause(() => Effect.void));
67917
68408
  return manifest;
67918
68409
  });
@@ -71765,6 +72256,10 @@ const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* (claudeSettin
71765
72256
  rawMethod: "claude/stream_event/message_delta",
71766
72257
  rawPayload: message
71767
72258
  });
72259
+ if (!context.contextBreakdownRequested) {
72260
+ context.contextBreakdownRequested = true;
72261
+ yield* queryCurrentContextUsage(context, context.lastKnownTotalProcessedTokens).pipe(Effect.flatMap((usage) => emitThreadTokenUsage(context, usage, { rawMethod: "claude/get_context_usage" })), Effect.forkChild);
72262
+ }
71768
72263
  return;
71769
72264
  }
71770
72265
  if (event.type === "content_block_delta") {
@@ -73042,6 +73537,7 @@ const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* (claudeSettin
73042
73537
  lastKnownContextWindow: initialContextWindow,
73043
73538
  lastKnownTokenUsage: void 0,
73044
73539
  lastKnownTotalProcessedTokens: void 0,
73540
+ contextBreakdownRequested: false,
73045
73541
  lastAssistantUuid: resumeState?.resumeSessionAt,
73046
73542
  lastThreadStartedId: void 0,
73047
73543
  interruptRequested: false,
@@ -103153,50 +103649,6 @@ const makeProviderInstallation = Effect.fn("makeProviderInstallation")(function*
103153
103649
  };
103154
103650
  });
103155
103651
  //#endregion
103156
- //#region src/usage/usageSourceHttp.ts
103157
- const MAX_RESPONSE_BYTES$1 = 1024 * 1024;
103158
- const LOOPBACK_HOSTS$1 = /* @__PURE__ */ new Set([
103159
- "localhost",
103160
- "127.0.0.1",
103161
- "[::1]"
103162
- ]);
103163
- /** The management key stays on the configured origin, including redirects. */
103164
- async function readManagementJson(config, path, body, signal) {
103165
- const base = new URL(config.url);
103166
- if (base.username || base.password || base.search || base.hash || base.protocol !== "https:" && !(base.protocol === "http:" && LOOPBACK_HOSTS$1.has(base.hostname))) throw new Error("Invalid usage source origin");
103167
- if (path !== "auth-files" && path !== "api-call") throw new Error("Unsupported management operation");
103168
- const response = await fetch(new URL(`/v0/management/${path}`, base), {
103169
- method: body === void 0 ? "GET" : "POST",
103170
- redirect: "error",
103171
- signal,
103172
- headers: {
103173
- authorization: `Bearer ${config.managementKey}`,
103174
- "content-type": "application/json"
103175
- },
103176
- ...body === void 0 ? {} : { body: JSON.stringify(body) }
103177
- });
103178
- if (!response.ok || !response.body) {
103179
- await response.body?.cancel();
103180
- throw new UsageLimitSourceError({ detail: response.status === 401 || response.status === 403 ? `The usage source rejected its management key (HTTP ${response.status}). Update the management key in Manage usage sources; a provider API key cannot replace it.` : `The usage source management request failed (HTTP ${response.status}). Check the source URL and management API availability.` });
103181
- }
103182
- const reader = response.body.getReader();
103183
- const chunks = [];
103184
- let total = 0;
103185
- try {
103186
- for (;;) {
103187
- const chunk = await reader.read();
103188
- if (chunk.done) break;
103189
- total += chunk.value.byteLength;
103190
- if (total > MAX_RESPONSE_BYTES$1) throw new Error("Usage source response too large");
103191
- chunks.push(chunk.value);
103192
- }
103193
- return JSON.parse(Buffer.concat(chunks).toString("utf8"));
103194
- } finally {
103195
- await reader.cancel().catch(() => void 0);
103196
- reader.releaseLock();
103197
- }
103198
- }
103199
- //#endregion
103200
103652
  //#region src/usage/cliproxyApi.ts
103201
103653
  const isUsageLimitSourceError$1 = Schema$1.is(UsageLimitSourceError);
103202
103654
  const MAX_SOURCE_ACCOUNTS = 128;