@omnicross/daemon 0.4.1 → 0.4.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -18,16 +18,18 @@ import { setSubscriptionRegistryForOutbound } from "@omnicross/core/outbound-api
18
18
  import { getSharedAccountHealth as getSharedAccountHealth4 } from "@omnicross/core/pipeline/SubscriptionAccountHealth";
19
19
  import {
20
20
  __resetSharedAccountAllowanceStoreForTests,
21
- AccountAllowanceStore as AccountAllowanceStore9,
21
+ AccountAllowanceStore as AccountAllowanceStore10,
22
22
  setSharedAccountAllowanceStore
23
23
  } from "@omnicross/core/pipeline/AccountAllowanceStore";
24
24
  import {
25
25
  __resetSharedAccountAllowanceSchedulingForTests,
26
26
  getSharedAccountAllowanceScheduling as getSharedAccountAllowanceScheduling5
27
27
  } from "@omnicross/core/pipeline/AccountAllowanceScheduling";
28
- import { fetchUpstream as fetchUpstream14, setUpstreamProxyResolver } from "@omnicross/core/pipeline/upstreamFetch";
28
+ import { fetchUpstream as fetchUpstream16, setUpstreamProxyResolver } from "@omnicross/core/pipeline/upstreamFetch";
29
29
  import { __resetSharedIdentityStoreForTests } from "@omnicross/core/provider-proxy/identity/SubscriptionIdentityStore";
30
30
  import { setGeminiCodeAssistResolver } from "@omnicross/core/ports/gemini-code-assist-resolver";
