@omnicross/core 0.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 (114) hide show
  1. package/LICENSE +21 -0
  2. package/NOTICE +57 -0
  3. package/README.md +15 -0
  4. package/dist/ApiKeyPoolService-BmMkau07.d.cts +170 -0
  5. package/dist/ApiKeyPoolService-BmMkau07.d.ts +170 -0
  6. package/dist/ProviderProxy-f_8ziIhW.d.cts +120 -0
  7. package/dist/ProviderProxy-vjt8sQQk.d.ts +120 -0
  8. package/dist/SubscriptionAuthSource-Cr4fVEYY.d.cts +264 -0
  9. package/dist/SubscriptionAuthSource-D89zmiSS.d.ts +264 -0
  10. package/dist/auth/GeminiCodeAssistProjectResolver.cjs +218 -0
  11. package/dist/auth/GeminiCodeAssistProjectResolver.d.cts +68 -0
  12. package/dist/auth/GeminiCodeAssistProjectResolver.d.ts +68 -0
  13. package/dist/auth/GeminiCodeAssistProjectResolver.js +189 -0
  14. package/dist/completion/ApiKeyPoolService.cjs +331 -0
  15. package/dist/completion/ApiKeyPoolService.d.cts +2 -0
  16. package/dist/completion/ApiKeyPoolService.d.ts +2 -0
  17. package/dist/completion/ApiKeyPoolService.js +306 -0
  18. package/dist/completion.cjs +4027 -0
  19. package/dist/completion.d.cts +17 -0
  20. package/dist/completion.d.ts +17 -0
  21. package/dist/completion.js +3983 -0
  22. package/dist/index-BTSmc9Sm.d.ts +645 -0
  23. package/dist/index-DXazdTzZ.d.cts +645 -0
  24. package/dist/index.cjs +10428 -0
  25. package/dist/index.d.cts +128 -0
  26. package/dist/index.d.ts +128 -0
  27. package/dist/index.js +10339 -0
  28. package/dist/outbound-api/subscriptionRegistryPort.cjs +38 -0
  29. package/dist/outbound-api/subscriptionRegistryPort.d.cts +73 -0
  30. package/dist/outbound-api/subscriptionRegistryPort.d.ts +73 -0
  31. package/dist/outbound-api/subscriptionRegistryPort.js +12 -0
  32. package/dist/outbound-api.cjs +5264 -0
  33. package/dist/outbound-api.d.cts +320 -0
  34. package/dist/outbound-api.d.ts +320 -0
  35. package/dist/outbound-api.js +5218 -0
  36. package/dist/pipeline/SubscriptionAuthSource.cjs +131 -0
  37. package/dist/pipeline/SubscriptionAuthSource.d.cts +3 -0
  38. package/dist/pipeline/SubscriptionAuthSource.d.ts +3 -0
  39. package/dist/pipeline/SubscriptionAuthSource.js +103 -0
  40. package/dist/pipeline/SubscriptionAuthStrategy.cjs +18 -0
  41. package/dist/pipeline/SubscriptionAuthStrategy.d.cts +61 -0
  42. package/dist/pipeline/SubscriptionAuthStrategy.d.ts +61 -0
  43. package/dist/pipeline/SubscriptionAuthStrategy.js +0 -0
  44. package/dist/ports/gemini-code-assist-resolver.cjs +38 -0
  45. package/dist/ports/gemini-code-assist-resolver.d.cts +26 -0
  46. package/dist/ports/gemini-code-assist-resolver.d.ts +26 -0
  47. package/dist/ports/gemini-code-assist-resolver.js +12 -0
  48. package/dist/ports.cjs +18 -0
  49. package/dist/ports.d.cts +15 -0
  50. package/dist/ports.d.ts +15 -0
  51. package/dist/ports.js +0 -0
  52. package/dist/provider-proxy/ingress/providerProxyShared.cjs +2958 -0
  53. package/dist/provider-proxy/ingress/providerProxyShared.d.cts +77 -0
  54. package/dist/provider-proxy/ingress/providerProxyShared.d.ts +77 -0
  55. package/dist/provider-proxy/ingress/providerProxyShared.js +2925 -0
  56. package/dist/provider-proxy/matchText.cjs +73 -0
  57. package/dist/provider-proxy/matchText.d.cts +47 -0
  58. package/dist/provider-proxy/matchText.d.ts +47 -0
  59. package/dist/provider-proxy/matchText.js +45 -0
  60. package/dist/provider-proxy/types.cjs +18 -0
  61. package/dist/provider-proxy/types.d.cts +12 -0
  62. package/dist/provider-proxy/types.d.ts +12 -0
  63. package/dist/provider-proxy/types.js +0 -0
  64. package/dist/provider-proxy.cjs +4667 -0
  65. package/dist/provider-proxy.d.cts +69 -0
  66. package/dist/provider-proxy.d.ts +69 -0
  67. package/dist/provider-proxy.js +4636 -0
  68. package/dist/serializeError.cjs +82 -0
  69. package/dist/serializeError.d.cts +24 -0
  70. package/dist/serializeError.d.ts +24 -0
  71. package/dist/serializeError.js +57 -0
  72. package/dist/sse-parser.cjs +456 -0
  73. package/dist/sse-parser.d.cts +143 -0
  74. package/dist/sse-parser.d.ts +143 -0
  75. package/dist/sse-parser.js +430 -0
  76. package/dist/transformer/TransformerChainExecutor.cjs +321 -0
  77. package/dist/transformer/TransformerChainExecutor.d.cts +104 -0
  78. package/dist/transformer/TransformerChainExecutor.d.ts +104 -0
  79. package/dist/transformer/TransformerChainExecutor.js +294 -0
  80. package/dist/transformer/TransformerService.cjs +290 -0
  81. package/dist/transformer/TransformerService.d.cts +138 -0
  82. package/dist/transformer/TransformerService.d.ts +138 -0
  83. package/dist/transformer/TransformerService.js +265 -0
  84. package/dist/transformer/transformers/GeminiCodeAssistTransformer.cjs +1115 -0
  85. package/dist/transformer/transformers/GeminiCodeAssistTransformer.d.cts +102 -0
  86. package/dist/transformer/transformers/GeminiCodeAssistTransformer.d.ts +102 -0
  87. package/dist/transformer/transformers/GeminiCodeAssistTransformer.js +1085 -0
  88. package/dist/transformer/transformers/GeminiTransformer.cjs +1013 -0
  89. package/dist/transformer/transformers/GeminiTransformer.d.cts +70 -0
  90. package/dist/transformer/transformers/GeminiTransformer.d.ts +70 -0
  91. package/dist/transformer/transformers/GeminiTransformer.js +986 -0
  92. package/dist/transformer/transformers/OpenAIResponseTransformer.cjs +538 -0
  93. package/dist/transformer/transformers/OpenAIResponseTransformer.d.cts +53 -0
  94. package/dist/transformer/transformers/OpenAIResponseTransformer.d.ts +53 -0
  95. package/dist/transformer/transformers/OpenAIResponseTransformer.js +513 -0
  96. package/dist/transformer/transformers/OpenCodeGoTransformer.cjs +73 -0
  97. package/dist/transformer/transformers/OpenCodeGoTransformer.d.cts +51 -0
  98. package/dist/transformer/transformers/OpenCodeGoTransformer.d.ts +51 -0
  99. package/dist/transformer/transformers/OpenCodeGoTransformer.js +48 -0
  100. package/dist/transformer/types.cjs +18 -0
  101. package/dist/transformer/types.d.cts +405 -0
  102. package/dist/transformer/types.d.ts +405 -0
  103. package/dist/transformer/types.js +0 -0
  104. package/dist/transformer.cjs +3736 -0
  105. package/dist/transformer.d.cts +33 -0
  106. package/dist/transformer.d.ts +33 -0
  107. package/dist/transformer.js +3712 -0
  108. package/dist/types-CGGrKqC_.d.cts +142 -0
  109. package/dist/types-CbCN2NQP.d.ts +142 -0
  110. package/dist/types-DCzHkhJt.d.ts +467 -0
  111. package/dist/types-DZIQbgp0.d.cts +467 -0
  112. package/dist/usage-event-sink-BX7FE1NL.d.cts +59 -0
  113. package/dist/usage-event-sink-BX7FE1NL.d.ts +59 -0
  114. package/package.json +62 -0
