@anthonyhaussman/opencode-agy-auth 1.1.13 → 1.1.14-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -516,6 +516,69 @@ function normalizeErrorEnvelope(parsed) {
516
516
  }
517
517
  return isObject(parsed) ? parsed : null;
518
518
  }
519
+ var MAX_QUOTA_RESET_WAIT_MS = 72e5;
520
+ function findResetTimeForModel(summary, model) {
521
+ if (!summary) return null;
522
+ const normalize2 = (str) => str.toLowerCase().replace(/[^a-z0-9]/g, "");
523
+ const modelNorm = model ? normalize2(model) : "";
524
+ let targetGroups = summary.groups ?? [];
525
+ if (modelNorm && targetGroups.length > 0) {
526
+ const matched = targetGroups.filter((g) => {
527
+ const gName = normalize2(g.displayName ?? "");
528
+ return gName.includes(modelNorm) || modelNorm.includes(gName);
529
+ });
530
+ if (matched.length > 0) {
531
+ targetGroups = matched;
532
+ }
533
+ }
534
+ const allBuckets = [];
535
+ for (const group of targetGroups) {
536
+ if (group.buckets) {
537
+ allBuckets.push(...group.buckets);
538
+ }
539
+ }
540
+ if (allBuckets.length === 0 && summary.buckets) {
541
+ allBuckets.push(...summary.buckets);
542
+ }
543
+ const now = Date.now();
544
+ const validResetBuckets = allBuckets.filter((b) => {
545
+ if (!b.resetTime) return false;
546
+ const t = new Date(b.resetTime).getTime();
547
+ return !Number.isNaN(t) && t > now;
548
+ });
549
+ if (validResetBuckets.length === 0) {
550
+ return null;
551
+ }
552
+ const exhaustedFiveHour = validResetBuckets.find(
553
+ (b) => (b.remainingFraction === 0 || b.disabled) && b.window?.toUpperCase() === "FIVE_HOUR"
554
+ );
555
+ if (exhaustedFiveHour?.resetTime) return exhaustedFiveHour.resetTime;
556
+ const exhaustedAny = validResetBuckets.find((b) => b.remainingFraction === 0 || b.disabled);
557
+ if (exhaustedAny?.resetTime) return exhaustedAny.resetTime;
558
+ const fiveHour = validResetBuckets.find((b) => b.window?.toUpperCase() === "FIVE_HOUR");
559
+ if (fiveHour?.resetTime) return fiveHour.resetTime;
560
+ validResetBuckets.sort(
561
+ (a, b) => new Date(a.resetTime).getTime() - new Date(b.resetTime).getTime()
562
+ );
563
+ return validResetBuckets[0]?.resetTime ?? null;
564
+ }
565
+ async function resolveQuotaResetDelay(accessToken, projectId, model, userAgentModel) {
566
+ try {
567
+ const summary = await retrieveUserQuotaSummary(accessToken, projectId, userAgentModel);
568
+ if (!summary) return null;
569
+ const resetTime = findResetTimeForModel(summary, model);
570
+ if (!resetTime) return null;
571
+ const resetTimestamp = new Date(resetTime).getTime();
572
+ if (Number.isNaN(resetTimestamp)) return null;
573
+ const waitMs = resetTimestamp - Date.now() + 1e3;
574
+ if (waitMs <= 0 || waitMs > MAX_QUOTA_RESET_WAIT_MS) {
575
+ return null;
576
+ }
577
+ return { waitMs, resetTime };
578
+ } catch {
579
+ return null;
580
+ }
581
+ }
519
582
 
520
583
  // src/sdk/retry/helpers.ts
521
584
  var DEFAULT_MAX_ATTEMPTS = 3;
