@cortexkit/opencode-antigravity-auth 1.0.1 → 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 (47) hide show
  1. package/README.md +24 -8
  2. package/dist/index.js +1299 -431
  3. package/dist/index.js.map +4 -4
  4. package/dist/src/plugin/accounts.d.ts +29 -9
  5. package/dist/src/plugin/accounts.d.ts.map +1 -1
  6. package/dist/src/plugin/accounts.js +253 -52
  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/image-saver.d.ts +2 -21
  17. package/dist/src/plugin/image-saver.d.ts.map +1 -1
  18. package/dist/src/plugin/image-saver.js +29 -63
  19. package/dist/src/plugin/image-saver.js.map +1 -1
  20. package/dist/src/plugin/request-helpers.d.ts +1 -0
  21. package/dist/src/plugin/request-helpers.d.ts.map +1 -1
  22. package/dist/src/plugin/request-helpers.js +9 -4
  23. package/dist/src/plugin/request-helpers.js.map +1 -1
  24. package/dist/src/plugin/request.d.ts +6 -0
  25. package/dist/src/plugin/request.d.ts.map +1 -1
  26. package/dist/src/plugin/request.js +230 -57
  27. package/dist/src/plugin/request.js.map +1 -1
  28. package/dist/src/plugin/session-context.d.ts +21 -0
  29. package/dist/src/plugin/session-context.d.ts.map +1 -0
  30. package/dist/src/plugin/session-context.js +63 -0
  31. package/dist/src/plugin/session-context.js.map +1 -0
  32. package/dist/src/plugin/storage.d.ts +6 -0
  33. package/dist/src/plugin/storage.d.ts.map +1 -1
  34. package/dist/src/plugin/storage.js +14 -3
  35. package/dist/src/plugin/storage.js.map +1 -1
  36. package/dist/src/plugin/ui/auth-menu.d.ts +1 -1
  37. package/dist/src/plugin/ui/auth-menu.d.ts.map +1 -1
  38. package/dist/src/plugin/ui/auth-menu.js +3 -1
  39. package/dist/src/plugin/ui/auth-menu.js.map +1 -1
  40. package/dist/src/plugin/ui/quota-status.d.ts.map +1 -1
  41. package/dist/src/plugin/ui/quota-status.js +2 -1
  42. package/dist/src/plugin/ui/quota-status.js.map +1 -1
  43. package/dist/src/plugin.d.ts +16 -0
  44. package/dist/src/plugin.d.ts.map +1 -1
  45. package/dist/src/plugin.js +261 -101
  46. package/dist/src/plugin.js.map +1 -1
  47. package/package.json +2 -2
