@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
@@ -1,12 +1,12 @@
1
1
  import crypto from "node:crypto";
2
- import { ANTIGRAVITY_ENDPOINT, GEMINI_CLI_ENDPOINT, EMPTY_SCHEMA_PLACEHOLDER_NAME, EMPTY_SCHEMA_PLACEHOLDER_DESCRIPTION, SKIP_THOUGHT_SIGNATURE, getRandomizedHeaders, } from "../constants";
2
+ import { ANTIGRAVITY_ENDPOINT, ANTIGRAVITY_DEFAULT_PROJECT_ID, GEMINI_CLI_ENDPOINT, EMPTY_SCHEMA_PLACEHOLDER_NAME, EMPTY_SCHEMA_PLACEHOLDER_DESCRIPTION, SKIP_THOUGHT_SIGNATURE, getRandomizedHeaders, } from "../constants";
3
3
  import { cacheSignature, getCachedSignature } from "./cache";
4
4
  import { getKeepThinking } from "./config";
5
5
  import { createStreamingTransformer, transformSseLine, transformStreamingPayload, } from "./core/streaming";
6
6
  import { defaultSignatureStore } from "./stores/signature-store";
7
7
  import { DEBUG_MESSAGE_PREFIX, isDebugEnabled, isDebugTuiEnabled, logAntigravityDebugResponse, logCacheStats, } from "./debug";
8
8
  import { createLogger } from "./logger";
9
- import { cleanJSONSchemaForAntigravity, DEFAULT_THINKING_BUDGET, deepFilterThinkingBlocks, extractThinkingConfig, extractVariantThinkingConfig, extractUsageFromSsePayload, extractUsageMetadata, fixToolResponseGrouping, validateAndFixClaudeToolPairing, applyToolPairingFixes, injectParameterSignatures, injectToolHardeningInstruction, isThinkingCapableModel, normalizeThinkingConfig, parseAntigravityApiBody, resolveThinkingConfig, rewriteAntigravityPreviewAccessError, transformThinkingParts, } from "./request-helpers";
9
+ import { cleanJSONSchemaForAntigravity, deepFilterThinkingBlocks, extractThinkingConfig, extractVariantThinkingConfig, extractUsageFromSsePayload, extractUsageMetadata, fixToolResponseGrouping, validateAndFixClaudeToolPairing, applyToolPairingFixes, injectParameterSignatures, injectToolHardeningInstruction, isThinkingCapableModel, normalizeThinkingConfig, parseAntigravityApiBody, resolveThinkingConfig, rewriteAntigravityPreviewAccessError, transformThinkingParts, } from "./request-helpers";
10
10
  import { CLAUDE_TOOL_SYSTEM_INSTRUCTION, CLAUDE_DESCRIPTION_PROMPT, } from "../constants";
11
11
  import { analyzeConversationState, closeToolLoopForThinking, needsThinkingRecovery, } from "./thinking-recovery";
12
12
  import { sanitizeCrossModelPayloadInPlace } from "./transform/cross-model-sanitizer";
