@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.
package/dist/cli.js CHANGED
@@ -17,6 +17,7 @@ import {
17
17
  appendCodexBodyDump,
18
18
  authenticateProvider,
19
19
  buildAntigravityChildEnv,
20
+ buildAntigravityRoutes,
20
21
  buildAppCatalogFile,
21
22
  buildCatalogFile,
22
23
  buildCatalogRoutes,
@@ -25,6 +26,8 @@ import {
25
26
  buildCodexAppRootConfig,
26
27
  buildHttpProxyRoutes,
27
28
  buildImportProviderList,
29
+ buildListExperimentsResponse,
30
+ buildListModelConfigsResponse,
28
31
  buildVertexRuntimeConfig,
29
32
  cachedModelToLocal,
30
33
  catalogEntryFromModel,
@@ -42,6 +45,7 @@ import {
42
45
  effectiveProviderBaseUrl,
43
46
  effortProviderOptions,
44
47
  encodeToolUseId,
48
+ evaluateAgySwitchCompatibility,
45
49
  extractApiKey,
46
50
  favoriteProviderDisplayName,
47
51
  fetchAnthropicModels,
@@ -73,6 +77,7 @@ import {
73
77
  hasApplicationDefaultCredentials,
74
78
  httpProxyModelId,
75
79
  injectClaudeIdentity,
80
+ injectRelayModels,
76
81
  isClaudeAppRunning,
77
82
  isCodexAppRunning,
78
83
  isFreeStatus,
@@ -92,6 +97,7 @@ import {
92
97
  makeRouteResolver,
93
98
  makeTraceLogger,
94
99
  maxToolsForNpm,
100
+ meetsContextFloor,
95
101
  migrateGlobalOpencodeCredential,
96
102
  migrateLegacyCloudProviders,
97
103
  modelSelectOption,
@@ -137,6 +143,7 @@ import {
137
143
  resolveProviderTemplate,
138
144
  resolveProvidersForDisplay,
139
145
  resolveRefreshCredential,
146
+ resolveRelayCatalogSlots,
140
147
  routableModelsForTarget,
141
148
  routeLookupIds,
142
149
  runServerCommand,
@@ -165,14 +172,14 @@ import {
165
172
  validateCustomEndpointUrl,
166
173
  writeSecureLogLine,
167
174
  zenRegistryStub
168
- } from "./chunk-IYYLLN5T.js";
175
+ } from "./chunk-6CBNKM55.js";
169
176
  import {
170
177
  filterTemplates,
171
178
  init_provider_templates,
172
179
  listAddableTemplates,
173
180
  listSupportedTemplates,
174
181
  listVisibleOAuthTemplates
175
- } from "./chunk-EJONCU3B.js";
182
+ } from "./chunk-HXGZ4CTV.js";
176
183
 
177
184
  // src/cli.ts
178
185
  import pc12 from "picocolors";
@@ -1538,7 +1545,16 @@ async function runTemplateAddFlow() {
1538
1545
  ]);
1539
1546
  }
1540
1547
  let baseUrlOverride;
1541
- if (template.urlPrompt) {
1548
+ if (template.accountIdPrompt) {
1549
+ const accountInput = await p5.text({
1550
+ message: template.accountIdPrompt,
1551
+ placeholder: "e.g. 4ff191dac2d0bd7538cb1c9126594de3",
1552
+ validate: (v) => v.trim() ? void 0 : "Account ID is required"
1553
+ });
1554
+ if (p5.isCancel(accountInput)) return 0;
1555
+ const accountId = String(accountInput).trim();
1556
+ baseUrlOverride = template.defaultBaseUrl?.replace("{ACCOUNT_ID}", accountId);
1557
+ } else if (template.urlPrompt) {
1542
1558
  const urlInput = await p5.text({
1543
1559
  message: template.urlPrompt,
1544
1560
  initialValue: template.defaultBaseUrl,
@@ -6468,655 +6484,6 @@ function formatCloudCodeChunk(opts) {
6468
6484
  };
6469
6485
  }
6470
6486
 
6471
- // src/antigravity/slot-registry.ts
6472
- var AGY_SLOT_VALIDATION_SOURCE = "AGY CLI 1.0.10 / Antigravity IDE 2.1.1 fixture capture 2026-06-23";
6473
- var AGY_NATIVE_SLOT_REGISTRY = [
6474
- {
6475
- slotId: "gemini-3.5-flash-low",
6476
- model: "MODEL_PLACEHOLDER_M20",
6477
- role: "agent-switch",
6478
- status: "validated",
6479
- validatedWith: AGY_SLOT_VALIDATION_SOURCE
6480
- },
6481
- {
6482
- slotId: "gemini-3.5-flash-extra-low",
6483
- model: "MODEL_PLACEHOLDER_M187",
6484
- role: "agent-switch",
6485
- status: "validated",
6486
- validatedWith: AGY_SLOT_VALIDATION_SOURCE
6487
- },
6488
- {
6489
- slotId: "gemini-3.1-pro-low",
6490
- model: "MODEL_PLACEHOLDER_M36",
6491
- role: "agent-switch",
6492
- status: "validated",
6493
- validatedWith: AGY_SLOT_VALIDATION_SOURCE
6494
- },
6495
- {
6496
- slotId: "gemini-pro-agent",
6497
- model: "MODEL_PLACEHOLDER_M16",
6498
- role: "agent-switch",
6499
- status: "validated",
6500
- validatedWith: AGY_SLOT_VALIDATION_SOURCE
6501
- },
6502
- {
6503
- slotId: "claude-sonnet-4-6",
6504
- model: "MODEL_PLACEHOLDER_M35",
6505
- role: "agent-switch",
6506
- status: "validated",
6507
- validatedWith: AGY_SLOT_VALIDATION_SOURCE
6508
- },
6509
- {
6510
- slotId: "claude-opus-4-6-thinking",
6511
- model: "MODEL_PLACEHOLDER_M26",
6512
- role: "agent-switch",
6513
- status: "validated",
6514
- validatedWith: AGY_SLOT_VALIDATION_SOURCE
6515
- },
6516
- {
6517
- slotId: "gpt-oss-120b-medium",
6518
- model: "MODEL_OPENAI_GPT_OSS_120B_MEDIUM",
6519
- role: "agent-switch",
6520
- status: "validated",
6521
- validatedWith: AGY_SLOT_VALIDATION_SOURCE
6522
- },
6523
- {
6524
- slotId: "gemini-3-flash-agent",
6525
- model: "MODEL_PLACEHOLDER_M132",
6526
- role: "cascade-plan",
6527
- status: "reserved",
6528
- validatedWith: AGY_SLOT_VALIDATION_SOURCE,
6529
- notes: "Visible in agentModelSorts, but reserved for cascade plan construction."
6530
- },
6531
- {
6532
- slotId: "gemini-2.5-flash",
6533
- model: "MODEL_GOOGLE_GEMINI_2_5_FLASH",
6534
- role: "cascade-intent",
6535
- status: "reserved",
6536
- validatedWith: AGY_SLOT_VALIDATION_SOURCE
6537
- },
6538
- {
6539
- slotId: "gemini-2.5-flash-lite",
6540
- model: "MODEL_GOOGLE_GEMINI_2_5_FLASH_LITE",
6541
- role: "cascade-fallback",
6542
- status: "reserved",
6543
- validatedWith: AGY_SLOT_VALIDATION_SOURCE
6544
- },
6545
- {
6546
- slotId: "gemini-3.1-pro-high",
6547
- model: "MODEL_PLACEHOLDER_M37",
6548
- role: "agent-switch",
6549
- status: "candidate",
6550
- validatedWith: AGY_SLOT_VALIDATION_SOURCE,
6551
- notes: "Model-shaped fixture entry; requires live switching proof before promotion."
6552
- },
6553
- {
6554
- slotId: "gemini-2.5-pro",
6555
- model: "MODEL_GOOGLE_GEMINI_2_5_PRO",
6556
- role: "agent-switch",
6557
- status: "candidate",
6558
- validatedWith: AGY_SLOT_VALIDATION_SOURCE,
6559
- notes: "Model-shaped fixture entry; requires live switching proof before promotion."
6560
- },
6561
- {
6562
- slotId: "gemini-2.5-flash-thinking",
6563
- model: "MODEL_GOOGLE_GEMINI_2_5_FLASH_THINKING",
6564
- role: "agent-switch",
6565
- status: "candidate",
6566
- validatedWith: AGY_SLOT_VALIDATION_SOURCE,
6567
- notes: "Model-shaped fixture entry; requires live switching proof before promotion."
6568
- },
6569
- {
6570
- slotId: "gemini-3-flash",
6571
- model: "MODEL_PLACEHOLDER_M18",
6572
- role: "command",
6573
- status: "candidate",
6574
- validatedWith: AGY_SLOT_VALIDATION_SOURCE,
6575
- notes: "Command model in the fixture; not switch-safe without live proof."
6576
- },
6577
- {
6578
- slotId: "gemini-3.1-flash-lite",
6579
- model: "MODEL_PLACEHOLDER_M50",
6580
- role: "cascade-checkpoint",
6581
- status: "candidate",
6582
- validatedWith: AGY_SLOT_VALIDATION_SOURCE,
6583
- notes: "Checkpoint/search/commit slot; route as helper until live proof exists."
6584
- },
6585
- {
6586
- slotId: "gemini-3.1-flash-image",
6587
- model: "MODEL_PLACEHOLDER_M21",
6588
- role: "image",
6589
- status: "candidate",
6590
- validatedWith: AGY_SLOT_VALIDATION_SOURCE,
6591
- notes: "Image generation slot; not switch-safe without live proof."
6592
- },
6593
- {
6594
- slotId: "tab_jump_flash_lite_preview",
6595
- model: "MODEL_PLACEHOLDER_M28",
6596
- role: "tab",
6597
- status: "unsafe",
6598
- validatedWith: AGY_SLOT_VALIDATION_SOURCE
6599
- },
6600
- {
6601
- slotId: "tab_flash_lite_preview",
6602
- model: "MODEL_PLACEHOLDER_M19",
6603
- role: "tab",
6604
- status: "unsafe",
6605
- validatedWith: AGY_SLOT_VALIDATION_SOURCE
6606
- },
6607
- {
6608
- slotId: "chat_20706",
6609
- model: "MODEL_CHAT_20706",
6610
- role: "chat",
6611
- status: "unsafe",
6612
- validatedWith: AGY_SLOT_VALIDATION_SOURCE
6613
- },
6614
- {
6615
- slotId: "chat_23310",
6616
- model: "MODEL_CHAT_23310",
6617
- role: "chat",
6618
- status: "unsafe",
6619
- validatedWith: AGY_SLOT_VALIDATION_SOURCE
6620
- }
6621
- ];
6622
- var KNOWN_COMPATIBLE_AGY_VERSIONS = /* @__PURE__ */ new Set(["1.0.10"]);
6623
- var KNOWN_INCOMPATIBLE_AGY_VERSIONS = /* @__PURE__ */ new Set(["1.0.9"]);
6624
- function withFixtureModel(definition, model) {
6625
- return model === definition.model ? definition : { ...definition, model };
6626
- }
6627
- function assertNoDuplicateSwitchEnums(fixture, definitions) {
6628
- const seen = /* @__PURE__ */ new Map();
6629
- for (const definition of definitions) {
6630
- if (definition.status !== "validated") continue;
6631
- const actualModel = fixture.models[definition.slotId]?.model;
6632
- if (!actualModel) continue;
6633
- const previousSlotId = seen.get(actualModel);
6634
- if (previousSlotId) {
6635
- throw new Error(
6636
- `Duplicate AGY switch slot enum ${actualModel}: ${previousSlotId} and ${definition.slotId}`
6637
- );
6638
- }
6639
- seen.set(actualModel, definition.slotId);
6640
- }
6641
- }
6642
- function validateAgySlotRegistry(fixture) {
6643
- assertNoDuplicateSwitchEnums(fixture, AGY_NATIVE_SLOT_REGISTRY);
6644
- const switchSlots = [];
6645
- const reservedSlots = [];
6646
- const candidateSlots = [];
6647
- const warnings = [];
6648
- for (const definition of AGY_NATIVE_SLOT_REGISTRY) {
6649
- const entry = fixture.models[definition.slotId];
6650
- if (!entry) {
6651
- if (definition.status === "validated" || definition.status === "reserved") {
6652
- warnings.push(`AGY slot ${definition.slotId} missing from fixture`);
6653
- }
6654
- continue;
6655
- }
6656
- if (entry.model !== definition.model) {
6657
- warnings.push(
6658
- `AGY slot ${definition.slotId} expected ${definition.model} but fixture has ${entry.model}`
6659
- );
6660
- continue;
6661
- }
6662
- if (definition.status === "validated") {
6663
- switchSlots.push(withFixtureModel(definition, entry.model));
6664
- } else if (definition.status === "reserved") {
6665
- reservedSlots.push(withFixtureModel(definition, entry.model));
6666
- } else if (definition.status === "candidate") {
6667
- candidateSlots.push(withFixtureModel(definition, entry.model));
6668
- }
6669
- }
6670
- return { switchSlots, reservedSlots, candidateSlots, warnings };
6671
- }
6672
- function getValidatedAgySwitchSlots(fixture) {
6673
- return validateAgySlotRegistry(fixture).switchSlots;
6674
- }
6675
- function evaluateAgySwitchCompatibility(opts) {
6676
- const validation = validateAgySlotRegistry(opts.fixture);
6677
- const shapeMatches = validation.warnings.length === 0 && validation.switchSlots.length > 0;
6678
- const warnings = [];
6679
- if (opts.versionReadError) {
6680
- warnings.push(`Could not read agy --version (${opts.versionReadError}); validating AGY fixture shape instead.`);
6681
- }
6682
- if (opts.version && KNOWN_INCOMPATIBLE_AGY_VERSIONS.has(opts.version)) {
6683
- return {
6684
- mode: "single-model",
6685
- validatedSwitchSlotCount: validation.switchSlots.length,
6686
- warnings: [
6687
- ...warnings,
6688
- `Known-incompatible AGY version ${opts.version}; falling back to single-model mode.`
6689
- ]
6690
- };
6691
- }
6692
- if (!shapeMatches) {
6693
- return {
6694
- mode: "single-model",
6695
- validatedSwitchSlotCount: validation.switchSlots.length,
6696
- warnings: [
6697
- ...warnings,
6698
- ...validation.warnings,
6699
- "AGY fixture shape does not match the validated slot registry; falling back to single-model mode."
6700
- ]
6701
- };
6702
- }
6703
- if (opts.version && !KNOWN_COMPATIBLE_AGY_VERSIONS.has(opts.version)) {
6704
- warnings.push(`Unvalidated AGY version ${opts.version}; fixture shape matches, so multi-model switching remains enabled.`);
6705
- } else if (!opts.version && !opts.versionReadError) {
6706
- warnings.push("AGY version is unknown; fixture shape matches, so multi-model switching remains enabled.");
6707
- }
6708
- return {
6709
- mode: "multi-model",
6710
- validatedSwitchSlotCount: validation.switchSlots.length,
6711
- warnings
6712
- };
6713
- }
6714
-
6715
- // src/antigravity/catalog.ts
6716
- var RELAY_CASCADE_PLAN_MODEL = "MODEL_PLACEHOLDER_M132";
6717
- var RELAY_AGENT_PLACEHOLDER = "MODEL_PLACEHOLDER_M20";
6718
- var RELAY_CASCADE_CHECKPOINT_MODEL = "MODEL_PLACEHOLDER_M50";
6719
- var RELAY_CASCADE_INTENT_MODEL = "MODEL_GOOGLE_GEMINI_2_5_FLASH";
6720
- var RELAY_CASCADE_ANCHOR_ID = "gemini-3.5-flash-low";
6721
- var RELAY_CASCADE_PLAN_ANCHOR_ID = "gemini-3-flash-agent";
6722
- var RELAY_CASCADE_FALLBACK_ID = "gemini-2.5-flash-lite";
6723
- var RELAY_CASCADE_INTENT_MODEL_ID = "gemini-2.5-flash";
6724
- function withCascadeCheckpointer(entry, maxTokenLimit = 128e3) {
6725
- const tokenThreshold = Math.min(5e4, Math.floor(maxTokenLimit * 0.75));
6726
- const existingModelExperiments = entry.modelExperiments;
6727
- entry.modelExperiments = {
6728
- ...existingModelExperiments,
6729
- experiments: {
6730
- ...existingModelExperiments?.experiments ?? {},
6731
- CASCADE_USE_EXPERIMENT_CHECKPOINTER: {
6732
- stringValue: JSON.stringify({
6733
- strategy: "CHECKPOINT_STRATEGY_SAME_MODEL",
6734
- max_token_limit: String(maxTokenLimit),
6735
- token_threshold: String(tokenThreshold),
6736
- max_overhead_ratio: "0.15",
6737
- moving_window_size: "1",
6738
- enabled: true,
6739
- max_output_tokens: "16384",
6740
- checkpoint_model: RELAY_CASCADE_CHECKPOINT_MODEL,
6741
- use_last_planner_model: true,
6742
- is_sync: true,
6743
- max_user_requests: 10,
6744
- include_last_user_message: true,
6745
- include_conversation_log: false,
6746
- include_running_task_snapshots: true,
6747
- include_subagent_snapshots: true,
6748
- include_artifact_snapshots: true,
6749
- retry_config: {
6750
- max_retries: 0,
6751
- initial_sleep_duration_ms: 1e3,
6752
- exponential_multiplier: 2,
6753
- include_error_feedback: false
6754
- }
6755
- })
6756
- }
6757
- }
6758
- };
6759
- return entry;
6760
- }
6761
- function applyRouteContextBounds(entry, route) {
6762
- const maxTokenLimit = route.contextWindow ?? 128e3;
6763
- const maxOutputTokens = Math.min(entry.maxOutputTokens ?? 65536, maxTokenLimit);
6764
- const checkpointTokenLimit = Math.min(
6765
- 128e3,
6766
- Math.max(1, maxTokenLimit - maxOutputTokens)
6767
- );
6768
- entry.maxTokens = maxTokenLimit;
6769
- entry.maxOutputTokens = maxOutputTokens;
6770
- return withCascadeCheckpointer(entry, checkpointTokenLimit);
6771
- }
6772
- var RELAY_CASCADE_FALLBACK_ENTRY = withCascadeCheckpointer({
6773
- displayName: "Gemini 3.1 Flash Lite",
6774
- model: "MODEL_GOOGLE_GEMINI_2_5_FLASH_LITE",
6775
- apiProvider: "API_PROVIDER_GOOGLE_GEMINI",
6776
- modelProvider: "MODEL_PROVIDER_GOOGLE",
6777
- tokenizerType: "LLAMA_WITH_SPECIAL",
6778
- maxTokens: 1048576,
6779
- maxOutputTokens: 65535,
6780
- quotaInfo: { remainingFraction: 1 }
6781
- });
6782
- var RELAY_CASCADE_INTENT_MODEL_ENTRY = withCascadeCheckpointer({
6783
- ...RELAY_CASCADE_FALLBACK_ENTRY,
6784
- model: RELAY_CASCADE_INTENT_MODEL
6785
- });
6786
- function planRelayCatalogSlots(catalog, routes, templateKey) {
6787
- const validation = validateAgySlotRegistry(catalog);
6788
- const switchSlots = getValidatedAgySwitchSlots(catalog);
6789
- const templateSlot = switchSlots.find((slot) => slot.slotId === templateKey);
6790
- const orderedSlots = templateSlot ? [templateSlot, ...switchSlots.filter((slot) => slot.slotId !== templateKey)] : switchSlots;
6791
- if (routes.length > 0 && orderedSlots.length === 0) {
6792
- throw new Error("No validated AGY switch slots are available for the selected launch route");
6793
- }
6794
- const switchableRoutes = routes.slice(0, orderedSlots.length);
6795
- const skippedRoutes = routes.slice(orderedSlots.length);
6796
- const slots = switchableRoutes.map((route, index) => ({
6797
- slotId: orderedSlots[index].slotId,
6798
- route
6799
- }));
6800
- return {
6801
- slots,
6802
- switchableRoutes,
6803
- skippedRoutes,
6804
- validation
6805
- };
6806
- }
6807
- function resolveRelayCatalogSlots(catalog, routes, templateKey) {
6808
- return planRelayCatalogSlots(catalog, routes, templateKey).slots;
6809
- }
6810
- function buildRelayCatalogEntry(route, template) {
6811
- const entry = structuredClone(template);
6812
- entry.displayName = route.displayName;
6813
- entry.model = template.model ?? RELAY_AGENT_PLACEHOLDER;
6814
- entry.requestedModelId = route.catalogId;
6815
- entry.modelVersion = route.catalogId;
6816
- entry.modelVersionId = route.catalogId;
6817
- entry.quotaInfo = { remainingFraction: 1, resetTime: "2026-06-23T02:00:57Z" };
6818
- return applyRouteContextBounds(entry, route);
6819
- }
6820
- function buildRelayCatalogSlotEntry(route, template) {
6821
- const entry = structuredClone(template);
6822
- entry.displayName = route.displayName;
6823
- entry.quotaInfo = { remainingFraction: 1, resetTime: "2026-06-23T02:00:57Z" };
6824
- delete entry.requestedModelId;
6825
- delete entry.modelVersion;
6826
- delete entry.modelVersionId;
6827
- delete entry.isInternal;
6828
- return applyRouteContextBounds(entry, route);
6829
- }
6830
- function injectRelayModels(fixture, routes, templateKey) {
6831
- const result = structuredClone(fixture);
6832
- const template = fixture.models[templateKey];
6833
- if (!template) {
6834
- throw new Error(`Template model "${templateKey}" not found in catalog fixture`);
6835
- }
6836
- const seen = /* @__PURE__ */ new Set();
6837
- for (const route of routes) {
6838
- if (seen.has(route.catalogId)) {
6839
- throw new Error(`Catalog ID collision: ${route.catalogId}`);
6840
- }
6841
- if (fixture.models[route.catalogId]) {
6842
- throw new Error(`Catalog ID collision with native model: ${route.catalogId}`);
6843
- }
6844
- seen.add(route.catalogId);
6845
- }
6846
- if (routes.length > 0) {
6847
- result.models[RELAY_CASCADE_ANCHOR_ID] ??= structuredClone(template);
6848
- result.models[RELAY_CASCADE_FALLBACK_ID] ??= structuredClone(RELAY_CASCADE_FALLBACK_ENTRY);
6849
- result.models[RELAY_CASCADE_INTENT_MODEL_ID] ??= structuredClone(RELAY_CASCADE_INTENT_MODEL_ENTRY);
6850
- if (!result.models[RELAY_CASCADE_PLAN_ANCHOR_ID]) {
6851
- const planAnchor = withCascadeCheckpointer(structuredClone(template));
6852
- planAnchor.model = RELAY_CASCADE_PLAN_MODEL;
6853
- result.models[RELAY_CASCADE_PLAN_ANCHOR_ID] = planAnchor;
6854
- }
6855
- const slotPlan = planRelayCatalogSlots(result, routes, templateKey);
6856
- const slots = slotPlan.slots;
6857
- for (const { slotId, route } of slots) {
6858
- const slotTemplate = result.models[slotId] ?? template;
6859
- if (result.models[slotId]) {
6860
- result.models[slotId] = buildRelayCatalogSlotEntry(route, slotTemplate);
6861
- }
6862
- result.models[route.catalogId] = buildRelayCatalogEntry(route, slotTemplate);
6863
- }
6864
- result.defaultAgentModelId = slots[0]?.slotId ?? RELAY_CASCADE_ANCHOR_ID;
6865
- result.agentModelSorts = [
6866
- {
6867
- displayName: "Recommended",
6868
- groups: [{
6869
- modelIds: slots.map((slot) => slot.slotId)
6870
- }]
6871
- }
6872
- ];
6873
- return result;
6874
- }
6875
- if (!result.agentModelSorts?.[0]?.groups?.[0]) {
6876
- result.agentModelSorts = [
6877
- {
6878
- displayName: "Recommended",
6879
- groups: [{ modelIds: [] }]
6880
- }
6881
- ];
6882
- }
6883
- return result;
6884
- }
6885
- function buildAntigravityRoutes(resolvedFavorites, maxRoutes = MAX_MODEL_CATALOG) {
6886
- const routes = [];
6887
- const seen = /* @__PURE__ */ new Set();
6888
- for (const fav of resolvedFavorites) {
6889
- if (routes.length >= maxRoutes) break;
6890
- const favModel = fav.model;
6891
- const modelId = favModel.id;
6892
- const catalogId = `relay-ai__${fav.providerId}__${modelId}`;
6893
- if (seen.has(catalogId)) continue;
6894
- seen.add(catalogId);
6895
- const npm = favModel.npm || "@ai-sdk/openai-compatible";
6896
- const upstreamModelId = favModel.upstreamModelId || modelId;
6897
- const baseURL = favModel.apiBaseUrl || favModel.completionsUrl || void 0;
6898
- const contextWindow = favModel.contextWindow;
6899
- const modelFormat = favModel.modelFormat;
6900
- routes.push({
6901
- catalogId,
6902
- providerId: fav.providerId,
6903
- providerName: fav.providerName,
6904
- modelId,
6905
- upstreamModelId,
6906
- displayName: `${favModel.name} (Relay)`,
6907
- ...modelFormat ? { modelFormat } : {},
6908
- npm,
6909
- apiKey: fav.apiKey,
6910
- ...fav.authType ? { authType: fav.authType } : {},
6911
- ...fav.oauthAccountId ? { oauthAccountId: fav.oauthAccountId } : {},
6912
- ...fav.providerData ? { providerData: fav.providerData } : {},
6913
- baseURL,
6914
- contextWindow
6915
- });
6916
- }
6917
- return applyUniqueAntigravityRouteLabels(routes);
6918
- }
6919
- function routeBaseModelName(route) {
6920
- const relayMatch = route.displayName.match(/^(.*) \(Relay(?: - .*)?\)$/);
6921
- return relayMatch?.[1] ?? route.displayName;
6922
- }
6923
- function authKindLabel(route) {
6924
- if (route.authType === "oauth") return "OAuth";
6925
- if (route.authType === "api") return "API key";
6926
- if (route.authType === "none") return "local";
6927
- return "provider";
6928
- }
6929
- function duplicateCounts(values) {
6930
- const counts = /* @__PURE__ */ new Map();
6931
- for (const value of values) {
6932
- counts.set(value, (counts.get(value) ?? 0) + 1);
6933
- }
6934
- return counts;
6935
- }
6936
- function assertUniqueRouteDisplayNames(routes) {
6937
- const counts = duplicateCounts(routes.map((route) => route.displayName));
6938
- const duplicate = [...counts.entries()].find(([, count]) => count > 1);
6939
- if (duplicate) {
6940
- throw new Error(`Duplicate AGY model label after disambiguation: ${duplicate[0]}`);
6941
- }
6942
- }
6943
- function applyUniqueAntigravityRouteLabels(routes) {
6944
- const baseNames = routes.map(routeBaseModelName);
6945
- const baseNameCounts = duplicateCounts(baseNames);
6946
- const upstreamCounts = duplicateCounts(routes.map((route) => route.upstreamModelId));
6947
- const providerNameCounts = duplicateCounts(routes.map((route) => route.providerName));
6948
- const labeled = routes.map((route, index) => {
6949
- const baseName = baseNames[index];
6950
- const needsSuffix = (baseNameCounts.get(baseName) ?? 0) > 1 || (upstreamCounts.get(route.upstreamModelId) ?? 0) > 1;
6951
- if (!needsSuffix) {
6952
- return { ...route, displayName: `${baseName} (Relay)` };
6953
- }
6954
- const providerName = route.providerName || route.providerId;
6955
- const providerSuffix = (providerNameCounts.get(providerName) ?? 0) > 1 ? `${providerName} ${authKindLabel(route)}` : providerName;
6956
- return {
6957
- ...route,
6958
- displayName: `${baseName} (Relay - ${providerSuffix})`
6959
- };
6960
- });
6961
- const firstPassCounts = duplicateCounts(labeled.map((route) => route.displayName));
6962
- const withProviderIds = labeled.map((route) => {
6963
- if ((firstPassCounts.get(route.displayName) ?? 0) <= 1) return route;
6964
- return {
6965
- ...route,
6966
- displayName: route.displayName.replace(/\)$/, ` - ${route.providerId})`)
6967
- };
6968
- });
6969
- assertUniqueRouteDisplayNames(withProviderIds);
6970
- return withProviderIds;
6971
- }
6972
- function routeLabels(routes) {
6973
- assertUniqueRouteDisplayNames(routes);
6974
- const labels = /* @__PURE__ */ new Map();
6975
- for (const route of routes) {
6976
- labels.set(route.catalogId, route.displayName);
6977
- }
6978
- return labels;
6979
- }
6980
- function buildClientModelConfigData(routes, catalog, templateKey = RELAY_CASCADE_ANCHOR_ID, precomputedSlots) {
6981
- const catalogRoutes = routes.slice(0, MAX_MODEL_CATALOG);
6982
- const slots = precomputedSlots ?? (catalog ? resolveRelayCatalogSlots(catalog, catalogRoutes, templateKey) : catalogRoutes.map((route) => ({ slotId: route.catalogId, route })));
6983
- const labels = routeLabels(catalogRoutes);
6984
- const clientModelConfigs = slots.map(({ slotId, route }) => {
6985
- const entry = catalog?.models[slotId] ?? catalog?.models[route.catalogId] ?? catalog?.models[RELAY_CASCADE_ANCHOR_ID];
6986
- const label = labels.get(route.catalogId) ?? route.displayName;
6987
- return {
6988
- label,
6989
- modelOrAlias: {
6990
- alias: slotId,
6991
- choice: { case: "alias", value: slotId }
6992
- },
6993
- disabled: false,
6994
- supportedMimeTypes: entry?.supportedMimeTypes ?? {},
6995
- quotaInfo: entry?.quotaInfo ?? { remainingFraction: 1 },
6996
- tagTitle: entry?.tagTitle,
6997
- tagDescription: entry?.tagDescription,
6998
- supportsThoughtCirculation: entry?.supportsThoughtCirculation ?? false
6999
- };
7000
- });
7001
- return {
7002
- clientModelConfigs,
7003
- clientModelSorts: [
7004
- {
7005
- name: "Recommended",
7006
- groups: [
7007
- {
7008
- groupName: "",
7009
- modelLabels: clientModelConfigs.map((config) => config.label)
7010
- }
7011
- ]
7012
- }
7013
- ],
7014
- defaultOverrideModelConfig: clientModelConfigs[0] ?? {}
7015
- };
7016
- }
7017
- function buildListModelConfigsResponse(routes, catalog, templateKey = RELAY_CASCADE_ANCHOR_ID) {
7018
- const catalogRoutes = routes.slice(0, MAX_MODEL_CATALOG);
7019
- const slots = catalog ? resolveRelayCatalogSlots(catalog, catalogRoutes, templateKey) : catalogRoutes.map((route) => ({ slotId: route.catalogId, route }));
7020
- const config = slots.map(({ slotId }) => ({
7021
- requestedModelId: slotId,
7022
- planModel: RELAY_CASCADE_PLAN_MODEL,
7023
- requestedModel: catalog?.models[slotId]?.model ?? RELAY_AGENT_PLACEHOLDER
7024
- }));
7025
- return {
7026
- ...buildClientModelConfigData(routes, catalog, templateKey, slots),
7027
- allowedModelConfigs: config,
7028
- defaultAgentModelConfig: config[0] ?? {}
7029
- };
7030
- }
7031
- var CURRENT_EXPERIMENT_IDS = [
7032
- 105979552,
7033
- 105979574,
7034
- 106015351,
7035
- 105979579,
7036
- 105867471,
7037
- 105979530,
7038
- 105995634,
7039
- 106121401,
7040
- 106100625,
7041
- 104638466,
7042
- 101868197,
7043
- 104817729,
7044
- 105695344,
7045
- 106064591,
7046
- 104913215,
7047
- 106324349,
7048
- 106309078,
7049
- 105821930,
7050
- 104922093,
7051
- 103012598,
7052
- 106143956,
7053
- 105856899,
7054
- 106312323,
7055
- 106064030,
7056
- 105746183,
7057
- 105757908,
7058
- 104892493,
7059
- 105822886,
7060
- 105785683,
7061
- 105721273,
7062
- 105897325,
7063
- 105658071,
7064
- 106240758,
7065
- 105943702,
7066
- 106106760,
7067
- 106283618,
7068
- 105620019,
7069
- 106038160,
7070
- 106309520,
7071
- 106281951,
7072
- 106264532,
7073
- 106222835,
7074
- 106094629,
7075
- 105887313,
7076
- 105849474,
7077
- 106032303,
7078
- 106228452,
7079
- 106113900,
7080
- 106121607,
7081
- 105979531,
7082
- 105979553,
7083
- 106015328,
7084
- 105867469,
7085
- 105979517,
7086
- 106121399,
7087
- 106100654,
7088
- 104638459,
7089
- 101551624,
7090
- 104673683,
7091
- 105695346,
7092
- 106064590,
7093
- 104913210,
7094
- 105821928,
7095
- 104922082,
7096
- 103012592,
7097
- 106064028,
7098
- 105746181,
7099
- 104892490,
7100
- 105822881,
7101
- 105721268,
7102
- 105895316,
7103
- 105658068,
7104
- 106240748,
7105
- 105943694,
7106
- 106283614,
7107
- 105620012,
7108
- 106038153,
7109
- 105887311,
7110
- 106032301,
7111
- 106113877,
7112
- 106121604
7113
- ];
7114
- function buildListExperimentsResponse() {
7115
- return {
7116
- experimentIds: [...CURRENT_EXPERIMENT_IDS]
7117
- };
7118
- }
7119
-
7120
6487
  // src/antigravity/fixtures/loadCodeAssist.json
7121
6488
  var loadCodeAssist_default = {
7122
6489
  currentTier: {
@@ -8551,6 +7918,22 @@ function respondJson(res, status, data) {
8551
7918
  });
8552
7919
  res.end(body);
8553
7920
  }
7921
+ function parsePseudoToolCall(text4, knownToolNames) {
7922
+ const trimmed = text4.trim();
7923
+ if (!trimmed.startsWith("{") || !trimmed.endsWith("}")) return null;
7924
+ try {
7925
+ const obj = JSON.parse(trimmed);
7926
+ if (obj && typeof obj === "object") {
7927
+ const name = obj.name || obj.type === "function" && typeof obj.function === "object" && obj.function?.name || obj.function_name;
7928
+ if (typeof name === "string" && knownToolNames.has(name)) {
7929
+ const args = obj.parameters || obj.arguments || obj.args || typeof obj.function === "object" && obj.function?.parameters || {};
7930
+ return { name, args: typeof args === "object" && args ? args : {} };
7931
+ }
7932
+ }
7933
+ } catch {
7934
+ }
7935
+ return null;
7936
+ }
8554
7937
  async function handleStreamingRequest(res, route, providerOptions, parsed, log14, options = {}) {
8555
7938
  const sdkParams = applyClaudeCodeOAuthIdentity(route, translateRequest(parsed, {
8556
7939
  ...options.requestOptions,
@@ -8589,8 +7972,39 @@ async function handleStreamingRequest(res, route, providerOptions, parsed, log14
8589
7972
  };
8590
7973
  const thinkFilter = createThinkFilter();
8591
7974
  const toolCallBuffers = /* @__PURE__ */ new Map();
7975
+ const knownToolNames = new Set(Object.keys(sdkParams.tools ?? {}));
8592
7976
  let responseReasoning = "";
8593
7977
  let sawToolCall = false;
7978
+ let textBuffer = "";
7979
+ let bufferingJsonText = false;
7980
+ const flushBufferedText = () => {
7981
+ if (!textBuffer) return;
7982
+ startSse();
7983
+ const chunk = formatCloudCodeChunk({
7984
+ text: textBuffer,
7985
+ modelVersion: route.catalogId,
7986
+ responseId
7987
+ });
7988
+ res.write(`data: ${JSON.stringify(chunk)}
7989
+
7990
+ `);
7991
+ textBuffer = "";
7992
+ bufferingJsonText = false;
7993
+ };
7994
+ const emitPseudoToolCall = (pseudoTool) => {
7995
+ sawToolCall = true;
7996
+ startSse();
7997
+ const chunk = formatCloudCodeChunk({
7998
+ functionCall: { name: pseudoTool.name, args: normalizeFunctionCallArgs(pseudoTool.args) },
7999
+ modelVersion: route.catalogId,
8000
+ responseId
8001
+ });
8002
+ res.write(`data: ${JSON.stringify(chunk)}
8003
+
8004
+ `);
8005
+ textBuffer = "";
8006
+ bufferingJsonText = false;
8007
+ };
8594
8008
  for await (const part of fullStream) {
8595
8009
  const p15 = part;
8596
8010
  if (p15.type === "reasoning-delta" || p15.type === "reasoning") {
@@ -8607,15 +8021,27 @@ async function handleStreamingRequest(res, route, providerOptions, parsed, log14
8607
8021
  }
8608
8022
  if (text4) {
8609
8023
  log14(`[gateway] text-delta: ${JSON.stringify(text4.slice(0, 500))}`);
8610
- startSse();
8611
- const chunk = formatCloudCodeChunk({
8612
- text: text4,
8613
- modelVersion: route.catalogId,
8614
- responseId
8615
- });
8616
- res.write(`data: ${JSON.stringify(chunk)}
8024
+ if (!bufferingJsonText && (textBuffer + text4).trimStart().startsWith("{")) {
8025
+ bufferingJsonText = true;
8026
+ }
8027
+ if (bufferingJsonText) {
8028
+ textBuffer += text4;
8029
+ const pseudoTool = textBuffer.trimEnd().endsWith("}") ? parsePseudoToolCall(textBuffer, knownToolNames) : null;
8030
+ if (pseudoTool) {
8031
+ log14(`[gateway] parsed pseudo tool-call from text: ${pseudoTool.name}`);
8032
+ emitPseudoToolCall(pseudoTool);
8033
+ }
8034
+ } else {
8035
+ startSse();
8036
+ const chunk = formatCloudCodeChunk({
8037
+ text: text4,
8038
+ modelVersion: route.catalogId,
8039
+ responseId
8040
+ });
8041
+ res.write(`data: ${JSON.stringify(chunk)}
8617
8042
 
8618
8043
  `);
8044
+ }
8619
8045
  }
8620
8046
  } else if (p15.type === "tool-input-start") {
8621
8047
  const id = p15.id ?? p15.toolCallId;
@@ -8647,6 +8073,15 @@ async function handleStreamingRequest(res, route, providerOptions, parsed, log14
8647
8073
  `);
8648
8074
  } else if (p15.type === "finish") {
8649
8075
  log14(`[gateway] finish: ${p15.finishReason ?? "unknown"}`);
8076
+ if (textBuffer) {
8077
+ const pseudoTool = parsePseudoToolCall(textBuffer, knownToolNames);
8078
+ if (pseudoTool) {
8079
+ log14(`[gateway] parsed pseudo tool-call on finish: ${pseudoTool.name}`);
8080
+ emitPseudoToolCall(pseudoTool);
8081
+ } else {
8082
+ flushBufferedText();
8083
+ }
8084
+ }
8650
8085
  startSse();
8651
8086
  const reason = mapFinishReason2(p15.finishReason ?? "");
8652
8087
  const chunk = formatCloudCodeChunk({
@@ -8664,6 +8099,7 @@ async function handleStreamingRequest(res, route, providerOptions, parsed, log14
8664
8099
  } else if (p15.type === "error") {
8665
8100
  const message = formatUpstreamError(p15.error);
8666
8101
  log14(`[gateway] stream provider error: ${message}`);
8102
+ flushBufferedText();
8667
8103
  emitStreamError(res, route, responseId, message, startSse);
8668
8104
  break;
8669
8105
  } else if (p15.type === "reasoning-start" || p15.type === "reasoning-end") {
@@ -8773,10 +8209,17 @@ async function resolveAntigravityLaunchRoutes(opts) {
8773
8209
  maxRoutes,
8774
8210
  { dropEmptyApiKey: true, trackCapacitySkipped: true }
8775
8211
  );
8212
+ const tooSmall = resolved.slice(1).filter(
8213
+ (entry) => !meetsContextFloor("antigravity", entry.model.contextWindow)
8214
+ );
8215
+ const launchable = resolved.filter((entry) => !tooSmall.includes(entry));
8776
8216
  return {
8777
- routes: buildAntigravityRoutes(resolved, maxRoutes),
8217
+ routes: buildAntigravityRoutes(launchable, maxRoutes),
8778
8218
  apiKey,
8779
- droppedFavorites,
8219
+ droppedFavorites: [
8220
+ ...droppedFavorites,
8221
+ ...tooSmall.map((entry) => ({ providerId: entry.providerId, modelId: entry.model.id }))
8222
+ ],
8780
8223
  capacitySkippedFavorites
8781
8224
  };
8782
8225
  }
@@ -8906,9 +8349,35 @@ function prepareIdeProfile(profileDir, gatewayUrl) {
8906
8349
  }
8907
8350
 
8908
8351
  // src/antigravity/launch-ide.ts
8352
+ var LINUX_APP_PROFILE_DIR = join7(homedir7(), ".relay-ai", "antigravity", "app-profile");
8353
+ var LINUX_IDE_PROFILE_DIR = join7(homedir7(), ".relay-ai", "antigravity", "profile");
8909
8354
  function sleep(ms) {
8910
8355
  return new Promise((resolve2) => setTimeout(resolve2, ms));
8911
8356
  }
8357
+ function linuxAntigravityBinary() {
8358
+ const candidates = [
8359
+ "/usr/share/antigravity/antigravity",
8360
+ "/opt/antigravity/antigravity",
8361
+ join7(homedir7(), ".local", "share", "antigravity", "antigravity")
8362
+ ];
8363
+ for (const candidate of candidates) {
8364
+ if (existsSync7(candidate)) return candidate;
8365
+ }
8366
+ return null;
8367
+ }
8368
+ function linuxKillByProfile(profileDir, signal) {
8369
+ const output = defaultProcessList();
8370
+ for (const line of output.split("\n")) {
8371
+ if (!line.includes(`--user-data-dir=${profileDir}`)) continue;
8372
+ const pid = Number.parseInt(line.trim().split(/\s+/)[0] ?? "", 10);
8373
+ if (Number.isFinite(pid) && pid > 0) {
8374
+ try {
8375
+ process.kill(pid, signal);
8376
+ } catch {
8377
+ }
8378
+ }
8379
+ }
8380
+ }
8912
8381
  function runPowerShell(script) {
8913
8382
  return execSync4(`powershell.exe -NoProfile -Command ${JSON.stringify(script)}`, {
8914
8383
  encoding: "utf8",
@@ -8944,9 +8413,10 @@ function winForceQuitProcess(exeName, profileDir) {
8944
8413
  }
8945
8414
  }
8946
8415
  function defaultProcessList() {
8947
- if (process.platform !== "darwin") return "";
8416
+ const psArgs = process.platform === "linux" ? ["-eo", "pid=,args="] : ["-axo", "pid=,command="];
8417
+ if (process.platform !== "darwin" && process.platform !== "linux") return "";
8948
8418
  try {
8949
- return execFileSync3("ps", ["-axo", "pid=,command="], {
8419
+ return execFileSync3("ps", psArgs, {
8950
8420
  encoding: "utf8",
8951
8421
  stdio: ["ignore", "pipe", "ignore"],
8952
8422
  maxBuffer: 1024 * 1024 * 4
@@ -8958,11 +8428,17 @@ function defaultProcessList() {
8958
8428
  function isAntigravityIdeRunning(profileDir, processList = defaultProcessList) {
8959
8429
  if (process.platform === "win32") return winIsProcessRunningForProfile("Antigravity IDE.exe", profileDir);
8960
8430
  const output = processList();
8431
+ if (process.platform === "linux") {
8432
+ return output.split("\n").some((line) => line.includes(`--user-data-dir=${profileDir}`));
8433
+ }
8961
8434
  return output.split("\n").some((line) => line.includes("Antigravity IDE.app") && line.includes(`--user-data-dir=${profileDir}`));
8962
8435
  }
8963
8436
  function isAntigravityAppRunning(profileDir, processList = defaultProcessList) {
8964
8437
  if (process.platform === "win32") return winIsProcessRunningForProfile("Antigravity.exe", profileDir);
8965
8438
  const output = processList();
8439
+ if (process.platform === "linux") {
8440
+ return output.split("\n").some((line) => line.includes(`--user-data-dir=${profileDir}`));
8441
+ }
8966
8442
  return output.split("\n").some((line) => line.includes("Antigravity.app") && line.includes(`--user-data-dir=${profileDir}`));
8967
8443
  }
8968
8444
  async function waitForAntigravityIdeQuit(profileDir, options = {}) {
@@ -8987,15 +8463,21 @@ async function waitForAntigravityAppQuit(profileDir, options = {}) {
8987
8463
  }
8988
8464
  function forceQuitAntigravityIde(profileDir) {
8989
8465
  if (process.platform === "win32") winForceQuitProcess("Antigravity IDE.exe", profileDir);
8466
+ else if (process.platform === "linux") linuxKillByProfile(profileDir, "SIGKILL");
8990
8467
  }
8991
8468
  function forceQuitAntigravityApp(profileDir) {
8992
8469
  if (process.platform === "win32") winForceQuitProcess("Antigravity.exe", profileDir);
8470
+ else if (process.platform === "linux") linuxKillByProfile(profileDir, "SIGKILL");
8993
8471
  }
8994
8472
  function quitAntigravityIdeGracefully() {
8995
8473
  if (process.platform === "win32") {
8996
8474
  winQuitProcess("Antigravity IDE.exe");
8997
8475
  return;
8998
8476
  }
8477
+ if (process.platform === "linux") {
8478
+ linuxKillByProfile(LINUX_IDE_PROFILE_DIR, "SIGTERM");
8479
+ return;
8480
+ }
8999
8481
  if (process.platform !== "darwin") return;
9000
8482
  try {
9001
8483
  execFileSync3("osascript", ["-e", 'tell application "Antigravity IDE" to quit'], {
@@ -9012,6 +8494,10 @@ function quitAntigravityAppGracefully() {
9012
8494
  winQuitProcess("Antigravity.exe");
9013
8495
  return;
9014
8496
  }
8497
+ if (process.platform === "linux") {
8498
+ linuxKillByProfile(LINUX_APP_PROFILE_DIR, "SIGTERM");
8499
+ return;
8500
+ }
9015
8501
  if (process.platform !== "darwin") return;
9016
8502
  try {
9017
8503
  execFileSync3("osascript", ["-e", 'tell application "Antigravity" to quit'], {
@@ -9031,6 +8517,7 @@ function findAntigravityAppBinary() {
9031
8517
  const winPath = join7(localAppData, "Programs", "Antigravity", "Antigravity.exe");
9032
8518
  return existsSync7(winPath) ? winPath : null;
9033
8519
  }
8520
+ if (process.platform === "linux") return linuxAntigravityBinary();
9034
8521
  if (process.platform !== "darwin") return null;
9035
8522
  const defaultPath = "/Applications/Antigravity.app/Contents/MacOS/Antigravity";
9036
8523
  if (existsSync7(defaultPath)) return defaultPath;
@@ -9046,6 +8533,7 @@ function findAntigravityIdeBinary() {
9046
8533
  const winPath = join7(localAppData, "Programs", "Antigravity IDE", "Antigravity IDE.exe");
9047
8534
  return existsSync7(winPath) ? winPath : null;
9048
8535
  }
8536
+ if (process.platform === "linux") return linuxAntigravityBinary();
9049
8537
  if (process.platform !== "darwin") return null;
9050
8538
  const defaultPath = "/Applications/Antigravity IDE.app/Contents/Resources/app/bin/antigravity-ide";
9051
8539
  if (existsSync7(defaultPath)) return defaultPath;
@@ -9063,8 +8551,8 @@ function launchAntigravityApp(env, profileDir, gatewayUrl, extraArgs) {
9063
8551
  };
9064
8552
  const binaryPath = findAntigravityAppBinary();
9065
8553
  if (!binaryPath) {
9066
- console.error('Antigravity app bundle not found at "/Applications/Antigravity.app".');
9067
- console.error("Please make sure Antigravity is installed on your Mac.");
8554
+ console.error("Antigravity app not found.");
8555
+ console.error("Please make sure Antigravity is installed.");
9068
8556
  settle(127);
9069
8557
  return;
9070
8558
  }
@@ -9074,7 +8562,12 @@ function launchAntigravityApp(env, profileDir, gatewayUrl, extraArgs) {
9074
8562
  ...extraArgs
9075
8563
  ];
9076
8564
  const child = spawn5(binaryPath, args, {
9077
- stdio: "inherit",
8565
+ // GUI app: don't inherit the terminal's stdio (its Electron logs would
8566
+ // corrupt relay's interactive prompts) and detach into its own process
8567
+ // group so a Ctrl+C meant for the relay gateway doesn't also kill the app
8568
+ // mid-render. Mirrors the Claude Desktop launcher.
8569
+ stdio: "ignore",
8570
+ detached: true,
9078
8571
  env
9079
8572
  });
9080
8573
  child.on("spawn", () => {
@@ -9093,8 +8586,8 @@ function launchAntigravityIde(env, profileDir, gatewayUrl, extraArgs) {
9093
8586
  return new Promise((resolve2) => {
9094
8587
  const binaryPath = findAntigravityIdeBinary();
9095
8588
  if (!binaryPath) {
9096
- console.error('Antigravity IDE app bundle not found at "/Applications/Antigravity IDE.app".');
9097
- console.error("Please make sure Antigravity IDE is installed on your Mac.");
8589
+ console.error("Antigravity IDE not found.");
8590
+ console.error("Please make sure Antigravity IDE is installed.");
9098
8591
  resolve2(127);
9099
8592
  return;
9100
8593
  }
@@ -9106,7 +8599,12 @@ function launchAntigravityIde(env, profileDir, gatewayUrl, extraArgs) {
9106
8599
  ...extraArgs
9107
8600
  ];
9108
8601
  const child = spawn5(binaryPath, args, {
9109
- stdio: "inherit",
8602
+ // GUI app: don't inherit the terminal's stdio (its Electron logs would
8603
+ // corrupt relay's interactive prompts) and detach into its own process
8604
+ // group so a Ctrl+C meant for the relay gateway doesn't also kill the app
8605
+ // mid-render. Mirrors the Claude Desktop launcher.
8606
+ stdio: "ignore",
8607
+ detached: true,
9110
8608
  env
9111
8609
  });
9112
8610
  child.on("exit", (code) => {
@@ -10530,6 +10028,9 @@ function getClaudeDesktopHome() {
10530
10028
  if (process.platform === "win32") {
10531
10029
  return join11(process.env.LOCALAPPDATA || join11(homedir9(), "AppData", "Local"), "Claude-3p");
10532
10030
  }
10031
+ if (process.platform === "linux") {
10032
+ return join11(process.env.XDG_CONFIG_HOME || join11(homedir9(), ".config"), "Claude-3p");
10033
+ }
10533
10034
  return join11(homedir9(), "Library", "Application Support", "Claude-3p");
10534
10035
  }
10535
10036
  function getConfigLibraryPath() {
@@ -10903,7 +10404,7 @@ ${pc11.bold("Description:")}
10903
10404
  Keep this terminal open while using Claude.
10904
10405
 
10905
10406
  ${pc11.bold("Platforms:")}
10906
- macOS and Windows. Linux is not supported.
10407
+ macOS, Windows, and Linux.
10907
10408
 
10908
10409
  ${pc11.bold("Cleanup:")}
10909
10410
  Ctrl+C stops the proxy and restores your previous Claude config.
@@ -13763,7 +13264,7 @@ Options:
13763
13264
  --trace Write debug logs under ~/.relay-ai/logs/`);
13764
13265
  return 0;
13765
13266
  }
13766
- const { runUiCommand } = await import("./ui-command-3CWMSARO.js");
13267
+ const { runUiCommand } = await import("./ui-command-S3TQKB3D.js");
13767
13268
  return runUiCommand({ trace: parsed.trace, serverMode: parsed.uiServerMode });
13768
13269
  }
13769
13270
  if (parsed.command === "models") {