@@ -6,7 +6,7 @@ import { createStreamingTransformer, transformSseLine, transformStreamingPayload
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)) {
@@ -734,6 +863,8 @@ export function prepareAntigravityRequest(input, init, accessToken, projectId, e
734
863
  headers.delete("x-api-key");
735
864
  headers.delete("x-goog-api-key");
736
865
  headers.delete("x-session-affinity");
866
+ headers.delete("x-session-id");
867
+ headers.delete("x-parent-session-id");
737
868
  // Strip x-goog-user-project header to prevent 403 auth/license conflicts.
738
869
  // This header is added by OpenCode/AI SDK and can force project-level checks
739
870
  // that are not required for Antigravity/Gemini CLI OAuth requests.
@@ -774,9 +905,6 @@ export function prepareAntigravityRequest(input, init, accessToken, projectId, e
774
905
  model: effectiveModel,
775
906
  };
776
907
  if (headerStyle === "antigravity") {
777
- if (typeof wrappedBody.requestId !== "string" || !wrappedBody.requestId) {
778
- wrappedBody.requestId = buildAntigravityRequestId("agent");
779
- }
780
908
  if (typeof wrappedBody.userAgent !== "string" || !wrappedBody.userAgent) {
781
909
  wrappedBody.userAgent = "antigravity";
782
910
  }
@@ -805,8 +933,6 @@ export function prepareAntigravityRequest(input, init, accessToken, projectId, e
805
933
  sessionId = signatureSessionKey;
806
934
  }
807
935
  for (const req of requestObjects) {
808
- // Use stable session ID for signature caching across multi-turn conversations
809
- req.sessionId = signatureSessionKey;
810
936
  stripInjectedDebugFromRequestPayload(req);
811
937
  if (isClaude) {
812
938
  // Step 0: Sanitize cross-model metadata (strips Gemini signatures when sending to Claude)
@@ -823,6 +949,11 @@ export function prepareAntigravityRequest(input, init, accessToken, projectId, e
823
949
  // Step 3: Apply tool pairing fixes (ID assignment, response matching, orphan recovery)
824
950
  applyToolPairingFixes(req, true);
825
951
  }
952
+ if (headerStyle === "antigravity") {
953
+ sanitizeRequestPayloadForAntigravity(req);
954
+ stripUnsupportedAntigravityFields(req);
955
+ configureAntigravityToolCalling(req);
956
+ }
826
957
  }
827
958
  // Guard against assistant prefill: Claude rejects conversations ending
828
959
  // with model/assistant messages. After context compaction, the conversation
@@ -853,15 +984,41 @@ export function prepareAntigravityRequest(input, init, accessToken, projectId, e
853
984
  const hasCachedThinking = defaultSignatureStore.has(signatureSessionKey);
854
985
  needsSignedThinkingWarmup = hasToolUse && !hasSignedThinking && !hasCachedThinking;
855
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
+ }
856
1000
  body = safeStringify(headerStyle === "antigravity" ? orderAntigravityEnvelope(wrappedBody) : wrappedBody);
857
1001
  }
858
1002
  else {
859
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
+ }
860
1014
  const rawGenerationConfig = requestPayload.generationConfig;
861
1015
  const extraBody = requestPayload.extra_body;
862
1016
  const variantConfig = extractVariantThinkingConfig(requestPayload.providerOptions, rawGenerationConfig);
863
1017
  const isGemini3 = effectiveModel.toLowerCase().includes("gemini-3");
864
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;
865
1022
  if (variantConfig?.thinkingLevel && isGemini3) {
866
1023
  // Gemini 3 native format - use thinkingLevel directly
867
1024
  const variantModelBase = rawModel
@@ -887,20 +1044,6 @@ export function prepareAntigravityRequest(input, init, accessToken, projectId, e
887
1044
  tierThinkingLevel = undefined;
888
1045
  }
889
1046
  }
890
- if (isClaude) {
891
- if (!requestPayload.toolConfig) {
892
- requestPayload.toolConfig = {};
893
- }
894
- if (typeof requestPayload.toolConfig === "object" && requestPayload.toolConfig !== null) {
895
- const toolConfig = requestPayload.toolConfig;
896
- if (!toolConfig.functionCallingConfig) {
897
- toolConfig.functionCallingConfig = {};
898
- }
899
- if (typeof toolConfig.functionCallingConfig === "object" && toolConfig.functionCallingConfig !== null) {
900
- toolConfig.functionCallingConfig.mode = "VALIDATED";
901
- }
902
- }
903
- }
904
1047
  // Resolve thinking configuration based on user settings and model capabilities
905
1048
  // Image generation models don't support thinking - skip thinking config entirely
906
1049
  const isImageModel = isImageGenerationModel(effectiveModel);
@@ -1019,25 +1162,27 @@ export function prepareAntigravityRequest(input, init, accessToken, projectId, e
1019
1162
  requestPayload.systemInstruction = requestPayload.system_instruction;
1020
1163
  delete requestPayload.system_instruction;
1021
1164
  }
1022
- // Normalize cached_content → cachedContent (camelCase) but preserve the value.
1023
- // OpenCode uses cachedContent for prompt caching — deleting it busts cache.
1024
- const cachedContentFromExtra = typeof requestPayload.extra_body === "object" && requestPayload.extra_body
1025
- ? requestPayload.extra_body.cached_content ??
1026
- requestPayload.extra_body.cachedContent
1027
- : undefined;
1028
- const cachedContent = requestPayload.cached_content ??
1029
- requestPayload.cachedContent ??
1030
- cachedContentFromExtra;
1031
- if (cachedContent) {
1032
- requestPayload.cachedContent = cachedContent;
1033
- }
1034
- // Only delete the snake_case duplicate — preserve camelCase
1035
- delete requestPayload.cached_content;
1036
- if (requestPayload.extra_body && typeof requestPayload.extra_body === "object") {
1037
- delete requestPayload.extra_body.cached_content;
1038
- delete requestPayload.extra_body.cachedContent;
1039
- if (Object.keys(requestPayload.extra_body).length === 0) {
1040
- 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
+ }
1041
1186
  }
1042
1187
  }
1043
1188
  // Normalize tools. For Claude models, keep full function declarations (names + schemas).
@@ -1317,17 +1462,30 @@ export function prepareAntigravityRequest(input, init, accessToken, projectId, e
1317
1462
  }
1318
1463
  stripInjectedDebugFromRequestPayload(requestPayload);
1319
1464
  sanitizeRequestPayloadForAntigravity(requestPayload);
1465
+ if (headerStyle === "antigravity") {
1466
+ stripUnsupportedAntigravityFields(requestPayload);
1467
+ configureAntigravityToolCalling(requestPayload);
1468
+ }
1320
1469
  // Use the stable default project ID (never a per-request random one):
1321
1470
  // a fresh random project each request busts the prompt cache and
1322
1471
  // fragments server-side quota/session state. ensureProjectContext
1323
1472
  // already floors empty cases to this default; this is defense in depth.
1324
1473
  const effectiveProjectId = projectId?.trim() || (headerStyle === "antigravity" ? ANTIGRAVITY_DEFAULT_PROJECT_ID : "");
1325
1474
  resolvedProjectId = effectiveProjectId;
1326
- // 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
+ }
1327
1485
  const wrappedBody = headerStyle === "antigravity"
1328
1486
  ? {
1329
1487
  project: effectiveProjectId,
1330
- requestId: buildAntigravityRequestId("agent"),
1488
+ requestId: agyMetadata.requestId,
1331
1489
  request: requestPayload,
1332
1490
  model: effectiveModel,
1333
1491
  userAgent: "antigravity",
@@ -1338,11 +1496,6 @@ export function prepareAntigravityRequest(input, init, accessToken, projectId, e
1338
1496
  model: effectiveModel,
1339
1497
  request: requestPayload,
1340
1498
  };
1341
- if (wrappedBody.request && typeof wrappedBody.request === 'object') {
1342
- // Use stable session ID for signature caching across multi-turn conversations
1343
- sessionId = signatureSessionKey;
1344
- wrappedBody.request.sessionId = signatureSessionKey;
1345
- }
1346
1499
  body = safeStringify(headerStyle === "antigravity" ? orderAntigravityEnvelope(wrappedBody) : wrappedBody);
1347
1500
  }
1348
1501
  }
