opencode-antigravity-auth-tweaked 1.8.7 → 1.9.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.
@@ -792,7 +792,7 @@ function extractRateLimitBodyInfo(body, rawBody) {
792
792
  let directMessage = undefined;
793
793
  // Pattern 1: { error: { message, details, ... } } - Google API format
794
794
  const errorProp = body.error;
795
- if (errorProp) {
795
+ if (errorProp && typeof errorProp === "object") {
796
796
  error = errorProp;
797
797
  }
798
798
  // Pattern 2: { message: string } - Direct message format
@@ -864,6 +864,10 @@ function extractRateLimitBodyInfo(body, rawBody) {
864
864
  async function extractRetryInfoFromBody(response) {
865
865
  try {
866
866
  const text = await response.clone().text();
867
+ // Handle empty response
868
+ if (!text || text.trim() === "") {
869
+ return { retryDelayMs: null, rawBody: text };
870
+ }
867
871
  try {
868
872
  let parsed = JSON.parse(text);
869
873
  // Handle case where response is wrapped in an array (SSE stream or batched response)
@@ -874,15 +878,33 @@ async function extractRetryInfoFromBody(response) {
874
878
  // Always include raw body for fallback display
875
879
  return { ...info, rawBody: text };
876
880
  }
877
- catch {
881
+ catch (parseError) {
878
882
  // JSON parsing failed, but text might contain a plain error message
883
+ // Return the raw text as the message
879
884
  return { retryDelayMs: null, message: text.trim() || undefined, rawBody: text };
880
885
  }
881
886
  }
882
- catch {
887
+ catch (readError) {
888
+ // Failed to read response body at all
883
889
  return { retryDelayMs: null };
884
890
  }
885
891
  }
892
+ /**
893
+ * Extract error message from raw response body when structured parsing fails.
894
+ * Uses regex to find message field in JSON-like content.
895
+ */
896
+ function extractMessageFromRawBody(rawBody) {
897
+ if (!rawBody || rawBody.trim() === "")
898
+ return undefined;
899
+ // Try to find "message": "..." pattern in the raw body
900
+ // This regex handles escaped quotes within the message value
901
+ const messageMatch = rawBody.match(/"message"\s*:\s*"((?:[^"\\]|\\.)*)"/i);
902
+ if (messageMatch && messageMatch[1]) {
903
+ // Unescape JSON string
904
+ return messageMatch[1].replace(/\\"/g, '"').replace(/\\\\/g, '\\');
905
+ }
906
+ return undefined;
907
+ }
886
908
  function formatWaitTime(ms) {
887
909
  if (ms < 1000)
888
910
  return `${ms}ms`;
@@ -1497,7 +1519,12 @@ export const createAntigravityPlugin = (providerId) => async ({ client, director
1497
1519
  });
1498
1520
  try {
1499
1521
  pushDebug("thinking-warmup: start");
1500
- const warmupResponse = await fetch(warmupUrl, warmupInit);
1522
+ // Add 30s timeout for warmup to prevent hanging the main request
1523
+ const warmupTimeoutSignal = AbortSignal.timeout(30000);
1524
+ const combinedWarmupSignal = warmupInit.signal
1525
+ ? AbortSignal.any([warmupInit.signal, warmupTimeoutSignal])
1526
+ : warmupTimeoutSignal;
1527
+ const warmupResponse = await fetch(warmupUrl, { ...warmupInit, signal: combinedWarmupSignal });
1501
1528
  const transformed = await transformAntigravityResponse(warmupResponse, true, warmupDebugContext, prepared.requestedModel, projectId, warmupUrl, prepared.effectiveModel, prepared.sessionId);
1502
1529
  await transformed.text();
1503
1530
  markWarmupSuccess(prepared.sessionId);
@@ -1597,6 +1624,10 @@ export const createAntigravityPlugin = (providerId) => async ({ client, director
1597
1624
  claudePromptAutoCaching: config.claude_prompt_auto_caching,
1598
1625
  fingerprint: account.fingerprint,
1599
1626
  });
1627
+ // Show recovery message if present (e.g. session fixation)
1628
+ if (prepared.thinkingRecoveryMessage) {
1629
+ await showToast(prepared.thinkingRecoveryMessage, "info");
1630
+ }
1600
1631
  const originalUrl = toUrlString(input);
1601
1632
  const resolvedUrl = toUrlString(prepared.request);
1602
1633
  pushDebug(`endpoint=${currentEndpoint}`);
@@ -1637,9 +1668,43 @@ export const createAntigravityPlugin = (providerId) => async ({ client, director
1637
1668
  }
1638
1669
  // Increment active requests for concurrency tracking
1639
1670
  getConcurrencyTracker().increment(account.index);
1671
+ // Calculate timeout for this request
1672
+ const timeoutMs = (config.request_timeout_seconds ?? 120) * 1000;
1673
+ const timeoutSignal = AbortSignal.timeout(timeoutMs);
1674
+ const combinedSignal = prepared.init.signal
1675
+ ? AbortSignal.any([prepared.init.signal, timeoutSignal])
1676
+ : timeoutSignal;
1640
1677
  let response;
1641
1678
  try {
1642
- response = await fetch(prepared.request, prepared.init);
1679
+ response = await fetch(prepared.request, { ...prepared.init, signal: combinedSignal });
1680
+ }
1681
+ catch (error) {
1682
+ // Special handling for timeouts to provide better user feedback
1683
+ if (error instanceof Error && (error.name === "TimeoutError" || (error.name === "AbortError" && timeoutSignal.aborted))) {
1684
+ const timeoutMsg = `Request timed out after ${config.request_timeout_seconds}s`;
1685
+ pushDebug(timeoutMsg);
1686
+ await showToast(`${timeoutMsg}. Try a different model or endpoint.`, "error");
1687
+ // Treat timeout as a 504 Gateway Timeout for the purpose of fallback logic
1688
+ lastFailure = {
1689
+ response: new Response(JSON.stringify({ error: { message: timeoutMsg } }), {
1690
+ status: 504,
1691
+ statusText: "Gateway Timeout",
1692
+ headers: { "Content-Type": "application/json" }
1693
+ }),
1694
+ streaming: prepared.streaming,
1695
+ debugContext,
1696
+ requestedModel: prepared.requestedModel,
1697
+ projectId: projectContext.effectiveProjectId,
1698
+ endpoint: prepared.endpoint,
1699
+ effectiveModel: prepared.effectiveModel,
1700
+ sessionId: prepared.sessionId,
1701
+ toolDebugMissing: prepared.toolDebugMissing,
1702
+ toolDebugSummary: prepared.toolDebugSummary,
1703
+ toolDebugPayload: prepared.toolDebugPayload,
1704
+ };
1705
+ continue; // Try next endpoint or account
1706
+ }
1707
+ throw error;
1643
1708
  }
1644
1709
  finally {
1645
1710
  // Decrement active requests - MUST happen even on network errors
@@ -1662,8 +1727,13 @@ export const createAntigravityPlugin = (providerId) => async ({ client, director
1662
1727
  const headerRetryMs = retryAfterMsFromResponse(response, defaultRetryMs);
1663
1728
  const bodyInfo = await extractRetryInfoFromBody(response);
1664
1729
  const serverRetryMs = bodyInfo.retryDelayMs ?? headerRetryMs;
1730
+ // Extract message from raw body if structured extraction failed
1731
+ // This ensures we can still classify the error correctly
1732
+ const messageForClassification = bodyInfo.message
1733
+ || extractMessageFromRawBody(bodyInfo.rawBody || "")
1734
+ || undefined;
1665
1735
  // [Enhanced Parsing] Pass status to handling logic
1666
- const rateLimitReason = parseRateLimitReason(bodyInfo.reason, bodyInfo.message, response.status);
1736
+ const rateLimitReason = parseRateLimitReason(bodyInfo.reason, messageForClassification, response.status);
1667
1737
  // Calculate backoff/wait time
1668
1738
  const quotaKey = headerStyleToQuotaKey(headerStyle, family);
1669
1739
  const { attempt, delayMs } = getRateLimitBackoff(account.index, quotaKey, serverRetryMs);
@@ -1681,17 +1751,23 @@ export const createAntigravityPlugin = (providerId) => async ({ client, director
1681
1751
  await logResponseBody(debugContext, response, response.status);
1682
1752
  getHealthTracker().recordRateLimit(account.index);
1683
1753
  // Define display reason for user feedback
1684
- // Never show "UNKNOWN" - show the full response body or "NO_BODY" instead
1754
+ // Never show "UNKNOWN" - show the actual server message or "NO_BODY" instead
1685
1755
  let displayReason;
1686
1756
  if (bodyInfo.message && bodyInfo.message.trim()) {
1687
1757
  displayReason = bodyInfo.message;
1688
1758
  }
1689
- else if (bodyInfo.rawBody && bodyInfo.rawBody.trim()) {
1690
- // Show the full raw response body when parsing failed or no structured message found
1691
- displayReason = bodyInfo.rawBody;
1692
- }
1693
1759
  else {
1694
- displayReason = "NO_BODY";
1760
+ // Try to extract a message from raw body as a last resort
1761
+ const extractedMessage = extractMessageFromRawBody(bodyInfo.rawBody || "");
1762
+ if (extractedMessage && extractedMessage.trim()) {
1763
+ displayReason = extractedMessage;
1764
+ }
1765
+ else if (bodyInfo.rawBody && bodyInfo.rawBody.trim()) {
1766
+ displayReason = bodyInfo.rawBody;
1767
+ }
1768
+ else {
1769
+ displayReason = "NO_BODY";
1770
+ }
1695
1771
  }
1696
1772
  // ENHANCED RATE LIMIT HANDLING:
1697
1773
  // Actually wait for the ratelimited amount of time (plus 1-3s random jitter)