@@ -812,6 +875,7 @@ async function fetchWithRetry(input, init) {
812
875
  const throttleKey = buildRetryThrottleKey(input, retryInit);
813
876
  await waitForRetryCooldown(throttleKey, retryInit.signal);
814
877
  let attempt = 1;
878
+ let hasRetriedQuotaReset = false;
815
879
  const url2 = readRequestUrl(input);
816
880
  while (attempt <= DEFAULT_MAX_ATTEMPTS) {
817
881
  let response;
@@ -837,6 +901,25 @@ async function fetchWithRetry(input, init) {
837
901
  if (quotaContext.reason === "MODEL_CAPACITY_EXHAUSTED") {
838
902
  const cooldownMs = quotaContext.retryDelayMs ?? MODEL_CAPACITY_COOLDOWN_MS;
839
903
  setRetryCooldown(throttleKey, cooldownMs);
904
+ return response;
905
+ }
906
+ if (quotaContext.reason === "QUOTA_EXHAUSTED" && !hasRetriedQuotaReset && !retryInit.signal?.aborted) {
907
+ const body = typeof retryInit.body === "string" ? safeParseBody(retryInit.body) : null;
908
+ const project = readString(body?.project);
909
+ const model = readString(body?.model);
910
+ const token = extractAuthToken(retryInit.headers);
911
+ if (token && project) {
912
+ const resetInfo = await resolveQuotaResetDelay(token, project, model);
913
+ if (resetInfo && resetInfo.waitMs > 0 && resetInfo.waitMs <= MAX_QUOTA_RESET_WAIT_MS) {
914
+ hasRetriedQuotaReset = true;
915
+ setRetryCooldown(throttleKey, resetInfo.waitMs);
916
+ await wait(resetInfo.waitMs);
917
+ if (retryInit.signal?.aborted) {
918
+ return response;
919
+ }
920
+ continue;
921
+ }
922
+ }
840
923
  }
841
924
  return response;
842
925
  }
@@ -922,6 +1005,14 @@ function safeParseBody(body) {
922
1005
  function readString(value) {
923
1006
  return typeof value === "string" && value.trim() ? value : void 0;
924
1007
  }
1008
+ function extractAuthToken(headers) {
1009
+ if (!headers) return void 0;
1010
+ const h = new Headers(headers);
1011
+ const auth = h.get("authorization") || h.get("Authorization");
1012
+ if (!auth) return void 0;
1013
+ const match = auth.match(/^Bearer\s+(.+)$/i);
1014
+ return match?.[1]?.trim() || auth.trim();
1015
+ }
925
1016
 
926
1017
  // src/sdk/terminal-hyperlink.ts
927
1018
  var OSC8_OPEN = "\x1B]8;;";
@@ -17756,7 +17847,7 @@ function normalizeRequestPayloadIdentifiers(payload) {
17756
17847
  }
17757
17848
 
17758
17849
  // src/sdk/request/openai.ts
17759
- function transformOpenAIToolCalls(requestPayload) {
17850
+ function transformOpenAIToolCalls(requestPayload, toolMapper) {
17760
17851
  const messages = requestPayload.messages;
17761
17852
  if (!messages || !Array.isArray(messages)) {
17762
17853
  return;
@@ -17782,10 +17873,11 @@ function transformOpenAIToolCalls(requestPayload) {
17782
17873
  if (!fn || typeof fn !== "object") {
17783
17874
  continue;
17784
17875
  }
17785
- const name = fn.name;
17876
+ const rawName = fn.name ?? "";
17877
+ const name = toolMapper ? toolMapper.toGemini(rawName) : rawName;
17786
17878
  const args = parseJsonObject(fn.arguments);
17787
17879
  const functionCallPart = {
17788
- name: name ?? "",
17880
+ name,
17789
17881
  args
17790
17882
  };
17791
17883
  if (typeof toolCall.id === "string" && toolCall.id.length > 0) {
@@ -17845,6 +17937,165 @@ function parseJsonObject(value) {
17845
17937
  }
17846
17938
  }
17847
17939
 
17940
+ // src/sdk/request/tool-mapper.ts
17941
+ function sanitizeToolName(name) {
17942
+ if (!name || typeof name !== "string") {
17943
+ return "unnamed_tool";
17944
+ }
17945
+ let sanitized = name.replace(/[^a-zA-Z0-9_]/g, "_");
17946
+ if (!/^[a-zA-Z_]/.test(sanitized)) {
17947
+ sanitized = `_${sanitized}`;
17948
+ }
17949
+ return sanitized;
17950
+ }
17951
+ var ToolMapper = class {
17952
+ originalToSanitized = /* @__PURE__ */ new Map();
17953
+ sanitizedToOriginal = /* @__PURE__ */ new Map();
17954
+ /**
17955
+ * Register a tool name and get its Gemini-compliant sanitized name.
17956
+ * Handles naming collisions by appending a numeric suffix if needed.
17957
+ */
17958
+ register(originalName) {
17959
+ if (!originalName || typeof originalName !== "string") {
17960
+ return originalName;
17961
+ }
17962
+ const existing = this.originalToSanitized.get(originalName);
17963
+ if (existing) {
17964
+ return existing;
17965
+ }
17966
+ const baseSanitized = sanitizeToolName(originalName);
17967
+ let sanitized = baseSanitized;
17968
+ let counter = 1;
17969
+ while (this.sanitizedToOriginal.has(sanitized) && this.sanitizedToOriginal.get(sanitized) !== originalName) {
17970
+ sanitized = `${baseSanitized}_${counter++}`;
17971
+ }
17972
+ this.originalToSanitized.set(originalName, sanitized);
17973
+ this.sanitizedToOriginal.set(sanitized, originalName);
17974
+ return sanitized;
17975
+ }
17976
+ /**
17977
+ * Map an original tool name to sanitized Gemini name.
17978
+ * If not already registered, registers it on the fly.
17979
+ */
17980
+ toGemini(originalName) {
17981
+ if (!originalName || typeof originalName !== "string") {
17982
+ return originalName;
17983
+ }
17984
+ const sanitized = this.originalToSanitized.get(originalName);
17985
+ if (sanitized) {
17986
+ return sanitized;
17987
+ }
17988
+ return this.register(originalName);
17989
+ }
17990
+ /**
17991
+ * Restore a sanitized Gemini tool name back to the original client tool name.
17992
+ */
17993
+ fromGemini(sanitizedName) {
17994
+ if (!sanitizedName || typeof sanitizedName !== "string") {
17995
+ return sanitizedName;
17996
+ }
17997
+ return this.sanitizedToOriginal.get(sanitizedName) ?? sanitizedName;
17998
+ }
17999
+ /**
18000
+ * Register tools from Gemini `tools[].functionDeclarations` array.
18001
+ */
18002
+ registerFromFunctionDeclarations(tools) {
18003
+ if (!Array.isArray(tools)) return;
18004
+ for (const tool2 of tools) {
18005
+ if (tool2 && Array.isArray(tool2.functionDeclarations)) {
18006
+ for (const fn of tool2.functionDeclarations) {
18007
+ if (fn && typeof fn.name === "string") {
18008
+ this.register(fn.name);
18009
+ }
18010
+ }
18011
+ }
18012
+ }
18013
+ }
18014
+ /**
18015
+ * Register tools from OpenAI format `tools[].function.name`.
18016
+ */
18017
+ registerFromOpenAITools(tools) {
18018
+ if (!Array.isArray(tools)) return;
18019
+ for (const tool2 of tools) {
18020
+ if (tool2 && typeof tool2 === "object") {
18021
+ const fn = tool2.function;
18022
+ if (fn && typeof fn.name === "string") {
18023
+ this.register(fn.name);
18024
+ }
18025
+ }
18026
+ }
18027
+ }
18028
+ /**
18029
+ * Scan contents/messages to register any previously used tool names.
18030
+ */
18031
+ registerFromContents(contents) {
18032
+ if (!Array.isArray(contents)) return;
18033
+ for (const content of contents) {
18034
+ if (!content || typeof content !== "object") continue;
18035
+ const parts = content.parts;
18036
+ if (Array.isArray(parts)) {
18037
+ for (const part of parts) {
18038
+ if (!part || typeof part !== "object") continue;
18039
+ const p = part;
18040
+ if (p.functionCall && typeof p.functionCall.name === "string") {
18041
+ this.register(p.functionCall.name);
18042
+ }
18043
+ if (p.functionResponse && typeof p.functionResponse.name === "string") {
18044
+ this.register(p.functionResponse.name);
18045
+ }
18046
+ }
18047
+ }
18048
+ }
18049
+ }
18050
+ };
18051
+ var sessionMappers = /* @__PURE__ */ new Map();
18052
+ var MAX_SESSION_AGE_MS = 24 * 60 * 60 * 1e3;
18053
+ function getToolMapper(sessionId) {
18054
+ if (!sessionId) {
18055
+ return new ToolMapper();
18056
+ }
18057
+ const now = Date.now();
18058
+ const existing = sessionMappers.get(sessionId);
18059
+ if (existing && now - existing.updatedAt < MAX_SESSION_AGE_MS) {
18060
+ existing.updatedAt = now;
18061
+ return existing.mapper;
18062
+ }
18063
+ if (sessionMappers.size > 1e3) {
18064
+ for (const [key, value] of sessionMappers.entries()) {
18065
+ if (now - value.updatedAt >= MAX_SESSION_AGE_MS) {
18066
+ sessionMappers.delete(key);
18067
+ }
18068
+ }
18069
+ }
18070
+ const mapper = new ToolMapper();
18071
+ sessionMappers.set(sessionId, { mapper, updatedAt: now });
18072
+ return mapper;
18073
+ }
18074
+ function restoreToolNamesInResponse(body, toolMapper) {
18075
+ if (!body || typeof body !== "object") return;
18076
+ const b = body;
18077
+ const target = b.response && typeof b.response === "object" ? b.response : b;
18078
+ const candidates = target.candidates;
18079
+ if (Array.isArray(candidates)) {
18080
+ for (const cand of candidates) {
18081
+ if (!cand || typeof cand !== "object") continue;
18082
+ const content = cand.content;
18083
+ if (!content || typeof content !== "object") continue;
18084
+ const parts = content.parts;
18085
+ if (Array.isArray(parts)) {
18086
+ for (const part of parts) {
18087
+ if (!part || typeof part !== "object") continue;
18088
+ const p = part;
18089
+ if (p.functionCall && typeof p.functionCall.name === "string") {
18090
+ const fnCall = p.functionCall;
18091
+ fnCall.name = toolMapper.fromGemini(fnCall.name);
18092
+ }
18093
+ }
18094
+ }
18095
+ }
18096
+ }
18097
+ }
18098
+
17848
18099
  // src/sdk/request/prepare.ts
17849
18100
  var STREAM_ACTION = "streamGenerateContent";
17850
18101
  function prepareAgyRequest(input, init, accessToken, projectId, thinkingConfigDefaults) {
@@ -17933,8 +18184,11 @@ function transformRequestBody(body, projectId, effectiveModel, requestedModel, t
17933
18184
  wrappedBody2.userAgent = wrappedBody2.userAgent || "antigravity";
17934
18185
  }
17935
18186
  const { userPromptId: userPromptId2, sessionId: sessionId2, requestId: requestId2 } = normalizeWrappedIdentifiers(wrappedBody2);
18187
+ const toolMapper2 = getToolMapper(sessionId2);
17936
18188
  const requestPayloadInside = wrappedBody2.request;
17937
18189
  if (requestPayloadInside) {
18190
+ toolMapper2.registerFromFunctionDeclarations(requestPayloadInside.tools);
18191
+ toolMapper2.registerFromContents(requestPayloadInside.contents);
17938
18192
  normalizeThinking(
17939
18193
  requestPayloadInside,
17940
18194
  resolveDefaultThinkingConfig(thinkingConfigDefaults, requestedModel, effectiveModel),
@@ -17952,10 +18206,14 @@ function transformRequestBody(body, projectId, effectiveModel, requestedModel, t
17952
18206
  };
17953
18207
  }
17954
18208
  if (requestPayloadInside && Array.isArray(requestPayloadInside.tools)) {
17955
- normalizeToolSchemaTypes(requestPayloadInside.tools);
18209
+ normalizeToolSchemaTypes(requestPayloadInside.tools, toolMapper2);
18210
+ }
18211
+ if (requestPayloadInside) {
18212
+ normalizeToolConfig(requestPayloadInside, toolMapper2);
17956
18213
  }
17957
18214
  if (requestPayloadInside && Array.isArray(requestPayloadInside.contents)) {
17958
18215
  let contents2 = requestPayloadInside.contents;
18216
+ normalizeToolNamesInContents(contents2, toolMapper2);
17959
18217
  injectMissingToolCallIds(contents2);
17960
18218
  fixOrphanedFunctionResponses(contents2);
17961
18219
  const tracker = getTurnStateTracker();
@@ -17975,10 +18233,16 @@ function transformRequestBody(body, projectId, effectiveModel, requestedModel, t
17975
18233
  return { body: JSON.stringify(wrappedBody2), userPromptId: userPromptId2, sessionId: sessionId2 };
17976
18234
  }
17977
18235
  const requestPayload = { ...parsedBody };
18236
+ const { userPromptId, sessionId, requestId } = normalizeRequestPayloadIdentifiers(requestPayload);
18237
+ const toolMapper = getToolMapper(sessionId);
18238
+ toolMapper.registerFromOpenAITools(requestPayload.tools);
18239
+ toolMapper.registerFromFunctionDeclarations(requestPayload.tools);
18240
+ toolMapper.registerFromContents(requestPayload.contents);
17978
18241
  if (Array.isArray(requestPayload.tools)) {
17979
- normalizeToolSchemaTypes(requestPayload.tools);
18242
+ normalizeToolSchemaTypes(requestPayload.tools, toolMapper);
17980
18243
  }
17981
- transformOpenAIToolCalls(requestPayload);
18244
+ normalizeToolConfig(requestPayload, toolMapper);
18245
+ transformOpenAIToolCalls(requestPayload, toolMapper);
17982
18246
  addThoughtSignaturesToFunctionCalls(requestPayload);
17983
18247
  normalizeThinking(
17984
18248
  requestPayload,
@@ -17987,9 +18251,9 @@ function transformRequestBody(body, projectId, effectiveModel, requestedModel, t
17987
18251
  );
17988
18252
  normalizeSystemInstruction(requestPayload);
17989
18253
  normalizeCachedContent(requestPayload);
17990
- const { userPromptId, sessionId, requestId } = normalizeRequestPayloadIdentifiers(requestPayload);
17991
18254
  let contents = requestPayload.contents;
17992
18255
  if (Array.isArray(contents)) {
18256
+ normalizeToolNamesInContents(contents, toolMapper);
17993
18257
  injectMissingToolCallIds(contents);
17994
18258
  fixOrphanedFunctionResponses(contents);
17995
18259
  const tracker = getTurnStateTracker();
@@ -18103,7 +18367,7 @@ function mergeThinkingConfigs(...configs) {
18103
18367
  function isRecord2(value) {
18104
18368
  return !!value && typeof value === "object" && !Array.isArray(value);
18105
18369
  }
18106
- function normalizeToolSchemaTypes(tools) {
18370
+ function normalizeToolSchemaTypes(tools, toolMapper) {
18107
18371
  if (!Array.isArray(tools)) return;
18108
18372
  const validSchemaKeys = /* @__PURE__ */ new Set([
18109
18373
  "type",
@@ -18154,7 +18418,7 @@ function normalizeToolSchemaTypes(tools) {
18154
18418
  if (tool2 && Array.isArray(tool2.functionDeclarations)) {
18155
18419
  for (const fn of tool2.functionDeclarations) {
18156
18420
  if (fn && typeof fn.name === "string") {
18157
- fn.name = fn.name.replace(/[^a-zA-Z0-9_]/g, "_");
18421
+ fn.name = toolMapper ? toolMapper.toGemini(fn.name) : sanitizeToolName(fn.name);
18158
18422
  }
18159
18423
  if (fn) {
18160
18424
  if (!fn.parameters) {
@@ -18166,6 +18430,37 @@ function normalizeToolSchemaTypes(tools) {
18166
18430
  }
18167
18431
  }
18168
18432
  }
18433
+ function normalizeToolConfig(requestPayload, toolMapper) {
18434
+ const toolConfig = requestPayload.toolConfig ?? requestPayload.tool_config;
18435
+ if (!toolConfig || typeof toolConfig !== "object") return;
18436
+ const fnCallingConfig = toolConfig.functionCallingConfig ?? toolConfig.function_calling_config;
18437
+ if (!fnCallingConfig || typeof fnCallingConfig !== "object") return;
18438
+ const allowedNames = fnCallingConfig.allowedFunctionNames ?? fnCallingConfig.allowed_function_names;
18439
+ if (Array.isArray(allowedNames)) {
18440
+ const mapped = allowedNames.map((name) => typeof name === "string" ? toolMapper.toGemini(name) : name);
18441
+ if (fnCallingConfig.allowedFunctionNames) {
18442
+ fnCallingConfig.allowedFunctionNames = mapped;
18443
+ }
18444
+ if (fnCallingConfig.allowed_function_names) {
18445
+ fnCallingConfig.allowed_function_names = mapped;
18446
+ }
18447
+ }
18448
+ }
18449
+ function normalizeToolNamesInContents(contents, toolMapper) {
18450
+ if (!Array.isArray(contents)) return;
18451
+ for (const msg of contents) {
18452
+ if (!msg || typeof msg !== "object" || !Array.isArray(msg.parts)) continue;
18453
+ for (const part of msg.parts) {
18454
+ if (!part || typeof part !== "object") continue;
18455
+ if (part.functionCall && typeof part.functionCall.name === "string") {
18456
+ part.functionCall.name = toolMapper.toGemini(part.functionCall.name);
18457
+ }
18458
+ if (part.functionResponse && typeof part.functionResponse.name === "string") {
18459
+ part.functionResponse.name = toolMapper.toGemini(part.functionResponse.name);
18460
+ }
18461
+ }
18462
+ }
18463
+ }
18169
18464
  function normalizeSystemInstruction(requestPayload) {
18170
18465
  if ("system_instruction" in requestPayload) {
18171
18466
  requestPayload.systemInstruction = requestPayload.system_instruction;
@@ -18249,7 +18544,7 @@ function applyLatestSignature(contents, latestSig) {
18249
18544
  if (content && typeof content === "object" && Array.isArray(content.parts)) {
18250
18545
  for (const part of content.parts) {
18251
18546
  if (part && typeof part === "object" && part.functionCall) {
18252
- allFunctionCalls.push(part);
18547
+ allFunctionCalls.push(part.functionCall);
18253
18548
  }
18254
18549
  }
18255
18550
  }
@@ -18342,6 +18637,10 @@ async function transformAgyResponse(response, streaming, _ignoredDebugContext, r
18342
18637
  const previewPatched = parsed ? rewriteGeminiPreviewAccessError(enhanced?.body ?? parsed, response.status, requestedModel) : null;
18343
18638
  const effectiveBodyRaw = previewPatched ?? enhanced?.body ?? parsed ?? void 0;
18344
18639
  const effectiveBody = effectiveBodyRaw && typeof effectiveBodyRaw === "object" ? injectResponseIdFromTrace(effectiveBodyRaw) : effectiveBodyRaw;
18640
+ if (effectiveBody) {
18641
+ const toolMapper = getToolMapper(sessionId);
18642
+ restoreToolNamesInResponse(effectiveBody, toolMapper);
18643
+ }
18345
18644
  attachUsageHeaders(headers, effectiveBody);
18346
18645
  if (!parsed) {
18347
18646
  return new Response(text, init);
@@ -18396,6 +18695,8 @@ function transformStreamingPayloadStream(stream, sessionId, chatLogger) {
18396
18695
  },
18397
18696
  transformThinkingParts: (response) => {
18398
18697
  if (response && typeof response === "object") {
18698
+ const toolMapper = getToolMapper(sessionId);
18699
+ restoreToolNamesInResponse(response, toolMapper);
18399
18700
  return injectResponseIdFromTrace(response);
18400
18701
  }
18401
18702
  return response;
@@ -19105,7 +19406,7 @@ function resolveThinkingConfigDefaults(provider) {
19105
19406
  const providerOptions = provider && typeof provider === "object" ? provider.options ?? void 0 : void 0;
19106
19407
  const providerThinkingConfig = providerOptions?.thinkingConfig;
19107
19408
  const modelThinkingConfigByModel = {};
19108
- for (const [modelId, model] of Object.entries(provider.models ?? {})) {
19409
+ for (const [modelId, model] of Object.entries(provider?.models ?? {})) {
19109
19410
  if (!model || typeof model !== "object") {
19110
19411
  continue;
19111
19412
  }