@jsonstudio/llms 0.6.954 → 0.6.1172

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 (134) hide show
  1. package/dist/conversion/hub/operation-table/operation-table-runner.d.ts +18 -0
  2. package/dist/conversion/hub/operation-table/operation-table-runner.js +158 -0
  3. package/dist/conversion/hub/operation-table/semantic-mappers/anthropic-mapper.d.ts +8 -0
  4. package/dist/conversion/hub/operation-table/semantic-mappers/anthropic-mapper.js +303 -0
  5. package/dist/conversion/hub/operation-table/semantic-mappers/chat-mapper.d.ts +8 -0
  6. package/dist/conversion/hub/operation-table/semantic-mappers/chat-mapper.js +413 -0
  7. package/dist/conversion/hub/operation-table/semantic-mappers/gemini-mapper.d.ts +7 -0
  8. package/dist/conversion/hub/operation-table/semantic-mappers/gemini-mapper.js +841 -0
  9. package/dist/conversion/hub/operation-table/semantic-mappers/responses-mapper.d.ts +21 -0
  10. package/dist/conversion/hub/operation-table/semantic-mappers/responses-mapper.js +535 -0
  11. package/dist/conversion/hub/ops/operations.d.ts +19 -0
  12. package/dist/conversion/hub/ops/operations.js +126 -0
  13. package/dist/conversion/hub/pipeline/hub-pipeline.d.ts +9 -0
  14. package/dist/conversion/hub/pipeline/hub-pipeline.js +489 -19
  15. package/dist/conversion/hub/pipeline/stages/req_inbound/req_inbound_stage2_semantic_map/index.js +6 -0
  16. package/dist/conversion/hub/pipeline/stages/req_outbound/req_outbound_stage1_semantic_map/index.js +11 -0
  17. package/dist/conversion/hub/policy/policy-engine.js +41 -9
  18. package/dist/conversion/hub/policy/protocol-spec.d.ts +25 -0
  19. package/dist/conversion/hub/policy/protocol-spec.js +73 -23
  20. package/dist/conversion/hub/process/chat-process.js +252 -41
  21. package/dist/conversion/hub/response/provider-response.js +175 -2
  22. package/dist/conversion/hub/response/response-runtime.js +1 -1
  23. package/dist/conversion/hub/semantic-mappers/anthropic-mapper.d.ts +1 -8
  24. package/dist/conversion/hub/semantic-mappers/anthropic-mapper.js +1 -365
  25. package/dist/conversion/hub/semantic-mappers/chat-mapper.d.ts +1 -8
  26. package/dist/conversion/hub/semantic-mappers/chat-mapper.js +1 -467
  27. package/dist/conversion/hub/semantic-mappers/gemini-mapper.d.ts +1 -7
  28. package/dist/conversion/hub/semantic-mappers/gemini-mapper.js +1 -903
  29. package/dist/conversion/hub/semantic-mappers/responses-mapper.d.ts +1 -21
  30. package/dist/conversion/hub/semantic-mappers/responses-mapper.js +1 -593
  31. package/dist/conversion/hub/tool-surface/tool-surface-engine.d.ts +18 -0
  32. package/dist/conversion/hub/tool-surface/tool-surface-engine.js +571 -0
  33. package/dist/conversion/responses/responses-openai-bridge.js +14 -2
  34. package/dist/conversion/shared/bridge-message-utils.js +2 -8
  35. package/dist/conversion/shared/bridge-policies.js +5 -105
  36. package/dist/conversion/shared/gemini-tool-utils.js +89 -15
  37. package/dist/conversion/shared/protocol-field-allowlists.d.ts +7 -0
  38. package/dist/conversion/shared/protocol-field-allowlists.js +145 -0
  39. package/dist/conversion/shared/reasoning-tool-normalizer.js +4 -2
  40. package/dist/conversion/shared/snapshot-hooks.js +166 -3
  41. package/dist/conversion/shared/text-markup-normalizer.d.ts +2 -0
  42. package/dist/conversion/shared/text-markup-normalizer.js +345 -9
  43. package/dist/conversion/shared/thought-signature-validator.d.ts +52 -0
  44. package/dist/conversion/shared/thought-signature-validator.js +170 -0
  45. package/dist/conversion/shared/tool-argument-repairer.d.ts +39 -0
  46. package/dist/conversion/shared/tool-argument-repairer.js +56 -0
  47. package/dist/conversion/shared/tool-call-id-manager.d.ts +113 -0
  48. package/dist/conversion/shared/tool-call-id-manager.js +231 -0
  49. package/dist/conversion/shared/tool-canonicalizer.js +2 -11
  50. package/dist/router/virtual-router/bootstrap.js +70 -5
  51. package/dist/router/virtual-router/context-advisor.d.ts +4 -0
  52. package/dist/router/virtual-router/context-advisor.js +3 -0
  53. package/dist/router/virtual-router/context-weighted.d.ts +31 -0
  54. package/dist/router/virtual-router/context-weighted.js +54 -0
  55. package/dist/router/virtual-router/engine-selection.js +284 -47
  56. package/dist/router/virtual-router/engine.d.ts +3 -0
  57. package/dist/router/virtual-router/engine.js +142 -33
  58. package/dist/router/virtual-router/health-weighted.d.ts +25 -0
  59. package/dist/router/virtual-router/health-weighted.js +63 -0
  60. package/dist/router/virtual-router/load-balancer.d.ts +2 -0
  61. package/dist/router/virtual-router/load-balancer.js +45 -16
  62. package/dist/router/virtual-router/routing-instructions.js +17 -1
  63. package/dist/router/virtual-router/sticky-session-store.js +136 -24
  64. package/dist/router/virtual-router/stop-message-file-resolver.d.ts +1 -0
  65. package/dist/router/virtual-router/stop-message-file-resolver.js +74 -0
  66. package/dist/router/virtual-router/stop-message-state-sync.d.ts +15 -0
  67. package/dist/router/virtual-router/stop-message-state-sync.js +57 -0
  68. package/dist/router/virtual-router/types.d.ts +98 -0
  69. package/dist/servertool/clock/config.d.ts +7 -0
  70. package/dist/servertool/clock/config.js +27 -0
  71. package/dist/servertool/clock/daemon.d.ts +3 -0
  72. package/dist/servertool/clock/daemon.js +79 -0
  73. package/dist/servertool/clock/io.d.ts +2 -0
  74. package/dist/servertool/clock/io.js +13 -0
  75. package/dist/servertool/clock/paths.d.ts +4 -0
  76. package/dist/servertool/clock/paths.js +25 -0
  77. package/dist/servertool/clock/session-store.d.ts +3 -0
  78. package/dist/servertool/clock/session-store.js +56 -0
  79. package/dist/servertool/clock/state.d.ts +5 -0
  80. package/dist/servertool/clock/state.js +62 -0
  81. package/dist/servertool/clock/task-store.d.ts +5 -0
  82. package/dist/servertool/clock/task-store.js +4 -0
  83. package/dist/servertool/clock/tasks.d.ts +17 -0
  84. package/dist/servertool/clock/tasks.js +221 -0
  85. package/dist/servertool/clock/types.d.ts +36 -0
  86. package/dist/servertool/clock/types.js +1 -0
  87. package/dist/servertool/engine.d.ts +2 -0
  88. package/dist/servertool/engine.js +161 -7
  89. package/dist/servertool/followup-shadow.d.ts +16 -0
  90. package/dist/servertool/followup-shadow.js +145 -0
  91. package/dist/servertool/handlers/apply-patch-guard.js +1 -265
  92. package/dist/servertool/handlers/clock-auto.d.ts +1 -0
  93. package/dist/servertool/handlers/clock-auto.js +160 -0
  94. package/dist/servertool/handlers/clock.d.ts +1 -0
  95. package/dist/servertool/handlers/clock.js +197 -0
  96. package/dist/servertool/handlers/exec-command-guard.js +7 -555
  97. package/dist/servertool/handlers/followup-request-builder.d.ts +15 -7
  98. package/dist/servertool/handlers/followup-request-builder.js +248 -28
  99. package/dist/servertool/handlers/gemini-empty-reply-continue.js +62 -169
  100. package/dist/servertool/handlers/iflow-model-error-retry.js +18 -28
  101. package/dist/servertool/handlers/recursive-detection-guard.d.ts +1 -0
  102. package/dist/servertool/handlers/recursive-detection-guard.js +333 -0
  103. package/dist/servertool/handlers/stop-message-auto.js +47 -175
  104. package/dist/servertool/handlers/vision.d.ts +7 -1
  105. package/dist/servertool/handlers/vision.js +61 -117
  106. package/dist/servertool/handlers/web-search.d.ts +7 -1
  107. package/dist/servertool/handlers/web-search.js +122 -105
  108. package/dist/servertool/reenter-backend.d.ts +23 -0
  109. package/dist/servertool/reenter-backend.js +18 -0
  110. package/dist/servertool/server-side-tools.d.ts +3 -2
  111. package/dist/servertool/server-side-tools.js +64 -10
  112. package/dist/servertool/types.d.ts +92 -3
  113. package/dist/sse/json-to-sse/event-generators/responses.js +3 -21
  114. package/dist/sse/shared/serializers/responses-event-serializer.d.ts +8 -0
  115. package/dist/sse/shared/serializers/responses-event-serializer.js +19 -0
  116. package/dist/sse/shared/writer.js +24 -7
  117. package/dist/tools/apply-patch/execution-capturer.js +3 -1
  118. package/dist/tools/apply-patch/json/parse-loose.d.ts +3 -0
  119. package/dist/tools/apply-patch/json/parse-loose.js +139 -0
  120. package/dist/tools/apply-patch/patch-text/context-diff.d.ts +1 -0
  121. package/dist/tools/apply-patch/patch-text/context-diff.js +173 -0
  122. package/dist/tools/apply-patch/patch-text/git-diff.d.ts +1 -0
  123. package/dist/tools/apply-patch/patch-text/git-diff.js +138 -0
  124. package/dist/tools/apply-patch/patch-text/looks-like-patch.d.ts +1 -0
  125. package/dist/tools/apply-patch/patch-text/looks-like-patch.js +13 -0
  126. package/dist/tools/apply-patch/patch-text/normalize.d.ts +3 -0
  127. package/dist/tools/apply-patch/patch-text/normalize.js +262 -0
  128. package/dist/tools/apply-patch/structured/coercion.d.ts +3 -0
  129. package/dist/tools/apply-patch/structured/coercion.js +82 -0
  130. package/dist/tools/apply-patch/validation/shared.d.ts +3 -0
  131. package/dist/tools/apply-patch/validation/shared.js +6 -0
  132. package/dist/tools/apply-patch/validator.d.ts +2 -2
  133. package/dist/tools/apply-patch/validator.js +6 -556
  134. package/package.json +1 -1
