@cortexkit/opencode-antigravity-auth 1.0.0 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/README.md +24 -8
  2. package/dist/index.js +1450 -467
  3. package/dist/index.js.map +4 -4
  4. package/dist/src/plugin/accounts.d.ts +36 -9
  5. package/dist/src/plugin/accounts.d.ts.map +1 -1
  6. package/dist/src/plugin/accounts.js +269 -57
  7. package/dist/src/plugin/accounts.js.map +1 -1
  8. package/dist/src/plugin/agy-request-metadata.d.ts +2 -0
  9. package/dist/src/plugin/agy-request-metadata.d.ts.map +1 -0
  10. package/dist/src/plugin/agy-request-metadata.js +2 -0
  11. package/dist/src/plugin/agy-request-metadata.js.map +1 -0
  12. package/dist/src/plugin/auth-doctor.d.ts +1 -1
  13. package/dist/src/plugin/auth-doctor.d.ts.map +1 -1
  14. package/dist/src/plugin/auth-doctor.js +9 -0
  15. package/dist/src/plugin/auth-doctor.js.map +1 -1
  16. package/dist/src/plugin/debug.d.ts.map +1 -1
  17. package/dist/src/plugin/debug.js +3 -2
  18. package/dist/src/plugin/debug.js.map +1 -1
  19. package/dist/src/plugin/gemini-dump.d.ts.map +1 -1
  20. package/dist/src/plugin/gemini-dump.js +8 -4
  21. package/dist/src/plugin/gemini-dump.js.map +1 -1
  22. package/dist/src/plugin/image-saver.d.ts +2 -21
  23. package/dist/src/plugin/image-saver.d.ts.map +1 -1
  24. package/dist/src/plugin/image-saver.js +29 -63
  25. package/dist/src/plugin/image-saver.js.map +1 -1
  26. package/dist/src/plugin/request-helpers.d.ts +1 -0
  27. package/dist/src/plugin/request-helpers.d.ts.map +1 -1
  28. package/dist/src/plugin/request-helpers.js +9 -4
  29. package/dist/src/plugin/request-helpers.js.map +1 -1
  30. package/dist/src/plugin/request.d.ts +15 -4
  31. package/dist/src/plugin/request.d.ts.map +1 -1
  32. package/dist/src/plugin/request.js +255 -71
  33. package/dist/src/plugin/request.js.map +1 -1
  34. package/dist/src/plugin/session-context.d.ts +21 -0
  35. package/dist/src/plugin/session-context.d.ts.map +1 -0
  36. package/dist/src/plugin/session-context.js +63 -0
  37. package/dist/src/plugin/session-context.js.map +1 -0
  38. package/dist/src/plugin/storage.d.ts +6 -0
  39. package/dist/src/plugin/storage.d.ts.map +1 -1
  40. package/dist/src/plugin/storage.js +27 -6
  41. package/dist/src/plugin/storage.js.map +1 -1
  42. package/dist/src/plugin/ui/auth-menu.d.ts +1 -1
  43. package/dist/src/plugin/ui/auth-menu.d.ts.map +1 -1
  44. package/dist/src/plugin/ui/auth-menu.js +3 -1
  45. package/dist/src/plugin/ui/auth-menu.js.map +1 -1
  46. package/dist/src/plugin/ui/quota-status.d.ts.map +1 -1
  47. package/dist/src/plugin/ui/quota-status.js +2 -1
  48. package/dist/src/plugin/ui/quota-status.js.map +1 -1
  49. package/dist/src/plugin.d.ts +16 -0
  50. package/dist/src/plugin.d.ts.map +1 -1
  51. package/dist/src/plugin.js +288 -102
  52. package/dist/src/plugin.js.map +1 -1
  53. package/package.json +2 -2
  54. package/dist/src/plugin/transform/claude.d.ts +0 -92
  55. package/dist/src/plugin/transform/claude.d.ts.map +0 -1
  56. package/dist/src/plugin/transform/claude.js +0 -280
  57. package/dist/src/plugin/transform/claude.js.map +0 -1
  58. package/dist/src/plugin/transform/gemini.d.ts +0 -100
  59. package/dist/src/plugin/transform/gemini.d.ts.map +0 -1
  60. package/dist/src/plugin/transform/gemini.js +0 -446
  61. package/dist/src/plugin/transform/gemini.js.map +0 -1
package/dist/index.js CHANGED
@@ -7,7 +7,6 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
7
7
 
8
8
  // src/plugin.ts
9
9
  import { exec } from "node:child_process";
10
- import crypto4 from "node:crypto";
11
10
 
12
11
  // ../../node_modules/@opencode-ai/plugin/dist/tool.js
13
12
  import { z } from "zod";
@@ -67,6 +66,90 @@ function createLogger(module) {
67
66
  };
68
67
  }
69
68
 
69
+ // ../core/dist/fingerprint.js
70
+ import * as crypto from "node:crypto";
71
+ var AGY_CLI_VERSION = "1.1.5";
72
+ var ANTIGRAVITY_API_CLIENT = "antigravity-cli";
73
+ var MAX_FINGERPRINT_HISTORY = 5;
74
+ function normalizeHarnessPlatform(platform = process.platform) {
75
+ return platform === "win32" ? "windows" : platform || "unknown";
76
+ }
77
+ function normalizeHarnessArch(arch = process.arch) {
78
+ switch (arch) {
79
+ case "x64":
80
+ return "amd64";
81
+ case "ia32":
82
+ return "386";
83
+ default:
84
+ return arch || "unknown";
85
+ }
86
+ }
87
+ function buildAntigravityHarnessUserAgent(version = AGY_CLI_VERSION, platform = process.platform, arch = process.arch, authMethod = "consumer") {
88
+ const osType = normalizeHarnessPlatform(platform);
89
+ const normalizedArch = normalizeHarnessArch(arch);
90
+ return `antigravity/cli/${version} (aidev_client; os_type=${osType}; arch=${normalizedArch}; auth_method=${authMethod})`;
91
+ }
92
+ function buildAntigravityHarnessLoadCodeAssistUserAgent(version = AGY_CLI_VERSION) {
93
+ return buildAntigravityHarnessUserAgent(version);
94
+ }
95
+ function platformToMetadataPlatform(platform = process.platform) {
96
+ return platform === "win32" ? "WINDOWS" : "MACOS";
97
+ }
98
+ function buildAntigravityLoadCodeAssistMetadata() {
99
+ return { ideType: "ANTIGRAVITY" };
100
+ }
101
+ function buildAntigravityHarnessBootstrapHeaders(accessToken) {
102
+ return {
103
+ "User-Agent": buildAntigravityHarnessLoadCodeAssistUserAgent(),
104
+ Authorization: `Bearer ${accessToken}`,
105
+ "Content-Type": "application/json",
106
+ "Accept-Encoding": "gzip"
107
+ };
108
+ }
109
+ function generateDeviceId() {
110
+ return crypto.randomUUID();
111
+ }
112
+ function generateSessionToken() {
113
+ return crypto.randomBytes(16).toString("hex");
114
+ }
115
+ function generateFingerprint() {
116
+ return {
117
+ deviceId: generateDeviceId(),
118
+ sessionToken: generateSessionToken(),
119
+ userAgent: buildAntigravityHarnessUserAgent(),
120
+ apiClient: ANTIGRAVITY_API_CLIENT,
121
+ clientMetadata: {
122
+ ideType: "ANTIGRAVITY",
123
+ platform: platformToMetadataPlatform(),
124
+ pluginType: "GEMINI"
125
+ },
126
+ createdAt: Date.now()
127
+ };
128
+ }
129
+ function updateFingerprintVersion(fingerprint) {
130
+ const userAgent = buildAntigravityHarnessUserAgent();
131
+ if (fingerprint.userAgent === userAgent) {
132
+ return false;
133
+ }
134
+ fingerprint.userAgent = userAgent;
135
+ return true;
136
+ }
137
+ function buildFingerprintHeaders(fingerprint) {
138
+ if (!fingerprint) {
139
+ return {};
140
+ }
141
+ return {
142
+ "User-Agent": fingerprint.userAgent
143
+ };
144
+ }
145
+ var sessionFingerprint = null;
146
+ function getSessionFingerprint() {
147
+ if (!sessionFingerprint) {
148
+ sessionFingerprint = generateFingerprint();
149
+ }
150
+ return sessionFingerprint;
151
+ }
152
+
70
153
  // ../core/dist/constants.js
71
154
  var ANTIGRAVITY_CLIENT_ID = "1071006060591-tmhssin2h21lcre235vtolojh4g403ep.apps.googleusercontent.com";
72
155
  var ANTIGRAVITY_CLIENT_SECRET = "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";
@@ -122,11 +205,6 @@ var GEMINI_CLI_HEADERS = {
122
205
  "X-Goog-Api-Client": "gl-node/22.17.0",
123
206
  "Client-Metadata": "ideType=IDE_UNSPECIFIED,platform=PLATFORM_UNSPECIFIED,pluginType=GEMINI"
124
207
  };