@@ -14,11 +14,14 @@ import { isGemini3Model, isImageGenerationModel, buildImageGenerationConfig, app
14
14
  import { resolveModelWithTier, resolveModelWithVariant, resolveModelForHeaderStyle, isClaudeModel, isClaudeThinkingModel, CLAUDE_THINKING_MAX_OUTPUT_TOKENS, computeClaudeMaxOutputTokens, appendClaudeThinkingHint, } from "./transform";
15
15
  import { detectErrorType } from "./recovery";
16
16
  import { getSessionFingerprint, buildFingerprintHeaders } from "./fingerprint";
17
+ import { buildAgyAgentRequestMetadata, createAgyRequestSessionContext, orderAgyRequestPayloadInPlace, } from "./agy-request-metadata";
17
18
  import { appendGeminiDumpResponseText, createGeminiDumpResponseTransform, noteGeminiDumpResponse, } from "./gemini-dump";
18
19
  const log = createLogger("request");
19
20
  const PLUGIN_SESSION_ID = `-${crypto.randomUUID()}`;
21
+ const DEFAULT_AGY_REQUEST_SESSION = createAgyRequestSessionContext("");
20
22
  const sessionDisplayedThinkingHashes = new Set();
21
23
  const MIN_SIGNATURE_LENGTH = 50;
24
+ const AGY_CLAUDE_THINKING_BUDGET = 1024;
22
25
  const ANTIGRAVITY_ENVELOPE_FIELD_ORDER = [
23
26
  "project",
24
27
  "requestId",
@@ -26,17 +29,71 @@ const ANTIGRAVITY_ENVELOPE_FIELD_ORDER = [
26
29
  "model",
27
30
  "userAgent",
28
31
  "requestType",
29
- "enabledCreditTypes",
30
32
  ];
31
- function buildAntigravityRequestId(type = "agent") {
32
- if (type === "checkpoint") {
33
- return `checkpoint/${crypto.randomUUID()}`;
33
+ const OPENCODE_TITLE_PROMPT_PREFIX = "Generate a title for this conversation:";
34
+ function getOpenCodeTitleSourceText(payload) {
35
+ if (!Array.isArray(payload.contents)) {
36
+ return undefined;
37
+ }
38
+ const texts = payload.contents.flatMap((content) => {
39
+ if (!content || typeof content !== "object") {
40
+ return [];
41
+ }
42
+ const parts = content.parts;
43
+ if (!Array.isArray(parts)) {
44
+ return [];
45
+ }
46
+ return parts.flatMap((part) => !!part && typeof part === "object" && typeof part.text === "string"
47
+ ? [part.text]
48
+ : []);
49
+ });
50
+ const promptIndex = texts.findIndex((text) => text.startsWith(OPENCODE_TITLE_PROMPT_PREFIX));
51
+ return promptIndex >= 0 ? texts.slice(promptIndex + 1).find((text) => text.trim().length > 0) : undefined;
52
+ }
53
+ function isOpenCodeTitleGenerationRequest(payload) {
54
+ return getOpenCodeTitleSourceText(payload) !== undefined;
55
+ }
56
+ function formatLocalImageTitle(sourceText) {
57
+ const normalized = sourceText
58
+ .trim()
59
+ .replace(/^(["“])(.*)(["”])$/s, "$2")
60
+ .replace(/\s+/g, " ");
61
+ const characters = Array.from(normalized);
62
+ if (characters.length <= 50) {
63
+ return normalized || "Image generation";
64
+ }
65
+ return `${characters.slice(0, 47).join("").trimEnd()}...`;
66
+ }
67
+ export function getImageModelLocalTitle(input, init) {
68
+ const url = fetchInputToUrl(input);
69
+ if (!/\/models\/[^/:]*(?:image|imagen)[^/:]*:streamGenerateContent/i.test(url)) {
70
+ return undefined;
71
+ }
72
+ const body = init?.body;
73
+ const bodyText = typeof body === "string"
74
+ ? body
75
+ : body instanceof Uint8Array
76
+ ? new TextDecoder().decode(body)
77
+ : "";
78
+ if (!bodyText) {
79
+ return undefined;
80
+ }
81
+ try {
82
+ const sourceText = getOpenCodeTitleSourceText(JSON.parse(bodyText));
83
+ return sourceText === undefined ? undefined : formatLocalImageTitle(sourceText);
84
+ }
85
+ catch {
86
+ return undefined;
34
87
  }
35
- return `agent/${crypto.randomUUID()}/${Date.now()}/${crypto.randomUUID()}/2`;
36
88
  }
37
89
  function getAgyMaxOutputTokens(model) {
38
90
  const lower = model.toLowerCase();
39
- if (lower === "gemini-3.5-flash-low" || lower === "gemini-3.5-flash-extra-low" || lower === "gemini-3-flash-agent") {
91
+ if (lower === "gemini-3.5-flash-low"
92
+ || lower === "gemini-3.5-flash-extra-low"
93
+ || lower === "gemini-3-flash-agent"
94
+ || lower === "gemini-3.6-flash-low"
95
+ || lower === "gemini-3.6-flash-medium"
96
+ || lower === "gemini-3.6-flash-high") {
40
97
  return 65536;
41
98
  }
42
99
  if (lower === "gemini-3.1-pro-low" || lower === "gemini-pro-agent") {
@@ -45,6 +102,9 @@ function getAgyMaxOutputTokens(model) {
45
102
  if (lower === "claude-sonnet-4-6" || lower === "claude-opus-4-6-thinking") {
46
103
  return 64000;
47
104
  }
105
+ if (lower === "gpt-oss-120b-medium") {
106
+ return 32768;
107
+ }
48
108
  return undefined;
49
109
  }
50
110
  function applyAgyGenerationDefaults(model, generationConfig, headerStyle) {
@@ -93,9 +153,9 @@ function buildSignatureSessionKey(sessionId, model, conversationKey, projectKey)
93
153
  const JSON_SCHEMA_TYPES = new Set(["boolean", "string", "number", "integer", "array", "object"]);
94
154
  function thinkingSafeReplacer(key, value) {
95
155
  if (key === "thinking" && typeof value === "object" && value !== null) {
96
- // Preserve Schema objects in tool declarations (e.g., {type: "boolean"})
156
+ // Preserve tool schemas before and after Gemini uppercases their type.
97
157
  const rec = value;
98
- if (typeof rec.type === "string" && JSON_SCHEMA_TYPES.has(rec.type)) {
158
+ if (typeof rec.type === "string" && JSON_SCHEMA_TYPES.has(rec.type.toLowerCase())) {
99
159
  return value;
100
160
  }
101
161
  // Flatten any non-string, non-Schema thinking to empty string
@@ -349,6 +409,75 @@ function isValidRequestPart(part) {
349
409
  Object.prototype.hasOwnProperty.call(record, "codeExecutionResult") ||
350
410
  Object.prototype.hasOwnProperty.call(record, "thought"));
351
411
  }
412
+ function stripCacheControlFromParts(parts) {
413
+ if (!Array.isArray(parts)) {
414
+ return;
415
+ }
416
+ for (const part of parts) {
417
+ if (!part || typeof part !== "object" || Array.isArray(part)) {
418
+ continue;
419
+ }
420
+ const record = part;
421
+ delete record.cache_control;
422
+ delete record.cacheControl;
423
+ }
424
+ }
425
+ function stripUnsupportedAntigravityFields(payload) {
426
+ delete payload.providerOptions;
427
+ delete payload.cached_content;
428
+ delete payload.cachedContent;
429
+ delete payload.cache_control;
430
+ delete payload.cacheControl;
431
+ const extraBody = payload.extra_body;
432
+ if (extraBody && typeof extraBody === "object" && !Array.isArray(extraBody)) {
433
+ const extraBodyRecord = extraBody;
434
+ delete extraBodyRecord.cached_content;
435
+ delete extraBodyRecord.cachedContent;
436
+ delete extraBodyRecord.cache_control;
437
+ delete extraBodyRecord.cacheControl;
438
+ if (Object.keys(extraBodyRecord).length === 0) {
439
+ delete payload.extra_body;
440
+ }
441
+ }
442
+ const stripContentParts = (items) => {
443
+ if (!Array.isArray(items)) {
444
+ return;
445
+ }
446
+ for (const item of items) {
447
+ if (!item || typeof item !== "object" || Array.isArray(item)) {
448
+ continue;
449
+ }
450
+ const record = item;
451
+ stripCacheControlFromParts(record.parts);
452
+ stripCacheControlFromParts(record.content);
453
+ }
454
+ };
455
+ stripContentParts(payload.contents);
456
+ stripContentParts(payload.messages);
457
+ for (const instructionKey of ["systemInstruction", "system_instruction"]) {
458
+ const instruction = payload[instructionKey];
459
+ if (instruction && typeof instruction === "object" && !Array.isArray(instruction)) {
460
+ stripCacheControlFromParts(instruction.parts);
461
+ }
462
+ }
463
+ }
464
+ function configureAntigravityToolCalling(payload) {
465
+ if (!Array.isArray(payload.tools) || payload.tools.length === 0) {
466
+ delete payload.toolConfig;
467
+ return;
468
+ }
469
+ const toolConfig = payload.toolConfig && typeof payload.toolConfig === "object" && !Array.isArray(payload.toolConfig)
470
+ ? payload.toolConfig
471
+ : {};
472
+ const functionCallingConfig = toolConfig.functionCallingConfig &&
473
+ typeof toolConfig.functionCallingConfig === "object" &&
474
+ !Array.isArray(toolConfig.functionCallingConfig)
475
+ ? toolConfig.functionCallingConfig
476
+ : {};
477
+ functionCallingConfig.mode = "VALIDATED";
478
+ toolConfig.functionCallingConfig = functionCallingConfig;
479
+ payload.toolConfig = toolConfig;
480
+ }
352
481
  function sanitizeRequestPayloadForAntigravity(payload) {
353
482
  const anyPayload = payload;
354
483
  if (Array.isArray(anyPayload.contents)) {
@@ -690,20 +819,27 @@ let _lastCacheStats = null;
690
819
  export function getLastCacheStats() {
691
820
  return _lastCacheStats;
692
821
  }
693
- function generateSyntheticProjectId() {
694
- const adjectives = ["useful", "bright", "swift", "calm", "bold"];
695
- const nouns = ["fuze", "wave", "spark", "flow", "core"];
696
- const adj = adjectives[Math.floor(Math.random() * adjectives.length)];
697
- const noun = nouns[Math.floor(Math.random() * nouns.length)];
698
- const randomPart = crypto.randomUUID().slice(0, 5).toLowerCase();
699
- return `${adj}-${noun}-${randomPart}`;
700
- }
701
822
  const STREAM_ACTION = "streamGenerateContent";
702
823
  /**
703
- * Detects requests headed to the Google Generative Language API so we can intercept them.
824
+ * Extract a URL string from any fetch() input shape (string, URL, or Request).
825
+ */
826
+ export function fetchInputToUrl(input) {
827
+ if (typeof input === "string")
828
+ return input;
829
+ if (input instanceof URL)
830
+ return input.href;
831
+ // Request-like object
832
+ const url = input.url;
833
+ return typeof url === "string" ? url : String(input);
834
+ }
835
+ /**
836
+ * Detects requests headed to the Google Generative Language API so we can
837
+ * intercept them. Handles string, URL, and Request inputs — matching on URL
838
+ * only would let `fetch(new Request(...))` / `fetch(new URL(...))` bypass the
839
+ * interceptor entirely.
704
840
  */
705
841
  export function isGenerativeLanguageRequest(input) {
706
- return typeof input === "string" && input.includes("generativelanguage.googleapis.com");
842
+ return fetchInputToUrl(input).includes("generativelanguage.googleapis.com");
707
843
  }
708
844
  export function prepareAntigravityRequest(input, init, accessToken, projectId, endpointOverride, headerStyle = "antigravity", forceThinkingRecovery = false, options) {
709
845
  const baseInit = { ...init };
@@ -727,11 +863,14 @@ export function prepareAntigravityRequest(input, init, accessToken, projectId, e
727
863
  headers.delete("x-api-key");
728
864
  headers.delete("x-goog-api-key");
729
865
  headers.delete("x-session-affinity");
866
+ headers.delete("x-session-id");
867
+ headers.delete("x-parent-session-id");
730
868
  // Strip x-goog-user-project header to prevent 403 auth/license conflicts.
731
869
  // This header is added by OpenCode/AI SDK and can force project-level checks
732
870
  // that are not required for Antigravity/Gemini CLI OAuth requests.
733
871
  headers.delete("x-goog-user-project");
734
- const match = input.match(/\/models\/([^:]+):(\w+)/);
872
+ const urlString = fetchInputToUrl(input);
873
+ const match = urlString.match(/\/models\/([^:]+):(\w+)/);
735
874
  if (!match) {
736
875
  return {
737
876
  request: input,
@@ -766,9 +905,6 @@ export function prepareAntigravityRequest(input, init, accessToken, projectId, e
766
905
  model: effectiveModel,
767
906
  };
768
907
  if (headerStyle === "antigravity") {
769
- if (typeof wrappedBody.requestId !== "string" || !wrappedBody.requestId) {
770
- wrappedBody.requestId = buildAntigravityRequestId("agent");
771
- }
772
908
  if (typeof wrappedBody.userAgent !== "string" || !wrappedBody.userAgent) {
773
909
  wrappedBody.userAgent = "antigravity";
774
910
  }
@@ -797,8 +933,6 @@ export function prepareAntigravityRequest(input, init, accessToken, projectId, e
797
933
  sessionId = signatureSessionKey;
798
934
  }
799
935
  for (const req of requestObjects) {
800
- // Use stable session ID for signature caching across multi-turn conversations
801
- req.sessionId = signatureSessionKey;
802
936
  stripInjectedDebugFromRequestPayload(req);
803
937
  if (isClaude) {
804
938
  // Step 0: Sanitize cross-model metadata (strips Gemini signatures when sending to Claude)
@@ -815,6 +949,11 @@ export function prepareAntigravityRequest(input, init, accessToken, projectId, e
815
949
  // Step 3: Apply tool pairing fixes (ID assignment, response matching, orphan recovery)
816
950
  applyToolPairingFixes(req, true);
817
951
  }
952
+ if (headerStyle === "antigravity") {
953
+ sanitizeRequestPayloadForAntigravity(req);
954
+ stripUnsupportedAntigravityFields(req);
955
+ configureAntigravityToolCalling(req);
956
+ }
818
957
  }
819
958
  // Guard against assistant prefill: Claude rejects conversations ending
820
959
  // with model/assistant messages. After context compaction, the conversation
@@ -845,15 +984,41 @@ export function prepareAntigravityRequest(input, init, accessToken, projectId, e
845
984
  const hasCachedThinking = defaultSignatureStore.has(signatureSessionKey);
846
985
  needsSignedThinkingWarmup = hasToolUse && !hasSignedThinking && !hasCachedThinking;
847
986
  }
987
+ const wireRequest = requestObjects.at(-1);
988
+ if (wireRequest) {
989
+ if (headerStyle === "antigravity") {
990
+ const metadata = buildAgyAgentRequestMetadata(options?.agySession ?? DEFAULT_AGY_REQUEST_SESSION, wireRequest, effectiveModel, options?.agyRequestTimestamp);
991
+ wrappedBody.requestId = metadata.requestId;
992
+ wireRequest.sessionId = metadata.sessionId;
993
+ wireRequest.labels = metadata.labels;
994
+ orderAgyRequestPayloadInPlace(wireRequest);
995
+ }
996
+ else {
997
+ wireRequest.sessionId = signatureSessionKey;
998
+ }
999
+ }
848
1000
  body = safeStringify(headerStyle === "antigravity" ? orderAntigravityEnvelope(wrappedBody) : wrappedBody);
849
1001
  }
850
1002
  else {
851
1003
  const requestPayload = { ...parsedBody };
1004
+ if (headerStyle === "antigravity" &&
1005
+ isImageGenerationModel(effectiveModel) &&
1006
+ isOpenCodeTitleGenerationRequest(requestPayload)) {
1007
+ // OpenCode runs title generation through the active model. Route that
1008
+ // text-only helper call away from image generation to avoid consuming
1009
+ // image quota and writing unrelated image files.
1010
+ effectiveModel = "gemini-3.5-flash-low";
1011
+ tierThinkingBudget = 4000;
1012
+ tierThinkingLevel = undefined;
1013
+ }
852
1014
  const rawGenerationConfig = requestPayload.generationConfig;
853
1015
  const extraBody = requestPayload.extra_body;
854
1016
  const variantConfig = extractVariantThinkingConfig(requestPayload.providerOptions, rawGenerationConfig);
855
1017
  const isGemini3 = effectiveModel.toLowerCase().includes("gemini-3");
856
1018
  log.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)}`);
1019
+ // providerOptions belongs to the host AI SDK and is only used above to
1020
+ // resolve the requested variant. It is never part of the Google wire schema.
1021
+ delete requestPayload.providerOptions;
857
1022
  if (variantConfig?.thinkingLevel && isGemini3) {
858
1023
  // Gemini 3 native format - use thinkingLevel directly
859
1024
  const variantModelBase = rawModel
@@ -879,20 +1044,6 @@ export function prepareAntigravityRequest(input, init, accessToken, projectId, e
879
1044
  tierThinkingLevel = undefined;
880
1045
  }
881
1046
  }
882
- if (isClaude) {
883
- if (!requestPayload.toolConfig) {
884
- requestPayload.toolConfig = {};
885
- }
886
- if (typeof requestPayload.toolConfig === "object" && requestPayload.toolConfig !== null) {
887
- const toolConfig = requestPayload.toolConfig;
888
- if (!toolConfig.functionCallingConfig) {
889
- toolConfig.functionCallingConfig = {};
890
- }
891
- if (typeof toolConfig.functionCallingConfig === "object" && toolConfig.functionCallingConfig !== null) {
892
- toolConfig.functionCallingConfig.mode = "VALIDATED";
893
- }
894
- }
895
- }
896
1047
  // Resolve thinking configuration based on user settings and model capabilities
897
1048
  // Image generation models don't support thinking - skip thinking config entirely
898
1049
  const isImageModel = isImageGenerationModel(effectiveModel);
@@ -1011,25 +1162,27 @@ export function prepareAntigravityRequest(input, init, accessToken, projectId, e
1011
1162
  requestPayload.systemInstruction = requestPayload.system_instruction;
1012
1163
  delete requestPayload.system_instruction;
1013
1164
  }
1014
- // Normalize cached_content → cachedContent (camelCase) but preserve the value.
1015
- // OpenCode uses cachedContent for prompt caching — deleting it busts cache.
1016
- const cachedContentFromExtra = typeof requestPayload.extra_body === "object" && requestPayload.extra_body
1017
- ? requestPayload.extra_body.cached_content ??
1018
- requestPayload.extra_body.cachedContent
1019
- : undefined;
1020
- const cachedContent = requestPayload.cached_content ??
1021
- requestPayload.cachedContent ??
1022
- cachedContentFromExtra;
1023
- if (cachedContent) {
1024
- requestPayload.cachedContent = cachedContent;
1025
- }
1026
- // Only delete the snake_case duplicate — preserve camelCase
1027
- delete requestPayload.cached_content;
1028
- if (requestPayload.extra_body && typeof requestPayload.extra_body === "object") {
1029
- delete requestPayload.extra_body.cached_content;
1030
- delete requestPayload.extra_body.cachedContent;
1031
- if (Object.keys(requestPayload.extra_body).length === 0) {
1032
- delete requestPayload.extra_body;
1165
+ if (headerStyle !== "antigravity") {
1166
+ // Gemini CLI accepts explicit cachedContent references. Normalize its
1167
+ // snake_case aliases only on that transport; agy relies on implicit
1168
+ // prefix caching and does not send any of these fields.
1169
+ const cachedContentFromExtra = typeof requestPayload.extra_body === "object" && requestPayload.extra_body
1170
+ ? requestPayload.extra_body.cached_content ??
1171
+ requestPayload.extra_body.cachedContent
1172
+ : undefined;
1173
+ const cachedContent = requestPayload.cached_content ??
1174
+ requestPayload.cachedContent ??
1175
+ cachedContentFromExtra;
1176
+ if (cachedContent) {
1177
+ requestPayload.cachedContent = cachedContent;
1178
+ }
1179
+ delete requestPayload.cached_content;
1180
+ if (requestPayload.extra_body && typeof requestPayload.extra_body === "object") {
1181
+ delete requestPayload.extra_body.cached_content;
1182
+ delete requestPayload.extra_body.cachedContent;
1183
+ if (Object.keys(requestPayload.extra_body).length === 0) {
1184
+ delete requestPayload.extra_body;
1185
+ }
1033
1186
  }
1034
1187
  }
1035
1188
  // Normalize tools. For Claude models, keep full function declarations (names + schemas).
@@ -1309,13 +1462,30 @@ export function prepareAntigravityRequest(input, init, accessToken, projectId, e
1309
1462
  }
1310
1463
  stripInjectedDebugFromRequestPayload(requestPayload);
1311
1464
  sanitizeRequestPayloadForAntigravity(requestPayload);
1312
- const effectiveProjectId = projectId?.trim() || (headerStyle === "antigravity" ? generateSyntheticProjectId() : "");
1465
+ if (headerStyle === "antigravity") {
1466
+ stripUnsupportedAntigravityFields(requestPayload);
1467
+ configureAntigravityToolCalling(requestPayload);
1468
+ }
1469
+ // Use the stable default project ID (never a per-request random one):
1470
+ // a fresh random project each request busts the prompt cache and
1471
+ // fragments server-side quota/session state. ensureProjectContext
1472
+ // already floors empty cases to this default; this is defense in depth.
1473
+ const effectiveProjectId = projectId?.trim() || (headerStyle === "antigravity" ? ANTIGRAVITY_DEFAULT_PROJECT_ID : "");
1313
1474
  resolvedProjectId = effectiveProjectId;
1314
- // System instruction injection removed — CLIProxyAPI v6.9.x no longer injects it
1475
+ // Keep internal signature-cache identity separate from AGY wire session metadata.
1476
+ sessionId = signatureSessionKey;
1477
+ const agyMetadata = headerStyle === "antigravity"
1478
+ ? buildAgyAgentRequestMetadata(options?.agySession ?? DEFAULT_AGY_REQUEST_SESSION, requestPayload, effectiveModel, options?.agyRequestTimestamp)
1479
+ : null;
1480
+ requestPayload.sessionId = agyMetadata?.sessionId ?? signatureSessionKey;
1481
+ if (agyMetadata) {
1482
+ requestPayload.labels = agyMetadata.labels;
1483
+ orderAgyRequestPayloadInPlace(requestPayload);
1484
+ }
1315
1485
  const wrappedBody = headerStyle === "antigravity"
1316
1486
  ? {
1317
1487
  project: effectiveProjectId,
1318
- requestId: buildAntigravityRequestId("agent"),
1488
+ requestId: agyMetadata.requestId,
1319
1489
  request: requestPayload,
1320
1490
  model: effectiveModel,
1321
1491
  userAgent: "antigravity",
@@ -1326,11 +1496,6 @@ export function prepareAntigravityRequest(input, init, accessToken, projectId, e
1326
1496
  model: effectiveModel,
1327
1497
  request: requestPayload,
1328
1498
  };
1329
- if (wrappedBody.request && typeof wrappedBody.request === 'object') {
1330
- // Use stable session ID for signature caching across multi-turn conversations
1331
- sessionId = signatureSessionKey;
1332
- wrappedBody.request.sessionId = signatureSessionKey;
1333
- }
1334
1499
  body = safeStringify(headerStyle === "antigravity" ? orderAntigravityEnvelope(wrappedBody) : wrappedBody);
1335
1500
  }
1336
1501
  }
@@ -1407,17 +1572,21 @@ export function buildThinkingWarmupBody(bodyText, isClaudeThinking) {
1407
1572
  return null;
1408
1573
  }
1409
1574
  const warmupPrompt = "Warmup request for thinking signature.";
1575
+ const wireModel = typeof parsed.model === "string" ? parsed.model : "claude-sonnet-4-6";
1576
+ const requestObjects = [];
1410
1577
  const updateRequest = (req) => {
1411
1578
  req.contents = [{ role: "user", parts: [{ text: warmupPrompt }] }];
1412
1579
  delete req.tools;
1413
1580
  delete req.toolConfig;
1414
1581
  const generationConfig = (req.generationConfig ?? {});
1415
1582
  generationConfig.thinkingConfig = {
1416
- include_thoughts: true,
1417
- thinking_budget: DEFAULT_THINKING_BUDGET,
1583
+ includeThoughts: true,
1584
+ thinkingBudget: AGY_CLAUDE_THINKING_BUDGET,
1418
1585
  };
1419
- generationConfig.maxOutputTokens = computeClaudeMaxOutputTokens(DEFAULT_THINKING_BUDGET);
1586
+ generationConfig.maxOutputTokens = getAgyMaxOutputTokens(wireModel) ??
1587
+ computeClaudeMaxOutputTokens(AGY_CLAUDE_THINKING_BUDGET);
1420
1588
  req.generationConfig = generationConfig;
1589
+ requestObjects.push(req);
1421
1590
  };
1422
1591
  if (parsed.request && typeof parsed.request === "object") {
1423
1592
  updateRequest(parsed.request);
@@ -1429,6 +1598,22 @@ export function buildThinkingWarmupBody(bodyText, isClaudeThinking) {
1429
1598
  else {
1430
1599
  updateRequest(parsed);
1431
1600
  }
1601
+ const wireRequest = requestObjects.at(-1);
1602
+ if (wireRequest) {
1603
+ const numericSessionId = typeof wireRequest.sessionId === "string"
1604
+ ? wireRequest.sessionId
1605
+ : DEFAULT_AGY_REQUEST_SESSION.numericSessionId;
1606
+ const warmupSession = {
1607
+ conversationId: crypto.randomUUID(),
1608
+ trajectoryId: crypto.randomUUID(),
1609
+ numericSessionId,
1610
+ };
1611
+ const metadata = buildAgyAgentRequestMetadata(warmupSession, wireRequest, wireModel);
1612
+ parsed.requestId = metadata.requestId;
1613
+ wireRequest.sessionId = metadata.sessionId;
1614
+ wireRequest.labels = metadata.labels;
1615
+ orderAgyRequestPayloadInPlace(wireRequest);
1616
+ }
1432
1617
  return safeStringify(parsed);
1433
1618
  }
1434
1619
  /**
@@ -1649,7 +1834,6 @@ export const __testExports = {
1649
1834
  hasToolUseInMessages,
1650
1835
  ensureThinkingBeforeToolUseInContents,
1651
1836
  ensureThinkingBeforeToolUseInMessages,
1652
- generateSyntheticProjectId,
1653
1837
  MIN_SIGNATURE_LENGTH,
1654
1838
  transformSseLine,
1655
1839
  transformStreamingPayload,