@@ -1,903 +1 @@
1
- import { isJsonObject, jsonClone } from '../types/json.js';
2
- import { buildOpenAIChatFromGeminiRequest } from '../../codecs/gemini-openai-codec.js';
3
- import { createBridgeActionState, runBridgeActionPipeline } from '../../shared/bridge-actions.js';
4
- import { resolveBridgePolicy, resolvePolicyActions } from '../../shared/bridge-policies.js';
5
- import { encodeMetadataPassthrough, extractMetadataPassthrough } from '../../shared/metadata-passthrough.js';
6
- import { mapBridgeToolsToChat, mapChatToolsToBridge } from '../../shared/tool-mapping.js';
7
- import { prepareGeminiToolsForBridge, buildGeminiToolsFromBridge } from '../../shared/gemini-tool-utils.js';
8
- import { ensureProtocolState, getProtocolState } from '../../shared/protocol-state.js';
9
- import { applyClaudeThinkingToolSchemaCompat } from '../../compat/actions/claude-thinking-tools.js';
10
- const GENERATION_CONFIG_KEYS = [
11
- { source: 'temperature', target: 'temperature' },
12
- { source: 'topP', target: 'top_p' },
13
- { source: 'topK', target: 'top_k' },
14
- { source: 'maxOutputTokens', target: 'max_output_tokens' },
15
- { source: 'candidateCount', target: 'candidate_count' },
16
- { source: 'responseMimeType', target: 'response_mime_type' },
17
- { source: 'stopSequences', target: 'stop_sequences' }
18
- ];
19
- const PASSTHROUGH_METADATA_PREFIX = 'rcc_passthrough_';
20
- const PASSTHROUGH_PARAMETERS = ['tool_choice'];
21
- const DUMMY_THOUGHT_SIGNATURE = 'skip_thought_signature_validator';
22
- function coerceThoughtSignature(value) {
23
- if (typeof value === 'string' && value.trim().length) {
24
- return value.trim();
25
- }
26
- return undefined;
27
- }
28
- function ensureGeminiSemanticsNode(chat) {
29
- if (!chat.semantics || typeof chat.semantics !== 'object') {
30
- chat.semantics = {};
31
- }
32
- if (!chat.semantics.gemini || !isJsonObject(chat.semantics.gemini)) {
33
- chat.semantics.gemini = {};
34
- }
35
- return chat.semantics.gemini;
36
- }
37
- function ensureSystemSemantics(chat) {
38
- if (!chat.semantics || typeof chat.semantics !== 'object') {
39
- chat.semantics = {};
40
- }
41
- if (!chat.semantics.system || !isJsonObject(chat.semantics.system)) {
42
- chat.semantics.system = {};
43
- }
44
- return chat.semantics.system;
45
- }
46
- function markGeminiExplicitEmptyTools(chat) {
47
- if (!chat.semantics || typeof chat.semantics !== 'object') {
48
- chat.semantics = {};
49
- }
50
- if (!chat.semantics.tools || !isJsonObject(chat.semantics.tools)) {
51
- chat.semantics.tools = {};
52
- }
53
- chat.semantics.tools.explicitEmpty = true;
54
- }
55
- function readGeminiSemantics(chat) {
56
- if (!chat.semantics || typeof chat.semantics !== 'object') {
57
- return undefined;
58
- }
59
- const node = chat.semantics.gemini;
60
- return node && isJsonObject(node) ? node : undefined;
61
- }
62
- function hasExplicitEmptyToolsSemantics(chat) {
63
- if (!chat.semantics || typeof chat.semantics !== 'object') {
64
- return false;
65
- }
66
- const toolsNode = chat.semantics.tools;
67
- if (!toolsNode || !isJsonObject(toolsNode)) {
68
- return false;
69
- }
70
- return Boolean(toolsNode.explicitEmpty);
71
- }
72
- function readSystemTextBlocksFromSemantics(chat) {
73
- if (!chat.semantics || typeof chat.semantics !== 'object') {
74
- return undefined;
75
- }
76
- const systemNode = chat.semantics.system;
77
- if (!systemNode || !isJsonObject(systemNode)) {
78
- return undefined;
79
- }
80
- const rawBlocks = systemNode.textBlocks;
81
- if (!Array.isArray(rawBlocks)) {
82
- return undefined;
83
- }
84
- const normalized = rawBlocks
85
- .map((entry) => (typeof entry === 'string' ? entry : undefined))
86
- .filter((value) => typeof value === 'string' && value.trim().length > 0);
87
- return normalized.length ? normalized : undefined;
88
- }
89
- function extractThoughtSignatureFromToolCall(tc) {
90
- if (!tc || typeof tc !== 'object') {
91
- return undefined;
92
- }
93
- const node = tc;
94
- const direct = coerceThoughtSignature(node.thought_signature ?? node.thoughtSignature);
95
- if (direct) {
96
- return direct;
97
- }
98
- const extra = node.extra_content ?? node.extraContent;
99
- if (extra && typeof extra === 'object') {
100
- const googleNode = extra.google ?? extra.Google;
101
- if (googleNode && typeof googleNode === 'object') {
102
- return coerceThoughtSignature(googleNode.thought_signature ?? googleNode.thoughtSignature);
103
- }
104
- }
105
- return undefined;
106
- }
107
- function normalizeToolOutputs(messages, missing) {
108
- const outputs = [];
109
- messages.forEach((msg, index) => {
110
- if (msg.role !== 'tool')
111
- return;
112
- const callId = msg.tool_call_id || msg.id;
113
- if (typeof callId !== 'string' || !callId.trim()) {
114
- missing.push({ path: `messages[${index}].tool_call_id`, reason: 'missing_tool_call_id' });
115
- return;
116
- }
117
- outputs.push({
118
- tool_call_id: callId.trim(),
119
- content: normalizeToolContent(msg.content),
120
- name: typeof msg.name === 'string' ? msg.name : undefined
121
- });
122
- });
123
- return outputs.length ? outputs : undefined;
124
- }
125
- function synthesizeToolOutputsFromMessages(messages) {
126
- if (!Array.isArray(messages)) {
127
- return [];
128
- }
129
- const outputs = [];
130
- for (const message of messages) {
131
- if (!message || typeof message !== 'object')
132
- continue;
133
- if (message.role !== 'assistant')
134
- continue;
135
- const toolCalls = Array.isArray(message.tool_calls)
136
- ? message.tool_calls
137
- : [];
138
- for (const call of toolCalls) {
139
- const callId = typeof call.id === 'string' ? call.id : undefined;
140
- if (!callId) {
141
- continue;
142
- }
143
- const existing = outputs.find((entry) => entry.tool_call_id === callId);
144
- if (existing) {
145
- continue;
146
- }
147
- outputs.push({
148
- tool_call_id: callId,
149
- content: '',
150
- name: (call.function && call.function.name) || undefined
151
- });
152
- }
153
- }
154
- return outputs;
155
- }
156
- function normalizeToolContent(value) {
157
- if (typeof value === 'string')
158
- return value;
159
- if (value == null)
160
- return '';
161
- try {
162
- return JSON.stringify(value);
163
- }
164
- catch {
165
- return String(value ?? '');
166
- }
167
- }
168
- function convertToolMessageToOutput(message, allowedIds) {
169
- const rawId = (message.tool_call_id ?? message.id);
170
- const callId = typeof rawId === 'string' && rawId.trim().length ? rawId.trim() : undefined;
171
- if (!callId) {
172
- return null;
173
- }
174
- if (allowedIds && !allowedIds.has(callId)) {
175
- return null;
176
- }
177
- return {
178
- tool_call_id: callId,
179
- content: normalizeToolContent(message.content),
180
- name: typeof message.name === 'string' ? message.name : undefined
181
- };
182
- }
183
- function selectAntigravityClaudeThinkingMessages(messages) {
184
- if (!Array.isArray(messages) || messages.length === 0) {
185
- return messages ?? [];
186
- }
187
- // 为了与 Responses 入口对齐,Claude-thinking 在发往 Antigravity 时仅保留
188
- // 当前这一轮的 user 消息,丢弃历史 model/assistant 片段(例如错误日志中的「{」)。
189
- let lastUserIndex = -1;
190
- for (let i = messages.length - 1; i >= 0; i -= 1) {
191
- const msg = messages[i];
192
- if (!msg || typeof msg !== 'object')
193
- continue;
194
- if (msg.role === 'user') {
195
- lastUserIndex = i;
196
- break;
197
- }
198
- }
199
- if (lastUserIndex === -1) {
200
- return messages;
201
- }
202
- return [messages[lastUserIndex]];
203
- }
204
- function buildFunctionResponseEntry(output) {
205
- const parsedPayload = safeParseJson(output.content);
206
- const normalizedPayload = ensureFunctionResponsePayload(cloneAsJsonValue(parsedPayload));
207
- const part = {
208
- functionResponse: {
209
- name: output.name || 'tool',
210
- id: output.tool_call_id,
211
- response: normalizedPayload
212
- }
213
- };
214
- return { role: 'user', parts: [part] };
215
- }
216
- function collectSystemSegments(systemInstruction) {
217
- if (!systemInstruction)
218
- return [];
219
- const flatten = (val) => {
220
- if (typeof val === 'string')
221
- return val;
222
- if (Array.isArray(val))
223
- return val.map((entry) => flatten(entry)).filter(Boolean).join('\n');
224
- if (val && typeof val === 'object') {
225
- const text = val.text;
226
- if (typeof text === 'string')
227
- return text;
228
- const parts = val.parts;
229
- if (Array.isArray(parts))
230
- return parts.map((entry) => flatten(entry)).filter(Boolean).join('\n');
231
- }
232
- return '';
233
- };
234
- const text = flatten(systemInstruction).trim();
235
- return text ? [text] : [];
236
- }
237
- function collectParameters(payload) {
238
- const params = {};
239
- if (typeof payload.model === 'string') {
240
- params.model = payload.model;
241
- }
242
- const gen = payload.generationConfig;
243
- if (gen && typeof gen === 'object') {
244
- for (const { source, target } of GENERATION_CONFIG_KEYS) {
245
- const value = gen[source];
246
- if (value !== undefined) {
247
- params[target] = value;
248
- }
249
- }
250
- }
251
- if (payload.toolConfig !== undefined) {
252
- params.tool_config = jsonClone(payload.toolConfig);
253
- }
254
- const meta = payload.metadata;
255
- if (meta && typeof meta === 'object' && Object.prototype.hasOwnProperty.call(meta, '__rcc_stream')) {
256
- params.stream = Boolean(meta.__rcc_stream);
257
- }
258
- return Object.keys(params).length ? params : undefined;
259
- }
260
- function appendChatContentToGeminiParts(message, targetParts) {
261
- const content = message.content;
262
- if (typeof content === 'string') {
263
- const text = content.trim();
264
- if (text.length) {
265
- targetParts.push({ text });
266
- }
267
- return;
268
- }
269
- if (!Array.isArray(content)) {
270
- return;
271
- }
272
- const items = content;
273
- for (const block of items) {
274
- if (block == null)
275
- continue;
276
- if (typeof block === 'string') {
277
- const text = block.trim();
278
- if (text.length) {
279
- targetParts.push({ text });
280
- }
281
- continue;
282
- }
283
- if (typeof block !== 'object') {
284
- const text = String(block);
285
- if (text.trim().length) {
286
- targetParts.push({ text: text.trim() });
287
- }
288
- continue;
289
- }
290
- const record = block;
291
- const rawType = record.type;
292
- const type = typeof rawType === 'string' ? rawType.toLowerCase() : '';
293
- // Text-style blocks
294
- if (!type || type === 'text') {
295
- const textValue = typeof record.text === 'string'
296
- ? record.text
297
- : typeof record.content === 'string'
298
- ? record.content
299
- : '';
300
- const text = textValue.trim();
301
- if (text.length) {
302
- targetParts.push({ text });
303
- }
304
- continue;
305
- }
306
- // Image-style blocks -> Gemini inlineData
307
- if (type === 'image' || type === 'image_url') {
308
- // Prefer OpenAI-style image_url.url, but also accept uri/url/data.
309
- let url;
310
- const imageUrlRaw = record.image_url;
311
- if (typeof imageUrlRaw === 'string') {
312
- url = imageUrlRaw;
313
- }
314
- else if (imageUrlRaw && typeof imageUrlRaw === 'object' && typeof imageUrlRaw.url === 'string') {
315
- url = imageUrlRaw.url;
316
- }
317
- else if (typeof record.uri === 'string') {
318
- url = record.uri;
319
- }
320
- else if (typeof record.url === 'string') {
321
- url = record.url;
322
- }
323
- else if (typeof record.data === 'string') {
324
- url = record.data;
325
- }
326
- const trimmed = (url ?? '').trim();
327
- if (!trimmed.length) {
328
- // Fallback: at least emit a textual marker so内容不会完全丢失
329
- targetParts.push({ text: '[image]' });
330
- continue;
331
- }
332
- let mimeType;
333
- let data;
334
- // data:URL → inlineData { mimeType, data }
335
- if (trimmed.startsWith('data:')) {
336
- const match = /^data:([^;,]+)?(?:;base64)?,(.*)$/s.exec(trimmed);
337
- if (match) {
338
- mimeType = (match[1] || '').trim() || undefined;
339
- data = match[2] || '';
340
- }
341
- }
342
- if (data && data.trim().length) {
343
- const inline = {
344
- inlineData: {
345
- data: data.trim()
346
- }
347
- };
348
- if (mimeType && mimeType.length) {
349
- inline.inlineData.mimeType = mimeType;
350
- }
351
- targetParts.push(inline);
352
- }
353
- else {
354
- // 非 data: URL 暂时作为文本 URL 传递,保持语义可见
355
- targetParts.push({ text: trimmed });
356
- }
357
- continue;
358
- }
359
- // 默认:回退为文本 JSON 表示,避免静默丢失内容
360
- try {
361
- const jsonText = JSON.stringify(record);
362
- if (jsonText.trim().length) {
363
- targetParts.push({ text: jsonText });
364
- }
365
- }
366
- catch {
367
- // ignore malformed block
368
- }
369
- }
370
- }
371
- function buildGeminiRequestFromChat(chat, metadata) {
372
- const contents = [];
373
- const emittedToolOutputs = new Set();
374
- const adapterContext = metadata?.context;
375
- const rawProviderId = adapterContext?.providerId;
376
- const entryEndpointRaw = adapterContext?.entryEndpoint;
377
- const entryEndpoint = typeof entryEndpointRaw === 'string' ? entryEndpointRaw.trim().toLowerCase() : '';
378
- const isAnthropicEntry = entryEndpoint === '/v1/messages';
379
- const normalizedProviderId = typeof rawProviderId === 'string' ? rawProviderId.toLowerCase() : '';
380
- const providerIdPrefix = normalizedProviderId.split('.')[0];
381
- const isAntigravityClaudeThinking = providerIdPrefix === 'antigravity' &&
382
- typeof chat.parameters?.model === 'string' &&
383
- chat.parameters.model.includes('claude-sonnet-4-5-thinking');
384
- // 保持对通用 gemini-cli 的保护(避免上游直接执行 functionCall),
385
- // 但对于 antigravity.* 明确允许通过 Gemini functionCall 协议执行工具,
386
- // 以便完整打通 tools → functionCall → functionResponse 链路。
387
- const omitFunctionCallPartsForCli = providerIdPrefix === 'gemini-cli';
388
- const semanticsNode = readGeminiSemantics(chat);
389
- const systemTextBlocksFromSemantics = readSystemTextBlocksFromSemantics(chat);
390
- const bridgeDefs = chat.tools && chat.tools.length ? mapChatToolsToBridge(chat.tools) : undefined;
391
- const toolSchemaKeys = bridgeDefs ? buildToolSchemaKeyMap(bridgeDefs) : new Map();
392
- const sourceMessages = chat.messages;
393
- // 收集当前 ChatEnvelope 中 assistant/tool_calls 的 id,用于过滤孤立的 tool_result:
394
- // 只有在本轮对话中存在对应 tool_call 的 tool_result 才允许映射为 Gemini functionResponse。
395
- const assistantToolCallIds = new Set();
396
- for (const msg of sourceMessages) {
397
- if (!msg || typeof msg !== 'object')
398
- continue;
399
- if (msg.role !== 'assistant')
400
- continue;
401
- const tcs = Array.isArray(msg.tool_calls)
402
- ? msg.tool_calls
403
- : [];
404
- for (const tc of tcs) {
405
- const id = typeof tc.id === 'string' ? tc.id.trim() : '';
406
- if (id) {
407
- assistantToolCallIds.add(id);
408
- }
409
- }
410
- }
411
- for (const message of sourceMessages) {
412
- if (!message || typeof message !== 'object')
413
- continue;
414
- if (message.role === 'system')
415
- continue;
416
- if (message.role === 'tool') {
417
- const toolOutput = convertToolMessageToOutput(message, assistantToolCallIds);
418
- if (toolOutput) {
419
- contents.push(buildFunctionResponseEntry(toolOutput));
420
- emittedToolOutputs.add(toolOutput.tool_call_id);
421
- }
422
- continue;
423
- }
424
- const entry = {
425
- role: mapChatRoleToGemini(message.role),
426
- parts: []
427
- };
428
- appendChatContentToGeminiParts(message, entry.parts);
429
- const toolCalls = Array.isArray(message.tool_calls)
430
- ? message.tool_calls
431
- : [];
432
- for (const tc of toolCalls) {
433
- if (!tc || typeof tc !== 'object')
434
- continue;
435
- if (omitFunctionCallPartsForCli) {
436
- continue;
437
- }
438
- const fn = tc.function || {};
439
- const name = typeof fn.name === 'string' ? fn.name : undefined;
440
- if (!name)
441
- continue;
442
- let argsStruct;
443
- if (typeof fn.arguments === 'string') {
444
- try {
445
- argsStruct = JSON.parse(fn.arguments);
446
- }
447
- catch {
448
- argsStruct = { _raw: fn.arguments };
449
- }
450
- }
451
- else {
452
- argsStruct = fn.arguments ?? {};
453
- }
454
- argsStruct = alignToolCallArgsToSchema({ toolName: name, args: argsStruct, schemaKeys: toolSchemaKeys });
455
- let argsJson = cloneAsJsonValue(argsStruct);
456
- // Gemini / Antigravity 期望 functionCall.args 为对象(Struct),
457
- // 若顶层为数组或原始类型,则包装到 value 字段下,避免产生非法的 list 形状。
458
- if (!argsJson || typeof argsJson !== 'object' || Array.isArray(argsJson)) {
459
- argsJson = { value: argsJson };
460
- }
461
- const functionCall = { name, args: argsJson };
462
- const part = { functionCall };
463
- if (typeof tc.id === 'string') {
464
- part.functionCall.id = tc.id;
465
- }
466
- const thoughtSignature = extractThoughtSignatureFromToolCall(tc) ?? DUMMY_THOUGHT_SIGNATURE;
467
- if (thoughtSignature) {
468
- part.thoughtSignature = thoughtSignature;
469
- }
470
- entry.parts.push(part);
471
- }
472
- if (entry.parts.length) {
473
- contents.push(entry);
474
- }
475
- }
476
- const toolOutputMap = new Map();
477
- if (Array.isArray(chat.toolOutputs)) {
478
- for (const entry of chat.toolOutputs) {
479
- if (entry && typeof entry.tool_call_id === 'string' && entry.tool_call_id.trim().length) {
480
- toolOutputMap.set(entry.tool_call_id.trim(), entry);
481
- }
482
- }
483
- }
484
- if (toolOutputMap.size === 0) {
485
- const syntheticOutputs = synthesizeToolOutputsFromMessages(chat.messages);
486
- for (const output of syntheticOutputs) {
487
- toolOutputMap.set(output.tool_call_id, output);
488
- }
489
- }
490
- for (const output of toolOutputMap.values()) {
491
- if (emittedToolOutputs.has(output.tool_call_id)) {
492
- continue;
493
- }
494
- contents.push(buildFunctionResponseEntry(output));
495
- emittedToolOutputs.add(output.tool_call_id);
496
- }
497
- const request = {
498
- model: chat.parameters?.model || 'models/gemini-pro',
499
- contents
500
- };
501
- const geminiState = getProtocolState(metadata, 'gemini');
502
- if (semanticsNode?.systemInstruction !== undefined) {
503
- request.systemInstruction = jsonClone(semanticsNode.systemInstruction);
504
- }
505
- else if (geminiState?.systemInstruction !== undefined) {
506
- request.systemInstruction = jsonClone(geminiState.systemInstruction);
507
- }
508
- else {
509
- const fallbackSystemInstructions = systemTextBlocksFromSemantics;
510
- if (fallbackSystemInstructions && fallbackSystemInstructions.length) {
511
- const sysBlocks = fallbackSystemInstructions
512
- .filter((value) => typeof value === 'string' && value.trim().length > 0)
513
- .map((value) => ({ text: value }));
514
- if (sysBlocks.length) {
515
- request.systemInstruction = { role: 'system', parts: sysBlocks };
516
- }
517
- }
518
- }
519
- if (chat.tools && chat.tools.length) {
520
- const geminiTools = buildGeminiToolsFromBridge(bridgeDefs);
521
- if (geminiTools) {
522
- request.tools = geminiTools;
523
- }
524
- }
525
- const generationConfig = buildGenerationConfigFromParameters(chat.parameters || {});
526
- if (semanticsNode?.generationConfig && isJsonObject(semanticsNode.generationConfig)) {
527
- for (const [key, value] of Object.entries(semanticsNode.generationConfig)) {
528
- if (generationConfig[key] !== undefined) {
529
- continue;
530
- }
531
- generationConfig[key] = jsonClone(value);
532
- }
533
- }
534
- if (Object.keys(generationConfig).length) {
535
- request.generationConfig = generationConfig;
536
- }
537
- if (semanticsNode?.safetySettings !== undefined) {
538
- request.safetySettings = jsonClone(semanticsNode.safetySettings);
539
- }
540
- if (chat.parameters?.tool_config && isJsonObject(chat.parameters.tool_config)) {
541
- request.toolConfig = jsonClone(chat.parameters.tool_config);
542
- }
543
- else if (semanticsNode?.toolConfig && isJsonObject(semanticsNode.toolConfig)) {
544
- request.toolConfig = jsonClone(semanticsNode.toolConfig);
545
- }
546
- // 为了保持协议解耦,只在 Gemini 自身或开放式 Chat 入口下透传 providerMetadata;
547
- // 对于 Anthropic (/v1/messages) 等其它协议的入口,不再将其 metadata 整块转发给 Gemini,
548
- // 避免跨协议泄漏上游专有字段。
549
- if (!isAnthropicEntry) {
550
- if (semanticsNode?.providerMetadata && isJsonObject(semanticsNode.providerMetadata)) {
551
- request.metadata = jsonClone(semanticsNode.providerMetadata);
552
- }
553
- else if (metadata?.providerMetadata && isJsonObject(metadata.providerMetadata)) {
554
- request.metadata = jsonClone(metadata.providerMetadata);
555
- }
556
- }
557
- if (chat.parameters && chat.parameters.stream !== undefined) {
558
- request.metadata = request.metadata ?? {};
559
- request.metadata.__rcc_stream = chat.parameters.stream;
560
- }
561
- if ((chat.metadata?.toolsFieldPresent || hasExplicitEmptyToolsSemantics(chat)) &&
562
- (!Array.isArray(chat.tools) || chat.tools.length === 0)) {
563
- request.metadata = request.metadata ?? {};
564
- request.metadata.__rcc_tools_field_present = '1';
565
- }
566
- const passthrough = encodeMetadataPassthrough(chat.parameters, {
567
- prefix: PASSTHROUGH_METADATA_PREFIX,
568
- keys: PASSTHROUGH_PARAMETERS
569
- });
570
- if (passthrough) {
571
- request.metadata = request.metadata ?? {};
572
- for (const [key, value] of Object.entries(passthrough)) {
573
- request.metadata[key] = value;
574
- }
575
- }
576
- // Apply claude-thinking compat at Gemini mapping time to ensure it is always active
577
- // for Claude models, regardless of compatibilityProfile wiring. Provider层负责进一步的
578
- // 传输层收紧(如 session_id / generationConfig),这里不做非标裁剪。
579
- const compatRequest = applyClaudeThinkingToolSchemaCompat(request, adapterContext);
580
- return compatRequest;
581
- }
582
- function isPlainRecord(value) {
583
- return !!value && typeof value === 'object' && !Array.isArray(value);
584
- }
585
- function buildToolSchemaKeyMap(defs) {
586
- const map = new Map();
587
- for (const def of defs) {
588
- const fnNode = def && typeof def === 'object' && def.function && typeof def.function === 'object'
589
- ? def.function
590
- : undefined;
591
- const name = typeof fnNode?.name === 'string'
592
- ? fnNode.name
593
- : typeof def?.name === 'string'
594
- ? String(def.name)
595
- : '';
596
- if (!name || !name.trim())
597
- continue;
598
- const parameters = (fnNode && fnNode.parameters) ??
599
- (def.parameters);
600
- if (!isPlainRecord(parameters))
601
- continue;
602
- const props = parameters.properties;
603
- if (!isPlainRecord(props))
604
- continue;
605
- const keys = Object.keys(props).filter((k) => typeof k === 'string' && k.trim().length > 0);
606
- if (!keys.length)
607
- continue;
608
- map.set(name, new Set(keys));
609
- }
610
- return map;
611
- }
612
- function alignToolCallArgsToSchema(options) {
613
- const name = typeof options.toolName === 'string' ? options.toolName.trim() : '';
614
- if (!name)
615
- return options.args;
616
- const schema = options.schemaKeys.get(name);
617
- if (!schema || schema.size === 0) {
618
- return options.args;
619
- }
620
- if (!isPlainRecord(options.args)) {
621
- return options.args;
622
- }
623
- const lowered = name.toLowerCase();
624
- const next = { ...options.args };
625
- // Align historical Codex tool args to the *declared schema* for Gemini.
626
- // Gemini validates historical functionCall.args against tool declarations, so mismatches like:
627
- // - exec_command: { cmd } vs schema { command } (or vice-versa)
628
- // - apply_patch: { patch/input } vs schema { instructions } (or vice-versa)
629
- // can cause MALFORMED_FUNCTION_CALL and empty responses.
630
- if (lowered === 'exec_command') {
631
- // Prefer the declared schema key; do not delete keys blindly.
632
- if (schema.has('cmd') && !Object.prototype.hasOwnProperty.call(next, 'cmd') && Object.prototype.hasOwnProperty.call(next, 'command')) {
633
- next.cmd = next.command;
634
- }
635
- if (schema.has('command') && !Object.prototype.hasOwnProperty.call(next, 'command') && Object.prototype.hasOwnProperty.call(next, 'cmd')) {
636
- next.command = next.cmd;
637
- }
638
- }
639
- else if (lowered === 'write_stdin') {
640
- if (schema.has('chars') && !Object.prototype.hasOwnProperty.call(next, 'chars') && Object.prototype.hasOwnProperty.call(next, 'text')) {
641
- next.chars = next.text;
642
- }
643
- if (schema.has('text') && !Object.prototype.hasOwnProperty.call(next, 'text') && Object.prototype.hasOwnProperty.call(next, 'chars')) {
644
- next.text = next.chars;
645
- }
646
- }
647
- else if (lowered === 'apply_patch') {
648
- if (schema.has('instructions') && !Object.prototype.hasOwnProperty.call(next, 'instructions')) {
649
- const patch = typeof next.patch === 'string' ? next.patch : undefined;
650
- const input = typeof next.input === 'string' ? next.input : undefined;
651
- const candidate = patch && patch.trim().length ? patch : input && input.trim().length ? input : undefined;
652
- if (candidate) {
653
- next.instructions = candidate;
654
- }
655
- }
656
- if (schema.has('patch') && !Object.prototype.hasOwnProperty.call(next, 'patch')) {
657
- const input = typeof next.input === 'string' ? next.input : undefined;
658
- if (input && input.trim().length) {
659
- next.patch = input;
660
- }
661
- }
662
- }
663
- // Prune to schema keys for known Codex tools to reduce strict upstream validation failures.
664
- if (lowered === 'exec_command' || lowered === 'write_stdin' || lowered === 'apply_patch') {
665
- const pruned = {};
666
- for (const key of schema) {
667
- if (Object.prototype.hasOwnProperty.call(next, key)) {
668
- pruned[key] = next[key];
669
- }
670
- }
671
- return pruned;
672
- }
673
- return next;
674
- }
675
- function buildGenerationConfigFromParameters(parameters) {
676
- const config = {};
677
- for (const { source, target } of GENERATION_CONFIG_KEYS) {
678
- const value = parameters[target] ?? (target === 'max_output_tokens' ? parameters.max_tokens : undefined);
679
- if (value !== undefined) {
680
- config[source] = value;
681
- }
682
- }
683
- return config;
684
- }
685
- function mapChatRoleToGemini(role) {
686
- const r = role.toLowerCase();
687
- if (r === 'assistant')
688
- return 'model';
689
- if (r === 'system')
690
- return 'system';
691
- if (r === 'tool')
692
- return 'tool';
693
- return 'user';
694
- }
695
- function safeParseJson(value) {
696
- try {
697
- return JSON.parse(value);
698
- }
699
- catch {
700
- return value;
701
- }
702
- }
703
- function ensureFunctionResponsePayload(value) {
704
- // Gemini function_response.response 字段在 CloudCode/Gemini CLI 协议里对应的是
705
- // protobuf Struct(JSON object),而不是顶层数组。
706
- // 这里做一层规范化:
707
- // - 对象:直接透传;
708
- // - 数组:包一层 { result: [...] } 避免把数组作为 Struct 根节点;
709
- // - 原始值:包一层 { result: value },并把 undefined 映射为 null。
710
- if (value && typeof value === 'object') {
711
- if (Array.isArray(value)) {
712
- return {
713
- result: value
714
- };
715
- }
716
- return value;
717
- }
718
- return {
719
- result: value === undefined ? null : value
720
- };
721
- }
722
- function cloneAsJsonValue(value) {
723
- try {
724
- return JSON.parse(JSON.stringify(value ?? null));
725
- }
726
- catch {
727
- if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean' || value === null) {
728
- return value;
729
- }
730
- if (Array.isArray(value)) {
731
- return value.map((entry) => cloneAsJsonValue(entry));
732
- }
733
- if (value && typeof value === 'object') {
734
- const out = {};
735
- for (const [key, entry] of Object.entries(value)) {
736
- out[key] = cloneAsJsonValue(entry);
737
- }
738
- return out;
739
- }
740
- return String(value ?? '');
741
- }
742
- }
743
- export class GeminiSemanticMapper {
744
- async toChat(format, ctx) {
745
- const payload = (format.payload ?? {});
746
- const missing = [];
747
- const { messages: builtMessages } = buildOpenAIChatFromGeminiRequest(payload);
748
- let messages = Array.isArray(builtMessages) ? builtMessages : [];
749
- if (!Array.isArray(payload.contents)) {
750
- missing.push({ path: 'contents', reason: 'absent' });
751
- }
752
- const bridgeTools = prepareGeminiToolsForBridge(payload.tools, missing);
753
- const tools = bridgeTools ? mapBridgeToolsToChat(bridgeTools) : undefined;
754
- let parameters = collectParameters(payload);
755
- const metadata = { context: ctx };
756
- const systemSegments = collectSystemSegments(payload.systemInstruction);
757
- if (payload.systemInstruction !== undefined) {
758
- const rawSystem = jsonClone(payload.systemInstruction);
759
- ensureProtocolState(metadata, 'gemini').systemInstruction = rawSystem;
760
- }
761
- if (missing.length) {
762
- metadata.missingFields = missing;
763
- }
764
- try {
765
- const bridgePolicy = resolveBridgePolicy({ protocol: 'gemini-chat' });
766
- const actions = resolvePolicyActions(bridgePolicy, 'request_inbound');
767
- if (actions?.length) {
768
- const actionState = createBridgeActionState({
769
- messages: messages,
770
- rawRequest: payload,
771
- metadata: metadata
772
- });
773
- runBridgeActionPipeline({
774
- stage: 'request_inbound',
775
- actions,
776
- protocol: bridgePolicy?.protocol ?? 'gemini-chat',
777
- moduleType: bridgePolicy?.moduleType ?? 'gemini-chat',
778
- requestId: ctx.requestId,
779
- state: actionState
780
- });
781
- messages = actionState.messages;
782
- }
783
- }
784
- catch {
785
- // best-effort policy execution
786
- }
787
- const toolOutputs = normalizeToolOutputs(messages, missing);
788
- const passthrough = extractMetadataPassthrough(payload.metadata, {
789
- prefix: PASSTHROUGH_METADATA_PREFIX,
790
- keys: PASSTHROUGH_PARAMETERS
791
- });
792
- if (passthrough.passthrough) {
793
- parameters = { ...(parameters || {}), ...passthrough.passthrough };
794
- }
795
- const providerMetadataSource = passthrough.metadata ?? payload.metadata;
796
- let providerMetadata;
797
- let explicitEmptyTools = Array.isArray(payload.tools) && payload.tools.length === 0;
798
- if (providerMetadataSource) {
799
- const cloned = jsonClone(providerMetadataSource);
800
- let toolsFieldPresent = false;
801
- if (isJsonObject(cloned)) {
802
- delete cloned.__rcc_stream;
803
- if (Object.prototype.hasOwnProperty.call(cloned, '__rcc_tools_field_present')) {
804
- const sentinel = cloned.__rcc_tools_field_present;
805
- toolsFieldPresent = sentinel === '1' || sentinel === true;
806
- delete cloned.__rcc_tools_field_present;
807
- }
808
- if (Object.prototype.hasOwnProperty.call(cloned, '__rcc_raw_system')) {
809
- delete cloned.__rcc_raw_system;
810
- }
811
- }
812
- if (toolsFieldPresent) {
813
- metadata.toolsFieldPresent = true;
814
- explicitEmptyTools = true;
815
- }
816
- providerMetadata = cloned;
817
- metadata.providerMetadata = providerMetadata;
818
- }
819
- const chatEnvelope = {
820
- messages,
821
- tools,
822
- toolOutputs,
823
- parameters,
824
- metadata
825
- };
826
- if (systemSegments.length) {
827
- const systemNode = ensureSystemSemantics(chatEnvelope);
828
- systemNode.textBlocks = systemSegments.map((segment) => segment);
829
- }
830
- let semanticsNode;
831
- const ensureSemanticsNode = () => {
832
- semanticsNode = semanticsNode ?? ensureGeminiSemanticsNode(chatEnvelope);
833
- return semanticsNode;
834
- };
835
- if (payload.systemInstruction !== undefined) {
836
- ensureSemanticsNode().systemInstruction = jsonClone(payload.systemInstruction);
837
- }
838
- if (payload.safetySettings) {
839
- ensureSemanticsNode().safetySettings = jsonClone(payload.safetySettings);
840
- }
841
- if (payload.generationConfig && isJsonObject(payload.generationConfig)) {
842
- ensureSemanticsNode().generationConfig = jsonClone(payload.generationConfig);
843
- }
844
- if (payload.toolConfig && isJsonObject(payload.toolConfig)) {
845
- ensureSemanticsNode().toolConfig = jsonClone(payload.toolConfig);
846
- }
847
- if (providerMetadata) {
848
- ensureSemanticsNode().providerMetadata = jsonClone(providerMetadata);
849
- }
850
- if (explicitEmptyTools) {
851
- markGeminiExplicitEmptyTools(chatEnvelope);
852
- }
853
- return chatEnvelope;
854
- }
855
- async fromChat(chat, ctx) {
856
- try {
857
- const { applyToolSessionCompat } = await import('../tool-session-compat.js');
858
- await applyToolSessionCompat(chat, ctx);
859
- }
860
- catch {
861
- // best-effort compat; do not block outbound mapping
862
- }
863
- const envelopePayload = buildGeminiRequestFromChat(chat, chat.metadata);
864
- try {
865
- const bridgePolicy = resolveBridgePolicy({ protocol: 'gemini-chat' });
866
- const actions = resolvePolicyActions(bridgePolicy, 'request_outbound');
867
- if (actions?.length) {
868
- const capturedToolResults = Array.isArray(chat.toolOutputs)
869
- ? chat.toolOutputs.map((entry) => ({
870
- tool_call_id: entry.tool_call_id,
871
- output: entry.content,
872
- name: entry.name
873
- }))
874
- : undefined;
875
- const actionState = createBridgeActionState({
876
- messages: Array.isArray(chat.messages) ? chat.messages : [],
877
- rawRequest: envelopePayload,
878
- metadata: chat.metadata,
879
- capturedToolResults
880
- });
881
- runBridgeActionPipeline({
882
- stage: 'request_outbound',
883
- actions,
884
- protocol: bridgePolicy?.protocol ?? 'gemini-chat',
885
- moduleType: bridgePolicy?.moduleType ?? 'gemini-chat',
886
- requestId: ctx.requestId,
887
- state: actionState
888
- });
889
- }
890
- }
891
- catch {
892
- // ignore policy failures
893
- }
894
- return {
895
- protocol: 'gemini-chat',
896
- direction: 'response',
897
- payload: envelopePayload,
898
- meta: {
899
- context: ctx
900
- }
901
- };
902
- }
903
- }
1
+ export { GeminiSemanticMapper } from '../operation-table/semantic-mappers/gemini-mapper.js';