31
+ import { setAntigravitySandboxFailover } from "@omnicross/core/transformer/transformers/antigravityFailover";
32
+ import { setOpenCodeGoUserAgent } from "@omnicross/core/provider-proxy/identity/openCodeGoHeaders";
31
33
  import {
32
34
  __resetProviderProxyForTests,
33
35
  createNativeResponsesHostedImageIngress,
@@ -411,7 +413,7 @@ function handleCopilotOAuthStatus(sessionId, deps) {
411
413
 
412
414
  // src/allowance/AccountAllowanceService.ts
413
415
  import {
414
- getSharedAccountAllowanceStore as getSharedAccountAllowanceStore8
416
+ getSharedAccountAllowanceStore as getSharedAccountAllowanceStore9
415
417
  } from "@omnicross/core/pipeline/AccountAllowanceStore";
416
418
  import {
417
419
  getSharedAccountAllowanceScheduling
@@ -1801,11 +1803,328 @@ var GeminiAllowanceCollector = class {
1801
1803
  }
1802
1804
  };
1803
1805
 
1804
- // src/allowance/OpenCodeGoAllowanceCollector.ts
1806
+ // src/allowance/AntigravityAllowanceCollector.ts
1807
+ import { ANTIGRAVITY_CODE_ASSIST_ENDPOINT } from "@omnicross/core/auth/GeminiCodeAssistProjectResolver";
1808
+ import {
1809
+ ANTIGRAVITY_COUNTER_TO_MODEL_FAMILY,
1810
+ antigravityCounterFamiliesForBucketId
1811
+ } from "@omnicross/core/pipeline/antigravityQuotaFamily";
1805
1812
  import {
1806
1813
  getSharedAccountAllowanceStore as getSharedAccountAllowanceStore7
1807
1814
  } from "@omnicross/core/pipeline/AccountAllowanceStore";
1808
1815
  import { fetchUpstream as fetchUpstream7 } from "@omnicross/core/pipeline/upstreamFetch";
1816
+ import { getAntigravityUserAgent } from "@omnicross/core/transformer/transformers/antigravityIdentity";
1817
+ var ANTIGRAVITY_ALLOWANCE_CACHE_MS = 5 * 6e4;
1818
+ var RETRIEVE_USER_QUOTA_SUMMARY_PATH = "/v1internal:retrieveUserQuotaSummary";
1819
+ var FETCH_AVAILABLE_MODELS_PATH = "/v1internal:fetchAvailableModels";
1820
+ var ANTIGRAVITY_DISCOVERY_DENYLIST = /* @__PURE__ */ new Set([
1821
+ "chat_20706",
1822
+ "chat_23310",
1823
+ "gemini-2.5-pro"
1824
+ ]);
1825
+ function isRecord5(value) {
1826
+ return !!value && typeof value === "object" && !Array.isArray(value);
1827
+ }
1828
+ function secondsUntil7(instant, now) {
1829
+ if (!instant) return void 0;
1830
+ return Math.max(0, Math.floor((Date.parse(instant) - now) / 1e3));
1831
+ }
1832
+ var WINDOW_LABELS = {
1833
+ "five-hour": "5 Hour",
1834
+ weekly: "Weekly",
1835
+ daily: "Daily"
1836
+ };
1837
+ function classifyWindowId(...sources) {
1838
+ for (const source of sources) {
1839
+ if (!source) continue;
1840
+ const text = source.toLowerCase();
1841
+ if (text.includes("week") || text.includes("7d") || /7[\s_-]*day/.test(text)) return "weekly";
1842
+ if (text.includes("5h") || text.includes("five hour") || /5[\s_-]*hour/.test(text)) return "five-hour";
1843
+ if (text.includes("day") || text.includes("daily") || text.includes("24h")) return "daily";
1844
+ }
1845
+ return void 0;
1846
+ }
1847
+ function inferWindowFromReset(resetsAt, now) {
1848
+ if (resetsAt !== void 0 && Date.parse(resetsAt) - now > 24 * 60 * 60 * 1e3) return "weekly";
1849
+ return "daily";
1850
+ }
1851
+ function clampFraction(value) {
1852
+ if (value === void 0 || !Number.isFinite(value)) return void 0;
1853
+ return Math.min(1, Math.max(0, value));
1854
+ }
1855
+ function usedPercentFromFraction(fraction) {
1856
+ const clamped = clampFraction(fraction);
1857
+ if (clamped === void 0) return null;
1858
+ return Math.round((1 - clamped) * 1e3) / 10;
1859
+ }
1860
+ function toResetsAt(resetTime) {
1861
+ if (!resetTime || !Number.isFinite(Date.parse(resetTime))) return void 0;
1862
+ return new Date(Date.parse(resetTime)).toISOString();
1863
+ }
1864
+ function parseAntigravityQuotaSummary(payload, now) {
1865
+ if (!isRecord5(payload)) return null;
1866
+ const groups = Array.isArray(payload["groups"]) ? payload["groups"] : [];
1867
+ const topBuckets = Array.isArray(payload["buckets"]) ? payload["buckets"] : [];
1868
+ const hasGrouped = groups.some((group) => Array.isArray(group.buckets) && group.buckets.length > 0);
1869
+ if (!hasGrouped && topBuckets.length === 0) return null;
1870
+ const windows = /* @__PURE__ */ new Map();
1871
+ const addBucket = (bucket, groupName) => {
1872
+ const families = antigravityCounterFamiliesForBucketId(bucket.bucketId, groupName);
1873
+ if (families.length === 0) return;
1874
+ const resetsAt = toResetsAt(bucket.resetTime);
1875
+ const windowId = classifyWindowId(bucket.window, bucket.displayName, bucket.bucketId) ?? (resetsAt !== void 0 ? inferWindowFromReset(resetsAt, now) : void 0);
1876
+ if (windowId === void 0) return;
1877
+ const usedPercent = usedPercentFromFraction(bucket.remainingFraction) ?? (bucket.disabled === true || bucket.resetTime ? bucket.disabled === true ? 100 : null : null);
1878
+ for (const family of families) {
1879
+ const id = `antigravity:${family}:${windowId}`;
1880
+ const candidate = {
1881
+ id,
1882
+ label: `${WINDOW_LABELS[windowId]} (${family})`,
1883
+ scope: "model-family",
1884
+ // The window carries the MODEL family (gemini/claude/gpt-oss) — the
1885
+ // scheduling gate compares it against the requested model's family.
1886
+ modelFamily: ANTIGRAVITY_COUNTER_TO_MODEL_FAMILY[family],
1887
+ usedPercent,
1888
+ ...resetsAt !== void 0 ? { resetsAt } : {},
1889
+ remainingSeconds: secondsUntil7(resetsAt, now),
1890
+ state: "fresh",
1891
+ ...bucket.disabled === true ? { disabled: true } : {}
1892
+ };
1893
+ const existing = windows.get(id);
1894
+ if (!existing || candidate.disabled === true || existing.disabled !== true && (candidate.usedPercent ?? -1) > (existing.usedPercent ?? -1)) {
1895
+ windows.set(id, candidate);
1896
+ }
1897
+ }
1898
+ };
1899
+ if (hasGrouped) {
1900
+ for (const group of groups) {
1901
+ for (const bucket of group.buckets ?? []) addBucket(bucket, group.displayName);
1902
+ }
1903
+ } else {
1904
+ for (const bucket of topBuckets) addBucket(bucket);
1905
+ }
1906
+ const result = [...windows.values()];
1907
+ return result.length > 0 ? result : null;
1908
+ }
1909
+ function legacyQuotaInfos(model) {
1910
+ const out = [];
1911
+ const source = {
1912
+ ...model.apiProvider ? { apiProvider: model.apiProvider } : {},
1913
+ ...model.modelProvider ? { modelProvider: model.modelProvider } : {}
1914
+ };
1915
+ const add = (value, windowDefault) => {
1916
+ if (!value) return;
1917
+ const list = Array.isArray(value) ? value : [value];
1918
+ for (const info of list) {
1919
+ out.push({ ...source, ...windowDefault ? { windowId: windowDefault } : {}, ...info });
1920
+ }
1921
+ };
1922
+ add(model.quotaInfo);
1923
+ add(model.quotaInfos);
1924
+ add(model.dailyQuotaInfo, "daily");
1925
+ add(model.dailyQuotaInfos, "daily");
1926
+ add(model.weeklyQuotaInfo, "weekly");
1927
+ add(model.weeklyQuotaInfos, "weekly");
1928
+ return out;
1929
+ }
1930
+ function legacyCounterFamily(info) {
1931
+ switch (info.modelProvider ?? info.apiProvider) {
1932
+ case "MODEL_PROVIDER_ANTHROPIC":
1933
+ case "API_PROVIDER_ANTHROPIC_VERTEX":
1934
+ return "anthropic";
1935
+ case "MODEL_PROVIDER_GOOGLE":
1936
+ case "API_PROVIDER_GOOGLE_GEMINI":
1937
+ return "google";
1938
+ case "MODEL_PROVIDER_OPENAI":
1939
+ case "API_PROVIDER_OPENAI_VERTEX":
1940
+ return "openai";
1941
+ default:
1942
+ return void 0;
1943
+ }
1944
+ }
1945
+ function parseAntigravityLegacyQuota(payload, now) {
1946
+ if (!isRecord5(payload)) return null;
1947
+ const models = payload["models"];
1948
+ if (!isRecord5(models)) return null;
1949
+ const windows = /* @__PURE__ */ new Map();
1950
+ for (const info of Object.values(models).flatMap(legacyQuotaInfos)) {
1951
+ const family = legacyCounterFamily(info);
1952
+ if (!family) continue;
1953
+ const resetsAt = toResetsAt(info.resetTime);
1954
+ const windowId = classifyWindowId(info.windowId, info.windowLabel) ?? (resetsAt !== void 0 ? inferWindowFromReset(resetsAt, now) : void 0);
1955
+ if (windowId === void 0) continue;
1956
+ const id = `antigravity:${family}:${windowId}`;
1957
+ const candidate = {
1958
+ id,
1959
+ label: `${WINDOW_LABELS[windowId]} (${family})`,
1960
+ scope: "model-family",
1961
+ modelFamily: ANTIGRAVITY_COUNTER_TO_MODEL_FAMILY[family],
1962
+ usedPercent: usedPercentFromFraction(info.remainingFraction),
1963
+ ...resetsAt !== void 0 ? { resetsAt } : {},
1964
+ remainingSeconds: secondsUntil7(resetsAt, now),
1965
+ state: "fresh"
1966
+ };
1967
+ const existing = windows.get(id);
1968
+ if (!existing || (candidate.usedPercent ?? -1) > (existing.usedPercent ?? -1)) {
1969
+ windows.set(id, candidate);
1970
+ }
1971
+ }
1972
+ const result = [...windows.values()];
1973
+ return result.length > 0 ? result : null;
1974
+ }
1975
+ var AntigravityAllowanceCollector = class {
1976
+ constructor(credentials, store = getSharedAccountAllowanceStore7(), fetchImpl = (url, init, accountId) => fetchUpstream7(url, init, { providerId: "antigravity", accountId, redactBodies: true }), now = Date.now) {
1977
+ this.credentials = credentials;
1978
+ this.store = store;
1979
+ this.fetchImpl = fetchImpl;
1980
+ this.now = now;
1981
+ }
1982
+ credentials;
1983
+ store;
1984
+ fetchImpl;
1985
+ now;
1986
+ inFlight = /* @__PURE__ */ new Map();
1987
+ async collectMany(accounts, options = {}) {
1988
+ const settled = await Promise.allSettled(
1989
+ accounts.map((account) => this.collect(account, options))
1990
+ );
1991
+ return settled.flatMap((result) => result.status === "fulfilled" ? [result.value] : []);
1992
+ }
1993
+ collect(account, options = {}) {
1994
+ const now = this.now();
1995
+ if (account.tokens.authMethod !== "oauth") {
1996
+ const existing = this.store.get("antigravity", account.id, now);
1997
+ if (existing?.windows.every((window) => window.state === "unsupported")) {
1998
+ return Promise.resolve(existing);
1999
+ }
2000
+ const snapshot = this.unsupportedSnapshot(account.id, now);
2001
+ this.store.set(snapshot);
2002
+ return Promise.resolve(snapshot);
2003
+ }
2004
+ const cached = this.store.get("antigravity", account.id, now);
2005
+ if (!options.force && cached && this.isCacheValid(cached, now, options.refreshAheadMs)) {
2006
+ return Promise.resolve(cached);
2007
+ }
2008
+ const running = this.inFlight.get(account.id);
2009
+ if (running) return running;
2010
+ const promise = this.fetchAccount(account.id).catch(() => this.failureSnapshot(account.id, "antigravity_usage_request_failed", this.now())).finally(() => this.inFlight.delete(account.id));
2011
+ this.inFlight.set(account.id, promise);
2012
+ return promise;
2013
+ }
2014
+ isCacheValid(snapshot, now, refreshAheadMs) {
2015
+ if (snapshot.windows.every((window) => window.state === "unsupported")) return true;
2016
+ const expiresAt = snapshot.expiresAt ? Date.parse(snapshot.expiresAt) : 0;
2017
+ const ahead = typeof refreshAheadMs === "number" && Number.isFinite(refreshAheadMs) ? Math.max(0, refreshAheadMs) : 0;
2018
+ return Number.isFinite(expiresAt) && expiresAt > now + ahead;
2019
+ }
2020
+ async fetchAccount(accountId) {
2021
+ let accessToken = await this.credentials.getAccessTokenForAccount("antigravity", accountId);
2022
+ if (!accessToken) return this.failureSnapshot(accountId, "antigravity_usage_token_unavailable", this.now());
2023
+ let response = await this.request(accountId, accessToken, RETRIEVE_USER_QUOTA_SUMMARY_PATH, {
2024
+ project: void 0
2025
+ });
2026
+ if (response.status === 401 || response.status === 403) {
2027
+ const refreshed = await this.credentials.refreshAccountToken("antigravity", accountId);
2028
+ if (!refreshed) return this.failureSnapshot(accountId, "antigravity_usage_unauthorized", this.now());
2029
+ accessToken = await this.credentials.getAccessTokenForAccount("antigravity", accountId);
2030
+ if (!accessToken) return this.failureSnapshot(accountId, "antigravity_usage_token_unavailable", this.now());
2031
+ response = await this.request(accountId, accessToken, RETRIEVE_USER_QUOTA_SUMMARY_PATH, {
2032
+ project: void 0
2033
+ });
2034
+ if (response.status === 401 || response.status === 403) {
2035
+ return this.failureSnapshot(accountId, "antigravity_usage_unauthorized", this.now());
2036
+ }
2037
+ }
2038
+ if (response.ok) {
2039
+ const payload = await response.json().catch(() => null);
2040
+ const windows = parseAntigravityQuotaSummary(payload, this.now());
2041
+ if (windows) {
2042
+ const snapshot2 = this.snapshot(accountId, windows);
2043
+ this.store.set(snapshot2);
2044
+ return snapshot2;
2045
+ }
2046
+ }
2047
+ const legacy = await this.request(accountId, accessToken, FETCH_AVAILABLE_MODELS_PATH, {});
2048
+ if (!legacy.ok) {
2049
+ return this.failureSnapshot(accountId, "antigravity_usage_http_error", this.now());
2050
+ }
2051
+ const legacyPayload = await legacy.json().catch(() => null);
2052
+ const legacyWindows = parseAntigravityLegacyQuota(legacyPayload, this.now());
2053
+ if (!legacyWindows) {
2054
+ return this.failureSnapshot(accountId, "antigravity_usage_invalid_response", this.now());
2055
+ }
2056
+ const snapshot = this.snapshot(accountId, legacyWindows);
2057
+ this.store.set(snapshot);
2058
+ return snapshot;
2059
+ }
2060
+ /** One upstream round-trip with the antigravity/hub masquerade UA. */
2061
+ request(accountId, accessToken, path2, body) {
2062
+ return this.fetchImpl(`${ANTIGRAVITY_CODE_ASSIST_ENDPOINT}${path2}`, {
2063
+ method: "POST",
2064
+ headers: {
2065
+ Authorization: `Bearer ${accessToken}`,
2066
+ Accept: "application/json",
2067
+ "Content-Type": "application/json",
2068
+ "User-Agent": getAntigravityUserAgent()
2069
+ },
2070
+ body: JSON.stringify(body),
2071
+ signal: AbortSignal.timeout(15e3)
2072
+ }, accountId);
2073
+ }
2074
+ snapshot(accountId, windows, now = this.now()) {
2075
+ return {
2076
+ providerId: "antigravity",
2077
+ accountId,
2078
+ source: "oauth-usage-api",
2079
+ observedAt: new Date(now).toISOString(),
2080
+ expiresAt: new Date(now + ANTIGRAVITY_ALLOWANCE_CACHE_MS).toISOString(),
2081
+ windows
2082
+ };
2083
+ }
2084
+ failureSnapshot(accountId, code, now) {
2085
+ const existing = this.store.get("antigravity", accountId, now);
2086
+ const snapshot = existing ? {
2087
+ ...existing,
2088
+ expiresAt: new Date(now + ANTIGRAVITY_ALLOWANCE_CACHE_MS).toISOString(),
2089
+ windows: existing.windows.map((window) => ({
2090
+ ...window,
2091
+ state: window.state === "unsupported" ? "unsupported" : window.usedPercent !== null || window.resetsAt || window.disabled ? "stale" : "unavailable"
2092
+ })),
2093
+ lastErrorCode: code
2094
+ } : {
2095
+ providerId: "antigravity",
2096
+ accountId,
2097
+ source: "oauth-usage-api",
2098
+ observedAt: new Date(now).toISOString(),
2099
+ expiresAt: new Date(now + ANTIGRAVITY_ALLOWANCE_CACHE_MS).toISOString(),
2100
+ windows: [
2101
+ { id: "antigravity-quota", label: "Antigravity quota", scope: "all", usedPercent: null, state: "unavailable" }
2102
+ ],
2103
+ lastErrorCode: code
2104
+ };
2105
+ this.store.set(snapshot);
2106
+ return snapshot;
2107
+ }
2108
+ unsupportedSnapshot(accountId, now) {
2109
+ return {
2110
+ providerId: "antigravity",
2111
+ accountId,
2112
+ source: "oauth-usage-api",
2113
+ observedAt: new Date(now).toISOString(),
2114
+ windows: [
2115
+ { id: "antigravity-quota", label: "Antigravity quota", scope: "all", usedPercent: null, state: "unsupported" }
2116
+ ],
2117
+ lastErrorCode: "antigravity_usage_unsupported_auth"
2118
+ };
2119
+ }
2120
+ };
2121
+
2122
+ // src/allowance/OpenCodeGoAllowanceCollector.ts
2123
+ import {
2124
+ getSharedAccountAllowanceStore as getSharedAccountAllowanceStore8
2125
+ } from "@omnicross/core/pipeline/AccountAllowanceStore";
2126
+ import { fetchUpstream as fetchUpstream8 } from "@omnicross/core/pipeline/upstreamFetch";
2127
+ import { getOpenCodeGoUserAgent } from "@omnicross/core/provider-proxy/identity/openCodeGoHeaders";
1809
2128
  import { normalizeOpenCodeGoBaseUrl } from "@omnicross/subscriptions";
1810
2129
  var OPENCODEGO_ALLOWANCE_CACHE_MS = 5 * 6e4;
1811
2130
  var OPENCODEGO_DEFAULT_GO_BASE = "https://opencode.ai/zen/go";
@@ -1819,7 +2138,7 @@ function isoInstant2(value) {
1819
2138
  const time = Date.parse(value);
1820
2139
  return Number.isFinite(time) ? new Date(time).toISOString() : void 0;
1821
2140
  }
1822
- function secondsUntil7(instant, now) {
2141
+ function secondsUntil8(instant, now) {
1823
2142
  if (!instant) return void 0;
1824
2143
  return Math.max(0, Math.floor((Date.parse(instant) - now) / 1e3));
1825
2144
  }
@@ -1834,12 +2153,12 @@ function windowFromPayload3(id, label, minutes, payload, now) {
1834
2153
  usedPercent,
1835
2154
  windowMinutes: minutes,
1836
2155
  ...resetsAt !== void 0 ? { resetsAt } : {},
1837
- remainingSeconds: secondsUntil7(resetsAt, now),
2156
+ remainingSeconds: secondsUntil8(resetsAt, now),
1838
2157
  state: usedPercent !== null || resetsAt ? "fresh" : "unavailable"
1839
2158
  };
1840
2159
  }
1841
2160
  var OpenCodeGoAllowanceCollector = class {
1842
- constructor(credentials, store = getSharedAccountAllowanceStore7(), fetchImpl = (url, init, accountId) => fetchUpstream7(url, init, { providerId: "opencodego", accountId, redactBodies: true }), now = Date.now) {
2161
+ constructor(credentials, store = getSharedAccountAllowanceStore8(), fetchImpl = (url, init, accountId) => fetchUpstream8(url, init, { providerId: "opencodego", accountId, redactBodies: true }), now = Date.now) {
1843
2162
  this.credentials = credentials;
1844
2163
  this.store = store;
1845
2164
  this.fetchImpl = fetchImpl;
@@ -1872,7 +2191,14 @@ var OpenCodeGoAllowanceCollector = class {
1872
2191
  const base = account.tokens.baseUrl ? normalizeOpenCodeGoBaseUrl(account.tokens.baseUrl) : OPENCODEGO_DEFAULT_GO_BASE;
1873
2192
  const response = await this.fetchImpl(`${base}/v1/usage`, {
1874
2193
  method: "GET",
1875
- headers: { Authorization: `Bearer ${apiKey}`, Accept: "application/json" },
2194
+ // opencodego-egress-identity: the background poll identifies itself with
2195
+ // the same configured/default UA the relay carries (no session header —
2196
+ // a poll has no conversation).
2197
+ headers: {
2198
+ Authorization: `Bearer ${apiKey}`,
2199
+ Accept: "application/json",
2200
+ "User-Agent": getOpenCodeGoUserAgent()
2201
+ },
1876
2202
  signal: AbortSignal.timeout(15e3)
1877
2203
  }, account.id);
1878
2204
  if (response.status === 401 || response.status === 403) {
@@ -1944,7 +2270,7 @@ function codexUnavailable(accountId, now) {
1944
2270
  };
1945
2271
  }
1946
2272
  var AccountAllowanceService = class {
1947
- constructor(credentials, store = getSharedAccountAllowanceStore8(), collector, codexCollector, kimiCollector, opencodegoCollector, grokCollector, copilotCollector, geminiCollector, now = Date.now) {
2273
+ constructor(credentials, store = getSharedAccountAllowanceStore9(), collector, codexCollector, kimiCollector, opencodegoCollector, grokCollector, copilotCollector, geminiCollector, antigravityCollector, now = Date.now) {
1948
2274
  this.credentials = credentials;
1949
2275
  this.store = store;
1950
2276
  this.now = now;
@@ -1955,6 +2281,7 @@ var AccountAllowanceService = class {
1955
2281
  this.grokCollector = grokCollector ?? new GrokAllowanceCollector(credentials, store);
1956
2282
  this.copilotCollector = copilotCollector ?? new CopilotAllowanceCollector(credentials, store);
1957
2283
  this.geminiCollector = geminiCollector ?? new GeminiAllowanceCollector(credentials, store);
2284
+ this.antigravityCollector = antigravityCollector ?? new AntigravityAllowanceCollector(credentials, store);
1958
2285
  }
1959
2286
  credentials;
1960
2287
  store;
@@ -1966,6 +2293,7 @@ var AccountAllowanceService = class {
1966
2293
  copilotCollector;
1967
2294
  opencodegoCollector;
1968
2295
  geminiCollector;
2296
+ antigravityCollector;
1969
2297
  /**
1970
2298
  * Read all/filtered snapshots. Claude's and Codex's five-minute caches are
1971
2299
  * refreshed lazily on read (Codex polls `/backend-api/wham/usage`; the
@@ -2014,6 +2342,11 @@ var AccountAllowanceService = class {
2014
2342
  (account) => !filter.accountId || account.id === filter.accountId
2015
2343
  );
2016
2344
  if (wantsGemini) await this.geminiCollector.collectMany(geminiAccounts);
2345
+ const wantsAntigravity = !filter.providerId || filter.providerId === "antigravity";
2346
+ const antigravityAccounts = (config.antigravityAccounts ?? []).filter(
2347
+ (account) => !filter.accountId || account.id === filter.accountId
2348
+ );
2349
+ if (wantsAntigravity) await this.antigravityCollector.collectMany(antigravityAccounts);
2017
2350
  const known = /* @__PURE__ */ new Set();
2018
2351
  if (wantsClaude) for (const account of claudeAccounts) known.add(`claude\0${account.id}`);
2019
2352
  if (wantsCodex) for (const account of codexAccounts) known.add(`codex\0${account.id}`);
@@ -2022,6 +2355,7 @@ var AccountAllowanceService = class {
2022
2355
  if (wantsGrok) for (const account of grokAccounts) known.add(`grok\0${account.id}`);
2023
2356
  if (wantsCopilot) for (const account of copilotAccounts) known.add(`copilot\0${account.id}`);
2024
2357
  if (wantsGemini) for (const account of geminiAccounts) known.add(`gemini\0${account.id}`);
2358
+ if (wantsAntigravity) for (const account of antigravityAccounts) known.add(`antigravity\0${account.id}`);
2025
2359
  return this.store.list(filter).filter((snapshot) => known.has(`${snapshot.providerId}\0${snapshot.accountId}`));
2026
2360
  }
2027
2361
  knownAccounts(config) {
@@ -2032,7 +2366,8 @@ var AccountAllowanceService = class {
2032
2366
  ...(config.opencodegoAccounts ?? []).map((account) => ({ providerId: "opencodego", accountId: account.id })),
2033
2367
  ...(config.grokAccounts ?? []).map((account) => ({ providerId: "grok", accountId: account.id })),
2034
2368
  ...(config.copilotAccounts ?? []).map((account) => ({ providerId: "copilot", accountId: account.id })),
2035
- ...(config.geminiAccounts ?? []).map((account) => ({ providerId: "gemini", accountId: account.id }))
2369
+ ...(config.geminiAccounts ?? []).map((account) => ({ providerId: "gemini", accountId: account.id })),
2370
+ ...(config.antigravityAccounts ?? []).map((account) => ({ providerId: "antigravity", accountId: account.id }))
2036
2371
  ];
2037
2372
  }
2038
2373
  /** Force-refresh Claude usage for one account or every stored Claude account. */
@@ -2102,6 +2437,15 @@ var AccountAllowanceService = class {
2102
2437
  );
2103
2438
  return this.geminiCollector.collectMany(accounts, { force: true });
2104
2439
  }
2440
+ /** Force-refresh Antigravity usage (quotaSummary dual buckets) for one/all accounts. */
2441
+ async refreshAntigravity(accountId) {
2442
+ const config = await this.credentials.getFullConfig();
2443
+ this.store.pruneToKnownAccounts(this.knownAccounts(config));
2444
+ const accounts = (config.antigravityAccounts ?? []).filter(
2445
+ (account) => !accountId || account.id === accountId
2446
+ );
2447
+ return this.antigravityCollector.collectMany(accounts, { force: true });
2448
+ }
2105
2449
  /**
2106
2450
  * Keep Claude + Codex + Kimi snapshots warm for allowance-aware routing. All
2107
2451
  * collectors preserve their cache + per-account in-flight coalescing; a tick
@@ -2119,6 +2463,7 @@ var AccountAllowanceService = class {
2119
2463
  await this.grokCollector.collectMany(config.grokAccounts ?? [], { refreshAheadMs });
2120
2464
  await this.copilotCollector.collectMany(config.copilotAccounts ?? [], { refreshAheadMs });
2121
2465
  await this.geminiCollector.collectMany(config.geminiAccounts ?? [], { refreshAheadMs });
2466
+ await this.antigravityCollector.collectMany(config.antigravityAccounts ?? [], { refreshAheadMs });
2122
2467
  }
2123
2468
  /** Remove a cache row as soon as an account is deleted by the admin path. */
2124
2469
  removeAccountSnapshot(providerId, accountId) {
@@ -2555,7 +2900,7 @@ import {
2555
2900
  } from "@omnicross/contracts/image-generation-types";
2556
2901
  import { getSharedAccountAllowanceScheduling as getSharedAccountAllowanceScheduling2 } from "@omnicross/core/pipeline/AccountAllowanceScheduling";
2557
2902
  import { getSharedAccountHealth } from "@omnicross/core/pipeline/SubscriptionAccountHealth";
2558
- import { fetchUpstream as fetchUpstream8 } from "@omnicross/core/pipeline/upstreamFetch";
2903
+ import { fetchUpstream as fetchUpstream10 } from "@omnicross/core/pipeline/upstreamFetch";
2559
2904
  import { mergeExtraHeaders } from "@omnicross/core";
2560
2905
 
2561
2906
  // src/image-generation/imagesConfigValidation.ts
@@ -3172,7 +3517,11 @@ var TOKEN_FIELDS = {
3172
3517
  claude: ["accessToken", "refreshToken"],
3173
3518
  codex: ["accessToken", "refreshToken", "idToken"],
3174
3519
  gemini: ["accessToken", "refreshToken"],
3175
- opencodego: ["apiKey"]
3520
+ opencodego: ["apiKey"],
3521
+ kimi: ["accessToken", "refreshToken"],
3522
+ grok: ["accessToken", "refreshToken"],
3523
+ copilot: ["accessToken", "refreshToken"],
3524
+ antigravity: ["accessToken", "refreshToken"]
3176
3525
  };
3177
3526
  function transformTokenBlock(block, fields, fn) {
3178
3527
  const next = { ...block };
@@ -3244,6 +3593,19 @@ function resolveAdminConfig(admin) {
3244
3593
  token: typeof admin?.token === "string" && admin.token.length > 0 ? admin.token : void 0
3245
3594
  };
3246
3595
  }
3596
+ function validateAntigravity(raw) {
3597
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) return void 0;
3598
+ const a = raw;
3599
+ if (typeof a["sandboxFailover"] !== "boolean") return void 0;
3600
+ return { sandboxFailover: a["sandboxFailover"] };
3601
+ }
3602
+ function validateOpenCodeGo(raw) {
3603
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) return void 0;
3604
+ const o = raw;
3605
+ const ua = o["userAgent"];
3606
+ if (typeof ua !== "string" || ua.trim().length === 0) return void 0;
3607
+ return { userAgent: ua.trim() };
3608
+ }
3247
3609
  function validateUsage(raw) {
3248
3610
  if (!raw || typeof raw !== "object" || Array.isArray(raw)) return void 0;
3249
3611
  const u = raw;
@@ -3537,7 +3899,9 @@ function validateConfig(raw) {
3537
3899
  const admin = validateAdmin(obj["admin"]);
3538
3900
  const logging = validateLogging(obj["logging"]);
3539
3901
  const usage = validateUsage(obj["usage"]);
3540
- return { providers, server, admin, logging };
3902
+ const antigravity = validateAntigravity(obj["antigravity"]);
3903
+ const opencodego = validateOpenCodeGo(obj["opencodego"]);
3904
+ return { providers, server, admin, logging, usage, antigravity, opencodego };
3541
3905
  }
3542
3906
  var secretBox = null;
3543
3907
  function setSecretBox(box) {
@@ -4642,6 +5006,167 @@ function createUpstreamProxyResolver(src = {}) {
4642
5006
  };
4643
5007
  }
4644
5008
 
5009
+ // src/admin/accountsAntigravityOAuth.ts
5010
+ import { getAntigravityProjectResolver } from "@omnicross/core/auth/GeminiCodeAssistProjectResolver";
5011
+ import { antigravityOAuth } from "@omnicross/subscriptions";
5012
+ function err5(status, message) {
5013
+ return { status, body: { error: { type: "admin_api_error", message } } };
5014
+ }
5015
+ var DEFAULT_ANTIGRAVITY_OAUTH_TTL_MS = 10 * 6e4;
5016
+ function handleAntigravityOAuthStart(deps) {
5017
+ if (deps.antigravitySessions.isBusy()) {
5018
+ return err5(
5019
+ 409,
5020
+ "an antigravity sign-in is already in progress (loopback 127.0.0.1:51121 is held) \u2014 finish it in the browser or wait for it to time out"
5021
+ );
5022
+ }
5023
+ const { authUrl, state } = antigravityOAuth.generateAuthParams();
5024
+ const { sessionId, signal } = deps.antigravitySessions.begin();
5025
+ void runAntigravityLoopback(sessionId, state, signal, deps);
5026
+ return { status: 200, body: { authUrl, sessionId } };
5027
+ }
5028
+ async function runAntigravityLoopback(sessionId, state, signal, deps) {
5029
+ const isPending = () => !signal.aborted && deps.antigravitySessions.get(sessionId)?.status === "pending";
5030
+ try {
5031
+ const code = await deps.antigravityAwaitLoopback(state, void 0, signal);
5032
+ if (!isPending()) return;
5033
+ const exchangeFetch = deps.oauthExchangeFetch("antigravity");
5034
+ const result = await antigravityOAuth.exchangeCodeForTokens(code, exchangeFetch);
5035
+ if (!isPending()) return;
5036
+ const expiresAt = new Date(Date.now() + result.expiresIn * 1e3).toISOString();
5037
+ const email = await antigravityOAuth.fetchUserEmail(result.accessToken, exchangeFetch);
5038
+ if (!isPending()) return;
5039
+ const projectId = await getAntigravityProjectResolver().resolveProject(result.accessToken);
5040
+ if (!isPending()) return;
5041
+ const block = {
5042
+ authMethod: "oauth",
5043
+ status: "authorized",
5044
+ accessToken: result.accessToken,
5045
+ refreshToken: result.refreshToken,
5046
+ expiresAt,
5047
+ ...email ? { email } : {},
5048
+ ...projectId ? { projectId } : {},
5049
+ lastRefreshedAt: (/* @__PURE__ */ new Date()).toISOString()
5050
+ };
5051
+ await deps.subscriptionAccountAppender.appendProviderAccount("antigravity", block);
5052
+ if (isPending()) deps.antigravitySessions.settle(sessionId, "done");
5053
+ } catch (e) {
5054
+ if (!isPending()) return;
5055
+ const reason = e instanceof Error ? e.message : "antigravity sign-in failed";
5056
+ deps.antigravitySessions.settle(sessionId, "error", reason);
5057
+ }
5058
+ }
5059
+ function handleAntigravityOAuthCancel(sessionId, deps) {
5060
+ if (!deps.antigravitySessions.cancel(sessionId)) {
5061
+ return err5(404, "unknown or expired antigravity sign-in session");
5062
+ }
5063
+ return { status: 200, body: { ok: true } };
5064
+ }
5065
+ function handleAntigravityOAuthStatus(sessionId, deps) {
5066
+ const s = deps.antigravitySessions.get(sessionId);
5067
+ if (!s) return err5(404, "unknown or expired antigravity sign-in session");
5068
+ return { status: 200, body: { state: s.status, ...s.error ? { message: s.error } : {} } };
5069
+ }
5070
+
5071
+ // src/allowance/AntigravityModelDiscovery.ts
5072
+ import { lookupCanonicalCapabilities } from "@omnicross/contracts/canonical-models";
5073
+ import { SUBSCRIPTION_MODEL_CATALOG } from "@omnicross/contracts/subscription-model-catalog";
5074
+ import { ANTIGRAVITY_CODE_ASSIST_ENDPOINT as ANTIGRAVITY_CODE_ASSIST_ENDPOINT2 } from "@omnicross/core/auth/GeminiCodeAssistProjectResolver";
5075
+ import { fetchUpstream as fetchUpstream9 } from "@omnicross/core/pipeline/upstreamFetch";
5076
+ import { getAntigravityUserAgent as getAntigravityUserAgent2 } from "@omnicross/core/transformer/transformers/antigravityIdentity";
5077
+ function isRecord6(value) {
5078
+ return !!value && typeof value === "object" && !Array.isArray(value);
5079
+ }
5080
+ function optionalString(value) {
5081
+ return typeof value === "string" && value.length > 0 ? value : void 0;
5082
+ }
5083
+ function optionalNumber(value) {
5084
+ return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : void 0;
5085
+ }
5086
+ function optionalBoolean(value) {
5087
+ return typeof value === "boolean" ? value : void 0;
5088
+ }
5089
+ function parseAntigravityAvailableModels(payload) {
5090
+ if (!isRecord6(payload)) return [];
5091
+ const models = payload["models"];
5092
+ if (!isRecord6(models)) return [];
5093
+ const out = [];
5094
+ for (const [id, raw] of Object.entries(models)) {
5095
+ if (ANTIGRAVITY_DISCOVERY_DENYLIST.has(id)) continue;
5096
+ if (!isRecord6(raw)) continue;
5097
+ if (raw["isInternal"] === true) continue;
5098
+ out.push({
5099
+ id,
5100
+ ...optionalString(raw["displayName"]) ? { displayName: optionalString(raw["displayName"]) } : {},
5101
+ ...optionalBoolean(raw["supportsImages"]) !== void 0 ? { supportsImages: optionalBoolean(raw["supportsImages"]) } : {},
5102
+ ...optionalBoolean(raw["supportsThinking"]) !== void 0 ? { supportsThinking: optionalBoolean(raw["supportsThinking"]) } : {},
5103
+ ...optionalNumber(raw["thinkingBudget"]) !== void 0 ? { thinkingBudget: optionalNumber(raw["thinkingBudget"]) } : {},
5104
+ ...optionalNumber(raw["maxTokens"]) !== void 0 ? { maxTokens: optionalNumber(raw["maxTokens"]) } : {},
5105
+ ...optionalNumber(raw["maxOutputTokens"]) !== void 0 ? { maxOutputTokens: optionalNumber(raw["maxOutputTokens"]) } : {}
5106
+ });
5107
+ }
5108
+ out.sort((a, b) => a.id.localeCompare(b.id));
5109
+ return out;
5110
+ }
5111
+ function mergeAntigravityCatalog(discovered, log = (line) => console.warn(line), staticIds = SUBSCRIPTION_MODEL_CATALOG.antigravity) {
5112
+ const entries = staticIds.map((id) => {
5113
+ const capabilities = lookupCanonicalCapabilities(id);
5114
+ return {
5115
+ id,
5116
+ origin: "static",
5117
+ displayName: id,
5118
+ ...capabilities?.vision !== void 0 ? { supportsImages: capabilities.vision } : {},
5119
+ ...capabilities?.reasoning !== void 0 ? { supportsThinking: capabilities.reasoning } : {},
5120
+ ...capabilities?.thinkingTokenLimit ? { thinkingBudget: capabilities.thinkingTokenLimit.max } : {},
5121
+ // Discovery calls the context window maxTokens, not the output ceiling.
5122
+ ...capabilities?.contextLength !== void 0 ? { maxTokens: capabilities.contextLength } : {},
5123
+ ...capabilities?.maxTokens !== void 0 ? { maxOutputTokens: capabilities.maxTokens } : {}
5124
+ };
5125
+ });
5126
+ const staticSet = new Set(staticIds);
5127
+ for (const model of discovered) {
5128
+ if (staticSet.has(model.id)) {
5129
+ log(
5130
+ `[AntigravityModelDiscovery] dynamic model '${model.id}' conflicts with the static catalog \u2014 static entry kept`
5131
+ );
5132
+ continue;
5133
+ }
5134
+ entries.push({ ...model, origin: "discovered" });
5135
+ }
5136
+ return entries;
5137
+ }
5138
+ async function fetchAntigravityAvailableModels(accessToken, fetchImpl = (url, init) => fetchUpstream9(url, init, { providerId: "antigravity", redactBodies: true })) {
5139
+ let response;
5140
+ try {
5141
+ response = await fetchImpl(`${ANTIGRAVITY_CODE_ASSIST_ENDPOINT2}/v1internal:fetchAvailableModels`, {
5142
+ method: "POST",
5143
+ headers: {
5144
+ Authorization: `Bearer ${accessToken}`,
5145
+ Accept: "application/json",
5146
+ "Content-Type": "application/json",
5147
+ "User-Agent": getAntigravityUserAgent2()
5148
+ },
5149
+ body: JSON.stringify({}),
5150
+ signal: AbortSignal.timeout(15e3)
5151
+ });
5152
+ } catch {
5153
+ return null;
5154
+ }
5155
+ if (!response.ok) return null;
5156
+ const payload = await response.json().catch(() => null);
5157
+ if (!isRecord6(payload) || !isRecord6(payload["models"])) return null;
5158
+ return parseAntigravityAvailableModels(payload);
5159
+ }
5160
+ async function handleAntigravityModelsRoute(deps) {
5161
+ const accessToken = await deps.resolveAntigravityAccessToken().catch(() => null);
5162
+ const discovered = accessToken ? await fetchAntigravityAvailableModels(accessToken, deps.fetchImpl) : null;
5163
+ const models = mergeAntigravityCatalog(discovered ?? []);
5164
+ return {
5165
+ status: 200,
5166
+ body: { models, discovered: discovered !== null }
5167
+ };
5168
+ }
5169
+
4645
5170
  // src/admin/accountsOAuth.ts
4646
5171
  import { claudeOAuth, geminiOAuth } from "@omnicross/subscriptions";
4647
5172
 
@@ -4653,7 +5178,8 @@ var VALID_PROVIDER_IDS = [
4653
5178
  "opencodego",
4654
5179
  "kimi",
4655
5180
  "grok",
4656
- "copilot"
5181
+ "copilot",
5182
+ "antigravity"
4657
5183
  ];
4658
5184
  function asSubscriptionProviderId(id) {
4659
5185
  return VALID_PROVIDER_IDS.includes(id) ? id : null;
@@ -4835,7 +5361,7 @@ function validateCopilot(body) {
4835
5361
  ]);
4836
5362
  return out;
4837
5363
  }
4838
- function validateOpenCodeGo(body) {
5364
+ function validateOpenCodeGo2(body) {
4839
5365
  const authMethod = str(body["authMethod"]);
4840
5366
  const status = str(body["status"]);
4841
5367
  if (authMethod !== "manual") return null;
@@ -4869,7 +5395,7 @@ function validateTokenBody(providerId, body) {
4869
5395
  case "gemini":
4870
5396
  return validateGemini(body);
4871
5397
  case "opencodego":
4872
- return validateOpenCodeGo(body);
5398
+ return validateOpenCodeGo2(body);
4873
5399
  case "kimi":
4874
5400
  return validateKimi(body);
4875
5401
  case "grok":
@@ -4905,12 +5431,12 @@ async function statusEntryFor(reader, providerId) {
4905
5431
 
4906
5432
  // src/admin/accountsOAuth.ts
4907
5433
  var OAUTH_HTTP_PROVIDERS = /* @__PURE__ */ new Set(["claude", "gemini"]);
4908
- function err5(status, message) {
5434
+ function err6(status, message) {
4909
5435
  return { status, body: { error: { type: "admin_api_error", message } } };
4910
5436
  }
4911
5437
  function handleOAuthStart(providerId, deps) {
4912
5438
  if (!OAUTH_HTTP_PROVIDERS.has(providerId)) {
4913
- return err5(400, `oauth not available for provider '${providerId}'`);
5439
+ return err6(400, `oauth not available for provider '${providerId}'`);
4914
5440
  }
4915
5441
  const flow = providerId === "claude" ? claudeOAuth : geminiOAuth;
4916
5442
  const { authUrl, codeVerifier, state } = flow.generateAuthParams();
@@ -4919,23 +5445,23 @@ function handleOAuthStart(providerId, deps) {
4919
5445
  }
4920
5446
  async function handleOAuthComplete(providerId, body, deps) {
4921
5447
  if (!OAUTH_HTTP_PROVIDERS.has(providerId)) {
4922
- return err5(400, `oauth not available for provider '${providerId}'`);
5448
+ return err6(400, `oauth not available for provider '${providerId}'`);
4923
5449
  }
4924
5450
  const sessionId = typeof body["sessionId"] === "string" ? body["sessionId"] : "";
4925
5451
  const rawCode = typeof body["code"] === "string" ? body["code"] : "";
4926
- if (!sessionId) return err5(400, "oauth complete requires { sessionId }");
4927
- if (!rawCode) return err5(400, "oauth complete requires { code }");
5452
+ if (!sessionId) return err6(400, "oauth complete requires { sessionId }");
5453
+ if (!rawCode) return err6(400, "oauth complete requires { code }");
4928
5454
  const session = deps.oauthSessions.peek(sessionId);
4929
- if (!session) return err5(410, "oauth session is unknown, expired, or already used");
5455
+ if (!session) return err6(410, "oauth session is unknown, expired, or already used");
4930
5456
  if (session.providerId !== providerId) {
4931
- return err5(400, `oauth session does not match provider '${providerId}'`);
5457
+ return err6(400, `oauth session does not match provider '${providerId}'`);
4932
5458
  }
4933
5459
  let code = rawCode.trim();
4934
5460
  if (providerId === "claude") {
4935
5461
  const [splitCode, pastedState] = code.split("#");
4936
- if (!splitCode) return err5(400, "no authorization code was provided");
5462
+ if (!splitCode) return err6(400, "no authorization code was provided");
4937
5463
  if (pastedState && pastedState !== session.state) {
4938
- return err5(400, "oauth state did not match (possible CSRF) \u2014 aborting");
5464
+ return err6(400, "oauth state did not match (possible CSRF) \u2014 aborting");
4939
5465
  }
4940
5466
  code = splitCode;
4941
5467
  }
@@ -4945,7 +5471,7 @@ async function handleOAuthComplete(providerId, body, deps) {
4945
5471
  block = providerId === "claude" ? await exchangeClaude(code, session.codeVerifier, session.state, exchangeFetch) : await exchangeGemini(code, session.codeVerifier, exchangeFetch);
4946
5472
  } catch (exchangeError) {
4947
5473
  const reason = exchangeError instanceof Error ? exchangeError.message : "token exchange failed";
4948
- return err5(502, `oauth token exchange failed for '${providerId}': ${reason}`);
5474
+ return err6(502, `oauth token exchange failed for '${providerId}': ${reason}`);
4949
5475
  }
4950
5476
  deps.oauthSessions.consume(sessionId);
4951
5477
  const label = typeof body["label"] === "string" && body["label"].trim() ? body["label"].trim() : void 0;
@@ -5283,8 +5809,8 @@ function errBody(message) {
5283
5809
  return { error: { type: "admin_api_error", message } };
5284
5810
  }
5285
5811
  var defaultCommandRunner = (command) => new Promise((resolve10) => {
5286
- exec(command, { timeout: 18e4 }, (err8, _stdout, stderr) => {
5287
- if (err8) resolve10({ ok: false, error: stderr.trim() || err8.message });
5812
+ exec(command, { timeout: 18e4 }, (err9, _stdout, stderr) => {
5813
+ if (err9) resolve10({ ok: false, error: stderr.trim() || err9.message });
5288
5814
  else resolve10({ ok: true });
5289
5815
  });
5290
5816
  });
@@ -5330,8 +5856,8 @@ async function handleCliLaunch(cli, body, ctx) {
5330
5856
  providerId: typeof body["providerId"] === "string" ? body["providerId"] : void 0,
5331
5857
  model: typeof body["model"] === "string" ? body["model"] : void 0
5332
5858
  });
5333
- } catch (err8) {
5334
- return { status: 400, body: errBody(err8 instanceof Error ? err8.message : "no launch target") };
5859
+ } catch (err9) {
5860
+ return { status: 400, body: errBody(err9 instanceof Error ? err9.message : "no launch target") };
5335
5861
  }
5336
5862
  const id = randomUUID2();
5337
5863
  let leaseId2;
@@ -5359,9 +5885,9 @@ async function handleCliLaunch(cli, body, ctx) {
5359
5885
  } else {
5360
5886
  launch = await buildLaunchEnv(cli, ctx.llmConfig, target);
5361
5887
  }
5362
- } catch (err8) {
5363
- const status = err8 instanceof RouteLeaseError2 ? err8.status : 400;
5364
- return { status, body: errBody(err8 instanceof Error ? err8.message : "failed to build launch env") };
5888
+ } catch (err9) {
5889
+ const status = err9 instanceof RouteLeaseError2 ? err9.status : 400;
5890
+ return { status, body: errBody(err9 instanceof Error ? err9.message : "failed to build launch env") };
5365
5891
  }
5366
5892
  const cwd = typeof body["cwd"] === "string" && body["cwd"].trim() ? body["cwd"].trim() : void 0;
5367
5893
  const opener = ctx.opener ?? defaultTerminalOpener;
@@ -5389,9 +5915,9 @@ async function handleCliLaunch(cli, body, ctx) {
5389
5915
  onFailure: onSessionEnd
5390
5916
  });
5391
5917
  if (cleanup) openerCleanup = cleanup;
5392
- } catch (err8) {
5918
+ } catch (err9) {
5393
5919
  onSessionEnd();
5394
- return { status: 500, body: errBody(err8 instanceof Error ? err8.message : "failed to open terminal") };
5920
+ return { status: 500, body: errBody(err9 instanceof Error ? err9.message : "failed to open terminal") };
5395
5921
  }
5396
5922
  if (ended) {
5397
5923
  openerCleanup?.();
@@ -5940,7 +6466,7 @@ async function handleSearchQuery(req, res, deps) {
5940
6466
  // src/admin/searchAdminView.ts
5941
6467
  var API_KEY_PROVIDERS = /* @__PURE__ */ new Set(["tavily", "jina", "zhipu", "z.ai"]);
5942
6468
  var BASIC_AUTH_PROVIDERS = /* @__PURE__ */ new Set(["searxng"]);
5943
- function isRecord5(value) {
6469
+ function isRecord7(value) {
5944
6470
  return value !== null && typeof value === "object" && !Array.isArray(value);
5945
6471
  }
5946
6472
  function redactSearchServerConfig(search) {
@@ -5990,13 +6516,13 @@ function resolveSecretField(entry, field, stored) {
5990
6516
  else delete entry[field];
5991
6517
  }
5992
6518
  function preserveSearchSecrets(incoming, current) {
5993
- if (!isRecord5(incoming)) return incoming;
6519
+ if (!isRecord7(incoming)) return incoming;
5994
6520
  const section = { ...incoming };
5995
6521
  const providersValue = section["providers"];
5996
- if (!isRecord5(providersValue)) return section;
6522
+ if (!isRecord7(providersValue)) return section;
5997
6523
  const providers = {};
5998
6524
  for (const [id, entryValue] of Object.entries(providersValue)) {
5999
- if (!isRecord5(entryValue)) {
6525
+ if (!isRecord7(entryValue)) {
6000
6526
  providers[id] = entryValue;
6001
6527
  continue;
6002
6528
  }
@@ -6074,7 +6600,7 @@ function parseKeyPolicyBody(body) {
6074
6600
  var ENDPOINTS = /* @__PURE__ */ new Set(["chat", "responses", "messages", "gemini"]);
6075
6601
  var TARGET_KINDS = /* @__PURE__ */ new Set(["account", "account-group", "account-pool", "provider"]);
6076
6602
  var FALLBACKS = /* @__PURE__ */ new Set(["next", "fail", "global"]);
6077
- function isRecord6(value) {
6603
+ function isRecord8(value) {
6078
6604
  return !!value && typeof value === "object" && !Array.isArray(value);
6079
6605
  }
6080
6606
  function nonBlank(value) {
@@ -6094,7 +6620,7 @@ function validateGatewayBindingsSegment(patch) {
6094
6620
  const ids = /* @__PURE__ */ new Set();
6095
6621
  raw.forEach((entry, index) => {
6096
6622
  const path2 = `bindings[${index}]`;
6097
- if (!isRecord6(entry)) {
6623
+ if (!isRecord8(entry)) {
6098
6624
  errors.push(`${path2} must be an object`);
6099
6625
  return;
6100
6626
  }
@@ -6123,12 +6649,12 @@ function validateGatewayBindingsSegment(patch) {
6123
6649
  } else if (entry.modelMappings.length > 100) {
6124
6650
  errors.push(`${path2}.modelMappings cannot contain more than 100 entries`);
6125
6651
  } else if (entry.modelMappings.some(
6126
- (mapping) => !isRecord6(mapping) || !nonBlank(mapping.source) || !nonBlank(mapping.target)
6652
+ (mapping) => !isRecord8(mapping) || !nonBlank(mapping.source) || !nonBlank(mapping.target)
6127
6653
  )) {
6128
6654
  errors.push(`${path2}.modelMappings must contain non-empty source and target strings`);
6129
6655
  }
6130
6656
  }
6131
- if (!isRecord6(entry.target) || !TARGET_KINDS.has(String(entry.target.kind))) {
6657
+ if (!isRecord8(entry.target) || !TARGET_KINDS.has(String(entry.target.kind))) {
6132
6658
  errors.push(`${path2}.target is invalid`);
6133
6659
  } else {
6134
6660
  if (!nonBlank(entry.target.providerId)) errors.push(`${path2}.target.providerId is required`);
@@ -6143,7 +6669,7 @@ function validateGatewayBindingsSegment(patch) {
6143
6669
  }
6144
6670
  }
6145
6671
  if (entry.modelMap !== void 0) {
6146
- if (!isRecord6(entry.modelMap) || Object.values(entry.modelMap).some((value) => typeof value !== "string")) {
6672
+ if (!isRecord8(entry.modelMap) || Object.values(entry.modelMap).some((value) => typeof value !== "string")) {
6147
6673
  errors.push(`${path2}.modelMap must contain string values`);
6148
6674
  }
6149
6675
  }
@@ -6456,7 +6982,12 @@ var PROVIDER_KEYS = {
6456
6982
  },
6457
6983
  kimi: { block: "kimi", accounts: "kimiAccounts", active: "activeKimiAccountId" },
6458
6984
  grok: { block: "grok", accounts: "grokAccounts", active: "activeGrokAccountId" },
6459
- copilot: { block: "copilot", accounts: "copilotAccounts", active: "activeCopilotAccountId" }
6985
+ copilot: { block: "copilot", accounts: "copilotAccounts", active: "activeCopilotAccountId" },
6986
+ antigravity: {
6987
+ block: "antigravity",
6988
+ accounts: "antigravityAccounts",
6989
+ active: "activeAntigravityAccountId"
6990
+ }
6460
6991
  };
6461
6992
  function clone(value) {
6462
6993
  return JSON.parse(JSON.stringify(value));
@@ -6978,7 +7509,7 @@ async function applyImport(packString, passphrase, mode, deps, parseProviderInpu
6978
7509
  }
6979
7510
 
6980
7511
  // src/admin/adminMigration.ts
6981
- function err6(status, message) {
7512
+ function err7(status, message) {
6982
7513
  return { status, body: { error: { type: "admin_api_error", message } } };
6983
7514
  }
6984
7515
  async function handleExport(body, deps) {
@@ -6988,30 +7519,30 @@ async function handleExport(body, deps) {
6988
7519
  return { status: 200, body: { pack, version: BUNDLE_VERSION } };
6989
7520
  } catch (error) {
6990
7521
  if (error instanceof WeakPassphraseError) {
6991
- return err6(400, error.message);
7522
+ return err7(400, error.message);
6992
7523
  }
6993
- return err6(500, "failed to build the migration pack");
7524
+ return err7(500, "failed to build the migration pack");
6994
7525
  }
6995
7526
  }
6996
7527
  async function handleImport(body, deps) {
6997
7528
  const blob = typeof body["blob"] === "string" ? body["blob"] : "";
6998
7529
  const passphrase = typeof body["passphrase"] === "string" ? body["passphrase"] : "";
6999
7530
  const mode = body["mode"] === "overwrite" ? "overwrite" : "merge";
7000
- if (!blob) return err6(400, "import requires { blob }");
7531
+ if (!blob) return err7(400, "import requires { blob }");
7001
7532
  try {
7002
7533
  const counts = await applyImport(blob, passphrase, mode, deps, deps.parseProviderInput);
7003
7534
  return { status: 200, body: counts };
7004
7535
  } catch (error) {
7005
7536
  if (error instanceof WeakPassphraseError) {
7006
- return err6(400, error.message);
7537
+ return err7(400, error.message);
7007
7538
  }
7008
- return err6(400, error instanceof Error ? error.message : "import failed");
7539
+ return err7(400, error instanceof Error ? error.message : "import failed");
7009
7540
  }
7010
7541
  }
7011
7542
 
7012
7543
  // src/admin/usagePricing.ts
7013
7544
  import { getSharedUsageThroughputTracker } from "@omnicross/core/usage";
7014
- var err7 = (status, message) => ({
7545
+ var err8 = (status, message) => ({
7015
7546
  status,
7016
7547
  body: { error: { type: "admin_api_error", message } }
7017
7548
  });
@@ -7024,7 +7555,7 @@ function parseRange(query2) {
7024
7555
  const startTs = parseFiniteInt(query2.get("startTs"));
7025
7556
  const endTs = parseFiniteInt(query2.get("endTs"));
7026
7557
  if (startTs === null || endTs === null) {
7027
- return err7(400, "startTs and endTs are required finite-integer unix-millis query params");
7558
+ return err8(400, "startTs and endTs are required finite-integer unix-millis query params");
7028
7559
  }
7029
7560
  return { startTs, endTs };
7030
7561
  }
@@ -7049,14 +7580,14 @@ async function handleUsageGet(view, query2, deps) {
7049
7580
  case "timeseries": {
7050
7581
  const bucket = query2.get("bucket");
7051
7582
  if (bucket !== "hour" && bucket !== "day" && bucket !== "month") {
7052
- return err7(400, "bucket must be one of 'hour', 'day', 'month'");
7583
+ return err8(400, "bucket must be one of 'hour', 'day', 'month'");
7053
7584
  }
7054
7585
  const now = Date.now();
7055
7586
  const clamped = { startTs: range.startTs, endTs: Math.min(range.endTs, now) };
7056
7587
  if (clamped.startTs < clamped.endTs) {
7057
7588
  const projected = Math.ceil((clamped.endTs - clamped.startTs) / BUCKET_SPAN_MS[bucket]) + 1;
7058
7589
  if (projected > MAX_TIMESERIES_BUCKETS) {
7059
- return err7(
7590
+ return err8(
7060
7591
  400,
7061
7592
  `requested range projects ~${projected} '${bucket}' buckets (max ${MAX_TIMESERIES_BUCKETS}); narrow the range or use a coarser bucket`
7062
7593
  );
@@ -7079,7 +7610,7 @@ async function handleUsageGet(view, query2, deps) {
7079
7610
  };
7080
7611
  }
7081
7612
  default:
7082
- return err7(404, `unknown usage view '${view ?? ""}'`);
7613
+ return err8(404, `unknown usage view '${view ?? ""}'`);
7083
7614
  }
7084
7615
  }
7085
7616
  function poolKeyLabels(cfg) {
@@ -7128,7 +7659,7 @@ async function handlePricingList(deps) {
7128
7659
  async function handlePricingUpsert(body, deps) {
7129
7660
  const input = parsePricingEntryInput(body);
7130
7661
  if (!input) {
7131
- return err7(400, "invalid pricing entry (providerId, modelId, finite numeric inputPricePer1m/outputPricePer1m required)");
7662
+ return err8(400, "invalid pricing entry (providerId, modelId, finite numeric inputPricePer1m/outputPricePer1m required)");
7132
7663
  }
7133
7664
  const entry = await deps.pricingEngine.upsertManual(input);
7134
7665
  return { status: 200, body: { entry } };
@@ -7137,7 +7668,7 @@ async function handlePricingDelete(query2, deps) {
7137
7668
  const providerId = query2.get("providerId")?.trim() ?? "";
7138
7669
  const modelId = query2.get("modelId")?.trim() ?? "";
7139
7670
  if (!providerId || !modelId) {
7140
- return err7(400, "delete requires providerId and modelId query params");
7671
+ return err8(400, "delete requires providerId and modelId query params");
7141
7672
  }
7142
7673
  const deleted = await deps.pricingStore.delete(providerId, modelId);
7143
7674
  if (deleted) await deps.pricingEngine.invalidateCache();
@@ -7157,13 +7688,13 @@ async function handlePricingFetchLatest(deps) {
7157
7688
  }
7158
7689
  };
7159
7690
  } catch (e) {
7160
- return err7(502, `pricing-source fetch failed: ${e instanceof Error ? e.message : String(e)}`);
7691
+ return err8(502, `pricing-source fetch failed: ${e instanceof Error ? e.message : String(e)}`);
7161
7692
  }
7162
7693
  }
7163
7694
  async function handlePricingResolveConflicts(body, deps) {
7164
7695
  const raw = body["resolutions"];
7165
7696
  if (!Array.isArray(raw)) {
7166
- return err7(400, "resolve-conflicts requires { resolutions: [...] }");
7697
+ return err8(400, "resolve-conflicts requires { resolutions: [...] }");
7167
7698
  }
7168
7699
  const currentRows = await deps.pricingStore.getAll();
7169
7700
  const userEditedKeys = new Set(
@@ -7173,21 +7704,21 @@ async function handlePricingResolveConflicts(body, deps) {
7173
7704
  const pendingIncoming = /* @__PURE__ */ new Map();
7174
7705
  let staleCount = 0;
7175
7706
  for (const item of raw) {
7176
- if (!item || typeof item !== "object") return err7(400, "invalid resolution entry");
7707
+ if (!item || typeof item !== "object") return err8(400, "invalid resolution entry");
7177
7708
  const r = item;
7178
7709
  const action = r["action"];
7179
7710
  if (action !== "overwrite" && action !== "skip") {
7180
- return err7(400, "resolution action must be 'overwrite' or 'skip'");
7711
+ return err8(400, "resolution action must be 'overwrite' or 'skip'");
7181
7712
  }
7182
7713
  const providerId = typeof r["providerId"] === "string" && r["providerId"].trim() ? r["providerId"].trim() : "";
7183
7714
  const modelId = typeof r["modelId"] === "string" && r["modelId"].trim() ? r["modelId"].trim() : "";
7184
7715
  if (!providerId || !modelId) {
7185
- return err7(400, "each resolution requires top-level providerId and modelId");
7716
+ return err8(400, "each resolution requires top-level providerId and modelId");
7186
7717
  }
7187
7718
  const incoming = parsePricingEntryInput(r["incoming"]);
7188
- if (!incoming) return err7(400, "each resolution must echo a valid incoming pricing entry");
7719
+ if (!incoming) return err8(400, "each resolution must echo a valid incoming pricing entry");
7189
7720
  if (incoming.providerId !== providerId || incoming.modelId !== modelId) {
7190
- return err7(400, "resolution providerId/modelId must match the echoed incoming entry");
7721
+ return err8(400, "resolution providerId/modelId must match the echoed incoming entry");
7191
7722
  }
7192
7723
  const key = `${providerId}::${modelId}`;
7193
7724
  if (action === "overwrite" && !userEditedKeys.has(key)) {
@@ -7232,7 +7763,7 @@ function query(req) {
7232
7763
  }
7233
7764
  function allowanceProvider(value) {
7234
7765
  if (!value) return void 0;
7235
- return value === "claude" || value === "codex" || value === "kimi" || value === "opencodego" || value === "grok" || value === "copilot" || value === "gemini" ? value : null;
7766
+ return value === "claude" || value === "codex" || value === "kimi" || value === "opencodego" || value === "grok" || value === "copilot" || value === "gemini" || value === "antigravity" ? value : null;
7236
7767
  }
7237
7768
  async function handleAccountAllowanceApi(req, res, method, rest, service) {
7238
7769
  if (!service) return writeError2(res, 501, "account allowance service is not available");
@@ -7309,6 +7840,16 @@ async function handleAccountAllowanceApi(req, res, method, rest, service) {
7309
7840
  }
7310
7841
  return writeJson3(res, 200, { allowances: allowances2 });
7311
7842
  }
7843
+ if (requestedProvider === "antigravity") {
7844
+ if (!service.refreshAntigravity) {
7845
+ return writeError2(res, 501, "antigravity allowance refresh is not available");
7846
+ }
7847
+ const allowances2 = await service.refreshAntigravity(accountId);
7848
+ if (accountId && allowances2.length === 0) {
7849
+ return writeError2(res, 404, `Antigravity account '${accountId}' not found`);
7850
+ }
7851
+ return writeJson3(res, 200, { allowances: allowances2 });
7852
+ }
7312
7853
  if (requestedProvider === "gemini") {
7313
7854
  if (!service.refreshGemini) {
7314
7855
  return writeError2(res, 501, "gemini allowance refresh is not available");
@@ -7492,8 +8033,8 @@ async function handleAdminApi(req, res, path2, deps) {
7492
8033
  default:
7493
8034
  return writeJsonError(res, 404, `unknown admin resource '${resource}'`);
7494
8035
  }
7495
- } catch (err8) {
7496
- writeJsonError(res, 500, err8 instanceof Error ? err8.message : String(err8));
8036
+ } catch (err9) {
8037
+ writeJsonError(res, 500, err9 instanceof Error ? err9.message : String(err9));
7497
8038
  }
7498
8039
  }
7499
8040
  function requestQuery(req) {
@@ -7668,7 +8209,7 @@ async function handleDiscoverModels(res, id, cfg) {
7668
8209
  const headers = { Accept: "application/json" };
7669
8210
  if (resolvedKey) headers["Authorization"] = `Bearer ${resolvedKey}`;
7670
8211
  Object.assign(headers, expandRowExtraHeaders(row));
7671
- const response = await fetchUpstream8(url, { method: "GET", headers }, { providerId: "byo" });
8212
+ const response = await fetchUpstream10(url, { method: "GET", headers }, { providerId: "byo" });
7672
8213
  if (!response.ok) {
7673
8214
  const text = await response.text().catch(() => "");
7674
8215
  let message = text.slice(0, 300);
@@ -7685,8 +8226,8 @@ async function handleDiscoverModels(res, id, cfg) {
7685
8226
  const data = await response.json();
7686
8227
  const models = Array.isArray(data?.data) ? data.data.map((m) => typeof m?.id === "string" ? m.id : "").filter((m) => m.length > 0) : [];
7687
8228
  return writeJson4(res, 200, { models });
7688
- } catch (err8) {
7689
- const message = err8 instanceof Error ? err8.message : String(err8);
8229
+ } catch (err9) {
8230
+ const message = err9 instanceof Error ? err9.message : String(err9);
7690
8231
  return writeJson4(res, 200, { models: [], error: `discovery failed: ${message}` });
7691
8232
  }
7692
8233
  }
@@ -7728,7 +8269,7 @@ async function handleTestModel(req, res, id, cfg) {
7728
8269
  Object.assign(headers, expandRowExtraHeaders(row));
7729
8270
  const startedAt = Date.now();
7730
8271
  try {
7731
- const response = await fetchUpstream8(
8272
+ const response = await fetchUpstream10(
7732
8273
  url,
7733
8274
  { method: "POST", headers, body: JSON.stringify(payload) },
7734
8275
  { providerId: "byo" }
@@ -7750,8 +8291,8 @@ async function handleTestModel(req, res, id, cfg) {
7750
8291
  latencyMs,
7751
8292
  sample: extractSampleText(text, row.apiFormat)
7752
8293
  });
7753
- } catch (err8) {
7754
- const message = err8 instanceof Error ? err8.message : String(err8);
8294
+ } catch (err9) {
8295
+ const message = err9 instanceof Error ? err9.message : String(err9);
7755
8296
  return writeJson4(res, 200, { ok: false, latencyMs: Date.now() - startedAt, message });
7756
8297
  }
7757
8298
  }
@@ -8336,8 +8877,8 @@ function imageConfigurationAuditFields(previous, next) {
8336
8877
  const after = next ?? DEFAULT_IMAGES_SERVER_CONFIG;
8337
8878
  const fields = [];
8338
8879
  if (before.enabled !== after.enabled) fields.push("enablement");
8339
- if (before.provider !== after.provider) fields.push("provider");
8340
- if (before.defaultModel !== after.defaultModel || !sameConfigValue(before.modelAliases, after.modelAliases)) fields.push("model");
8880
+ if (!sameConfigValue(before.models, after.models)) fields.push("provider");
8881
+ if (before.defaultModel !== after.defaultModel || !sameConfigValue(before.aliases, after.aliases) || !sameConfigValue(before.codex, after.codex)) fields.push("model");
8341
8882
  if (!sameConfigValue(before.account, after.account)) fields.push("account");
8342
8883
  if (!sameConfigValue(before.queue, after.queue)) fields.push("queue");
8343
8884
  if (!sameConfigValue(before.temporary, after.temporary)) fields.push("temporary");
@@ -8541,6 +9082,12 @@ async function handleAccounts(req, res, method, rest, deps) {
8541
9082
  deps.accountAllowanceService
8542
9083
  );
8543
9084
  }
9085
+ if (method === "GET" && rest[0] === "antigravity" && rest[1] === "models") {
9086
+ const result = await handleAntigravityModelsRoute({
9087
+ resolveAntigravityAccessToken: deps.resolveAntigravityAccessToken ?? (async () => null)
9088
+ });
9089
+ return writeJson4(res, result.status, result.body);
9090
+ }
8544
9091
  if (method === "GET" && rest.length === 0) {
8545
9092
  const accounts = await deps.subscriptionAccounts.listAll();
8546
9093
  const providerAccounts = await deps.subscriptionTokenWriter.listSanitizedAccounts();
@@ -8566,12 +9113,12 @@ async function handleAccounts(req, res, method, rest, deps) {
8566
9113
  }
8567
9114
  return writeJson4(res, 200, { ok: true, affected: result.affected });
8568
9115
  }
8569
- if (method === "GET" && (rest[0] === "codex" || rest[0] === "kimi" || rest[0] === "grok" || rest[0] === "copilot") && rest[1] === "oauth" && rest[3] === "status") {
8570
- const result = rest[0] === "codex" ? handleCodexOAuthStatus(rest[2], deps) : rest[0] === "kimi" ? handleKimiOAuthStatus(rest[2], deps) : rest[0] === "grok" ? handleGrokOAuthStatus(rest[2], deps) : handleCopilotOAuthStatus(rest[2], deps);
9116
+ if (method === "GET" && (rest[0] === "codex" || rest[0] === "kimi" || rest[0] === "grok" || rest[0] === "copilot" || rest[0] === "antigravity") && rest[1] === "oauth" && rest[3] === "status") {
9117
+ const result = rest[0] === "codex" ? handleCodexOAuthStatus(rest[2], deps) : rest[0] === "kimi" ? handleKimiOAuthStatus(rest[2], deps) : rest[0] === "grok" ? handleGrokOAuthStatus(rest[2], deps) : rest[0] === "copilot" ? handleCopilotOAuthStatus(rest[2], deps) : handleAntigravityOAuthStatus(rest[2], deps);
8571
9118
  return writeJson4(res, result.status, result.body);
8572
9119
  }
8573
- if (method === "DELETE" && (rest[0] === "codex" || rest[0] === "kimi" || rest[0] === "grok" || rest[0] === "copilot") && rest[1] === "oauth" && rest[2]) {
8574
- const result = rest[0] === "codex" ? handleCodexOAuthCancel(rest[2], deps) : rest[0] === "kimi" ? handleKimiOAuthCancel(rest[2], deps) : rest[0] === "grok" ? handleGrokOAuthCancel(rest[2], deps) : handleCopilotOAuthCancel(rest[2], deps);
9120
+ if (method === "DELETE" && (rest[0] === "codex" || rest[0] === "kimi" || rest[0] === "grok" || rest[0] === "copilot" || rest[0] === "antigravity") && rest[1] === "oauth" && rest[2]) {
9121
+ const result = rest[0] === "codex" ? handleCodexOAuthCancel(rest[2], deps) : rest[0] === "kimi" ? handleKimiOAuthCancel(rest[2], deps) : rest[0] === "grok" ? handleGrokOAuthCancel(rest[2], deps) : rest[0] === "copilot" ? handleCopilotOAuthCancel(rest[2], deps) : handleAntigravityOAuthCancel(rest[2], deps);
8575
9122
  return writeJson4(res, result.status, result.body);
8576
9123
  }
8577
9124
  if (method === "GET" && rest.length === 3 && rest[2] === "diagnostics") {
@@ -8641,6 +9188,10 @@ async function handleAccounts(req, res, method, rest, deps) {
8641
9188
  const result2 = await handleCopilotOAuthStart(deps, body2["enterpriseUrl"]);
8642
9189
  return writeJson4(res, result2.status, result2.body);
8643
9190
  }
9191
+ if (providerId === "antigravity") {
9192
+ const result2 = handleAntigravityOAuthStart(deps);
9193
+ return writeJson4(res, result2.status, result2.body);
9194
+ }
8644
9195
  const result = handleOAuthStart(providerId, deps);
8645
9196
  return writeJson4(res, result.status, result.body);
8646
9197
  }
@@ -9035,7 +9586,7 @@ async function handleImages(res, method, rest, deps) {
9035
9586
  return writeJson4(res, 200, {
9036
9587
  configured: {
9037
9588
  enabled: images.enabled,
9038
- provider: images.provider,
9589
+ provider: images.models[images.defaultModel] ?? "codex-subscription",
9039
9590
  model: images.defaultModel,
9040
9591
  remoteUrlsEnabled: images.remote.enabled,
9041
9592
  referenceTtlMs: images.references.ttlMs
@@ -9135,12 +9686,12 @@ async function handlePlayground(req, res, method, deps) {
9135
9686
  const payload = body["body"];
9136
9687
  const status = deps.outboundApiServer.getStatus();
9137
9688
  if (!status.running || !status.port) return writeJsonError(res, 503, "outbound server not running");
9138
- const path2 = resolvePlaygroundPath(endpoint, isRecord7(payload) ? payload : {});
9689
+ const path2 = resolvePlaygroundPath(endpoint, isRecord9(payload) ? payload : {});
9139
9690
  if (!path2) return writeJsonError(res, 400, `unknown endpoint '${endpoint}'`);
9140
9691
  const upstreamBody = typeof payload === "string" ? payload : JSON.stringify(payload ?? {});
9141
9692
  await proxyToOutbound(res, status.port, path2, key, upstreamBody);
9142
9693
  }
9143
- function isRecord7(v) {
9694
+ function isRecord9(v) {
9144
9695
  return !!v && typeof v === "object" && !Array.isArray(v);
9145
9696
  }
9146
9697
  function proxyToOutbound(res, outboundPort, path2, key, body) {
@@ -9169,8 +9720,8 @@ function proxyToOutbound(res, outboundPort, path2, key, body) {
9169
9720
  });
9170
9721
  }
9171
9722
  );
9172
- upstream.on("error", (err8) => {
9173
- if (!res.headersSent) writeJsonError(res, 502, `playground proxy failed: ${err8.message}`);
9723
+ upstream.on("error", (err9) => {
9724
+ if (!res.headersSent) writeJsonError(res, 502, `playground proxy failed: ${err9.message}`);
9174
9725
  else res.end();
9175
9726
  resolve10();
9176
9727
  });
@@ -9275,7 +9826,7 @@ async function handleUiStatic(req, res, urlPath, uiDist) {
9275
9826
  }
9276
9827
 
9277
9828
  // src/admin/version.ts
9278
- var DAEMON_VERSION = true ? "0.4.1" : "0.0.0-dev";
9829
+ var DAEMON_VERSION = true ? "0.4.3" : "0.0.0-dev";
9279
9830
 
9280
9831
  // src/admin/AdminServer.ts
9281
9832
  var LOOPBACK_ADDR = "127.0.0.1";
@@ -9318,13 +9869,13 @@ var AdminServer = class {
9318
9869
  const server = http2.createServer((req, res) => {
9319
9870
  this.onRequest(req, res);
9320
9871
  });
9321
- const onError = (err8) => {
9322
- if (err8.code === "EADDRINUSE" && port !== 0) {
9872
+ const onError = (err9) => {
9873
+ if (err9.code === "EADDRINUSE" && port !== 0) {
9323
9874
  server.removeListener("error", onError);
9324
9875
  this.listen(bindAddr, 0).then(resolve10, reject);
9325
9876
  return;
9326
9877
  }
9327
- reject(err8);
9878
+ reject(err9);
9328
9879
  };
9329
9880
  server.on("error", onError);
9330
9881
  server.listen(port, bindAddr, () => {
@@ -9342,8 +9893,8 @@ var AdminServer = class {
9342
9893
  }
9343
9894
  /** Per-request handler: auth gate (when a token is set) → routing. */
9344
9895
  onRequest(req, res) {
9345
- void this.dispatch(req, res).catch((err8) => {
9346
- const message = err8 instanceof Error ? err8.message : String(err8);
9896
+ void this.dispatch(req, res).catch((err9) => {
9897
+ const message = err9 instanceof Error ? err9.message : String(err9);
9347
9898
  this.deps.logger.error("[AdminServer] unhandled error:", message);
9348
9899
  if (!res.headersSent) {
9349
9900
  res.writeHead(500, { "Content-Type": "application/json" });
@@ -9566,7 +10117,10 @@ var HTML_HEADERS = {
9566
10117
  function pageHtml(message) {
9567
10118
  return `<!doctype html><meta charset="utf-8"><title>omnicross login</title><body style="font-family:sans-serif;padding:2rem"><h2>${message}</h2><p>You can close this window and return to the terminal.</p></body>`;
9568
10119
  }
9569
- function awaitLoopbackCode(expectedState, timeoutMs = DEFAULT_TIMEOUT_MS, signal) {
10120
+ function awaitLoopbackCode(expectedState, timeoutMs = DEFAULT_TIMEOUT_MS, signal, binding = {}) {
10121
+ const port = binding.port ?? LOOPBACK_PORT;
10122
+ const callbackPath = binding.path ?? CALLBACK_PATH;
10123
+ const label = binding.label ?? "codex";
9570
10124
  return new Promise((resolve10, reject) => {
9571
10125
  let settled = false;
9572
10126
  const finish = (server2, fn) => {
@@ -9577,8 +10131,8 @@ function awaitLoopbackCode(expectedState, timeoutMs = DEFAULT_TIMEOUT_MS, signal
9577
10131
  server2.close();
9578
10132
  };
9579
10133
  const server = createServer2((req, res) => {
9580
- const url = new URL(req.url ?? "", `http://${LOOPBACK_HOST}:${LOOPBACK_PORT}`);
9581
- if (url.pathname !== CALLBACK_PATH) {
10134
+ const url = new URL(req.url ?? "", `http://${LOOPBACK_HOST}:${port}`);
10135
+ if (url.pathname !== callbackPath) {
9582
10136
  res.writeHead(404, HTML_HEADERS);
9583
10137
  res.end(pageHtml("Not found"));
9584
10138
  return;
@@ -9607,25 +10161,25 @@ function awaitLoopbackCode(expectedState, timeoutMs = DEFAULT_TIMEOUT_MS, signal
9607
10161
  return;
9608
10162
  }
9609
10163
  signal?.addEventListener("abort", abort, { once: true });
9610
- server.on("error", (err8) => {
10164
+ server.on("error", (err9) => {
9611
10165
  if (settled) return;
9612
10166
  settled = true;
9613
10167
  clearTimeout(timer);
9614
- if (err8.code === "EADDRINUSE") {
10168
+ if (err9.code === "EADDRINUSE") {
9615
10169
  reject(
9616
10170
  new Error(
9617
- `login: cannot bind ${LOOPBACK_HOST}:${LOOPBACK_PORT} (address in use) \u2014 another codex login or process is holding the port`
10171
+ `login: cannot bind ${LOOPBACK_HOST}:${port} (address in use) \u2014 another ${label} login or process is holding the port`
9618
10172
  )
9619
10173
  );
9620
10174
  } else {
9621
- reject(err8);
10175
+ reject(err9);
9622
10176
  }
9623
10177
  });
9624
10178
  const timer = setTimeout(() => {
9625
10179
  finish(server, () => reject(new Error(`login: timed out after ${Math.round(timeoutMs / 1e3)}s waiting for the callback`)));
9626
10180
  }, timeoutMs);
9627
10181
  if (typeof timer.unref === "function") timer.unref();
9628
- server.listen(LOOPBACK_PORT, LOOPBACK_HOST);
10182
+ server.listen(port, LOOPBACK_HOST);
9629
10183
  });
9630
10184
  }
9631
10185
 
@@ -9695,7 +10249,7 @@ function createPoolKeysLoader(getProviderRow, autoDisabled) {
9695
10249
 
9696
10250
  // src/allowance/ProviderKeyQuotaService.ts
9697
10251
  import { mergeExtraHeaders as mergeExtraHeaders2 } from "@omnicross/core";
9698
- import { fetchUpstream as fetchUpstream9 } from "@omnicross/core/pipeline/upstreamFetch";
10252
+ import { fetchUpstream as fetchUpstream11 } from "@omnicross/core/pipeline/upstreamFetch";
9699
10253
 
9700
10254
  // src/allowance/ProviderKeyQuota.ts
9701
10255
  var MINUTE_MS3 = 6e4;
@@ -9724,11 +10278,11 @@ function isoInstant3(value) {
9724
10278
  }
9725
10279
  return void 0;
9726
10280
  }
9727
- function secondsUntil8(instant, now) {
10281
+ function secondsUntil9(instant, now) {
9728
10282
  if (!instant) return void 0;
9729
10283
  return Math.max(0, Math.floor((Date.parse(instant) - now) / 1e3));
9730
10284
  }
9731
- function isRecord8(value) {
10285
+ function isRecord10(value) {
9732
10286
  return !!value && typeof value === "object" && !Array.isArray(value);
9733
10287
  }
9734
10288
  function detectProviderKeyQuotaAdapter(baseUrl) {
@@ -9795,17 +10349,17 @@ function zaiWindowIdLabel(durationMs) {
9795
10349
  return { id: "quota", label: "Quota" };
9796
10350
  }
9797
10351
  function parseZaiQuotaPayload(payload, now) {
9798
- if (!isRecord8(payload)) return null;
9799
- const data = isRecord8(payload["data"]) ? payload["data"] : payload;
10352
+ if (!isRecord10(payload)) return null;
10353
+ const data = isRecord10(payload["data"]) ? payload["data"] : payload;
9800
10354
  if (payload["success"] === false) return null;
9801
10355
  const limits = Array.isArray(data["limits"]) ? data["limits"] : [];
9802
10356
  const byWindow = /* @__PURE__ */ new Map();
9803
10357
  for (const raw of limits) {
9804
- if (!isRecord8(raw)) continue;
10358
+ if (!isRecord10(raw)) continue;
9805
10359
  const item = raw;
9806
10360
  if (item.type === void 0) continue;
9807
10361
  const details = raw["usageDetails"];
9808
- if (Array.isArray(details) && details.some((d) => isRecord8(d) && d["modelCode"] === "zread")) {
10362
+ if (Array.isArray(details) && details.some((d) => isRecord10(d) && d["modelCode"] === "zread")) {
9809
10363
  continue;
9810
10364
  }
9811
10365
  const durationMs = zaiWindowDurationMs(item);
@@ -9824,7 +10378,7 @@ function parseZaiQuotaPayload(payload, now) {
9824
10378
  usedPercent,
9825
10379
  ...durationMs !== void 0 ? { windowMinutes: Math.round(durationMs / MINUTE_MS3) } : {},
9826
10380
  ...resetsAt !== void 0 ? { resetsAt } : {},
9827
- remainingSeconds: secondsUntil8(resetsAt, now),
10381
+ remainingSeconds: secondsUntil9(resetsAt, now),
9828
10382
  state: "fresh"
9829
10383
  };
9830
10384
  const existing = byWindow.get(id);
@@ -9838,7 +10392,7 @@ function parseZaiQuotaPayload(payload, now) {
9838
10392
  var MINIMAX_STATUS_EXHAUSTED = 2;
9839
10393
  var MINIMAX_SHARED_BUCKET = "general";
9840
10394
  function parseMiniMaxBucket(value) {
9841
- if (!isRecord8(value)) return null;
10395
+ if (!isRecord10(value)) return null;
9842
10396
  const modelName = typeof value["model_name"] === "string" ? value["model_name"].trim() : "";
9843
10397
  if (!modelName) return null;
9844
10398
  const instant = (v) => {
@@ -9865,14 +10419,14 @@ function minimaxWindow(id, label, windowMinutes, resetsAtMs, remainingPercent, s
9865
10419
  usedPercent,
9866
10420
  ...windowMinutes !== void 0 ? { windowMinutes } : {},
9867
10421
  ...resetsAt !== void 0 ? { resetsAt } : {},
9868
- remainingSeconds: secondsUntil8(resetsAt, now),
10422
+ remainingSeconds: secondsUntil9(resetsAt, now),
9869
10423
  state: usedPercent !== null ? "fresh" : "unavailable"
9870
10424
  };
9871
10425
  }
9872
10426
  function parseMiniMaxTokenPlanPayload(payload, now) {
9873
- if (!isRecord8(payload)) return null;
10427
+ if (!isRecord10(payload)) return null;
9874
10428
  const baseResp = payload["base_resp"];
9875
- if (!isRecord8(baseResp) || baseResp["status_code"] !== 0) return null;
10429
+ if (!isRecord10(baseResp) || baseResp["status_code"] !== 0) return null;
9876
10430
  const buckets = Array.isArray(payload["model_remains"]) ? payload["model_remains"] : [];
9877
10431
  let general = null;
9878
10432
  for (const raw of buckets) {
@@ -9905,11 +10459,11 @@ function parseMiniMaxTokenPlanPayload(payload, now) {
9905
10459
  ];
9906
10460
  }
9907
10461
  function parseUmansUsagePayload(payload, now) {
9908
- if (!isRecord8(payload)) return null;
9909
- const limits = isRecord8(payload["limits"]) ? payload["limits"] : void 0;
9910
- const requests = limits && isRecord8(limits["requests"]) ? limits["requests"] : void 0;
9911
- const usage = isRecord8(payload["usage"]) ? payload["usage"] : void 0;
9912
- const window = isRecord8(payload["window"]) ? payload["window"] : void 0;
10462
+ if (!isRecord10(payload)) return null;
10463
+ const limits = isRecord10(payload["limits"]) ? payload["limits"] : void 0;
10464
+ const requests = limits && isRecord10(limits["requests"]) ? limits["requests"] : void 0;
10465
+ const usage = isRecord10(payload["usage"]) ? payload["usage"] : void 0;
10466
+ const window = isRecord10(payload["window"]) ? payload["window"] : void 0;
9913
10467
  const hardCap = finiteNumber5(requests?.["hard_cap"]);
9914
10468
  const softLimit = finiteNumber5(requests?.["limit"]);
9915
10469
  const requestsInWindow = finiteNumber5(usage?.["requests_in_window"]);
@@ -9930,15 +10484,15 @@ function parseUmansUsagePayload(payload, now) {
9930
10484
  usedPercent,
9931
10485
  windowMinutes: 5 * 60,
9932
10486
  ...resetsAt !== void 0 ? { resetsAt } : {},
9933
- remainingSeconds: secondsUntil8(resetsAt, now),
10487
+ remainingSeconds: secondsUntil9(resetsAt, now),
9934
10488
  state: "fresh"
9935
10489
  }
9936
10490
  ];
9937
10491
  }
9938
10492
  function parseSyntheticQuotasPayload(payload, now) {
9939
- if (!isRecord8(payload)) return null;
9940
- const fiveHour = isRecord8(payload["rollingFiveHourLimit"]) ? payload["rollingFiveHourLimit"] : void 0;
9941
- const weekly = isRecord8(payload["weeklyTokenLimit"]) ? payload["weeklyTokenLimit"] : void 0;
10493
+ if (!isRecord10(payload)) return null;
10494
+ const fiveHour = isRecord10(payload["rollingFiveHourLimit"]) ? payload["rollingFiveHourLimit"] : void 0;
10495
+ const weekly = isRecord10(payload["weeklyTokenLimit"]) ? payload["weeklyTokenLimit"] : void 0;
9942
10496
  const windows = [];
9943
10497
  if (fiveHour) {
9944
10498
  const max = finiteNumber5(fiveHour["max"]);
@@ -9952,7 +10506,7 @@ function parseSyntheticQuotasPayload(payload, now) {
9952
10506
  usedPercent,
9953
10507
  windowMinutes: 5 * 60,
9954
10508
  ...resetsAt !== void 0 ? { resetsAt } : {},
9955
- remainingSeconds: secondsUntil8(resetsAt, now),
10509
+ remainingSeconds: secondsUntil9(resetsAt, now),
9956
10510
  state: usedPercent !== null || resetsAt ? "fresh" : "unavailable"
9957
10511
  });
9958
10512
  }
@@ -9967,7 +10521,7 @@ function parseSyntheticQuotasPayload(payload, now) {
9967
10521
  usedPercent,
9968
10522
  windowMinutes: 7 * 24 * 60,
9969
10523
  ...resetsAt !== void 0 ? { resetsAt } : {},
9970
- remainingSeconds: secondsUntil8(resetsAt, now),
10524
+ remainingSeconds: secondsUntil9(resetsAt, now),
9971
10525
  state: usedPercent !== null || resetsAt ? "fresh" : "unavailable"
9972
10526
  });
9973
10527
  }
@@ -9979,12 +10533,12 @@ var CLINE_WINDOW_CONFIG = {
9979
10533
  monthly: { id: "thirty-day", label: "30 days", minutes: 30 * 24 * 60 }
9980
10534
  };
9981
10535
  function parseClinePassUsageLimitsPayload(payload, now) {
9982
- if (!isRecord8(payload)) return null;
9983
- const data = isRecord8(payload["data"]) ? payload["data"] : payload;
10536
+ if (!isRecord10(payload)) return null;
10537
+ const data = isRecord10(payload["data"]) ? payload["data"] : payload;
9984
10538
  const limits = Array.isArray(data["limits"]) ? data["limits"] : [];
9985
10539
  const windows = [];
9986
10540
  for (const raw of limits) {
9987
- if (!isRecord8(raw)) continue;
10541
+ if (!isRecord10(raw)) continue;
9988
10542
  const config = CLINE_WINDOW_CONFIG[typeof raw["type"] === "string" ? raw["type"] : ""];
9989
10543
  if (!config) continue;
9990
10544
  const usedPercent = finitePercent4(raw["percentUsed"]);
@@ -9997,7 +10551,7 @@ function parseClinePassUsageLimitsPayload(payload, now) {
9997
10551
  usedPercent,
9998
10552
  windowMinutes: config.minutes,
9999
10553
  ...resetsAt !== void 0 ? { resetsAt } : {},
10000
- remainingSeconds: secondsUntil8(resetsAt, now),
10554
+ remainingSeconds: secondsUntil9(resetsAt, now),
10001
10555
  state: "fresh"
10002
10556
  });
10003
10557
  }
@@ -10036,7 +10590,7 @@ function rowKeyEntries(row) {
10036
10590
  return [];
10037
10591
  }
10038
10592
  var ProviderKeyQuotaService = class {
10039
- constructor(box, fetchImpl = (url, init) => fetchUpstream9(url, init, { redactBodies: true }), now = Date.now) {
10593
+ constructor(box, fetchImpl = (url, init) => fetchUpstream11(url, init, { redactBodies: true }), now = Date.now) {
10040
10594
  this.box = box;
10041
10595
  this.fetchImpl = fetchImpl;
10042
10596
  this.now = now;
@@ -10643,12 +11197,12 @@ function createImageDoctorService(options) {
10643
11197
  authStrategy: strategy,
10644
11198
  generationTimeoutMs: config.queue.generationTimeoutMs
10645
11199
  }));
10646
- const readAccount = async () => {
10647
- const codex = (await options.subscriptionAccounts.listAll()).find((entry) => entry.providerId === "codex");
11200
+ const readAccount = async (providerId) => {
11201
+ const entry = (await options.subscriptionAccounts.listAll()).find((candidate) => candidate.providerId === providerId);
10648
11202
  return Object.freeze({
10649
- present: codex !== void 0,
10650
- usable: codex?.credentialStatus.ok === true,
10651
- reason: codex === void 0 ? "missing" : codex.credentialStatus.ok ? "ready" : "unavailable"
11203
+ present: entry !== void 0,
11204
+ usable: entry?.credentialStatus.ok === true,
11205
+ reason: entry === void 0 ? "missing" : entry.credentialStatus.ok ? "ready" : "unavailable"
10652
11206
  });
10653
11207
  };
10654
11208
  const activePaths = () => options.storageCatalog.active().resolver;
@@ -10686,6 +11240,7 @@ function createImageDoctorService(options) {
10686
11240
  } catch {
10687
11241
  storesValid = false;
10688
11242
  }
11243
+ const antigravityAccount = await readAccount("antigravity");
10689
11244
  const rows = await options.keyDb.outboundApiKeysList();
10690
11245
  let legacyRows = 0;
10691
11246
  let invalidRows = 0;
@@ -10702,7 +11257,7 @@ function createImageDoctorService(options) {
10702
11257
  invalidRows += 1;
10703
11258
  }
10704
11259
  }
10705
- const account = await readAccount();
11260
+ const account = await readAccount("codex");
10706
11261
  let evidence;
10707
11262
  let evidenceStore;
10708
11263
  try {
@@ -10716,10 +11271,11 @@ function createImageDoctorService(options) {
10716
11271
  return Object.freeze({
10717
11272
  config: Object.freeze({
10718
11273
  enabled: config.enabled,
10719
- provider: config.provider,
11274
+ provider: config.models[config.defaultModel] ?? "codex-subscription",
10720
11275
  model: config.defaultModel,
10721
11276
  valid: configErrors.length === 0,
10722
- errorCount: configErrors.length
11277
+ errorCount: configErrors.length,
11278
+ routedProviders: Object.freeze([...new Set(Object.values(config.models))])
10723
11279
  }),
10724
11280
  roots: Object.freeze({
10725
11281
  valid: verifiedAreas === ROOT_AREAS.length,
@@ -10744,12 +11300,13 @@ function createImageDoctorService(options) {
10744
11300
  imagesAuthorizedRows
10745
11301
  }),
10746
11302
  account,
11303
+ antigravityAccount,
10747
11304
  evidence: Object.freeze(evidence)
10748
11305
  });
10749
11306
  },
10750
11307
  verifyLive: async (config, signal) => {
10751
11308
  if (!config.enabled) return Object.freeze({ ok: false, code: "images_disabled" });
10752
- const account = await readAccount();
11309
+ const account = await readAccount("codex");
10753
11310
  if (!account.usable) {
10754
11311
  return Object.freeze({ ok: false, code: "codex_account_unavailable" });
10755
11312
  }
@@ -11010,6 +11567,7 @@ import {
11010
11567
  validateImagesServerConfig as validateImagesServerConfig3
11011
11568
  } from "@omnicross/core/outbound-api";
11012
11569
  import {
11570
+ createAntigravitySubscriptionImageProvider,
11013
11571
  createCodexSubscriptionImageProvider
11014
11572
  } from "@omnicross/subscriptions";
11015
11573
 
@@ -11041,10 +11599,11 @@ function createTrustedImageApiRuntimeResolver(options) {
11041
11599
  throw new TypeError("enabled image remote loading requires a proven resolver");
11042
11600
  }
11043
11601
  const hmacKey = Buffer.from(options.hmacKey);
11044
- const modelAliases = new Map(Object.entries(options.config.modelAliases));
11602
+ const modelAliases = new Map(Object.entries(options.config.aliases));
11603
+ const modelRoutes = new Map(Object.entries(options.config.models));
11045
11604
  const limits = Object.freeze({ ...options.config.limits });
11046
- const providerId = options.config.provider;
11047
11605
  const defaultModel = options.config.defaultModel;
11606
+ const providerId = modelRoutes.get(defaultModel) ?? "codex-subscription";
11048
11607
  const referenceStore = options.referenceStore;
11049
11608
  const retention = Object.freeze({
11050
11609
  enabled: true,
@@ -11065,6 +11624,7 @@ function createTrustedImageApiRuntimeResolver(options) {
11065
11624
  providerId,
11066
11625
  defaultModel,
11067
11626
  modelAliases,
11627
+ modelRoutes,
11068
11628
  limits,
11069
11629
  ...preferredAccountId ? { preferredAccountId } : {},
11070
11630
  ...preferredAccountGroup ? { preferredAccountGroup } : {},
@@ -11469,7 +12029,9 @@ var GENERATION_ID = /^[A-Za-z0-9][A-Za-z0-9_.:-]{0,127}$/u;
11469
12029
  function snapshotConfig(config) {
11470
12030
  return {
11471
12031
  ...config,
11472
- modelAliases: { ...config.modelAliases },
12032
+ models: { ...config.models },
12033
+ aliases: { ...config.aliases },
12034
+ codex: { ...config.codex },
11473
12035
  account: { ...config.account },
11474
12036
  queue: { ...config.queue },
11475
12037
  temporary: { ...config.temporary },
@@ -11503,10 +12065,15 @@ function createImageRuntimeGeneration(options) {
11503
12065
  }
11504
12066
  });
11505
12067
  }
11506
- const authStrategy = options.subscriptionAccounts.getStrategy("codex");
11507
- if (!authStrategy || authStrategy.providerId !== "codex") {
12068
+ const routedProviders = [...new Set(Object.values(config.models))];
12069
+ const codexStrategy = routedProviders.includes("codex-subscription") ? options.subscriptionAccounts.getStrategy("codex") : void 0;
12070
+ if (routedProviders.includes("codex-subscription") && (!codexStrategy || codexStrategy.providerId !== "codex")) {
11508
12071
  throw new TypeError("enabled image runtime requires the Codex subscription strategy");
11509
12072
  }
12073
+ const antigravityStrategy = routedProviders.includes("antigravity-subscription") ? options.subscriptionAccounts.getStrategy("antigravity") : void 0;
12074
+ if (routedProviders.includes("antigravity-subscription") && (!antigravityStrategy || antigravityStrategy.providerId !== "antigravity")) {
12075
+ throw new TypeError("enabled image runtime requires the Antigravity subscription strategy");
12076
+ }
11510
12077
  const privateHmacKey = options.privateHmacKey ? Buffer.from(options.privateHmacKey) : loadOrCreateImageTenantHmacSalt(options.storage.paths, randomBytes6);
11511
12078
  if (privateHmacKey.byteLength !== 32) {
11512
12079
  privateHmacKey.fill(0);
@@ -11541,23 +12108,32 @@ function createImageRuntimeGeneration(options) {
11541
12108
  if (options.testOnlySyntheticVerifiedProvider && options.testOnlySyntheticVerifiedProvider.label !== "synthetic-verified-image-provider-test-only") {
11542
12109
  throw new TypeError("synthetic verified image provider test seam label is invalid");
11543
12110
  }
11544
- const provider = options.testOnlySyntheticVerifiedProvider ? options.testOnlySyntheticVerifiedProvider.createProvider({
12111
+ const providers = options.testOnlySyntheticVerifiedProvider ? [options.testOnlySyntheticVerifiedProvider.createProvider({
11545
12112
  generationId: options.generationId,
11546
12113
  scheduler,
11547
12114
  now: options.now ?? Date.now,
11548
12115
  referenceStore: options.storage.referenceStore,
11549
12116
  stateStore: options.storage.stateStore
11550
- }) : createCodexSubscriptionImageProvider({
11551
- authStrategy,
12117
+ })] : routedProviders.map((providerId) => providerId === "codex-subscription" ? createCodexSubscriptionImageProvider({
12118
+ authStrategy: codexStrategy,
11552
12119
  evidenceSource: generationEvidenceSource,
11553
12120
  executionScheduler: scheduler,
11554
12121
  generationTimeoutMs: config.queue.generationTimeoutMs,
12122
+ now: options.now,
12123
+ wire: {
12124
+ imageModel: config.codex.imageModel,
12125
+ carrierModel: config.codex.carrierModel
12126
+ }
12127
+ }) : createAntigravitySubscriptionImageProvider({
12128
+ authStrategy: antigravityStrategy,
12129
+ executionScheduler: scheduler,
12130
+ generationTimeoutMs: config.queue.generationTimeoutMs,
11555
12131
  now: options.now
11556
- });
11557
- if (provider.id !== config.provider) {
12132
+ }));
12133
+ if (providers.length === 1 && providers[0].id !== "codex-subscription") {
11558
12134
  throw new TypeError("synthetic verified image provider id must match configured provider");
11559
12135
  }
11560
- const providerRegistry = new ImageProviderRegistry([provider]);
12136
+ const providerRegistry = new ImageProviderRegistry(providers);
11561
12137
  const orchestrator = new ImageOrchestrator({
11562
12138
  registry: providerRegistry,
11563
12139
  referenceStore: options.storage.referenceStore,
@@ -11579,35 +12155,53 @@ function createImageRuntimeGeneration(options) {
11579
12155
  ...options.createCallId ? { createCallId: options.createCallId } : {},
11580
12156
  ...options.now ? { now: options.now } : {}
11581
12157
  });
12158
+ const defaultProviderId = config.models[config.defaultModel] ?? "codex-subscription";
12159
+ const inspectOneProvider = async (providerId, apiKeyId) => {
12160
+ const capabilities = await orchestrator.getCapabilities(providerId, {
12161
+ requestId: `${options.generationId}:capability-inspection`,
12162
+ tenantId: apiKeyId,
12163
+ signal: new AbortController().signal,
12164
+ sessionKey: `outbound:images:${apiKeyId}`,
12165
+ ...config.account.id ? { preferredAccountId: config.account.id } : {},
12166
+ ...config.account.group ? { preferredAccountGroup: config.account.group } : {},
12167
+ boundAccountFallbackPolicy: config.account.fallback
12168
+ });
12169
+ return capabilities;
12170
+ };
11582
12171
  const inspectCapability = async (apiKeyId) => {
11583
- try {
11584
- const capabilities = await orchestrator.getCapabilities(config.provider, {
11585
- requestId: `${options.generationId}:capability-inspection`,
11586
- tenantId: apiKeyId,
11587
- signal: new AbortController().signal,
11588
- sessionKey: `outbound:images:${apiKeyId}`,
11589
- ...config.account.id ? { preferredAccountId: config.account.id } : {},
11590
- ...config.account.group ? { preferredAccountGroup: config.account.group } : {},
11591
- boundAccountFallbackPolicy: config.account.fallback
11592
- });
11593
- const available = capabilities.available === true && capabilities.generate === true && capabilities.models.includes(config.defaultModel);
11594
- return Object.freeze({
11595
- enabled: true,
11596
- available,
11597
- providerId: config.provider,
11598
- model: config.defaultModel,
11599
- ...!available ? { reason: capabilities.reason ?? "runtime_unavailable" } : {},
11600
- capabilities
11601
- });
11602
- } catch (error) {
12172
+ const providerCapabilities = /* @__PURE__ */ new Map();
12173
+ let defaultProviderError;
12174
+ for (const providerId of [...new Set(Object.values(config.models))]) {
12175
+ try {
12176
+ providerCapabilities.set(providerId, await inspectOneProvider(providerId, apiKeyId));
12177
+ } catch (error) {
12178
+ if (providerId === defaultProviderId) defaultProviderError = error;
12179
+ }
12180
+ }
12181
+ const routedModels = Object.freeze(
12182
+ [...providerCapabilities.entries()].flatMap(([providerId, capabilities2]) => capabilities2.available === true && capabilities2.generate === true ? Object.entries(config.models).filter(([model, modelProvider]) => modelProvider === providerId && capabilities2.models.includes(model)).map(([model]) => model) : [])
12183
+ );
12184
+ const capabilities = providerCapabilities.get(defaultProviderId);
12185
+ if (!capabilities) {
11603
12186
  return Object.freeze({
11604
12187
  enabled: true,
11605
12188
  available: false,
11606
- providerId: config.provider,
12189
+ providerId: defaultProviderId,
11607
12190
  model: config.defaultModel,
11608
- reason: error instanceof ImageGenerationError4 && (error.code === "upstream_auth_required" || error.code === "invalid_api_key") ? "account_unverified" : "runtime_unavailable"
12191
+ routedModels,
12192
+ reason: defaultProviderError instanceof ImageGenerationError4 && (defaultProviderError.code === "upstream_auth_required" || defaultProviderError.code === "invalid_api_key") ? "account_unverified" : "runtime_unavailable"
11609
12193
  });
11610
12194
  }
12195
+ const available = capabilities.available === true && capabilities.generate === true && capabilities.models.includes(config.defaultModel);
12196
+ return Object.freeze({
12197
+ enabled: true,
12198
+ available,
12199
+ providerId: defaultProviderId,
12200
+ model: config.defaultModel,
12201
+ routedModels,
12202
+ ...!available ? { reason: capabilities.reason ?? "runtime_unavailable" } : {},
12203
+ capabilities
12204
+ });
11611
12205
  };
11612
12206
  const resolverToDispose = runtimeResolver;
11613
12207
  const schedulerToDispose = scheduler;
@@ -11645,7 +12239,7 @@ function createImageRuntimeGeneration(options) {
11645
12239
  imageApi,
11646
12240
  hosted,
11647
12241
  hostedRuntime: Object.freeze({
11648
- providerId: config.provider,
12242
+ providerId: defaultProviderId,
11649
12243
  imageModel: config.defaultModel,
11650
12244
  referenceTtlMs: config.references.ttlMs,
11651
12245
  maxOutputBytes: config.limits.maxOutputBytes,
@@ -14642,6 +15236,9 @@ var ImageRuntimeManager = class {
14642
15236
  }
14643
15237
  async listAvailableModels(apiKeyId) {
14644
15238
  const inspection = await this.inspectCapability(apiKeyId);
15239
+ if (inspection.routedModels !== void 0) {
15240
+ return Object.freeze([...inspection.routedModels]);
15241
+ }
14645
15242
  return inspection.available && inspection.model === "gpt-image-2" ? Object.freeze([inspection.model]) : Object.freeze([]);
14646
15243
  }
14647
15244
  resourceStatus() {
@@ -16857,11 +17454,13 @@ var JsonVoucherDb = class {
16857
17454
  // src/ports/JsonSubscriptionCredentialStore.ts
16858
17455
  import { existsSync as existsSync22, mkdirSync as mkdirSync6, readFileSync as readFileSync18, renameSync as renameSync12 } from "fs";
16859
17456
  import { dirname as dirname15 } from "path";
17457
+ import { getAntigravityProjectResolver as getAntigravityProjectResolver2 } from "@omnicross/core/auth/GeminiCodeAssistProjectResolver";
16860
17458
  import { getSharedAccountHealth as getSharedAccountHealth2 } from "@omnicross/core/pipeline/SubscriptionAccountHealth";
16861
17459
  import { getSharedAccountAllowanceScheduling as getSharedAccountAllowanceScheduling3 } from "@omnicross/core/pipeline/AccountAllowanceScheduling";
16862
- import { fetchUpstream as fetchUpstream10 } from "@omnicross/core/pipeline/upstreamFetch";
17460
+ import { fetchUpstream as fetchUpstream12 } from "@omnicross/core/pipeline/upstreamFetch";
16863
17461
  import { getSharedIdentityStore as getSharedIdentityStore2 } from "@omnicross/core/provider-proxy/identity/SubscriptionIdentityStore";
16864
17462
  import {
17463
+ antigravityOAuth as antigravityOAuth2,
16865
17464
  claudeOAuth as claudeOAuth2,
16866
17465
  codexOAuth as codexOAuth2,
16867
17466
  geminiOAuth as geminiOAuth2,
@@ -17015,7 +17614,7 @@ var JsonSubscriptionCredentialStore = class {
17015
17614
  * a plaintext token pair into `upstream-trace.jsonl`.
17016
17615
  */
17017
17616
  buildRefreshFetch(providerId, accountId) {
17018
- return this.fetchImpl ?? ((url, init) => fetchUpstream10(url, init, { providerId, accountId, redactBodies: true }));
17617
+ return this.fetchImpl ?? ((url, init) => fetchUpstream12(url, init, { providerId, accountId, redactBodies: true }));
17019
17618
  }
17020
17619
  /**
17021
17620
  * In-flight refresh coalescing. OAuth refresh tokens are
@@ -17056,7 +17655,7 @@ var JsonSubscriptionCredentialStore = class {
17056
17655
  * other hot reads. Never returns token material.
17057
17656
  */
17058
17657
  getAccountProxy(providerId, accountId) {
17059
- if (providerId !== "claude" && providerId !== "codex" && providerId !== "gemini" && providerId !== "opencodego" && providerId !== "kimi" && providerId !== "grok" && providerId !== "copilot") {
17658
+ if (providerId !== "claude" && providerId !== "codex" && providerId !== "gemini" && providerId !== "opencodego" && providerId !== "kimi" && providerId !== "grok" && providerId !== "copilot" && providerId !== "antigravity") {
17060
17659
  return void 0;
17061
17660
  }
17062
17661
  return getAccountProxy(this.readConfig(), providerId, accountId);
@@ -17075,7 +17674,7 @@ var JsonSubscriptionCredentialStore = class {
17075
17674
  const fingerprintOn = identityStore.isEnabled();
17076
17675
  const now = Date.now();
17077
17676
  const out = {};
17078
- for (const provider of ["claude", "codex", "gemini", "opencodego", "kimi", "grok", "copilot"]) {
17677
+ for (const provider of ["claude", "codex", "gemini", "opencodego", "kimi", "grok", "copilot", "antigravity"]) {
17079
17678
  const sanitized = sanitizeAccounts(config, provider);
17080
17679
  if (sanitized.length === 0) continue;
17081
17680
  for (const account of sanitized) {
@@ -17334,6 +17933,76 @@ var JsonSubscriptionCredentialStore = class {
17334
17933
  return false;
17335
17934
  });
17336
17935
  }
17936
+ /**
17937
+ * Refresh the Antigravity (Google) OAuth access token. Like gemini, the
17938
+ * Google token endpoint does NOT return a refresh_token on refresh, so this
17939
+ * writes ONLY access+expiresAt (+status/lastRefreshedAt) and DELIBERATELY
17940
+ * preserves the stored `refreshToken` — plus the account's `projectId`
17941
+ * (a handshake product; the post-refresh re-validation is the refresh
17942
+ * scheduler's hook, not this write) and `email`. HONEST `false` when no
17943
+ * refresh_token.
17944
+ */
17945
+ async refreshAntigravityToken() {
17946
+ return this.coalesce("antigravity:active", async () => {
17947
+ const config = this.readConfig();
17948
+ const active = getActiveAccount(config, "antigravity");
17949
+ const antigravity = active?.tokens;
17950
+ if (!active || !antigravity?.refreshToken) return false;
17951
+ const capturedId = active.id;
17952
+ this.materializeMigration(config);
17953
+ const refreshFetch = this.buildRefreshFetch("antigravity", capturedId);
17954
+ try {
17955
+ const result = await antigravityOAuth2.refreshAccessToken(antigravity.refreshToken, refreshFetch);
17956
+ const expiresAt = new Date(Date.now() + result.expiresIn * 1e3).toISOString();
17957
+ const next = {
17958
+ ...antigravity,
17959
+ // KEEP the existing refreshToken/projectId/email.
17960
+ accessToken: result.accessToken,
17961
+ expiresAt,
17962
+ status: "authorized",
17963
+ lastRefreshedAt: (/* @__PURE__ */ new Date()).toISOString(),
17964
+ errorMessage: void 0
17965
+ };
17966
+ this.writeBackById("antigravity", capturedId, next);
17967
+ await this.revalidateAntigravityProject(capturedId);
17968
+ return true;
17969
+ } catch (error) {
17970
+ this.markExpiredById("antigravity", capturedId, antigravity, error);
17971
+ return false;
17972
+ }
17973
+ });
17974
+ }
17975
+ /**
17976
+ * Post-refresh project re-validation hook (antigravity design D8): after a
17977
+ * successful antigravity token refresh, re-run the Code Assist project
17978
+ * handshake and write the (possibly rotated) `projectId` back to the account.
17979
+ * A handshake FAILURE keeps the stored projectId untouched (logged) so the
17980
+ * account keeps serving with the last-known-good project until a later
17981
+ * refresh succeeds. Returns whether the handshake produced a project.
17982
+ */
17983
+ async revalidateAntigravityProject(accountId) {
17984
+ const before = getAccountById(this.readConfig(), "antigravity", accountId);
17985
+ const accessToken = before?.tokens?.accessToken;
17986
+ if (!accessToken) return false;
17987
+ try {
17988
+ const projectId = await getAntigravityProjectResolver2().resolveProject(accessToken);
17989
+ if (projectId !== void 0) {
17990
+ const config = this.readConfig();
17991
+ const account = getAccountById(config, "antigravity", accountId);
17992
+ const tokens = account?.tokens;
17993
+ if (account && tokens?.accessToken === accessToken && tokens.projectId !== projectId) {
17994
+ this.writeBackById("antigravity", accountId, { ...tokens, projectId });
17995
+ }
17996
+ return true;
17997
+ }
17998
+ return false;
17999
+ } catch (error) {
18000
+ console.warn(
18001
+ `[JsonSubscriptionCredentialStore] antigravity project re-validation failed for account ${accountId}: ` + (error instanceof Error ? error.message : String(error))
18002
+ );
18003
+ return false;
18004
+ }
18005
+ }
17337
18006
  /**
17338
18007
  * Refresh a SPECIFIC managed account by id (background scheduler sweep and
17339
18008
  * account-pool resolution). It uses only that account's stored refresh
@@ -17362,6 +18031,7 @@ var JsonSubscriptionCredentialStore = class {
17362
18031
  };
17363
18032
  if (refreshed.idToken) next.idToken = refreshed.idToken;
17364
18033
  this.writeBackById(provider, id, next);
18034
+ if (provider === "antigravity") await this.revalidateAntigravityProject(id);
17365
18035
  return true;
17366
18036
  } catch (error) {
17367
18037
  this.markExpiredById(provider, id, captured, error);
@@ -17386,7 +18056,7 @@ var JsonSubscriptionCredentialStore = class {
17386
18056
  }
17387
18057
  const oauth = account.tokens;
17388
18058
  if (!oauth.accessToken) return null;
17389
- if (providerId === "codex" || providerId === "gemini" || providerId === "kimi" || providerId === "grok" || providerId === "copilot") {
18059
+ if (providerId === "codex" || providerId === "gemini" || providerId === "kimi" || providerId === "grok" || providerId === "copilot" || providerId === "antigravity") {
17390
18060
  const expiresAtMs = oauth.expiresAt ? Date.parse(oauth.expiresAt) : 0;
17391
18061
  const expiringSoon = expiresAtMs > 0 && Date.now() >= expiresAtMs - ACCOUNT_REFRESH_LEAD_MS;
17392
18062
  if (expiringSoon && oauth.refreshToken) {
@@ -17502,6 +18172,13 @@ var JsonSubscriptionCredentialStore = class {
17502
18172
  if (provider === "copilot") {
17503
18173
  throw new Error("GitHub Copilot tokens cannot be refreshed \u2014 re-authenticate the account");
17504
18174
  }
18175
+ if (provider === "antigravity") {
18176
+ const r2 = await antigravityOAuth2.refreshAccessToken(refreshToken, refreshFetch);
18177
+ return {
18178
+ accessToken: r2.accessToken,
18179
+ expiresAt: new Date(Date.now() + r2.expiresIn * 1e3).toISOString()
18180
+ };
18181
+ }
17505
18182
  const flow = provider === "claude" ? claudeOAuth2 : provider === "codex" ? codexOAuth2 : geminiOAuth2;
17506
18183
  const r = await flow.refreshAccessToken(refreshToken, refreshFetch);
17507
18184
  return {
@@ -17745,7 +18422,7 @@ var JsonSubscriptionCredentialStore = class {
17745
18422
  };
17746
18423
 
17747
18424
  // src/AccountHealthProbeScheduler.ts
17748
- import { fetchUpstream as fetchUpstream11 } from "@omnicross/core/pipeline/upstreamFetch";
18425
+ import { fetchUpstream as fetchUpstream13 } from "@omnicross/core/pipeline/upstreamFetch";
17749
18426
 
17750
18427
  // src/probe/CodexGenerationProbe.ts
17751
18428
  import {
@@ -17900,7 +18577,11 @@ var PROVIDER_PROBE_PLANS = {
17900
18577
  // The Copilot quota endpoint (copilot_internal/user) is a verified FREE
17901
18578
  // authed GET but lives on api.github.com with its own auth dialect and a
17902
18579
  // monthly-only window — the allowance collector owns the health surface.
17903
- copilot: { kind: "local" }
18580
+ copilot: { kind: "local" },
18581
+ // Antigravity's quota endpoints are POST RPCs on daily-cloudcode-pa (not a
18582
+ // cheap GET) and need the antigravity/hub UA — the allowance collector owns
18583
+ // the health surface; the probe stays local.
18584
+ antigravity: { kind: "local" }
17904
18585
  };
17905
18586
  function probePlanFor(providerId) {
17906
18587
  return PROVIDER_PROBE_PLANS[providerId] ?? { kind: "local" };
@@ -17922,7 +18603,7 @@ var AccountHealthProbeScheduler = class {
17922
18603
  this.logger = logger;
17923
18604
  this.config = config;
17924
18605
  this.now = opts.now ?? Date.now;
17925
- this.fetchImpl = opts.fetchImpl ?? fetchUpstream11;
18606
+ this.fetchImpl = opts.fetchImpl ?? fetchUpstream13;
17926
18607
  this.sleep = opts.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
17927
18608
  this.planFor = opts.planFor ?? probePlanFor;
17928
18609
  }
@@ -19627,7 +20308,7 @@ var AuditWriter = class {
19627
20308
  import { appendFileSync as appendFileSync3, mkdirSync as mkdirSync8 } from "fs";
19628
20309
  import { createHmac as createHmac5 } from "crypto";
19629
20310
  import { join as join26 } from "path";
19630
- import { fetchUpstream as fetchUpstream12 } from "@omnicross/core/pipeline/upstreamFetch";
20311
+ import { fetchUpstream as fetchUpstream14 } from "@omnicross/core/pipeline/upstreamFetch";
19631
20312
 
19632
20313
  // src/billing/billingFiles.ts
19633
20314
  var BILLING_FILE_RE = /^billing-(\d{4})-(\d{2})-(\d{2})\.jsonl$/;
@@ -19650,7 +20331,7 @@ var BillingPublisher = class {
19650
20331
  constructor(billingDir, logger, opts = {}) {
19651
20332
  this.billingDir = billingDir;
19652
20333
  this.logger = logger;
19653
- this.fetchImpl = opts.fetchImpl ?? ((url, init) => fetchUpstream12(url, init));
20334
+ this.fetchImpl = opts.fetchImpl ?? ((url, init) => fetchUpstream14(url, init));
19654
20335
  this.defer = opts.defer ?? ((fn) => setTimeout(fn, 0));
19655
20336
  this.timeoutMs = opts.timeoutMs ?? BILLING_POST_TIMEOUT_MS;
19656
20337
  this.now = opts.now ?? Date.now;
@@ -19900,7 +20581,7 @@ var BillingRetrySweeper = class {
19900
20581
  // src/TokenRefreshScheduler.ts
19901
20582
  var REFRESH_LEAD_MS2 = 5 * 6e4;
19902
20583
  var SWEEP_INTERVAL_MS5 = 6e4;
19903
- var OAUTH_PROVIDERS2 = ["claude", "codex", "gemini", "kimi", "grok", "copilot"];
20584
+ var OAUTH_PROVIDERS2 = ["claude", "codex", "gemini", "kimi", "grok", "copilot", "antigravity"];
19904
20585
  var TokenRefreshScheduler = class {
19905
20586
  constructor(store, logger, intervalMs = SWEEP_INTERVAL_MS5, leadMs = REFRESH_LEAD_MS2) {
19906
20587
  this.store = store;
@@ -19991,6 +20672,8 @@ var TokenRefreshScheduler = class {
19991
20672
  // never reaches this — the branch exists for union totality.
19992
20673
  case "copilot":
19993
20674
  return this.store.refreshCopilotToken();
20675
+ case "antigravity":
20676
+ return this.store.refreshAntigravityToken();
19994
20677
  }
19995
20678
  }
19996
20679
  };
@@ -20069,7 +20752,7 @@ function createRouteLeaseSubscriptionPreflight(credentials) {
20069
20752
 
20070
20753
  // src/webhook/WebhookDispatcher.ts
20071
20754
  import { createHmac as createHmac6 } from "crypto";
20072
- import { fetchUpstream as fetchUpstream13 } from "@omnicross/core/pipeline/upstreamFetch";
20755
+ import { fetchUpstream as fetchUpstream15 } from "@omnicross/core/pipeline/upstreamFetch";
20073
20756
  var WEBHOOK_MAX_ATTEMPTS = 3;
20074
20757
  var WEBHOOK_QUEUE_MAX = 1e3;
20075
20758
  var WEBHOOK_SEND_TIMEOUT_MS = 1e4;
@@ -20089,7 +20772,7 @@ var WebhookDispatcher = class {
20089
20772
  sleep;
20090
20773
  now;
20091
20774
  constructor(opts = {}) {
20092
- this.fetchImpl = opts.fetchImpl ?? ((url, init) => fetchUpstream13(url, init));
20775
+ this.fetchImpl = opts.fetchImpl ?? ((url, init) => fetchUpstream15(url, init));
20093
20776
  this.logger = opts.logger;
20094
20777
  this.maxAttempts = opts.maxAttempts ?? WEBHOOK_MAX_ATTEMPTS;
20095
20778
  this.queueMax = opts.queueMax ?? WEBHOOK_QUEUE_MAX;
@@ -20175,8 +20858,8 @@ var WebhookDispatcher = class {
20175
20858
  signal: AbortSignal.timeout(this.timeoutMs)
20176
20859
  });
20177
20860
  return res.ok ? { ok: true, status: res.status } : { ok: false, status: res.status };
20178
- } catch (err8) {
20179
- return { ok: false, error: err8 instanceof Error ? err8.message : String(err8) };
20861
+ } catch (err9) {
20862
+ return { ok: false, error: err9 instanceof Error ? err9.message : String(err9) };
20180
20863
  }
20181
20864
  }
20182
20865
  /**
@@ -20318,7 +21001,7 @@ function buildDaemon(config, paths) {
20318
21001
  setSecretBox(secretBox3);
20319
21002
  setSecretBox2(secretBox3);
20320
21003
  const decryptedConfig = decryptConfigSecrets(config, secretBox3);
20321
- const accountAllowanceStore = new AccountAllowanceStore9(
21004
+ const accountAllowanceStore = new AccountAllowanceStore10(
20322
21005
  Date.now,
20323
21006
  void 0,
20324
21007
  new JsonAccountAllowancePersistence(defaultAccountAllowancePath(paths.configPath))
@@ -20362,6 +21045,8 @@ function buildDaemon(config, paths) {
20362
21045
  })
20363
21046
  );
20364
21047
  setGeminiCodeAssistResolver(getGeminiCodeAssistProjectResolver2());
21048
+ setAntigravitySandboxFailover(decryptedConfig.antigravity?.sandboxFailover === true);
21049
+ setOpenCodeGoUserAgent(decryptedConfig.opencodego?.userAgent ?? null);
20365
21050
  const autoDisableStore = new AutoDisableStore();
20366
21051
  const providerKeyQuotaService = new ProviderKeyQuotaService(secretBox3);
20367
21052
  const apiKeyPool = new ApiKeyPoolService(
@@ -20380,7 +21065,7 @@ function buildDaemon(config, paths) {
20380
21065
  const pricingEngine = new PricingEngine(pricingStore, logger, {
20381
21066
  // Catalog egress follows the same global/env proxy policy as every other
20382
21067
  // daemon upstream call; no provider/account override applies here.
20383
- fetchImpl: ((input, init) => fetchUpstream14(String(input), init ?? {}))
21068
+ fetchImpl: ((input, init) => fetchUpstream16(String(input), init ?? {}))
20384
21069
  });
20385
21070
  const pricingRefreshScheduler = new PricingRefreshScheduler(
20386
21071
  pricingEngine,
@@ -20665,7 +21350,7 @@ function buildDaemon(config, paths) {
20665
21350
  // — `server.proxy.byProvider[...]` was silently skipped — and the call was
20666
21351
  // excluded from the upstream trace, so a failing login left no evidence.
20667
21352
  // `redactBodies` keeps the code/verifier + minted token out of that trace.
20668
- oauthExchangeFetch: paths.oauthExchangeFetch ? () => paths.oauthExchangeFetch : (providerId) => (url, init) => fetchUpstream14(url, init, { providerId, redactBodies: true }),
21353
+ oauthExchangeFetch: paths.oauthExchangeFetch ? () => paths.oauthExchangeFetch : (providerId) => (url, init) => fetchUpstream16(url, init, { providerId, redactBodies: true }),
20669
21354
  subscriptionAccountAppender: credentialStore,
20670
21355
  // Codex interactive OAuth (app-parity-2 child 5) — the async loopback flow store
20671
21356
  // + the one-shot 127.0.0.1:1455 listener. Token captured + persisted daemon-side;
@@ -20680,6 +21365,21 @@ function buildDaemon(config, paths) {
20680
21365
  // Grok interactive OAuth — the same async DEVICE-CODE shape as kimi.
20681
21366
  grokSessions: new CodexOAuthSessionStore(),
20682
21367
  copilotSessions: new CodexOAuthSessionStore(),
21368
+ // Antigravity interactive OAuth — the async LOOPBACK flow store + the
21369
+ // one-shot 127.0.0.1:51121 listener (same shape as codex; test seam below).
21370
+ antigravitySessions: new CodexOAuthSessionStore(),
21371
+ antigravityAwaitLoopback: paths.antigravityAwaitLoopback ?? ((state, timeoutMs, signal) => awaitLoopbackCode(state, timeoutMs, signal, {
21372
+ port: 51121,
21373
+ path: "/oauth-callback",
21374
+ label: "antigravity"
21375
+ })),
21376
+ // The dynamic model-catalog probe's token source (the ACTIVE antigravity
21377
+ // account; refreshed by the by-id near-expiry seam inside the lookup).
21378
+ resolveAntigravityAccessToken: async () => {
21379
+ const config2 = await credentialStore.getFullConfig();
21380
+ const activeId = config2.activeAntigravityAccountId ?? config2.antigravityAccounts?.[0]?.id;
21381
+ return activeId ? credentialStore.getAccessTokenForAccount("antigravity", activeId) : null;
21382
+ },
20683
21383
  // Migration pack (app-parity child 6, design D2/D3) — the concrete credential
20684
21384
  // store provides BOTH the full DECRYPTED read (`getFullConfig`, export) and
20685
21385
  // the multi-account append (`appendProviderAccount`, import re-encrypts at-
@@ -20738,7 +21438,7 @@ function buildDaemon(config, paths) {
20738
21438
  });
20739
21439
  const webhookDispatcher = new WebhookDispatcher({
20740
21440
  logger,
20741
- fetchImpl: (url, init) => fetchUpstream14(url, init)
21441
+ fetchImpl: (url, init) => fetchUpstream16(url, init)
20742
21442
  });
20743
21443
  setWebhookRuntime(webhookDispatcher, getSharedAccountHealth4());
20744
21444
  const auditWriter = new AuditWriter(auditDir, logger);