@jacobbd/relay-ai 0.7.0 → 0.7.1

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.
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  getTemplateById,
4
4
  init_provider_templates
5
- } from "./chunk-EJONCU3B.js";
5
+ } from "./chunk-HXGZ4CTV.js";
6
6
 
7
7
  // src/constants.ts
8
8
  import { homedir } from "os";
@@ -11,7 +11,7 @@ import { join } from "path";
11
11
  // package.json
12
12
  var package_default = {
13
13
  name: "@jacobbd/relay-ai",
14
- version: "0.7.0",
14
+ version: "0.7.1",
15
15
  publishConfig: {
16
16
  access: "public"
17
17
  },
@@ -157,6 +157,7 @@ var PARENT_SESSION_ENV_VARS = [
157
157
  ];
158
158
  var OPENCODE_CACHE_PATH = join(homedir(), ".cache", "opencode", "models.json");
159
159
  var MAX_MODEL_CATALOG = 20;
160
+ var MIN_CONTEXT_WINDOW = 128e3;
160
161
  var VERTEX_ANTHROPIC_NPM = "@ai-sdk/google-vertex/anthropic";
161
162
  function classifyModelFormat(modelId, providerNpm) {
162
163
  if (providerNpm === "@ai-sdk/anthropic") return "anthropic";
@@ -1472,13 +1473,36 @@ function providerSelectOption(provider) {
1472
1473
  }
1473
1474
  function modelSelectOption(model, hint) {
1474
1475
  const label = formatCodexModelLabel(model);
1475
- const defaultHint = hint ?? (model.name !== model.id ? model.id : model.brand || model.family || "");
1476
+ let defaultHint = hint;
1477
+ if (!defaultHint) {
1478
+ const isCloudflare = model.id.startsWith("@cf/") || model.id.startsWith("@hf/");
1479
+ if (model.isFree) {
1480
+ defaultHint = pc.green(isCloudflare ? "Free (10k/day)" : "Free");
1481
+ } else if (model.cost && (model.cost.input > 0 || model.cost.output > 0)) {
1482
+ const inputStr = `$${model.cost.input}`;
1483
+ const outputStr = `$${model.cost.output}`;
1484
+ defaultHint = pc.dim(isCloudflare ? `Paid plan req (${inputStr}/${outputStr} 1M)` : `${inputStr}/${outputStr} 1M`);
1485
+ } else {
1486
+ defaultHint = model.name !== model.id ? model.id : model.brand || model.family || "";
1487
+ }
1488
+ } else if (hint === "recent") {
1489
+ const isCloudflare = model.id.startsWith("@cf/") || model.id.startsWith("@hf/");
1490
+ const freeLabel = isCloudflare ? "Free (10k/day)" : "Free";
1491
+ const freeSuffix = model.isFree ? " \xB7 " + pc.green(freeLabel) : "";
1492
+ defaultHint = fmtRecentHint() + freeSuffix;
1493
+ }
1494
+ const ctxSuffix = fmtContextWindow(model.contextWindow);
1476
1495
  return {
1477
1496
  value: model.id,
1478
1497
  label: fmtModel(label),
1479
- hint: hint === "recent" ? fmtRecentHint() : defaultHint
1498
+ hint: defaultHint && ctxSuffix ? `${defaultHint} \xB7 ${ctxSuffix}` : defaultHint || ctxSuffix
1480
1499
  };
1481
1500
  }
1501
+ function fmtContextWindow(contextWindow) {
1502
+ if (!contextWindow) return "";
1503
+ const k = contextWindow >= 1e3 ? `${Math.round(contextWindow / 1e3)}k` : String(contextWindow);
1504
+ return pc.dim(`${k} ctx`);
1505
+ }
1482
1506
  function navOption(value, label, hint = "") {
1483
1507
  return { value, label: pc.cyan(label), hint };
1484
1508
  }
@@ -3689,6 +3713,7 @@ function isFreeProviderAccess(providerId, templateId) {
3689
3713
  return FREE_PROVIDER_IDS.has((providerId ?? "").toLowerCase()) || FREE_PROVIDER_IDS.has((templateId ?? "").toLowerCase());
3690
3714
  }
3691
3715
  function classifyFreeStatus(opts) {
3716
+ if (opts.freeAccess === true) return "free_provider";
3692
3717
  if (isFreeProviderAccess(opts.providerId, opts.templateId)) return "free_provider";
3693
3718
  if (isZeroCost(opts.model.cost)) return "verified_free";
3694
3719
  if (isPaidCost(opts.model.cost)) return "paid";
@@ -3850,7 +3875,12 @@ function enrichModelsWithPricing(models, index, platform) {
3850
3875
  return models.map((model) => {
3851
3876
  const cost = lookupModelCost(index, model.id, platform) ?? lookupModelCost(index, model.upstreamModelId, platform);
3852
3877
  if (!cost) return model;
3853
- const freeStatus = classifyFreeStatus({ model: { ...model, cost } });
3878
+ const freeStatus = classifyFreeStatus({
3879
+ model: { ...model, cost },
3880
+ // Keep provider-granted free access (e.g. Cloudflare's daily allowance) when
3881
+ // real pricing resolves later.
3882
+ freeAccess: model.freeStatus === "free_provider"
3883
+ });
3854
3884
  return { ...model, cost, isFree: isFreeStatus(freeStatus), freeStatus };
3855
3885
  });
3856
3886
  }
@@ -7288,16 +7318,707 @@ function localProvidersToServerModels(localProviders) {
7288
7318
  );
7289
7319
  }
7290
7320
 
7321
+ // src/antigravity/slot-registry.ts
7322
+ var AGY_SLOT_VALIDATION_SOURCE = "AGY CLI 1.0.10 / Antigravity IDE 2.1.1 fixture capture 2026-06-23";
7323
+ var AGY_NATIVE_SLOT_REGISTRY = [
7324
+ {
7325
+ slotId: "gemini-3.5-flash-low",
7326
+ model: "MODEL_PLACEHOLDER_M20",
7327
+ role: "agent-switch",
7328
+ status: "validated",
7329
+ validatedWith: AGY_SLOT_VALIDATION_SOURCE
7330
+ },
7331
+ {
7332
+ slotId: "gemini-3.5-flash-extra-low",
7333
+ model: "MODEL_PLACEHOLDER_M187",
7334
+ role: "agent-switch",
7335
+ status: "validated",
7336
+ validatedWith: AGY_SLOT_VALIDATION_SOURCE
7337
+ },
7338
+ {
7339
+ slotId: "gemini-3.1-pro-low",
7340
+ model: "MODEL_PLACEHOLDER_M36",
7341
+ role: "agent-switch",
7342
+ status: "validated",
7343
+ validatedWith: AGY_SLOT_VALIDATION_SOURCE
7344
+ },
7345
+ {
7346
+ slotId: "gemini-pro-agent",
7347
+ model: "MODEL_PLACEHOLDER_M16",
7348
+ role: "agent-switch",
7349
+ status: "validated",
7350
+ validatedWith: AGY_SLOT_VALIDATION_SOURCE
7351
+ },
7352
+ {
7353
+ slotId: "claude-sonnet-4-6",
7354
+ model: "MODEL_PLACEHOLDER_M35",
7355
+ role: "agent-switch",
7356
+ status: "validated",
7357
+ validatedWith: AGY_SLOT_VALIDATION_SOURCE
7358
+ },
7359
+ {
7360
+ slotId: "claude-opus-4-6-thinking",
7361
+ model: "MODEL_PLACEHOLDER_M26",
7362
+ role: "agent-switch",
7363
+ status: "validated",
7364
+ validatedWith: AGY_SLOT_VALIDATION_SOURCE
7365
+ },
7366
+ {
7367
+ slotId: "gpt-oss-120b-medium",
7368
+ model: "MODEL_OPENAI_GPT_OSS_120B_MEDIUM",
7369
+ role: "agent-switch",
7370
+ status: "validated",
7371
+ validatedWith: AGY_SLOT_VALIDATION_SOURCE
7372
+ },
7373
+ {
7374
+ slotId: "gemini-3-flash-agent",
7375
+ model: "MODEL_PLACEHOLDER_M132",
7376
+ role: "cascade-plan",
7377
+ status: "reserved",
7378
+ validatedWith: AGY_SLOT_VALIDATION_SOURCE,
7379
+ notes: "Visible in agentModelSorts, but reserved for cascade plan construction."
7380
+ },
7381
+ {
7382
+ slotId: "gemini-2.5-flash",
7383
+ model: "MODEL_GOOGLE_GEMINI_2_5_FLASH",
7384
+ role: "cascade-intent",
7385
+ status: "reserved",
7386
+ validatedWith: AGY_SLOT_VALIDATION_SOURCE
7387
+ },
7388
+ {
7389
+ slotId: "gemini-2.5-flash-lite",
7390
+ model: "MODEL_GOOGLE_GEMINI_2_5_FLASH_LITE",
7391
+ role: "cascade-fallback",
7392
+ status: "reserved",
7393
+ validatedWith: AGY_SLOT_VALIDATION_SOURCE
7394
+ },
7395
+ {
7396
+ slotId: "gemini-3.1-pro-high",
7397
+ model: "MODEL_PLACEHOLDER_M37",
7398
+ role: "agent-switch",
7399
+ status: "candidate",
7400
+ validatedWith: AGY_SLOT_VALIDATION_SOURCE,
7401
+ notes: "Model-shaped fixture entry; requires live switching proof before promotion."
7402
+ },
7403
+ {
7404
+ slotId: "gemini-2.5-pro",
7405
+ model: "MODEL_GOOGLE_GEMINI_2_5_PRO",
7406
+ role: "agent-switch",
7407
+ status: "candidate",
7408
+ validatedWith: AGY_SLOT_VALIDATION_SOURCE,
7409
+ notes: "Model-shaped fixture entry; requires live switching proof before promotion."
7410
+ },
7411
+ {
7412
+ slotId: "gemini-2.5-flash-thinking",
7413
+ model: "MODEL_GOOGLE_GEMINI_2_5_FLASH_THINKING",
7414
+ role: "agent-switch",
7415
+ status: "candidate",
7416
+ validatedWith: AGY_SLOT_VALIDATION_SOURCE,
7417
+ notes: "Model-shaped fixture entry; requires live switching proof before promotion."
7418
+ },
7419
+ {
7420
+ slotId: "gemini-3-flash",
7421
+ model: "MODEL_PLACEHOLDER_M18",
7422
+ role: "command",
7423
+ status: "candidate",
7424
+ validatedWith: AGY_SLOT_VALIDATION_SOURCE,
7425
+ notes: "Command model in the fixture; not switch-safe without live proof."
7426
+ },
7427
+ {
7428
+ slotId: "gemini-3.1-flash-lite",
7429
+ model: "MODEL_PLACEHOLDER_M50",
7430
+ role: "cascade-checkpoint",
7431
+ status: "candidate",
7432
+ validatedWith: AGY_SLOT_VALIDATION_SOURCE,
7433
+ notes: "Checkpoint/search/commit slot; route as helper until live proof exists."
7434
+ },
7435
+ {
7436
+ slotId: "gemini-3.1-flash-image",
7437
+ model: "MODEL_PLACEHOLDER_M21",
7438
+ role: "image",
7439
+ status: "candidate",
7440
+ validatedWith: AGY_SLOT_VALIDATION_SOURCE,
7441
+ notes: "Image generation slot; not switch-safe without live proof."
7442
+ },
7443
+ {
7444
+ slotId: "tab_jump_flash_lite_preview",
7445
+ model: "MODEL_PLACEHOLDER_M28",
7446
+ role: "tab",
7447
+ status: "unsafe",
7448
+ validatedWith: AGY_SLOT_VALIDATION_SOURCE
7449
+ },
7450
+ {
7451
+ slotId: "tab_flash_lite_preview",
7452
+ model: "MODEL_PLACEHOLDER_M19",
7453
+ role: "tab",
7454
+ status: "unsafe",
7455
+ validatedWith: AGY_SLOT_VALIDATION_SOURCE
7456
+ },
7457
+ {
7458
+ slotId: "chat_20706",
7459
+ model: "MODEL_CHAT_20706",
7460
+ role: "chat",
7461
+ status: "unsafe",
7462
+ validatedWith: AGY_SLOT_VALIDATION_SOURCE
7463
+ },
7464
+ {
7465
+ slotId: "chat_23310",
7466
+ model: "MODEL_CHAT_23310",
7467
+ role: "chat",
7468
+ status: "unsafe",
7469
+ validatedWith: AGY_SLOT_VALIDATION_SOURCE
7470
+ }
7471
+ ];
7472
+ var KNOWN_COMPATIBLE_AGY_VERSIONS = /* @__PURE__ */ new Set([
7473
+ "1.0.10",
7474
+ "1.1.0",
7475
+ "1.1.1",
7476
+ "1.1.2",
7477
+ "1.1.3",
7478
+ "1.1.4",
7479
+ "1.1.5",
7480
+ "1.1.6",
7481
+ "1.1.7"
7482
+ ]);
7483
+ var KNOWN_INCOMPATIBLE_AGY_VERSIONS = /* @__PURE__ */ new Set(["1.0.9"]);
7484
+ function withFixtureModel(definition, model) {
7485
+ return model === definition.model ? definition : { ...definition, model };
7486
+ }
7487
+ function assertNoDuplicateSwitchEnums(fixture, definitions) {
7488
+ const seen = /* @__PURE__ */ new Map();
7489
+ for (const definition of definitions) {
7490
+ if (definition.status !== "validated") continue;
7491
+ const actualModel = fixture.models[definition.slotId]?.model;
7492
+ if (!actualModel) continue;
7493
+ const previousSlotId = seen.get(actualModel);
7494
+ if (previousSlotId) {
7495
+ throw new Error(
7496
+ `Duplicate AGY switch slot enum ${actualModel}: ${previousSlotId} and ${definition.slotId}`
7497
+ );
7498
+ }
7499
+ seen.set(actualModel, definition.slotId);
7500
+ }
7501
+ }
7502
+ function validateAgySlotRegistry(fixture) {
7503
+ assertNoDuplicateSwitchEnums(fixture, AGY_NATIVE_SLOT_REGISTRY);
7504
+ const switchSlots = [];
7505
+ const reservedSlots = [];
7506
+ const candidateSlots = [];
7507
+ const warnings = [];
7508
+ for (const definition of AGY_NATIVE_SLOT_REGISTRY) {
7509
+ const entry = fixture.models[definition.slotId];
7510
+ if (!entry) {
7511
+ if (definition.status === "validated" || definition.status === "reserved") {
7512
+ warnings.push(`AGY slot ${definition.slotId} missing from fixture`);
7513
+ }
7514
+ continue;
7515
+ }
7516
+ if (entry.model !== definition.model) {
7517
+ warnings.push(
7518
+ `AGY slot ${definition.slotId} expected ${definition.model} but fixture has ${entry.model}`
7519
+ );
7520
+ continue;
7521
+ }
7522
+ if (definition.status === "validated") {
7523
+ switchSlots.push(withFixtureModel(definition, entry.model));
7524
+ } else if (definition.status === "reserved") {
7525
+ reservedSlots.push(withFixtureModel(definition, entry.model));
7526
+ } else if (definition.status === "candidate") {
7527
+ candidateSlots.push(withFixtureModel(definition, entry.model));
7528
+ }
7529
+ }
7530
+ return { switchSlots, reservedSlots, candidateSlots, warnings };
7531
+ }
7532
+ function getValidatedAgySwitchSlots(fixture) {
7533
+ return validateAgySlotRegistry(fixture).switchSlots;
7534
+ }
7535
+ function evaluateAgySwitchCompatibility(opts) {
7536
+ const validation = validateAgySlotRegistry(opts.fixture);
7537
+ const shapeMatches = validation.warnings.length === 0 && validation.switchSlots.length > 0;
7538
+ const warnings = [];
7539
+ if (opts.versionReadError) {
7540
+ warnings.push(`Could not read agy --version (${opts.versionReadError}); validating AGY fixture shape instead.`);
7541
+ }
7542
+ if (opts.version && KNOWN_INCOMPATIBLE_AGY_VERSIONS.has(opts.version)) {
7543
+ return {
7544
+ mode: "single-model",
7545
+ validatedSwitchSlotCount: validation.switchSlots.length,
7546
+ warnings: [
7547
+ ...warnings,
7548
+ `Known-incompatible AGY version ${opts.version}; falling back to single-model mode.`
7549
+ ]
7550
+ };
7551
+ }
7552
+ if (!shapeMatches) {
7553
+ return {
7554
+ mode: "single-model",
7555
+ validatedSwitchSlotCount: validation.switchSlots.length,
7556
+ warnings: [
7557
+ ...warnings,
7558
+ ...validation.warnings,
7559
+ "AGY fixture shape does not match the validated slot registry; falling back to single-model mode."
7560
+ ]
7561
+ };
7562
+ }
7563
+ if (opts.version && !KNOWN_COMPATIBLE_AGY_VERSIONS.has(opts.version)) {
7564
+ return {
7565
+ mode: "single-model",
7566
+ validatedSwitchSlotCount: validation.switchSlots.length,
7567
+ warnings: [
7568
+ ...warnings,
7569
+ `Unvalidated AGY version ${opts.version}; falling back to single-model mode for maximum stability.`
7570
+ ]
7571
+ };
7572
+ } else if (!opts.version && !opts.versionReadError) {
7573
+ warnings.push("AGY version is unknown; fixture shape matches, so multi-model switching remains enabled.");
7574
+ }
7575
+ return {
7576
+ mode: "multi-model",
7577
+ validatedSwitchSlotCount: validation.switchSlots.length,
7578
+ warnings
7579
+ };
7580
+ }
7581
+
7582
+ // src/antigravity/catalog.ts
7583
+ var RELAY_CASCADE_PLAN_MODEL = "MODEL_PLACEHOLDER_M132";
7584
+ var RELAY_AGENT_PLACEHOLDER = "MODEL_PLACEHOLDER_M20";
7585
+ var RELAY_CASCADE_CHECKPOINT_MODEL = "MODEL_PLACEHOLDER_M50";
7586
+ var RELAY_CASCADE_INTENT_MODEL = "MODEL_GOOGLE_GEMINI_2_5_FLASH";
7587
+ var RELAY_CASCADE_ANCHOR_ID = "gemini-3.5-flash-low";
7588
+ var RELAY_CASCADE_PLAN_ANCHOR_ID = "gemini-3-flash-agent";
7589
+ var RELAY_CASCADE_FALLBACK_ID = "gemini-2.5-flash-lite";
7590
+ var RELAY_CASCADE_INTENT_MODEL_ID = "gemini-2.5-flash";
7591
+ function withCascadeCheckpointer(entry, maxTokenLimit = 128e3) {
7592
+ const tokenThreshold = Math.min(5e4, Math.floor(maxTokenLimit * 0.75));
7593
+ const existingModelExperiments = entry.modelExperiments;
7594
+ entry.modelExperiments = {
7595
+ ...existingModelExperiments,
7596
+ experiments: {
7597
+ ...existingModelExperiments?.experiments ?? {},
7598
+ CASCADE_USE_EXPERIMENT_CHECKPOINTER: {
7599
+ stringValue: JSON.stringify({
7600
+ strategy: "CHECKPOINT_STRATEGY_SAME_MODEL",
7601
+ max_token_limit: String(maxTokenLimit),
7602
+ token_threshold: String(tokenThreshold),
7603
+ max_overhead_ratio: "0.15",
7604
+ moving_window_size: "1",
7605
+ enabled: true,
7606
+ max_output_tokens: "16384",
7607
+ checkpoint_model: RELAY_CASCADE_CHECKPOINT_MODEL,
7608
+ use_last_planner_model: true,
7609
+ is_sync: true,
7610
+ max_user_requests: 10,
7611
+ include_last_user_message: true,
7612
+ include_conversation_log: false,
7613
+ include_running_task_snapshots: true,
7614
+ include_subagent_snapshots: true,
7615
+ include_artifact_snapshots: true,
7616
+ retry_config: {
7617
+ max_retries: 0,
7618
+ initial_sleep_duration_ms: 1e3,
7619
+ exponential_multiplier: 2,
7620
+ include_error_feedback: false
7621
+ }
7622
+ })
7623
+ }
7624
+ }
7625
+ };
7626
+ return entry;
7627
+ }
7628
+ var ANTIGRAVITY_REQUIRED_INPUT_TOKENS = 128e3;
7629
+ var ANTIGRAVITY_MIN_OUTPUT_TOKENS = 8192;
7630
+ var ANTIGRAVITY_MIN_CONTEXT_WINDOW = ANTIGRAVITY_REQUIRED_INPUT_TOKENS + ANTIGRAVITY_MIN_OUTPUT_TOKENS;
7631
+ function applyRouteContextBounds(entry, route) {
7632
+ const maxTokenLimit = route.contextWindow ?? 128e3;
7633
+ const maxOutputTokens = Math.min(
7634
+ entry.maxOutputTokens ?? 65536,
7635
+ Math.max(
7636
+ ANTIGRAVITY_MIN_OUTPUT_TOKENS,
7637
+ maxTokenLimit - ANTIGRAVITY_REQUIRED_INPUT_TOKENS
7638
+ )
7639
+ );
7640
+ const checkpointTokenLimit = Math.min(
7641
+ 128e3,
7642
+ Math.max(1, maxTokenLimit - maxOutputTokens)
7643
+ );
7644
+ entry.maxTokens = maxTokenLimit;
7645
+ entry.maxOutputTokens = maxOutputTokens;
7646
+ return withCascadeCheckpointer(entry, checkpointTokenLimit);
7647
+ }
7648
+ var RELAY_CASCADE_FALLBACK_ENTRY = withCascadeCheckpointer({
7649
+ displayName: "Gemini 3.1 Flash Lite",
7650
+ model: "MODEL_GOOGLE_GEMINI_2_5_FLASH_LITE",
7651
+ apiProvider: "API_PROVIDER_GOOGLE_GEMINI",
7652
+ modelProvider: "MODEL_PROVIDER_GOOGLE",
7653
+ tokenizerType: "LLAMA_WITH_SPECIAL",
7654
+ maxTokens: 1048576,
7655
+ maxOutputTokens: 65535,
7656
+ quotaInfo: { remainingFraction: 1 }
7657
+ });
7658
+ var RELAY_CASCADE_INTENT_MODEL_ENTRY = withCascadeCheckpointer({
7659
+ ...RELAY_CASCADE_FALLBACK_ENTRY,
7660
+ model: RELAY_CASCADE_INTENT_MODEL
7661
+ });
7662
+ function planRelayCatalogSlots(catalog, routes, templateKey) {
7663
+ const validation = validateAgySlotRegistry(catalog);
7664
+ const switchSlots = getValidatedAgySwitchSlots(catalog);
7665
+ const templateSlot = switchSlots.find((slot) => slot.slotId === templateKey);
7666
+ const orderedSlots = templateSlot ? [templateSlot, ...switchSlots.filter((slot) => slot.slotId !== templateKey)] : switchSlots;
7667
+ if (routes.length > 0 && orderedSlots.length === 0) {
7668
+ throw new Error("No validated AGY switch slots are available for the selected launch route");
7669
+ }
7670
+ const switchableRoutes = routes.slice(0, orderedSlots.length);
7671
+ const skippedRoutes = routes.slice(orderedSlots.length);
7672
+ const slots = switchableRoutes.map((route, index) => ({
7673
+ slotId: orderedSlots[index].slotId,
7674
+ route
7675
+ }));
7676
+ return {
7677
+ slots,
7678
+ switchableRoutes,
7679
+ skippedRoutes,
7680
+ validation
7681
+ };
7682
+ }
7683
+ function resolveRelayCatalogSlots(catalog, routes, templateKey) {
7684
+ return planRelayCatalogSlots(catalog, routes, templateKey).slots;
7685
+ }
7686
+ function buildRelayCatalogEntry(route, template) {
7687
+ const entry = structuredClone(template);
7688
+ entry.displayName = route.displayName;
7689
+ entry.model = template.model ?? RELAY_AGENT_PLACEHOLDER;
7690
+ entry.requestedModelId = route.catalogId;
7691
+ entry.modelVersion = route.catalogId;
7692
+ entry.modelVersionId = route.catalogId;
7693
+ entry.quotaInfo = { remainingFraction: 1, resetTime: "2026-06-23T02:00:57Z" };
7694
+ return applyRouteContextBounds(entry, route);
7695
+ }
7696
+ function buildRelayCatalogSlotEntry(route, template) {
7697
+ const entry = structuredClone(template);
7698
+ entry.displayName = route.displayName;
7699
+ entry.quotaInfo = { remainingFraction: 1, resetTime: "2026-06-23T02:00:57Z" };
7700
+ delete entry.requestedModelId;
7701
+ delete entry.modelVersion;
7702
+ delete entry.modelVersionId;
7703
+ delete entry.isInternal;
7704
+ return applyRouteContextBounds(entry, route);
7705
+ }
7706
+ function injectRelayModels(fixture, routes, templateKey) {
7707
+ const result = structuredClone(fixture);
7708
+ const template = fixture.models[templateKey];
7709
+ if (!template) {
7710
+ throw new Error(`Template model "${templateKey}" not found in catalog fixture`);
7711
+ }
7712
+ const seen = /* @__PURE__ */ new Set();
7713
+ for (const route of routes) {
7714
+ if (seen.has(route.catalogId)) {
7715
+ throw new Error(`Catalog ID collision: ${route.catalogId}`);
7716
+ }
7717
+ if (fixture.models[route.catalogId]) {
7718
+ throw new Error(`Catalog ID collision with native model: ${route.catalogId}`);
7719
+ }
7720
+ seen.add(route.catalogId);
7721
+ }
7722
+ if (routes.length > 0) {
7723
+ result.models[RELAY_CASCADE_ANCHOR_ID] ??= structuredClone(template);
7724
+ result.models[RELAY_CASCADE_FALLBACK_ID] ??= structuredClone(RELAY_CASCADE_FALLBACK_ENTRY);
7725
+ result.models[RELAY_CASCADE_INTENT_MODEL_ID] ??= structuredClone(RELAY_CASCADE_INTENT_MODEL_ENTRY);
7726
+ if (!result.models[RELAY_CASCADE_PLAN_ANCHOR_ID]) {
7727
+ const planAnchor = withCascadeCheckpointer(structuredClone(template));
7728
+ planAnchor.model = RELAY_CASCADE_PLAN_MODEL;
7729
+ result.models[RELAY_CASCADE_PLAN_ANCHOR_ID] = planAnchor;
7730
+ }
7731
+ const slotPlan = planRelayCatalogSlots(result, routes, templateKey);
7732
+ const slots = slotPlan.slots;
7733
+ for (const { slotId, route } of slots) {
7734
+ const slotTemplate = result.models[slotId] ?? template;
7735
+ if (result.models[slotId]) {
7736
+ result.models[slotId] = buildRelayCatalogSlotEntry(route, slotTemplate);
7737
+ }
7738
+ result.models[route.catalogId] = buildRelayCatalogEntry(route, slotTemplate);
7739
+ }
7740
+ result.defaultAgentModelId = slots[0]?.slotId ?? RELAY_CASCADE_ANCHOR_ID;
7741
+ result.agentModelSorts = [
7742
+ {
7743
+ displayName: "Recommended",
7744
+ groups: [{
7745
+ modelIds: slots.map((slot) => slot.slotId)
7746
+ }]
7747
+ }
7748
+ ];
7749
+ return result;
7750
+ }
7751
+ if (!result.agentModelSorts?.[0]?.groups?.[0]) {
7752
+ result.agentModelSorts = [
7753
+ {
7754
+ displayName: "Recommended",
7755
+ groups: [{ modelIds: [] }]
7756
+ }
7757
+ ];
7758
+ }
7759
+ return result;
7760
+ }
7761
+ function buildAntigravityRoutes(resolvedFavorites, maxRoutes = MAX_MODEL_CATALOG) {
7762
+ const routes = [];
7763
+ const seen = /* @__PURE__ */ new Set();
7764
+ for (const fav of resolvedFavorites) {
7765
+ if (routes.length >= maxRoutes) break;
7766
+ const favModel = fav.model;
7767
+ const modelId = favModel.id;
7768
+ const safeModelSlug = modelId.replace(/[^a-zA-Z0-9_-]/g, "_");
7769
+ const catalogId = `relay-ai__${fav.providerId}__${safeModelSlug}`;
7770
+ if (seen.has(catalogId)) continue;
7771
+ seen.add(catalogId);
7772
+ const npm = favModel.npm || "@ai-sdk/openai-compatible";
7773
+ const upstreamModelId2 = favModel.upstreamModelId || modelId;
7774
+ const baseURL = favModel.apiBaseUrl || favModel.completionsUrl || void 0;
7775
+ const contextWindow = favModel.contextWindow;
7776
+ const modelFormat = favModel.modelFormat;
7777
+ routes.push({
7778
+ catalogId,
7779
+ providerId: fav.providerId,
7780
+ providerName: fav.providerName,
7781
+ modelId,
7782
+ upstreamModelId: upstreamModelId2,
7783
+ displayName: `${favModel.name} (Relay)`,
7784
+ ...modelFormat ? { modelFormat } : {},
7785
+ npm,
7786
+ apiKey: fav.apiKey,
7787
+ ...fav.authType ? { authType: fav.authType } : {},
7788
+ ...fav.oauthAccountId ? { oauthAccountId: fav.oauthAccountId } : {},
7789
+ ...fav.providerData ? { providerData: fav.providerData } : {},
7790
+ baseURL,
7791
+ contextWindow
7792
+ });
7793
+ }
7794
+ return applyUniqueAntigravityRouteLabels(routes);
7795
+ }
7796
+ function routeBaseModelName(route) {
7797
+ const relayMatch = route.displayName.match(/^(.*) \(Relay(?: - .*)?\)$/);
7798
+ return relayMatch?.[1] ?? route.displayName;
7799
+ }
7800
+ function authKindLabel(route) {
7801
+ if (route.authType === "oauth") return "OAuth";
7802
+ if (route.authType === "api") return "API key";
7803
+ if (route.authType === "none") return "local";
7804
+ return "provider";
7805
+ }
7806
+ function duplicateCounts(values) {
7807
+ const counts = /* @__PURE__ */ new Map();
7808
+ for (const value of values) {
7809
+ counts.set(value, (counts.get(value) ?? 0) + 1);
7810
+ }
7811
+ return counts;
7812
+ }
7813
+ function assertUniqueRouteDisplayNames(routes) {
7814
+ const counts = duplicateCounts(routes.map((route) => route.displayName));
7815
+ const duplicate = [...counts.entries()].find(([, count]) => count > 1);
7816
+ if (duplicate) {
7817
+ throw new Error(`Duplicate AGY model label after disambiguation: ${duplicate[0]}`);
7818
+ }
7819
+ }
7820
+ function applyUniqueAntigravityRouteLabels(routes) {
7821
+ const baseNames = routes.map(routeBaseModelName);
7822
+ const baseNameCounts = duplicateCounts(baseNames);
7823
+ const upstreamCounts = duplicateCounts(routes.map((route) => route.upstreamModelId));
7824
+ const providerNameCounts = duplicateCounts(routes.map((route) => route.providerName));
7825
+ const labeled = routes.map((route, index) => {
7826
+ const baseName = baseNames[index];
7827
+ const needsSuffix = (baseNameCounts.get(baseName) ?? 0) > 1 || (upstreamCounts.get(route.upstreamModelId) ?? 0) > 1;
7828
+ if (!needsSuffix) {
7829
+ return { ...route, displayName: `${baseName} (Relay)` };
7830
+ }
7831
+ const providerName = route.providerName || route.providerId;
7832
+ const providerSuffix = (providerNameCounts.get(providerName) ?? 0) > 1 ? `${providerName} ${authKindLabel(route)}` : providerName;
7833
+ return {
7834
+ ...route,
7835
+ displayName: `${baseName} (Relay - ${providerSuffix})`
7836
+ };
7837
+ });
7838
+ const firstPassCounts = duplicateCounts(labeled.map((route) => route.displayName));
7839
+ const withProviderIds = labeled.map((route) => {
7840
+ if ((firstPassCounts.get(route.displayName) ?? 0) <= 1) return route;
7841
+ return {
7842
+ ...route,
7843
+ displayName: route.displayName.replace(/\)$/, ` - ${route.providerId})`)
7844
+ };
7845
+ });
7846
+ assertUniqueRouteDisplayNames(withProviderIds);
7847
+ return withProviderIds;
7848
+ }
7849
+ function routeLabels(routes) {
7850
+ assertUniqueRouteDisplayNames(routes);
7851
+ const labels = /* @__PURE__ */ new Map();
7852
+ for (const route of routes) {
7853
+ labels.set(route.catalogId, route.displayName);
7854
+ }
7855
+ return labels;
7856
+ }
7857
+ function buildClientModelConfigData(routes, catalog, templateKey = RELAY_CASCADE_ANCHOR_ID, precomputedSlots) {
7858
+ const catalogRoutes = routes.slice(0, MAX_MODEL_CATALOG);
7859
+ const slots = precomputedSlots ?? (catalog ? resolveRelayCatalogSlots(catalog, catalogRoutes, templateKey) : catalogRoutes.map((route) => ({ slotId: route.catalogId, route })));
7860
+ const labels = routeLabels(catalogRoutes);
7861
+ const clientModelConfigs = slots.map(({ slotId, route }) => {
7862
+ const entry = catalog?.models[slotId] ?? catalog?.models[route.catalogId] ?? catalog?.models[RELAY_CASCADE_ANCHOR_ID];
7863
+ const label = labels.get(route.catalogId) ?? route.displayName;
7864
+ return {
7865
+ label,
7866
+ modelOrAlias: {
7867
+ alias: slotId,
7868
+ choice: { case: "alias", value: slotId }
7869
+ },
7870
+ disabled: false,
7871
+ supportedMimeTypes: entry?.supportedMimeTypes ?? {},
7872
+ quotaInfo: entry?.quotaInfo ?? { remainingFraction: 1 },
7873
+ tagTitle: entry?.tagTitle,
7874
+ tagDescription: entry?.tagDescription,
7875
+ supportsThoughtCirculation: entry?.supportsThoughtCirculation ?? false
7876
+ };
7877
+ });
7878
+ return {
7879
+ clientModelConfigs,
7880
+ clientModelSorts: [
7881
+ {
7882
+ name: "Recommended",
7883
+ groups: [
7884
+ {
7885
+ groupName: "",
7886
+ modelLabels: clientModelConfigs.map((config) => config.label)
7887
+ }
7888
+ ]
7889
+ }
7890
+ ],
7891
+ defaultOverrideModelConfig: clientModelConfigs[0] ?? {}
7892
+ };
7893
+ }
7894
+ function buildListModelConfigsResponse(routes, catalog, templateKey = RELAY_CASCADE_ANCHOR_ID) {
7895
+ const catalogRoutes = routes.slice(0, MAX_MODEL_CATALOG);
7896
+ const slots = catalog ? resolveRelayCatalogSlots(catalog, catalogRoutes, templateKey) : catalogRoutes.map((route) => ({ slotId: route.catalogId, route }));
7897
+ const config = slots.map(({ slotId }) => ({
7898
+ requestedModelId: slotId,
7899
+ planModel: RELAY_CASCADE_PLAN_MODEL,
7900
+ requestedModel: catalog?.models[slotId]?.model ?? RELAY_AGENT_PLACEHOLDER
7901
+ }));
7902
+ return {
7903
+ ...buildClientModelConfigData(routes, catalog, templateKey, slots),
7904
+ allowedModelConfigs: config,
7905
+ defaultAgentModelConfig: config[0] ?? {}
7906
+ };
7907
+ }
7908
+ var CURRENT_EXPERIMENT_IDS = [
7909
+ 105979552,
7910
+ 105979574,
7911
+ 106015351,
7912
+ 105979579,
7913
+ 105867471,
7914
+ 105979530,
7915
+ 105995634,
7916
+ 106121401,
7917
+ 106100625,
7918
+ 104638466,
7919
+ 101868197,
7920
+ 104817729,
7921
+ 105695344,
7922
+ 106064591,
7923
+ 104913215,
7924
+ 106324349,
7925
+ 106309078,
7926
+ 105821930,
7927
+ 104922093,
7928
+ 103012598,
7929
+ 106143956,
7930
+ 105856899,
7931
+ 106312323,
7932
+ 106064030,
7933
+ 105746183,
7934
+ 105757908,
7935
+ 104892493,
7936
+ 105822886,
7937
+ 105785683,
7938
+ 105721273,
7939
+ 105897325,
7940
+ 105658071,
7941
+ 106240758,
7942
+ 105943702,
7943
+ 106106760,
7944
+ 106283618,
7945
+ 105620019,
7946
+ 106038160,
7947
+ 106309520,
7948
+ 106281951,
7949
+ 106264532,
7950
+ 106222835,
7951
+ 106094629,
7952
+ 105887313,
7953
+ 105849474,
7954
+ 106032303,
7955
+ 106228452,
7956
+ 106113900,
7957
+ 106121607,
7958
+ 105979531,
7959
+ 105979553,
7960
+ 106015328,
7961
+ 105867469,
7962
+ 105979517,
7963
+ 106121399,
7964
+ 106100654,
7965
+ 104638459,
7966
+ 101551624,
7967
+ 104673683,
7968
+ 105695346,
7969
+ 106064590,
7970
+ 104913210,
7971
+ 105821928,
7972
+ 104922082,
7973
+ 103012592,
7974
+ 106064028,
7975
+ 105746181,
7976
+ 104892490,
7977
+ 105822881,
7978
+ 105721268,
7979
+ 105895316,
7980
+ 105658068,
7981
+ 106240748,
7982
+ 105943694,
7983
+ 106283614,
7984
+ 105620012,
7985
+ 106038153,
7986
+ 105887311,
7987
+ 106032301,
7988
+ 106113877,
7989
+ 106121604
7990
+ ];
7991
+ function buildListExperimentsResponse() {
7992
+ return {
7993
+ experimentIds: [...CURRENT_EXPERIMENT_IDS]
7994
+ };
7995
+ }
7996
+
7291
7997
  // src/target-compatibility.ts
7292
7998
  function blacklistAgentForTarget(target) {
7293
7999
  if (target === "claude-app") return "codex-app";
7294
8000
  return target;
7295
8001
  }
8002
+ function contextFloorForTarget(target) {
8003
+ if (target === "antigravity") return ANTIGRAVITY_MIN_CONTEXT_WINDOW;
8004
+ if (target === "server") return 0;
8005
+ return MIN_CONTEXT_WINDOW;
8006
+ }
8007
+ function meetsContextFloor(target, contextWindow) {
8008
+ return contextWindow === void 0 || contextWindow >= contextFloorForTarget(target);
8009
+ }
7296
8010
  function isTargetCompatibleModel(ctx) {
7297
8011
  const blacklistAgent = blacklistAgentForTarget(ctx.target);
7298
8012
  if (shouldHideModel({ providerId: ctx.providerId, modelId: ctx.model.id, agent: blacklistAgent })) {
7299
8013
  return { compatible: false, reason: "model is hidden by compatibility filters" };
7300
8014
  }
8015
+ if (!meetsContextFloor(ctx.target, ctx.model.contextWindow)) {
8016
+ const floor = contextFloorForTarget(ctx.target);
8017
+ return {
8018
+ compatible: false,
8019
+ reason: `${ctx.target} needs a ${Math.round(floor / 1e3)}K+ context window; this model has ${Math.round(ctx.model.contextWindow / 1e3)}K`
8020
+ };
8021
+ }
7301
8022
  if (ctx.model.modelFormat === "cloud-code") {
7302
8023
  if (ctx.target === "server") {
7303
8024
  return { compatible: false, reason: "Cloud Code models are not supported for the server target yet" };
@@ -7362,8 +8083,11 @@ function modelFormatForNpm(npm) {
7362
8083
  return npm === "@ai-sdk/anthropic" ? "anthropic" : "openai";
7363
8084
  }
7364
8085
  function modelsUrl(baseUrl, template) {
7365
- const trimmed = baseUrl.replace(/\/$/, "");
8086
+ let trimmed = baseUrl.replace(/\/$/, "");
7366
8087
  if (template.modelsPath) {
8088
+ if (trimmed.endsWith("/v1") && (template.modelsPath.startsWith("/models") || template.modelsPath.startsWith("/ai/models"))) {
8089
+ trimmed = trimmed.slice(0, -3);
8090
+ }
7367
8091
  const path = template.modelsPath.startsWith("/") ? template.modelsPath : `/${template.modelsPath}`;
7368
8092
  return `${trimmed}${path}`;
7369
8093
  }
@@ -7400,20 +8124,64 @@ function parseNativePricing(pricing) {
7400
8124
  if (cacheWrite !== void 0) cost.cache_write = cacheWrite;
7401
8125
  return cost;
7402
8126
  }
8127
+ function parseCloudflarePricing(priceValue) {
8128
+ if (!Array.isArray(priceValue)) return void 0;
8129
+ let input;
8130
+ let output;
8131
+ for (const item of priceValue) {
8132
+ if (typeof item !== "object" || !item) continue;
8133
+ const row = item;
8134
+ const unit = String(row.unit || "").toLowerCase();
8135
+ const price = Number(row.price);
8136
+ if (!Number.isFinite(price)) continue;
8137
+ if (unit.includes("input")) input = price;
8138
+ else if (unit.includes("output")) output = price;
8139
+ }
8140
+ if (input === void 0 && output === void 0) return void 0;
8141
+ return { input: input ?? 0, output: output ?? 0 };
8142
+ }
8143
+ var LEGACY_NON_TOOL_MODELS = /* @__PURE__ */ new Set([
8144
+ "@cf/google/gemma-2b-it-lora",
8145
+ "@cf/google/gemma-7b-it-lora",
8146
+ "@cf/meta-llama/llama-2-7b-chat-hf-lora",
8147
+ "@cf/mistral/mistral-7b-instruct-v0.2-lora"
8148
+ ]);
7403
8149
  function parseModelList(body, npm) {
7404
- const rows = body.data ?? body.models ?? [];
8150
+ const rows = body.data ?? body.models ?? body.result ?? [];
7405
8151
  const format = modelFormatForNpm(npm);
7406
8152
  const models = [];
7407
8153
  for (const row of rows) {
7408
- const rawId = row.id?.trim();
8154
+ const rawId = (row.name?.startsWith("@cf/") || row.name?.startsWith("@hf/") ? row.name : row.id)?.trim();
7409
8155
  if (!rawId) continue;
8156
+ let contextWindowFromProps;
8157
+ let isFreeFromProps;
8158
+ let costFromProps;
8159
+ if (Array.isArray(row.properties)) {
8160
+ if (LEGACY_NON_TOOL_MODELS.has(rawId)) continue;
8161
+ const cwProp = row.properties.find((p8) => p8.property_id === "context_window");
8162
+ if (cwProp?.value) contextWindowFromProps = toNumber(cwProp.value);
8163
+ const priceProp = row.properties.find((p8) => p8.property_id === "price");
8164
+ const isRestrictedPaidPlan = rawId.includes("/glm-") || rawId.includes("/kimi-");
8165
+ if (isRestrictedPaidPlan) {
8166
+ costFromProps = parseCloudflarePricing(priceProp?.value);
8167
+ isFreeFromProps = false;
8168
+ } else {
8169
+ isFreeFromProps = true;
8170
+ if (priceProp?.value) {
8171
+ costFromProps = parseCloudflarePricing(priceProp.value);
8172
+ }
8173
+ }
8174
+ }
7410
8175
  const { id, upstreamModelId: upstreamModelId2 } = normalizeGoogleModelId(rawId, npm);
7411
- const family = id.split(/[-/:]/)[0] ?? id;
7412
- const cost = parseNativePricing(row.pricing);
8176
+ const family = id.replace(/^@[a-z0-9_-]+\//i, "").split(/[-/:]/)[0] ?? id;
8177
+ const cost = costFromProps ?? parseNativePricing(row.pricing);
7413
8178
  const freeStatus = classifyFreeStatus({
7414
- model: { cost, isFree: row.isFree }
8179
+ model: { cost, isFree: row.isFree },
8180
+ // Cloudflare standard models carry a list price but are covered by the free
8181
+ // daily Neuron allowance, so free access is a provider rule, not a price.
8182
+ freeAccess: isFreeFromProps === true
7415
8183
  });
7416
- const contextWindow = row.context_length ?? row.contextWindow ?? row.context_window ?? resolveContextWindow(id);
8184
+ const contextWindow = contextWindowFromProps ?? row.context_length ?? row.contextWindow ?? row.context_window ?? resolveContextWindow(id);
7417
8185
  models.push({
7418
8186
  id,
7419
8187
  name: normalizeGoogleDisplayName(row.name, id),
@@ -10697,14 +11465,14 @@ function codexAppInstallHint() {
10697
11465
 
10698
11466
  // src/claude-desktop/app-launch.ts
10699
11467
  import { execSync as execSync3, spawn as spawn3 } from "child_process";
10700
- import { existsSync as existsSync12, readdirSync as readdirSync2, statSync as statSync4 } from "fs";
11468
+ import { existsSync as existsSync12, readdirSync as readdirSync2, readFileSync as readFileSync12, statSync as statSync4 } from "fs";
10701
11469
  import { homedir as homedir8 } from "os";
10702
11470
  import { join as join12 } from "path";
10703
11471
  import * as p7 from "@clack/prompts";
10704
11472
  var CLAUDE_BUNDLE_ID = "com.anthropic.claudefordesktop";
10705
11473
  function claudeAppSupported() {
10706
- if (process.platform !== "darwin" && process.platform !== "win32") {
10707
- throw new Error("Claude Desktop launch is supported on macOS and Windows only.");
11474
+ if (process.platform !== "darwin" && process.platform !== "win32" && process.platform !== "linux") {
11475
+ throw new Error("Claude Desktop launch is supported on macOS, Windows, and Linux only.");
10708
11476
  }
10709
11477
  }
10710
11478
  function run2(cmd, encoding = "utf8") {
@@ -10746,6 +11514,58 @@ function winClaudeExeCandidates() {
10746
11514
  }
10747
11515
  return out;
10748
11516
  }
11517
+ function linuxClaudeCandidates() {
11518
+ return [
11519
+ // Wrapper launcher installed by the .deb/.rpm — sets up the Electron sandbox.
11520
+ "/usr/bin/claude-desktop",
11521
+ "/usr/lib/claude-desktop/claude-desktop",
11522
+ "/opt/Claude/claude-desktop",
11523
+ join12(homedir8(), ".local", "bin", "claude-desktop")
11524
+ ];
11525
+ }
11526
+ function linuxWhichClaude() {
11527
+ try {
11528
+ const out = run2("command -v claude-desktop");
11529
+ return out && existsSync12(out) ? out : null;
11530
+ } catch {
11531
+ return null;
11532
+ }
11533
+ }
11534
+ function linuxMatchingPids() {
11535
+ try {
11536
+ const out = run2("pgrep -x claude-desktop");
11537
+ return out.split(/\s+/).map((s) => Number.parseInt(s, 10)).filter((n) => Number.isFinite(n) && n > 0);
11538
+ } catch {
11539
+ return [];
11540
+ }
11541
+ }
11542
+ function linuxMainPid() {
11543
+ const pids = linuxMatchingPids();
11544
+ for (const pid of pids) {
11545
+ try {
11546
+ const cmdline = readFileSync12(`/proc/${pid}/cmdline`, "utf8");
11547
+ if (!cmdline.includes("--type=")) return pid;
11548
+ } catch {
11549
+ }
11550
+ }
11551
+ return pids[0] ?? null;
11552
+ }
11553
+ function linuxQuit() {
11554
+ const pid = linuxMainPid();
11555
+ if (pid === null) return;
11556
+ try {
11557
+ process.kill(pid, "SIGTERM");
11558
+ } catch {
11559
+ }
11560
+ }
11561
+ function linuxForceQuit() {
11562
+ for (const pid of linuxMatchingPids()) {
11563
+ try {
11564
+ process.kill(pid, "SIGKILL");
11565
+ } catch {
11566
+ }
11567
+ }
11568
+ }
10749
11569
  function mdfindClaudeApp() {
10750
11570
  try {
10751
11571
  const out = run2(`mdfind "kMDItemCFBundleIdentifier == '${CLAUDE_BUNDLE_ID}'"`);
@@ -10777,6 +11597,15 @@ function findClaudeApp() {
10777
11597
  } catch {
10778
11598
  }
10779
11599
  }
11600
+ if (process.platform === "linux") {
11601
+ for (const path of linuxClaudeCandidates()) {
11602
+ try {
11603
+ if (existsSync12(path)) return path;
11604
+ } catch {
11605
+ }
11606
+ }
11607
+ return linuxWhichClaude();
11608
+ }
10780
11609
  return null;
10781
11610
  }
10782
11611
  function darwinIsRunning2() {
@@ -10809,6 +11638,7 @@ function winHasWindow2() {
10809
11638
  function isClaudeAppRunning() {
10810
11639
  if (process.platform === "darwin") return darwinIsRunning2();
10811
11640
  if (process.platform === "win32") return winMatchingPids2().length > 0 || winHasWindow2();
11641
+ if (process.platform === "linux") return linuxMatchingPids().length > 0;
10812
11642
  return false;
10813
11643
  }
10814
11644
  function sleep2(ms) {
@@ -10819,12 +11649,16 @@ async function waitForQuit2(timeoutMs) {
10819
11649
  while (Date.now() < deadline) {
10820
11650
  if (process.platform === "win32") {
10821
11651
  if (winMatchingPids2().length === 0) return true;
11652
+ } else if (process.platform === "linux") {
11653
+ if (linuxMatchingPids().length === 0) return true;
10822
11654
  } else if (!darwinIsRunning2()) {
10823
11655
  return true;
10824
11656
  }
10825
11657
  await sleep2(200);
10826
11658
  }
10827
- return process.platform === "win32" ? winMatchingPids2().length === 0 : !darwinIsRunning2();
11659
+ if (process.platform === "win32") return winMatchingPids2().length === 0;
11660
+ if (process.platform === "linux") return linuxMatchingPids().length === 0;
11661
+ return !darwinIsRunning2();
10828
11662
  }
10829
11663
  function openClaudeAppAt(path) {
10830
11664
  if (process.platform === "darwin") {
@@ -10841,6 +11675,10 @@ function openClaudeAppAt(path) {
10841
11675
  } else {
10842
11676
  runPowerShell2(`Start-Process -FilePath '${path.replace(/'/g, "''")}'`);
10843
11677
  }
11678
+ return;
11679
+ }
11680
+ if (process.platform === "linux") {
11681
+ spawn3(path, [], { stdio: "ignore", detached: true }).unref();
10844
11682
  }
10845
11683
  }
10846
11684
  function openClaudeApp() {
@@ -10867,6 +11705,7 @@ function winQuitGraceful2() {
10867
11705
  function quitClaudeAppGracefully() {
10868
11706
  if (process.platform === "darwin") darwinQuit2();
10869
11707
  else if (process.platform === "win32") winQuitGraceful2();
11708
+ else if (process.platform === "linux") linuxQuit();
10870
11709
  }
10871
11710
  function winForceQuit2() {
10872
11711
  const pids = winMatchingPids2();
@@ -10888,9 +11727,11 @@ async function launchOrRestartClaudeApp(prompt = "Restart Claude Desktop to appl
10888
11727
  return;
10889
11728
  }
10890
11729
  if (process.platform === "darwin") darwinQuit2();
10891
- else winQuitGraceful2();
11730
+ else if (process.platform === "win32") winQuitGraceful2();
11731
+ else if (process.platform === "linux") linuxQuit();
10892
11732
  if (!await waitForQuit2(5e3)) {
10893
11733
  if (process.platform === "win32") winForceQuit2();
11734
+ else if (process.platform === "linux") linuxForceQuit();
10894
11735
  await waitForQuit2(5e3);
10895
11736
  }
10896
11737
  if (appPath) openClaudeAppAt(appPath);
@@ -11070,6 +11911,13 @@ export {
11070
11911
  resolveLocalProviderApiKey,
11071
11912
  formatRegistryAuthLabel,
11072
11913
  resolveProvidersForDisplay,
11914
+ evaluateAgySwitchCompatibility,
11915
+ resolveRelayCatalogSlots,
11916
+ injectRelayModels,
11917
+ buildAntigravityRoutes,
11918
+ buildListModelConfigsResponse,
11919
+ buildListExperimentsResponse,
11920
+ meetsContextFloor,
11073
11921
  routableModelsForTarget,
11074
11922
  providersForTarget,
11075
11923
  refreshProviderModels,
@@ -11111,4 +11959,4 @@ export {
11111
11959
  supportsClaudeTransparentMode,
11112
11960
  buildHttpProxyRoutes
11113
11961
  };
11114
- //# sourceMappingURL=chunk-IYYLLN5T.js.map
11962
+ //# sourceMappingURL=chunk-6CBNKM55.js.map