claude-use 11.32.0 → 11.34.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.
package/dist/index.mjs CHANGED
@@ -212,6 +212,9 @@ function isHttpProvider(provider) {
212
212
  function isCodexProvider(provider) {
213
213
  return provider.kind === "codex";
214
214
  }
215
+ function isRouterProvider(provider) {
216
+ return provider.kind === "router";
217
+ }
215
218
  function isAntigravityProvider(provider) {
216
219
  return provider.kind === "antigravity";
217
220
  }
@@ -226,7 +229,7 @@ function isTimeZone(name) {
226
229
  return false;
227
230
  }
228
231
  }
229
- var CATEGORY_NAMES, OVERRIDABLE_CATEGORIES, CategoryMapSchema, DURATION_RE, DurationSchema, WhenConditionObjectSchema, WhenSchema, EntryValueSchema, ENTRY_KEY_RE, EntriesSchema, LaunchSchema, CREDENTIAL_TARGETS, PROVIDER_CREDENTIAL_TARGETS, CREDENTIAL_TARGET_VARS, ArgvSchema, CommandSourceOptions, EnvCredentialSourceSchema, FileCredentialSourceSchema, CommandCredentialSourceSchema, OpCredentialSourceSchema, KeychainCredentialSourceSchema, LiteralCredentialSourceSchema, CredentialSourceSchema, RemoteCredentialSourceSchema, CREDENTIAL_CACHE_STORES, CredentialCacheSchema, CredentialSchema, ProviderCredentialSchema, RESERVED_PROVIDER_ENV_KEYS, ProviderEnvSchema, PROVIDER_KINDS, ANTIGRAVITY_MODES, CLAUDE_TIERS, ANTIGRAVITY_EFFORTS, ANTIGRAVITY_DEFAULT_MODE, ANTIGRAVITY_DEFAULT_MODEL, ANTIGRAVITY_DEFAULT_TIER_MODELS, ANTIGRAVITY_DEFAULT_EFFORT, AntigravityModelTargetSchema, AntigravityProviderConfigSchema, CODEX_EFFORTS, CODEX_LOGINS, CODEX_DEFAULT_LOGIN, CODEX_DEFAULT_MODEL, CODEX_DEFAULT_TIER_MODELS, CODEX_DEFAULT_EFFORT, CodexProviderConfigSchema, ProviderRouteSchema, PickerModelSchema, PickerModelsSchema, ProviderCatalogueSchema, HttpProviderSchema, CodexProviderSchema, AntigravityProviderSchema, ProviderSchema, ConfigProfileSchema, POOL_SELECTOR_PREFIX, POOL_NAME_PATTERN, IDENTITY_NAME_PATTERN, PoolNameSchema, SELECTOR_RE, IdentitySelectorSchema, PoolMemberSchema, POOL_PREFERENCES, PoolPreferenceSchema, WHOLE_PERCENT_MAX, CompactAtUtilisationSchema, PoolSchema, WEEKDAYS, AnchorSchema, JOB_WINDOWS, JobTriggerSchema, JobTargetSchema, JobSchema, DirectoryRuleSchema, DirectoryRulesSchema, PortableConfigSchema, HEADROOM_MODES, HEADROOM_CCR_MODES, HEADROOM_ROLLOUT_CHANNELS, INTERCEPT_CHANNELS, READ_MATURATION_CHANNELS, HeadroomGlobalConfigObjectSchema, HeadroomGlobalConfigSchema, HEADROOM_DEFAULT_SOURCE, HEADROOM_DEFAULT_IDLE_SHUTDOWN_MINUTES, FrontDoorGlobalConfigSchema, UPDATE_MODES, UpdateGlobalConfigSchema, GlobalConfigSchema, IdentitySchema, CategoryClassificationSchema, CategoryClassificationOverlaySchema, SHIPPED_CATEGORY_DEFAULTS;
232
+ var CATEGORY_NAMES, OVERRIDABLE_CATEGORIES, CategoryMapSchema, DURATION_RE, DurationSchema, WhenConditionObjectSchema, WhenSchema, EntryValueSchema, ENTRY_KEY_RE, EntriesSchema, LaunchSchema, CREDENTIAL_TARGETS, PROVIDER_CREDENTIAL_TARGETS, CREDENTIAL_TARGET_VARS, ArgvSchema, CommandSourceOptions, EnvCredentialSourceSchema, FileCredentialSourceSchema, CommandCredentialSourceSchema, OpCredentialSourceSchema, KeychainCredentialSourceSchema, LiteralCredentialSourceSchema, CredentialSourceSchema, RemoteCredentialSourceSchema, CREDENTIAL_CACHE_STORES, CredentialCacheSchema, CredentialSchema, ProviderCredentialSchema, RESERVED_PROVIDER_ENV_KEYS, ProviderEnvSchema, PROVIDER_KINDS, ANTIGRAVITY_MODES, CLAUDE_TIERS, ANTIGRAVITY_EFFORTS, ANTIGRAVITY_DEFAULT_MODE, ANTIGRAVITY_DEFAULT_MODEL, ANTIGRAVITY_DEFAULT_TIER_MODELS, ANTIGRAVITY_DEFAULT_EFFORT, AntigravityModelTargetSchema, AntigravityProviderConfigSchema, CODEX_EFFORTS, CODEX_LOGINS, CODEX_DEFAULT_LOGIN, CODEX_DEFAULT_MODEL, CODEX_DEFAULT_TIER_MODELS, CODEX_DEFAULT_EFFORT, CodexProviderConfigSchema, ProviderRouteSchema, PickerModelSchema, PickerModelsSchema, ProviderCatalogueSchema, HttpProviderSchema, CodexProviderSchema, RouterProviderSchema, AntigravityProviderSchema, ProviderSchema, SELECTION_MODES, VirtualModelSchema, ModelPricesSchema, ModelsConfigSchema, ModelsLayerSchema, ConfigProfileSchema, POOL_SELECTOR_PREFIX, POOL_NAME_PATTERN, IDENTITY_NAME_PATTERN, PoolNameSchema, SELECTOR_RE, IdentitySelectorSchema, PoolMemberSchema, POOL_PREFERENCES, PoolPreferenceSchema, WHOLE_PERCENT_MAX, CompactAtUtilisationSchema, PoolSchema, WEEKDAYS, AnchorSchema, JOB_WINDOWS, JobTriggerSchema, JobTargetSchema, JobSchema, DirectoryRuleSchema, DirectoryRulesSchema, PortableConfigSchema, HEADROOM_MODES, HEADROOM_CCR_MODES, HEADROOM_ROLLOUT_CHANNELS, INTERCEPT_CHANNELS, READ_MATURATION_CHANNELS, HeadroomGlobalConfigObjectSchema, HeadroomGlobalConfigSchema, HEADROOM_DEFAULT_SOURCE, HEADROOM_DEFAULT_IDLE_SHUTDOWN_MINUTES, FrontDoorGlobalConfigSchema, UPDATE_MODES, UpdateGlobalConfigSchema, GlobalConfigSchema, IdentitySchema, CategoryClassificationSchema, CategoryClassificationOverlaySchema, SHIPPED_CATEGORY_DEFAULTS;
230
233
  var init_schema = __esm({
231
234
  "src/config/schema.ts"() {
232
235
  "use strict";
@@ -268,6 +271,8 @@ var init_schema = __esm({
268
271
  delay: z.string().regex(DURATION_RE, "must be a duration such as 5s or 500ms").optional(),
269
272
  /** Whether the launcher waits for a key press after its own output before it starts claude. Takes the place of `delay` when both are set. Applies only when a terminal is watching. */
270
273
  pause: z.boolean().optional(),
274
+ /** Whether a provider session routes provider-qualified model names (`z/glm-5.3`, `auto`) through the `models` block to the provider that serves them, instead of sending every request to the launch provider. */
275
+ routeByName: z.boolean().optional(),
271
276
  /** Whether the launcher's report, for a launch that picks from a pool, lists every member the pick ranked with why each stands where it does, and not only the member it picked. */
272
277
  explain: z.boolean().optional()
273
278
  });
@@ -328,7 +333,7 @@ var init_schema = __esm({
328
333
  ProviderEnvSchema = z.record(z.string().min(1), z.string()).refine((env) => !Object.keys(env).some((key) => RESERVED_PROVIDER_ENV_KEYS.includes(key)), {
329
334
  message: `env may not set ${RESERVED_PROVIDER_ENV_KEYS.join(", ")}: the credential block's target sets one and agent-shim removes the others`
330
335
  });
331
- PROVIDER_KINDS = ["http", "codex", "antigravity"];
336
+ PROVIDER_KINDS = ["http", "codex", "antigravity", "router"];
332
337
  ANTIGRAVITY_MODES = ["cloud", "local"];
333
338
  CLAUDE_TIERS = ["fable", "opus", "sonnet", "haiku"];
334
339
  ANTIGRAVITY_EFFORTS = ["none", "low", "medium", "high", "xhigh", "max"];
@@ -441,6 +446,11 @@ var init_schema = __esm({
441
446
  pickerModels: PickerModelsSchema.optional(),
442
447
  codex: CodexProviderConfigSchema.optional()
443
448
  });
449
+ RouterProviderSchema = z.strictObject({
450
+ $schema: z.string().optional(),
451
+ kind: z.literal("router"),
452
+ displayName: z.string().min(1)
453
+ });
444
454
  AntigravityProviderSchema = z.strictObject({
445
455
  $schema: z.string().optional(),
446
456
  kind: z.literal("antigravity"),
@@ -451,14 +461,55 @@ var init_schema = __esm({
451
461
  pickerModels: PickerModelsSchema.optional(),
452
462
  antigravity: AntigravityProviderConfigSchema.optional()
453
463
  });
454
- ProviderSchema = z.discriminatedUnion("kind", [HttpProviderSchema, CodexProviderSchema, AntigravityProviderSchema]);
464
+ ProviderSchema = z.discriminatedUnion("kind", [HttpProviderSchema, CodexProviderSchema, AntigravityProviderSchema, RouterProviderSchema]);
465
+ SELECTION_MODES = ["ordered", "most-allowance", "soonest-reset", "spread", "adaptive", "lowest-latency", "highest-success", "cheapest"];
466
+ VirtualModelSchema = z.strictObject({
467
+ /** How the virtual model chooses its member for a request. */
468
+ mode: z.enum(SELECTION_MODES),
469
+ /** The members to choose among, in preference order where the mode reads order. Each names a target (`z/glm-5.3`), an account model (`acct:work/opus`), another virtual model, or a family (`family:<canonical>`). */
470
+ members: z.array(z.string().min(1)).min(1)
471
+ });
472
+ ModelPricesSchema = z.strictObject({
473
+ input: z.number().positive(),
474
+ output: z.number().positive()
475
+ });
476
+ ModelsConfigSchema = z.strictObject({
477
+ /** Virtual models, by name. */
478
+ virtual: z.record(z.string().min(1), VirtualModelSchema).optional(),
479
+ /** Which model ref each Claude tier (`fable`, `opus`, `sonnet`, `haiku`) points at: a concrete target, an account model, a virtual model, or a family. */
480
+ tiers: z.strictObject({
481
+ fable: z.string().min(1).optional(),
482
+ opus: z.string().min(1).optional(),
483
+ sonnet: z.string().min(1).optional(),
484
+ haiku: z.string().min(1).optional()
485
+ }).optional(),
486
+ /** Prices per model ref, for the `cheapest` mode. */
487
+ prices: z.record(z.string().min(1), ModelPricesSchema).optional(),
488
+ /** Model refs hidden from the picker and from routing, whatever else names them. */
489
+ hide: z.array(z.string().min(1)).optional(),
490
+ /** When set, the only model refs visible; everything else is hidden. */
491
+ show: z.array(z.string().min(1)).optional()
492
+ });
493
+ ModelsLayerSchema = z.strictObject({
494
+ virtual: z.record(z.string().min(1), z.union([VirtualModelSchema, z.null()])).optional(),
495
+ tiers: z.strictObject({
496
+ fable: z.union([z.string().min(1), z.null()]).optional(),
497
+ opus: z.union([z.string().min(1), z.null()]).optional(),
498
+ sonnet: z.union([z.string().min(1), z.null()]).optional(),
499
+ haiku: z.union([z.string().min(1), z.null()]).optional()
500
+ }).optional(),
501
+ prices: z.record(z.string().min(1), z.union([ModelPricesSchema, z.null()])).optional(),
502
+ hide: z.array(z.string().min(1)).optional(),
503
+ show: z.array(z.string().min(1)).optional()
504
+ });
455
505
  ConfigProfileSchema = z.strictObject({
456
506
  $schema: z.string().optional(),
457
507
  description: z.string().optional(),
458
508
  extends: z.array(z.string().min(1)).optional(),
459
509
  categories: CategoryMapSchema.optional(),
460
510
  entries: EntriesSchema.optional(),
461
- launch: LaunchSchema.optional()
511
+ launch: LaunchSchema.optional(),
512
+ models: ModelsLayerSchema.optional()
462
513
  });
463
514
  POOL_SELECTOR_PREFIX = "pool:";
464
515
  POOL_NAME_PATTERN = "[A-Za-z0-9][A-Za-z0-9._-]*";
@@ -568,6 +619,7 @@ var init_schema = __esm({
568
619
  frontdoor: FrontDoorGlobalConfigSchema.optional(),
569
620
  update: UpdateGlobalConfigSchema.optional(),
570
621
  pools: z.record(PoolNameSchema, PoolSchema).optional(),
622
+ models: ModelsConfigSchema.optional(),
571
623
  jobs: z.record(PoolNameSchema, JobSchema).optional()
572
624
  });
573
625
  IdentitySchema = z.strictObject({
@@ -1344,6 +1396,7 @@ function flattenLayers(layers, options) {
1344
1396
  const rules = /* @__PURE__ */ new Map();
1345
1397
  const launch = {};
1346
1398
  const diagnostics = [];
1399
+ let models;
1347
1400
  for (const layer of layers) {
1348
1401
  if (layer.categories !== void 0) {
1349
1402
  for (const [name, value] of Object.entries(layer.categories)) {
@@ -1382,6 +1435,9 @@ function flattenLayers(layers, options) {
1382
1435
  launch.explain = layer.launch.explain;
1383
1436
  }
1384
1437
  }
1438
+ if (layer.models !== void 0) {
1439
+ models = mergeModels(models, layer.models);
1440
+ }
1385
1441
  if (layer.entries === void 0) {
1386
1442
  continue;
1387
1443
  }
@@ -1444,7 +1500,7 @@ function flattenLayers(layers, options) {
1444
1500
  rules.set(pattern, rule);
1445
1501
  }
1446
1502
  }
1447
- return { categories, rules, launch, diagnostics };
1503
+ return { categories, rules, launch, ...models === void 0 ? {} : { models }, diagnostics };
1448
1504
  }
1449
1505
  function orderedKeys(layer) {
1450
1506
  const present = new Set(Object.keys(layer.entries ?? {}));
@@ -1469,6 +1525,67 @@ function matchingRules(flattened, relPath) {
1469
1525
  }
1470
1526
  return matches.sort((a, b) => compareSpecificity(b, a));
1471
1527
  }
1528
+ function mergeModels(current, layer) {
1529
+ const pieces = { ...current ?? {} };
1530
+ if (layer.virtual !== void 0) {
1531
+ const virtual = {};
1532
+ for (const [name, definition] of Object.entries(current?.virtual ?? {})) {
1533
+ if (layer.virtual[name] === void 0) {
1534
+ virtual[name] = definition;
1535
+ }
1536
+ }
1537
+ for (const [name, definition] of Object.entries(layer.virtual)) {
1538
+ if (definition !== null) {
1539
+ virtual[name] = definition;
1540
+ }
1541
+ }
1542
+ pieces.virtual = virtual;
1543
+ }
1544
+ if (layer.tiers !== void 0) {
1545
+ const tiers = {};
1546
+ for (const tier of ["fable", "opus", "sonnet", "haiku"]) {
1547
+ const ref = layer.tiers[tier];
1548
+ const inherited = current?.tiers?.[tier];
1549
+ if (ref !== null && ref !== void 0) {
1550
+ tiers[tier] = ref;
1551
+ } else if (ref === void 0 && inherited !== void 0) {
1552
+ tiers[tier] = inherited;
1553
+ }
1554
+ }
1555
+ pieces.tiers = tiers;
1556
+ }
1557
+ if (layer.prices !== void 0) {
1558
+ const priced = {};
1559
+ for (const [ref, prices] of Object.entries(current?.prices ?? {})) {
1560
+ if (layer.prices[ref] === void 0) {
1561
+ priced[ref] = prices;
1562
+ }
1563
+ }
1564
+ for (const [ref, prices] of Object.entries(layer.prices)) {
1565
+ if (prices !== null) {
1566
+ priced[ref] = prices;
1567
+ }
1568
+ }
1569
+ pieces.prices = priced;
1570
+ }
1571
+ if (layer.hide !== void 0) {
1572
+ pieces.hide = layer.hide;
1573
+ }
1574
+ if (layer.show !== void 0) {
1575
+ pieces.show = layer.show;
1576
+ }
1577
+ const merged = modelsOf(pieces);
1578
+ return Object.keys(merged).length === 0 ? void 0 : merged;
1579
+ }
1580
+ function modelsOf(pieces) {
1581
+ return {
1582
+ ...pieces.virtual === void 0 || Object.keys(pieces.virtual).length === 0 ? {} : { virtual: pieces.virtual },
1583
+ ...pieces.tiers === void 0 || Object.keys(pieces.tiers).length === 0 ? {} : { tiers: pieces.tiers },
1584
+ ...pieces.prices === void 0 || Object.keys(pieces.prices).length === 0 ? {} : { prices: pieces.prices },
1585
+ ...pieces.hide === void 0 || pieces.hide.length === 0 ? {} : { hide: [...pieces.hide] },
1586
+ ...pieces.show === void 0 || pieces.show.length === 0 ? {} : { show: [...pieces.show] }
1587
+ };
1588
+ }
1472
1589
  var init_flatten = __esm({
1473
1590
  "src/resolve/flatten.ts"() {
1474
1591
  "use strict";
@@ -1843,7 +1960,8 @@ function profileLayers(rootName, load, startId) {
1843
1960
  ...source.profile.categories === void 0 ? {} : { categories: source.profile.categories },
1844
1961
  ...source.profile.entries === void 0 ? {} : { entries: source.profile.entries },
1845
1962
  ...source.entryOrder === void 0 ? {} : { entryOrder: source.entryOrder },
1846
- ...source.profile.launch === void 0 ? {} : { launch: source.profile.launch }
1963
+ ...source.profile.launch === void 0 ? {} : { launch: source.profile.launch },
1964
+ ...source.profile.models === void 0 ? {} : { models: source.profile.models }
1847
1965
  });
1848
1966
  nextId += 1;
1849
1967
  }
@@ -1932,7 +2050,8 @@ function assembleCascade(input) {
1932
2050
  ...source.config.categories === void 0 ? {} : { categories: source.config.categories },
1933
2051
  ...source.config.entries === void 0 ? {} : { entries: source.config.entries },
1934
2052
  ...source.entryOrder === void 0 ? {} : { entryOrder: source.entryOrder },
1935
- ...source.config.launch === void 0 ? {} : { launch: source.config.launch }
2053
+ ...source.config.launch === void 0 ? {} : { launch: source.config.launch },
2054
+ ...source.config.models === void 0 ? {} : { models: source.config.models }
1936
2055
  });
1937
2056
  nextId += 1;
1938
2057
  }
@@ -2959,7 +3078,7 @@ var init_package = __esm({
2959
3078
  "package.json"() {
2960
3079
  package_default = {
2961
3080
  name: "claude-use",
2962
- version: "11.32.0",
3081
+ version: "11.34.0",
2963
3082
  description: "A profile manager and launcher for Claude Code that lets one person run multiple logins from one machine while controlling what gets shared between them.",
2964
3083
  license: "MIT",
2965
3084
  author: "Joseph Mearman <joseph@mearman.co.uk>",
@@ -6659,7 +6778,7 @@ function describeProviderEndpoint(provider) {
6659
6778
  if (isAntigravityProvider(provider)) {
6660
6779
  return (provider.antigravity?.mode ?? ANTIGRAVITY_DEFAULT_MODE) === "local" ? "antigravity local (ACP daemon)" : "antigravity cloud (Gemini API)";
6661
6780
  }
6662
- return provider.baseUrl;
6781
+ return isRouterProvider(provider) ? "the models block" : provider.baseUrl;
6663
6782
  }
6664
6783
  function isEmptyCodexConfig(config) {
6665
6784
  return config === void 0 || config.defaultModel === void 0 && config.effort === void 0 && config.login === void 0 && (config.models === void 0 || Object.keys(config.models).length === 0);
@@ -6681,10 +6800,16 @@ function addProvider(paths, name, input) {
6681
6800
  if (kind === "antigravity" && input.baseUrl !== void 0) {
6682
6801
  throw new ProviderKindMismatchError("An antigravity provider has no base URL: the launcher points its sessions at the front-door daemon. Drop --base-url.");
6683
6802
  }
6803
+ if (kind === "router" && input.baseUrl !== void 0) {
6804
+ throw new ProviderKindMismatchError("A router provider has no base URL: it resolves each request's model name through the models block. Drop --base-url.");
6805
+ }
6684
6806
  if (kind === "http" && input.baseUrl === void 0) {
6685
6807
  throw new ProviderKindMismatchError("An http provider needs --base-url.");
6686
6808
  }
6687
- if (kind !== "http" && input.catalogue !== void 0) {
6809
+ if (kind === "router" && (input.sources.length > 0 || input.target !== void 0 || input.env !== void 0 || input.pickerModels !== void 0)) {
6810
+ throw new ProviderKindMismatchError("A router provider has no credential, env or picker models of its own: the models block's targets carry their own.");
6811
+ }
6812
+ if (kind !== "http" && kind !== "router" && input.catalogue !== void 0) {
6688
6813
  throw new ProviderKindMismatchError("A model catalogue applies only to an http provider.");
6689
6814
  }
6690
6815
  if (kind !== "codex" && !isEmptyCodexConfig(input.codex)) {
@@ -6700,7 +6825,7 @@ function addProvider(paths, name, input) {
6700
6825
  ...input.pickerModels === void 0 || input.pickerModels.length === 0 ? {} : { pickerModels: input.pickerModels }
6701
6826
  };
6702
6827
  const parsed = ProviderSchema.safeParse(
6703
- kind === "codex" ? { kind, ...shared, ...isEmptyCodexConfig(input.codex) ? {} : { codex: input.codex } } : kind === "antigravity" ? { kind, ...shared, ...isEmptyAntigravityConfig(input.antigravity) ? {} : { antigravity: input.antigravity } } : { ...shared, baseUrl: input.baseUrl, ...input.catalogue === void 0 ? {} : { catalogue: input.catalogue } }
6828
+ kind === "router" ? { kind, displayName: input.displayName } : kind === "codex" ? { kind, ...shared, ...isEmptyCodexConfig(input.codex) ? {} : { codex: input.codex } } : kind === "antigravity" ? { kind, ...shared, ...isEmptyAntigravityConfig(input.antigravity) ? {} : { antigravity: input.antigravity } } : { ...shared, baseUrl: input.baseUrl, ...input.catalogue === void 0 ? {} : { catalogue: input.catalogue } }
6704
6829
  );
6705
6830
  if (!parsed.success) {
6706
6831
  throw new ConfigValidationError(providerJsonPath(paths, name), parsed.error.issues);
@@ -6714,15 +6839,18 @@ function updateProvider(paths, name, input) {
6714
6839
  throw new ProviderNotFoundError(name);
6715
6840
  }
6716
6841
  const unset = new Set(input.unsetEnv);
6717
- const env = Object.fromEntries(Object.entries({ ...existing.env, ...input.env }).filter(([key]) => !unset.has(key)));
6842
+ const existingEnv = isRouterProvider(existing) ? {} : existing.env;
6843
+ const env = Object.fromEntries(Object.entries({ ...existingEnv, ...input.env }).filter(([key]) => !unset.has(key)));
6718
6844
  const unsetPicker = new Set(input.unsetPickerModels);
6719
6845
  const added = new Map((input.pickerModels ?? []).map((entry) => [entry.model, entry]));
6846
+ const existingPicker = isRouterProvider(existing) ? [] : existing.pickerModels;
6720
6847
  const pickerModels = [
6721
- ...(existing.pickerModels ?? []).filter((entry) => !unsetPicker.has(entry.model)).map((entry) => added.get(entry.model) ?? entry),
6722
- ...[...added.values()].filter((entry) => !(existing.pickerModels ?? []).some((kept) => kept.model === entry.model))
6848
+ ...(existingPicker ?? []).filter((entry) => !unsetPicker.has(entry.model)).map((entry) => added.get(entry.model) ?? entry),
6849
+ ...[...added.values()].filter((entry) => !(existingPicker ?? []).some((kept) => kept.model === entry.model))
6723
6850
  ];
6724
- const target = input.target ?? existing.credential.target;
6725
- const cache = input.cache === void 0 ? existing.credential.cache : input.cache === false ? void 0 : input.cache;
6851
+ const router = isRouterProvider(existing);
6852
+ const target = router ? void 0 : input.target ?? existing.credential.target;
6853
+ const cache = router ? void 0 : input.cache === void 0 ? existing.credential.cache : input.cache === false ? void 0 : input.cache;
6726
6854
  if (isCodexProvider(existing) && input.baseUrl !== void 0) {
6727
6855
  throw new ProviderKindMismatchError(`Provider "${name}" is a codex provider, which has no base URL.`);
6728
6856
  }
@@ -6740,10 +6868,12 @@ function updateProvider(paths, name, input) {
6740
6868
  }
6741
6869
  const shared = {
6742
6870
  ...input.displayName === void 0 ? {} : { displayName: input.displayName },
6743
- credential: {
6744
- sources: input.sources ?? existing.credential.sources,
6745
- ...target === void 0 ? {} : { target },
6746
- ...cache === void 0 ? {} : { cache }
6871
+ ...router ? {} : {
6872
+ credential: {
6873
+ sources: input.sources ?? existing.credential.sources,
6874
+ ...target === void 0 ? {} : { target },
6875
+ ...cache === void 0 ? {} : { cache }
6876
+ }
6747
6877
  },
6748
6878
  env: Object.keys(env).length === 0 ? void 0 : env,
6749
6879
  pickerModels: pickerModels.length === 0 ? void 0 : pickerModels
@@ -6763,6 +6893,8 @@ function updateProvider(paths, name, input) {
6763
6893
  ...existing.antigravity?.models === void 0 && input.antigravity?.models === void 0 ? {} : { models: { ...existing.antigravity?.models, ...input.antigravity?.models } }
6764
6894
  };
6765
6895
  candidate = { ...existing, ...shared, antigravity: isEmptyAntigravityConfig(antigravity) ? void 0 : antigravity };
6896
+ } else if (router) {
6897
+ candidate = { kind: "router", ...shared };
6766
6898
  } else {
6767
6899
  const catalogue = input.catalogue === void 0 ? existing.catalogue : input.catalogue === false ? void 0 : input.catalogue;
6768
6900
  candidate = { ...existing, ...shared, catalogue, ...input.baseUrl === void 0 ? {} : { baseUrl: input.baseUrl } };
@@ -6812,6 +6944,9 @@ function resolveProvider(params) {
6812
6944
  return selection;
6813
6945
  }
6814
6946
  const { name, definition } = selection.provider;
6947
+ if (isRouterProvider(definition)) {
6948
+ return { ok: true, provider: { name, definition, credential: { target: "bearer", token: "", source: { env: "AGENT_SHIM_ROUTER" }, warnings: [] } }, warnings: [] };
6949
+ }
6815
6950
  if (params.credentials === void 0) {
6816
6951
  return { ok: false, status: 1, message: `agent-shim: provider ${name} needs its credential resolved, but this launcher has no credential port wired` };
6817
6952
  }
@@ -7788,7 +7923,7 @@ function buildEnv(params) {
7788
7923
  env.CLAUDE_CONFIG_DIR = path36.join(params.identitiesDir, params.resolvedIdentityName);
7789
7924
  }
7790
7925
  if (params.provider !== void 0) {
7791
- const providerEnv = params.provider.definition.env ?? {};
7926
+ const providerEnv = params.provider.definition.kind !== void 0 && params.provider.definition.kind !== "http" ? {} : params.provider.definition.env ?? {};
7792
7927
  providerCustomHeaders = providerEnv.ANTHROPIC_CUSTOM_HEADERS;
7793
7928
  env.ANTHROPIC_BASE_URL = void 0;
7794
7929
  env.AGENT_SHIM_PROVIDER = params.provider.definition.displayName;
@@ -8449,8 +8584,8 @@ function catalogueRows(provider, models) {
8449
8584
  return filterCatalogue(models, provider.catalogue).map((model) => ({ model: model.id, label: labelOf(model.id), description: describedAs(model.id, model.name === void 0 ? "Listed by the provider" : `Listed by the provider as ${model.name}`), behavesAs: TIER_BEHAVES_AS[tier] })).sort((left, right) => left.model.localeCompare(right.model));
8450
8585
  }
8451
8586
  function providerModelRows(provider, catalogue = []) {
8452
- const derived = provider.kind === "codex" ? codexRows(provider) : provider.kind === "antigravity" ? antigravityRows(provider) : httpRows(provider);
8453
- const declared = (provider.pickerModels ?? []).map(declaredRow);
8587
+ const derived = provider.kind === "codex" ? codexRows(provider) : provider.kind === "antigravity" ? antigravityRows(provider) : provider.kind === "router" ? [] : httpRows(provider);
8588
+ const declared = provider.kind === "router" ? [] : (provider.pickerModels ?? []).map(declaredRow);
8454
8589
  const declaredModels = new Set(declared.map((row) => row.model));
8455
8590
  const named = [...derived.filter((row) => !declaredModels.has(row.model)), ...declared];
8456
8591
  const taken = new Set(named.map((row) => labelOf(row.model)));
@@ -9070,7 +9205,7 @@ function resolveLaunch(params) {
9070
9205
  });
9071
9206
  const credentialNames = decided.identityCredentialBlock === void 0 ? [] : Object.keys(credentialVariables(decided.identityCredentialBlock.target ?? "bearer", ""));
9072
9207
  const environment = [.../* @__PURE__ */ new Set([...Object.keys(built).filter((name) => built[name] !== env[name]), ...credentialNames])].sort();
9073
- const credential = decided.provider !== void 0 ? { subject: "provider", name: decided.provider.name, summary: summariseCredential(decided.provider.definition.credential) } : decided.identityCredentialBlock !== void 0 && decided.farmIdentity !== void 0 ? { subject: "identity", name: decided.farmIdentity, summary: summariseCredential(decided.identityCredentialBlock) } : void 0;
9208
+ const credential = decided.provider !== void 0 ? isRouterProvider(decided.provider.definition) ? void 0 : { subject: "provider", name: decided.provider.name, summary: summariseCredential(decided.provider.definition.credential) } : decided.identityCredentialBlock !== void 0 && decided.farmIdentity !== void 0 ? { subject: "identity", name: decided.farmIdentity, summary: summariseCredential(decided.identityCredentialBlock) } : void 0;
9074
9209
  return {
9075
9210
  decision: decided.decision,
9076
9211
  flags: decided.flags,
@@ -13914,11 +14049,12 @@ function cachedState(cacheEnv, owner, block) {
13914
14049
  function buildCredentialReport(provider, identity, identityName, cacheEnv) {
13915
14050
  const identityCredential = identity?.credential === void 0 ? void 0 : summariseCredential(identity.credential);
13916
14051
  const identityCached = identity?.credential === void 0 || identityName === void 0 ? void 0 : cachedState(cacheEnv, `identity ${identityName}`, identity.credential);
13917
- const providerCached = provider?.definition === void 0 ? void 0 : cachedState(cacheEnv, `provider ${provider.name}`, provider.definition.credential);
14052
+ const providerCredential = provider?.definition !== void 0 && !isRouterProvider(provider.definition) ? provider.definition.credential : void 0;
14053
+ const providerCached = providerCredential === void 0 || provider === void 0 ? void 0 : cachedState(cacheEnv, `provider ${provider.name}`, providerCredential);
13918
14054
  return {
13919
14055
  applies: provider !== void 0 ? "provider" : identityCredential !== void 0 ? "identity" : "stored-login",
13920
14056
  ...provider === void 0 ? {} : {
13921
- provider: provider.problem === void 0 ? { name: provider.name, credential: summariseCredential(provider.definition.credential), ...providerCached === void 0 ? {} : { cached: providerCached } } : { name: provider.name, problem: provider.problem }
14057
+ provider: provider.problem === void 0 ? { name: provider.name, ...providerCredential === void 0 ? {} : { credential: summariseCredential(providerCredential), ...providerCached === void 0 ? {} : { cached: providerCached } } } : { name: provider.name, problem: provider.problem }
13922
14058
  },
13923
14059
  ...identityCredential === void 0 ? {} : { identity: identityCredential },
13924
14060
  ...identityCached === void 0 ? {} : { identityCached }
@@ -14013,7 +14149,7 @@ function formatCheckReport(report) {
14013
14149
  const { credential } = report;
14014
14150
  if (credential.provider !== void 0) {
14015
14151
  lines.push(
14016
- ` Provider ${credential.provider.name}: ${credential.provider.credential === void 0 ? `unusable: ${credential.provider.problem}` : formatCredentialSummary(credential.provider.credential)}${credential.provider.cached === void 0 ? "" : `; ${formatCachedCredentialState(credential.provider.cached)}`}`
14152
+ ` Provider ${credential.provider.name}: ${credential.provider.credential === void 0 ? `unusable: ${credential.provider.problem ?? ""}` : formatCredentialSummary(credential.provider.credential)}${credential.provider.cached === void 0 ? "" : `; ${formatCachedCredentialState(credential.provider.cached)}`}`
14017
14153
  );
14018
14154
  }
14019
14155
  if (credential.identity !== void 0) {
@@ -14299,7 +14435,7 @@ var CachedCredentialStateSchema = z17.union([
14299
14435
  z17.strictObject({ store: z17.enum(CREDENTIAL_CACHE_STORES), status: z17.enum(["fresh", "expired"]), ageMs: z17.number(), expiresInMs: z17.number().optional() })
14300
14436
  ]);
14301
14437
  var CheckCredentialProviderSchema = z17.union([
14302
- z17.strictObject({ name: z17.string(), credential: CredentialSummarySchema, cached: CachedCredentialStateSchema.optional() }),
14438
+ z17.strictObject({ name: z17.string(), credential: CredentialSummarySchema.optional(), cached: CachedCredentialStateSchema.optional() }),
14303
14439
  z17.strictObject({ name: z17.string(), problem: z17.string() })
14304
14440
  ]);
14305
14441
  var CheckCredentialSchema = z17.strictObject({
@@ -14672,6 +14808,107 @@ init_credentialCache();
14672
14808
  init_realCredentialCache();
14673
14809
  init_source();
14674
14810
 
14811
+ // src/modelsStore.ts
14812
+ init_store();
14813
+ init_schema();
14814
+ var VirtualModelNotFoundError = class extends Error {
14815
+ constructor(name) {
14816
+ super(`No virtual model named "${name}".`);
14817
+ this.name = "VirtualModelNotFoundError";
14818
+ }
14819
+ };
14820
+ function memberKindOf(ref, models) {
14821
+ if (ref.startsWith("acct:")) {
14822
+ return "account";
14823
+ }
14824
+ if (ref.startsWith("family:")) {
14825
+ return "family";
14826
+ }
14827
+ return models?.virtual !== void 0 && ref in models.virtual ? "virtual" : "provider";
14828
+ }
14829
+ function expandMembers(name, models) {
14830
+ const virtual = models?.virtual ?? {};
14831
+ const expanded = [];
14832
+ const walk = (current, chain) => {
14833
+ const nested = virtual[current];
14834
+ if (nested !== void 0) {
14835
+ if (chain.includes(current)) {
14836
+ throw new Error(`virtual model cycle: ${[...chain, current].join(" -> ")}`);
14837
+ }
14838
+ for (const member of nested.members) {
14839
+ walk(member, [...chain, current]);
14840
+ }
14841
+ return;
14842
+ }
14843
+ if (!expanded.includes(current)) {
14844
+ expanded.push(current);
14845
+ }
14846
+ };
14847
+ const top = virtual[name];
14848
+ if (top === void 0) {
14849
+ throw new VirtualModelNotFoundError(name);
14850
+ }
14851
+ for (const member of top.members) {
14852
+ walk(member, [name]);
14853
+ }
14854
+ return expanded;
14855
+ }
14856
+
14857
+ // src/models/doctor.ts
14858
+ function modelsFindings(input) {
14859
+ const findings = [];
14860
+ const { models } = input;
14861
+ const providers = new Set(input.providers);
14862
+ const identities = new Set(input.identities);
14863
+ const virtualNames = new Set(Object.keys(models.virtual ?? {}));
14864
+ for (const [name, model] of Object.entries(models.virtual ?? {})) {
14865
+ for (const member of model.members) {
14866
+ const kind = memberKindOf(member, models);
14867
+ if (kind === "provider" && !member.includes("/")) {
14868
+ findings.push({ severity: "fail", message: `Virtual model "${name}" names member "${member}", which is neither a defined virtual model nor a provider model (a provider model is <provider>/<model>).` });
14869
+ } else if (kind === "provider" && !providers.has(member.split("/")[0] ?? "")) {
14870
+ findings.push({ severity: "fail", message: `Virtual model "${name}" names member "${member}", but no provider "${member.split("/")[0] ?? ""}" is defined.` });
14871
+ }
14872
+ if (kind === "virtual" && !virtualNames.has(member)) {
14873
+ findings.push({ severity: "fail", message: `Virtual model "${name}" names member "${member}", but no virtual model "${member}" is defined.` });
14874
+ }
14875
+ if (kind === "account") {
14876
+ const identity = member.slice("acct:".length).split("/")[0] ?? "";
14877
+ if (!identities.has(identity)) {
14878
+ findings.push({ severity: "fail", message: `Virtual model "${name}" names member "${member}", but no identity "${identity}" is defined.` });
14879
+ }
14880
+ }
14881
+ }
14882
+ try {
14883
+ expandMembers(name, models);
14884
+ } catch (error) {
14885
+ findings.push({ severity: "fail", message: `Virtual model "${name}" cannot be expanded: ${error instanceof Error ? error.message : String(error)}` });
14886
+ }
14887
+ if (model.mode === "cheapest") {
14888
+ const expanded = expandMembers(name, models);
14889
+ const unpriced = expanded.filter((member) => models.prices?.[member] === void 0 && memberKindOf(member, models) !== "virtual");
14890
+ if (unpriced.length > 0) {
14891
+ findings.push({ severity: "warn", message: `Virtual model "${name}" chooses by price, but no prices are set for: ${unpriced.join(", ")}.` });
14892
+ }
14893
+ }
14894
+ }
14895
+ for (const [tier, ref] of Object.entries(models.tiers ?? {})) {
14896
+ const kind = memberKindOf(ref, models);
14897
+ const known = kind === "virtual" ? virtualNames.has(ref) : kind === "provider" ? providers.has(ref.split("/")[0] ?? "") : true;
14898
+ if (!known) {
14899
+ findings.push({ severity: "fail", message: `Tier ${tier} points at "${ref}", which names nothing defined.` });
14900
+ }
14901
+ }
14902
+ const hidden = new Set(models.hide ?? []);
14903
+ const shown = new Set(models.show ?? []);
14904
+ for (const ref of hidden) {
14905
+ if (shown.has(ref)) {
14906
+ findings.push({ severity: "fail", message: `Model ref "${ref}" is both hidden and in the show-only list.` });
14907
+ }
14908
+ }
14909
+ return findings;
14910
+ }
14911
+
14675
14912
  // src/headroom/allowlistDrift.ts
14676
14913
  init_earlierRelease();
14677
14914
  init_state2();
@@ -14858,7 +15095,8 @@ var DOCTOR_SECTIONS = [
14858
15095
  "categories-local",
14859
15096
  "active-identity",
14860
15097
  "headroom",
14861
- "frontdoor"
15098
+ "frontdoor",
15099
+ "models"
14862
15100
  ];
14863
15101
  function refinePathShadow(status, ownExecutablePath, realpath) {
14864
15102
  if (status.status !== "shadowed") {
@@ -14981,9 +15219,11 @@ function pushProvider(push, entry, cacheEnv, signIn) {
14981
15219
  if (validated3.ignored.length > 0) {
14982
15220
  push("provider", "warn", describeIgnoredKeys(entry.path, validated3.ignored), entry.name);
14983
15221
  }
14984
- const cache = cacheClause(cacheEnv, `provider ${entry.name}`, validated3.data.credential);
14985
- push("provider", cache.unreadable ? "fail" : "pass", `${entry.name} is valid (${describeProviderEndpoint(validated3.data)}, credential ${describeCredential(validated3.data.credential)})${cache.text}.`, entry.name);
14986
- if (validated3.data.env?.ENABLE_TOOL_SEARCH === void 0) {
15222
+ const router = isRouterProvider(validated3.data);
15223
+ const noCache = { unreadable: false, text: "" };
15224
+ const cache = router ? noCache : cacheClause(cacheEnv, `provider ${entry.name}`, validated3.data.credential);
15225
+ push("provider", cache.unreadable ? "fail" : "pass", router ? `${entry.name} is valid (${describeProviderEndpoint(validated3.data)})` : `${entry.name} is valid (${describeProviderEndpoint(validated3.data)}, credential ${describeCredential(validated3.data.credential)})${cache.text}.`, entry.name);
15226
+ if (!router && validated3.data.env?.ENABLE_TOOL_SEARCH === void 0) {
14987
15227
  push("provider", "warn", `${entry.name} does not set ENABLE_TOOL_SEARCH: a session on this provider keeps Claude Code's own API as its base URL, so it defers MCP tool loading and sends tool_reference payloads the upstream backend must understand. Set ENABLE_TOOL_SEARCH=false in the provider's env block to load tools upfront instead, when the backend does not support them.`, entry.name);
14988
15228
  }
14989
15229
  if (signIn !== void 0 && isCodexProvider(validated3.data) && validated3.data.codex?.login === "chatgpt-sign-in") {
@@ -15010,6 +15250,9 @@ function pushLegacyName(push, params) {
15010
15250
  );
15011
15251
  }
15012
15252
  }
15253
+ function parsedModelsOf(paths) {
15254
+ return readGlobalConfig(paths)?.models;
15255
+ }
15013
15256
  function runDoctor(params) {
15014
15257
  const findings = [];
15015
15258
  const push = (section, severity, message, subject) => {
@@ -15211,6 +15454,17 @@ function runDoctor(params) {
15211
15454
  }
15212
15455
  }
15213
15456
  }
15457
+ {
15458
+ const models = params.models;
15459
+ if (models === void 0) {
15460
+ push("models", "pass", "No models block configured.");
15461
+ } else {
15462
+ for (const finding of modelsFindings({ models, providers: params.providers.map((entry) => entry.name), identities: params.identities.map((entry) => entry.name) })) {
15463
+ push("models", finding.severity, finding.message);
15464
+ }
15465
+ push("models", "pass", "The models block is consistent.");
15466
+ }
15467
+ }
15214
15468
  if (params.categoriesLocal.raw === void 0) {
15215
15469
  push("categories-local", "pass", "No categories.local.json configured.");
15216
15470
  } else {
@@ -15305,6 +15559,7 @@ function runDoctor(params) {
15305
15559
  return { findings, ok: !findings.some((finding) => finding.severity === "fail") };
15306
15560
  }
15307
15561
  var SECTION_TITLES = {
15562
+ models: "Virtual models",
15308
15563
  "ambient-credential": "Ambient-credential exposure",
15309
15564
  "binary-discovery": "Claude Code binary discovery",
15310
15565
  "claude-shim": "`claude` command shim",
@@ -15437,6 +15692,7 @@ function collectDoctorReport(params) {
15437
15692
  providers,
15438
15693
  directoryRules: { path: paths.directoryRulesFile, raw: realFsPort.readFileUtf8(paths.directoryRulesFile) },
15439
15694
  globalConfig: { path: paths.globalConfigFile, raw: realFsPort.readFileUtf8(paths.globalConfigFile) },
15695
+ models: parsedModelsOf(paths),
15440
15696
  categoriesLocal: { path: paths.categoriesLocalFile, raw: realFsPort.readFileUtf8(paths.categoriesLocalFile) },
15441
15697
  activeIdentity: { path: paths.activeIdentityFile, raw: realFsPort.readFileUtf8(paths.activeIdentityFile) },
15442
15698
  binaryDiscovery,
@@ -15765,7 +16021,7 @@ var ProviderValueSchema = z24.strictObject({
15765
16021
  displayName: z24.string(),
15766
16022
  baseUrl: z24.string().optional(),
15767
16023
  codex: CodexProviderConfigSchema.optional(),
15768
- credential: CredentialSummarySchema2,
16024
+ credential: CredentialSummarySchema2.optional(),
15769
16025
  env: z24.record(z24.string(), z24.string()).optional()
15770
16026
  });
15771
16027
  var ConfigNameInputSchema = z24.strictObject({ name: z24.string().min(1) });
@@ -15915,9 +16171,8 @@ function providerValue(name, provider) {
15915
16171
  name,
15916
16172
  kind: provider.kind ?? "http",
15917
16173
  displayName: provider.displayName,
15918
- ...isCodexProvider(provider) ? provider.codex === void 0 ? {} : { codex: provider.codex } : isAntigravityProvider(provider) ? provider.antigravity === void 0 ? {} : { antigravity: provider.antigravity } : { baseUrl: provider.baseUrl },
15919
- credential: summariseCredential(provider.credential),
15920
- ...provider.env === void 0 ? {} : { env: provider.env }
16174
+ ...isCodexProvider(provider) ? provider.codex === void 0 ? {} : { codex: provider.codex } : isAntigravityProvider(provider) ? provider.antigravity === void 0 ? {} : { antigravity: provider.antigravity } : isRouterProvider(provider) ? {} : { baseUrl: provider.baseUrl },
16175
+ ...isRouterProvider(provider) ? {} : { credential: summariseCredential(provider.credential), ...provider.env === void 0 ? {} : { env: provider.env } }
15921
16176
  };
15922
16177
  }
15923
16178
  function profileValue(name, paths) {
@@ -16099,7 +16354,7 @@ function createConfigApiRouter(deps) {
16099
16354
  if (input.baseUrl !== void 0 && isHttpProvider(existing) && input.baseUrl !== existing.baseUrl && credential?.sources === void 0) {
16100
16355
  throw new ORPCError2("BAD_REQUEST", { message: `Changing the base URL of provider "${input.name}" requires a credential block in the same call: the stored credential is not sent to a different endpoint.` });
16101
16356
  }
16102
- const cache = credential?.cache === void 0 ? void 0 : mergeCredentialCache(credential.cache === false ? { enabled: false } : { ...credential.cache, enabled: true }, existing.credential.cache);
16357
+ const cache = credential?.cache === void 0 || isRouterProvider(existing) ? void 0 : mergeCredentialCache(credential.cache === false ? { enabled: false } : { ...credential.cache, enabled: true }, isRouterProvider(existing) ? void 0 : existing.credential.cache);
16103
16358
  const updated = updateProvider(paths, input.name, {
16104
16359
  ...input.displayName === void 0 ? {} : { displayName: input.displayName },
16105
16360
  ...input.baseUrl === void 0 ? {} : { baseUrl: input.baseUrl },