@@ -1419,17 +1572,21 @@ export function buildThinkingWarmupBody(bodyText, isClaudeThinking) {
1419
1572
  return null;
1420
1573
  }
1421
1574
  const warmupPrompt = "Warmup request for thinking signature.";
1575
+ const wireModel = typeof parsed.model === "string" ? parsed.model : "claude-sonnet-4-6";
1576
+ const requestObjects = [];
1422
1577
  const updateRequest = (req) => {
1423
1578
  req.contents = [{ role: "user", parts: [{ text: warmupPrompt }] }];
1424
1579
  delete req.tools;
1425
1580
  delete req.toolConfig;
1426
1581
  const generationConfig = (req.generationConfig ?? {});
1427
1582
  generationConfig.thinkingConfig = {
1428
- include_thoughts: true,
1429
- thinking_budget: DEFAULT_THINKING_BUDGET,
1583
+ includeThoughts: true,
1584
+ thinkingBudget: AGY_CLAUDE_THINKING_BUDGET,
1430
1585
  };
1431
- generationConfig.maxOutputTokens = computeClaudeMaxOutputTokens(DEFAULT_THINKING_BUDGET);
1586
+ generationConfig.maxOutputTokens = getAgyMaxOutputTokens(wireModel) ??
1587
+ computeClaudeMaxOutputTokens(AGY_CLAUDE_THINKING_BUDGET);
1432
1588
  req.generationConfig = generationConfig;
1589
+ requestObjects.push(req);
1433
1590
  };
1434
1591
  if (parsed.request && typeof parsed.request === "object") {
1435
1592
  updateRequest(parsed.request);
@@ -1441,6 +1598,22 @@ export function buildThinkingWarmupBody(bodyText, isClaudeThinking) {
1441
1598
  else {
1442
1599
  updateRequest(parsed);
1443
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
+ }
1444
1617
  return safeStringify(parsed);
1445
1618
  }
1446
1619
  /**