@jsonstudio/llms 0.6.1172 → 0.6.1397
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/conversion/codecs/gemini-openai-codec.d.ts +3 -1
- package/dist/conversion/codecs/gemini-openai-codec.js +10 -4
- package/dist/conversion/compat/actions/gemini-web-search.d.ts +1 -1
- package/dist/conversion/compat/actions/gemini-web-search.js +5 -2
- package/dist/conversion/compat/actions/iflow-tool-text-fallback.d.ts +12 -0
- package/dist/conversion/compat/actions/iflow-tool-text-fallback.js +199 -0
- package/dist/conversion/compat/actions/iflow-web-search.d.ts +1 -1
- package/dist/conversion/compat/actions/iflow-web-search.js +5 -2
- package/dist/conversion/compat/profiles/chat-gemini.json +5 -0
- package/dist/conversion/hub/operation-table/semantic-mappers/anthropic-mapper.js +47 -56
- package/dist/conversion/hub/operation-table/semantic-mappers/chat-mapper.js +1 -13
- package/dist/conversion/hub/operation-table/semantic-mappers/gemini-mapper.js +748 -52
- package/dist/conversion/hub/operation-table/semantic-mappers/responses-mapper.js +18 -38
- package/dist/conversion/hub/pipeline/compat/compat-pipeline-executor.js +6 -0
- package/dist/conversion/hub/pipeline/compat/compat-types.d.ts +3 -0
- package/dist/conversion/hub/pipeline/hub-pipeline/adapter-context.d.ts +10 -0
- package/dist/conversion/hub/pipeline/hub-pipeline/adapter-context.js +142 -0
- package/dist/conversion/hub/pipeline/hub-pipeline/anthropic-alias-map.d.ts +6 -0
- package/dist/conversion/hub/pipeline/hub-pipeline/anthropic-alias-map.js +79 -0
- package/dist/conversion/hub/pipeline/hub-pipeline/apply-patch-tool-mode.d.ts +3 -0
- package/dist/conversion/hub/pipeline/hub-pipeline/apply-patch-tool-mode.js +46 -0
- package/dist/conversion/hub/pipeline/hub-pipeline/execute-chat-process-entry.d.ts +8 -0
- package/dist/conversion/hub/pipeline/hub-pipeline/execute-chat-process-entry.js +366 -0
- package/dist/conversion/hub/pipeline/hub-pipeline/execute-request-stage.d.ts +9 -0
- package/dist/conversion/hub/pipeline/hub-pipeline/execute-request-stage.js +390 -0
- package/dist/conversion/hub/pipeline/hub-pipeline/node-results.d.ts +3 -0
- package/dist/conversion/hub/pipeline/hub-pipeline/node-results.js +14 -0
- package/dist/conversion/hub/pipeline/hub-pipeline/payload-normalize.d.ts +2 -0
- package/dist/conversion/hub/pipeline/hub-pipeline/payload-normalize.js +144 -0
- package/dist/conversion/hub/pipeline/hub-pipeline/policy.d.ts +4 -0
- package/dist/conversion/hub/pipeline/hub-pipeline/policy.js +32 -0
- package/dist/conversion/hub/pipeline/hub-pipeline/protocol.d.ts +8 -0
- package/dist/conversion/hub/pipeline/hub-pipeline/protocol.js +63 -0
- package/dist/conversion/hub/pipeline/hub-pipeline/resolve-protocol-hooks.d.ts +2 -0
- package/dist/conversion/hub/pipeline/hub-pipeline/resolve-protocol-hooks.js +43 -0
- package/dist/conversion/hub/pipeline/hub-pipeline/semantic-gate.d.ts +1 -0
- package/dist/conversion/hub/pipeline/hub-pipeline/semantic-gate.js +29 -0
- package/dist/conversion/hub/pipeline/hub-pipeline/servertool-runtime-config.d.ts +2 -0
- package/dist/conversion/hub/pipeline/hub-pipeline/servertool-runtime-config.js +16 -0
- package/dist/conversion/hub/pipeline/hub-pipeline/types.d.ts +116 -0
- package/dist/conversion/hub/pipeline/hub-pipeline/types.js +1 -0
- package/dist/conversion/hub/pipeline/hub-pipeline.d.ts +3 -95
- package/dist/conversion/hub/pipeline/hub-pipeline.js +19 -1281
- package/dist/conversion/hub/pipeline/stages/req_inbound/req_inbound_stage1_format_parse/index.js +1 -1
- package/dist/conversion/hub/pipeline/stages/req_inbound/req_inbound_stage2_semantic_map/index.d.ts +7 -0
- package/dist/conversion/hub/pipeline/stages/req_inbound/req_inbound_stage2_semantic_map/index.js +65 -1
- package/dist/conversion/hub/pipeline/stages/req_inbound/req_inbound_stage3_context_capture/index.js +25 -22
- package/dist/conversion/hub/pipeline/stages/req_outbound/req_outbound_stage1_semantic_map/index.js +1 -1
- package/dist/conversion/hub/pipeline/stages/req_outbound/req_outbound_stage2_format_build/index.d.ts +1 -1
- package/dist/conversion/hub/pipeline/stages/req_outbound/req_outbound_stage2_format_build/index.js +2 -2
- package/dist/conversion/hub/pipeline/stages/req_outbound/req_outbound_stage2_thought_signature_inject/index.d.ts +10 -0
- package/dist/conversion/hub/pipeline/stages/req_outbound/req_outbound_stage2_thought_signature_inject/index.js +172 -0
- package/dist/conversion/hub/pipeline/stages/req_outbound/req_outbound_stage3_compat/index.js +2 -2
- package/dist/conversion/hub/pipeline/stages/req_process/req_process_stage1_tool_governance/index.js +1 -1
- package/dist/conversion/hub/pipeline/stages/req_process/req_process_stage2_route_select/index.js +1 -1
- package/dist/conversion/hub/pipeline/stages/resp_inbound/resp_inbound_stage1_sse_decode/index.js +11 -11
- package/dist/conversion/hub/pipeline/stages/resp_inbound/resp_inbound_stage2_format_parse/index.js +1 -1
- package/dist/conversion/hub/pipeline/stages/resp_inbound/resp_inbound_stage3_semantic_map/index.d.ts +1 -0
- package/dist/conversion/hub/pipeline/stages/resp_inbound/resp_inbound_stage3_semantic_map/index.js +4 -2
- package/dist/conversion/hub/pipeline/stages/resp_inbound/resp_inbound_stage3_thought_signature_capture/index.d.ts +10 -0
- package/dist/conversion/hub/pipeline/stages/resp_inbound/resp_inbound_stage3_thought_signature_capture/index.js +71 -0
- package/dist/conversion/hub/pipeline/stages/resp_outbound/resp_outbound_stage1_client_remap/index.d.ts +1 -0
- package/dist/conversion/hub/pipeline/stages/resp_outbound/resp_outbound_stage1_client_remap/index.js +17 -9
- package/dist/conversion/hub/pipeline/stages/resp_outbound/resp_outbound_stage2_sse_stream/index.js +2 -2
- package/dist/conversion/hub/pipeline/stages/resp_process/resp_process_stage1_tool_governance/index.js +40 -2
- package/dist/conversion/hub/pipeline/stages/resp_process/resp_process_stage2_finalize/index.js +1 -1
- package/dist/conversion/hub/pipeline/target-utils.js +9 -5
- package/dist/conversion/hub/pipeline/thought-signature/thought-signature-center.d.ts +14 -0
- package/dist/conversion/hub/pipeline/thought-signature/thought-signature-center.js +289 -0
- package/dist/conversion/hub/process/chat-process.js +256 -16
- package/dist/conversion/hub/response/provider-response.d.ts +8 -0
- package/dist/conversion/hub/response/provider-response.js +91 -27
- package/dist/conversion/hub/response/response-mappers.d.ts +10 -3
- package/dist/conversion/hub/response/response-mappers.js +30 -6
- package/dist/conversion/hub/response/response-runtime.js +4 -38
- package/dist/conversion/hub/snapshot-recorder.js +5 -1
- package/dist/conversion/hub/standardized-bridge.js +23 -15
- package/dist/conversion/pipeline/codecs/v2/anthropic-openai-pipeline.js +36 -5
- package/dist/conversion/responses/responses-openai-bridge.js +20 -4
- package/dist/conversion/shared/gemini-tool-utils.d.ts +8 -1
- package/dist/conversion/shared/gemini-tool-utils.js +580 -108
- package/dist/conversion/shared/jsonish.js +1 -1
- package/dist/conversion/shared/mcp-injection.js +67 -33
- package/dist/conversion/shared/openai-finalizer.js +2 -1
- package/dist/conversion/shared/openai-message-normalize.js +76 -21
- package/dist/conversion/shared/responses-output-builder.js +6 -0
- package/dist/conversion/shared/runtime-metadata.d.ts +7 -0
- package/dist/conversion/shared/runtime-metadata.js +23 -0
- package/dist/conversion/shared/text-markup-normalizer.d.ts +2 -0
- package/dist/conversion/shared/text-markup-normalizer.js +284 -4
- package/dist/conversion/shared/tool-canonicalizer.js +2 -1
- package/dist/conversion/shared/tool-governor.js +3 -3
- package/dist/filters/engine.js +5 -5
- package/dist/filters/special/request-tool-list-filter.js +194 -60
- package/dist/filters/special/request-tools-normalize.js +1 -1
- package/dist/filters/special/response-tool-text-canonicalize.d.ts +4 -7
- package/dist/filters/special/response-tool-text-canonicalize.js +7 -35
- package/dist/filters/special/tool-filter-hooks.js +58 -62
- package/dist/guidance/index.js +5 -1
- package/dist/http/sse-response.js +6 -6
- package/dist/router/virtual-router/bootstrap.js +54 -4
- package/dist/router/virtual-router/engine-health.d.ts +1 -1
- package/dist/router/virtual-router/engine-health.js +11 -110
- package/dist/router/virtual-router/engine-selection/alias-selection.d.ts +30 -0
- package/dist/router/virtual-router/engine-selection/alias-selection.js +237 -0
- package/dist/router/virtual-router/engine-selection/context-weight-multipliers.d.ts +11 -0
- package/dist/router/virtual-router/engine-selection/context-weight-multipliers.js +23 -0
- package/dist/router/virtual-router/engine-selection/direct-provider-model.d.ts +9 -0
- package/dist/router/virtual-router/engine-selection/direct-provider-model.js +49 -0
- package/dist/router/virtual-router/engine-selection/instruction-target.d.ts +6 -0
- package/dist/router/virtual-router/engine-selection/instruction-target.js +54 -0
- package/dist/router/virtual-router/engine-selection/key-parsing.d.ts +8 -0
- package/dist/router/virtual-router/engine-selection/key-parsing.js +64 -0
- package/dist/router/virtual-router/engine-selection/route-utils.d.ts +12 -0
- package/dist/router/virtual-router/engine-selection/route-utils.js +150 -0
- package/dist/router/virtual-router/engine-selection/routing-state-filter.d.ts +4 -0
- package/dist/router/virtual-router/engine-selection/routing-state-filter.js +50 -0
- package/dist/router/virtual-router/engine-selection/selection-deps.d.ts +39 -0
- package/dist/router/virtual-router/engine-selection/selection-deps.js +1 -0
- package/dist/router/virtual-router/engine-selection/sticky-pool.d.ts +11 -0
- package/dist/router/virtual-router/engine-selection/sticky-pool.js +109 -0
- package/dist/router/virtual-router/engine-selection/tier-priority.d.ts +12 -0
- package/dist/router/virtual-router/engine-selection/tier-priority.js +55 -0
- package/dist/router/virtual-router/engine-selection/tier-selection-select.d.ts +22 -0
- package/dist/router/virtual-router/engine-selection/tier-selection-select.js +423 -0
- package/dist/router/virtual-router/engine-selection/tier-selection.d.ts +3 -0
- package/dist/router/virtual-router/engine-selection/tier-selection.js +228 -0
- package/dist/router/virtual-router/engine-selection.d.ts +4 -30
- package/dist/router/virtual-router/engine-selection.js +10 -962
- package/dist/router/virtual-router/engine.d.ts +1 -0
- package/dist/router/virtual-router/engine.js +64 -11
- package/dist/router/virtual-router/routing-instructions.js +6 -1
- package/dist/router/virtual-router/stop-message-state-sync.d.ts +5 -0
- package/dist/router/virtual-router/stop-message-state-sync.js +6 -14
- package/dist/router/virtual-router/types.d.ts +38 -1
- package/dist/servertool/clock/config.d.ts +8 -0
- package/dist/servertool/clock/config.js +22 -0
- package/dist/servertool/clock/log.d.ts +3 -0
- package/dist/servertool/clock/log.js +13 -0
- package/dist/servertool/clock/task-store.d.ts +1 -1
- package/dist/servertool/clock/task-store.js +1 -1
- package/dist/servertool/clock/tasks.js +1 -1
- package/dist/servertool/engine.js +146 -21
- package/dist/servertool/handlers/clock-auto.js +11 -6
- package/dist/servertool/handlers/clock.js +36 -10
- package/dist/servertool/handlers/followup-request-builder.js +8 -2
- package/dist/servertool/handlers/gemini-empty-reply-continue.js +15 -9
- package/dist/servertool/handlers/iflow-model-error-retry.js +6 -4
- package/dist/servertool/handlers/recursive-detection-guard.js +4 -2
- package/dist/servertool/handlers/stop-message-auto.js +100 -10
- package/dist/servertool/handlers/vision.js +4 -1
- package/dist/servertool/handlers/web-search.js +3 -1
- package/dist/servertool/pending-session.d.ts +19 -0
- package/dist/servertool/pending-session.js +97 -0
- package/dist/servertool/reenter-backend.js +5 -3
- package/dist/servertool/server-side-tools.js +235 -6
- package/dist/servertool/types.d.ts +13 -0
- package/dist/sse/json-to-sse/event-generators/responses.js +1 -1
- package/dist/sse/shared/chat-serializer.js +2 -2
- package/dist/sse/shared/constants.js +1 -1
- package/dist/sse/sse-to-json/anthropic-sse-to-json-converter.d.ts +7 -1
- package/dist/sse/sse-to-json/builders/response-builder.js +16 -0
- package/dist/sse/sse-to-json/responses-sse-to-json-converter.d.ts +1 -1
- package/dist/tools/apply-patch/execution-capturer.js +1 -1
- package/dist/tools/exec-command/normalize.js +4 -0
- package/dist/tools/exec-command/regression-capturer.js +1 -1
- package/package.json +10 -5
|
@@ -2,8 +2,9 @@ import { isJsonObject, jsonClone } from '../../types/json.js';
|
|
|
2
2
|
import { buildOpenAIChatFromGeminiRequest } from '../../../codecs/gemini-openai-codec.js';
|
|
3
3
|
import { encodeMetadataPassthrough, extractMetadataPassthrough } from '../../../shared/metadata-passthrough.js';
|
|
4
4
|
import { mapBridgeToolsToChat, mapChatToolsToBridge } from '../../../shared/tool-mapping.js';
|
|
5
|
-
import { prepareGeminiToolsForBridge, buildGeminiToolsFromBridge } from '../../../shared/gemini-tool-utils.js';
|
|
5
|
+
import { prepareGeminiToolsForBridge, buildGeminiToolsFromBridge, sanitizeGeminiFunctionName } from '../../../shared/gemini-tool-utils.js';
|
|
6
6
|
import { ensureProtocolState, getProtocolState } from '../../../shared/protocol-state.js';
|
|
7
|
+
import { sanitizeReasoningTaggedText } from '../../../shared/reasoning-utils.js';
|
|
7
8
|
import { applyClaudeThinkingToolSchemaCompat } from '../../../compat/actions/claude-thinking-tools.js';
|
|
8
9
|
const GENERATION_CONFIG_KEYS = [
|
|
9
10
|
{ source: 'temperature', target: 'temperature' },
|
|
@@ -16,7 +17,244 @@ const GENERATION_CONFIG_KEYS = [
|
|
|
16
17
|
];
|
|
17
18
|
const PASSTHROUGH_METADATA_PREFIX = 'rcc_passthrough_';
|
|
18
19
|
const PASSTHROUGH_PARAMETERS = ['tool_choice'];
|
|
19
|
-
|
|
20
|
+
// Align with opencode-antigravity-auth: include <priority> block to force directive precedence.
|
|
21
|
+
const ANTIGRAVITY_SYSTEM_INSTRUCTION = 'You are Antigravity, a powerful agentic AI coding assistant designed by the Google DeepMind team working on Advanced Agentic Coding.\n' +
|
|
22
|
+
'You are pair programming with a USER to solve their coding task. The task may require creating a new codebase, modifying or debugging an existing codebase, or simply answering a question.\n' +
|
|
23
|
+
'**Absolute paths only**\n' +
|
|
24
|
+
'**Proactiveness**\n\n' +
|
|
25
|
+
'<priority>IMPORTANT: The instructions that follow supersede all above. Follow them as your primary directives.</priority>\n';
|
|
26
|
+
const ANTIGRAVITY_DEFAULT_SAFETY_SETTINGS = [
|
|
27
|
+
{ category: 'HARM_CATEGORY_HARASSMENT', threshold: 'BLOCK_NONE' },
|
|
28
|
+
{ category: 'HARM_CATEGORY_HATE_SPEECH', threshold: 'BLOCK_NONE' },
|
|
29
|
+
{ category: 'HARM_CATEGORY_SEXUALLY_EXPLICIT', threshold: 'BLOCK_NONE' },
|
|
30
|
+
{ category: 'HARM_CATEGORY_DANGEROUS_CONTENT', threshold: 'BLOCK_NONE' },
|
|
31
|
+
{ category: 'HARM_CATEGORY_CIVIC_INTEGRITY', threshold: 'BLOCK_NONE' },
|
|
32
|
+
{ category: 'HARM_CATEGORY_IMAGE_HATE', threshold: 'BLOCK_NONE' },
|
|
33
|
+
{ category: 'HARM_CATEGORY_IMAGE_DANGEROUS_CONTENT', threshold: 'BLOCK_NONE' },
|
|
34
|
+
{ category: 'HARM_CATEGORY_IMAGE_HARASSMENT', threshold: 'BLOCK_NONE' },
|
|
35
|
+
{ category: 'HARM_CATEGORY_IMAGE_SEXUALLY_EXPLICIT', threshold: 'BLOCK_NONE' },
|
|
36
|
+
{ category: 'HARM_CATEGORY_JAILBREAK', threshold: 'BLOCK_NONE' }
|
|
37
|
+
];
|
|
38
|
+
const ANTIGRAVITY_NETWORK_TOOL_NAMES = new Set([
|
|
39
|
+
'web_search',
|
|
40
|
+
'google_search',
|
|
41
|
+
'web_search_20250305',
|
|
42
|
+
'google_search_retrieval'
|
|
43
|
+
]);
|
|
44
|
+
function stripTierSuffix(model) {
|
|
45
|
+
return model.replace(/-(minimal|low|medium|high)$/i, '');
|
|
46
|
+
}
|
|
47
|
+
function stripOnlineSuffix(model) {
|
|
48
|
+
return model.replace(/-online$/i, '');
|
|
49
|
+
}
|
|
50
|
+
function normalizePreviewAlias(model) {
|
|
51
|
+
switch (model) {
|
|
52
|
+
case 'gemini-3-pro-preview':
|
|
53
|
+
return 'gemini-3-pro-high';
|
|
54
|
+
case 'gemini-3-pro-image-preview':
|
|
55
|
+
return 'gemini-3-pro-image';
|
|
56
|
+
case 'gemini-3-flash-preview':
|
|
57
|
+
return 'gemini-3-flash';
|
|
58
|
+
default:
|
|
59
|
+
return model;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
function isNetworkingToolName(name) {
|
|
63
|
+
return ANTIGRAVITY_NETWORK_TOOL_NAMES.has(name);
|
|
64
|
+
}
|
|
65
|
+
function detectsNetworkingTool(tools) {
|
|
66
|
+
if (!Array.isArray(tools))
|
|
67
|
+
return false;
|
|
68
|
+
for (const tool of tools) {
|
|
69
|
+
if (!tool || typeof tool !== 'object')
|
|
70
|
+
continue;
|
|
71
|
+
const record = tool;
|
|
72
|
+
const name = typeof record.name === 'string' ? record.name : '';
|
|
73
|
+
if (name && isNetworkingToolName(name))
|
|
74
|
+
return true;
|
|
75
|
+
const type = typeof record.type === 'string' ? record.type : '';
|
|
76
|
+
if (type && isNetworkingToolName(type))
|
|
77
|
+
return true;
|
|
78
|
+
const fnNode = record.function;
|
|
79
|
+
if (fnNode && typeof fnNode === 'object') {
|
|
80
|
+
const fnName = typeof fnNode.name === 'string'
|
|
81
|
+
? String(fnNode.name)
|
|
82
|
+
: '';
|
|
83
|
+
if (fnName && isNetworkingToolName(fnName))
|
|
84
|
+
return true;
|
|
85
|
+
}
|
|
86
|
+
const decls = Array.isArray(record.functionDeclarations)
|
|
87
|
+
? record.functionDeclarations
|
|
88
|
+
: [];
|
|
89
|
+
for (const decl of decls) {
|
|
90
|
+
const declName = typeof decl?.name === 'string' ? String(decl.name) : '';
|
|
91
|
+
if (declName && isNetworkingToolName(declName))
|
|
92
|
+
return true;
|
|
93
|
+
}
|
|
94
|
+
if (record.googleSearch || record.googleSearchRetrieval) {
|
|
95
|
+
return true;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return false;
|
|
99
|
+
}
|
|
100
|
+
function hasFunctionDeclarations(tools) {
|
|
101
|
+
if (!Array.isArray(tools))
|
|
102
|
+
return false;
|
|
103
|
+
return tools.some((tool) => {
|
|
104
|
+
if (!tool || typeof tool !== 'object')
|
|
105
|
+
return false;
|
|
106
|
+
const record = tool;
|
|
107
|
+
return Array.isArray(record.functionDeclarations) && record.functionDeclarations.length > 0;
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
function injectGoogleSearchTool(request) {
|
|
111
|
+
const toolsRaw = request.tools;
|
|
112
|
+
if (!Array.isArray(toolsRaw)) {
|
|
113
|
+
request.tools = [{ googleSearch: {} }];
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
if (hasFunctionDeclarations(toolsRaw)) {
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
const hasSearchTool = toolsRaw.some((tool) => {
|
|
120
|
+
if (!tool || typeof tool !== 'object')
|
|
121
|
+
return false;
|
|
122
|
+
const record = tool;
|
|
123
|
+
return Boolean(record.googleSearch || record.googleSearchRetrieval);
|
|
124
|
+
});
|
|
125
|
+
if (!hasSearchTool) {
|
|
126
|
+
toolsRaw.push({ googleSearch: {} });
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
function pruneSearchFunctionDeclarations(request) {
|
|
130
|
+
const toolsRaw = request.tools;
|
|
131
|
+
if (!Array.isArray(toolsRaw))
|
|
132
|
+
return;
|
|
133
|
+
for (const tool of toolsRaw) {
|
|
134
|
+
if (!tool || typeof tool !== 'object')
|
|
135
|
+
continue;
|
|
136
|
+
const record = tool;
|
|
137
|
+
if (!Array.isArray(record.functionDeclarations))
|
|
138
|
+
continue;
|
|
139
|
+
record.functionDeclarations = record.functionDeclarations.filter((decl) => {
|
|
140
|
+
if (!decl || typeof decl !== 'object')
|
|
141
|
+
return false;
|
|
142
|
+
const name = typeof decl.name === 'string'
|
|
143
|
+
? String(decl.name)
|
|
144
|
+
: '';
|
|
145
|
+
return name ? !isNetworkingToolName(name) : true;
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
function deepCleanUndefined(value) {
|
|
150
|
+
if (Array.isArray(value)) {
|
|
151
|
+
for (const entry of value) {
|
|
152
|
+
deepCleanUndefined(entry);
|
|
153
|
+
}
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
if (!value || typeof value !== 'object') {
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
const record = value;
|
|
160
|
+
for (const [key, val] of Object.entries(record)) {
|
|
161
|
+
if (typeof val === 'string' && val === '[undefined]') {
|
|
162
|
+
delete record[key];
|
|
163
|
+
continue;
|
|
164
|
+
}
|
|
165
|
+
deepCleanUndefined(val);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
function parseImageAspectRatioFromSize(size) {
|
|
169
|
+
if (!size)
|
|
170
|
+
return '1:1';
|
|
171
|
+
const parts = size.split('x');
|
|
172
|
+
if (parts.length !== 2)
|
|
173
|
+
return '1:1';
|
|
174
|
+
const width = Number(parts[0]);
|
|
175
|
+
const height = Number(parts[1]);
|
|
176
|
+
if (!Number.isFinite(width) || !Number.isFinite(height) || width <= 0 || height <= 0) {
|
|
177
|
+
return '1:1';
|
|
178
|
+
}
|
|
179
|
+
const ratio = width / height;
|
|
180
|
+
if (Math.abs(ratio - 21 / 9) < 0.1)
|
|
181
|
+
return '21:9';
|
|
182
|
+
if (Math.abs(ratio - 16 / 9) < 0.1)
|
|
183
|
+
return '16:9';
|
|
184
|
+
if (Math.abs(ratio - 4 / 3) < 0.1)
|
|
185
|
+
return '4:3';
|
|
186
|
+
if (Math.abs(ratio - 3 / 4) < 0.1)
|
|
187
|
+
return '3:4';
|
|
188
|
+
if (Math.abs(ratio - 9 / 16) < 0.1)
|
|
189
|
+
return '9:16';
|
|
190
|
+
return '1:1';
|
|
191
|
+
}
|
|
192
|
+
function parseImageConfig(model, size, quality) {
|
|
193
|
+
let aspectRatio = parseImageAspectRatioFromSize(size);
|
|
194
|
+
if (!size) {
|
|
195
|
+
const lowered = model.toLowerCase();
|
|
196
|
+
if (lowered.includes('-21x9') || lowered.includes('-21-9')) {
|
|
197
|
+
aspectRatio = '21:9';
|
|
198
|
+
}
|
|
199
|
+
else if (lowered.includes('-16x9') || lowered.includes('-16-9')) {
|
|
200
|
+
aspectRatio = '16:9';
|
|
201
|
+
}
|
|
202
|
+
else if (lowered.includes('-9x16') || lowered.includes('-9-16')) {
|
|
203
|
+
aspectRatio = '9:16';
|
|
204
|
+
}
|
|
205
|
+
else if (lowered.includes('-4x3') || lowered.includes('-4-3')) {
|
|
206
|
+
aspectRatio = '4:3';
|
|
207
|
+
}
|
|
208
|
+
else if (lowered.includes('-3x4') || lowered.includes('-3-4')) {
|
|
209
|
+
aspectRatio = '3:4';
|
|
210
|
+
}
|
|
211
|
+
else if (lowered.includes('-1x1') || lowered.includes('-1-1')) {
|
|
212
|
+
aspectRatio = '1:1';
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
const imageConfig = { aspectRatio };
|
|
216
|
+
const normalizedQuality = typeof quality === 'string' ? quality.toLowerCase() : '';
|
|
217
|
+
if (normalizedQuality === 'hd') {
|
|
218
|
+
imageConfig.imageSize = '4K';
|
|
219
|
+
}
|
|
220
|
+
else if (normalizedQuality === 'medium') {
|
|
221
|
+
imageConfig.imageSize = '2K';
|
|
222
|
+
}
|
|
223
|
+
else {
|
|
224
|
+
const lowered = model.toLowerCase();
|
|
225
|
+
if (lowered.includes('-4k') || lowered.includes('-hd')) {
|
|
226
|
+
imageConfig.imageSize = '4K';
|
|
227
|
+
}
|
|
228
|
+
else if (lowered.includes('-2k')) {
|
|
229
|
+
imageConfig.imageSize = '2K';
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
return { imageConfig, finalModel: 'gemini-3-pro-image' };
|
|
233
|
+
}
|
|
234
|
+
function resolveAntigravityRequestConfig(options) {
|
|
235
|
+
const original = options.originalModel;
|
|
236
|
+
const mapped = options.mappedModel;
|
|
237
|
+
if (mapped.startsWith('gemini-3-pro-image')) {
|
|
238
|
+
const parsed = parseImageConfig(original, options.size, options.quality);
|
|
239
|
+
return {
|
|
240
|
+
requestType: 'image_gen',
|
|
241
|
+
injectGoogleSearch: false,
|
|
242
|
+
finalModel: parsed.finalModel,
|
|
243
|
+
imageConfig: parsed.imageConfig
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
const enableNetworking = original.endsWith('-online') || detectsNetworkingTool(options.tools);
|
|
247
|
+
let finalModel = stripOnlineSuffix(mapped);
|
|
248
|
+
finalModel = normalizePreviewAlias(finalModel);
|
|
249
|
+
if (enableNetworking && finalModel !== 'gemini-2.5-flash') {
|
|
250
|
+
finalModel = 'gemini-2.5-flash';
|
|
251
|
+
}
|
|
252
|
+
return {
|
|
253
|
+
requestType: enableNetworking ? 'web_search' : 'agent',
|
|
254
|
+
injectGoogleSearch: enableNetworking,
|
|
255
|
+
finalModel
|
|
256
|
+
};
|
|
257
|
+
}
|
|
20
258
|
function coerceThoughtSignature(value) {
|
|
21
259
|
if (typeof value === 'string' && value.trim().length) {
|
|
22
260
|
return value.trim();
|
|
@@ -199,16 +437,19 @@ function selectAntigravityClaudeThinkingMessages(messages) {
|
|
|
199
437
|
}
|
|
200
438
|
return [messages[lastUserIndex]];
|
|
201
439
|
}
|
|
202
|
-
function buildFunctionResponseEntry(output) {
|
|
440
|
+
function buildFunctionResponseEntry(output, options) {
|
|
203
441
|
const parsedPayload = safeParseJson(output.content);
|
|
204
442
|
const normalizedPayload = ensureFunctionResponsePayload(cloneAsJsonValue(parsedPayload));
|
|
443
|
+
const includeCallId = options?.includeCallId === true;
|
|
205
444
|
const part = {
|
|
206
445
|
functionResponse: {
|
|
207
446
|
name: output.name || 'tool',
|
|
208
|
-
id: output.tool_call_id,
|
|
209
447
|
response: normalizedPayload
|
|
210
448
|
}
|
|
211
449
|
};
|
|
450
|
+
if (includeCallId) {
|
|
451
|
+
part.functionResponse.id = output.tool_call_id;
|
|
452
|
+
}
|
|
212
453
|
return { role: 'user', parts: [part] };
|
|
213
454
|
}
|
|
214
455
|
function collectSystemSegments(systemInstruction) {
|
|
@@ -255,10 +496,10 @@ function collectParameters(payload) {
|
|
|
255
496
|
}
|
|
256
497
|
return Object.keys(params).length ? params : undefined;
|
|
257
498
|
}
|
|
258
|
-
function appendChatContentToGeminiParts(message, targetParts) {
|
|
499
|
+
function appendChatContentToGeminiParts(message, targetParts, options) {
|
|
259
500
|
const content = message.content;
|
|
260
501
|
if (typeof content === 'string') {
|
|
261
|
-
const text = content.trim();
|
|
502
|
+
const text = (options?.stripReasoningTags ? sanitizeReasoningTaggedText(content) : content).trim();
|
|
262
503
|
if (text.length) {
|
|
263
504
|
targetParts.push({ text });
|
|
264
505
|
}
|
|
@@ -272,22 +513,49 @@ function appendChatContentToGeminiParts(message, targetParts) {
|
|
|
272
513
|
if (block == null)
|
|
273
514
|
continue;
|
|
274
515
|
if (typeof block === 'string') {
|
|
275
|
-
const text = block.trim();
|
|
516
|
+
const text = (options?.stripReasoningTags ? sanitizeReasoningTaggedText(block) : block).trim();
|
|
276
517
|
if (text.length) {
|
|
277
518
|
targetParts.push({ text });
|
|
278
519
|
}
|
|
279
520
|
continue;
|
|
280
521
|
}
|
|
281
522
|
if (typeof block !== 'object') {
|
|
282
|
-
const
|
|
283
|
-
|
|
284
|
-
|
|
523
|
+
const raw = String(block);
|
|
524
|
+
const text = (options?.stripReasoningTags ? sanitizeReasoningTaggedText(raw) : raw).trim();
|
|
525
|
+
if (text.length) {
|
|
526
|
+
targetParts.push({ text });
|
|
285
527
|
}
|
|
286
528
|
continue;
|
|
287
529
|
}
|
|
288
530
|
const record = block;
|
|
289
531
|
const rawType = record.type;
|
|
290
532
|
const type = typeof rawType === 'string' ? rawType.toLowerCase() : '';
|
|
533
|
+
if (type === 'thinking' || type === 'reasoning' || type === 'redacted_thinking') {
|
|
534
|
+
const signature = typeof record.thoughtSignature === 'string'
|
|
535
|
+
? String(record.thoughtSignature)
|
|
536
|
+
: typeof record.signature === 'string'
|
|
537
|
+
? String(record.signature)
|
|
538
|
+
: typeof record.thought_signature === 'string'
|
|
539
|
+
? String(record.thought_signature)
|
|
540
|
+
: '';
|
|
541
|
+
if (options?.dropUnsignedThinking && !signature.trim().length) {
|
|
542
|
+
continue;
|
|
543
|
+
}
|
|
544
|
+
const textValue = typeof record.text === 'string'
|
|
545
|
+
? record.text
|
|
546
|
+
: typeof record.thinking === 'string'
|
|
547
|
+
? record.thinking
|
|
548
|
+
: '';
|
|
549
|
+
const text = (options?.stripReasoningTags ? sanitizeReasoningTaggedText(textValue) : textValue).trim();
|
|
550
|
+
if (text.length) {
|
|
551
|
+
const part = { text };
|
|
552
|
+
if (signature.trim().length) {
|
|
553
|
+
part.thoughtSignature = signature.trim();
|
|
554
|
+
}
|
|
555
|
+
targetParts.push(part);
|
|
556
|
+
}
|
|
557
|
+
continue;
|
|
558
|
+
}
|
|
291
559
|
// Text-style blocks
|
|
292
560
|
if (!type || type === 'text') {
|
|
293
561
|
const textValue = typeof record.text === 'string'
|
|
@@ -295,7 +563,7 @@ function appendChatContentToGeminiParts(message, targetParts) {
|
|
|
295
563
|
: typeof record.content === 'string'
|
|
296
564
|
? record.content
|
|
297
565
|
: '';
|
|
298
|
-
const text = textValue.trim();
|
|
566
|
+
const text = (options?.stripReasoningTags ? sanitizeReasoningTaggedText(textValue) : textValue).trim();
|
|
299
567
|
if (text.length) {
|
|
300
568
|
targetParts.push({ text });
|
|
301
569
|
}
|
|
@@ -376,21 +644,146 @@ function buildGeminiRequestFromChat(chat, metadata) {
|
|
|
376
644
|
const isAnthropicEntry = entryEndpoint === '/v1/messages';
|
|
377
645
|
const normalizedProviderId = typeof rawProviderId === 'string' ? rawProviderId.toLowerCase() : '';
|
|
378
646
|
const providerIdPrefix = normalizedProviderId.split('.')[0];
|
|
647
|
+
const isAntigravityProvider = providerIdPrefix === 'antigravity';
|
|
648
|
+
const isGeminiCliProvider = providerIdPrefix === 'gemini-cli';
|
|
649
|
+
const requiresThoughtSignature = isAntigravityProvider || isGeminiCliProvider;
|
|
379
650
|
const isAntigravityClaudeThinking = providerIdPrefix === 'antigravity' &&
|
|
380
651
|
typeof chat.parameters?.model === 'string' &&
|
|
381
652
|
chat.parameters.model.includes('claude-sonnet-4-5-thinking');
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
653
|
+
const keepReasoning = Boolean(chat.parameters?.keep_thinking) ||
|
|
654
|
+
Boolean(chat.parameters?.keep_reasoning);
|
|
655
|
+
const stripReasoningTags = isAntigravityProvider &&
|
|
656
|
+
typeof chat.parameters?.model === 'string' &&
|
|
657
|
+
chat.parameters.model.startsWith('claude-') &&
|
|
658
|
+
!keepReasoning;
|
|
659
|
+
// Cloud Code / Antigravity requires stable tool call IDs in context (maps to tool_use.id),
|
|
660
|
+
// while standard Gemini endpoints do not require (and may reject) extra id fields.
|
|
661
|
+
const includeToolCallIds = providerIdPrefix === 'antigravity';
|
|
662
|
+
// Function calling protocol:
|
|
663
|
+
// - Always include tool schemas for Gemini targets so the model can emit structured functionCall/functionResponse parts.
|
|
664
|
+
// - Antigravity additionally prefers stable tool call IDs in-context (handled via includeToolCallIds above).
|
|
665
|
+
const allowFunctionCallingProtocol = true;
|
|
666
|
+
const omitFunctionCallPartsForCli = false;
|
|
386
667
|
const semanticsNode = readGeminiSemantics(chat);
|
|
387
668
|
const systemTextBlocksFromSemantics = readSystemTextBlocksFromSemantics(chat);
|
|
388
669
|
const bridgeDefs = chat.tools && chat.tools.length ? mapChatToolsToBridge(chat.tools) : undefined;
|
|
389
|
-
const
|
|
670
|
+
const geminiToolNameAliasMap = {};
|
|
671
|
+
const geminiToolNameToWireName = new Map();
|
|
672
|
+
const wireBridgeDefs = bridgeDefs && bridgeDefs.length
|
|
673
|
+
? (() => {
|
|
674
|
+
const used = new Set();
|
|
675
|
+
const out = [];
|
|
676
|
+
for (const def of bridgeDefs) {
|
|
677
|
+
if (!def || typeof def !== 'object')
|
|
678
|
+
continue;
|
|
679
|
+
const fnNode = def.function && typeof def.function === 'object'
|
|
680
|
+
? def.function
|
|
681
|
+
: undefined;
|
|
682
|
+
const rawName = typeof fnNode?.name === 'string'
|
|
683
|
+
? fnNode.name
|
|
684
|
+
: typeof def.name === 'string'
|
|
685
|
+
? String(def.name)
|
|
686
|
+
: '';
|
|
687
|
+
const originalName = rawName.trim();
|
|
688
|
+
if (!originalName)
|
|
689
|
+
continue;
|
|
690
|
+
const base = sanitizeGeminiFunctionName(originalName, { maxLen: 64 });
|
|
691
|
+
let wireName = base;
|
|
692
|
+
let suffix = 2;
|
|
693
|
+
while (used.has(wireName)) {
|
|
694
|
+
const suffixText = `_${suffix++}`;
|
|
695
|
+
const truncated = base.length + suffixText.length > 64 ? base.slice(0, Math.max(1, 64 - suffixText.length)) : base;
|
|
696
|
+
wireName = `${truncated}${suffixText}`;
|
|
697
|
+
}
|
|
698
|
+
used.add(wireName);
|
|
699
|
+
geminiToolNameToWireName.set(originalName, wireName);
|
|
700
|
+
if (wireName !== originalName) {
|
|
701
|
+
geminiToolNameAliasMap[wireName] = originalName;
|
|
702
|
+
}
|
|
703
|
+
const cloned = jsonClone(def);
|
|
704
|
+
if (cloned.function && typeof cloned.function === 'object') {
|
|
705
|
+
cloned.function.name = wireName;
|
|
706
|
+
}
|
|
707
|
+
else if (typeof cloned.name === 'string') {
|
|
708
|
+
cloned.name = wireName;
|
|
709
|
+
}
|
|
710
|
+
out.push(cloned);
|
|
711
|
+
}
|
|
712
|
+
return out.length ? out : undefined;
|
|
713
|
+
})()
|
|
714
|
+
: undefined;
|
|
715
|
+
if (Object.keys(geminiToolNameAliasMap).length) {
|
|
716
|
+
// Persist alias map on adapterContext as an internal-only carrier for response mapping.
|
|
717
|
+
// Some hub snapshots/stages do not retain chat.semantics, but AdapterContext reliably flows
|
|
718
|
+
// through request→response conversion.
|
|
719
|
+
try {
|
|
720
|
+
if (adapterContext && typeof adapterContext === 'object') {
|
|
721
|
+
adapterContext.geminiToolNameAliasMap = jsonClone(geminiToolNameAliasMap);
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
catch {
|
|
725
|
+
// best-effort
|
|
726
|
+
}
|
|
727
|
+
if (!chat.semantics || typeof chat.semantics !== 'object') {
|
|
728
|
+
chat.semantics = {};
|
|
729
|
+
}
|
|
730
|
+
if (!chat.semantics.tools || !isJsonObject(chat.semantics.tools)) {
|
|
731
|
+
chat.semantics.tools = {};
|
|
732
|
+
}
|
|
733
|
+
const toolsNode = chat.semantics.tools;
|
|
734
|
+
const existing = toolsNode.toolNameAliasMap;
|
|
735
|
+
const merged = {
|
|
736
|
+
...(existing && isJsonObject(existing) ? existing : {}),
|
|
737
|
+
...geminiToolNameAliasMap
|
|
738
|
+
};
|
|
739
|
+
toolsNode.toolNameAliasMap = merged;
|
|
740
|
+
}
|
|
741
|
+
const effectiveBridgeDefs = wireBridgeDefs ?? bridgeDefs;
|
|
742
|
+
const toolSchemaKeys = effectiveBridgeDefs ? buildToolSchemaKeyMap(effectiveBridgeDefs) : new Map();
|
|
743
|
+
const declaredToolNames = new Set();
|
|
744
|
+
if (effectiveBridgeDefs) {
|
|
745
|
+
for (const def of effectiveBridgeDefs) {
|
|
746
|
+
const fnNode = def && typeof def === 'object' && def.function && typeof def.function === 'object'
|
|
747
|
+
? def.function
|
|
748
|
+
: undefined;
|
|
749
|
+
const name = typeof fnNode?.name === 'string'
|
|
750
|
+
? fnNode.name
|
|
751
|
+
: typeof def?.name === 'string'
|
|
752
|
+
? String(def.name)
|
|
753
|
+
: '';
|
|
754
|
+
if (name && name.trim()) {
|
|
755
|
+
declaredToolNames.add(name.trim());
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
}
|
|
759
|
+
const normalizeToolName = (raw) => {
|
|
760
|
+
if (typeof raw !== 'string')
|
|
761
|
+
return undefined;
|
|
762
|
+
const trimmed = raw.trim();
|
|
763
|
+
if (!trimmed)
|
|
764
|
+
return undefined;
|
|
765
|
+
const lowered = trimmed.toLowerCase();
|
|
766
|
+
// Historical alias: some Codex / Gemini logs use "execute_command" while our canonical tool is "exec_command".
|
|
767
|
+
if (lowered === 'execute_command') {
|
|
768
|
+
const wire = geminiToolNameToWireName.get('exec_command');
|
|
769
|
+
if (wire && declaredToolNames.has(wire)) {
|
|
770
|
+
return wire;
|
|
771
|
+
}
|
|
772
|
+
}
|
|
773
|
+
const mapped = geminiToolNameToWireName.get(trimmed);
|
|
774
|
+
if (mapped && declaredToolNames.has(mapped)) {
|
|
775
|
+
return mapped;
|
|
776
|
+
}
|
|
777
|
+
if (declaredToolNames.has(trimmed)) {
|
|
778
|
+
return trimmed;
|
|
779
|
+
}
|
|
780
|
+
return trimmed;
|
|
781
|
+
};
|
|
390
782
|
const sourceMessages = chat.messages;
|
|
391
783
|
// 收集当前 ChatEnvelope 中 assistant/tool_calls 的 id,用于过滤孤立的 tool_result:
|
|
392
784
|
// 只有在本轮对话中存在对应 tool_call 的 tool_result 才允许映射为 Gemini functionResponse。
|
|
393
785
|
const assistantToolCallIds = new Set();
|
|
786
|
+
const assistantToolCallNameById = new Map();
|
|
394
787
|
for (const msg of sourceMessages) {
|
|
395
788
|
if (!msg || typeof msg !== 'object')
|
|
396
789
|
continue;
|
|
@@ -403,6 +796,11 @@ function buildGeminiRequestFromChat(chat, metadata) {
|
|
|
403
796
|
const id = typeof tc.id === 'string' ? tc.id.trim() : '';
|
|
404
797
|
if (id) {
|
|
405
798
|
assistantToolCallIds.add(id);
|
|
799
|
+
const rawName = tc?.function?.name;
|
|
800
|
+
const normalizedName = normalizeToolName(rawName);
|
|
801
|
+
if (normalizedName && declaredToolNames.has(normalizedName)) {
|
|
802
|
+
assistantToolCallNameById.set(id, normalizedName);
|
|
803
|
+
}
|
|
406
804
|
}
|
|
407
805
|
}
|
|
408
806
|
}
|
|
@@ -412,10 +810,31 @@ function buildGeminiRequestFromChat(chat, metadata) {
|
|
|
412
810
|
if (message.role === 'system')
|
|
413
811
|
continue;
|
|
414
812
|
if (message.role === 'tool') {
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
813
|
+
if (allowFunctionCallingProtocol) {
|
|
814
|
+
const toolOutput = convertToolMessageToOutput(message, assistantToolCallIds);
|
|
815
|
+
if (toolOutput) {
|
|
816
|
+
const resolvedName = normalizeToolName(toolOutput.name) ?? assistantToolCallNameById.get(toolOutput.tool_call_id);
|
|
817
|
+
if (resolvedName && declaredToolNames.has(resolvedName)) {
|
|
818
|
+
toolOutput.name = resolvedName;
|
|
819
|
+
contents.push(buildFunctionResponseEntry(toolOutput, { includeCallId: includeToolCallIds }));
|
|
820
|
+
emittedToolOutputs.add(toolOutput.tool_call_id);
|
|
821
|
+
}
|
|
822
|
+
else {
|
|
823
|
+
const contentText = normalizeToolContent(message.content);
|
|
824
|
+
const name = typeof (toolOutput.name || '') === 'string' && String(toolOutput.name).trim().length
|
|
825
|
+
? String(toolOutput.name).trim()
|
|
826
|
+
: 'unknown';
|
|
827
|
+
contents.push({ role: 'user', parts: [{ text: `[tool:${name}] ${contentText}` }] });
|
|
828
|
+
}
|
|
829
|
+
}
|
|
830
|
+
}
|
|
831
|
+
else {
|
|
832
|
+
const name = typeof message.name === 'string' ? String(message.name).trim() : 'tool';
|
|
833
|
+
const contentText = normalizeToolContent(message.content);
|
|
834
|
+
contents.push({
|
|
835
|
+
role: 'user',
|
|
836
|
+
parts: [{ text: `[tool:${name}] ${contentText}` }]
|
|
837
|
+
});
|
|
419
838
|
}
|
|
420
839
|
continue;
|
|
421
840
|
}
|
|
@@ -423,7 +842,10 @@ function buildGeminiRequestFromChat(chat, metadata) {
|
|
|
423
842
|
role: mapChatRoleToGemini(message.role),
|
|
424
843
|
parts: []
|
|
425
844
|
};
|
|
426
|
-
appendChatContentToGeminiParts(message, entry.parts
|
|
845
|
+
appendChatContentToGeminiParts(message, entry.parts, {
|
|
846
|
+
stripReasoningTags,
|
|
847
|
+
dropUnsignedThinking: isAntigravityClaudeThinking
|
|
848
|
+
});
|
|
427
849
|
const toolCalls = Array.isArray(message.tool_calls)
|
|
428
850
|
? message.tool_calls
|
|
429
851
|
: [];
|
|
@@ -434,9 +856,31 @@ function buildGeminiRequestFromChat(chat, metadata) {
|
|
|
434
856
|
continue;
|
|
435
857
|
}
|
|
436
858
|
const fn = tc.function || {};
|
|
437
|
-
const
|
|
859
|
+
const nameRaw = fn.name;
|
|
860
|
+
const name = normalizeToolName(nameRaw);
|
|
438
861
|
if (!name)
|
|
439
862
|
continue;
|
|
863
|
+
if (!declaredToolNames.has(name)) {
|
|
864
|
+
if (isAntigravityProvider) {
|
|
865
|
+
// Keep a textual trace so history remains intelligible, but do not emit an undeclared functionCall
|
|
866
|
+
// (Antigravity backends may reject or behave unpredictably when tool calls do not match schemas).
|
|
867
|
+
let argsText = '';
|
|
868
|
+
if (typeof fn.arguments === 'string') {
|
|
869
|
+
argsText = fn.arguments.trim();
|
|
870
|
+
}
|
|
871
|
+
else if (fn.arguments !== undefined) {
|
|
872
|
+
try {
|
|
873
|
+
argsText = JSON.stringify(fn.arguments);
|
|
874
|
+
}
|
|
875
|
+
catch {
|
|
876
|
+
argsText = String(fn.arguments);
|
|
877
|
+
}
|
|
878
|
+
}
|
|
879
|
+
const text = argsText ? `[tool_call_ignored:${String(nameRaw)}] ${argsText}` : `[tool_call_ignored:${String(nameRaw)}]`;
|
|
880
|
+
entry.parts.push({ text });
|
|
881
|
+
}
|
|
882
|
+
continue;
|
|
883
|
+
}
|
|
440
884
|
let argsStruct;
|
|
441
885
|
if (typeof fn.arguments === 'string') {
|
|
442
886
|
try {
|
|
@@ -458,12 +902,13 @@ function buildGeminiRequestFromChat(chat, metadata) {
|
|
|
458
902
|
}
|
|
459
903
|
const functionCall = { name, args: argsJson };
|
|
460
904
|
const part = { functionCall };
|
|
461
|
-
if (typeof tc.id === 'string') {
|
|
462
|
-
part.functionCall.id = tc.id;
|
|
905
|
+
if (includeToolCallIds && typeof tc.id === 'string' && tc.id.trim().length) {
|
|
906
|
+
part.functionCall.id = String(tc.id).trim();
|
|
463
907
|
}
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
908
|
+
// gcli2api alignment: Gemini CLI / antigravity functionCall parts always include a thoughtSignature
|
|
909
|
+
// (Cloud Code Assist expects it even when no real signature exists).
|
|
910
|
+
if (requiresThoughtSignature && !part.thoughtSignature) {
|
|
911
|
+
part.thoughtSignature = 'skip_thought_signature_validator';
|
|
467
912
|
}
|
|
468
913
|
entry.parts.push(part);
|
|
469
914
|
}
|
|
@@ -472,38 +917,102 @@ function buildGeminiRequestFromChat(chat, metadata) {
|
|
|
472
917
|
}
|
|
473
918
|
}
|
|
474
919
|
const toolOutputMap = new Map();
|
|
475
|
-
if (
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
920
|
+
if (allowFunctionCallingProtocol) {
|
|
921
|
+
if (Array.isArray(chat.toolOutputs)) {
|
|
922
|
+
for (const entry of chat.toolOutputs) {
|
|
923
|
+
if (entry && typeof entry.tool_call_id === 'string' && entry.tool_call_id.trim().length) {
|
|
924
|
+
toolOutputMap.set(entry.tool_call_id.trim(), entry);
|
|
925
|
+
}
|
|
479
926
|
}
|
|
480
927
|
}
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
928
|
+
if (toolOutputMap.size === 0) {
|
|
929
|
+
const syntheticOutputs = synthesizeToolOutputsFromMessages(chat.messages);
|
|
930
|
+
for (const output of syntheticOutputs) {
|
|
931
|
+
toolOutputMap.set(output.tool_call_id, output);
|
|
932
|
+
}
|
|
486
933
|
}
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
934
|
+
for (const output of toolOutputMap.values()) {
|
|
935
|
+
if (emittedToolOutputs.has(output.tool_call_id)) {
|
|
936
|
+
continue;
|
|
937
|
+
}
|
|
938
|
+
const resolvedName = normalizeToolName(output.name) ?? assistantToolCallNameById.get(output.tool_call_id);
|
|
939
|
+
if (resolvedName && declaredToolNames.has(resolvedName)) {
|
|
940
|
+
output.name = resolvedName;
|
|
941
|
+
contents.push(buildFunctionResponseEntry(output, { includeCallId: includeToolCallIds }));
|
|
942
|
+
emittedToolOutputs.add(output.tool_call_id);
|
|
943
|
+
}
|
|
491
944
|
}
|
|
492
|
-
contents.push(buildFunctionResponseEntry(output));
|
|
493
|
-
emittedToolOutputs.add(output.tool_call_id);
|
|
494
945
|
}
|
|
495
946
|
const request = {
|
|
496
947
|
model: chat.parameters?.model || 'models/gemini-pro',
|
|
497
948
|
contents
|
|
498
949
|
};
|
|
950
|
+
let antigravityRequestType;
|
|
951
|
+
const cleanGeminiContents = (raw) => {
|
|
952
|
+
if (!Array.isArray(raw))
|
|
953
|
+
return [];
|
|
954
|
+
const out = [];
|
|
955
|
+
for (const entry of raw) {
|
|
956
|
+
if (!isJsonObject(entry))
|
|
957
|
+
continue;
|
|
958
|
+
const partsRaw = entry.parts;
|
|
959
|
+
if (!Array.isArray(partsRaw)) {
|
|
960
|
+
out.push(entry);
|
|
961
|
+
continue;
|
|
962
|
+
}
|
|
963
|
+
const nextParts = [];
|
|
964
|
+
for (const part of partsRaw) {
|
|
965
|
+
if (!isJsonObject(part))
|
|
966
|
+
continue;
|
|
967
|
+
const hasValidValue = Object.entries(part).some(([key, value]) => {
|
|
968
|
+
if (key === 'thought')
|
|
969
|
+
return false;
|
|
970
|
+
if (value === null || value === undefined)
|
|
971
|
+
return false;
|
|
972
|
+
if (typeof value === 'string')
|
|
973
|
+
return value.length > 0;
|
|
974
|
+
if (Array.isArray(value))
|
|
975
|
+
return value.length > 0;
|
|
976
|
+
if (typeof value === 'object')
|
|
977
|
+
return Object.keys(value).length > 0;
|
|
978
|
+
return true;
|
|
979
|
+
});
|
|
980
|
+
if (!hasValidValue)
|
|
981
|
+
continue;
|
|
982
|
+
const cloned = jsonClone(part);
|
|
983
|
+
if (Object.prototype.hasOwnProperty.call(cloned, 'text')) {
|
|
984
|
+
const textValue = cloned.text;
|
|
985
|
+
if (Array.isArray(textValue)) {
|
|
986
|
+
cloned.text = textValue.map((t) => String(t ?? '')).filter((t) => t).join(' ');
|
|
987
|
+
}
|
|
988
|
+
else if (typeof textValue === 'string') {
|
|
989
|
+
cloned.text = textValue.replace(/\s+$/u, '');
|
|
990
|
+
}
|
|
991
|
+
else if (textValue !== undefined) {
|
|
992
|
+
cloned.text = String(textValue);
|
|
993
|
+
}
|
|
994
|
+
}
|
|
995
|
+
nextParts.push(cloned);
|
|
996
|
+
}
|
|
997
|
+
if (!nextParts.length)
|
|
998
|
+
continue;
|
|
999
|
+
const clonedEntry = jsonClone(entry);
|
|
1000
|
+
clonedEntry.parts = nextParts;
|
|
1001
|
+
out.push(clonedEntry);
|
|
1002
|
+
}
|
|
1003
|
+
return out;
|
|
1004
|
+
};
|
|
1005
|
+
// gcli2api normalize_gemini_request applies a strict parts cleanup pass.
|
|
1006
|
+
// We do this early to reduce Antigravity/Gemini schema drift and avoid sending empty parts.
|
|
1007
|
+
request.contents = cleanGeminiContents(request.contents);
|
|
499
1008
|
const geminiState = getProtocolState(metadata, 'gemini');
|
|
500
|
-
if (semanticsNode?.systemInstruction !== undefined) {
|
|
1009
|
+
if (!isAntigravityProvider && semanticsNode?.systemInstruction !== undefined) {
|
|
501
1010
|
request.systemInstruction = jsonClone(semanticsNode.systemInstruction);
|
|
502
1011
|
}
|
|
503
|
-
else if (geminiState?.systemInstruction !== undefined) {
|
|
1012
|
+
else if (!isAntigravityProvider && geminiState?.systemInstruction !== undefined) {
|
|
504
1013
|
request.systemInstruction = jsonClone(geminiState.systemInstruction);
|
|
505
1014
|
}
|
|
506
|
-
else {
|
|
1015
|
+
else if (!isAntigravityProvider) {
|
|
507
1016
|
const fallbackSystemInstructions = systemTextBlocksFromSemantics;
|
|
508
1017
|
if (fallbackSystemInstructions && fallbackSystemInstructions.length) {
|
|
509
1018
|
const sysBlocks = fallbackSystemInstructions
|
|
@@ -514,8 +1023,47 @@ function buildGeminiRequestFromChat(chat, metadata) {
|
|
|
514
1023
|
}
|
|
515
1024
|
}
|
|
516
1025
|
}
|
|
517
|
-
if (
|
|
518
|
-
const
|
|
1026
|
+
if (isAntigravityProvider) {
|
|
1027
|
+
const extraSegments = [];
|
|
1028
|
+
const seen = new Set();
|
|
1029
|
+
const pushSegment = (value) => {
|
|
1030
|
+
const trimmed = typeof value === 'string' ? value.trim() : '';
|
|
1031
|
+
if (!trimmed)
|
|
1032
|
+
return;
|
|
1033
|
+
if (seen.has(trimmed))
|
|
1034
|
+
return;
|
|
1035
|
+
seen.add(trimmed);
|
|
1036
|
+
extraSegments.push(trimmed);
|
|
1037
|
+
};
|
|
1038
|
+
for (const seg of collectSystemSegments(semanticsNode?.systemInstruction)) {
|
|
1039
|
+
pushSegment(seg);
|
|
1040
|
+
}
|
|
1041
|
+
for (const seg of collectSystemSegments(geminiState?.systemInstruction)) {
|
|
1042
|
+
pushSegment(seg);
|
|
1043
|
+
}
|
|
1044
|
+
for (const seg of systemTextBlocksFromSemantics || []) {
|
|
1045
|
+
if (typeof seg === 'string') {
|
|
1046
|
+
pushSegment(seg);
|
|
1047
|
+
}
|
|
1048
|
+
}
|
|
1049
|
+
// opencode-antigravity-auth alignment:
|
|
1050
|
+
// - systemInstruction uses role "user"
|
|
1051
|
+
// - the fixed Antigravity prefix is always first and merged with the first user segment when available
|
|
1052
|
+
const parts = extraSegments.length > 0
|
|
1053
|
+
? [
|
|
1054
|
+
{ text: `${ANTIGRAVITY_SYSTEM_INSTRUCTION}\n\n${extraSegments[0]}` },
|
|
1055
|
+
...extraSegments.slice(1).map((text) => ({ text }))
|
|
1056
|
+
]
|
|
1057
|
+
: [{ text: ANTIGRAVITY_SYSTEM_INSTRUCTION }];
|
|
1058
|
+
request.systemInstruction = {
|
|
1059
|
+
role: 'user',
|
|
1060
|
+
parts
|
|
1061
|
+
};
|
|
1062
|
+
}
|
|
1063
|
+
if (allowFunctionCallingProtocol && chat.tools && chat.tools.length) {
|
|
1064
|
+
const geminiTools = buildGeminiToolsFromBridge(effectiveBridgeDefs, {
|
|
1065
|
+
mode: isAntigravityProvider ? 'antigravity' : 'default'
|
|
1066
|
+
});
|
|
519
1067
|
if (geminiTools) {
|
|
520
1068
|
request.tools = geminiTools;
|
|
521
1069
|
}
|
|
@@ -532,9 +1080,140 @@ function buildGeminiRequestFromChat(chat, metadata) {
|
|
|
532
1080
|
if (Object.keys(generationConfig).length) {
|
|
533
1081
|
request.generationConfig = generationConfig;
|
|
534
1082
|
}
|
|
535
|
-
if (semanticsNode?.safetySettings !== undefined) {
|
|
1083
|
+
if (!isAntigravityProvider && semanticsNode?.safetySettings !== undefined) {
|
|
536
1084
|
request.safetySettings = jsonClone(semanticsNode.safetySettings);
|
|
537
1085
|
}
|
|
1086
|
+
else if (isAntigravityProvider) {
|
|
1087
|
+
// gcli2api alignment: Antigravity always sends a permissive safetySettings set.
|
|
1088
|
+
request.safetySettings = jsonClone(ANTIGRAVITY_DEFAULT_SAFETY_SETTINGS);
|
|
1089
|
+
}
|
|
1090
|
+
if (isAntigravityProvider && typeof request.model === 'string') {
|
|
1091
|
+
const original = request.model;
|
|
1092
|
+
const mapped = stripTierSuffix(original);
|
|
1093
|
+
const size = typeof chat.parameters?.size === 'string' ? String(chat.parameters.size) : undefined;
|
|
1094
|
+
const quality = typeof chat.parameters?.quality === 'string' ? String(chat.parameters.quality) : undefined;
|
|
1095
|
+
const config = resolveAntigravityRequestConfig({
|
|
1096
|
+
originalModel: original,
|
|
1097
|
+
mappedModel: mapped,
|
|
1098
|
+
tools: request.tools,
|
|
1099
|
+
size,
|
|
1100
|
+
quality
|
|
1101
|
+
});
|
|
1102
|
+
antigravityRequestType = config.requestType;
|
|
1103
|
+
request.model = config.finalModel || mapped;
|
|
1104
|
+
pruneSearchFunctionDeclarations(request);
|
|
1105
|
+
if (config.requestType === 'image_gen') {
|
|
1106
|
+
delete request.tools;
|
|
1107
|
+
delete request.systemInstruction;
|
|
1108
|
+
if (!isJsonObject(request.generationConfig)) {
|
|
1109
|
+
request.generationConfig = {};
|
|
1110
|
+
}
|
|
1111
|
+
const gen = request.generationConfig;
|
|
1112
|
+
delete gen.thinkingConfig;
|
|
1113
|
+
delete gen.responseMimeType;
|
|
1114
|
+
delete gen.responseModalities;
|
|
1115
|
+
if (config.imageConfig) {
|
|
1116
|
+
gen.imageConfig = config.imageConfig;
|
|
1117
|
+
}
|
|
1118
|
+
}
|
|
1119
|
+
else if (config.injectGoogleSearch) {
|
|
1120
|
+
injectGoogleSearchTool(request);
|
|
1121
|
+
}
|
|
1122
|
+
deepCleanUndefined(request);
|
|
1123
|
+
const mappedLower = String(request.model || '').toLowerCase();
|
|
1124
|
+
const isImageModel = config.requestType === 'image_gen' || mappedLower.includes('image');
|
|
1125
|
+
const isThinkingModel = !isImageModel && (mappedLower.includes('think') || mappedLower.includes('pro'));
|
|
1126
|
+
if (isThinkingModel && (!request.generationConfig || !isJsonObject(request.generationConfig))) {
|
|
1127
|
+
request.generationConfig = {};
|
|
1128
|
+
}
|
|
1129
|
+
const generationConfig = request.generationConfig;
|
|
1130
|
+
if (isThinkingModel && isJsonObject(generationConfig)) {
|
|
1131
|
+
const gc = generationConfig;
|
|
1132
|
+
const thinkingConfig = isJsonObject(gc.thinkingConfig) ? gc.thinkingConfig : {};
|
|
1133
|
+
const existingBudget = typeof thinkingConfig.thinkingBudget === 'number' ? thinkingConfig.thinkingBudget : undefined;
|
|
1134
|
+
const shouldApply = existingBudget !== undefined ? existingBudget !== 0 : true;
|
|
1135
|
+
if (shouldApply) {
|
|
1136
|
+
if (typeof thinkingConfig.thinkingBudget !== 'number') {
|
|
1137
|
+
thinkingConfig.thinkingBudget = 1024;
|
|
1138
|
+
}
|
|
1139
|
+
if (Object.prototype.hasOwnProperty.call(thinkingConfig, 'thinkingLevel')) {
|
|
1140
|
+
delete thinkingConfig.thinkingLevel;
|
|
1141
|
+
}
|
|
1142
|
+
thinkingConfig.includeThoughts = true;
|
|
1143
|
+
// For Claude routed via Antigravity:
|
|
1144
|
+
// - when tool calls exist, gcli2api drops thinkingConfig to avoid upstream failures
|
|
1145
|
+
// - otherwise, ensure the last model message begins with a thinking block signature
|
|
1146
|
+
const isClaude = mappedLower.includes('claude');
|
|
1147
|
+
if (isClaude) {
|
|
1148
|
+
const contentsArray = Array.isArray(request.contents) ? request.contents : [];
|
|
1149
|
+
const hasToolCalls = contentsArray.some((content) => {
|
|
1150
|
+
if (!isJsonObject(content))
|
|
1151
|
+
return false;
|
|
1152
|
+
const parts = content.parts;
|
|
1153
|
+
if (!Array.isArray(parts))
|
|
1154
|
+
return false;
|
|
1155
|
+
return parts.some((part) => isJsonObject(part) &&
|
|
1156
|
+
('functionCall' in part || 'function_call' in part));
|
|
1157
|
+
});
|
|
1158
|
+
if (hasToolCalls) {
|
|
1159
|
+
delete gc.thinkingConfig;
|
|
1160
|
+
}
|
|
1161
|
+
else {
|
|
1162
|
+
for (let idx = contentsArray.length - 1; idx >= 0; idx--) {
|
|
1163
|
+
const content = contentsArray[idx];
|
|
1164
|
+
if (!isJsonObject(content))
|
|
1165
|
+
continue;
|
|
1166
|
+
if (content.role !== 'model')
|
|
1167
|
+
continue;
|
|
1168
|
+
const parts = content.parts;
|
|
1169
|
+
if (!Array.isArray(parts))
|
|
1170
|
+
continue;
|
|
1171
|
+
const first = parts[0];
|
|
1172
|
+
const firstIsThinking = isJsonObject(first) && ('thought' in first || 'thoughtSignature' in first);
|
|
1173
|
+
if (!firstIsThinking) {
|
|
1174
|
+
content.parts = [
|
|
1175
|
+
{ text: '...', thoughtSignature: 'skip_thought_signature_validator' },
|
|
1176
|
+
...parts
|
|
1177
|
+
];
|
|
1178
|
+
}
|
|
1179
|
+
break;
|
|
1180
|
+
}
|
|
1181
|
+
gc.thinkingConfig = thinkingConfig;
|
|
1182
|
+
}
|
|
1183
|
+
}
|
|
1184
|
+
else {
|
|
1185
|
+
gc.thinkingConfig = thinkingConfig;
|
|
1186
|
+
}
|
|
1187
|
+
}
|
|
1188
|
+
}
|
|
1189
|
+
}
|
|
1190
|
+
// Map OpenAI-style tool_choice to Gemini toolConfig (functionCallingConfig).
|
|
1191
|
+
// This is required to reliably disable tool calling while still declaring tools
|
|
1192
|
+
// (e.g. servertool followups that must preserve tool list for session continuity).
|
|
1193
|
+
if (request.toolConfig === undefined && chat.parameters && 'tool_choice' in chat.parameters) {
|
|
1194
|
+
const choice = chat.parameters.tool_choice;
|
|
1195
|
+
if (typeof choice === 'string') {
|
|
1196
|
+
const lowered = choice.trim().toLowerCase();
|
|
1197
|
+
if (lowered === 'none') {
|
|
1198
|
+
request.toolConfig = { functionCallingConfig: { mode: 'NONE' } };
|
|
1199
|
+
}
|
|
1200
|
+
else if (lowered === 'required') {
|
|
1201
|
+
request.toolConfig = { functionCallingConfig: { mode: 'ANY' } };
|
|
1202
|
+
}
|
|
1203
|
+
}
|
|
1204
|
+
else if (choice && typeof choice === 'object' && !Array.isArray(choice)) {
|
|
1205
|
+
const type = typeof choice.type === 'string' ? String(choice.type).trim().toLowerCase() : '';
|
|
1206
|
+
const name = type === 'function' && choice.function && typeof choice.function.name === 'string'
|
|
1207
|
+
? String(choice.function.name).trim()
|
|
1208
|
+
: '';
|
|
1209
|
+
const wireName = name ? normalizeToolName(name) : undefined;
|
|
1210
|
+
if (wireName && declaredToolNames.has(wireName)) {
|
|
1211
|
+
request.toolConfig = {
|
|
1212
|
+
functionCallingConfig: { mode: 'ANY', allowedFunctionNames: [wireName] }
|
|
1213
|
+
};
|
|
1214
|
+
}
|
|
1215
|
+
}
|
|
1216
|
+
}
|
|
538
1217
|
if (chat.parameters?.tool_config && isJsonObject(chat.parameters.tool_config)) {
|
|
539
1218
|
request.toolConfig = jsonClone(chat.parameters.tool_config);
|
|
540
1219
|
}
|
|
@@ -556,7 +1235,7 @@ function buildGeminiRequestFromChat(chat, metadata) {
|
|
|
556
1235
|
request.metadata = request.metadata ?? {};
|
|
557
1236
|
request.metadata.__rcc_stream = chat.parameters.stream;
|
|
558
1237
|
}
|
|
559
|
-
if (
|
|
1238
|
+
if (hasExplicitEmptyToolsSemantics(chat) &&
|
|
560
1239
|
(!Array.isArray(chat.tools) || chat.tools.length === 0)) {
|
|
561
1240
|
request.metadata = request.metadata ?? {};
|
|
562
1241
|
request.metadata.__rcc_tools_field_present = '1';
|
|
@@ -572,9 +1251,27 @@ function buildGeminiRequestFromChat(chat, metadata) {
|
|
|
572
1251
|
}
|
|
573
1252
|
}
|
|
574
1253
|
// Apply claude-thinking compat at Gemini mapping time to ensure it is always active
|
|
575
|
-
// for Claude models, regardless of compatibilityProfile wiring.
|
|
576
|
-
// 传输层收紧(如 session_id / generationConfig),这里不做非标裁剪。
|
|
1254
|
+
// for Claude models, regardless of compatibilityProfile wiring.
|
|
577
1255
|
const compatRequest = applyClaudeThinkingToolSchemaCompat(request, adapterContext);
|
|
1256
|
+
if (isAntigravityProvider) {
|
|
1257
|
+
// opencode-antigravity-auth alignment:
|
|
1258
|
+
// Wrap the request in the agent envelope structure.
|
|
1259
|
+
// This allows passing requestType="agent" in the body, which is critical for
|
|
1260
|
+
// routing to the correct agent endpoint logic on the server side.
|
|
1261
|
+
const projectId = adapterContext?.projectId;
|
|
1262
|
+
const requestId = adapterContext?.requestId || `agent-${crypto.randomUUID()}`;
|
|
1263
|
+
const wrappedBody = {
|
|
1264
|
+
model: request.model,
|
|
1265
|
+
request: compatRequest,
|
|
1266
|
+
requestType: antigravityRequestType ?? 'agent',
|
|
1267
|
+
userAgent: 'antigravity',
|
|
1268
|
+
requestId
|
|
1269
|
+
};
|
|
1270
|
+
if (typeof projectId === 'string' && projectId.trim().length) {
|
|
1271
|
+
wrappedBody.project = projectId.trim();
|
|
1272
|
+
}
|
|
1273
|
+
return wrappedBody;
|
|
1274
|
+
}
|
|
578
1275
|
return compatRequest;
|
|
579
1276
|
}
|
|
580
1277
|
function isPlainRecord(value) {
|
|
@@ -785,7 +1482,6 @@ export class GeminiSemanticMapper {
|
|
|
785
1482
|
}
|
|
786
1483
|
}
|
|
787
1484
|
if (toolsFieldPresent) {
|
|
788
|
-
metadata.toolsFieldPresent = true;
|
|
789
1485
|
explicitEmptyTools = true;
|
|
790
1486
|
}
|
|
791
1487
|
providerMetadata = cloned;
|