@@ -0,0 +1,1085 @@
1
+ // src/transformer/transformers/utils/gemini.response-in.ts
2
+ var FINISH_REASON_TO_GEMINI = {
3
+ stop: "STOP",
4
+ length: "MAX_TOKENS",
5
+ tool_calls: "STOP",
6
+ content_filter: "SAFETY",
7
+ // Already-lowercased Gemini reasons (pass-through from another transformer)
8
+ max_tokens: "MAX_TOKENS",
9
+ safety: "SAFETY"
10
+ };
11
+ function toGeminiFinishReason(openaiReason) {
12
+ if (!openaiReason) return null;
13
+ return FINISH_REASON_TO_GEMINI[openaiReason] || openaiReason.toUpperCase();
14
+ }
15
+ function convertOpenAIResponseToGemini(openaiData) {
16
+ const choice = openaiData.choices?.[0];
17
+ const message = choice?.message ?? {};
18
+ const usage = openaiData.usage;
19
+ const usagePromptDetails = usage?.prompt_tokens_details;
20
+ const usageOutputDetails = usage?.output_tokens_details;
21
+ const parts = [];
22
+ const thinking = message.thinking;
23
+ if (thinking?.content) {
24
+ parts.push({ text: thinking.content, thought: true });
25
+ }
26
+ if (thinking?.signature) {
27
+ parts.push({ thoughtSignature: thinking.signature });
28
+ }
29
+ if (message.content) {
30
+ parts.push({ text: message.content });
31
+ }
32
+ const toolCalls = message.tool_calls;
33
+ if (toolCalls?.length) {
34
+ for (const tc of toolCalls) {
35
+ const func = tc.function;
36
+ let args = {};
37
+ try {
38
+ const raw = func.arguments;
39
+ args = typeof raw === "string" ? JSON.parse(raw) : raw;
40
+ } catch {
41
+ args = {};
42
+ }
43
+ parts.push({
44
+ functionCall: {
45
+ id: tc.id,
46
+ name: func.name,
47
+ args
48
+ }
49
+ });
50
+ }
51
+ }
52
+ if (parts.length === 0) {
53
+ parts.push({ text: "" });
54
+ }
55
+ return {
56
+ responseId: openaiData.id || "",
57
+ modelVersion: openaiData.model || "",
58
+ candidates: [{
59
+ content: { parts },
60
+ finishReason: toGeminiFinishReason(choice?.finish_reason)
61
+ }],
62
+ usageMetadata: usage ? {
63
+ promptTokenCount: usage.prompt_tokens || 0,
64
+ candidatesTokenCount: usage.completion_tokens || 0,
65
+ totalTokenCount: usage.total_tokens || 0,
66
+ cachedContentTokenCount: usagePromptDetails?.cached_tokens || 0,
67
+ thoughtsTokenCount: usageOutputDetails?.reasoning_tokens || 0
68
+ } : void 0
69
+ };
70
+ }
71
+ function convertOpenAIStreamToGemini(openaiStream, logger) {
72
+ const decoder = new TextDecoder();
73
+ const encoder = new TextEncoder();
74
+ const pendingToolCalls = /* @__PURE__ */ new Map();
75
+ let model = "";
76
+ let responseId = "";
77
+ return new ReadableStream({
78
+ start: async (controller) => {
79
+ const reader = openaiStream.getReader();
80
+ let buffer = "";
81
+ let isClosed = false;
82
+ const emit = (data) => {
83
+ if (isClosed) return;
84
+ try {
85
+ controller.enqueue(encoder.encode(`data: ${JSON.stringify(data)}
86
+
87
+ `));
88
+ } catch {
89
+ isClosed = true;
90
+ }
91
+ };
92
+ const flushToolCalls = () => {
93
+ if (pendingToolCalls.size === 0) return;
94
+ const parts = [];
95
+ for (const tc of pendingToolCalls.values()) {
96
+ let args = {};
97
+ try {
98
+ args = JSON.parse(tc.args || "{}");
99
+ } catch {
100
+ }
101
+ parts.push({
102
+ functionCall: { id: tc.id, name: tc.name, args }
103
+ });
104
+ }
105
+ pendingToolCalls.clear();
106
+ emit({
107
+ responseId,
108
+ modelVersion: model,
109
+ candidates: [{ content: { parts }, finishReason: null }]
110
+ });
111
+ };
112
+ try {
113
+ while (true) {
114
+ const { done, value } = await reader.read();
115
+ if (done) break;
116
+ buffer += decoder.decode(value, { stream: true });
117
+ const lines = buffer.split("\n");
118
+ buffer = lines.pop() || "";
119
+ for (const line of lines) {
120
+ if (isClosed) break;
121
+ if (!line.startsWith("data:")) continue;
122
+ const data = line.slice(5).trim();
123
+ if (!data || data === "[DONE]") continue;
124
+ try {
125
+ const chunk = JSON.parse(data);
126
+ if (!responseId && chunk.id) responseId = chunk.id;
127
+ if (!model && chunk.model) model = chunk.model;
128
+ const choice = chunk.choices?.[0];
129
+ if (!choice) continue;
130
+ const delta = choice.delta ?? {};
131
+ if (delta.thinking) {
132
+ const parts = [];
133
+ if (delta.thinking.content) {
134
+ parts.push({ text: delta.thinking.content, thought: true });
135
+ }
136
+ if (delta.thinking.signature) {
137
+ parts.push({ thoughtSignature: delta.thinking.signature });
138
+ }
139
+ if (parts.length > 0) {
140
+ emit({
141
+ responseId,
142
+ modelVersion: model,
143
+ candidates: [{ content: { parts }, finishReason: null }]
144
+ });
145
+ }
146
+ }
147
+ if (delta.content) {
148
+ emit({
149
+ responseId,
150
+ modelVersion: model,
151
+ candidates: [{
152
+ content: { parts: [{ text: delta.content }] },
153
+ finishReason: null
154
+ }]
155
+ });
156
+ }
157
+ if (delta.tool_calls) {
158
+ for (const tc of delta.tool_calls) {
159
+ const idx = tc.index ?? 0;
160
+ const existing = pendingToolCalls.get(idx);
161
+ const func = tc.function;
162
+ if (existing) {
163
+ if (func?.arguments) {
164
+ existing.args += func.arguments;
165
+ }
166
+ } else {
167
+ pendingToolCalls.set(idx, {
168
+ id: tc.id || `tool_${Date.now()}_${idx}`,
169
+ name: func?.name || "",
170
+ args: func?.arguments || ""
171
+ });
172
+ }
173
+ }
174
+ }
175
+ if (choice.finish_reason) {
176
+ flushToolCalls();
177
+ const geminiUsage = chunk.usage ? {
178
+ promptTokenCount: chunk.usage.prompt_tokens || 0,
179
+ candidatesTokenCount: chunk.usage.completion_tokens || 0,
180
+ totalTokenCount: chunk.usage.total_tokens || 0,
181
+ cachedContentTokenCount: chunk.usage.prompt_tokens_details?.cached_tokens || 0,
182
+ thoughtsTokenCount: chunk.usage.output_tokens_details?.reasoning_tokens || 0
183
+ } : void 0;
184
+ emit({
185
+ responseId,
186
+ modelVersion: model,
187
+ candidates: [{
188
+ content: { parts: [{ text: "" }] },
189
+ finishReason: toGeminiFinishReason(choice.finish_reason) || "STOP"
190
+ }],
191
+ usageMetadata: geminiUsage
192
+ });
193
+ }
194
+ } catch (e) {
195
+ logger?.error(`Error parsing OpenAI stream chunk for Gemini conversion: ${e}`);
196
+ }
197
+ }
198
+ }
199
+ flushToolCalls();
200
+ } catch (e) {
201
+ if (!isClosed) controller.error(e);
202
+ } finally {
203
+ if (!isClosed) {
204
+ try {
205
+ controller.close();
206
+ } catch {
207
+ }
208
+ }
209
+ reader.releaseLock();
210
+ }
211
+ }
212
+ });
213
+ }
214
+ async function transformResponseIn(response, logger) {
215
+ const contentType = response.headers.get("Content-Type") ?? "";
216
+ if (contentType.includes("text/event-stream")) {
217
+ if (!response.body) {
218
+ throw new Error("Stream response body is null");
219
+ }
220
+ const geminiStream = convertOpenAIStreamToGemini(response.body, logger);
221
+ return new Response(geminiStream, {
222
+ headers: {
223
+ "Content-Type": "text/event-stream",
224
+ "Cache-Control": "no-cache",
225
+ Connection: "keep-alive"
226
+ }
227
+ });
228
+ }
229
+ const data = await response.json();
230
+ const geminiResponse = convertOpenAIResponseToGemini(data);
231
+ return new Response(JSON.stringify(geminiResponse), {
232
+ status: response.status,
233
+ statusText: response.statusText,
234
+ headers: { "Content-Type": "application/json" }
235
+ });
236
+ }
237
+
238
+ // src/transformer/transformers/utils/gemini.stream.ts
239
+ async function transformResponseOut(response, providerName, logger) {
240
+ const contentType = response.headers.get("Content-Type") ?? "";
241
+ if (contentType.includes("application/json")) {
242
+ return handleJsonResponse(response, providerName, logger);
243
+ } else if (contentType.includes("stream") || contentType.includes("text/event-stream")) {
244
+ return handleStreamResponse(response, providerName, logger);
245
+ }
246
+ return response;
247
+ }
248
+ async function handleJsonResponse(response, providerName, logger) {
249
+ const jsonResponse = await response.json();
250
+ logger?.debug(`${providerName} JSON response received`);
251
+ const parts = jsonResponse.candidates?.[0]?.content?.parts || [];
252
+ let thinkingContent = "";
253
+ let thinkingSignature = "";
254
+ const nonThinkingParts = [];
255
+ for (const part of parts) {
256
+ if (part.text && part.thought === true) {
257
+ thinkingContent += part.text;
258
+ } else {
259
+ nonThinkingParts.push(part);
260
+ }
261
+ }
262
+ thinkingSignature = parts.find((part) => part.thoughtSignature)?.thoughtSignature ?? "";
263
+ const toolCalls = nonThinkingParts.filter((part) => part.functionCall).map((part) => ({
264
+ id: part.functionCall?.id || `tool_${Math.random().toString(36).substring(2, 15)}`,
265
+ type: "function",
266
+ function: {
267
+ name: part.functionCall?.name ?? "",
268
+ arguments: JSON.stringify(part.functionCall?.args || {})
269
+ }
270
+ }));
271
+ const textContent = nonThinkingParts.filter((part) => part.text).map((part) => part.text).join("\n");
272
+ const openAIResponse = {
273
+ id: jsonResponse.responseId ?? "",
274
+ choices: [
275
+ {
276
+ finish_reason: (jsonResponse.candidates?.[0]?.finishReason ?? "").toLowerCase() || null,
277
+ index: 0,
278
+ message: {
279
+ content: textContent,
280
+ role: "assistant",
281
+ tool_calls: toolCalls.length > 0 ? toolCalls : void 0,
282
+ ...thinkingSignature && {
283
+ thinking: {
284
+ content: thinkingContent || "(no content)",
285
+ signature: thinkingSignature
286
+ }
287
+ }
288
+ }
289
+ }
290
+ ],
291
+ created: Math.floor(Date.now() / 1e3),
292
+ model: jsonResponse.modelVersion ?? "",
293
+ object: "chat.completion",
294
+ usage: {
295
+ completion_tokens: jsonResponse.usageMetadata?.candidatesTokenCount || 0,
296
+ prompt_tokens: jsonResponse.usageMetadata?.promptTokenCount || 0,
297
+ prompt_tokens_details: {
298
+ cached_tokens: jsonResponse.usageMetadata?.cachedContentTokenCount || 0
299
+ },
300
+ total_tokens: jsonResponse.usageMetadata?.totalTokenCount || 0,
301
+ output_tokens_details: {
302
+ reasoning_tokens: jsonResponse.usageMetadata?.thoughtsTokenCount || 0
303
+ }
304
+ }
305
+ };
306
+ return new Response(JSON.stringify(openAIResponse), {
307
+ status: response.status,
308
+ statusText: response.statusText,
309
+ headers: response.headers
310
+ });
311
+ }
312
+ function handleStreamResponse(response, providerName, logger) {
313
+ if (!response.body) {
314
+ return response;
315
+ }
316
+ const decoder = new TextDecoder();
317
+ const encoder = new TextEncoder();
318
+ let signatureSent = false;
319
+ let contentSent = false;
320
+ let hasThinkingContent = false;
321
+ let pendingContent = "";
322
+ let contentIndex = 0;
323
+ let toolCallIndex = -1;
324
+ const stream = new ReadableStream({
325
+ async start(controller) {
326
+ const reader = response.body.getReader();
327
+ let buffer = "";
328
+ const processLine = async (line) => {
329
+ if (!line.startsWith("data: ")) return;
330
+ const chunkStr = line.slice(6).trim();
331
+ if (!chunkStr) return;
332
+ logger?.debug(`${providerName} chunk: ${chunkStr.substring(0, 100)}...`);
333
+ try {
334
+ const chunk = JSON.parse(chunkStr);
335
+ if (!chunk.candidates?.[0]) {
336
+ logger?.debug("Invalid chunk structure");
337
+ return;
338
+ }
339
+ const candidate = chunk.candidates[0];
340
+ const parts = candidate.content?.parts || [];
341
+ parts.filter((part) => part.text && part.thought === true).forEach((part) => {
342
+ hasThinkingContent = true;
343
+ const thinkingChunk = createChunk({
344
+ responseId: chunk.responseId,
345
+ modelVersion: chunk.modelVersion,
346
+ contentIndex,
347
+ delta: {
348
+ role: "assistant",
349
+ content: null,
350
+ thinking: { content: part.text }
351
+ }
352
+ });
353
+ controller.enqueue(encoder.encode(`data: ${JSON.stringify(thinkingChunk)}
354
+
355
+ `));
356
+ });
357
+ const signature = parts.find((part) => part.thoughtSignature)?.thoughtSignature;
358
+ if (signature && !signatureSent) {
359
+ if (!hasThinkingContent) {
360
+ const thinkingChunk = createChunk({
361
+ responseId: chunk.responseId,
362
+ modelVersion: chunk.modelVersion,
363
+ contentIndex,
364
+ delta: {
365
+ role: "assistant",
366
+ content: null,
367
+ thinking: { content: "" }
368
+ }
369
+ });
370
+ controller.enqueue(encoder.encode(`data: ${JSON.stringify(thinkingChunk)}
371
+
372
+ `));
373
+ }
374
+ const signatureChunk = createChunk({
375
+ responseId: chunk.responseId,
376
+ modelVersion: chunk.modelVersion,
377
+ contentIndex,
378
+ delta: {
379
+ role: "assistant",
380
+ content: null,
381
+ thinking: { signature }
382
+ }
383
+ });
384
+ controller.enqueue(encoder.encode(`data: ${JSON.stringify(signatureChunk)}
385
+
386
+ `));
387
+ signatureSent = true;
388
+ contentIndex++;
389
+ if (pendingContent) {
390
+ const pendingChunk = createChunk({
391
+ responseId: chunk.responseId,
392
+ modelVersion: chunk.modelVersion,
393
+ contentIndex,
394
+ delta: { role: "assistant", content: pendingContent }
395
+ });
396
+ controller.enqueue(encoder.encode(`data: ${JSON.stringify(pendingChunk)}
397
+
398
+ `));
399
+ pendingContent = "";
400
+ contentSent = true;
401
+ }
402
+ }
403
+ const toolCalls = parts.filter((part) => part.functionCall).map((part) => ({
404
+ id: part.functionCall?.id || `ccr_tool_${Math.random().toString(36).substring(2, 15)}`,
405
+ type: "function",
406
+ function: {
407
+ name: part.functionCall?.name ?? "",
408
+ arguments: JSON.stringify(part.functionCall?.args || {})
409
+ }
410
+ }));
411
+ const textContent = parts.filter((part) => part.text && part.thought !== true).map((part) => part.text).join("\n");
412
+ if (!textContent && signatureSent && !contentSent) {
413
+ contentSent = true;
414
+ }
415
+ if (hasThinkingContent && textContent && !signatureSent) {
416
+ if (chunk.modelVersion?.includes("3")) {
417
+ pendingContent += textContent;
418
+ return;
419
+ } else {
420
+ const signatureChunk = createChunk({
421
+ responseId: chunk.responseId,
422
+ modelVersion: chunk.modelVersion,
423
+ contentIndex,
424
+ delta: {
425
+ role: "assistant",
426
+ content: null,
427
+ thinking: { signature: `ccr_${Date.now()}` }
428
+ }
429
+ });
430
+ controller.enqueue(encoder.encode(`data: ${JSON.stringify(signatureChunk)}
431
+
432
+ `));
433
+ signatureSent = true;
434
+ }
435
+ }
436
+ if (textContent) {
437
+ if (!pendingContent) contentIndex++;
438
+ const contentChunk = createChunk({
439
+ responseId: chunk.responseId,
440
+ modelVersion: chunk.modelVersion,
441
+ contentIndex,
442
+ delta: { role: "assistant", content: textContent },
443
+ finishReason: candidate.finishReason,
444
+ usageMetadata: chunk.usageMetadata,
445
+ groundingMetadata: candidate.groundingMetadata
446
+ });
447
+ controller.enqueue(encoder.encode(`data: ${JSON.stringify(contentChunk)}
448
+
449
+ `));
450
+ contentSent = true;
451
+ }
452
+ if (toolCalls.length > 0) {
453
+ for (const tool of toolCalls) {
454
+ contentIndex++;
455
+ toolCallIndex++;
456
+ const toolChunk = createChunk({
457
+ responseId: chunk.responseId,
458
+ modelVersion: chunk.modelVersion,
459
+ contentIndex,
460
+ delta: {
461
+ role: "assistant",
462
+ tool_calls: [{ ...tool, index: toolCallIndex }]
463
+ },
464
+ finishReason: candidate.finishReason,
465
+ usageMetadata: chunk.usageMetadata,
466
+ groundingMetadata: candidate.groundingMetadata
467
+ });
468
+ controller.enqueue(encoder.encode(`data: ${JSON.stringify(toolChunk)}
469
+
470
+ `));
471
+ }
472
+ contentSent = true;
473
+ }
474
+ } catch (_error) {
475
+ logger?.error(`Error parsing ${providerName} stream chunk: ${chunkStr}`);
476
+ }
477
+ };
478
+ try {
479
+ while (true) {
480
+ const { done, value } = await reader.read();
481
+ if (done) {
482
+ if (buffer) await processLine(buffer);
483
+ break;
484
+ }
485
+ buffer += decoder.decode(value, { stream: true });
486
+ const lines = buffer.split("\n");
487
+ buffer = lines.pop() || "";
488
+ for (const line of lines) {
489
+ await processLine(line);
490
+ }
491
+ }
492
+ } catch (error) {
493
+ controller.error(error);
494
+ } finally {
495
+ controller.close();
496
+ }
497
+ }
498
+ });
499
+ return new Response(stream, {
500
+ status: response.status,
501
+ statusText: response.statusText,
502
+ headers: response.headers
503
+ });
504
+ }
505
+ function createChunk(options) {
506
+ const {
507
+ responseId,
508
+ modelVersion,
509
+ contentIndex,
510
+ delta,
511
+ finishReason,
512
+ usageMetadata,
513
+ groundingMetadata
514
+ } = options;
515
+ const chunk = {
516
+ choices: [
517
+ {
518
+ delta,
519
+ finish_reason: finishReason?.toLowerCase() || null,
520
+ index: contentIndex,
521
+ logprobs: null
522
+ }
523
+ ],
524
+ created: Math.floor(Date.now() / 1e3),
525
+ id: responseId || "",
526
+ model: modelVersion || "",
527
+ object: "chat.completion.chunk",
528
+ system_fingerprint: "fp_a49d71b8a1"
529
+ };
530
+ if (usageMetadata) {
531
+ chunk.usage = {
532
+ completion_tokens: usageMetadata.candidatesTokenCount || 0,
533
+ prompt_tokens: usageMetadata.promptTokenCount || 0,
534
+ prompt_tokens_details: {
535
+ cached_tokens: usageMetadata.cachedContentTokenCount || 0
536
+ },
537
+ total_tokens: usageMetadata.totalTokenCount || 0,
538
+ output_tokens_details: {
539
+ reasoning_tokens: usageMetadata.thoughtsTokenCount || 0
540
+ }
541
+ };
542
+ }
543
+ if (groundingMetadata?.groundingChunks?.length) {
544
+ const annotations = groundingMetadata.groundingChunks.map((groundingChunk, index) => {
545
+ const support = groundingMetadata.groundingSupports?.find(
546
+ (s) => s.groundingChunkIndices?.includes(index)
547
+ );
548
+ return {
549
+ type: "url_citation",
550
+ url_citation: {
551
+ url: groundingChunk.web?.uri || "",
552
+ title: groundingChunk.web?.title || "",
553
+ content: support?.segment?.text || "",
554
+ start_index: support?.segment?.startIndex || 0,
555
+ end_index: support?.segment?.endIndex || 0
556
+ }
557
+ };
558
+ });
559
+ chunk.choices[0].delta.annotations = annotations;
560
+ }
561
+ return chunk;
562
+ }
563
+
564
+ // src/transformer/transformers/utils/gemini.schema.ts
565
+ var GeminiType = {
566
+ TYPE_UNSPECIFIED: "TYPE_UNSPECIFIED",
567
+ STRING: "STRING",
568
+ NUMBER: "NUMBER",
569
+ INTEGER: "INTEGER",
570
+ BOOLEAN: "BOOLEAN",
571
+ ARRAY: "ARRAY",
572
+ OBJECT: "OBJECT",
573
+ NULL: "NULL"
574
+ };
575
+ function flattenTypeArrayToAnyOf(typeList, resultingSchema) {
576
+ if (typeList.includes("null")) {
577
+ resultingSchema.nullable = true;
578
+ }
579
+ const listWithoutNull = typeList.filter((type) => type !== "null");
580
+ if (listWithoutNull.length === 1) {
581
+ const upperCaseType = listWithoutNull[0].toUpperCase();
582
+ resultingSchema.type = Object.values(GeminiType).includes(upperCaseType) ? upperCaseType : GeminiType.TYPE_UNSPECIFIED;
583
+ } else {
584
+ resultingSchema.anyOf = listWithoutNull.map((typeName) => {
585
+ const upperCaseType = typeName.toUpperCase();
586
+ return {
587
+ type: Object.values(GeminiType).includes(upperCaseType) ? upperCaseType : GeminiType.TYPE_UNSPECIFIED
588
+ };
589
+ });
590
+ }
591
+ }
592
+ function processJsonSchema(jsonSchema) {
593
+ const genAISchema = {};
594
+ const schemaFieldNames = ["items"];
595
+ const listSchemaFieldNames = ["anyOf"];
596
+ const dictSchemaFieldNames = ["properties"];
597
+ let workingSchema = jsonSchema;
598
+ if (workingSchema.type && workingSchema.anyOf) {
599
+ throw new Error("type and anyOf cannot be both populated.");
600
+ }
601
+ const incomingAnyOf = workingSchema.anyOf;
602
+ if (incomingAnyOf && Array.isArray(incomingAnyOf) && incomingAnyOf.length === 2) {
603
+ if (incomingAnyOf[0]?.type === "null") {
604
+ genAISchema.nullable = true;
605
+ workingSchema = incomingAnyOf[1];
606
+ } else if (incomingAnyOf[1]?.type === "null") {
607
+ genAISchema.nullable = true;
608
+ workingSchema = incomingAnyOf[0];
609
+ }
610
+ }
611
+ if (workingSchema.type && Array.isArray(workingSchema.type)) {
612
+ flattenTypeArrayToAnyOf(workingSchema.type, genAISchema);
613
+ }
614
+ for (const [fieldName, fieldValue] of Object.entries(workingSchema)) {
615
+ if (fieldValue == null) {
616
+ continue;
617
+ }
618
+ if (fieldName === "type") {
619
+ if (fieldValue === "null") {
620
+ throw new Error("type: null cannot be the only possible type for the field.");
621
+ }
622
+ if (Array.isArray(fieldValue)) {
623
+ continue;
624
+ }
625
+ const upperCaseValue = fieldValue.toUpperCase();
626
+ genAISchema.type = Object.values(GeminiType).includes(upperCaseValue) ? upperCaseValue : GeminiType.TYPE_UNSPECIFIED;
627
+ } else if (schemaFieldNames.includes(fieldName)) {
628
+ genAISchema[fieldName] = processJsonSchema(fieldValue);
629
+ } else if (listSchemaFieldNames.includes(fieldName)) {
630
+ const listValue = [];
631
+ for (const item of fieldValue) {
632
+ if (item.type === "null") {
633
+ genAISchema.nullable = true;
634
+ continue;
635
+ }
636
+ listValue.push(processJsonSchema(item));
637
+ }
638
+ genAISchema[fieldName] = listValue;
639
+ } else if (dictSchemaFieldNames.includes(fieldName)) {
640
+ const dictValue = {};
641
+ for (const [key, value] of Object.entries(fieldValue)) {
642
+ dictValue[key] = processJsonSchema(value);
643
+ }
644
+ genAISchema[fieldName] = dictValue;
645
+ } else {
646
+ if (fieldName === "additionalProperties") {
647
+ continue;
648
+ }
649
+ genAISchema[fieldName] = fieldValue;
650
+ }
651
+ }
652
+ return genAISchema;
653
+ }
654
+ function transformTool(tool) {
655
+ const functionDeclarations = tool.functionDeclarations;
656
+ if (functionDeclarations) {
657
+ for (const functionDeclaration of functionDeclarations) {
658
+ if (functionDeclaration.parameters) {
659
+ const params = functionDeclaration.parameters;
660
+ if (!Object.keys(params).includes("$schema")) {
661
+ functionDeclaration.parameters = processJsonSchema(params);
662
+ } else {
663
+ if (!functionDeclaration.parametersJsonSchema) {
664
+ functionDeclaration.parametersJsonSchema = functionDeclaration.parameters;
665
+ delete functionDeclaration.parameters;
666
+ }
667
+ }
668
+ }
669
+ if (functionDeclaration.response) {
670
+ const response = functionDeclaration.response;
671
+ if (!Object.keys(response).includes("$schema")) {
672
+ functionDeclaration.response = processJsonSchema(response);
673
+ } else {
674
+ if (!functionDeclaration.responseJsonSchema) {
675
+ functionDeclaration.responseJsonSchema = functionDeclaration.response;
676
+ delete functionDeclaration.response;
677
+ }
678
+ }
679
+ }
680
+ }
681
+ }
682
+ return tool;
683
+ }
684
+
685
+ // src/transformer/transformers/utils/gemini.util.ts
686
+ function buildRequestBody(request) {
687
+ const tools = [];
688
+ const functionDeclarations = request.tools?.filter((tool) => tool.function.name !== "web_search")?.map((tool) => ({
689
+ name: tool.function.name,
690
+ description: tool.function.description,
691
+ parametersJsonSchema: tool.function.parameters
692
+ }));
693
+ if (functionDeclarations?.length) {
694
+ tools.push(
695
+ transformTool({
696
+ functionDeclarations
697
+ })
698
+ );
699
+ }
700
+ const webSearch = request.tools?.find((tool) => tool.function.name === "web_search");
701
+ if (webSearch) {
702
+ tools.push({ googleSearch: {} });
703
+ }
704
+ const contents = [];
705
+ const toolResponses = request.messages.filter((item) => item.role === "tool");
706
+ request.messages.filter((item) => item.role !== "tool").forEach((message) => {
707
+ let role;
708
+ if (message.role === "assistant") {
709
+ role = "model";
710
+ } else if (["user", "system"].includes(message.role)) {
711
+ role = "user";
712
+ } else {
713
+ role = "user";
714
+ }
715
+ const parts = [];
716
+ if (typeof message.content === "string") {
717
+ const part = { text: message.content };
718
+ if (message.thinking?.signature) {
719
+ part.thoughtSignature = message.thinking.signature;
720
+ }
721
+ parts.push(part);
722
+ } else if (Array.isArray(message.content)) {
723
+ for (const content of message.content) {
724
+ if (content.type === "text") {
725
+ parts.push({ text: content.text || "" });
726
+ } else if (content.type === "image_url") {
727
+ const imageUrl = content.image_url?.url ?? "";
728
+ if (imageUrl.startsWith("http")) {
729
+ parts.push({
730
+ file_data: {
731
+ mime_type: content.media_type,
732
+ file_uri: imageUrl
733
+ }
734
+ });
735
+ } else {
736
+ const data = imageUrl.split(",").pop() || imageUrl;
737
+ parts.push({
738
+ inlineData: {
739
+ mime_type: content.media_type || "image/png",
740
+ data
741
+ }
742
+ });
743
+ }
744
+ }
745
+ }
746
+ } else if (message.content && typeof message.content === "object") {
747
+ const contentObj = message.content;
748
+ if (contentObj.text) {
749
+ parts.push({ text: contentObj.text });
750
+ } else {
751
+ parts.push({ text: JSON.stringify(message.content) });
752
+ }
753
+ }
754
+ if (Array.isArray(message.tool_calls)) {
755
+ for (let index = 0; index < message.tool_calls.length; index++) {
756
+ const toolCall = message.tool_calls[index];
757
+ const functionCallPart = {
758
+ functionCall: {
759
+ id: toolCall.id || `tool_${Math.random().toString(36).substring(2, 15)}`,
760
+ name: toolCall.function.name,
761
+ args: JSON.parse(toolCall.function.arguments || "{}")
762
+ }
763
+ };
764
+ if (index === 0 && message.thinking?.signature) {
765
+ functionCallPart.thoughtSignature = message.thinking.signature;
766
+ }
767
+ parts.push(functionCallPart);
768
+ }
769
+ }
770
+ if (parts.length === 0) {
771
+ parts.push({ text: "" });
772
+ }
773
+ contents.push({ role, parts });
774
+ if (role === "model" && message.tool_calls) {
775
+ const functionResponses = message.tool_calls.map(
776
+ (tool) => {
777
+ const response = toolResponses.find((item) => item.tool_call_id === tool.id);
778
+ return {
779
+ functionResponse: {
780
+ name: tool.function?.name ?? "",
781
+ response: { result: response?.content }
782
+ }
783
+ };
784
+ }
785
+ );
786
+ contents.push({
787
+ role: "user",
788
+ parts: functionResponses
789
+ });
790
+ }
791
+ });
792
+ const generationConfig = {};
793
+ if (request.reasoning?.effort && request.reasoning.effort !== "none") {
794
+ generationConfig.thinkingConfig = {
795
+ includeThoughts: true
796
+ };
797
+ if (request.model.includes("gemini-3")) {
798
+ generationConfig.thinkingConfig.thinkingLevel = request.reasoning.effort;
799
+ } else {
800
+ const thinkingBudgets = request.model.includes("pro") ? [128, 32768] : [0, 24576];
801
+ const maxTokens = request.reasoning.max_tokens;
802
+ if (typeof maxTokens !== "undefined") {
803
+ let thinkingBudget;
804
+ if (maxTokens >= thinkingBudgets[0] && maxTokens <= thinkingBudgets[1]) {
805
+ thinkingBudget = maxTokens;
806
+ } else if (maxTokens < thinkingBudgets[0]) {
807
+ thinkingBudget = thinkingBudgets[0];
808
+ } else {
809
+ thinkingBudget = thinkingBudgets[1];
810
+ }
811
+ generationConfig.thinkingConfig.thinkingBudget = thinkingBudget;
812
+ }
813
+ }
814
+ }
815
+ const body = {
816
+ contents,
817
+ tools: tools.length > 0 ? tools : void 0,
818
+ generationConfig: Object.keys(generationConfig).length > 0 ? generationConfig : void 0
819
+ };
820
+ if (request.tool_choice) {
821
+ const toolConfig = {
822
+ functionCallingConfig: {}
823
+ };
824
+ if (request.tool_choice === "auto") {
825
+ toolConfig.functionCallingConfig.mode = "auto";
826
+ } else if (request.tool_choice === "none") {
827
+ toolConfig.functionCallingConfig.mode = "none";
828
+ } else if (request.tool_choice === "required") {
829
+ toolConfig.functionCallingConfig.mode = "any";
830
+ } else if (typeof request.tool_choice === "object" && request.tool_choice.function?.name) {
831
+ toolConfig.functionCallingConfig.mode = "any";
832
+ toolConfig.functionCallingConfig.allowedFunctionNames = [
833
+ request.tool_choice.function.name
834
+ ];
835
+ }
836
+ body.toolConfig = toolConfig;
837
+ }
838
+ return body;
839
+ }
840
+ function transformRequestOut(request) {
841
+ const contents = request.contents;
842
+ const tools = request.tools;
843
+ const model = request.model;
844
+ const maxTokens = request.max_tokens;
845
+ const temperature = request.temperature;
846
+ const stream = request.stream;
847
+ const toolChoice = request.tool_choice;
848
+ const unifiedRequest = {
849
+ messages: [],
850
+ model,
851
+ max_tokens: maxTokens,
852
+ temperature,
853
+ stream,
854
+ tool_choice: toolChoice
855
+ };
856
+ if (Array.isArray(contents)) {
857
+ for (const content of contents) {
858
+ if (typeof content === "string") {
859
+ unifiedRequest.messages.push({
860
+ role: "user",
861
+ content
862
+ });
863
+ } else if ("text" in content && typeof content.text === "string") {
864
+ unifiedRequest.messages.push({
865
+ role: "user",
866
+ content: content.text || null
867
+ });
868
+ } else if ("role" in content && content.role === "user") {
869
+ const geminiContent = content;
870
+ unifiedRequest.messages.push({
871
+ role: "user",
872
+ content: geminiContent.parts?.map((part) => ({
873
+ type: "text",
874
+ text: part.text || ""
875
+ })) || []
876
+ });
877
+ } else if (content.role === "model") {
878
+ unifiedRequest.messages.push({
879
+ role: "assistant",
880
+ content: content.parts?.map((part) => ({
881
+ type: "text",
882
+ text: part.text || ""
883
+ })) || []
884
+ });
885
+ }
886
+ }
887
+ }
888
+ if (Array.isArray(tools)) {
889
+ unifiedRequest.tools = [];
890
+ for (const tool of tools) {
891
+ if (Array.isArray(tool.functionDeclarations)) {
892
+ for (const funcDecl of tool.functionDeclarations) {
893
+ unifiedRequest.tools.push({
894
+ type: "function",
895
+ function: {
896
+ name: funcDecl.name,
897
+ description: funcDecl.description ?? "",
898
+ parameters: funcDecl.parameters ?? {}
899
+ }
900
+ });
901
+ }
902
+ }
903
+ }
904
+ }
905
+ return unifiedRequest;
906
+ }
907
+
908
+ // src/transformer/transformers/GeminiCodeAssistTransformer.ts
909
+ var DEFAULT_CODE_ASSIST_ENDPOINT = "https://cloudcode-pa.googleapis.com";
910
+ var DEFAULT_CODE_ASSIST_API_VERSION = "v1internal";
911
+ function resolveCodeAssistEndpoint() {
912
+ return (process.env.CODE_ASSIST_ENDPOINT || DEFAULT_CODE_ASSIST_ENDPOINT).replace(/\/+$/, "");
913
+ }
914
+ function resolveCodeAssistApiVersion() {
915
+ return process.env.CODE_ASSIST_API_VERSION || DEFAULT_CODE_ASSIST_API_VERSION;
916
+ }
917
+ function buildCodeAssistUrl(stream) {
918
+ const base = resolveCodeAssistEndpoint();
919
+ const version = resolveCodeAssistApiVersion();
920
+ const method = stream ? "streamGenerateContent?alt=sse" : "generateContent";
921
+ return `${base}/${version}:${method}`;
922
+ }
923
+ function generateUserPromptId() {
924
+ const c = globalThis.crypto;
925
+ if (c?.randomUUID) return c.randomUUID();
926
+ return `omnicross-${Date.now()}-${Math.random().toString(36).slice(2, 10)}`;
927
+ }
928
+ function peelResponseEnvelope(parsed) {
929
+ if (parsed && typeof parsed === "object" && "response" in parsed) {
930
+ return parsed.response;
931
+ }
932
+ return parsed;
933
+ }
934
+ async function unwrapCodeAssistResponse(response) {
935
+ const contentType = response.headers.get("Content-Type") ?? "";
936
+ if (contentType.includes("stream") || contentType.includes("text/event-stream")) {
937
+ const sourceBody = response.body;
938
+ if (!sourceBody) return response;
939
+ const decoder = new TextDecoder();
940
+ const encoder = new TextEncoder();
941
+ const peeled = new ReadableStream({
942
+ async start(controller) {
943
+ const reader = sourceBody.getReader();
944
+ let buffer = "";
945
+ const processLine = (line) => {
946
+ if (!line.startsWith("data:")) {
947
+ if (line.length > 0) controller.enqueue(encoder.encode(`${line}
948
+ `));
949
+ return;
950
+ }
951
+ const payload = line.slice(line.indexOf(":") + 1).trim();
952
+ if (!payload || payload === "[DONE]") {
953
+ controller.enqueue(encoder.encode(`${line}
954
+ `));
955
+ return;
956
+ }
957
+ try {
958
+ const parsed = JSON.parse(payload);
959
+ const inner2 = peelResponseEnvelope(parsed);
960
+ controller.enqueue(encoder.encode(`data: ${JSON.stringify(inner2)}
961
+ `));
962
+ } catch {
963
+ controller.enqueue(encoder.encode(`${line}
964
+ `));
965
+ }
966
+ };
967
+ try {
968
+ while (true) {
969
+ const { done, value } = await reader.read();
970
+ if (done) {
971
+ if (buffer) processLine(buffer);
972
+ break;
973
+ }
974
+ buffer += decoder.decode(value, { stream: true });
975
+ const lines = buffer.split("\n");
976
+ buffer = lines.pop() || "";
977
+ for (const line of lines) processLine(line);
978
+ }
979
+ } catch (err) {
980
+ controller.error(err);
981
+ } finally {
982
+ controller.close();
983
+ }
984
+ }
985
+ });
986
+ return new Response(peeled, {
987
+ status: response.status,
988
+ statusText: response.statusText,
989
+ headers: response.headers
990
+ });
991
+ }
992
+ const raw = await response.json().catch(() => null);
993
+ const inner = peelResponseEnvelope(raw);
994
+ return new Response(JSON.stringify(inner), {
995
+ status: response.status,
996
+ statusText: response.statusText,
997
+ headers: response.headers
998
+ });
999
+ }
1000
+ var GeminiCodeAssistTransformer = class {
1001
+ static TransformerName = "gemini-code-assist";
1002
+ name = "gemini-code-assist";
1003
+ logger;
1004
+ /** Code Assist has no fixed `/models/:modelAndAction` endpoint pattern — the
1005
+ * URL is built per-request in `transformRequestIn`. Left undefined so the
1006
+ * TransformerService does NOT treat this as an endpoint (reverse) transformer. */
1007
+ endPoint = void 0;
1008
+ /**
1009
+ * unified → Code Assist envelope.
1010
+ *
1011
+ * Builds the inner public-Gemini body via the shared `buildRequestBody`, then
1012
+ * wraps it as `{ model, project, user_prompt_id, request: <inner> }` and sets
1013
+ * the Code Assist URL + Bearer-only headers.
1014
+ *
1015
+ * The resolved Code Assist `project` is threaded in via `provider.geminiProject`
1016
+ * (stashed by the subscription dispatch seam — see SubscriptionDispatcher /
1017
+ * openaiResponsesIngress). `undefined` is the valid fresh free-tier value.
1018
+ */
1019
+ async transformRequestIn(request, provider, _context) {
1020
+ const inner = buildRequestBody(request);
1021
+ const envelope = {
1022
+ model: request.model,
1023
+ project: provider.geminiProject,
1024
+ user_prompt_id: generateUserPromptId(),
1025
+ request: inner
1026
+ };
1027
+ const url = buildCodeAssistUrl(Boolean(request.stream));
1028
+ const headers = {
1029
+ "x-goog-api-key": void 0,
1030
+ "X-Goog-Api-Key": void 0
1031
+ };
1032
+ return {
1033
+ body: envelope,
1034
+ config: { url, headers }
1035
+ };
1036
+ }
1037
+ /**
1038
+ * Code Assist request → unified (endpoint-decode parity with GeminiTransformer).
1039
+ * Peels the top-level `request` envelope first, then delegates to the shared
1040
+ * gemini request decoder. Not used on the subscription dispatch path (the
1041
+ * endpoint transformer there is Anthropic/OpenAI-Response), but provided for
1042
+ * completeness so this transformer is a drop-in for the gemini one.
1043
+ */
1044
+ async transformRequestOut(request, _context) {
1045
+ const r = request;
1046
+ const inner = r && typeof r === "object" && "request" in r ? r.request : r;
1047
+ if (inner && typeof inner === "object" && !("model" in inner) && "model" in r) {
1048
+ inner.model = r.model;
1049
+ }
1050
+ return transformRequestOut(inner);
1051
+ }
1052
+ /**
1053
+ * Code Assist response → OpenAI-compatible. PEEL the `.response` envelope from
1054
+ * the body / each SSE chunk, then DELEGATE to the existing gemini parser.
1055
+ */
1056
+ async transformResponseOut(response, _context) {
1057
+ const unwrapped = await unwrapCodeAssistResponse(response);
1058
+ return transformResponseOut(unwrapped, this.name, this.logger);
1059
+ }
1060
+ /**
1061
+ * OpenAI-compatible response → Code Assist (endpoint-encode parity). Re-wraps
1062
+ * the standard gemini response under the top-level `response` key after the
1063
+ * shared gemini encoder produces a public-Gemini body. Symmetric with
1064
+ * `transformResponseOut`'s peel.
1065
+ */
1066
+ async transformResponseIn(response, _context) {
1067
+ const geminiResponse = await transformResponseIn(response, this.logger);
1068
+ const contentType = geminiResponse.headers.get("Content-Type") ?? "";
1069
+ if (contentType.includes("text/event-stream")) {
1070
+ return geminiResponse;
1071
+ }
1072
+ const data = await geminiResponse.json().catch(() => null);
1073
+ return new Response(JSON.stringify({ response: data }), {
1074
+ status: geminiResponse.status,
1075
+ statusText: geminiResponse.statusText,
1076
+ headers: { "Content-Type": "application/json" }
1077
+ });
1078
+ }
1079
+ };
1080
+ export {
1081
+ GeminiCodeAssistTransformer,
1082
+ buildCodeAssistUrl,
1083
+ resolveCodeAssistApiVersion,
1084
+ resolveCodeAssistEndpoint
1085
+ };