125
- function buildAntigravityPlatformArch() {
126
- const platform = process.platform === "win32" ? "windows" : process.platform || "unknown";
127
- const arch = process.arch === "x64" ? "amd64" : process.arch === "ia32" ? "386" : process.arch || "unknown";
128
- return `${platform}/${arch}`;
129
- }
130
208
  function getRandomizedHeaders(style, model) {
131
209
  if (style === "gemini-cli") {
132
210
  return {
@@ -135,9 +213,8 @@ function getRandomizedHeaders(style, model) {
135
213
  "Client-Metadata": GEMINI_CLI_HEADERS["Client-Metadata"]
136
214
  };
137
215
  }
138
- const platform = buildAntigravityPlatformArch();
139
216
  return {
140
- "User-Agent": `antigravity/cli/1.0.4 ${platform}`
217
+ "User-Agent": buildAntigravityHarnessUserAgent()
141
218
  };
142
219
  }
143
220
  var ANTIGRAVITY_PROVIDER_ID = "google";
@@ -205,91 +282,6 @@ function calculateTokenExpiry(requestTimeMs, expiresInSeconds) {
205
282
  return requestTimeMs + seconds * 1e3;
206
283
  }
207
284
 
208
- // ../core/dist/fingerprint.js
209
- import * as crypto from "node:crypto";
210
- var AGY_CLI_VERSION = "1.0.4";
211
- var ANTIGRAVITY_API_CLIENT = "antigravity-cli";
212
- var MAX_FINGERPRINT_HISTORY = 5;
213
- function normalizeHarnessPlatform(platform = process.platform) {
214
- return platform === "win32" ? "windows" : platform || "unknown";
215
- }
216
- function normalizeHarnessArch(arch = process.arch) {
217
- switch (arch) {
218
- case "x64":
219
- return "amd64";
220
- case "ia32":
221
- return "386";
222
- default:
223
- return arch || "unknown";
224
- }
225
- }
226
- function buildAntigravityHarnessPlatformArch(platform = process.platform, arch = process.arch) {
227
- return `${normalizeHarnessPlatform(platform)}/${normalizeHarnessArch(arch)}`;
228
- }
229
- function buildAntigravityHarnessUserAgent(version = AGY_CLI_VERSION, platform = process.platform, arch = process.arch) {
230
- return `antigravity/cli/${version} ${buildAntigravityHarnessPlatformArch(platform, arch)}`;
231
- }
232
- function buildAntigravityHarnessLoadCodeAssistUserAgent(version = AGY_CLI_VERSION) {
233
- return buildAntigravityHarnessUserAgent(version);
234
- }
235
- function platformToMetadataPlatform(platform = process.platform) {
236
- return platform === "win32" ? "WINDOWS" : "MACOS";
237
- }
238
- function buildAntigravityLoadCodeAssistMetadata() {
239
- return { ideType: "ANTIGRAVITY" };
240
- }
241
- function buildAntigravityHarnessBootstrapHeaders(accessToken) {
242
- return {
243
- "User-Agent": buildAntigravityHarnessLoadCodeAssistUserAgent(),
244
- Authorization: `Bearer ${accessToken}`,
245
- "Content-Type": "application/json",
246
- "Accept-Encoding": "gzip"
247
- };
248
- }
249
- function generateDeviceId() {
250
- return crypto.randomUUID();
251
- }
252
- function generateSessionToken() {
253
- return crypto.randomBytes(16).toString("hex");
254
- }
255
- function generateFingerprint() {
256
- return {
257
- deviceId: generateDeviceId(),
258
- sessionToken: generateSessionToken(),
259
- userAgent: buildAntigravityHarnessUserAgent(),
260
- apiClient: ANTIGRAVITY_API_CLIENT,
261
- clientMetadata: {
262
- ideType: "ANTIGRAVITY",
263
- platform: platformToMetadataPlatform(),
264
- pluginType: "GEMINI"
265
- },
266
- createdAt: Date.now()
267
- };
268
- }
269
- function updateFingerprintVersion(fingerprint) {
270
- const userAgent = buildAntigravityHarnessUserAgent();
271
- if (fingerprint.userAgent === userAgent) {
272
- return false;
273
- }
274
- fingerprint.userAgent = userAgent;
275
- return true;
276
- }
277
- function buildFingerprintHeaders(fingerprint) {
278
- if (!fingerprint) {
279
- return {};
280
- }
281
- return {
282
- "User-Agent": fingerprint.userAgent
283
- };
284
- }
285
- var sessionFingerprint = null;
286
- function getSessionFingerprint() {
287
- if (!sessionFingerprint) {
288
- sessionFingerprint = generateFingerprint();
289
- }
290
- return sessionFingerprint;
291
- }
292
-
293
285
  // ../core/dist/agy-transport.js
294
286
  import * as net from "node:net";
295
287
  import * as tls from "node:tls";
@@ -299,6 +291,7 @@ import { createGunzip } from "node:zlib";
299
291
  var DEFAULT_HTTPS_PORT = 443;
300
292
  var DEFAULT_PROXY_PORT = 8080;
301
293
  var DEFAULT_AGY_RESPONSE_HEADER_TIMEOUT_MS = 18e4;
294
+ var DEFAULT_AGY_IDLE_TIMEOUT_MS = 18e4;
302
295
  function headersToRecord(headers) {
303
296
  const result = {};
304
297
  if (!headers)
@@ -331,7 +324,7 @@ function buildAgyCliHeaderPairs(url, init = {}) {
331
324
  const headers = headersToRecord(init.headers);
332
325
  const body = bodyToBuffer(init.body);
333
326
  const host = parsedUrl.port ? `${parsedUrl.hostname}:${parsedUrl.port}` : parsedUrl.hostname;
334
- const userAgent = getHeader(headers, "User-Agent") ?? "antigravity/cli/1.0.4 darwin/arm64";
327
+ const userAgent = getHeader(headers, "User-Agent") ?? buildAntigravityHarnessUserAgent();
335
328
  const authorization = getHeader(headers, "Authorization");
336
329
  const contentType = getHeader(headers, "Content-Type") ?? "application/json";
337
330
  const acceptEncoding = getHeader(headers, "Accept-Encoding") ?? "gzip";
@@ -380,7 +373,7 @@ function getHttpsProxy(url) {
380
373
  }
381
374
  }
382
375
  function waitForHead(socket, timeoutMs, onTimeout) {
383
- return new Promise((resolve, reject) => {
376
+ return new Promise((resolve2, reject) => {
384
377
  let buffer = Buffer2.alloc(0);
385
378
  const timeout = setTimeout(() => {
386
379
  onTimeout();
@@ -400,7 +393,7 @@ function waitForHead(socket, timeoutMs, onTimeout) {
400
393
  return;
401
394
  const head = buffer.subarray(0, marker).toString("latin1");
402
395
  const leftover = buffer.subarray(marker + 4);
403
- cleanup(() => resolve({ head, leftover }));
396
+ cleanup(() => resolve2({ head, leftover }));
404
397
  };
405
398
  socket.on("data", onData);
406
399
  socket.once("error", onError);
@@ -411,7 +404,7 @@ async function connectViaProxy(proxyUrl, targetUrl, timeoutMs, onDebug) {
411
404
  host: proxyUrl.hostname,
412
405
  port: Number(proxyUrl.port || DEFAULT_PROXY_PORT)
413
406
  });
414
- await new Promise((resolve, reject) => {
407
+ await new Promise((resolve2, reject) => {
415
408
  const timeout = setTimeout(() => {
416
409
  onDebug?.(`agy transport proxy connect timeout after ${timeoutMs}ms`);
417
410
  proxySocket.destroy();
@@ -420,7 +413,7 @@ async function connectViaProxy(proxyUrl, targetUrl, timeoutMs, onDebug) {
420
413
  const cleanup = () => clearTimeout(timeout);
421
414
  proxySocket.once("connect", () => {
422
415
  cleanup();
423
- resolve();
416
+ resolve2();
424
417
  });
425
418
  proxySocket.once("error", (error) => {
426
419
  cleanup();
@@ -445,7 +438,7 @@ Host: ${targetHost}:${targetPort}\r
445
438
  if (leftover.length > 0) {
446
439
  proxySocket.unshift(leftover);
447
440
  }
448
- return await new Promise((resolve, reject) => {
441
+ return await new Promise((resolve2, reject) => {
449
442
  const tlsSocket = tls.connect({ socket: proxySocket, servername: targetHost });
450
443
  const timeout = setTimeout(() => {
451
444
  onDebug?.(`agy transport proxy TLS handshake timeout after ${timeoutMs}ms`);
@@ -455,7 +448,7 @@ Host: ${targetHost}:${targetPort}\r
455
448
  const cleanup = () => clearTimeout(timeout);
456
449
  tlsSocket.once("secureConnect", () => {
457
450
  cleanup();
458
- resolve(tlsSocket);
451
+ resolve2(tlsSocket);
459
452
  });
460
453
  tlsSocket.once("error", (error) => {
461
454
  cleanup();
@@ -464,7 +457,7 @@ Host: ${targetHost}:${targetPort}\r
464
457
  });
465
458
  }
466
459
  async function connectDirect(targetUrl, timeoutMs, onDebug) {
467
- return await new Promise((resolve, reject) => {
460
+ return await new Promise((resolve2, reject) => {
468
461
  const socket = tls.connect({
469
462
  host: targetUrl.hostname,
470
463
  port: Number(targetUrl.port || DEFAULT_HTTPS_PORT),
@@ -478,7 +471,7 @@ async function connectDirect(targetUrl, timeoutMs, onDebug) {
478
471
  const cleanup = () => clearTimeout(timeout);
479
472
  socket.once("secureConnect", () => {
480
473
  cleanup();
481
- resolve(socket);
474
+ resolve2(socket);
482
475
  });
483
476
  socket.once("error", (error) => {
484
477
  cleanup();
@@ -492,9 +485,9 @@ async function connectTls(targetUrl, timeoutMs, onDebug) {
492
485
  }
493
486
  function serializeRequest(url, init, body) {
494
487
  const method = init.method ?? "POST";
495
- const path5 = `${url.pathname}${url.search}`;
488
+ const path4 = `${url.pathname}${url.search}`;
496
489
  const headerLines = buildAgyCliHeaderPairs(url.toString(), init).map(([key, value]) => `${key}: ${value}`).join("\r\n");
497
- const head = Buffer2.from(`${method} ${path5} HTTP/1.1\r
490
+ const head = Buffer2.from(`${method} ${path4} HTTP/1.1\r
498
491
  ${headerLines}\r
499
492
  \r
500
493
  `);
@@ -522,6 +515,7 @@ function parseResponseHead(head) {
522
515
  const headers = new Headers();
523
516
  let chunked = false;
524
517
  let gzip = false;
518
+ let contentLength;
525
519
  for (const line of lines) {
526
520
  const index = line.indexOf(":");
527
521
  if (index <= 0)
@@ -538,8 +532,13 @@ function parseResponseHead(head) {
538
532
  gzip = true;
539
533
  continue;
540
534
  }
541
- if (gzip && lowerKey === "content-length") {
542
- continue;
535
+ if (lowerKey === "content-length") {
536
+ const parsed = Number.parseInt(value, 10);
537
+ if (Number.isFinite(parsed) && parsed >= 0) {
538
+ contentLength = parsed;
539
+ }
540
+ if (gzip)
541
+ continue;
543
542
  }
544
543
  headers.append(key, value);
545
544
  }
@@ -548,9 +547,34 @@ function parseResponseHead(head) {
548
547
  statusText: match[2] ?? "",
549
548
  headers,
550
549
  chunked,
551
- gzip
550
+ gzip,
551
+ contentLength
552
552
  };
553
553
  }
554
+ var ContentLengthStream = class extends Transform {
555
+ remaining;
556
+ constructor(contentLength) {
557
+ super();
558
+ this.remaining = contentLength;
559
+ }
560
+ _transform(chunk, _encoding, callback) {
561
+ if (this.remaining <= 0) {
562
+ callback();
563
+ return;
564
+ }
565
+ if (chunk.length <= this.remaining) {
566
+ this.remaining -= chunk.length;
567
+ this.push(chunk);
568
+ } else {
569
+ this.push(chunk.subarray(0, this.remaining));
570
+ this.remaining = 0;
571
+ }
572
+ if (this.remaining <= 0) {
573
+ this.push(null);
574
+ }
575
+ callback();
576
+ }
577
+ };
554
578
  var ChunkedDecodeStream = class extends Transform {
555
579
  buffer = Buffer2.alloc(0);
556
580
  _transform(chunk, _encoding, callback) {
@@ -596,7 +620,7 @@ var ChunkedDecodeStream = class extends Transform {
596
620
  }
597
621
  }
598
622
  };
599
- function buildResponseStream(socket, leftover, head, signal) {
623
+ function buildResponseStream(socket, leftover, head, signal, idleTimeoutMs, onDebug) {
600
624
  const source = new PassThrough();
601
625
  if (leftover.length > 0) {
602
626
  source.write(leftover);
@@ -605,12 +629,36 @@ function buildResponseStream(socket, leftover, head, signal) {
605
629
  let responseBody = source;
606
630
  if (head.chunked) {
607
631
  responseBody = responseBody.pipe(new ChunkedDecodeStream());
632
+ } else if (typeof head.contentLength === "number") {
633
+ responseBody = responseBody.pipe(new ContentLengthStream(head.contentLength));
608
634
  }
609
635
  if (head.gzip) {
610
636
  responseBody = responseBody.pipe(createGunzip());
611
637
  }
638
+ let idleTimer;
639
+ const clearIdle = () => {
640
+ if (idleTimer) {
641
+ clearTimeout(idleTimer);
642
+ idleTimer = void 0;
643
+ }
644
+ };
645
+ const armIdle = () => {
646
+ if (!idleTimeoutMs || idleTimeoutMs <= 0)
647
+ return;
648
+ clearIdle();
649
+ idleTimer = setTimeout(() => {
650
+ onDebug?.(`agy transport idle timeout after ${idleTimeoutMs}ms with no body data`);
651
+ socket.destroy(new Error(`Antigravity response stalled: no data for ${idleTimeoutMs}ms`));
652
+ }, idleTimeoutMs);
653
+ };
654
+ socket.on("data", armIdle);
655
+ armIdle();
612
656
  const abort = () => socket.destroy(new DOMException("The operation was aborted", "AbortError"));
613
- const cleanup = () => signal?.removeEventListener("abort", abort);
657
+ const cleanup = () => {
658
+ clearIdle();
659
+ socket.off("data", armIdle);
660
+ signal?.removeEventListener("abort", abort);
661
+ };
614
662
  if (signal?.aborted) {
615
663
  abort();
616
664
  } else {
@@ -632,11 +680,15 @@ async function fetchWithAgyCliTransport(url, init = {}, options = {}) {
632
680
  if (parsedUrl.protocol !== "https:") {
633
681
  throw new Error(`agy transport only supports https URLs: ${url}`);
634
682
  }
683
+ if (options.signal?.aborted) {
684
+ throw new DOMException("The operation was aborted", "AbortError");
685
+ }
635
686
  const body = bodyToBuffer(init.body);
636
687
  const requestBytes = serializeRequest(parsedUrl, init, body);
637
688
  const timeoutMs = options.timeoutMs ?? DEFAULT_AGY_RESPONSE_HEADER_TIMEOUT_MS;
689
+ const idleTimeoutMs = options.idleTimeoutMs ?? DEFAULT_AGY_IDLE_TIMEOUT_MS;
638
690
  options.onDebug?.(`agy transport connecting to ${parsedUrl.hostname} with header timeout ${timeoutMs}ms`);
639
- const socket = await connectTls(parsedUrl, timeoutMs, options.onDebug);
691
+ const socket = await connectTlsWithAbort(parsedUrl, timeoutMs, options.signal, options.onDebug);
640
692
  const abort = () => {
641
693
  socket.destroy(new DOMException("The operation was aborted", "AbortError"));
642
694
  };
@@ -650,7 +702,7 @@ async function fetchWithAgyCliTransport(url, init = {}, options = {}) {
650
702
  });
651
703
  const parsedHead = parseResponseHead(head);
652
704
  options.onDebug?.(`agy transport response headers received: ${parsedHead.status} ${parsedHead.statusText}`);
653
- const bodyStream = buildResponseStream(socket, leftover, parsedHead, options.signal);
705
+ const bodyStream = buildResponseStream(socket, leftover, parsedHead, options.signal, idleTimeoutMs, options.onDebug);
654
706
  return new Response(bodyStream, {
655
707
  status: parsedHead.status,
656
708
  statusText: parsedHead.statusText,
@@ -663,6 +715,199 @@ async function fetchWithAgyCliTransport(url, init = {}, options = {}) {
663
715
  options.signal?.removeEventListener("abort", abort);
664
716
  }
665
717
  }
718
+ async function connectTlsWithAbort(targetUrl, timeoutMs, signal, onDebug) {
719
+ if (!signal) {
720
+ return connectTls(targetUrl, timeoutMs, onDebug);
721
+ }
722
+ const connectPromise = connectTls(targetUrl, timeoutMs, onDebug);
723
+ let onAbort;
724
+ const abortPromise = new Promise((_, reject) => {
725
+ onAbort = () => reject(new DOMException("The operation was aborted", "AbortError"));
726
+ signal.addEventListener("abort", onAbort, { once: true });
727
+ });
728
+ try {
729
+ return await Promise.race([connectPromise, abortPromise]);
730
+ } catch (error) {
731
+ void connectPromise.then((socket) => socket.destroy()).catch(() => {
732
+ });
733
+ throw error;
734
+ } finally {
735
+ if (onAbort)
736
+ signal.removeEventListener("abort", onAbort);
737
+ }
738
+ }
739
+
740
+ // ../core/dist/agy-request-metadata.js
741
+ import { Buffer as Buffer3 } from "node:buffer";
742
+ import { randomUUID as randomUUID2 } from "node:crypto";
743
+ var FNV1A_64_OFFSET_BASIS = 0xcbf29ce484222325n;
744
+ var FNV1A_64_PRIME = 0x100000001b3n;
745
+ var DEFAULT_SESSION_STATE_TTL_MS = 24 * 60 * 60 * 1e3;
746
+ var DEFAULT_MAX_SESSION_STATES = 256;
747
+ var AGY_REQUEST_FIELD_ORDER = [
748
+ "contents",
749
+ "systemInstruction",
750
+ "tools",
751
+ "toolConfig",
752
+ "labels",
753
+ "generationConfig",
754
+ "sessionId"
755
+ ];
756
+ var AGY_MODEL_ENUM_BY_WIRE_MODEL = {
757
+ "gemini-3.5-flash-extra-low": "MODEL_PLACEHOLDER_M187",
758
+ "gemini-3.5-flash-low": "MODEL_PLACEHOLDER_M20",
759
+ "gemini-3-flash-agent": "MODEL_PLACEHOLDER_M84",
760
+ "gemini-3.6-flash-low": "MODEL_PLACEHOLDER_M266",
761
+ "gemini-3.6-flash-medium": "MODEL_PLACEHOLDER_M265",
762
+ "gemini-3.6-flash-high": "MODEL_PLACEHOLDER_M264",
763
+ "gemini-3.1-pro-low": "MODEL_PLACEHOLDER_M36",
764
+ "gemini-pro-agent": "MODEL_PLACEHOLDER_M16",
765
+ "claude-sonnet-4-6": "MODEL_PLACEHOLDER_M35",
766
+ "claude-opus-4-6-thinking": "MODEL_PLACEHOLDER_M26",
767
+ "gemini-3.1-flash-image": "MODEL_PLACEHOLDER_M21",
768
+ "gpt-oss-120b-medium": "MODEL_OPENAI_GPT_OSS_120B_MEDIUM"
769
+ };
770
+ function fnv1a64Signed(input2) {
771
+ let hash = FNV1A_64_OFFSET_BASIS;
772
+ for (const byte of Buffer3.from(input2, "utf8")) {
773
+ hash ^= BigInt(byte);
774
+ hash = BigInt.asUintN(64, hash * FNV1A_64_PRIME);
775
+ }
776
+ return BigInt.asIntN(64, hash).toString();
777
+ }
778
+ function createAgyRequestSessionContext(workspaceUri, ids = {}) {
779
+ return {
780
+ conversationId: ids.conversationId ?? randomUUID2(),
781
+ trajectoryId: ids.trajectoryId ?? randomUUID2(),
782
+ numericSessionId: fnv1a64Signed(workspaceUri)
783
+ };
784
+ }
785
+ var AgyRequestSessionStore = class {
786
+ entries = /* @__PURE__ */ new Map();
787
+ workspaceUri;
788
+ ttlMs;
789
+ maxEntries;
790
+ now;
791
+ constructor(workspaceUri, options = {}) {
792
+ this.workspaceUri = workspaceUri;
793
+ this.ttlMs = options.ttlMs ?? DEFAULT_SESSION_STATE_TTL_MS;
794
+ this.maxEntries = options.maxEntries ?? DEFAULT_MAX_SESSION_STATES;
795
+ this.now = options.now ?? Date.now;
796
+ }
797
+ getOrCreate(key) {
798
+ const timestamp = this.now();
799
+ this.prune(timestamp, key);
800
+ const existing = this.entries.get(key);
801
+ if (existing) {
802
+ existing.lastAccessedAt = timestamp;
803
+ return existing.context;
804
+ }
805
+ const context = createAgyRequestSessionContext(this.workspaceUri);
806
+ this.entries.set(key, {
807
+ context,
808
+ lastAccessedAt: timestamp,
809
+ lastRequestTimestamp: 0
810
+ });
811
+ return context;
812
+ }
813
+ beginRequest(key) {
814
+ const session = this.getOrCreate(key);
815
+ const stored = this.entries.get(key);
816
+ const timestamp = Math.max(stored.lastAccessedAt, stored.lastRequestTimestamp + 1);
817
+ stored.lastRequestTimestamp = timestamp;
818
+ return { session, timestamp };
819
+ }
820
+ has(key) {
821
+ return this.entries.has(key);
822
+ }
823
+ delete(key) {
824
+ this.entries.delete(key);
825
+ }
826
+ get size() {
827
+ return this.entries.size;
828
+ }
829
+ prune(timestamp, preservedKey) {
830
+ const expiry = timestamp - this.ttlMs;
831
+ for (const [key, value] of this.entries) {
832
+ if (key !== preservedKey && value.lastAccessedAt < expiry) {
833
+ this.entries.delete(key);
834
+ }
835
+ }
836
+ while (this.entries.size >= this.maxEntries && !this.entries.has(preservedKey)) {
837
+ let oldestKey = null;
838
+ let oldestAccess = Number.POSITIVE_INFINITY;
839
+ for (const [key, value] of this.entries) {
840
+ if (key !== preservedKey && value.lastAccessedAt < oldestAccess) {
841
+ oldestKey = key;
842
+ oldestAccess = value.lastAccessedAt;
843
+ }
844
+ }
845
+ if (!oldestKey) {
846
+ break;
847
+ }
848
+ this.entries.delete(oldestKey);
849
+ }
850
+ }
851
+ };
852
+ function getAgyModelEnum(model) {
853
+ return AGY_MODEL_ENUM_BY_WIRE_MODEL[model.toLowerCase()];
854
+ }
855
+ function orderAgyRequestPayloadInPlace(payload) {
856
+ const ordered = {};
857
+ const remaining = new Set(Object.keys(payload));
858
+ for (const key of AGY_REQUEST_FIELD_ORDER) {
859
+ if (key in payload) {
860
+ ordered[key] = payload[key];
861
+ remaining.delete(key);
862
+ }
863
+ }
864
+ for (const key of remaining) {
865
+ ordered[key] = payload[key];
866
+ }
867
+ for (const key of Object.keys(payload)) {
868
+ delete payload[key];
869
+ }
870
+ Object.assign(payload, ordered);
871
+ }
872
+ function countAgyRequestSteps(payload) {
873
+ const contents = payload.contents;
874
+ if (!Array.isArray(contents)) {
875
+ return 1;
876
+ }
877
+ let partCount = 0;
878
+ for (const content of contents) {
879
+ if (!content || typeof content !== "object" || Array.isArray(content)) {
880
+ continue;
881
+ }
882
+ const parts = content.parts;
883
+ if (Array.isArray(parts)) {
884
+ partCount += parts.length;
885
+ }
886
+ }
887
+ return Math.max(1, partCount);
888
+ }
889
+ function buildAgyAgentRequestMetadata(session, payload, model, timestamp = Date.now()) {
890
+ const lastStepIndex = countAgyRequestSteps(payload);
891
+ const isClaude = model.toLowerCase().startsWith("claude-");
892
+ const isNonGemini = isClaude || model.toLowerCase().startsWith("gpt-");
893
+ session.usedClaude = session.usedClaude === true || isClaude;
894
+ session.usedNonGeminiModel = session.usedNonGeminiModel === true || isNonGemini;
895
+ const modelEnum = getAgyModelEnum(model);
896
+ const labels = {
897
+ last_step_index: String(lastStepIndex),
898
+ ...modelEnum ? { model_enum: modelEnum } : {},
899
+ trajectory_id: session.trajectoryId,
900
+ used_claude: session.usedClaude ? "true" : "false",
901
+ used_claude_conservative: session.usedClaude ? "true" : "false",
902
+ used_non_gemini_model: session.usedNonGeminiModel ? "true" : "false"
903
+ };
904
+ return {
905
+ requestId: `agent/${session.conversationId}/${timestamp}/${session.trajectoryId}/${lastStepIndex + 1}`,
906
+ sessionId: session.numericSessionId,
907
+ labels,
908
+ lastStepIndex
909
+ };
910
+ }
666
911
 
667
912
  // ../core/dist/version.js
668
913
  var VERSION_URL = "https://antigravity-auto-updater-974169037036.us-central1.run.app";
@@ -749,8 +994,8 @@ function getDefaultTierId(allowedTiers) {
749
994
  return allowedTiers[0]?.id;
750
995
  }
751
996
  function wait(ms) {
752
- return new Promise(function(resolve) {
753
- setTimeout(resolve, ms);
997
+ return new Promise(function(resolve2) {
998
+ setTimeout(resolve2, ms);
754
999
  });
755
1000
  }
756
1001
  function extractManagedProjectId(payload) {
@@ -954,6 +1199,16 @@ var ALL_MODEL_DEFINITIONS = {
954
1199
  high: { thinkingLevel: "high" }
955
1200
  }
956
1201
  }),
1202
+ "antigravity-gemini-3.6-flash": defineModel("antigravity-gemini-3.6-flash", {
1203
+ name: "Gemini 3.6 Flash (Antigravity)",
1204
+ reasoning: true,
1205
+ limit: { context: 1048576, output: 65536 },
1206
+ modalities: DEFAULT_MODALITIES,
1207
+ variants: {
1208
+ low: { thinkingLevel: "low" },
1209
+ high: { thinkingLevel: "high" }
1210
+ }
1211
+ }),
957
1212
  "antigravity-gemini-3.5-flash": defineModel("antigravity-gemini-3.5-flash", {
958
1213
  name: "Gemini 3.5 Flash (Antigravity)",
959
1214
  reasoning: true,
@@ -993,14 +1248,11 @@ var ALL_MODEL_DEFINITIONS = {
993
1248
  output: ["text", "image"]
994
1249
  }
995
1250
  }),
996
- "antigravity-gpt-oss-120b": defineModel("antigravity-gpt-oss-120b", {
997
- name: "GPT-OSS 120B (Antigravity)",
998
- reasoning: false,
999
- limit: { context: 128e3, output: 16384 },
1000
- modalities: DEFAULT_MODALITIES,
1001
- variants: {
1002
- medium: {}
1003
- }
1251
+ "antigravity-gpt-oss-120b-medium": defineModel("antigravity-gpt-oss-120b-medium", {
1252
+ name: "GPT-OSS 120B Medium (Antigravity)",
1253
+ reasoning: true,
1254
+ limit: { context: 131072, output: 32768 },
1255
+ modalities: DEFAULT_MODALITIES
1004
1256
  }),
1005
1257
  "gemini-2.5-flash": defineModel("gemini-2.5-flash", {
1006
1258
  name: "Gemini 2.5 Flash (Gemini CLI)",
@@ -1066,6 +1318,9 @@ var RESOLVER_ALIASES = {
1066
1318
  "gemini-3.5-flash-low": "gemini-3.5-flash",
1067
1319
  "gemini-3.5-flash-medium": "gemini-3.5-flash",
1068
1320
  "gemini-3.5-flash-high": "gemini-3.5-flash",
1321
+ "gemini-3.6-flash-low": "gemini-3.6-flash",
1322
+ "gemini-3.6-flash-medium": "gemini-3.6-flash",
1323
+ "gemini-3.6-flash-high": "gemini-3.6-flash",
1069
1324
  "gemini-claude-opus-4-6-thinking-low": "claude-opus-4-6-thinking",
1070
1325
  "gemini-claude-opus-4-6-thinking-medium": "claude-opus-4-6-thinking",
1071
1326
  "gemini-claude-opus-4-6-thinking-high": "claude-opus-4-6-thinking",
@@ -1076,7 +1331,8 @@ var RESOLVER_ALIASES = {
1076
1331
  "claude-sonnet-4-6-thinking": "claude-sonnet-4-6",
1077
1332
  "claude-sonnet-4-6-thinking-low": "claude-sonnet-4-6",
1078
1333
  "claude-sonnet-4-6-thinking-medium": "claude-sonnet-4-6",
1079
- "claude-sonnet-4-6-thinking-high": "claude-sonnet-4-6"
1334
+ "claude-sonnet-4-6-thinking-high": "claude-sonnet-4-6",
1335
+ "gpt-oss-120b": "gpt-oss-120b-medium"
1080
1336
  };
1081
1337
  var GEMINI_35_FLASH_ROUTES = {
1082
1338
  antigravity: {
@@ -1089,6 +1345,14 @@ var GEMINI_35_FLASH_ROUTES = {
1089
1345
  },
1090
1346
  geminiCliFallbackModel: "gemini-3-flash-preview"
1091
1347
  };
1348
+ var GEMINI_36_FLASH_ROUTES = {
1349
+ defaultModel: "gemini-3.6-flash-medium",
1350
+ byTier: {
1351
+ low: "gemini-3.6-flash-low",
1352
+ medium: "gemini-3.6-flash-medium",
1353
+ high: "gemini-3.6-flash-high"
1354
+ }
1355
+ };
1092
1356
  var QUOTA_GROUP_BY_MODEL_ID = {
1093
1357
  "claude-opus-4-6-thinking": "claude",
1094
1358
  "claude-opus-4-6": "claude",
@@ -1102,15 +1366,22 @@ var QUOTA_GROUP_BY_MODEL_ID = {
1102
1366
  "gemini-3-flash-agent": "gemini-flash",
1103
1367
  "gemini-3.5-flash-low": "gemini-flash",
1104
1368
  "gemini-3.5-flash-extra-low": "gemini-flash",
1369
+ "gemini-3.6-flash-low": "gemini-flash",
1370
+ "gemini-3.6-flash-medium": "gemini-flash",
1371
+ "gemini-3.6-flash-high": "gemini-flash",
1372
+ "gemini-3.6-flash-tiered": "gemini-flash",
1105
1373
  "gemini-3.1-flash-image": "gemini-flash",
1106
1374
  "gpt-oss-120b": "gpt-oss",
1107
1375
  "gpt-oss-120b-medium": "gpt-oss"
1108
1376
  };
1109
1377
  var ANTIGRAVITY_OPENCODE_MODEL_IDS = [
1378
+ "antigravity-gemini-3.6-flash",
1110
1379
  "antigravity-gemini-3.5-flash",
1111
1380
  "antigravity-gemini-3.1-pro",
1112
1381
  "antigravity-claude-sonnet-4-6-thinking",
1113
- "antigravity-claude-opus-4-6-thinking"
1382
+ "antigravity-claude-opus-4-6-thinking",
1383
+ "antigravity-gemini-3.1-flash-image",
1384
+ "antigravity-gpt-oss-120b-medium"
1114
1385
  ];
1115
1386
  function pickModelDefinitions(ids) {
1116
1387
  return Object.fromEntries(ids.map((id) => [id, ALL_MODEL_DEFINITIONS[id]]));
@@ -1131,6 +1402,12 @@ function getGemini35FlashAntigravityModel(tier) {
1131
1402
  function getGemini35FlashGeminiCliFallbackModel() {
1132
1403
  return GEMINI_35_FLASH_ROUTES.geminiCliFallbackModel;
1133
1404
  }
1405
+ function getGemini36FlashAntigravityModel(tier) {
1406
+ if (!tier) {
1407
+ return GEMINI_36_FLASH_ROUTES.defaultModel;
1408
+ }
1409
+ return GEMINI_36_FLASH_ROUTES.byTier[tier] ?? GEMINI_36_FLASH_ROUTES.defaultModel;
1410
+ }
1134
1411
  function getQuotaGroupForModel(modelId) {
1135
1412
  return QUOTA_GROUP_BY_MODEL_ID[modelId.toLowerCase()];
1136
1413
  }
@@ -1187,7 +1464,7 @@ function isGemini35FlashModel(model) {
1187
1464
  function resolveGemini35FlashAntigravityModel(tier) {
1188
1465
  return getGemini35FlashAntigravityModel(tier);
1189
1466
  }
1190
- function getAgyGemini35FlashThinkingBudget(tier) {
1467
+ function getAgyGeminiFlashThinkingBudget(tier) {
1191
1468
  switch (tier) {
1192
1469
  case "low":
1193
1470
  return 1e3;
@@ -1220,6 +1497,8 @@ function resolveModelWithTier(requestedModel, options = {}) {
1220
1497
  const isGemini3Flash = isGemini3FlashModel(modelWithoutQuota);
1221
1498
  const isGemini31Pro = /^gemini-3\.1-pro/i.test(baseName);
1222
1499
  const isGemini35Flash = /^gemini-3\.5-flash/i.test(baseName);
1500
+ const isGemini36Flash = /^gemini-3\.6-flash/i.test(baseName);
1501
+ const isGptOss120b = /^gpt-oss-120b(?:-medium)?$/i.test(baseName);
1223
1502
  if (isGemini31Pro && quotaPreference === "antigravity") {
1224
1503
  return {
1225
1504
  actualModel: getAgyGemini31ProModel(tier),
@@ -1233,13 +1512,32 @@ function resolveModelWithTier(requestedModel, options = {}) {
1233
1512
  if (isGemini35Flash && quotaPreference === "antigravity") {
1234
1513
  return {
1235
1514
  actualModel: resolveGemini35FlashAntigravityModel(tier ?? "medium"),
1236
- thinkingBudget: getAgyGemini35FlashThinkingBudget(tier),
1515
+ thinkingBudget: getAgyGeminiFlashThinkingBudget(tier),
1516
+ tier: tier ?? "medium",
1517
+ isThinkingModel: true,
1518
+ quotaPreference,
1519
+ explicitQuota
1520
+ };
1521
+ }
1522
+ if (isGemini36Flash && quotaPreference === "antigravity") {
1523
+ return {
1524
+ actualModel: getGemini36FlashAntigravityModel(tier ?? "medium"),
1525
+ thinkingBudget: getAgyGeminiFlashThinkingBudget(tier),
1237
1526
  tier: tier ?? "medium",
1238
1527
  isThinkingModel: true,
1239
1528
  quotaPreference,
1240
1529
  explicitQuota
1241
1530
  };
1242
1531
  }
1532
+ if (isGptOss120b && quotaPreference === "antigravity") {
1533
+ return {
1534
+ actualModel: "gpt-oss-120b-medium",
1535
+ thinkingBudget: 8192,
1536
+ isThinkingModel: true,
1537
+ quotaPreference,
1538
+ explicitQuota
1539
+ };
1540
+ }
1243
1541
  let antigravityModel = modelWithoutQuota;
1244
1542
  if (skipAlias) {
1245
1543
  if ((isGemini3Pro || isGemini3Flash) && !tier && !isImageModel) {
@@ -1436,12 +1734,26 @@ var UNSUPPORTED_SCHEMA_FIELDS = /* @__PURE__ */ new Set([
1436
1734
  "minContains",
1437
1735
  "maxContains"
1438
1736
  ]);
1439
- function toGeminiSchema(schema) {
1737
+ var NUMERIC_SCHEMA_CONSTRAINTS = /* @__PURE__ */ new Set([
1738
+ "minLength",
1739
+ "maxLength",
1740
+ "minItems",
1741
+ "maxItems",
1742
+ "minProperties",
1743
+ "maxProperties",
1744
+ "minimum",
1745
+ "maximum",
1746
+ "exclusiveMinimum",
1747
+ "exclusiveMaximum",
1748
+ "multipleOf"
1749
+ ]);
1750
+ function toGeminiSchema(schema, options = {}) {
1440
1751
  if (!schema || typeof schema !== "object" || Array.isArray(schema)) {
1441
1752
  return schema;
1442
1753
  }
1443
1754
  const inputSchema = schema;
1444
1755
  const result = {};
1756
+ const numericConstraintHints = [];
1445
1757
  const propertyNames = /* @__PURE__ */ new Set();
1446
1758
  if (inputSchema.properties && typeof inputSchema.properties === "object") {
1447
1759
  for (const propName of Object.keys(inputSchema.properties)) {
@@ -1457,15 +1769,19 @@ function toGeminiSchema(schema) {
1457
1769
  } else if (key === "properties" && typeof value === "object" && value !== null) {
1458
1770
  const props = {};
1459
1771
  for (const [propName, propSchema] of Object.entries(value)) {
1460
- props[propName] = toGeminiSchema(propSchema);
1772
+ props[propName] = toGeminiSchema(propSchema, options);
1461
1773
  }
1462
1774
  result[key] = props;
1463
1775
  } else if (key === "items" && typeof value === "object") {
1464
- result[key] = toGeminiSchema(value);
1776
+ result[key] = toGeminiSchema(value, options);
1465
1777
  } else if ((key === "anyOf" || key === "oneOf" || key === "allOf") && Array.isArray(value)) {
1466
- result[key] = value.map((item) => toGeminiSchema(item));
1778
+ result[key] = value.map((item) => toGeminiSchema(item, options));
1467
1779
  } else if (key === "enum" && Array.isArray(value)) {
1468
1780
  result[key] = value;
1781
+ } else if (options.moveNumericConstraintsToDescription && NUMERIC_SCHEMA_CONSTRAINTS.has(key)) {
1782
+ if (typeof value === "string" || typeof value === "number") {
1783
+ numericConstraintHints.push(`${key}: ${value}`);
1784
+ }
1469
1785
  } else if (key === "default" || key === "examples") {
1470
1786
  result[key] = value;
1471
1787
  } else if (key === "required" && Array.isArray(value)) {
@@ -1481,6 +1797,10 @@ function toGeminiSchema(schema) {
1481
1797
  result[key] = value;
1482
1798
  }
1483
1799
  }
1800
+ if (numericConstraintHints.length > 0) {
1801
+ const hint = numericConstraintHints.join(", ");
1802
+ result.description = typeof result.description === "string" && result.description ? `${result.description} (${hint})` : hint;
1803
+ }
1484
1804
  if (result.type === "ARRAY" && !result.items) {
1485
1805
  result.items = { type: "STRING" };
1486
1806
  }
@@ -1518,7 +1838,7 @@ function buildImageGenerationConfig() {
1518
1838
  console.warn(`[gemini] Invalid aspect ratio "${aspectRatio}". Using default "1:1". Valid values: ${VALID_ASPECT_RATIOS.join(", ")}`);
1519
1839
  return { aspectRatio: "1:1" };
1520
1840
  }
1521
- function normalizeGeminiTools(payload) {
1841
+ function normalizeGeminiTools(payload, schemaOptions = {}) {
1522
1842
  let toolDebugMissing = 0;
1523
1843
  const toolDebugSummaries = [];
1524
1844
  if (!Array.isArray(payload.tools)) {
@@ -1529,6 +1849,21 @@ function normalizeGeminiTools(payload) {
1529
1849
  if (t.googleSearch || t.googleSearchRetrieval) {
1530
1850
  return t;
1531
1851
  }
1852
+ if (Array.isArray(t.functionDeclarations)) {
1853
+ return {
1854
+ ...t,
1855
+ functionDeclarations: t.functionDeclarations.map((declaration) => {
1856
+ const normalized = { ...declaration };
1857
+ const rawSchema = normalized.parameters ?? normalized.parametersJsonSchema ?? {
1858
+ type: "OBJECT",
1859
+ properties: {}
1860
+ };
1861
+ normalized.parameters = toGeminiSchema(rawSchema, schemaOptions);
1862
+ delete normalized.parametersJsonSchema;
1863
+ return normalized;
1864
+ })
1865
+ };
1866
+ }
1532
1867
  const newTool = { ...t };
1533
1868
  const schemaCandidates = [
1534
1869
  newTool.function?.input_schema,
@@ -1556,7 +1891,7 @@ function normalizeGeminiTools(payload) {
1556
1891
  schema = placeholderSchema;
1557
1892
  toolDebugMissing += 1;
1558
1893
  } else {
1559
- schema = toGeminiSchema(schema);
1894
+ schema = toGeminiSchema(schema, schemaOptions);
1560
1895
  }
1561
1896
  const nameCandidate = newTool.name || newTool.function?.name || newTool.custom?.name || `tool-${toolIndex}`;
1562
1897
  if (newTool.function && schema) {
@@ -1621,7 +1956,9 @@ function applyGeminiTransforms(payload, options) {
1621
1956
  googleSearch: {}
1622
1957
  });
1623
1958
  }
1624
- const result = normalizeGeminiTools(payload);
1959
+ const result = normalizeGeminiTools(payload, {
1960
+ moveNumericConstraintsToDescription: model.toLowerCase().startsWith("gpt-oss-")
1961
+ });
1625
1962
  const wrapResult = wrapToolsAsFunctionDeclarations(payload);
1626
1963
  return {
1627
1964
  ...result,
@@ -2122,7 +2459,7 @@ async function select(items, options) {
2122
2459
  }
2123
2460
  renderedLines = linesWritten;
2124
2461
  };
2125
- return new Promise((resolve) => {
2462
+ return new Promise((resolve2) => {
2126
2463
  const wasRaw = stdin.isRaw ?? false;
2127
2464
  const cleanup = () => {
2128
2465
  if (isCleanedUp) return;
@@ -2143,11 +2480,11 @@ async function select(items, options) {
2143
2480
  };
2144
2481
  const onSignal = () => {
2145
2482
  cleanup();
2146
- resolve(null);
2483
+ resolve2(null);
2147
2484
  };
2148
2485
  const finishWithValue = (value) => {
2149
2486
  cleanup();
2150
- resolve(value);
2487
+ resolve2(value);
2151
2488
  };
2152
2489
  const findNextSelectable = (from, direction) => {
2153
2490
  if (items.length === 0) return from;
@@ -2193,7 +2530,7 @@ async function select(items, options) {
2193
2530
  stdin.setRawMode(true);
2194
2531
  } catch {
2195
2532
  cleanup();
2196
- resolve(null);
2533
+ resolve2(null);
2197
2534
  return;
2198
2535
  }
2199
2536
  stdin.resume();
@@ -2320,7 +2657,7 @@ function classifyOverallQuotaHealth(cachedQuota) {
2320
2657
  if (!cachedQuota) {
2321
2658
  return { health: "unknown" };
2322
2659
  }
2323
- const QUOTA_KEYS2 = ["claude", "gemini-pro", "gemini-flash"];
2660
+ const QUOTA_KEYS2 = ["claude", "gemini-pro", "gemini-flash", "gpt-oss"];
2324
2661
  let groupsWithData = 0;
2325
2662
  let exhaustedCount = 0;
2326
2663
  let maxResetMs;
@@ -2354,7 +2691,8 @@ function formatCachedQuotaWithStatus(cachedQuota) {
2354
2691
  const entries = [
2355
2692
  { key: "claude", label: "Claude" },
2356
2693
  { key: "gemini-pro", label: "Gemini Pro" },
2357
- { key: "gemini-flash", label: "Gemini Flash" }
2694
+ { key: "gemini-flash", label: "Gemini Flash" },
2695
+ { key: "gpt-oss", label: "GPT-OSS" }
2358
2696
  ].flatMap(({ key, label }) => {
2359
2697
  const value = cachedQuota[key]?.remainingFraction;
2360
2698
  if (typeof value !== "number" || !Number.isFinite(value)) {
@@ -2414,6 +2752,8 @@ function getStatusBadge(status, account) {
2414
2752
  return ` ${ANSI.red}[expired]${ANSI.reset}`;
2415
2753
  case "verification-required":
2416
2754
  return ` ${ANSI.red}[needs verification]${ANSI.reset}`;
2755
+ case "ineligible":
2756
+ return ` ${ANSI.red}[ineligible]${ANSI.reset}`;
2417
2757
  default:
2418
2758
  return "";
2419
2759
  }
@@ -2445,7 +2785,8 @@ function getHealthLabel(acc) {
2445
2785
  var QUOTA_KEYS = [
2446
2786
  { key: "claude", label: "Claude" },
2447
2787
  { key: "gemini-pro", label: "Gemini Pro" },
2448
- { key: "gemini-flash", label: "Gemini Flash" }
2788
+ { key: "gemini-flash", label: "Gemini Flash" },
2789
+ { key: "gpt-oss", label: "GPT-OSS" }
2449
2790
  ];
2450
2791
  function parseResetTimeToMs2(resetTime) {
2451
2792
  if (!resetTime) return null;
@@ -3083,11 +3424,13 @@ async function getLockFunction() {
3083
3424
  if (_cachedLock) return _cachedLock;
3084
3425
  const mod = await import("proper-lockfile");
3085
3426
  const fn = (typeof mod.lock === "function" ? mod.lock : void 0) || (mod.default && typeof mod.default.lock === "function" ? mod.default.lock : void 0);
3086
- _cachedLock = fn || (async () => async () => {
3087
- });
3427
+ if (typeof fn !== "function") {
3428
+ throw new Error("proper-lockfile did not expose a lock() function; cannot acquire account file lock");
3429
+ }
3430
+ _cachedLock = fn;
3088
3431
  return _cachedLock;
3089
3432
  }
3090
- var lockfile = { lock: (path5, options) => getLockFunction().then((fn) => fn(path5, options)) };
3433
+ var lockfile = { lock: (path4, options) => getLockFunction().then((fn) => fn(path4, options)) };
3091
3434
  var log3 = createLogger2("storage");
3092
3435
  var GITIGNORE_ENTRIES = [
3093
3436
  ".gitignore",
@@ -3255,29 +3598,29 @@ var LOCK_OPTIONS = {
3255
3598
  factor: 2
3256
3599
  }
3257
3600
  };
3258
- async function ensureSecurePermissions(path5) {
3601
+ async function ensureSecurePermissions(path4) {
3259
3602
  try {
3260
- await fs.chmod(path5, 384);
3603
+ await fs.chmod(path4, 384);
3261
3604
  } catch {
3262
3605
  }
3263
3606
  }
3264
- async function ensureFileExists(path5) {
3607
+ async function ensureFileExists(path4) {
3265
3608
  try {
3266
- await fs.access(path5);
3609
+ await fs.access(path4);
3267
3610
  } catch {
3268
- await fs.mkdir(dirname2(path5), { recursive: true });
3611
+ await fs.mkdir(dirname2(path4), { recursive: true });
3269
3612
  await fs.writeFile(
3270
- path5,
3613
+ path4,
3271
3614
  JSON.stringify({ version: 4, accounts: [], activeIndex: 0 }, null, 2),
3272
3615
  { encoding: "utf-8", mode: 384 }
3273
3616
  );
3274
3617
  }
3275
3618
  }
3276
- async function withFileLock(path5, fn) {
3277
- await ensureFileExists(path5);
3619
+ async function withFileLock(path4, fn) {
3620
+ await ensureFileExists(path4);
3278
3621
  let release = null;
3279
3622
  try {
3280
- release = await lockfile.lock(path5, LOCK_OPTIONS);
3623
+ release = await lockfile.lock(path4, LOCK_OPTIONS);
3281
3624
  return await fn();
3282
3625
  } finally {
3283
3626
  if (release) {
@@ -3300,7 +3643,8 @@ function mergeAccountStorage(existing, incoming) {
3300
3643
  if (acc.refreshToken) {
3301
3644
  const existingAcc = accountMap.get(acc.refreshToken);
3302
3645
  if (existingAcc) {
3303
- accountMap.set(acc.refreshToken, {
3646
+ const eligibilitySource = (acc.eligibilityStateUpdatedAt ?? 0) >= (existingAcc.eligibilityStateUpdatedAt ?? 0) ? acc : existingAcc;
3647
+ const merged = {
3304
3648
  ...existingAcc,
3305
3649
  ...acc,
3306
3650
  // Preserve manually configured projectId/managedProjectId if not in incoming
@@ -3310,8 +3654,16 @@ function mergeAccountStorage(existing, incoming) {
3310
3654
  ...existingAcc.rateLimitResetTimes,
3311
3655
  ...acc.rateLimitResetTimes
3312
3656
  },
3313
- lastUsed: Math.max(existingAcc.lastUsed || 0, acc.lastUsed || 0)
3314
- });
3657
+ lastUsed: Math.max(existingAcc.lastUsed || 0, acc.lastUsed || 0),
3658
+ accountIneligible: eligibilitySource.accountIneligible,
3659
+ accountIneligibleAt: eligibilitySource.accountIneligibleAt,
3660
+ accountIneligibleReason: eligibilitySource.accountIneligibleReason,
3661
+ eligibilityStateUpdatedAt: eligibilitySource.eligibilityStateUpdatedAt
3662
+ };
3663
+ if (merged.accountIneligible) {
3664
+ merged.enabled = false;
3665
+ }
3666
+ accountMap.set(acc.refreshToken, merged);
3315
3667
  } else {
3316
3668
  accountMap.set(acc.refreshToken, acc);
3317
3669
  }
@@ -3429,9 +3781,9 @@ function migrateV3ToV4(v3) {
3429
3781
  }
3430
3782
  async function loadAccounts() {
3431
3783
  try {
3432
- const path5 = getStoragePath();
3433
- await ensureSecurePermissions(path5);
3434
- const content = await fs.readFile(path5, "utf-8");
3784
+ const path4 = getStoragePath();
3785
+ await ensureSecurePermissions(path4);
3786
+ const content = await fs.readFile(path4, "utf-8");
3435
3787
  const data = JSON.parse(content);
3436
3788
  if (!Array.isArray(data.accounts)) {
3437
3789
  log3.warn("Invalid storage format, ignoring");
@@ -3545,32 +3897,32 @@ async function atomicWriteFile(targetPath, content) {
3545
3897
  }
3546
3898
  }
3547
3899
  async function saveAccounts(storage) {
3548
- const path5 = getStoragePath();
3549
- const configDir = dirname2(path5);
3900
+ const path4 = getStoragePath();
3901
+ const configDir = dirname2(path4);
3550
3902
  await fs.mkdir(configDir, { recursive: true });
3551
3903
  await ensureGitignore(configDir);
3552
- await withFileLock(path5, async () => {
3904
+ await withFileLock(path4, async () => {
3553
3905
  const existing = await loadAccountsUnsafe();
3554
3906
  const merged = existing ? mergeAccountStorage(existing, storage) : storage;
3555
3907
  const content = JSON.stringify(merged, null, 2);
3556
- await atomicWriteFile(path5, content);
3908
+ await atomicWriteFile(path4, content);
3557
3909
  });
3558
3910
  }
3559
3911
  async function saveAccountsReplace(storage) {
3560
- const path5 = getStoragePath();
3561
- const configDir = dirname2(path5);
3912
+ const path4 = getStoragePath();
3913
+ const configDir = dirname2(path4);
3562
3914
  await fs.mkdir(configDir, { recursive: true });
3563
3915
  await ensureGitignore(configDir);
3564
- await withFileLock(path5, async () => {
3916
+ await withFileLock(path4, async () => {
3565
3917
  const content = JSON.stringify(storage, null, 2);
3566
- await atomicWriteFile(path5, content);
3918
+ await atomicWriteFile(path4, content);
3567
3919
  });
3568
3920
  }
3569
3921
  async function loadAccountsUnsafe() {
3570
3922
  try {
3571
- const path5 = getStoragePath();
3572
- await ensureSecurePermissions(path5);
3573
- const content = await fs.readFile(path5, "utf-8");
3923
+ const path4 = getStoragePath();
3924
+ await ensureSecurePermissions(path4);
3925
+ const content = await fs.readFile(path4, "utf-8");
3574
3926
  const parsed = JSON.parse(content);
3575
3927
  if (parsed.version === 1) {
3576
3928
  return migrateV3ToV4(migrateV2ToV3(migrateV1ToV2(parsed)));
@@ -3598,9 +3950,11 @@ async function loadAccountsUnsafe() {
3598
3950
  }
3599
3951
  }
3600
3952
  async function clearAccounts() {
3953
+ const path4 = getStoragePath();
3601
3954
  try {
3602
- const path5 = getStoragePath();
3603
- await fs.unlink(path5);
3955
+ await withFileLock(path4, async () => {
3956
+ await fs.unlink(path4);
3957
+ });
3604
3958
  } catch (error) {
3605
3959
  const code = error.code;
3606
3960
  if (code !== "ENOENT") {
@@ -3625,7 +3979,7 @@ function getConfigDir2() {
3625
3979
  function getLogsDir(customLogDir) {
3626
3980
  const logsDir = customLogDir || join3(getConfigDir2(), "antigravity-logs");
3627
3981
  try {
3628
- mkdirSync3(logsDir, { recursive: true });
3982
+ mkdirSync3(logsDir, { recursive: true, mode: 448 });
3629
3983
  } catch {
3630
3984
  }
3631
3985
  return logsDir;
@@ -3661,7 +4015,7 @@ function createLogWriter(filePath) {
3661
4015
  };
3662
4016
  }
3663
4017
  try {
3664
- const stream = createWriteStream(filePath, { flags: "a" });
4018
+ const stream = createWriteStream(filePath, { flags: "a", mode: 384 });
3665
4019
  stream.on("error", () => {
3666
4020
  });
3667
4021
  return (line) => {
@@ -4844,17 +5198,17 @@ function getUserConfigPath() {
4844
5198
  function getProjectConfigPath(directory) {
4845
5199
  return join5(directory, ".opencode", "antigravity.json");
4846
5200
  }
4847
- function loadConfigFile(path5) {
5201
+ function loadConfigFile(path4) {
4848
5202
  try {
4849
- if (!existsSync4(path5)) {
5203
+ if (!existsSync4(path4)) {
4850
5204
  return null;
4851
5205
  }
4852
- const content = readFileSync4(path5, "utf-8");
5206
+ const content = readFileSync4(path4, "utf-8");
4853
5207
  const rawConfig = JSON.parse(content);
4854
5208
  const result = AntigravityConfigSchema.partial().safeParse(rawConfig);
4855
5209
  if (!result.success) {
4856
5210
  log4.warn("Config validation error", {
4857
- path: path5,
5211
+ path: path4,
4858
5212
  issues: result.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join(", ")
4859
5213
  });
4860
5214
  return null;
@@ -4862,9 +5216,9 @@ function loadConfigFile(path5) {
4862
5216
  return result.data;
4863
5217
  } catch (error) {
4864
5218
  if (error instanceof SyntaxError) {
4865
- log4.warn("Invalid JSON in config file", { path: path5, error: error.message });
5219
+ log4.warn("Invalid JSON in config file", { path: path4, error: error.message });
4866
5220
  } else {
4867
- log4.warn("Failed to load config file", { path: path5, error: String(error) });
5221
+ log4.warn("Failed to load config file", { path: path4, error: String(error) });
4868
5222
  }
4869
5223
  return null;
4870
5224
  }
@@ -4903,50 +5257,54 @@ function getKeepThinking() {
4903
5257
  }
4904
5258
 
4905
5259
  // src/plugin/image-saver.ts
4906
- import * as fs2 from "fs";
4907
- import * as path from "path";
4908
- import * as os from "os";
4909
- function getImageOutputDir() {
4910
- const homeDir = os.homedir();
4911
- const outputDir = path.join(homeDir, ".opencode", "generated-images");
4912
- if (!fs2.existsSync(outputDir)) {
4913
- fs2.mkdirSync(outputDir, { recursive: true });
5260
+ import {
5261
+ chmodSync,
5262
+ existsSync as existsSync5,
5263
+ mkdirSync as mkdirSync5,
5264
+ writeFileSync as writeFileSync4
5265
+ } from "node:fs";
5266
+ import { homedir as homedir6 } from "node:os";
5267
+ import { join as join6 } from "node:path";
5268
+ function getImageOutputDir(outputDir) {
5269
+ const resolved = outputDir ?? join6(homedir6(), ".opencode", "generated-images");
5270
+ if (!existsSync5(resolved)) {
5271
+ mkdirSync5(resolved, { recursive: true, mode: 448 });
4914
5272
  }
4915
- return outputDir;
5273
+ chmodSync(resolved, 448);
5274
+ return resolved;
4916
5275
  }
4917
5276
  function generateImageFilename(mimeType) {
4918
5277
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
4919
5278
  const random = Math.random().toString(36).substring(2, 8);
4920
- let ext = "png";
5279
+ let extension = "png";
4921
5280
  if (mimeType.includes("jpeg") || mimeType.includes("jpg")) {
4922
- ext = "jpg";
5281
+ extension = "jpg";
4923
5282
  } else if (mimeType.includes("gif")) {
4924
- ext = "gif";
5283
+ extension = "gif";
4925
5284
  } else if (mimeType.includes("webp")) {
4926
- ext = "webp";
5285
+ extension = "webp";
4927
5286
  }
4928
- return `image-${timestamp}-${random}.${ext}`;
5287
+ return `image-${timestamp}-${random}.${extension}`;
4929
5288
  }
4930
- function saveImageToDisk(base64Data, mimeType) {
5289
+ function saveImageToDisk(base64Data, mimeType, outputDir) {
4931
5290
  try {
4932
- const outputDir = getImageOutputDir();
4933
- const filename = generateImageFilename(mimeType);
4934
- const filePath = path.join(outputDir, filename);
4935
- const buffer = Buffer.from(base64Data, "base64");
4936
- fs2.writeFileSync(filePath, buffer);
5291
+ const resolvedOutputDir = getImageOutputDir(outputDir);
5292
+ const filePath = join6(resolvedOutputDir, generateImageFilename(mimeType));
5293
+ writeFileSync4(filePath, Buffer.from(base64Data, "base64"), { mode: 384 });
5294
+ chmodSync(filePath, 384);
4937
5295
  return filePath;
4938
5296
  } catch (error) {
4939
5297
  console.error("[image-saver] Failed to save image:", error);
4940
5298
  return "";
4941
5299
  }
4942
5300
  }
4943
- function processImageData(inlineData) {
5301
+ function processImageData(inlineData, outputDir) {
4944
5302
  const mimeType = inlineData.mimeType || "image/png";
4945
5303
  const data = inlineData.data;
4946
5304
  if (!data) {
4947
5305
  return null;
4948
5306
  }
4949
- const filePath = saveImageToDisk(data, mimeType);
5307
+ const filePath = saveImageToDisk(data, mimeType, outputDir);
4950
5308
  if (filePath) {
4951
5309
  return `![Generated Image](${filePath})
4952
5310
 
@@ -6995,14 +7353,14 @@ function applyToolPairingFixes(payload, isClaude) {
6995
7353
  }
6996
7354
  return { contentsFixed, messagesFixed };
6997
7355
  }
6998
- function createSyntheticErrorResponse(errorMessage, _requestedModel = "unknown") {
6999
- const outputTokens = Math.max(1, Math.ceil(errorMessage.length / 4));
7356
+ function createSyntheticTextResponse(text, extraHeaders = {}) {
7357
+ const outputTokens = Math.max(1, Math.ceil(text.length / 4));
7000
7358
  const event = {
7001
7359
  candidates: [
7002
7360
  {
7003
7361
  content: {
7004
7362
  role: "model",
7005
- parts: [{ text: errorMessage }]
7363
+ parts: [{ text }]
7006
7364
  },
7007
7365
  finishReason: "STOP"
7008
7366
  }
@@ -7022,10 +7380,15 @@ function createSyntheticErrorResponse(errorMessage, _requestedModel = "unknown")
7022
7380
  "Cache-Control": "no-cache",
7023
7381
  "Connection": "keep-alive",
7024
7382
  "X-Antigravity-Synthetic": "true",
7025
- "X-Antigravity-Error-Type": "synthetic_error"
7383
+ ...extraHeaders
7026
7384
  }
7027
7385
  });
7028
7386
  }
7387
+ function createSyntheticErrorResponse(errorMessage, _requestedModel = "unknown") {
7388
+ return createSyntheticTextResponse(errorMessage, {
7389
+ "X-Antigravity-Error-Type": "synthetic_error"
7390
+ });
7391
+ }
7029
7392
 
7030
7393
  // src/plugin/thinking-recovery.ts
7031
7394
  function isThinkingPart2(part) {
@@ -7644,6 +8007,8 @@ var DUMP_USAGE_TITLE = "## Gemini Dump Usage";
7644
8007
  var DUMP_USAGE = "Usage: `/gemini-dump`, `/gemini-dump on`, or `/gemini-dump off`.";
7645
8008
  var DUMP_DIR_ENV = "OPENCODE_ANTIGRAVITY_GEMINI_DUMP_DIR";
7646
8009
  var DEFAULT_DUMP_DIR = join9(tmpdir2(), "opencode-antigravity-gemini-dumps");
8010
+ var DUMP_DIR_MODE = 448;
8011
+ var DUMP_FILE_MODE = 384;
7647
8012
  var dumpEnabled = process.env.OPENCODE_ANTIGRAVITY_GEMINI_DUMP === "1";
7648
8013
  var nextDumpId = 0;
7649
8014
  function setGeminiDumpEnabled(enabled) {
@@ -7762,9 +8127,9 @@ function bodyStructureSummary(bodyText) {
7762
8127
  bodyBytes: bodyText.length
7763
8128
  };
7764
8129
  }
7765
- function writeJson(path5, value) {
7766
- writeFileSync6(path5, `${JSON.stringify(value, null, 2)}
7767
- `, "utf8");
8130
+ function writeJson(path4, value) {
8131
+ writeFileSync6(path4, `${JSON.stringify(value, null, 2)}
8132
+ `, { encoding: "utf8", mode: DUMP_FILE_MODE });
7768
8133
  }
7769
8134
  function updateMetadata(context, patch) {
7770
8135
  context.metadata = {
@@ -7781,7 +8146,7 @@ function dumpGeminiRequest(input2) {
7781
8146
  const id = `${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")}-${String(nextDumpId).padStart(5, "0")}-${input2.streaming ? "stream" : "json"}`;
7782
8147
  const dumpDir = getGeminiDumpDirectory();
7783
8148
  const prefix = join9(dumpDir, id);
7784
- mkdirSync7(dumpDir, { recursive: true });
8149
+ mkdirSync7(dumpDir, { recursive: true, mode: DUMP_DIR_MODE });
7785
8150
  const context = {
7786
8151
  id,
7787
8152
  files: {
@@ -7809,8 +8174,8 @@ function dumpGeminiRequest(input2) {
7809
8174
  }
7810
8175
  }
7811
8176
  };
7812
- writeFileSync6(context.files.request, input2.body, "utf8");
7813
- writeFileSync6(context.files.response, "", "utf8");
8177
+ writeFileSync6(context.files.request, input2.body, { encoding: "utf8", mode: DUMP_FILE_MODE });
8178
+ writeFileSync6(context.files.response, "", { encoding: "utf8", mode: DUMP_FILE_MODE });
7814
8179
  writeJson(context.files.metadata, context.metadata);
7815
8180
  return context;
7816
8181
  }
@@ -7843,26 +8208,69 @@ function createGeminiDumpResponseTransform(context) {
7843
8208
  // src/plugin/request.ts
7844
8209
  var log6 = createLogger2("request");
7845
8210
  var PLUGIN_SESSION_ID = `-${crypto2.randomUUID()}`;
8211
+ var DEFAULT_AGY_REQUEST_SESSION = createAgyRequestSessionContext("");
7846
8212
  var sessionDisplayedThinkingHashes = /* @__PURE__ */ new Set();
7847
8213
  var MIN_SIGNATURE_LENGTH = 50;
8214
+ var AGY_CLAUDE_THINKING_BUDGET = 1024;
7848
8215
  var ANTIGRAVITY_ENVELOPE_FIELD_ORDER = [
7849
8216
  "project",
7850
8217
  "requestId",
7851
8218
  "request",
7852
8219
  "model",
7853
8220
  "userAgent",
7854
- "requestType",
7855
- "enabledCreditTypes"
8221
+ "requestType"
7856
8222
  ];
7857
- function buildAntigravityRequestId(type = "agent") {
7858
- if (type === "checkpoint") {
7859
- return `checkpoint/${crypto2.randomUUID()}`;
8223
+ var OPENCODE_TITLE_PROMPT_PREFIX = "Generate a title for this conversation:";
8224
+ function getOpenCodeTitleSourceText(payload) {
8225
+ if (!Array.isArray(payload.contents)) {
8226
+ return void 0;
8227
+ }
8228
+ const texts = payload.contents.flatMap((content) => {
8229
+ if (!content || typeof content !== "object") {
8230
+ return [];
8231
+ }
8232
+ const parts = content.parts;
8233
+ if (!Array.isArray(parts)) {
8234
+ return [];
8235
+ }
8236
+ return parts.flatMap(
8237
+ (part) => !!part && typeof part === "object" && typeof part.text === "string" ? [part.text] : []
8238
+ );
8239
+ });
8240
+ const promptIndex = texts.findIndex((text) => text.startsWith(OPENCODE_TITLE_PROMPT_PREFIX));
8241
+ return promptIndex >= 0 ? texts.slice(promptIndex + 1).find((text) => text.trim().length > 0) : void 0;
8242
+ }
8243
+ function isOpenCodeTitleGenerationRequest(payload) {
8244
+ return getOpenCodeTitleSourceText(payload) !== void 0;
8245
+ }
8246
+ function formatLocalImageTitle(sourceText) {
8247
+ const normalized = sourceText.trim().replace(/^(["“])(.*)(["”])$/s, "$2").replace(/\s+/g, " ");
8248
+ const characters = Array.from(normalized);
8249
+ if (characters.length <= 50) {
8250
+ return normalized || "Image generation";
8251
+ }
8252
+ return `${characters.slice(0, 47).join("").trimEnd()}...`;
8253
+ }
8254
+ function getImageModelLocalTitle(input2, init) {
8255
+ const url = fetchInputToUrl(input2);
8256
+ if (!/\/models\/[^/:]*(?:image|imagen)[^/:]*:streamGenerateContent/i.test(url)) {
8257
+ return void 0;
8258
+ }
8259
+ const body = init?.body;
8260
+ const bodyText = typeof body === "string" ? body : body instanceof Uint8Array ? new TextDecoder().decode(body) : "";
8261
+ if (!bodyText) {
8262
+ return void 0;
8263
+ }
8264
+ try {
8265
+ const sourceText = getOpenCodeTitleSourceText(JSON.parse(bodyText));
8266
+ return sourceText === void 0 ? void 0 : formatLocalImageTitle(sourceText);
8267
+ } catch {
8268
+ return void 0;
7860
8269
  }
7861
- return `agent/${crypto2.randomUUID()}/${Date.now()}/${crypto2.randomUUID()}/2`;
7862
8270
  }
7863
8271
  function getAgyMaxOutputTokens(model) {
7864
8272
  const lower = model.toLowerCase();
7865
- if (lower === "gemini-3.5-flash-low" || lower === "gemini-3.5-flash-extra-low" || lower === "gemini-3-flash-agent") {
8273
+ if (lower === "gemini-3.5-flash-low" || lower === "gemini-3.5-flash-extra-low" || lower === "gemini-3-flash-agent" || lower === "gemini-3.6-flash-low" || lower === "gemini-3.6-flash-medium" || lower === "gemini-3.6-flash-high") {
7866
8274
  return 65536;
7867
8275
  }
7868
8276
  if (lower === "gemini-3.1-pro-low" || lower === "gemini-pro-agent") {
@@ -7871,6 +8279,9 @@ function getAgyMaxOutputTokens(model) {
7871
8279
  if (lower === "claude-sonnet-4-6" || lower === "claude-opus-4-6-thinking") {
7872
8280
  return 64e3;
7873
8281
  }
8282
+ if (lower === "gpt-oss-120b-medium") {
8283
+ return 32768;
8284
+ }
7874
8285
  return void 0;
7875
8286
  }
7876
8287
  function applyAgyGenerationDefaults(model, generationConfig, headerStyle) {
@@ -7907,7 +8318,7 @@ var JSON_SCHEMA_TYPES = /* @__PURE__ */ new Set(["boolean", "string", "number",
7907
8318
  function thinkingSafeReplacer(key, value) {
7908
8319
  if (key === "thinking" && typeof value === "object" && value !== null) {
7909
8320
  const rec = value;
7910
- if (typeof rec.type === "string" && JSON_SCHEMA_TYPES.has(rec.type)) {
8321
+ if (typeof rec.type === "string" && JSON_SCHEMA_TYPES.has(rec.type.toLowerCase())) {
7911
8322
  return value;
7912
8323
  }
7913
8324
  return "";
@@ -8110,16 +8521,79 @@ function stripInjectedDebugFromRequestPayload(payload) {
8110
8521
  if (Array.isArray(message.content)) {
8111
8522
  return { ...message, content: stripInjectedDebugFromParts(message.content) };
8112
8523
  }
8113
- return message;
8114
- });
8524
+ return message;
8525
+ });
8526
+ }
8527
+ }
8528
+ function isValidRequestPart(part) {
8529
+ if (!part || typeof part !== "object") {
8530
+ return false;
8531
+ }
8532
+ const record = part;
8533
+ return Object.prototype.hasOwnProperty.call(record, "text") || Object.prototype.hasOwnProperty.call(record, "functionCall") || Object.prototype.hasOwnProperty.call(record, "functionResponse") || Object.prototype.hasOwnProperty.call(record, "inlineData") || Object.prototype.hasOwnProperty.call(record, "fileData") || Object.prototype.hasOwnProperty.call(record, "executableCode") || Object.prototype.hasOwnProperty.call(record, "codeExecutionResult") || Object.prototype.hasOwnProperty.call(record, "thought");
8534
+ }
8535
+ function stripCacheControlFromParts(parts) {
8536
+ if (!Array.isArray(parts)) {
8537
+ return;
8538
+ }
8539
+ for (const part of parts) {
8540
+ if (!part || typeof part !== "object" || Array.isArray(part)) {
8541
+ continue;
8542
+ }
8543
+ const record = part;
8544
+ delete record.cache_control;
8545
+ delete record.cacheControl;
8546
+ }
8547
+ }
8548
+ function stripUnsupportedAntigravityFields(payload) {
8549
+ delete payload.providerOptions;
8550
+ delete payload.cached_content;
8551
+ delete payload.cachedContent;
8552
+ delete payload.cache_control;
8553
+ delete payload.cacheControl;
8554
+ const extraBody = payload.extra_body;
8555
+ if (extraBody && typeof extraBody === "object" && !Array.isArray(extraBody)) {
8556
+ const extraBodyRecord = extraBody;
8557
+ delete extraBodyRecord.cached_content;
8558
+ delete extraBodyRecord.cachedContent;
8559
+ delete extraBodyRecord.cache_control;
8560
+ delete extraBodyRecord.cacheControl;
8561
+ if (Object.keys(extraBodyRecord).length === 0) {
8562
+ delete payload.extra_body;
8563
+ }
8564
+ }
8565
+ const stripContentParts = (items) => {
8566
+ if (!Array.isArray(items)) {
8567
+ return;
8568
+ }
8569
+ for (const item of items) {
8570
+ if (!item || typeof item !== "object" || Array.isArray(item)) {
8571
+ continue;
8572
+ }
8573
+ const record = item;
8574
+ stripCacheControlFromParts(record.parts);
8575
+ stripCacheControlFromParts(record.content);
8576
+ }
8577
+ };
8578
+ stripContentParts(payload.contents);
8579
+ stripContentParts(payload.messages);
8580
+ for (const instructionKey of ["systemInstruction", "system_instruction"]) {
8581
+ const instruction = payload[instructionKey];
8582
+ if (instruction && typeof instruction === "object" && !Array.isArray(instruction)) {
8583
+ stripCacheControlFromParts(instruction.parts);
8584
+ }
8115
8585
  }
8116
8586
  }
8117
- function isValidRequestPart(part) {
8118
- if (!part || typeof part !== "object") {
8119
- return false;
8587
+ function configureAntigravityToolCalling(payload) {
8588
+ if (!Array.isArray(payload.tools) || payload.tools.length === 0) {
8589
+ delete payload.toolConfig;
8590
+ return;
8120
8591
  }
8121
- const record = part;
8122
- return Object.prototype.hasOwnProperty.call(record, "text") || Object.prototype.hasOwnProperty.call(record, "functionCall") || Object.prototype.hasOwnProperty.call(record, "functionResponse") || Object.prototype.hasOwnProperty.call(record, "inlineData") || Object.prototype.hasOwnProperty.call(record, "fileData") || Object.prototype.hasOwnProperty.call(record, "executableCode") || Object.prototype.hasOwnProperty.call(record, "codeExecutionResult") || Object.prototype.hasOwnProperty.call(record, "thought");
8592
+ const toolConfig = payload.toolConfig && typeof payload.toolConfig === "object" && !Array.isArray(payload.toolConfig) ? payload.toolConfig : {};
8593
+ const functionCallingConfig = toolConfig.functionCallingConfig && typeof toolConfig.functionCallingConfig === "object" && !Array.isArray(toolConfig.functionCallingConfig) ? toolConfig.functionCallingConfig : {};
8594
+ functionCallingConfig.mode = "VALIDATED";
8595
+ toolConfig.functionCallingConfig = functionCallingConfig;
8596
+ payload.toolConfig = toolConfig;
8123
8597
  }
8124
8598
  function sanitizeRequestPayloadForAntigravity(payload) {
8125
8599
  const anyPayload = payload;
@@ -8421,17 +8895,15 @@ var _lastCacheStats = null;
8421
8895
  function getLastCacheStats() {
8422
8896
  return _lastCacheStats;
8423
8897
  }
8424
- function generateSyntheticProjectId() {
8425
- const adjectives = ["useful", "bright", "swift", "calm", "bold"];
8426
- const nouns = ["fuze", "wave", "spark", "flow", "core"];
8427
- const adj = adjectives[Math.floor(Math.random() * adjectives.length)];
8428
- const noun = nouns[Math.floor(Math.random() * nouns.length)];
8429
- const randomPart = crypto2.randomUUID().slice(0, 5).toLowerCase();
8430
- return `${adj}-${noun}-${randomPart}`;
8431
- }
8432
8898
  var STREAM_ACTION = "streamGenerateContent";
8899
+ function fetchInputToUrl(input2) {
8900
+ if (typeof input2 === "string") return input2;
8901
+ if (input2 instanceof URL) return input2.href;
8902
+ const url = input2.url;
8903
+ return typeof url === "string" ? url : String(input2);
8904
+ }
8433
8905
  function isGenerativeLanguageRequest(input2) {
8434
- return typeof input2 === "string" && input2.includes("generativelanguage.googleapis.com");
8906
+ return fetchInputToUrl(input2).includes("generativelanguage.googleapis.com");
8435
8907
  }
8436
8908
  function prepareAntigravityRequest(input2, init, accessToken, projectId, endpointOverride, headerStyle = "antigravity", forceThinkingRecovery = false, options) {
8437
8909
  const baseInit = { ...init };
@@ -8455,8 +8927,11 @@ function prepareAntigravityRequest(input2, init, accessToken, projectId, endpoin
8455
8927
  headers.delete("x-api-key");
8456
8928
  headers.delete("x-goog-api-key");
8457
8929
  headers.delete("x-session-affinity");
8930
+ headers.delete("x-session-id");
8931
+ headers.delete("x-parent-session-id");
8458
8932
  headers.delete("x-goog-user-project");
8459
- const match = input2.match(/\/models\/([^:]+):(\w+)/);
8933
+ const urlString = fetchInputToUrl(input2);
8934
+ const match = urlString.match(/\/models\/([^:]+):(\w+)/);
8460
8935
  if (!match) {
8461
8936
  return {
8462
8937
  request: input2,
@@ -8495,9 +8970,6 @@ function prepareAntigravityRequest(input2, init, accessToken, projectId, endpoin
8495
8970
  model: effectiveModel
8496
8971
  };
8497
8972
  if (headerStyle === "antigravity") {
8498
- if (typeof wrappedBody.requestId !== "string" || !wrappedBody.requestId) {
8499
- wrappedBody.requestId = buildAntigravityRequestId("agent");
8500
- }
8501
8973
  if (typeof wrappedBody.userAgent !== "string" || !wrappedBody.userAgent) {
8502
8974
  wrappedBody.userAgent = "antigravity";
8503
8975
  }
@@ -8521,7 +8993,6 @@ function prepareAntigravityRequest(input2, init, accessToken, projectId, endpoin
8521
8993
  sessionId = signatureSessionKey;
8522
8994
  }
8523
8995
  for (const req of requestObjects) {
8524
- req.sessionId = signatureSessionKey;
8525
8996
  stripInjectedDebugFromRequestPayload(req);
8526
8997
  if (isClaude) {
8527
8998
  sanitizeCrossModelPayloadInPlace(req, { targetModel: effectiveModel });
@@ -8534,6 +9005,11 @@ function prepareAntigravityRequest(input2, init, accessToken, projectId, endpoin
8534
9005
  }
8535
9006
  applyToolPairingFixes(req, true);
8536
9007
  }
9008
+ if (headerStyle === "antigravity") {
9009
+ sanitizeRequestPayloadForAntigravity(req);
9010
+ stripUnsupportedAntigravityFields(req);
9011
+ configureAntigravityToolCalling(req);
9012
+ }
8537
9013
  }
8538
9014
  if (isClaude) {
8539
9015
  for (const req of requestObjects) {
@@ -8563,9 +9039,31 @@ function prepareAntigravityRequest(input2, init, accessToken, projectId, endpoin
8563
9039
  const hasCachedThinking = defaultSignatureStore.has(signatureSessionKey);
8564
9040
  needsSignedThinkingWarmup = hasToolUse && !hasSignedThinking && !hasCachedThinking;
8565
9041
  }
9042
+ const wireRequest = requestObjects.at(-1);
9043
+ if (wireRequest) {
9044
+ if (headerStyle === "antigravity") {
9045
+ const metadata = buildAgyAgentRequestMetadata(
9046
+ options?.agySession ?? DEFAULT_AGY_REQUEST_SESSION,
9047
+ wireRequest,
9048
+ effectiveModel,
9049
+ options?.agyRequestTimestamp
9050
+ );
9051
+ wrappedBody.requestId = metadata.requestId;
9052
+ wireRequest.sessionId = metadata.sessionId;
9053
+ wireRequest.labels = metadata.labels;
9054
+ orderAgyRequestPayloadInPlace(wireRequest);
9055
+ } else {
9056
+ wireRequest.sessionId = signatureSessionKey;
9057
+ }
9058
+ }
8566
9059
  body = safeStringify(headerStyle === "antigravity" ? orderAntigravityEnvelope(wrappedBody) : wrappedBody);
8567
9060
  } else {
8568
9061
  const requestPayload = { ...parsedBody };
9062
+ if (headerStyle === "antigravity" && isImageGenerationModel(effectiveModel) && isOpenCodeTitleGenerationRequest(requestPayload)) {
9063
+ effectiveModel = "gemini-3.5-flash-low";
9064
+ tierThinkingBudget = 4e3;
9065
+ tierThinkingLevel = void 0;
9066
+ }
8569
9067
  const rawGenerationConfig = requestPayload.generationConfig;
8570
9068
  const extraBody = requestPayload.extra_body;
8571
9069
  const variantConfig = extractVariantThinkingConfig(
@@ -8574,6 +9072,7 @@ function prepareAntigravityRequest(input2, init, accessToken, projectId, endpoin
8574
9072
  );
8575
9073
  const isGemini3 = effectiveModel.toLowerCase().includes("gemini-3");
8576
9074
  log6.debug(`[ThinkingResolution] rawModel=${rawModel} resolvedModel=${effectiveModel} resolvedTier=${tierThinkingLevel ?? "none"} variantLevel=${variantConfig?.thinkingLevel ?? "none"} variantBudget=${variantConfig?.thinkingBudget ?? "none"} providerOptions.google=${JSON.stringify(requestPayload.providerOptions?.google ?? null)} generationConfig.thinkingConfig=${JSON.stringify(rawGenerationConfig?.thinkingConfig ?? null)}`);
9075
+ delete requestPayload.providerOptions;
8577
9076
  if (variantConfig?.thinkingLevel && isGemini3) {
8578
9077
  const variantModelBase = rawModel.replace(/-preview-customtools$/i, "").replace(/-preview$/i, "").replace(/-(minimal|low|medium|high)$/i, "");
8579
9078
  const variantResolved = resolveModelForHeaderStyle(
@@ -8593,20 +9092,6 @@ function prepareAntigravityRequest(input2, init, accessToken, projectId, endpoin
8593
9092
  tierThinkingLevel = void 0;
8594
9093
  }
8595
9094
  }
8596
- if (isClaude) {
8597
- if (!requestPayload.toolConfig) {
8598
- requestPayload.toolConfig = {};
8599
- }
8600
- if (typeof requestPayload.toolConfig === "object" && requestPayload.toolConfig !== null) {
8601
- const toolConfig = requestPayload.toolConfig;
8602
- if (!toolConfig.functionCallingConfig) {
8603
- toolConfig.functionCallingConfig = {};
8604
- }
8605
- if (typeof toolConfig.functionCallingConfig === "object" && toolConfig.functionCallingConfig !== null) {
8606
- toolConfig.functionCallingConfig.mode = "VALIDATED";
8607
- }
8608
- }
8609
- }
8610
9095
  const isImageModel = isImageGenerationModel(effectiveModel);
8611
9096
  const userThinkingConfig = isImageModel ? void 0 : extractThinkingConfig(requestPayload, rawGenerationConfig, extraBody);
8612
9097
  const hasAssistantHistory = Array.isArray(requestPayload.contents) && requestPayload.contents.some((c) => c?.role === "model" || c?.role === "assistant");
@@ -8703,17 +9188,19 @@ function prepareAntigravityRequest(input2, init, accessToken, projectId, endpoin
8703
9188
  requestPayload.systemInstruction = requestPayload.system_instruction;
8704
9189
  delete requestPayload.system_instruction;
8705
9190
  }
8706
- const cachedContentFromExtra = typeof requestPayload.extra_body === "object" && requestPayload.extra_body ? requestPayload.extra_body.cached_content ?? requestPayload.extra_body.cachedContent : void 0;
8707
- const cachedContent = requestPayload.cached_content ?? requestPayload.cachedContent ?? cachedContentFromExtra;
8708
- if (cachedContent) {
8709
- requestPayload.cachedContent = cachedContent;
8710
- }
8711
- delete requestPayload.cached_content;
8712
- if (requestPayload.extra_body && typeof requestPayload.extra_body === "object") {
8713
- delete requestPayload.extra_body.cached_content;
8714
- delete requestPayload.extra_body.cachedContent;
8715
- if (Object.keys(requestPayload.extra_body).length === 0) {
8716
- delete requestPayload.extra_body;
9191
+ if (headerStyle !== "antigravity") {
9192
+ const cachedContentFromExtra = typeof requestPayload.extra_body === "object" && requestPayload.extra_body ? requestPayload.extra_body.cached_content ?? requestPayload.extra_body.cachedContent : void 0;
9193
+ const cachedContent = requestPayload.cached_content ?? requestPayload.cachedContent ?? cachedContentFromExtra;
9194
+ if (cachedContent) {
9195
+ requestPayload.cachedContent = cachedContent;
9196
+ }
9197
+ delete requestPayload.cached_content;
9198
+ if (requestPayload.extra_body && typeof requestPayload.extra_body === "object") {
9199
+ delete requestPayload.extra_body.cached_content;
9200
+ delete requestPayload.extra_body.cachedContent;
9201
+ if (Object.keys(requestPayload.extra_body).length === 0) {
9202
+ delete requestPayload.extra_body;
9203
+ }
8717
9204
  }
8718
9205
  }
8719
9206
  const hasTools = Array.isArray(requestPayload.tools) && requestPayload.tools.length > 0;
@@ -8909,11 +9396,27 @@ function prepareAntigravityRequest(input2, init, accessToken, projectId, endpoin
8909
9396
  }
8910
9397
  stripInjectedDebugFromRequestPayload(requestPayload);
8911
9398
  sanitizeRequestPayloadForAntigravity(requestPayload);
8912
- const effectiveProjectId = projectId?.trim() || (headerStyle === "antigravity" ? generateSyntheticProjectId() : "");
9399
+ if (headerStyle === "antigravity") {
9400
+ stripUnsupportedAntigravityFields(requestPayload);
9401
+ configureAntigravityToolCalling(requestPayload);
9402
+ }
9403
+ const effectiveProjectId = projectId?.trim() || (headerStyle === "antigravity" ? ANTIGRAVITY_DEFAULT_PROJECT_ID : "");
8913
9404
  resolvedProjectId = effectiveProjectId;
9405
+ sessionId = signatureSessionKey;
9406
+ const agyMetadata = headerStyle === "antigravity" ? buildAgyAgentRequestMetadata(
9407
+ options?.agySession ?? DEFAULT_AGY_REQUEST_SESSION,
9408
+ requestPayload,
9409
+ effectiveModel,
9410
+ options?.agyRequestTimestamp
9411
+ ) : null;
9412
+ requestPayload.sessionId = agyMetadata?.sessionId ?? signatureSessionKey;
9413
+ if (agyMetadata) {
9414
+ requestPayload.labels = agyMetadata.labels;
9415
+ orderAgyRequestPayloadInPlace(requestPayload);
9416
+ }
8914
9417
  const wrappedBody = headerStyle === "antigravity" ? {
8915
9418
  project: effectiveProjectId,
8916
- requestId: buildAntigravityRequestId("agent"),
9419
+ requestId: agyMetadata.requestId,
8917
9420
  request: requestPayload,
8918
9421
  model: effectiveModel,
8919
9422
  userAgent: "antigravity",
@@ -8923,10 +9426,6 @@ function prepareAntigravityRequest(input2, init, accessToken, projectId, endpoin
8923
9426
  model: effectiveModel,
8924
9427
  request: requestPayload
8925
9428
  };
8926
- if (wrappedBody.request && typeof wrappedBody.request === "object") {
8927
- sessionId = signatureSessionKey;
8928
- wrappedBody.request.sessionId = signatureSessionKey;
8929
- }
8930
9429
  body = safeStringify(headerStyle === "antigravity" ? orderAntigravityEnvelope(wrappedBody) : wrappedBody);
8931
9430
  }
8932
9431
  } catch {
@@ -8988,17 +9487,20 @@ function buildThinkingWarmupBody(bodyText, isClaudeThinking) {
8988
9487
  return null;
8989
9488
  }
8990
9489
  const warmupPrompt = "Warmup request for thinking signature.";
9490
+ const wireModel = typeof parsed.model === "string" ? parsed.model : "claude-sonnet-4-6";
9491
+ const requestObjects = [];
8991
9492
  const updateRequest = (req) => {
8992
9493
  req.contents = [{ role: "user", parts: [{ text: warmupPrompt }] }];
8993
9494
  delete req.tools;
8994
9495
  delete req.toolConfig;
8995
9496
  const generationConfig = req.generationConfig ?? {};
8996
9497
  generationConfig.thinkingConfig = {
8997
- include_thoughts: true,
8998
- thinking_budget: DEFAULT_THINKING_BUDGET
9498
+ includeThoughts: true,
9499
+ thinkingBudget: AGY_CLAUDE_THINKING_BUDGET
8999
9500
  };
9000
- generationConfig.maxOutputTokens = computeClaudeMaxOutputTokens(DEFAULT_THINKING_BUDGET);
9501
+ generationConfig.maxOutputTokens = getAgyMaxOutputTokens(wireModel) ?? computeClaudeMaxOutputTokens(AGY_CLAUDE_THINKING_BUDGET);
9001
9502
  req.generationConfig = generationConfig;
9503
+ requestObjects.push(req);
9002
9504
  };
9003
9505
  if (parsed.request && typeof parsed.request === "object") {
9004
9506
  updateRequest(parsed.request);
@@ -9009,6 +9511,20 @@ function buildThinkingWarmupBody(bodyText, isClaudeThinking) {
9009
9511
  } else {
9010
9512
  updateRequest(parsed);
9011
9513
  }
9514
+ const wireRequest = requestObjects.at(-1);
9515
+ if (wireRequest) {
9516
+ const numericSessionId = typeof wireRequest.sessionId === "string" ? wireRequest.sessionId : DEFAULT_AGY_REQUEST_SESSION.numericSessionId;
9517
+ const warmupSession = {
9518
+ conversationId: crypto2.randomUUID(),
9519
+ trajectoryId: crypto2.randomUUID(),
9520
+ numericSessionId
9521
+ };
9522
+ const metadata = buildAgyAgentRequestMetadata(warmupSession, wireRequest, wireModel);
9523
+ parsed.requestId = metadata.requestId;
9524
+ wireRequest.sessionId = metadata.sessionId;
9525
+ wireRequest.labels = metadata.labels;
9526
+ orderAgyRequestPayloadInPlace(wireRequest);
9527
+ }
9012
9528
  return safeStringify(parsed);
9013
9529
  }
9014
9530
  async function transformAntigravityResponse(response, streaming, debugContext, requestedModel, projectId, endpoint, effectiveModel, sessionId, toolDebugMissing, toolDebugSummary, toolDebugPayload, debugLines, dumpContext) {
@@ -9381,12 +9897,12 @@ async function startOAuthListener({ timeoutMs = 5 * 60 * 1e3 } = {}) {
9381
9897
  let resolveCallback;
9382
9898
  let rejectCallback;
9383
9899
  let timeoutHandle;
9384
- const callbackPromise = new Promise((resolve, reject) => {
9900
+ const callbackPromise = new Promise((resolve2, reject) => {
9385
9901
  resolveCallback = (url) => {
9386
9902
  if (settled) return;
9387
9903
  settled = true;
9388
9904
  if (timeoutHandle) clearTimeout(timeoutHandle);
9389
- resolve(url);
9905
+ resolve2(url);
9390
9906
  };
9391
9907
  rejectCallback = (error) => {
9392
9908
  if (settled) return;
@@ -9544,7 +10060,7 @@ async function startOAuthListener({ timeoutMs = 5 * 60 * 1e3 } = {}) {
9544
10060
  });
9545
10061
  });
9546
10062
  const bindAddress = getBindAddress();
9547
- await new Promise((resolve, reject) => {
10063
+ await new Promise((resolve2, reject) => {
9548
10064
  const handleError = (error) => {
9549
10065
  server.off("error", handleError);
9550
10066
  if (error.code === "EADDRINUSE") {
@@ -9558,7 +10074,7 @@ async function startOAuthListener({ timeoutMs = 5 * 60 * 1e3 } = {}) {
9558
10074
  server.once("error", handleError);
9559
10075
  server.listen(port, bindAddress, () => {
9560
10076
  server.off("error", handleError);
9561
- resolve();
10077
+ resolve2();
9562
10078
  });
9563
10079
  });
9564
10080
  server.on("error", (error) => {
@@ -9566,7 +10082,7 @@ async function startOAuthListener({ timeoutMs = 5 * 60 * 1e3 } = {}) {
9566
10082
  });
9567
10083
  return {
9568
10084
  waitForCallback: () => callbackPromise,
9569
- close: () => new Promise((resolve, reject) => {
10085
+ close: () => new Promise((resolve2, reject) => {
9570
10086
  server.close((error) => {
9571
10087
  if (error && error.code !== "ERR_SERVER_NOT_RUNNING") {
9572
10088
  reject(error);
@@ -9575,7 +10091,7 @@ async function startOAuthListener({ timeoutMs = 5 * 60 * 1e3 } = {}) {
9575
10091
  if (!settled) {
9576
10092
  rejectCallback(new Error("OAuth listener closed before callback"));
9577
10093
  }
9578
- resolve();
10094
+ resolve2();
9579
10095
  });
9580
10096
  })
9581
10097
  };
@@ -9969,6 +10485,8 @@ function computeSoftQuotaCacheTtlMs(ttlConfig, refreshIntervalMinutes) {
9969
10485
  }
9970
10486
  return ttlConfig * 60 * 1e3;
9971
10487
  }
10488
+ var ACCOUNT_SESSION_STATE_TTL_MS = 24 * 60 * 60 * 1e3;
10489
+ var MAX_ACCOUNT_SESSION_STATES = 256;
9972
10490
  var AccountManager = class _AccountManager {
9973
10491
  accounts = [];
9974
10492
  cursorByFamily = { claude: 0, gemini: 0 };
@@ -9988,6 +10506,7 @@ var AccountManager = class _AccountManager {
9988
10506
  sessionStartTime = Date.now();
9989
10507
  sessionRequestCounts = /* @__PURE__ */ new Map();
9990
10508
  sessionUsedAccounts = /* @__PURE__ */ new Set();
10509
+ requestSessionStates = /* @__PURE__ */ new Map();
9991
10510
  static async loadFromDisk(authFallback) {
9992
10511
  const stored = await loadAccounts();
9993
10512
  return new _AccountManager(authFallback, stored);
@@ -10032,7 +10551,11 @@ var AccountManager = class _AccountManager {
10032
10551
  verificationRequired: acc.verificationRequired,
10033
10552
  verificationRequiredAt: acc.verificationRequiredAt,
10034
10553
  verificationRequiredReason: acc.verificationRequiredReason,
10035
- verificationUrl: acc.verificationUrl
10554
+ verificationUrl: acc.verificationUrl,
10555
+ accountIneligible: acc.accountIneligible,
10556
+ accountIneligibleAt: acc.accountIneligibleAt,
10557
+ accountIneligibleReason: acc.accountIneligibleReason,
10558
+ eligibilityStateUpdatedAt: acc.eligibilityStateUpdatedAt
10036
10559
  };
10037
10560
  }).filter((a) => a !== null);
10038
10561
  let fingerprintVersionChanged = false;
@@ -10114,11 +10637,104 @@ var AccountManager = class _AccountManager {
10114
10637
  getEnabledAccounts() {
10115
10638
  return this.accounts.filter((account) => account.enabled !== false);
10116
10639
  }
10640
+ getEffectiveSoftQuotaThreshold(thresholdPercent) {
10641
+ return this.getEnabledAccounts().length > 1 ? thresholdPercent : 100;
10642
+ }
10117
10643
  getAccountsSnapshot() {
10118
10644
  return this.accounts.map((a) => ({ ...a, parts: { ...a.parts }, rateLimitResetTimes: { ...a.rateLimitResetTimes } }));
10119
10645
  }
10120
- getCurrentAccountForFamily(family) {
10121
- const currentIndex = this.currentAccountIndexByFamily[family];
10646
+ getRequestSessionState(identity) {
10647
+ const now = nowMs();
10648
+ this.pruneRequestSessionStates(now, identity.id);
10649
+ const existing = this.requestSessionStates.get(identity.id);
10650
+ if (existing) {
10651
+ existing.lastAccessedAt = now;
10652
+ if (identity.parentId) {
10653
+ existing.parentId = identity.parentId;
10654
+ }
10655
+ return existing;
10656
+ }
10657
+ const state = {
10658
+ parentId: identity.parentId ?? null,
10659
+ currentAccountIndexByFamily: { claude: -1, gemini: -1 },
10660
+ cursorByFamily: { ...this.cursorByFamily },
10661
+ offsetAppliedByFamily: { claude: false, gemini: false },
10662
+ usedAccounts: /* @__PURE__ */ new Set(),
10663
+ lastAccessedAt: now
10664
+ };
10665
+ this.requestSessionStates.set(identity.id, state);
10666
+ return state;
10667
+ }
10668
+ pruneRequestSessionStates(now, preservedId) {
10669
+ const expiry = now - ACCOUNT_SESSION_STATE_TTL_MS;
10670
+ for (const [id, state] of this.requestSessionStates) {
10671
+ if (id !== preservedId && state.lastAccessedAt < expiry) {
10672
+ this.requestSessionStates.delete(id);
10673
+ }
10674
+ }
10675
+ if (this.requestSessionStates.size < MAX_ACCOUNT_SESSION_STATES || this.requestSessionStates.has(preservedId)) {
10676
+ return;
10677
+ }
10678
+ let oldestId = null;
10679
+ let oldestAccess = Number.POSITIVE_INFINITY;
10680
+ for (const [id, state] of this.requestSessionStates) {
10681
+ if (id !== preservedId && state.lastAccessedAt < oldestAccess) {
10682
+ oldestId = id;
10683
+ oldestAccess = state.lastAccessedAt;
10684
+ }
10685
+ }
10686
+ if (oldestId) {
10687
+ this.requestSessionStates.delete(oldestId);
10688
+ }
10689
+ }
10690
+ getActiveIndex(family, identity) {
10691
+ return identity ? this.getRequestSessionState(identity).currentAccountIndexByFamily[family] : this.currentAccountIndexByFamily[family];
10692
+ }
10693
+ setActiveIndex(family, index, identity) {
10694
+ if (!identity) {
10695
+ this.currentAccountIndexByFamily[family] = index;
10696
+ return;
10697
+ }
10698
+ const state = this.getRequestSessionState(identity);
10699
+ state.currentAccountIndexByFamily[family] = index;
10700
+ if (!state.parentId) {
10701
+ this.currentAccountIndexByFamily[family] = index;
10702
+ }
10703
+ }
10704
+ getCursor(family, identity) {
10705
+ return identity ? this.getRequestSessionState(identity).cursorByFamily[family] : this.cursorByFamily[family];
10706
+ }
10707
+ advanceCursor(family, identity) {
10708
+ const nextGlobalCursor = this.cursorByFamily[family] + 1;
10709
+ this.cursorByFamily[family] = nextGlobalCursor;
10710
+ if (identity) {
10711
+ this.getRequestSessionState(identity).cursorByFamily[family] += 1;
10712
+ }
10713
+ }
10714
+ getUsedAccounts(identity) {
10715
+ return identity ? this.getRequestSessionState(identity).usedAccounts : this.sessionUsedAccounts;
10716
+ }
10717
+ preferAccountOutsideParent(accounts, family, identity) {
10718
+ if (!identity) {
10719
+ return accounts;
10720
+ }
10721
+ const parentId = this.getRequestSessionState(identity).parentId;
10722
+ if (!parentId) {
10723
+ return accounts;
10724
+ }
10725
+ const parentState = this.requestSessionStates.get(parentId);
10726
+ const parentIndex = parentState?.currentAccountIndexByFamily[family] ?? -1;
10727
+ if (parentIndex < 0) {
10728
+ return accounts;
10729
+ }
10730
+ const isolated = accounts.filter((account) => account.index !== parentIndex);
10731
+ return isolated.length > 0 ? isolated : accounts;
10732
+ }
10733
+ deleteSessionState(sessionId) {
10734
+ this.requestSessionStates.delete(sessionId);
10735
+ }
10736
+ getCurrentAccountForFamily(family, identity) {
10737
+ const currentIndex = this.getActiveIndex(family, identity);
10122
10738
  if (currentIndex >= 0 && currentIndex < this.accounts.length) {
10123
10739
  const account = this.accounts[currentIndex] ?? null;
10124
10740
  if (account && account.enabled !== false) {
@@ -10127,9 +10743,9 @@ var AccountManager = class _AccountManager {
10127
10743
  }
10128
10744
  return null;
10129
10745
  }
10130
- markSwitched(account, reason, family) {
10746
+ markSwitched(account, reason, family, identity) {
10131
10747
  account.lastSwitchReason = reason;
10132
- this.currentAccountIndexByFamily[family] = account.index;
10748
+ this.setActiveIndex(family, account.index, identity);
10133
10749
  }
10134
10750
  /**
10135
10751
  * Check if we should show an account switch toast.
@@ -10146,82 +10762,111 @@ var AccountManager = class _AccountManager {
10146
10762
  this.lastToastAccountIndex = accountIndex;
10147
10763
  this.lastToastTime = nowMs();
10148
10764
  }
10149
- getCurrentOrNextForFamily(family, model, strategy = "sticky", headerStyle = "antigravity", pidOffsetEnabled = false, softQuotaThresholdPercent = 100, softQuotaCacheTtlMs = 10 * 60 * 1e3) {
10765
+ getCurrentOrNextForFamily(family, model, strategy = "sticky", headerStyle = "antigravity", pidOffsetEnabled = false, softQuotaThresholdPercent = 100, softQuotaCacheTtlMs = 10 * 60 * 1e3, identity) {
10150
10766
  const quotaKey = getQuotaKey(family, headerStyle, model);
10767
+ const effectiveSoftQuotaThreshold = this.getEffectiveSoftQuotaThreshold(softQuotaThresholdPercent);
10768
+ if (identity) {
10769
+ const pinned = this.getCurrentAccountForFamily(family, identity);
10770
+ if (pinned) {
10771
+ clearExpiredRateLimits(pinned);
10772
+ const unavailable = isRateLimitedForHeaderStyle(pinned, family, headerStyle, model) || isOverSoftQuotaThreshold(pinned, family, effectiveSoftQuotaThreshold, softQuotaCacheTtlMs, model) || this.isAccountCoolingDown(pinned);
10773
+ if (!unavailable) {
10774
+ this.markTouchedForQuota(pinned, quotaKey);
10775
+ return pinned;
10776
+ }
10777
+ }
10778
+ }
10151
10779
  if (strategy === "round-robin") {
10152
- const next2 = this.getNextForFamily(family, model, headerStyle, softQuotaThresholdPercent, softQuotaCacheTtlMs);
10780
+ const next2 = this.getNextForFamily(family, model, headerStyle, effectiveSoftQuotaThreshold, softQuotaCacheTtlMs, identity);
10153
10781
  if (next2) {
10154
10782
  this.markTouchedForQuota(next2, quotaKey);
10155
- this.currentAccountIndexByFamily[family] = next2.index;
10783
+ this.setActiveIndex(family, next2.index, identity);
10156
10784
  }
10157
10785
  return next2;
10158
10786
  }
10159
10787
  if (strategy === "hybrid") {
10160
10788
  const healthTracker = getHealthTracker();
10161
10789
  const tokenTracker = getTokenTracker();
10162
- const accountsWithMetrics = this.accounts.filter((acc) => acc.enabled !== false).map((acc) => {
10790
+ const eligibleAccounts = this.preferAccountOutsideParent(
10791
+ this.accounts.filter((acc) => acc.enabled !== false),
10792
+ family,
10793
+ identity
10794
+ );
10795
+ const accountsWithMetrics = eligibleAccounts.map((acc) => {
10163
10796
  clearExpiredRateLimits(acc);
10164
10797
  return {
10165
10798
  index: acc.index,
10166
10799
  lastUsed: acc.lastUsed,
10167
10800
  healthScore: healthTracker.getScore(acc.index),
10168
- isRateLimited: isRateLimitedForFamily(acc, family, model) || isOverSoftQuotaThreshold(acc, family, softQuotaThresholdPercent, softQuotaCacheTtlMs, model),
10801
+ isRateLimited: isRateLimitedForFamily(acc, family, model) || isOverSoftQuotaThreshold(acc, family, effectiveSoftQuotaThreshold, softQuotaCacheTtlMs, model),
10169
10802
  isCoolingDown: this.isAccountCoolingDown(acc)
10170
10803
  };
10171
10804
  });
10172
- const currentIndex = this.currentAccountIndexByFamily[family] ?? null;
10805
+ const currentIndex = this.getActiveIndex(family, identity);
10173
10806
  const selectedIndex = selectHybridAccount(accountsWithMetrics, tokenTracker, currentIndex);
10174
10807
  if (selectedIndex !== null) {
10175
10808
  const selected = this.accounts[selectedIndex];
10176
10809
  if (selected) {
10177
10810
  selected.lastUsed = nowMs();
10178
10811
  this.markTouchedForQuota(selected, quotaKey);
10179
- this.currentAccountIndexByFamily[family] = selected.index;
10812
+ this.setActiveIndex(family, selected.index, identity);
10180
10813
  return selected;
10181
10814
  }
10182
10815
  }
10183
10816
  }
10184
- if (pidOffsetEnabled && !this.sessionOffsetApplied[family] && this.accounts.length > 1) {
10817
+ const offsetApplied = identity ? this.getRequestSessionState(identity).offsetAppliedByFamily : this.sessionOffsetApplied;
10818
+ if (pidOffsetEnabled && !offsetApplied[family] && this.accounts.length > 1) {
10185
10819
  const pidOffset = process.pid % this.accounts.length;
10186
- const baseIndex = this.currentAccountIndexByFamily[family] ?? 0;
10820
+ const activeIndex = this.getActiveIndex(family, identity);
10821
+ const baseIndex = activeIndex >= 0 ? activeIndex : this.getCursor(family, identity);
10187
10822
  const newIndex = (baseIndex + pidOffset) % this.accounts.length;
10188
10823
  debugLogToFile(`[Account] Applying PID offset: pid=${process.pid} offset=${pidOffset} family=${family} index=${baseIndex}->${newIndex}`);
10189
- this.currentAccountIndexByFamily[family] = newIndex;
10190
- this.sessionOffsetApplied[family] = true;
10824
+ this.setActiveIndex(family, newIndex, identity);
10825
+ offsetApplied[family] = true;
10191
10826
  }
10192
- const current = this.getCurrentAccountForFamily(family);
10827
+ const current = this.getCurrentAccountForFamily(family, identity);
10193
10828
  if (current) {
10194
10829
  clearExpiredRateLimits(current);
10195
10830
  const isLimitedForRequestedStyle = isRateLimitedForHeaderStyle(current, family, headerStyle, model);
10196
- const isOverThreshold = isOverSoftQuotaThreshold(current, family, softQuotaThresholdPercent, softQuotaCacheTtlMs, model);
10831
+ const isOverThreshold = isOverSoftQuotaThreshold(current, family, effectiveSoftQuotaThreshold, softQuotaCacheTtlMs, model);
10197
10832
  if (!isLimitedForRequestedStyle && !isOverThreshold && !this.isAccountCoolingDown(current)) {
10198
10833
  this.markTouchedForQuota(current, quotaKey);
10199
10834
  return current;
10200
10835
  }
10201
10836
  }
10202
- const next = this.getNextForFamily(family, model, headerStyle, softQuotaThresholdPercent, softQuotaCacheTtlMs);
10837
+ const next = this.getNextForFamily(
10838
+ family,
10839
+ model,
10840
+ headerStyle,
10841
+ effectiveSoftQuotaThreshold,
10842
+ softQuotaCacheTtlMs,
10843
+ identity
10844
+ );
10203
10845
  if (next) {
10204
10846
  this.markTouchedForQuota(next, quotaKey);
10205
- this.currentAccountIndexByFamily[family] = next.index;
10847
+ this.setActiveIndex(family, next.index, identity);
10206
10848
  }
10207
10849
  return next;
10208
10850
  }
10209
- getNextForFamily(family, model, headerStyle = "antigravity", softQuotaThresholdPercent = 100, softQuotaCacheTtlMs = 10 * 60 * 1e3) {
10210
- const available = this.accounts.filter((a) => {
10211
- clearExpiredRateLimits(a);
10212
- return a.enabled !== false && !isRateLimitedForHeaderStyle(a, family, headerStyle, model) && !isOverSoftQuotaThreshold(a, family, softQuotaThresholdPercent, softQuotaCacheTtlMs, model) && !this.isAccountCoolingDown(a);
10851
+ getNextForFamily(family, model, headerStyle = "antigravity", softQuotaThresholdPercent = 100, softQuotaCacheTtlMs = 10 * 60 * 1e3, identity) {
10852
+ const effectiveSoftQuotaThreshold = this.getEffectiveSoftQuotaThreshold(softQuotaThresholdPercent);
10853
+ const allAvailable = this.accounts.filter((account2) => {
10854
+ clearExpiredRateLimits(account2);
10855
+ return account2.enabled !== false && !isRateLimitedForHeaderStyle(account2, family, headerStyle, model) && !isOverSoftQuotaThreshold(account2, family, effectiveSoftQuotaThreshold, softQuotaCacheTtlMs, model) && !this.isAccountCoolingDown(account2);
10213
10856
  });
10857
+ const available = this.preferAccountOutsideParent(allAvailable, family, identity);
10214
10858
  if (available.length === 0) {
10215
10859
  return null;
10216
10860
  }
10217
- const sessionUsed = available.filter((a) => this.sessionUsedAccounts.has(a.index));
10861
+ const usedAccounts = this.getUsedAccounts(identity);
10862
+ const sessionUsed = available.filter((account2) => usedAccounts.has(account2.index));
10218
10863
  const candidates = sessionUsed.length > 0 ? sessionUsed : available;
10219
- const cursor = this.cursorByFamily[family];
10864
+ const cursor = this.getCursor(family, identity);
10220
10865
  const account = candidates[cursor % candidates.length];
10221
10866
  if (!account) {
10222
10867
  return null;
10223
10868
  }
10224
- this.cursorByFamily[family] = cursor + 1;
10869
+ this.advanceCursor(family, identity);
10225
10870
  return account;
10226
10871
  }
10227
10872
  markRateLimited(account, retryAfterMs, family, headerStyle = "antigravity", model) {
@@ -10239,15 +10884,15 @@ var AccountManager = class _AccountManager {
10239
10884
  account.lastUsed = nowMs();
10240
10885
  }
10241
10886
  }
10242
- recordSessionUsage(accountIndex) {
10243
- this.sessionUsedAccounts.add(accountIndex);
10887
+ recordSessionUsage(accountIndex, identity) {
10888
+ this.getUsedAccounts(identity).add(accountIndex);
10244
10889
  }
10245
- wasUsedInSession(accountIndex) {
10246
- return this.sessionUsedAccounts.has(accountIndex);
10890
+ wasUsedInSession(accountIndex, identity) {
10891
+ return this.getUsedAccounts(identity).has(accountIndex);
10247
10892
  }
10248
- shouldProactivelyRotate(family, model, thresholdPercent, cacheTtlMs) {
10893
+ shouldProactivelyRotate(family, model, thresholdPercent, cacheTtlMs, identity) {
10249
10894
  if (thresholdPercent <= 0) return false;
10250
- const current = this.getCurrentAccountForFamily(family);
10895
+ const current = this.getCurrentAccountForFamily(family, identity);
10251
10896
  if (!current || !current.cachedQuota || current.cachedQuotaUpdatedAt == null) return false;
10252
10897
  const age = nowMs() - current.cachedQuotaUpdatedAt;
10253
10898
  if (age > cacheTtlMs) return false;
@@ -10257,9 +10902,9 @@ var AccountManager = class _AccountManager {
10257
10902
  const remainingPercent = Math.max(0, Math.min(100, groupData.remainingFraction * 100));
10258
10903
  return remainingPercent < thresholdPercent;
10259
10904
  }
10260
- proactivelyRotateForFamily(family, model, headerStyle, softQuotaThresholdPercent, softQuotaCacheTtlMs) {
10261
- const currentIndex = this.currentAccountIndexByFamily[family];
10262
- const candidates = this.accounts.filter((acc) => {
10905
+ proactivelyRotateForFamily(family, model, headerStyle, softQuotaThresholdPercent, softQuotaCacheTtlMs, identity) {
10906
+ const currentIndex = this.getActiveIndex(family, identity);
10907
+ const candidates = this.preferAccountOutsideParent(this.accounts.filter((acc) => {
10263
10908
  if (acc.enabled === false) return false;
10264
10909
  if (acc.index === currentIndex) return false;
10265
10910
  clearExpiredRateLimits(acc);
@@ -10267,9 +10912,10 @@ var AccountManager = class _AccountManager {
10267
10912
  if (isOverSoftQuotaThreshold(acc, family, softQuotaThresholdPercent, softQuotaCacheTtlMs, model)) return false;
10268
10913
  if (this.isAccountCoolingDown(acc)) return false;
10269
10914
  return true;
10270
- });
10915
+ }), family, identity);
10271
10916
  if (candidates.length === 0) return null;
10272
- const warmCandidates = candidates.filter((a) => this.sessionUsedAccounts.has(a.index));
10917
+ const usedAccounts = this.getUsedAccounts(identity);
10918
+ const warmCandidates = candidates.filter((account) => usedAccounts.has(account.index));
10273
10919
  const pool = warmCandidates.length > 0 ? warmCandidates : candidates;
10274
10920
  const quotaGroup = resolveQuotaGroup(family, model);
10275
10921
  pool.sort((a, b) => {
@@ -10281,7 +10927,7 @@ var AccountManager = class _AccountManager {
10281
10927
  if (!selected) return null;
10282
10928
  const quotaKey = getQuotaKey(family, headerStyle, model);
10283
10929
  this.markTouchedForQuota(selected, quotaKey);
10284
- this.currentAccountIndexByFamily[family] = selected.index;
10930
+ this.setActiveIndex(family, selected.index, identity);
10285
10931
  return selected;
10286
10932
  }
10287
10933
  markRateLimitedWithReason(account, family, headerStyle, model, reason, retryAfterMs, failureTtlMs = 36e5) {
@@ -10407,6 +11053,9 @@ var AccountManager = class _AccountManager {
10407
11053
  if (!account) {
10408
11054
  return false;
10409
11055
  }
11056
+ if (enabled && account.accountIneligible) {
11057
+ return false;
11058
+ }
10410
11059
  account.enabled = enabled;
10411
11060
  if (!enabled) {
10412
11061
  for (const family of Object.keys(this.currentAccountIndexByFamily)) {
@@ -10424,9 +11073,16 @@ var AccountManager = class _AccountManager {
10424
11073
  if (!account) {
10425
11074
  return false;
10426
11075
  }
11076
+ const timestamp = nowMs();
10427
11077
  account.verificationRequired = true;
10428
- account.verificationRequiredAt = nowMs();
11078
+ account.verificationRequiredAt = timestamp;
10429
11079
  account.verificationRequiredReason = reason?.trim() || void 0;
11080
+ if (account.accountIneligible === true || account.accountIneligibleAt !== void 0 || account.accountIneligibleReason !== void 0) {
11081
+ account.accountIneligible = false;
11082
+ account.accountIneligibleAt = void 0;
11083
+ account.accountIneligibleReason = void 0;
11084
+ account.eligibilityStateUpdatedAt = timestamp;
11085
+ }
10430
11086
  const normalizedVerifyUrl = verifyUrl?.trim();
10431
11087
  if (normalizedVerifyUrl) {
10432
11088
  account.verificationUrl = normalizedVerifyUrl;
@@ -10438,20 +11094,48 @@ var AccountManager = class _AccountManager {
10438
11094
  }
10439
11095
  return true;
10440
11096
  }
10441
- clearAccountVerificationRequired(accountIndex, enableAccount = false) {
11097
+ markAccountIneligible(accountIndex, reason) {
11098
+ const account = this.accounts[accountIndex];
11099
+ if (!account) {
11100
+ return false;
11101
+ }
11102
+ const timestamp = nowMs();
11103
+ account.accountIneligible = true;
11104
+ account.accountIneligibleAt = timestamp;
11105
+ account.accountIneligibleReason = reason?.trim() || "Google marked this account as ineligible.";
11106
+ account.eligibilityStateUpdatedAt = timestamp;
11107
+ account.verificationRequired = false;
11108
+ account.verificationRequiredAt = void 0;
11109
+ account.verificationRequiredReason = void 0;
11110
+ account.verificationUrl = void 0;
11111
+ if (account.enabled !== false) {
11112
+ this.setAccountEnabled(accountIndex, false);
11113
+ } else {
11114
+ this.requestSaveToDisk();
11115
+ }
11116
+ return true;
11117
+ }
11118
+ clearAccountAccessBlocks(accountIndex, enableAccount = false) {
10442
11119
  const account = this.accounts[accountIndex];
10443
11120
  if (!account) {
10444
11121
  return false;
10445
11122
  }
10446
11123
  const wasVerificationRequired = account.verificationRequired === true;
10447
- const hadMetadata = account.verificationRequiredAt !== void 0 || account.verificationRequiredReason !== void 0 || account.verificationUrl !== void 0;
11124
+ const wasIneligible = account.accountIneligible === true;
11125
+ const hadMetadata = wasVerificationRequired || wasIneligible || account.verificationRequiredAt !== void 0 || account.verificationRequiredReason !== void 0 || account.verificationUrl !== void 0 || account.accountIneligibleAt !== void 0 || account.accountIneligibleReason !== void 0 || account.eligibilityStateUpdatedAt !== void 0;
10448
11126
  account.verificationRequired = false;
10449
11127
  account.verificationRequiredAt = void 0;
10450
11128
  account.verificationRequiredReason = void 0;
10451
11129
  account.verificationUrl = void 0;
10452
- if (enableAccount && wasVerificationRequired && account.enabled === false) {
11130
+ account.accountIneligible = false;
11131
+ account.accountIneligibleAt = void 0;
11132
+ account.accountIneligibleReason = void 0;
11133
+ if (wasIneligible || account.eligibilityStateUpdatedAt !== void 0) {
11134
+ account.eligibilityStateUpdatedAt = nowMs();
11135
+ }
11136
+ if (enableAccount && (wasVerificationRequired || wasIneligible) && account.enabled === false) {
10453
11137
  this.setAccountEnabled(accountIndex, true);
10454
- } else if (wasVerificationRequired || hadMetadata) {
11138
+ } else if (hadMetadata) {
10455
11139
  this.requestSaveToDisk();
10456
11140
  }
10457
11141
  return true;
@@ -10479,6 +11163,7 @@ var AccountManager = class _AccountManager {
10479
11163
  this.cursorByFamily = { claude: 0, gemini: 0 };
10480
11164
  this.currentAccountIndexByFamily.claude = -1;
10481
11165
  this.currentAccountIndexByFamily.gemini = -1;
11166
+ this.requestSessionStates.clear();
10482
11167
  return true;
10483
11168
  }
10484
11169
  for (const family of ["claude", "gemini"]) {
@@ -10492,6 +11177,23 @@ var AccountManager = class _AccountManager {
10492
11177
  if (this.currentAccountIndexByFamily[family] >= this.accounts.length) {
10493
11178
  this.currentAccountIndexByFamily[family] = -1;
10494
11179
  }
11180
+ for (const state of this.requestSessionStates.values()) {
11181
+ const currentIndex = state.currentAccountIndexByFamily[family];
11182
+ if (currentIndex === idx) {
11183
+ state.currentAccountIndexByFamily[family] = -1;
11184
+ } else if (currentIndex > idx) {
11185
+ state.currentAccountIndexByFamily[family] -= 1;
11186
+ }
11187
+ if (state.cursorByFamily[family] > idx) {
11188
+ state.cursorByFamily[family] -= 1;
11189
+ }
11190
+ state.cursorByFamily[family] %= this.accounts.length;
11191
+ }
11192
+ }
11193
+ for (const state of this.requestSessionStates.values()) {
11194
+ state.usedAccounts = new Set(
11195
+ [...state.usedAccounts].filter((accountIndex) => accountIndex !== idx).map((accountIndex) => accountIndex > idx ? accountIndex - 1 : accountIndex)
11196
+ );
10495
11197
  }
10496
11198
  return true;
10497
11199
  }
@@ -10547,10 +11249,10 @@ var AccountManager = class _AccountManager {
10547
11249
  getAccounts() {
10548
11250
  return [...this.accounts];
10549
11251
  }
10550
- async saveToDisk() {
11252
+ buildStorageSnapshot() {
10551
11253
  const claudeIndex = Math.max(0, this.currentAccountIndexByFamily.claude);
10552
11254
  const geminiIndex = Math.max(0, this.currentAccountIndexByFamily.gemini);
10553
- const storage = {
11255
+ return {
10554
11256
  version: 4,
10555
11257
  accounts: this.accounts.map((a) => ({
10556
11258
  email: a.email,
@@ -10569,7 +11271,11 @@ var AccountManager = class _AccountManager {
10569
11271
  verificationRequired: a.verificationRequired,
10570
11272
  verificationRequiredAt: a.verificationRequiredAt,
10571
11273
  verificationRequiredReason: a.verificationRequiredReason,
10572
- verificationUrl: a.verificationUrl
11274
+ verificationUrl: a.verificationUrl,
11275
+ accountIneligible: a.accountIneligible,
11276
+ accountIneligibleAt: a.accountIneligibleAt,
11277
+ accountIneligibleReason: a.accountIneligibleReason,
11278
+ eligibilityStateUpdatedAt: a.eligibilityStateUpdatedAt
10573
11279
  })),
10574
11280
  activeIndex: claudeIndex,
10575
11281
  activeIndexByFamily: {
@@ -10577,7 +11283,17 @@ var AccountManager = class _AccountManager {
10577
11283
  gemini: geminiIndex
10578
11284
  }
10579
11285
  };
10580
- await saveAccounts(storage);
11286
+ }
11287
+ async saveToDisk() {
11288
+ await saveAccounts(this.buildStorageSnapshot());
11289
+ }
11290
+ /**
11291
+ * Persist via full-file replace (no merge). Required after destructive
11292
+ * operations (account removal) so a deleted account is not resurrected by
11293
+ * mergeAccountStorage re-reading it from disk.
11294
+ */
11295
+ async saveToDiskReplace() {
11296
+ await saveAccountsReplace(this.buildStorageSnapshot());
10581
11297
  }
10582
11298
  requestSaveToDisk() {
10583
11299
  if (this.savePending) {
@@ -10592,8 +11308,8 @@ var AccountManager = class _AccountManager {
10592
11308
  if (!this.savePending) {
10593
11309
  return;
10594
11310
  }
10595
- return new Promise((resolve, reject) => {
10596
- this.savePromiseResolvers.push({ resolve, reject });
11311
+ return new Promise((resolve2, reject) => {
11312
+ this.savePromiseResolvers.push({ resolve: resolve2, reject });
10597
11313
  });
10598
11314
  }
10599
11315
  async executeSave() {
@@ -10603,14 +11319,14 @@ var AccountManager = class _AccountManager {
10603
11319
  this.savePromiseResolvers = [];
10604
11320
  try {
10605
11321
  await this.saveToDisk();
10606
- for (const { resolve } of resolvers) {
10607
- resolve();
11322
+ for (const { resolve: resolve2 } of resolvers) {
11323
+ resolve2();
10608
11324
  }
10609
11325
  } catch (error) {
10610
11326
  if (isStorageLockContention(error)) {
10611
11327
  debugLogToFile(`[Account] Skipped account-state persist because another plugin instance holds the storage lock: ${String(error)}`);
10612
- for (const { resolve } of resolvers) {
10613
- resolve();
11328
+ for (const { resolve: resolve2 } of resolvers) {
11329
+ resolve2();
10614
11330
  }
10615
11331
  return;
10616
11332
  }
@@ -10788,7 +11504,13 @@ var AccountManager = class _AccountManager {
10788
11504
  };
10789
11505
  }
10790
11506
  isAccountOverSoftQuota(account, family, thresholdPercent, cacheTtlMs, model) {
10791
- return isOverSoftQuotaThreshold(account, family, thresholdPercent, cacheTtlMs, model);
11507
+ return isOverSoftQuotaThreshold(
11508
+ account,
11509
+ family,
11510
+ this.getEffectiveSoftQuotaThreshold(thresholdPercent),
11511
+ cacheTtlMs,
11512
+ model
11513
+ );
10792
11514
  }
10793
11515
  getAccountsForQuotaCheck() {
10794
11516
  return this.accounts.map((a) => ({
@@ -10814,7 +11536,7 @@ var AccountManager = class _AccountManager {
10814
11536
  areAllAccountsOverSoftQuota(family, thresholdPercent, cacheTtlMs, model) {
10815
11537
  if (thresholdPercent >= 100) return false;
10816
11538
  const enabled = this.accounts.filter((a) => a.enabled !== false);
10817
- if (enabled.length === 0) return false;
11539
+ if (enabled.length <= 1) return false;
10818
11540
  return enabled.every((a) => isOverSoftQuotaThreshold(a, family, thresholdPercent, cacheTtlMs, model));
10819
11541
  }
10820
11542
  /**
@@ -10827,6 +11549,7 @@ var AccountManager = class _AccountManager {
10827
11549
  if (thresholdPercent >= 100) return 0;
10828
11550
  const enabled = this.accounts.filter((a) => a.enabled !== false);
10829
11551
  if (enabled.length === 0) return null;
11552
+ if (enabled.length === 1) return 0;
10830
11553
  const available = enabled.filter((a) => !isOverSoftQuotaThreshold(a, family, thresholdPercent, cacheTtlMs, model));
10831
11554
  if (available.length > 0) return 0;
10832
11555
  if (!model && family !== "claude") return null;
@@ -10849,24 +11572,24 @@ var AccountManager = class _AccountManager {
10849
11572
  };
10850
11573
 
10851
11574
  // src/hooks/auto-update-checker/checker.ts
10852
- import * as fs3 from "node:fs";
10853
- import * as path3 from "node:path";
11575
+ import * as fs2 from "node:fs";
11576
+ import * as path2 from "node:path";
10854
11577
  import { fileURLToPath } from "node:url";
10855
11578
 
10856
11579
  // src/hooks/auto-update-checker/constants.ts
10857
- import * as path2 from "node:path";
10858
- import * as os2 from "node:os";
11580
+ import * as path from "node:path";
11581
+ import * as os from "node:os";
10859
11582
  var PACKAGE_NAME = "@cortexkit/opencode-antigravity-auth";
10860
11583
  var NPM_REGISTRY_URL = `https://registry.npmjs.org/-/package/${PACKAGE_NAME}/dist-tags`;
10861
11584
  var NPM_FETCH_TIMEOUT = 5e3;
10862
11585
  function getCacheDir() {
10863
11586
  if (process.platform === "win32") {
10864
- return path2.join(process.env.LOCALAPPDATA ?? os2.homedir(), "opencode");
11587
+ return path.join(process.env.LOCALAPPDATA ?? os.homedir(), "opencode");
10865
11588
  }
10866
- return path2.join(os2.homedir(), ".cache", "opencode");
11589
+ return path.join(os.homedir(), ".cache", "opencode");
10867
11590
  }
10868
11591
  var CACHE_DIR = getCacheDir();
10869
- var INSTALLED_PACKAGE_JSON = path2.join(
11592
+ var INSTALLED_PACKAGE_JSON = path.join(
10870
11593
  CACHE_DIR,
10871
11594
  "node_modules",
10872
11595
  PACKAGE_NAME,
@@ -10874,13 +11597,13 @@ var INSTALLED_PACKAGE_JSON = path2.join(
10874
11597
  );
10875
11598
  function getUserConfigDir() {
10876
11599
  if (process.platform === "win32") {
10877
- return process.env.APPDATA ?? path2.join(os2.homedir(), "AppData", "Roaming");
11600
+ return process.env.APPDATA ?? path.join(os.homedir(), "AppData", "Roaming");
10878
11601
  }
10879
- return process.env.XDG_CONFIG_HOME ?? path2.join(os2.homedir(), ".config");
11602
+ return process.env.XDG_CONFIG_HOME ?? path.join(os.homedir(), ".config");
10880
11603
  }
10881
11604
  var USER_CONFIG_DIR = getUserConfigDir();
10882
- var USER_OPENCODE_CONFIG = path2.join(USER_CONFIG_DIR, "opencode", "opencode.json");
10883
- var USER_OPENCODE_CONFIG_JSONC = path2.join(USER_CONFIG_DIR, "opencode", "opencode.jsonc");
11605
+ var USER_OPENCODE_CONFIG = path.join(USER_CONFIG_DIR, "opencode", "opencode.json");
11606
+ var USER_OPENCODE_CONFIG_JSONC = path.join(USER_CONFIG_DIR, "opencode", "opencode.jsonc");
10884
11607
 
10885
11608
  // src/hooks/auto-update-checker/logging.ts
10886
11609
  var AUTO_UPDATE_LOG_PREFIX = "[auto-update-checker]";
@@ -10897,9 +11620,9 @@ function stripJsonComments(json) {
10897
11620
  }
10898
11621
  function getConfigPaths(directory) {
10899
11622
  return [
10900
- path3.join(directory, ".opencode", "opencode.json"),
10901
- path3.join(directory, ".opencode", "opencode.jsonc"),
10902
- path3.join(directory, ".opencode.json"),
11623
+ path2.join(directory, ".opencode", "opencode.json"),
11624
+ path2.join(directory, ".opencode", "opencode.jsonc"),
11625
+ path2.join(directory, ".opencode.json"),
10903
11626
  USER_OPENCODE_CONFIG,
10904
11627
  USER_OPENCODE_CONFIG_JSONC
10905
11628
  ];
@@ -10907,8 +11630,8 @@ function getConfigPaths(directory) {
10907
11630
  function getLocalDevPath(directory) {
10908
11631
  for (const configPath of getConfigPaths(directory)) {
10909
11632
  try {
10910
- if (!fs3.existsSync(configPath)) continue;
10911
- const content = fs3.readFileSync(configPath, "utf-8");
11633
+ if (!fs2.existsSync(configPath)) continue;
11634
+ const content = fs2.readFileSync(configPath, "utf-8");
10912
11635
  const config = JSON.parse(stripJsonComments(content));
10913
11636
  const plugins = config.plugin ?? [];
10914
11637
  for (const entry of plugins) {
@@ -10928,20 +11651,20 @@ function getLocalDevPath(directory) {
10928
11651
  }
10929
11652
  function findPackageJsonUp(startPath) {
10930
11653
  try {
10931
- const stat = fs3.statSync(startPath);
10932
- let dir = stat.isDirectory() ? startPath : path3.dirname(startPath);
11654
+ const stat = fs2.statSync(startPath);
11655
+ let dir = stat.isDirectory() ? startPath : path2.dirname(startPath);
10933
11656
  for (let i = 0; i < 10; i++) {
10934
- const pkgPath = path3.join(dir, "package.json");
10935
- if (fs3.existsSync(pkgPath)) {
11657
+ const pkgPath = path2.join(dir, "package.json");
11658
+ if (fs2.existsSync(pkgPath)) {
10936
11659
  try {
10937
- const content = fs3.readFileSync(pkgPath, "utf-8");
11660
+ const content = fs2.readFileSync(pkgPath, "utf-8");
10938
11661
  const pkg = JSON.parse(content);
10939
11662
  if (pkg.name === PACKAGE_NAME) return pkgPath;
10940
11663
  } catch {
10941
11664
  continue;
10942
11665
  }
10943
11666
  }
10944
- const parent = path3.dirname(dir);
11667
+ const parent = path2.dirname(dir);
10945
11668
  if (parent === dir) break;
10946
11669
  dir = parent;
10947
11670
  }
@@ -10956,7 +11679,7 @@ function getLocalDevVersion(directory) {
10956
11679
  try {
10957
11680
  const pkgPath = findPackageJsonUp(localPath);
10958
11681
  if (!pkgPath) return null;
10959
- const content = fs3.readFileSync(pkgPath, "utf-8");
11682
+ const content = fs2.readFileSync(pkgPath, "utf-8");
10960
11683
  const pkg = JSON.parse(content);
10961
11684
  return pkg.version ?? null;
10962
11685
  } catch {
@@ -10966,8 +11689,8 @@ function getLocalDevVersion(directory) {
10966
11689
  function findPluginEntry(directory) {
10967
11690
  for (const configPath of getConfigPaths(directory)) {
10968
11691
  try {
10969
- if (!fs3.existsSync(configPath)) continue;
10970
- const content = fs3.readFileSync(configPath, "utf-8");
11692
+ if (!fs2.existsSync(configPath)) continue;
11693
+ const content = fs2.readFileSync(configPath, "utf-8");
10971
11694
  const config = JSON.parse(stripJsonComments(content));
10972
11695
  const plugins = config.plugin ?? [];
10973
11696
  for (const entry of plugins) {
@@ -10991,8 +11714,8 @@ function findPluginEntry(directory) {
10991
11714
  }
10992
11715
  function getCachedVersion() {
10993
11716
  try {
10994
- if (fs3.existsSync(INSTALLED_PACKAGE_JSON)) {
10995
- const content = fs3.readFileSync(INSTALLED_PACKAGE_JSON, "utf-8");
11717
+ if (fs2.existsSync(INSTALLED_PACKAGE_JSON)) {
11718
+ const content = fs2.readFileSync(INSTALLED_PACKAGE_JSON, "utf-8");
10996
11719
  const pkg = JSON.parse(content);
10997
11720
  if (pkg.version) return pkg.version;
10998
11721
  }
@@ -11000,10 +11723,10 @@ function getCachedVersion() {
11000
11723
  return null;
11001
11724
  }
11002
11725
  try {
11003
- const currentDir = path3.dirname(fileURLToPath(import.meta.url));
11726
+ const currentDir = path2.dirname(fileURLToPath(import.meta.url));
11004
11727
  const pkgPath = findPackageJsonUp(currentDir);
11005
11728
  if (pkgPath) {
11006
- const content = fs3.readFileSync(pkgPath, "utf-8");
11729
+ const content = fs2.readFileSync(pkgPath, "utf-8");
11007
11730
  const pkg = JSON.parse(content);
11008
11731
  if (pkg.version) return pkg.version;
11009
11732
  }
@@ -11014,7 +11737,7 @@ function getCachedVersion() {
11014
11737
  }
11015
11738
  function updatePinnedVersion(configPath, oldEntry, newVersion) {
11016
11739
  try {
11017
- const content = fs3.readFileSync(configPath, "utf-8");
11740
+ const content = fs2.readFileSync(configPath, "utf-8");
11018
11741
  const newEntry = `${PACKAGE_NAME}@${newVersion}`;
11019
11742
  const pluginMatch = content.match(/"plugin"\s*:\s*\[/);
11020
11743
  if (!pluginMatch || pluginMatch.index === void 0) {
@@ -11044,7 +11767,7 @@ function updatePinnedVersion(configPath, oldEntry, newVersion) {
11044
11767
  logAutoUpdate(`No changes made to ${configPath}`);
11045
11768
  return false;
11046
11769
  }
11047
- fs3.writeFileSync(configPath, updatedContent, "utf-8");
11770
+ fs2.writeFileSync(configPath, updatedContent, "utf-8");
11048
11771
  logAutoUpdate(`Updated ${configPath}: ${oldEntry} \u2192 ${newEntry}`);
11049
11772
  return true;
11050
11773
  } catch (err) {
@@ -11071,16 +11794,16 @@ async function getLatestVersion() {
11071
11794
  }
11072
11795
 
11073
11796
  // src/hooks/auto-update-checker/cache.ts
11074
- import * as fs4 from "node:fs";
11075
- import * as path4 from "node:path";
11797
+ import * as fs3 from "node:fs";
11798
+ import * as path3 from "node:path";
11076
11799
  function stripTrailingCommas(json) {
11077
11800
  return json.replace(/,(\s*[}\]])/g, "$1");
11078
11801
  }
11079
11802
  function removeFromBunLock(packageName) {
11080
- const lockPath = path4.join(CACHE_DIR, "bun.lock");
11081
- if (!fs4.existsSync(lockPath)) return false;
11803
+ const lockPath = path3.join(CACHE_DIR, "bun.lock");
11804
+ if (!fs3.existsSync(lockPath)) return false;
11082
11805
  try {
11083
- const content = fs4.readFileSync(lockPath, "utf-8");
11806
+ const content = fs3.readFileSync(lockPath, "utf-8");
11084
11807
  const lock = JSON.parse(stripTrailingCommas(content));
11085
11808
  let modified = false;
11086
11809
  if (lock.workspaces?.[""]?.dependencies?.[packageName]) {
@@ -11092,7 +11815,7 @@ function removeFromBunLock(packageName) {
11092
11815
  modified = true;
11093
11816
  }
11094
11817
  if (modified) {
11095
- fs4.writeFileSync(lockPath, JSON.stringify(lock, null, 2));
11818
+ fs3.writeFileSync(lockPath, JSON.stringify(lock, null, 2));
11096
11819
  console.log(`[auto-update-checker] Removed from bun.lock: ${packageName}`);
11097
11820
  }
11098
11821
  return modified;
@@ -11102,22 +11825,22 @@ function removeFromBunLock(packageName) {
11102
11825
  }
11103
11826
  function invalidatePackage(packageName = PACKAGE_NAME) {
11104
11827
  try {
11105
- const pkgDir = path4.join(CACHE_DIR, "node_modules", packageName);
11106
- const pkgJsonPath = path4.join(CACHE_DIR, "package.json");
11828
+ const pkgDir = path3.join(CACHE_DIR, "node_modules", packageName);
11829
+ const pkgJsonPath = path3.join(CACHE_DIR, "package.json");
11107
11830
  let packageRemoved = false;
11108
11831
  let dependencyRemoved = false;
11109
11832
  let lockRemoved = false;
11110
- if (fs4.existsSync(pkgDir)) {
11111
- fs4.rmSync(pkgDir, { recursive: true, force: true });
11833
+ if (fs3.existsSync(pkgDir)) {
11834
+ fs3.rmSync(pkgDir, { recursive: true, force: true });
11112
11835
  console.log(`[auto-update-checker] Package removed: ${pkgDir}`);
11113
11836
  packageRemoved = true;
11114
11837
  }
11115
- if (fs4.existsSync(pkgJsonPath)) {
11116
- const content = fs4.readFileSync(pkgJsonPath, "utf-8");
11838
+ if (fs3.existsSync(pkgJsonPath)) {
11839
+ const content = fs3.readFileSync(pkgJsonPath, "utf-8");
11117
11840
  const pkgJson = JSON.parse(content);
11118
11841
  if (pkgJson.dependencies?.[packageName]) {
11119
11842
  delete pkgJson.dependencies[packageName];
11120
- fs4.writeFileSync(pkgJsonPath, JSON.stringify(pkgJson, null, 2));
11843
+ fs3.writeFileSync(pkgJsonPath, JSON.stringify(pkgJson, null, 2));
11121
11844
  console.log(`[auto-update-checker] Dependency removed from package.json: ${packageName}`);
11122
11845
  dependencyRemoved = true;
11123
11846
  }
@@ -11419,6 +12142,15 @@ function createAuthDoctorReport(input2) {
11419
12142
  }
11420
12143
  }
11421
12144
  for (const account of storage.accounts) {
12145
+ if (account.accountIneligible) {
12146
+ findings.push({
12147
+ code: "account-ineligible",
12148
+ severity: "warning",
12149
+ message: account.accountIneligibleReason ?? "Google marked this account as ineligible for Antigravity.",
12150
+ repair: "verify-account",
12151
+ accountEmail: account.email
12152
+ });
12153
+ }
11422
12154
  if (account.verificationRequired) {
11423
12155
  findings.push({
11424
12156
  code: "verification-required",
@@ -12199,6 +12931,69 @@ async function resolvePromptContext(client, sessionId) {
12199
12931
  return result;
12200
12932
  }
12201
12933
 
12934
+ // src/plugin/session-context.ts
12935
+ import { resolve } from "node:path";
12936
+ import { pathToFileURL } from "node:url";
12937
+ var FALLBACK_SESSION_KEY = "__default__";
12938
+ function extractOpenCodeSessionIdentity(headers) {
12939
+ const normalized = new Headers(headers);
12940
+ return {
12941
+ sessionId: normalized.get("x-session-affinity") ?? normalized.get("x-session-id"),
12942
+ parentSessionId: normalized.get("x-parent-session-id")
12943
+ };
12944
+ }
12945
+ var AgySessionRegistry = class {
12946
+ requestSessions;
12947
+ parentSessionIds = /* @__PURE__ */ new Map();
12948
+ constructor(directory, options = {}) {
12949
+ const workspaceUri = directory ? pathToFileURL(resolve(directory)).href : "";
12950
+ this.requestSessions = new AgyRequestSessionStore(workspaceUri, options);
12951
+ }
12952
+ getOrCreate(identity) {
12953
+ const key = identity.sessionId ?? FALLBACK_SESSION_KEY;
12954
+ const request = this.requestSessions.getOrCreate(key);
12955
+ this.recordParent(key, identity.parentSessionId);
12956
+ this.pruneParentRelationships();
12957
+ return request;
12958
+ }
12959
+ beginRequest(identity) {
12960
+ const key = identity.sessionId ?? FALLBACK_SESSION_KEY;
12961
+ const scope = this.requestSessions.beginRequest(key);
12962
+ this.recordParent(key, identity.parentSessionId);
12963
+ this.pruneParentRelationships();
12964
+ return scope;
12965
+ }
12966
+ register(sessionId, parentSessionId = null) {
12967
+ this.getOrCreate({ sessionId, parentSessionId });
12968
+ }
12969
+ getParentSessionId(sessionId) {
12970
+ if (!this.requestSessions.has(sessionId)) {
12971
+ this.parentSessionIds.delete(sessionId);
12972
+ return null;
12973
+ }
12974
+ return this.parentSessionIds.get(sessionId) ?? null;
12975
+ }
12976
+ delete(sessionId) {
12977
+ this.requestSessions.delete(sessionId);
12978
+ this.parentSessionIds.delete(sessionId);
12979
+ }
12980
+ get size() {
12981
+ return this.requestSessions.size;
12982
+ }
12983
+ recordParent(key, parentSessionId) {
12984
+ if (parentSessionId || !this.parentSessionIds.has(key)) {
12985
+ this.parentSessionIds.set(key, parentSessionId);
12986
+ }
12987
+ }
12988
+ pruneParentRelationships() {
12989
+ for (const sessionId of this.parentSessionIds.keys()) {
12990
+ if (!this.requestSessions.has(sessionId)) {
12991
+ this.parentSessionIds.delete(sessionId);
12992
+ }
12993
+ }
12994
+ }
12995
+ };
12996
+
12202
12997
  // src/plugin.ts
12203
12998
  var MAX_OAUTH_ACCOUNTS = 10;
12204
12999
  var MAX_WARMUP_SESSIONS = 1e3;
@@ -12209,8 +13004,6 @@ function isCapacityRetryBudgetExhausted(totalCapacityRetries) {
12209
13004
  }
12210
13005
  var warmupAttemptedSessionIds = /* @__PURE__ */ new Set();
12211
13006
  var warmupSucceededSessionIds = /* @__PURE__ */ new Set();
12212
- var isChildSession = false;
12213
- var childSessionParentID = void 0;
12214
13007
  var log10 = createLogger2("plugin");
12215
13008
  var rateLimitToastCooldowns = /* @__PURE__ */ new Map();
12216
13009
  var RATE_LIMIT_TOAST_COOLDOWN_MS = 5e3;
@@ -12401,10 +13194,12 @@ function selectBestVerificationUrl(urls) {
12401
13194
  });
12402
13195
  return unique[0];
12403
13196
  }
12404
- function extractVerificationErrorDetails(bodyText) {
13197
+ function extractAccountAccessErrorDetails(bodyText) {
12405
13198
  const decodedBody = decodeEscapedText(bodyText);
12406
13199
  const lowerBody = decodedBody.toLowerCase();
12407
13200
  let validationRequired = lowerBody.includes("validation_required");
13201
+ const ineligiblePattern = /(^|[^a-z0-9_])account_ineligible([^a-z0-9_]|$)/i;
13202
+ let accountIneligible = ineligiblePattern.test(decodedBody);
12408
13203
  let message;
12409
13204
  const verificationUrls = /* @__PURE__ */ new Set();
12410
13205
  const collectUrlsFromText = (text) => {
@@ -12447,6 +13242,9 @@ function extractVerificationErrorDetails(bodyText) {
12447
13242
  if (lowerValue.includes("validation_required")) {
12448
13243
  validationRequired = true;
12449
13244
  }
13245
+ if (ineligiblePattern.test(normalizedValue)) {
13246
+ accountIneligible = true;
13247
+ }
12450
13248
  if (!message && (lowerKey.includes("message") || lowerKey.includes("detail") || lowerKey.includes("description"))) {
12451
13249
  message = normalizedValue;
12452
13250
  }
@@ -12477,17 +13275,72 @@ function extractVerificationErrorDetails(bodyText) {
12477
13275
  validationRequired = lowerBody.includes("verification required") || lowerBody.includes("verify your account") || lowerBody.includes("account verification");
12478
13276
  }
12479
13277
  if (!message) {
12480
- const fallback = decodedBody.split("\n").map((line) => line.trim()).find((line) => line && !line.startsWith("data:") && /(verify|validation|required)/i.test(line));
13278
+ const fallback = decodedBody.split("\n").map((line) => line.trim()).find((line) => line && !line.startsWith("data:") && /(verify|validation|required|ineligible)/i.test(line));
12481
13279
  if (fallback) {
12482
13280
  message = fallback;
12483
13281
  }
12484
13282
  }
12485
13283
  return {
12486
13284
  validationRequired,
13285
+ accountIneligible,
12487
13286
  message,
12488
13287
  verifyUrl: selectBestVerificationUrl([...verificationUrls])
12489
13288
  };
12490
13289
  }
13290
+ function buildAccountAccessProbeRequest(projectId) {
13291
+ const wireModel = "gemini-3.5-flash-low";
13292
+ const request = {
13293
+ contents: [{ role: "user", parts: [{ text: "ping" }] }],
13294
+ generationConfig: { maxOutputTokens: 1, temperature: 0 }
13295
+ };
13296
+ const requestMetadata = buildAgyAgentRequestMetadata(
13297
+ createAgyRequestSessionContext(""),
13298
+ request,
13299
+ wireModel
13300
+ );
13301
+ request.labels = requestMetadata.labels;
13302
+ request.sessionId = requestMetadata.sessionId;
13303
+ orderAgyRequestPayloadInPlace(request);
13304
+ return {
13305
+ project: projectId,
13306
+ requestId: requestMetadata.requestId,
13307
+ request,
13308
+ model: wireModel,
13309
+ userAgent: "antigravity",
13310
+ requestType: "agent"
13311
+ };
13312
+ }
13313
+ async function interpretAccountAccessProbeResponse(response) {
13314
+ if (response.ok) {
13315
+ await response.body?.cancel().catch(() => {
13316
+ });
13317
+ return { status: "ok", message: "Account verification check passed." };
13318
+ }
13319
+ let responseBody = "";
13320
+ try {
13321
+ responseBody = await response.text();
13322
+ } catch {
13323
+ responseBody = "";
13324
+ }
13325
+ const extracted = extractAccountAccessErrorDetails(responseBody);
13326
+ if (response.status === 403 && extracted.accountIneligible) {
13327
+ return {
13328
+ status: "ineligible",
13329
+ message: extracted.message ?? "Google marked this account as ineligible for Antigravity."
13330
+ };
13331
+ }
13332
+ if (response.status === 403 && extracted.validationRequired) {
13333
+ return {
13334
+ status: "verification-required",
13335
+ message: extracted.message ?? "Google requires additional account verification.",
13336
+ verifyUrl: extracted.verifyUrl
13337
+ };
13338
+ }
13339
+ return {
13340
+ status: "error",
13341
+ message: extracted.message ?? `Request failed (${response.status} ${response.statusText}).`
13342
+ };
13343
+ }
12491
13344
  async function verifyAccountAccess(account, client, providerId) {
12492
13345
  const parsed = parseRefreshParts(account.refreshToken);
12493
13346
  if (!parsed.refreshToken) {
@@ -12523,18 +13376,7 @@ async function verifyAccountAccess(account, client, providerId) {
12523
13376
  "Content-Type": "application/json",
12524
13377
  "Accept-Encoding": "gzip"
12525
13378
  };
12526
- const requestBody = {
12527
- project: projectId,
12528
- requestId: `agent/${crypto4.randomUUID()}/${Date.now()}/${crypto4.randomUUID()}/2`,
12529
- request: {
12530
- model: "gemini-3-flash",
12531
- contents: [{ role: "user", parts: [{ text: "ping" }] }],
12532
- generationConfig: { maxOutputTokens: 1, temperature: 0 }
12533
- },
12534
- model: "gemini-3-flash",
12535
- userAgent: "antigravity",
12536
- requestType: "agent"
12537
- };
13379
+ const requestBody = buildAccountAccessProbeRequest(projectId);
12538
13380
  const controller = new AbortController();
12539
13381
  const timeoutId = setTimeout(() => controller.abort(), 2e4);
12540
13382
  let response;
@@ -12552,28 +13394,7 @@ async function verifyAccountAccess(account, client, providerId) {
12552
13394
  } finally {
12553
13395
  clearTimeout(timeoutId);
12554
13396
  }
12555
- let responseBody = "";
12556
- try {
12557
- responseBody = await response.text();
12558
- } catch {
12559
- responseBody = "";
12560
- }
12561
- if (response.ok) {
12562
- return { status: "ok", message: "Account verification check passed." };
12563
- }
12564
- const extracted = extractVerificationErrorDetails(responseBody);
12565
- if (response.status === 403 && extracted.validationRequired) {
12566
- return {
12567
- status: "blocked",
12568
- message: extracted.message ?? "Google requires additional account verification.",
12569
- verifyUrl: extracted.verifyUrl
12570
- };
12571
- }
12572
- const fallbackMessage = extracted.message ?? `Request failed (${response.status} ${response.statusText}).`;
12573
- return {
12574
- status: "error",
12575
- message: fallbackMessage
12576
- };
13397
+ return interpretAccountAccessProbeResponse(response);
12577
13398
  }
12578
13399
  async function promptAccountIndexForVerification(accounts) {
12579
13400
  const { createInterface: createInterface2 } = await import("node:readline/promises");
@@ -12615,12 +13436,20 @@ async function promptOpenVerificationUrl() {
12615
13436
  function markStoredAccountVerificationRequired(account, reason, verifyUrl) {
12616
13437
  let changed = false;
12617
13438
  const wasVerificationRequired = account.verificationRequired === true;
13439
+ const timestamp = Date.now();
12618
13440
  if (!wasVerificationRequired) {
12619
13441
  account.verificationRequired = true;
12620
13442
  changed = true;
12621
13443
  }
12622
13444
  if (!wasVerificationRequired || account.verificationRequiredAt === void 0) {
12623
- account.verificationRequiredAt = Date.now();
13445
+ account.verificationRequiredAt = timestamp;
13446
+ changed = true;
13447
+ }
13448
+ if (account.accountIneligible === true || account.accountIneligibleAt !== void 0 || account.accountIneligibleReason !== void 0) {
13449
+ account.accountIneligible = false;
13450
+ account.accountIneligibleAt = void 0;
13451
+ account.accountIneligibleReason = void 0;
13452
+ account.eligibilityStateUpdatedAt = timestamp;
12624
13453
  changed = true;
12625
13454
  }
12626
13455
  const normalizedReason = reason.trim();
@@ -12639,8 +13468,25 @@ function markStoredAccountVerificationRequired(account, reason, verifyUrl) {
12639
13468
  }
12640
13469
  return changed;
12641
13470
  }
12642
- function clearStoredAccountVerificationRequired(account, enableIfRequired = false) {
13471
+ function markStoredAccountIneligible(account, reason) {
13472
+ const timestamp = Date.now();
13473
+ const normalizedReason = reason.trim() || "Google marked this account as ineligible.";
13474
+ const changed = account.accountIneligible !== true || account.accountIneligibleReason !== normalizedReason || account.verificationRequired === true || account.verificationRequiredAt !== void 0 || account.verificationRequiredReason !== void 0 || account.verificationUrl !== void 0 || account.enabled !== false;
13475
+ account.accountIneligible = true;
13476
+ account.accountIneligibleAt = timestamp;
13477
+ account.accountIneligibleReason = normalizedReason;
13478
+ account.eligibilityStateUpdatedAt = timestamp;
13479
+ account.verificationRequired = false;
13480
+ account.verificationRequiredAt = void 0;
13481
+ account.verificationRequiredReason = void 0;
13482
+ account.verificationUrl = void 0;
13483
+ account.enabled = false;
13484
+ return changed;
13485
+ }
13486
+ function clearStoredAccountAccessBlocks(account, enableIfBlocked = false) {
12643
13487
  const wasVerificationRequired = account.verificationRequired === true;
13488
+ const wasIneligible = account.accountIneligible === true;
13489
+ const wasAccessBlocked = wasVerificationRequired || wasIneligible;
12644
13490
  let changed = false;
12645
13491
  if (account.verificationRequired !== false) {
12646
13492
  account.verificationRequired = false;
@@ -12658,11 +13504,27 @@ function clearStoredAccountVerificationRequired(account, enableIfRequired = fals
12658
13504
  account.verificationUrl = void 0;
12659
13505
  changed = true;
12660
13506
  }
12661
- if (enableIfRequired && wasVerificationRequired && account.enabled === false) {
13507
+ if (account.accountIneligible !== false) {
13508
+ account.accountIneligible = false;
13509
+ changed = true;
13510
+ }
13511
+ if (account.accountIneligibleAt !== void 0) {
13512
+ account.accountIneligibleAt = void 0;
13513
+ changed = true;
13514
+ }
13515
+ if (account.accountIneligibleReason !== void 0) {
13516
+ account.accountIneligibleReason = void 0;
13517
+ changed = true;
13518
+ }
13519
+ if (wasIneligible || account.eligibilityStateUpdatedAt !== void 0) {
13520
+ account.eligibilityStateUpdatedAt = Date.now();
13521
+ changed = true;
13522
+ }
13523
+ if (enableIfBlocked && wasAccessBlocked && account.enabled === false) {
12662
13524
  account.enabled = true;
12663
13525
  changed = true;
12664
13526
  }
12665
- return { changed, wasVerificationRequired };
13527
+ return { changed, wasAccessBlocked };
12666
13528
  }
12667
13529
  async function promptOAuthCallbackValue(message) {
12668
13530
  const { createInterface: createInterface2 } = await import("node:readline/promises");
@@ -13054,14 +13916,14 @@ function throwHandledCommandSentinel() {
13054
13916
  throw new Error(HANDLED_COMMAND_SENTINEL);
13055
13917
  }
13056
13918
  function sleep(ms, signal) {
13057
- return new Promise((resolve, reject) => {
13919
+ return new Promise((resolve2, reject) => {
13058
13920
  if (signal?.aborted) {
13059
13921
  reject(signal.reason instanceof Error ? signal.reason : new Error("Aborted"));
13060
13922
  return;
13061
13923
  }
13062
13924
  const timeout = setTimeout(() => {
13063
13925
  cleanup();
13064
- resolve();
13926
+ resolve2();
13065
13927
  }, ms);
13066
13928
  const onAbort = () => {
13067
13929
  cleanup();
@@ -13077,6 +13939,7 @@ function sleep(ms, signal) {
13077
13939
  var createAntigravityPlugin = (providerId) => async ({ client, directory }) => {
13078
13940
  const config = loadConfig(directory);
13079
13941
  initRuntimeConfig(config);
13942
+ const agySessionRegistry = new AgySessionRegistry(directory);
13080
13943
  let cachedGetAuth = null;
13081
13944
  initializeDebug(config);
13082
13945
  initLogger(client);
@@ -13110,25 +13973,34 @@ var createAntigravityPlugin = (providerId) => async ({ client, directory }) => {
13110
13973
  const eventHandler = async (input2) => {
13111
13974
  await updateChecker.event(input2);
13112
13975
  if (input2.event.type === "session.created") {
13113
- const prevSummary = activeAccountManager?.getSessionSummary();
13114
- if (prevSummary && (prevSummary.totalClaude > 0 || prevSummary.totalGemini > 0)) {
13115
- log10.debug("prev-session-quota-summary", {
13116
- durationMinutes: prevSummary.durationMinutes,
13117
- totalClaude: prevSummary.totalClaude,
13118
- totalGemini: prevSummary.totalGemini,
13119
- requestsPerHour: prevSummary.requestsPerHour,
13120
- accountsUsed: prevSummary.accountsUsed
13121
- });
13122
- }
13123
13976
  const props = input2.event.properties;
13124
- if (props?.info?.parentID) {
13125
- isChildSession = true;
13126
- childSessionParentID = props.info.parentID;
13127
- log10.debug("child-session-detected", { parentID: props.info.parentID });
13977
+ const sessionId = props?.info?.id;
13978
+ const parentSessionId = props?.info?.parentID ?? null;
13979
+ if (sessionId) {
13980
+ agySessionRegistry.register(sessionId, parentSessionId);
13981
+ }
13982
+ if (parentSessionId) {
13983
+ log10.debug("child-session-detected", { sessionId, parentID: parentSessionId });
13128
13984
  } else {
13129
- isChildSession = false;
13130
- childSessionParentID = void 0;
13131
- log10.debug("root-session-detected", {});
13985
+ const prevSummary = activeAccountManager?.getSessionSummary();
13986
+ if (prevSummary && (prevSummary.totalClaude > 0 || prevSummary.totalGemini > 0)) {
13987
+ log10.debug("prev-session-quota-summary", {
13988
+ durationMinutes: prevSummary.durationMinutes,
13989
+ totalClaude: prevSummary.totalClaude,
13990
+ totalGemini: prevSummary.totalGemini,
13991
+ requestsPerHour: prevSummary.requestsPerHour,
13992
+ accountsUsed: prevSummary.accountsUsed
13993
+ });
13994
+ }
13995
+ log10.debug("root-session-detected", { sessionId });
13996
+ }
13997
+ }
13998
+ if (input2.event.type === "session.deleted") {
13999
+ const props = input2.event.properties;
14000
+ const sessionId = props?.sessionID ?? props?.info?.id;
14001
+ if (sessionId) {
14002
+ agySessionRegistry.delete(sessionId);
14003
+ activeAccountManager?.deleteSessionState(sessionId);
13132
14004
  }
13133
14005
  }
13134
14006
  if (sessionRecovery && input2.event.type === "session.error") {
@@ -13152,8 +14024,13 @@ var createAntigravityPlugin = (providerId) => async ({ client, directory }) => {
13152
14024
  }).catch(() => {
13153
14025
  });
13154
14026
  const successToast = getRecoverySuccessToast();
13155
- log10.debug("recovery-toast", { ...successToast, isChildSession, toastScope: config.toast_scope });
13156
- if (!(config.toast_scope === "root_only" && isChildSession)) {
14027
+ const isChildRecovery = agySessionRegistry.getParentSessionId(sessionID) !== null;
14028
+ log10.debug("recovery-toast", {
14029
+ ...successToast,
14030
+ isChildSession: isChildRecovery,
14031
+ toastScope: config.toast_scope
14032
+ });
14033
+ if (!(config.toast_scope === "root_only" && isChildRecovery)) {
13157
14034
  await client.tui.showToast({
13158
14035
  body: {
13159
14036
  title: successToast.title,
@@ -13318,6 +14195,39 @@ var createAntigravityPlugin = (providerId) => async ({ client, directory }) => {
13318
14195
  if (!isOAuthAuth(latestAuth)) {
13319
14196
  return fetch(input2, init);
13320
14197
  }
14198
+ if (typeof input2 !== "string") {
14199
+ if (input2 instanceof Request) {
14200
+ const req = input2;
14201
+ const headers = new Headers(req.headers);
14202
+ if (init?.headers) {
14203
+ new Headers(init.headers).forEach((v, k) => headers.set(k, v));
14204
+ }
14205
+ const bodyBuffer = req.body ? await req.clone().arrayBuffer() : void 0;
14206
+ init = {
14207
+ method: init?.method ?? req.method,
14208
+ headers,
14209
+ body: init?.body ?? (bodyBuffer ? Buffer.from(bodyBuffer) : void 0),
14210
+ signal: init?.signal ?? req.signal
14211
+ };
14212
+ input2 = req.url;
14213
+ } else {
14214
+ input2 = String(input2.href ?? input2);
14215
+ }
14216
+ }
14217
+ const localImageTitle = getImageModelLocalTitle(input2, init);
14218
+ if (localImageTitle !== void 0) {
14219
+ return createSyntheticTextResponse(localImageTitle, {
14220
+ "X-Antigravity-Response-Type": "local_title"
14221
+ });
14222
+ }
14223
+ const requestSessionIdentity = extractOpenCodeSessionIdentity(init?.headers);
14224
+ const agyRequestScope = agySessionRegistry.beginRequest(requestSessionIdentity);
14225
+ const agyRequestSession = agyRequestScope.session;
14226
+ const accountSessionIdentity = requestSessionIdentity.sessionId ? {
14227
+ id: requestSessionIdentity.sessionId,
14228
+ parentId: requestSessionIdentity.parentSessionId
14229
+ } : void 0;
14230
+ const isChildRequest = requestSessionIdentity.parentSessionId !== null;
13321
14231
  if (accountManager.getAccountCount() === 0) {
13322
14232
  return createSyntheticErrorResponse(
13323
14233
  "No Antigravity accounts configured. Run `opencode auth login`.",
@@ -13333,6 +14243,11 @@ var createAntigravityPlugin = (providerId) => async ({ client, directory }) => {
13333
14243
  debugLines.push(line);
13334
14244
  };
13335
14245
  pushDebug(`request=${urlString}`);
14246
+ if (requestSessionIdentity.sessionId) {
14247
+ pushDebug(
14248
+ `[Session] id=${requestSessionIdentity.sessionId} parent=${requestSessionIdentity.parentSessionId ?? "none"} child=${isChildRequest}`
14249
+ );
14250
+ }
13336
14251
  const cachedStats = getLastCacheStats();
13337
14252
  if (cachedStats) {
13338
14253
  const label = cachedStats.hitRate > 0 ? "HIT" : "MISS";
@@ -13349,11 +14264,15 @@ var createAntigravityPlugin = (providerId) => async ({ client, directory }) => {
13349
14264
  const quietMode = config.quiet_mode;
13350
14265
  const toastScope = config.toast_scope;
13351
14266
  const showToast = async (message, variant) => {
13352
- log10.debug("toast", { message, variant, isChildSession, toastScope });
14267
+ log10.debug("toast", { message, variant, isChildSession: isChildRequest, toastScope });
13353
14268
  if (quietMode) return;
13354
14269
  if (abortSignal?.aborted) return;
13355
- if (toastScope === "root_only" && isChildSession) {
13356
- log10.debug("toast-suppressed-child-session", { message, variant, parentID: childSessionParentID });
14270
+ if (toastScope === "root_only" && isChildRequest) {
14271
+ log10.debug("toast-suppressed-child-session", {
14272
+ message,
14273
+ variant,
14274
+ parentID: requestSessionIdentity.parentSessionId
14275
+ });
13357
14276
  return;
13358
14277
  }
13359
14278
  if (variant === "warning" && message.toLowerCase().includes("rate")) {
@@ -13403,7 +14322,8 @@ var createAntigravityPlugin = (providerId) => async ({ client, directory }) => {
13403
14322
  preferredHeaderStyle,
13404
14323
  config.pid_offset_enabled,
13405
14324
  config.soft_quota_threshold_percent,
13406
- softQuotaCacheTtlMs
14325
+ softQuotaCacheTtlMs,
14326
+ accountSessionIdentity
13407
14327
  );
13408
14328
  if (!account && allowQuotaFallback) {
13409
14329
  const alternateHeaderStyle = preferredHeaderStyle === "antigravity" ? "gemini-cli" : "antigravity";
@@ -13414,7 +14334,8 @@ var createAntigravityPlugin = (providerId) => async ({ client, directory }) => {
13414
14334
  alternateHeaderStyle,
13415
14335
  config.pid_offset_enabled,
13416
14336
  config.soft_quota_threshold_percent,
13417
- softQuotaCacheTtlMs
14337
+ softQuotaCacheTtlMs,
14338
+ accountSessionIdentity
13418
14339
  );
13419
14340
  if (account) {
13420
14341
  pushDebug(
@@ -13492,7 +14413,7 @@ var createAntigravityPlugin = (providerId) => async ({ client, directory }) => {
13492
14413
  pushDebug(`account-switch: ${previousAccountIndex} \u2192 ${account.index}, warmup=${needsCacheWarmup}`);
13493
14414
  }
13494
14415
  previousAccountIndex = account.index;
13495
- accountManager.recordSessionUsage(account.index);
14416
+ accountManager.recordSessionUsage(account.index, accountSessionIdentity);
13496
14417
  if (isDebugEnabled()) {
13497
14418
  logAccountContext("Selected", {
13498
14419
  index: account.index,
@@ -13542,7 +14463,7 @@ var createAntigravityPlugin = (providerId) => async ({ client, directory }) => {
13542
14463
  if (removed) {
13543
14464
  log10.warn("Removed revoked account from pool - reauthenticate via `opencode auth login`");
13544
14465
  try {
13545
- await accountManager.saveToDisk();
14466
+ await accountManager.saveToDiskReplace();
13546
14467
  } catch (persistError) {
13547
14468
  log10.error("Failed to persist revoked account removal", { error: String(persistError) });
13548
14469
  }
@@ -13785,7 +14706,9 @@ var createAntigravityPlugin = (providerId) => async ({ client, directory }) => {
13785
14706
  {
13786
14707
  claudeToolHardening: config.claude_tool_hardening,
13787
14708
  claudePromptAutoCaching: config.claude_prompt_auto_caching,
13788
- fingerprint: account.fingerprint
14709
+ fingerprint: account.fingerprint,
14710
+ agySession: agyRequestSession,
14711
+ agyRequestTimestamp: agyRequestScope.timestamp
13789
14712
  }
13790
14713
  );
13791
14714
  const originalUrl = toUrlString(input2);
@@ -14013,7 +14936,24 @@ var createAntigravityPlugin = (providerId) => async ({ client, directory }) => {
14013
14936
  resetAccountFailureState(account.index);
14014
14937
  if (response.status === 403) {
14015
14938
  const errorBodyText = await response.clone().text().catch(() => "");
14016
- const extracted = extractVerificationErrorDetails(errorBodyText);
14939
+ const extracted = extractAccountAccessErrorDetails(errorBodyText);
14940
+ if (extracted.accountIneligible) {
14941
+ const ineligibleReason = extracted.message ?? "Google marked this account as ineligible for Antigravity.";
14942
+ accountManager.markAccountIneligible(account.index, ineligibleReason);
14943
+ const label = account.email || `Account ${account.index + 1}`;
14944
+ if (accountManager.shouldShowAccountToast(account.index, 6e4)) {
14945
+ await showToast(
14946
+ `${label} is not eligible for Antigravity and has been disabled. Recheck it from opencode auth login > Verify accounts.`,
14947
+ "warning"
14948
+ );
14949
+ accountManager.markToastShown(account.index);
14950
+ }
14951
+ pushDebug(`account-ineligible: disabled account ${account.index}`);
14952
+ getHealthTracker().recordFailure(account.index);
14953
+ lastFailure = createFailureContext(response);
14954
+ shouldSwitchAccount = true;
14955
+ break;
14956
+ }
14017
14957
  if (extracted.validationRequired) {
14018
14958
  const verificationReason = extracted.message ?? "Google requires account verification.";
14019
14959
  const cooldownMs = 10 * 60 * 1e3;
@@ -14057,20 +14997,24 @@ var createAntigravityPlugin = (providerId) => async ({ client, directory }) => {
14057
14997
  family,
14058
14998
  model,
14059
14999
  proactiveThreshold,
14060
- softQuotaCacheTtlMs
15000
+ softQuotaCacheTtlMs,
15001
+ accountSessionIdentity
14061
15002
  )) {
14062
15003
  const rotated = accountManager.proactivelyRotateForFamily(
14063
15004
  family,
14064
15005
  model,
14065
15006
  headerStyle,
14066
15007
  config.soft_quota_threshold_percent,
14067
- softQuotaCacheTtlMs
15008
+ softQuotaCacheTtlMs,
15009
+ accountSessionIdentity
14068
15010
  );
14069
15011
  if (rotated) {
14070
15012
  const remaining = account.cachedQuota?.[resolveQuotaGroup(family, model)]?.remainingFraction;
14071
15013
  const remainingPct = remaining != null ? `${(remaining * 100).toFixed(1)}%` : "?";
14072
15014
  pushDebug(`[ProactiveRotation] account ${account.index} quota ${remainingPct} < ${proactiveThreshold}%, pre-switched to account ${rotated.index} for next request`);
14073
- pushDebug(`[ProactiveRotation] ${account.index} \u2192 ${rotated.index} (warm=${accountManager.wasUsedInSession(rotated.index)})`);
15015
+ pushDebug(
15016
+ `[ProactiveRotation] ${account.index} \u2192 ${rotated.index} (warm=${accountManager.wasUsedInSession(rotated.index, accountSessionIdentity)})`
15017
+ );
14074
15018
  }
14075
15019
  }
14076
15020
  }
@@ -14169,7 +15113,7 @@ Alternatively, you can:
14169
15113
  pushDebug(`[Quota] Total ${family} requests today (all accounts): ${totalToday}`);
14170
15114
  const cachedQuota = account.cachedQuota;
14171
15115
  if (cachedQuota) {
14172
- const quotaFamily = family === "claude" ? "claude" : "gemini-flash";
15116
+ const quotaFamily = resolveQuotaGroup(family, model);
14173
15117
  const groupQuota = cachedQuota[quotaFamily];
14174
15118
  if (groupQuota?.remainingFraction != null) {
14175
15119
  const pct = Math.round(groupQuota.remainingFraction * 100);
@@ -14324,7 +15268,9 @@ Alternatively, you can:
14324
15268
  const now = Date.now();
14325
15269
  const existingAccounts = existingStorage2.accounts.map((acc, idx) => {
14326
15270
  let status = "unknown";
14327
- if (acc.verificationRequired) {
15271
+ if (acc.accountIneligible) {
15272
+ status = "ineligible";
15273
+ } else if (acc.verificationRequired) {
14328
15274
  status = "verification-required";
14329
15275
  } else {
14330
15276
  const rateLimits = acc.rateLimitResetTimes;
@@ -14460,7 +15406,8 @@ Alternatively, you can:
14460
15406
  const groupEntries = [
14461
15407
  { name: "Claude", data: groups.claude },
14462
15408
  { name: "Gemini 3 Pro", data: groups["gemini-pro"] },
14463
- { name: "Gemini 3 Flash", data: groups["gemini-flash"] }
15409
+ { name: "Gemini 3 Flash", data: groups["gemini-flash"] },
15410
+ { name: "GPT-OSS", data: groups["gpt-oss"] }
14464
15411
  ].filter((g) => g.data);
14465
15412
  groupEntries.forEach((g, idx) => {
14466
15413
  const isLast = idx === groupEntries.length - 1;
@@ -14519,7 +15466,16 @@ ${formatAuthDoctorReport(report)}
14519
15466
  if (menuResult.toggleAccountIndex !== void 0) {
14520
15467
  const acc = existingStorage2.accounts[menuResult.toggleAccountIndex];
14521
15468
  if (acc) {
14522
- acc.enabled = acc.enabled === false;
15469
+ const shouldEnable = acc.enabled === false;
15470
+ if (shouldEnable && acc.accountIneligible) {
15471
+ console.log(
15472
+ `
15473
+ ${acc.email || `Account ${menuResult.toggleAccountIndex + 1}`} remains disabled. Use Verify accounts to recheck eligibility.
15474
+ `
15475
+ );
15476
+ continue;
15477
+ }
15478
+ acc.enabled = shouldEnable;
14523
15479
  await saveAccounts(existingStorage2);
14524
15480
  activeAccountManager?.setAccountEnabled(menuResult.toggleAccountIndex, acc.enabled);
14525
15481
  console.log(`
@@ -14541,6 +15497,7 @@ Checking verification status for ${existingStorage2.accounts.length} account(s).
14541
15497
  `);
14542
15498
  let okCount = 0;
14543
15499
  let blockedCount = 0;
15500
+ let ineligibleCount = 0;
14544
15501
  let errorCount = 0;
14545
15502
  let storageUpdated = false;
14546
15503
  const blockedResults = [];
@@ -14551,16 +15508,16 @@ Checking verification status for ${existingStorage2.accounts.length} account(s).
14551
15508
  process.stdout.write(`- [${i + 1}/${existingStorage2.accounts.length}] ${label2} ... `);
14552
15509
  const verification2 = await verifyAccountAccess(account2, client, providerId);
14553
15510
  if (verification2.status === "ok") {
14554
- const { changed, wasVerificationRequired } = clearStoredAccountVerificationRequired(account2, true);
15511
+ const { changed, wasAccessBlocked } = clearStoredAccountAccessBlocks(account2, true);
14555
15512
  if (changed) {
14556
15513
  storageUpdated = true;
14557
15514
  }
14558
- activeAccountManager?.clearAccountVerificationRequired(i, wasVerificationRequired);
15515
+ activeAccountManager?.clearAccountAccessBlocks(i, wasAccessBlocked);
14559
15516
  okCount += 1;
14560
15517
  console.log("ok");
14561
15518
  continue;
14562
15519
  }
14563
- if (verification2.status === "blocked") {
15520
+ if (verification2.status === "verification-required") {
14564
15521
  const changed = markStoredAccountVerificationRequired(
14565
15522
  account2,
14566
15523
  verification2.message,
@@ -14580,14 +15537,26 @@ Checking verification status for ${existingStorage2.accounts.length} account(s).
14580
15537
  });
14581
15538
  continue;
14582
15539
  }
15540
+ if (verification2.status === "ineligible") {
15541
+ const changed = markStoredAccountIneligible(account2, verification2.message);
15542
+ if (changed) {
15543
+ storageUpdated = true;
15544
+ }
15545
+ activeAccountManager?.markAccountIneligible(i, verification2.message);
15546
+ ineligibleCount += 1;
15547
+ console.log("ineligible");
15548
+ continue;
15549
+ }
14583
15550
  errorCount += 1;
14584
15551
  console.log(`error (${verification2.message})`);
14585
15552
  }
14586
15553
  if (storageUpdated) {
14587
15554
  await saveAccounts(existingStorage2);
14588
15555
  }
14589
- console.log(`
14590
- Verification summary: ${okCount} ready, ${blockedCount} need verification, ${errorCount} errors.`);
15556
+ console.log(
15557
+ `
15558
+ Verification summary: ${okCount} ready, ${blockedCount} need verification, ${ineligibleCount} ineligible, ${errorCount} errors.`
15559
+ );
14591
15560
  if (blockedResults.length > 0) {
14592
15561
  console.log("\nAccounts needing verification:");
14593
15562
  for (const result of blockedResults) {
@@ -14627,12 +15596,12 @@ Checking verification status for ${label}...
14627
15596
  `);
14628
15597
  const verification = await verifyAccountAccess(account, client, providerId);
14629
15598
  if (verification.status === "ok") {
14630
- const { changed, wasVerificationRequired } = clearStoredAccountVerificationRequired(account, true);
15599
+ const { changed, wasAccessBlocked } = clearStoredAccountAccessBlocks(account, true);
14631
15600
  if (changed) {
14632
15601
  await saveAccounts(existingStorage2);
14633
15602
  }
14634
- activeAccountManager?.clearAccountVerificationRequired(verifyAccountIndex, wasVerificationRequired);
14635
- if (wasVerificationRequired) {
15603
+ activeAccountManager?.clearAccountAccessBlocks(verifyAccountIndex, wasAccessBlocked);
15604
+ if (wasAccessBlocked) {
14636
15605
  console.log(`\u2713 ${label} is ready for requests and has been re-enabled.
14637
15606
  `);
14638
15607
  } else {
@@ -14641,7 +15610,7 @@ Checking verification status for ${label}...
14641
15610
  }
14642
15611
  continue;
14643
15612
  }
14644
- if (verification.status === "blocked") {
15613
+ if (verification.status === "verification-required") {
14645
15614
  const changed = markStoredAccountVerificationRequired(
14646
15615
  account,
14647
15616
  verification.message,
@@ -14679,6 +15648,20 @@ ${verifyUrl}
14679
15648
  }
14680
15649
  continue;
14681
15650
  }
15651
+ if (verification.status === "ineligible") {
15652
+ const changed = markStoredAccountIneligible(account, verification.message);
15653
+ if (changed) {
15654
+ await saveAccounts(existingStorage2);
15655
+ }
15656
+ activeAccountManager?.markAccountIneligible(
15657
+ verifyAccountIndex,
15658
+ verification.message
15659
+ );
15660
+ console.log(`\u26A0 ${label} is not eligible for Antigravity and has been disabled.`);
15661
+ console.log(`${verification.message}
15662
+ `);
15663
+ continue;
15664
+ }
14682
15665
  console.log(`\u2717 ${label}: ${verification.message}
14683
15666
  `);
14684
15667
  continue;