claude-use 11.32.0 → 11.33.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/README.md CHANGED
@@ -125,6 +125,13 @@ agent-shim provider add|set <name> [--catalogue-tier <tier>] [--catalogue-includ
125
125
  agent-shim provider models <name> [--refresh] [--json] # the catalogue last fetched, and which of its models /model lists
126
126
  agent-shim provider list [--json]
127
127
  agent-shim provider show <name> [--json]
128
+ agent-shim model match [--json] # which model names are the same model across providers
129
+ agent-shim model add <name> --mode <mode> --member <ref>... # define a virtual model
130
+ agent-shim model set <name> [--mode <mode>] [--member <ref>]...
131
+ agent-shim model remove <name> [--yes]
132
+ agent-shim model tier <tier> --ref <ref> | --clear
133
+ agent-shim model price <ref> --input <usd> --output <usd> | --clear
134
+ agent-shim model list [--json]
128
135
  agent-shim provider remove <name> [--yes] [--json]
129
136
 
130
137
  agent-shim rule add <path> [--config-profile <profile>] [--identity <identity>] [--json]
package/dist/cli.cjs CHANGED
@@ -26987,7 +26987,7 @@ function isTimeZone(name) {
26987
26987
  return false;
26988
26988
  }
26989
26989
  }
26990
- var import_node_path4, 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, FRONTDOOR_DEFAULT_IDLE_SHUTDOWN_MINUTES, FrontDoorGlobalConfigSchema, UPDATE_MODES, UpdateGlobalConfigSchema, GlobalConfigSchema, IdentitySchema, CategoryClassificationSchema, CategoryClassificationOverlaySchema, SHIPPED_CATEGORY_DEFAULTS;
26990
+ var import_node_path4, 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, 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, FRONTDOOR_DEFAULT_IDLE_SHUTDOWN_MINUTES, FrontDoorGlobalConfigSchema, UPDATE_MODES, UpdateGlobalConfigSchema, GlobalConfigSchema, IdentitySchema, CategoryClassificationSchema, CategoryClassificationOverlaySchema, SHIPPED_CATEGORY_DEFAULTS;
26991
26991
  var init_schema = __esm({
26992
26992
  "src/config/schema.ts"() {
26993
26993
  "use strict";
@@ -27216,13 +27216,54 @@ var init_schema = __esm({
27216
27216
  antigravity: AntigravityProviderConfigSchema.optional()
27217
27217
  });
27218
27218
  ProviderSchema = external_exports.discriminatedUnion("kind", [HttpProviderSchema, CodexProviderSchema, AntigravityProviderSchema]);
27219
+ SELECTION_MODES = ["ordered", "most-allowance", "soonest-reset", "spread", "adaptive", "lowest-latency", "highest-success", "cheapest"];
27220
+ VirtualModelSchema = external_exports.strictObject({
27221
+ /** How the virtual model chooses its member for a request. */
27222
+ mode: external_exports.enum(SELECTION_MODES),
27223
+ /** 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>`). */
27224
+ members: external_exports.array(external_exports.string().min(1)).min(1)
27225
+ });
27226
+ ModelPricesSchema = external_exports.strictObject({
27227
+ input: external_exports.number().positive(),
27228
+ output: external_exports.number().positive()
27229
+ });
27230
+ ModelsConfigSchema = external_exports.strictObject({
27231
+ /** Virtual models, by name. */
27232
+ virtual: external_exports.record(external_exports.string().min(1), VirtualModelSchema).optional(),
27233
+ /** Which model ref each Claude tier (`fable`, `opus`, `sonnet`, `haiku`) points at: a concrete target, an account model, a virtual model, or a family. */
27234
+ tiers: external_exports.strictObject({
27235
+ fable: external_exports.string().min(1).optional(),
27236
+ opus: external_exports.string().min(1).optional(),
27237
+ sonnet: external_exports.string().min(1).optional(),
27238
+ haiku: external_exports.string().min(1).optional()
27239
+ }).optional(),
27240
+ /** Prices per model ref, for the `cheapest` mode. */
27241
+ prices: external_exports.record(external_exports.string().min(1), ModelPricesSchema).optional(),
27242
+ /** Model refs hidden from the picker and from routing, whatever else names them. */
27243
+ hide: external_exports.array(external_exports.string().min(1)).optional(),
27244
+ /** When set, the only model refs visible; everything else is hidden. */
27245
+ show: external_exports.array(external_exports.string().min(1)).optional()
27246
+ });
27247
+ ModelsLayerSchema = external_exports.strictObject({
27248
+ virtual: external_exports.record(external_exports.string().min(1), external_exports.union([VirtualModelSchema, external_exports.null()])).optional(),
27249
+ tiers: external_exports.strictObject({
27250
+ fable: external_exports.union([external_exports.string().min(1), external_exports.null()]).optional(),
27251
+ opus: external_exports.union([external_exports.string().min(1), external_exports.null()]).optional(),
27252
+ sonnet: external_exports.union([external_exports.string().min(1), external_exports.null()]).optional(),
27253
+ haiku: external_exports.union([external_exports.string().min(1), external_exports.null()]).optional()
27254
+ }).optional(),
27255
+ prices: external_exports.record(external_exports.string().min(1), external_exports.union([ModelPricesSchema, external_exports.null()])).optional(),
27256
+ hide: external_exports.array(external_exports.string().min(1)).optional(),
27257
+ show: external_exports.array(external_exports.string().min(1)).optional()
27258
+ });
27219
27259
  ConfigProfileSchema = external_exports.strictObject({
27220
27260
  $schema: external_exports.string().optional(),
27221
27261
  description: external_exports.string().optional(),
27222
27262
  extends: external_exports.array(external_exports.string().min(1)).optional(),
27223
27263
  categories: CategoryMapSchema.optional(),
27224
27264
  entries: EntriesSchema.optional(),
27225
- launch: LaunchSchema.optional()
27265
+ launch: LaunchSchema.optional(),
27266
+ models: ModelsLayerSchema.optional()
27226
27267
  });
27227
27268
  POOL_SELECTOR_PREFIX = "pool:";
27228
27269
  POOL_NAME_PATTERN = "[A-Za-z0-9][A-Za-z0-9._-]*";
@@ -27333,6 +27374,7 @@ var init_schema = __esm({
27333
27374
  frontdoor: FrontDoorGlobalConfigSchema.optional(),
27334
27375
  update: UpdateGlobalConfigSchema.optional(),
27335
27376
  pools: external_exports.record(PoolNameSchema, PoolSchema).optional(),
27377
+ models: ModelsConfigSchema.optional(),
27336
27378
  jobs: external_exports.record(PoolNameSchema, JobSchema).optional()
27337
27379
  });
27338
27380
  IdentitySchema = external_exports.strictObject({
@@ -31833,7 +31875,8 @@ function profileLayers(rootName, load, startId) {
31833
31875
  ...source.profile.categories === void 0 ? {} : { categories: source.profile.categories },
31834
31876
  ...source.profile.entries === void 0 ? {} : { entries: source.profile.entries },
31835
31877
  ...source.entryOrder === void 0 ? {} : { entryOrder: source.entryOrder },
31836
- ...source.profile.launch === void 0 ? {} : { launch: source.profile.launch }
31878
+ ...source.profile.launch === void 0 ? {} : { launch: source.profile.launch },
31879
+ ...source.profile.models === void 0 ? {} : { models: source.profile.models }
31837
31880
  });
31838
31881
  nextId += 1;
31839
31882
  }
@@ -31921,7 +31964,8 @@ function assembleCascade(input2) {
31921
31964
  ...source.config.categories === void 0 ? {} : { categories: source.config.categories },
31922
31965
  ...source.config.entries === void 0 ? {} : { entries: source.config.entries },
31923
31966
  ...source.entryOrder === void 0 ? {} : { entryOrder: source.entryOrder },
31924
- ...source.config.launch === void 0 ? {} : { launch: source.config.launch }
31967
+ ...source.config.launch === void 0 ? {} : { launch: source.config.launch },
31968
+ ...source.config.models === void 0 ? {} : { models: source.config.models }
31925
31969
  });
31926
31970
  nextId += 1;
31927
31971
  }
@@ -32539,6 +32583,7 @@ function flattenLayers(layers, options) {
32539
32583
  const rules = /* @__PURE__ */ new Map();
32540
32584
  const launch = {};
32541
32585
  const diagnostics = [];
32586
+ let models;
32542
32587
  for (const layer of layers) {
32543
32588
  if (layer.categories !== void 0) {
32544
32589
  for (const [name, value2] of Object.entries(layer.categories)) {
@@ -32577,6 +32622,9 @@ function flattenLayers(layers, options) {
32577
32622
  launch.explain = layer.launch.explain;
32578
32623
  }
32579
32624
  }
32625
+ if (layer.models !== void 0) {
32626
+ models = mergeModels(models, layer.models);
32627
+ }
32580
32628
  if (layer.entries === void 0) {
32581
32629
  continue;
32582
32630
  }
@@ -32639,7 +32687,7 @@ function flattenLayers(layers, options) {
32639
32687
  rules.set(pattern, rule);
32640
32688
  }
32641
32689
  }
32642
- return { categories, rules, launch, diagnostics };
32690
+ return { categories, rules, launch, ...models === void 0 ? {} : { models }, diagnostics };
32643
32691
  }
32644
32692
  function orderedKeys(layer) {
32645
32693
  const present = new Set(Object.keys(layer.entries ?? {}));
@@ -32664,6 +32712,67 @@ function matchingRules(flattened, relPath) {
32664
32712
  }
32665
32713
  return matches.sort((a10, b) => compareSpecificity(b, a10));
32666
32714
  }
32715
+ function mergeModels(current, layer) {
32716
+ const pieces = { ...current ?? {} };
32717
+ if (layer.virtual !== void 0) {
32718
+ const virtual = {};
32719
+ for (const [name, definition] of Object.entries(current?.virtual ?? {})) {
32720
+ if (layer.virtual[name] === void 0) {
32721
+ virtual[name] = definition;
32722
+ }
32723
+ }
32724
+ for (const [name, definition] of Object.entries(layer.virtual)) {
32725
+ if (definition !== null) {
32726
+ virtual[name] = definition;
32727
+ }
32728
+ }
32729
+ pieces.virtual = virtual;
32730
+ }
32731
+ if (layer.tiers !== void 0) {
32732
+ const tiers = {};
32733
+ for (const tier of ["fable", "opus", "sonnet", "haiku"]) {
32734
+ const ref = layer.tiers[tier];
32735
+ const inherited = current?.tiers?.[tier];
32736
+ if (ref !== null && ref !== void 0) {
32737
+ tiers[tier] = ref;
32738
+ } else if (ref === void 0 && inherited !== void 0) {
32739
+ tiers[tier] = inherited;
32740
+ }
32741
+ }
32742
+ pieces.tiers = tiers;
32743
+ }
32744
+ if (layer.prices !== void 0) {
32745
+ const priced = {};
32746
+ for (const [ref, prices] of Object.entries(current?.prices ?? {})) {
32747
+ if (layer.prices[ref] === void 0) {
32748
+ priced[ref] = prices;
32749
+ }
32750
+ }
32751
+ for (const [ref, prices] of Object.entries(layer.prices)) {
32752
+ if (prices !== null) {
32753
+ priced[ref] = prices;
32754
+ }
32755
+ }
32756
+ pieces.prices = priced;
32757
+ }
32758
+ if (layer.hide !== void 0) {
32759
+ pieces.hide = layer.hide;
32760
+ }
32761
+ if (layer.show !== void 0) {
32762
+ pieces.show = layer.show;
32763
+ }
32764
+ const merged = modelsOf(pieces);
32765
+ return Object.keys(merged).length === 0 ? void 0 : merged;
32766
+ }
32767
+ function modelsOf(pieces) {
32768
+ return {
32769
+ ...pieces.virtual === void 0 || Object.keys(pieces.virtual).length === 0 ? {} : { virtual: pieces.virtual },
32770
+ ...pieces.tiers === void 0 || Object.keys(pieces.tiers).length === 0 ? {} : { tiers: pieces.tiers },
32771
+ ...pieces.prices === void 0 || Object.keys(pieces.prices).length === 0 ? {} : { prices: pieces.prices },
32772
+ ...pieces.hide === void 0 || pieces.hide.length === 0 ? {} : { hide: [...pieces.hide] },
32773
+ ...pieces.show === void 0 || pieces.show.length === 0 ? {} : { show: [...pieces.show] }
32774
+ };
32775
+ }
32667
32776
  var init_flatten = __esm({
32668
32777
  "src/resolve/flatten.ts"() {
32669
32778
  "use strict";
@@ -34323,7 +34432,7 @@ var init_package = __esm({
34323
34432
  "package.json"() {
34324
34433
  package_default = {
34325
34434
  name: "claude-use",
34326
- version: "11.32.0",
34435
+ version: "11.33.0",
34327
34436
  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.",
34328
34437
  license: "MIT",
34329
34438
  author: "Joseph Mearman <joseph@mearman.co.uk>",
@@ -119011,6 +119120,162 @@ init_credentialCache();
119011
119120
  init_realCredentialCache();
119012
119121
  init_source();
119013
119122
 
119123
+ // src/modelsStore.ts
119124
+ init_store();
119125
+ init_schema();
119126
+ function readModels(paths) {
119127
+ return readGlobalConfig(paths)?.models;
119128
+ }
119129
+ function writeModels(paths, models) {
119130
+ const emptied = models !== void 0 && Object.keys(models).length === 0 ? void 0 : models;
119131
+ applyPatch(paths.globalConfigFile, GlobalConfigSchema, { models: emptied }, { defaults: {} });
119132
+ }
119133
+ function stripEmpty(models) {
119134
+ const kept = {};
119135
+ if (models.virtual !== void 0 && Object.keys(models.virtual).length > 0) {
119136
+ kept.virtual = models.virtual;
119137
+ }
119138
+ if (models.tiers !== void 0 && Object.keys(models.tiers).length > 0) {
119139
+ kept.tiers = models.tiers;
119140
+ }
119141
+ if (models.prices !== void 0 && Object.keys(models.prices).length > 0) {
119142
+ kept.prices = models.prices;
119143
+ }
119144
+ if (models.hide !== void 0 && models.hide.length > 0) {
119145
+ kept.hide = models.hide;
119146
+ }
119147
+ if (models.show !== void 0 && models.show.length > 0) {
119148
+ kept.show = models.show;
119149
+ }
119150
+ return kept;
119151
+ }
119152
+ var VirtualModelNotFoundError = class extends Error {
119153
+ constructor(name) {
119154
+ super(`No virtual model named "${name}".`);
119155
+ this.name = "VirtualModelNotFoundError";
119156
+ }
119157
+ };
119158
+ function memberKindOf(ref, models) {
119159
+ if (ref.startsWith("acct:")) {
119160
+ return "account";
119161
+ }
119162
+ if (ref.startsWith("family:")) {
119163
+ return "family";
119164
+ }
119165
+ return models?.virtual !== void 0 && ref in models.virtual ? "virtual" : "provider";
119166
+ }
119167
+ function expandMembers(name, models) {
119168
+ const virtual = models?.virtual ?? {};
119169
+ const expanded = [];
119170
+ const walk2 = (current, chain) => {
119171
+ const nested = virtual[current];
119172
+ if (nested !== void 0) {
119173
+ if (chain.includes(current)) {
119174
+ throw new Error(`virtual model cycle: ${[...chain, current].join(" -> ")}`);
119175
+ }
119176
+ for (const member of nested.members) {
119177
+ walk2(member, [...chain, current]);
119178
+ }
119179
+ return;
119180
+ }
119181
+ if (!expanded.includes(current)) {
119182
+ expanded.push(current);
119183
+ }
119184
+ };
119185
+ const top = virtual[name];
119186
+ if (top === void 0) {
119187
+ throw new VirtualModelNotFoundError(name);
119188
+ }
119189
+ for (const member of top.members) {
119190
+ walk2(member, [name]);
119191
+ }
119192
+ return expanded;
119193
+ }
119194
+ function setVirtualModel(paths, name, model) {
119195
+ const current = readModels(paths) ?? {};
119196
+ writeModels(paths, { ...current, virtual: { ...current.virtual ?? {}, [name]: model } });
119197
+ }
119198
+ function removeVirtualModel(paths, name) {
119199
+ const current = readModels(paths);
119200
+ if (current?.virtual?.[name] === void 0) {
119201
+ throw new VirtualModelNotFoundError(name);
119202
+ }
119203
+ const virtual = Object.fromEntries(Object.entries(current.virtual ?? {}).filter(([existing]) => existing !== name));
119204
+ writeModels(paths, stripEmpty({ ...current, virtual: Object.keys(virtual).length === 0 ? void 0 : virtual }));
119205
+ }
119206
+ function setPrice(paths, ref, prices) {
119207
+ const current = readModels(paths) ?? {};
119208
+ const priced = Object.fromEntries(Object.entries(current.prices ?? {}).filter(([existing]) => existing !== ref));
119209
+ const next = prices === void 0 ? priced : { ...priced, [ref]: prices };
119210
+ writeModels(paths, stripEmpty({ ...current, prices: Object.keys(next).length === 0 ? void 0 : next }));
119211
+ }
119212
+ function setTier(paths, tier, ref) {
119213
+ const current = readModels(paths) ?? {};
119214
+ const tiers = {};
119215
+ const merged = { ...current.tiers ?? {}, [tier]: ref };
119216
+ for (const [key, value2] of Object.entries(merged)) {
119217
+ if (value2 !== void 0) {
119218
+ tiers[key] = value2;
119219
+ }
119220
+ }
119221
+ writeModels(paths, stripEmpty({ ...current, tiers: Object.keys(tiers).length === 0 ? void 0 : tiers }));
119222
+ }
119223
+
119224
+ // src/models/doctor.ts
119225
+ function modelsFindings(input2) {
119226
+ const findings = [];
119227
+ const { models } = input2;
119228
+ const providers = new Set(input2.providers);
119229
+ const identities = new Set(input2.identities);
119230
+ const virtualNames = new Set(Object.keys(models.virtual ?? {}));
119231
+ for (const [name, model] of Object.entries(models.virtual ?? {})) {
119232
+ for (const member of model.members) {
119233
+ const kind = memberKindOf(member, models);
119234
+ if (kind === "provider" && !member.includes("/")) {
119235
+ 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>).` });
119236
+ } else if (kind === "provider" && !providers.has(member.split("/")[0] ?? "")) {
119237
+ findings.push({ severity: "fail", message: `Virtual model "${name}" names member "${member}", but no provider "${member.split("/")[0] ?? ""}" is defined.` });
119238
+ }
119239
+ if (kind === "virtual" && !virtualNames.has(member)) {
119240
+ findings.push({ severity: "fail", message: `Virtual model "${name}" names member "${member}", but no virtual model "${member}" is defined.` });
119241
+ }
119242
+ if (kind === "account") {
119243
+ const identity = member.slice("acct:".length).split("/")[0] ?? "";
119244
+ if (!identities.has(identity)) {
119245
+ findings.push({ severity: "fail", message: `Virtual model "${name}" names member "${member}", but no identity "${identity}" is defined.` });
119246
+ }
119247
+ }
119248
+ }
119249
+ try {
119250
+ expandMembers(name, models);
119251
+ } catch (error63) {
119252
+ findings.push({ severity: "fail", message: `Virtual model "${name}" cannot be expanded: ${error63 instanceof Error ? error63.message : String(error63)}` });
119253
+ }
119254
+ if (model.mode === "cheapest") {
119255
+ const expanded = expandMembers(name, models);
119256
+ const unpriced = expanded.filter((member) => models.prices?.[member] === void 0 && memberKindOf(member, models) !== "virtual");
119257
+ if (unpriced.length > 0) {
119258
+ findings.push({ severity: "warn", message: `Virtual model "${name}" chooses by price, but no prices are set for: ${unpriced.join(", ")}.` });
119259
+ }
119260
+ }
119261
+ }
119262
+ for (const [tier, ref] of Object.entries(models.tiers ?? {})) {
119263
+ const kind = memberKindOf(ref, models);
119264
+ const known = kind === "virtual" ? virtualNames.has(ref) : kind === "provider" ? providers.has(ref.split("/")[0] ?? "") : true;
119265
+ if (!known) {
119266
+ findings.push({ severity: "fail", message: `Tier ${tier} points at "${ref}", which names nothing defined.` });
119267
+ }
119268
+ }
119269
+ const hidden = new Set(models.hide ?? []);
119270
+ const shown = new Set(models.show ?? []);
119271
+ for (const ref of hidden) {
119272
+ if (shown.has(ref)) {
119273
+ findings.push({ severity: "fail", message: `Model ref "${ref}" is both hidden and in the show-only list.` });
119274
+ }
119275
+ }
119276
+ return findings;
119277
+ }
119278
+
119014
119279
  // src/headroom/allowlistDrift.ts
119015
119280
  init_earlierRelease();
119016
119281
  init_state2();
@@ -119197,7 +119462,8 @@ var DOCTOR_SECTIONS = [
119197
119462
  "categories-local",
119198
119463
  "active-identity",
119199
119464
  "headroom",
119200
- "frontdoor"
119465
+ "frontdoor",
119466
+ "models"
119201
119467
  ];
119202
119468
  function refinePathShadow(status, ownExecutablePath, realpath) {
119203
119469
  if (status.status !== "shadowed") {
@@ -119349,6 +119615,9 @@ function pushLegacyName(push, params) {
119349
119615
  );
119350
119616
  }
119351
119617
  }
119618
+ function parsedModelsOf(paths) {
119619
+ return readGlobalConfig(paths)?.models;
119620
+ }
119352
119621
  function runDoctor(params) {
119353
119622
  const findings = [];
119354
119623
  const push = (section, severity, message, subject) => {
@@ -119550,6 +119819,17 @@ function runDoctor(params) {
119550
119819
  }
119551
119820
  }
119552
119821
  }
119822
+ {
119823
+ const models = params.models;
119824
+ if (models === void 0) {
119825
+ push("models", "pass", "No models block configured.");
119826
+ } else {
119827
+ for (const finding of modelsFindings({ models, providers: params.providers.map((entry) => entry.name), identities: params.identities.map((entry) => entry.name) })) {
119828
+ push("models", finding.severity, finding.message);
119829
+ }
119830
+ push("models", "pass", "The models block is consistent.");
119831
+ }
119832
+ }
119553
119833
  if (params.categoriesLocal.raw === void 0) {
119554
119834
  push("categories-local", "pass", "No categories.local.json configured.");
119555
119835
  } else {
@@ -119644,6 +119924,7 @@ function runDoctor(params) {
119644
119924
  return { findings, ok: !findings.some((finding) => finding.severity === "fail") };
119645
119925
  }
119646
119926
  var SECTION_TITLES = {
119927
+ models: "Virtual models",
119647
119928
  "ambient-credential": "Ambient-credential exposure",
119648
119929
  "binary-discovery": "Claude Code binary discovery",
119649
119930
  "claude-shim": "`claude` command shim",
@@ -119776,6 +120057,7 @@ function collectDoctorReport(params) {
119776
120057
  providers,
119777
120058
  directoryRules: { path: paths.directoryRulesFile, raw: realFsPort.readFileUtf8(paths.directoryRulesFile) },
119778
120059
  globalConfig: { path: paths.globalConfigFile, raw: realFsPort.readFileUtf8(paths.globalConfigFile) },
120060
+ models: parsedModelsOf(paths),
119779
120061
  categoriesLocal: { path: paths.categoriesLocalFile, raw: realFsPort.readFileUtf8(paths.categoriesLocalFile) },
119780
120062
  activeIdentity: { path: paths.activeIdentityFile, raw: realFsPort.readFileUtf8(paths.activeIdentityFile) },
119781
120063
  binaryDiscovery,
@@ -146828,6 +147110,8 @@ init_providersStore();
146828
147110
  init_modelPicker();
146829
147111
  init_providerCatalogue();
146830
147112
  init_realPorts();
147113
+ init_schema();
147114
+ init_cliError();
146831
147115
 
146832
147116
  // src/models/identity.ts
146833
147117
  var NON_IDENTITY_BRACKET_RE = /\[[^\]]*\]/g;
@@ -146978,8 +147262,8 @@ function matchModels(models) {
146978
147262
  }
146979
147263
  function registerModelsCommand(program2, deps) {
146980
147264
  const { paths } = deps;
146981
- const modelsCommand = program2.command("model").description("Inspect the models the defined providers offer: which names are the same model across providers, and which only look alike.");
146982
- withExamples(modelsCommand, ["agent-shim model match"]);
147265
+ const modelsCommand = program2.command("model").description("Define and inspect the models the providers offer and the virtual models chosen among them.");
147266
+ withExamples(modelsCommand, ["agent-shim model match", "agent-shim model add fast --mode soonest-reset --member z/glm-5.3 --member m/MiniMax-M3"]);
146983
147267
  withExamples(
146984
147268
  modelsCommand.command("match").description("List every model every provider offers, grouped into the ones that are the same model across providers, with the pairs that are merely similar named beside them.").option("--json", "Print the match as JSON."),
146985
147269
  ["agent-shim model match"]
@@ -147002,6 +147286,114 @@ function registerModelsCommand(program2, deps) {
147002
147286
  console.log(` similar: ${left.provider}/${left.model} ~ ${right.provider}/${right.model}`);
147003
147287
  }
147004
147288
  });
147289
+ withExamples(
147290
+ modelsCommand.command("add <name>").description("Define a virtual model: a named choice over members, taken by its mode.").requiredOption("--mode <mode>", "How the virtual model chooses its member.", (value2) => asMode(value2)).option("--member <ref>", "One member it chooses among: a provider model (z/glm-5.3), an account model (acct:work/opus), another virtual model, or a family (family:<canonical>). Repeatable; order is the preference where the mode reads it.", collectRepeated),
147291
+ ["agent-shim model add fast --mode soonest-reset --member z/glm-5.3 --member m/MiniMax-M3"]
147292
+ ).action((name, options) => {
147293
+ if (options.member === void 0 || options.member.length === 0) {
147294
+ throw new UsageError("A virtual model needs at least one member: pass --member <ref> (repeatable).");
147295
+ }
147296
+ setVirtualModel(paths, name, { mode: options.mode, members: options.member });
147297
+ console.log(`Defined virtual model "${name}" (${options.mode}): ${options.member.join(", ")}`);
147298
+ });
147299
+ withExamples(
147300
+ modelsCommand.command("set <name>").description("Change a virtual model's mode or replace its member list; members not named are kept.").option("--mode <mode>", "The new mode.", (value2) => asMode(value2)).option("--member <ref>", "Add or replace one member (repeatable).", collectRepeated),
147301
+ ["agent-shim model set fast --mode most-allowance"]
147302
+ ).action((name, options) => {
147303
+ const existing = readModels(paths)?.virtual?.[name];
147304
+ if (existing === void 0) {
147305
+ throw new VirtualModelNotFoundError(name);
147306
+ }
147307
+ if (options.mode === void 0 && options.member === void 0) {
147308
+ throw new UsageError("Nothing to change: pass --mode or --member.");
147309
+ }
147310
+ const members2 = options.member === void 0 ? existing.members : [.../* @__PURE__ */ new Set([...existing.members, ...options.member])];
147311
+ setVirtualModel(paths, name, { mode: options.mode ?? existing.mode, members: members2 });
147312
+ console.log(`Updated virtual model "${name}" (${options.mode ?? existing.mode}): ${members2.join(", ")}`);
147313
+ });
147314
+ withExamples(
147315
+ modelsCommand.command("remove <name>").description("Remove a virtual model definition.").option("--yes", "Remove without asking for confirmation.").option("--json", "Print the result as JSON."),
147316
+ ["agent-shim model remove fast --yes"]
147317
+ ).action(async (name, options) => {
147318
+ await confirmRemoval(deps, options.yes, `virtual model "${name}"`);
147319
+ removeVirtualModel(paths, name);
147320
+ reportMutation(options.json, { action: "removed", kind: "model", name, value: void 0 }, () => {
147321
+ console.log(`Removed virtual model "${name}".`);
147322
+ });
147323
+ });
147324
+ withExamples(
147325
+ modelsCommand.command("tier <tier>").description("Point one Claude tier (fable, opus, sonnet, haiku) at a model ref: a concrete target, an account model, a virtual model, or a family.").option("--ref <ref>", "The model ref the tier points at. Omit with --clear to reset the tier to its per-provider behaviour.").option("--clear", "Reset the tier to its per-provider behaviour."),
147326
+ ["agent-shim model tier opus --ref fast", "agent-shim model tier opus --clear"]
147327
+ ).action((tier, options) => {
147328
+ const which = asTier(tier);
147329
+ if (options.clear === true) {
147330
+ setTier(paths, which, void 0);
147331
+ console.log(`Tier ${which} reset to its per-provider behaviour.`);
147332
+ return;
147333
+ }
147334
+ if (options.ref === void 0) {
147335
+ throw new UsageError("Pass --ref <ref>, or --clear to reset the tier.");
147336
+ }
147337
+ setTier(paths, which, options.ref);
147338
+ console.log(`Tier ${which} points at ${options.ref}.`);
147339
+ });
147340
+ withExamples(
147341
+ modelsCommand.command("price <ref>").description("Set a model ref's prices, in USD per million tokens, for the cheapest mode.").option("--input <usd>", "The input price per million tokens.", (value2) => asPrice(value2)).option("--output <usd>", "The output price per million tokens.", (value2) => asPrice(value2)).option("--clear", "Remove the ref's prices instead."),
147342
+ ["agent-shim model price z/glm-5.3 --input 0.6 --output 2.2"]
147343
+ ).action((ref, options) => {
147344
+ if (options.clear !== true && (options.input === void 0 || options.output === void 0)) {
147345
+ throw new UsageError("Pass both --input and --output, or --clear to remove the ref's prices.");
147346
+ }
147347
+ setPrice(paths, ref, options.clear === true ? void 0 : { input: options.input ?? 0, output: options.output ?? 0 });
147348
+ console.log(options.clear === true ? `Removed prices for ${ref}.` : `Prices for ${ref}: ${String(options.input)} in, ${String(options.output)} out per million tokens.`);
147349
+ });
147350
+ withExamples(
147351
+ modelsCommand.command("list").description("List the defined virtual models, the tier pointers and the visibility filter.").option("--json", "Print the models block as JSON."),
147352
+ ["agent-shim model list"]
147353
+ ).action((options) => {
147354
+ const found = readModels(paths);
147355
+ if (options.json === true) {
147356
+ printJson(found ?? null);
147357
+ return;
147358
+ }
147359
+ if (found === void 0) {
147360
+ console.log("No models block is configured.");
147361
+ return;
147362
+ }
147363
+ for (const [name, model] of Object.entries(found.virtual ?? {})) {
147364
+ console.log(`${name} (${model.mode}): ${model.members.join(", ")}`);
147365
+ }
147366
+ for (const [tier, ref] of Object.entries(found.tiers ?? {})) {
147367
+ console.log(`tier ${tier} -> ${ref}`);
147368
+ }
147369
+ if (found.hide !== void 0) {
147370
+ console.log(`hidden: ${found.hide.join(", ")}`);
147371
+ }
147372
+ if (found.show !== void 0) {
147373
+ console.log(`visible only: ${found.show.join(", ")}`);
147374
+ }
147375
+ });
147376
+ }
147377
+ function asMode(value2) {
147378
+ const mode = SELECTION_MODES.find((candidate) => candidate === value2);
147379
+ if (mode === void 0) {
147380
+ throw new UsageError(`Unknown mode "${value2}". Known modes: ${SELECTION_MODES.join(", ")}.`);
147381
+ }
147382
+ return mode;
147383
+ }
147384
+ function asPrice(value2) {
147385
+ const parsed = Number(value2);
147386
+ if (!Number.isFinite(parsed) || parsed <= 0) {
147387
+ throw new UsageError(`"${value2}" is not a price: pass a positive number of US dollars.`);
147388
+ }
147389
+ return parsed;
147390
+ }
147391
+ function asTier(value2) {
147392
+ const tier = CLAUDE_TIERS.find((candidate) => candidate === value2);
147393
+ if (tier === void 0) {
147394
+ throw new UsageError(`Unknown tier "${value2}". Known tiers: ${CLAUDE_TIERS.join(", ")}.`);
147395
+ }
147396
+ return tier;
147005
147397
  }
147006
147398
 
147007
147399
  // src/directoryRules.ts