@jsonstudio/llms 0.6.1397 → 0.6.1402
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 +1 -3
- package/dist/conversion/codecs/gemini-openai-codec.js +4 -10
- package/dist/conversion/compat/actions/gemini-cli-request.d.ts +2 -0
- package/dist/conversion/compat/actions/gemini-cli-request.js +490 -0
- package/dist/conversion/compat/profiles/chat-gemini-cli.json +27 -0
- package/dist/conversion/hub/operation-table/semantic-mappers/gemini-mapper.js +76 -348
- package/dist/conversion/hub/pipeline/compat/compat-pipeline-executor.js +6 -0
- package/dist/conversion/hub/pipeline/compat/compat-types.d.ts +2 -0
- package/dist/conversion/hub/pipeline/hub-pipeline.d.ts +95 -3
- package/dist/conversion/hub/pipeline/hub-pipeline.js +1365 -19
- package/dist/conversion/hub/pipeline/stages/req_inbound/req_inbound_stage3_context_capture/index.js +22 -0
- package/dist/conversion/hub/policy/policy-engine.js +50 -3
- package/dist/conversion/hub/process/chat-process.js +5 -146
- package/dist/conversion/hub/response/provider-response.js +11 -10
- package/dist/conversion/hub/response/response-mappers.d.ts +1 -3
- package/dist/conversion/hub/response/response-mappers.js +2 -20
- package/dist/conversion/hub/tool-surface/tool-surface-engine.js +2 -1
- package/dist/conversion/responses/responses-openai-bridge.js +4 -3
- package/dist/conversion/shared/gemini-tool-utils.d.ts +1 -6
- package/dist/conversion/shared/gemini-tool-utils.js +164 -542
- package/dist/conversion/shared/mcp-injection.js +29 -0
- package/dist/conversion/shared/openai-message-normalize.js +3 -17
- package/dist/filters/special/request-tool-list-filter.js +21 -13
- package/dist/filters/special/tool-filter-hooks.js +60 -3
- package/dist/router/virtual-router/bootstrap.js +8 -6
- package/dist/router/virtual-router/engine-health.d.ts +1 -1
- package/dist/router/virtual-router/engine-health.js +110 -11
- package/dist/router/virtual-router/engine-selection/alias-selection.d.ts +0 -15
- package/dist/router/virtual-router/engine-selection/alias-selection.js +4 -85
- package/dist/router/virtual-router/engine-selection/route-utils.js +6 -12
- package/dist/router/virtual-router/engine-selection/tier-selection-select.js +17 -40
- package/dist/router/virtual-router/engine-selection/tier-selection.js +2 -5
- package/dist/router/virtual-router/engine.js +6 -21
- package/dist/router/virtual-router/types.d.ts +1 -14
- package/dist/servertool/clock/config.d.ts +1 -1
- package/dist/servertool/clock/config.js +5 -9
- package/dist/servertool/engine.js +6 -83
- package/dist/servertool/handlers/gemini-empty-reply-continue.js +1 -2
- package/dist/sse/sse-to-json/builders/response-builder.js +0 -16
- package/package.json +1 -1
- package/dist/conversion/hub/pipeline/hub-pipeline/adapter-context.d.ts +0 -10
- package/dist/conversion/hub/pipeline/hub-pipeline/adapter-context.js +0 -142
- package/dist/conversion/hub/pipeline/hub-pipeline/anthropic-alias-map.d.ts +0 -6
- package/dist/conversion/hub/pipeline/hub-pipeline/anthropic-alias-map.js +0 -79
- package/dist/conversion/hub/pipeline/hub-pipeline/apply-patch-tool-mode.d.ts +0 -3
- package/dist/conversion/hub/pipeline/hub-pipeline/apply-patch-tool-mode.js +0 -46
- package/dist/conversion/hub/pipeline/hub-pipeline/execute-chat-process-entry.d.ts +0 -8
- package/dist/conversion/hub/pipeline/hub-pipeline/execute-chat-process-entry.js +0 -366
- package/dist/conversion/hub/pipeline/hub-pipeline/execute-request-stage.d.ts +0 -9
- package/dist/conversion/hub/pipeline/hub-pipeline/execute-request-stage.js +0 -390
- package/dist/conversion/hub/pipeline/hub-pipeline/node-results.d.ts +0 -3
- package/dist/conversion/hub/pipeline/hub-pipeline/node-results.js +0 -14
- package/dist/conversion/hub/pipeline/hub-pipeline/payload-normalize.d.ts +0 -2
- package/dist/conversion/hub/pipeline/hub-pipeline/payload-normalize.js +0 -144
- package/dist/conversion/hub/pipeline/hub-pipeline/policy.d.ts +0 -4
- package/dist/conversion/hub/pipeline/hub-pipeline/policy.js +0 -32
- package/dist/conversion/hub/pipeline/hub-pipeline/protocol.d.ts +0 -8
- package/dist/conversion/hub/pipeline/hub-pipeline/protocol.js +0 -63
- package/dist/conversion/hub/pipeline/hub-pipeline/resolve-protocol-hooks.d.ts +0 -2
- package/dist/conversion/hub/pipeline/hub-pipeline/resolve-protocol-hooks.js +0 -43
- package/dist/conversion/hub/pipeline/hub-pipeline/semantic-gate.d.ts +0 -1
- package/dist/conversion/hub/pipeline/hub-pipeline/semantic-gate.js +0 -29
- package/dist/conversion/hub/pipeline/hub-pipeline/servertool-runtime-config.d.ts +0 -2
- package/dist/conversion/hub/pipeline/hub-pipeline/servertool-runtime-config.js +0 -16
- package/dist/conversion/hub/pipeline/hub-pipeline/types.d.ts +0 -116
- package/dist/conversion/hub/pipeline/hub-pipeline/types.js +0 -1
- package/dist/conversion/hub/pipeline/stages/req_outbound/req_outbound_stage2_thought_signature_inject/index.d.ts +0 -10
- package/dist/conversion/hub/pipeline/stages/req_outbound/req_outbound_stage2_thought_signature_inject/index.js +0 -172
- package/dist/conversion/hub/pipeline/stages/resp_inbound/resp_inbound_stage3_thought_signature_capture/index.d.ts +0 -10
- package/dist/conversion/hub/pipeline/stages/resp_inbound/resp_inbound_stage3_thought_signature_capture/index.js +0 -71
- package/dist/conversion/hub/pipeline/thought-signature/thought-signature-center.d.ts +0 -14
- package/dist/conversion/hub/pipeline/thought-signature/thought-signature-center.js +0 -289
|
@@ -2,545 +2,145 @@ import { jsonClone } from '../hub/types/json.js';
|
|
|
2
2
|
function isPlainRecord(value) {
|
|
3
3
|
return !!value && typeof value === 'object' && !Array.isArray(value);
|
|
4
4
|
}
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
:
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
'contentEncoding',
|
|
34
|
-
'if',
|
|
35
|
-
'then',
|
|
36
|
-
'else',
|
|
37
|
-
'not',
|
|
38
|
-
'patternProperties',
|
|
39
|
-
'unevaluatedProperties',
|
|
40
|
-
'unevaluatedItems',
|
|
41
|
-
'dependentRequired',
|
|
42
|
-
'dependentSchemas',
|
|
43
|
-
'propertyNames',
|
|
44
|
-
'minContains',
|
|
45
|
-
'maxContains'
|
|
46
|
-
]);
|
|
47
|
-
const EMPTY_SCHEMA_PLACEHOLDER_NAME = '_placeholder';
|
|
48
|
-
const EMPTY_SCHEMA_PLACEHOLDER_DESCRIPTION = 'Placeholder. Always pass true.';
|
|
49
|
-
function appendDescriptionHint(schema, hint) {
|
|
50
|
-
const existing = typeof schema.description === 'string' ? schema.description : '';
|
|
51
|
-
const description = existing ? `${existing} (${hint})` : hint;
|
|
52
|
-
return { ...schema, description };
|
|
53
|
-
}
|
|
54
|
-
function convertRefsToHints(schema) {
|
|
55
|
-
if (!schema || typeof schema !== 'object') {
|
|
56
|
-
return schema;
|
|
57
|
-
}
|
|
58
|
-
if (Array.isArray(schema)) {
|
|
59
|
-
return schema.map((entry) => convertRefsToHints(entry));
|
|
60
|
-
}
|
|
61
|
-
const record = schema;
|
|
62
|
-
if (typeof record.$ref === 'string') {
|
|
63
|
-
const refVal = record.$ref;
|
|
64
|
-
const defName = refVal.includes('/') ? refVal.split('/').pop() : refVal;
|
|
65
|
-
const hint = `See: ${defName || refVal}`;
|
|
66
|
-
const base = { type: 'object' };
|
|
67
|
-
if (typeof record.description === 'string' && record.description.trim()) {
|
|
68
|
-
base.description = `${record.description} (${hint})`;
|
|
69
|
-
}
|
|
70
|
-
else {
|
|
71
|
-
base.description = hint;
|
|
5
|
+
function pickBestSchemaVariant(variants) {
|
|
6
|
+
if (!Array.isArray(variants) || variants.length === 0) {
|
|
7
|
+
return { type: 'object', properties: {} };
|
|
8
|
+
}
|
|
9
|
+
const score = (value) => {
|
|
10
|
+
if (!isPlainRecord(value)) {
|
|
11
|
+
return 0;
|
|
12
|
+
}
|
|
13
|
+
const type = typeof value.type === 'string' ? value.type.trim().toLowerCase() : '';
|
|
14
|
+
// Prefer 'string' to maximize compatibility (can carry arbitrary serialized content),
|
|
15
|
+
// then object/array, then primitives.
|
|
16
|
+
if (type === 'string')
|
|
17
|
+
return 100;
|
|
18
|
+
if (type === 'object')
|
|
19
|
+
return 80;
|
|
20
|
+
if (type === 'array')
|
|
21
|
+
return 60;
|
|
22
|
+
if (type === 'integer' || type === 'number' || type === 'boolean')
|
|
23
|
+
return 50;
|
|
24
|
+
return 10;
|
|
25
|
+
};
|
|
26
|
+
let best = variants[0];
|
|
27
|
+
let bestScore = score(best);
|
|
28
|
+
for (const candidate of variants.slice(1)) {
|
|
29
|
+
const candidateScore = score(candidate);
|
|
30
|
+
if (candidateScore > bestScore) {
|
|
31
|
+
best = candidate;
|
|
32
|
+
bestScore = candidateScore;
|
|
72
33
|
}
|
|
73
|
-
return base;
|
|
74
|
-
}
|
|
75
|
-
const out = {};
|
|
76
|
-
for (const [key, value] of Object.entries(record)) {
|
|
77
|
-
out[key] = convertRefsToHints(value);
|
|
78
34
|
}
|
|
79
|
-
return
|
|
35
|
+
return best;
|
|
80
36
|
}
|
|
81
|
-
function
|
|
82
|
-
if (!
|
|
83
|
-
return
|
|
37
|
+
function normalizeSchemaTypes(value) {
|
|
38
|
+
if (!value || typeof value !== 'object') {
|
|
39
|
+
return value;
|
|
84
40
|
}
|
|
85
|
-
if (Array.isArray(
|
|
86
|
-
return
|
|
41
|
+
if (Array.isArray(value)) {
|
|
42
|
+
return value.map((entry) => normalizeSchemaTypes(entry));
|
|
87
43
|
}
|
|
88
|
-
const record =
|
|
44
|
+
const record = value;
|
|
89
45
|
const out = {};
|
|
90
|
-
for (const [key,
|
|
91
|
-
if (key === '
|
|
92
|
-
out
|
|
46
|
+
for (const [key, val] of Object.entries(record)) {
|
|
47
|
+
if (key === 'type' && typeof val === 'string') {
|
|
48
|
+
out[key] = val.toUpperCase();
|
|
93
49
|
continue;
|
|
94
50
|
}
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
return out;
|
|
98
|
-
}
|
|
99
|
-
function addEnumHints(schema) {
|
|
100
|
-
if (!schema || typeof schema !== 'object') {
|
|
101
|
-
return schema;
|
|
102
|
-
}
|
|
103
|
-
if (Array.isArray(schema)) {
|
|
104
|
-
return schema.map((entry) => addEnumHints(entry));
|
|
105
|
-
}
|
|
106
|
-
const record = schema;
|
|
107
|
-
let out = { ...record };
|
|
108
|
-
if (Array.isArray(record.enum) && record.enum.length > 1 && record.enum.length <= 10) {
|
|
109
|
-
const vals = record.enum.map((v) => String(v)).join(', ');
|
|
110
|
-
out = appendDescriptionHint(out, `Allowed: ${vals}`);
|
|
111
|
-
}
|
|
112
|
-
for (const [key, value] of Object.entries(out)) {
|
|
113
|
-
if (key === 'enum')
|
|
51
|
+
if ((key === 'properties' || key === 'items') && val && typeof val === 'object') {
|
|
52
|
+
out[key] = normalizeSchemaTypes(val);
|
|
114
53
|
continue;
|
|
115
|
-
if (value && typeof value === 'object') {
|
|
116
|
-
out[key] = addEnumHints(value);
|
|
117
54
|
}
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
}
|
|
121
|
-
function addAdditionalPropertiesHints(schema) {
|
|
122
|
-
if (!schema || typeof schema !== 'object') {
|
|
123
|
-
return schema;
|
|
124
|
-
}
|
|
125
|
-
if (Array.isArray(schema)) {
|
|
126
|
-
return schema.map((entry) => addAdditionalPropertiesHints(entry));
|
|
127
|
-
}
|
|
128
|
-
const record = schema;
|
|
129
|
-
let out = { ...record };
|
|
130
|
-
if (record.additionalProperties === false) {
|
|
131
|
-
out = appendDescriptionHint(out, 'No extra properties allowed');
|
|
132
|
-
}
|
|
133
|
-
for (const [key, value] of Object.entries(out)) {
|
|
134
|
-
if (key === 'additionalProperties')
|
|
55
|
+
if ((key === 'anyOf' || key === 'oneOf' || key === 'allOf') && Array.isArray(val)) {
|
|
56
|
+
out[key] = val.map((entry) => normalizeSchemaTypes(entry));
|
|
135
57
|
continue;
|
|
136
|
-
if (value && typeof value === 'object') {
|
|
137
|
-
out[key] = addAdditionalPropertiesHints(value);
|
|
138
58
|
}
|
|
59
|
+
out[key] = normalizeSchemaTypes(val);
|
|
139
60
|
}
|
|
140
61
|
return out;
|
|
141
62
|
}
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
if (!schema || typeof schema !== 'object') {
|
|
146
|
-
return schema;
|
|
147
|
-
}
|
|
148
|
-
if (Array.isArray(schema)) {
|
|
149
|
-
return schema.map((entry) => mergeAllOf(entry));
|
|
63
|
+
function cloneParameters(value) {
|
|
64
|
+
if (value === null || typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {
|
|
65
|
+
return value;
|
|
150
66
|
}
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
if (
|
|
155
|
-
const
|
|
156
|
-
const
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
}
|
|
163
|
-
if (Array.isArray(item.required)) {
|
|
164
|
-
for (const req of item.required) {
|
|
165
|
-
if (typeof req === 'string' && !mergedRequired.includes(req)) {
|
|
166
|
-
mergedRequired.push(req);
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
for (const [key, value] of Object.entries(item)) {
|
|
171
|
-
if (key === 'properties' || key === 'required')
|
|
67
|
+
if (Array.isArray(value)) {
|
|
68
|
+
return value.map((entry) => cloneParameters(entry));
|
|
69
|
+
}
|
|
70
|
+
if (isPlainRecord(value)) {
|
|
71
|
+
// Normalize JSON Schema const → enum to improve compatibility with functionDeclarations.parameters.
|
|
72
|
+
// Some upstreams reject `const` but accept `enum`.
|
|
73
|
+
if (Object.prototype.hasOwnProperty.call(value, 'const') && !Object.prototype.hasOwnProperty.call(value, 'enum')) {
|
|
74
|
+
const cloned = {};
|
|
75
|
+
for (const [key, entry] of Object.entries(value)) {
|
|
76
|
+
if (key === 'const') {
|
|
77
|
+
cloned.enum = [cloneParameters(entry)];
|
|
172
78
|
continue;
|
|
173
|
-
if (merged[key] === undefined)
|
|
174
|
-
merged[key] = value;
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
delete out.allOf;
|
|
178
|
-
if (merged.properties) {
|
|
179
|
-
out.properties = { ...(isPlainRecord(out.properties) ? out.properties : {}), ...merged.properties };
|
|
180
|
-
}
|
|
181
|
-
if (mergedRequired.length > 0) {
|
|
182
|
-
const existingRequired = Array.isArray(out.required) ? out.required.filter((r) => typeof r === 'string') : [];
|
|
183
|
-
out.required = Array.from(new Set([...existingRequired, ...mergedRequired]));
|
|
184
|
-
}
|
|
185
|
-
for (const [key, value] of Object.entries(merged)) {
|
|
186
|
-
if (key === 'properties' || key === 'required')
|
|
187
|
-
continue;
|
|
188
|
-
if (out[key] === undefined)
|
|
189
|
-
out[key] = value;
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
for (const [key, value] of Object.entries(out)) {
|
|
193
|
-
if (value && typeof value === 'object') {
|
|
194
|
-
out[key] = mergeAllOf(value);
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
return out;
|
|
198
|
-
}
|
|
199
|
-
function tryMergeEnumFromUnion(options) {
|
|
200
|
-
if (!Array.isArray(options) || options.length === 0)
|
|
201
|
-
return null;
|
|
202
|
-
const enumValues = [];
|
|
203
|
-
for (const option of options) {
|
|
204
|
-
if (!isPlainRecord(option))
|
|
205
|
-
return null;
|
|
206
|
-
if (option.const !== undefined) {
|
|
207
|
-
enumValues.push(option.const);
|
|
208
|
-
continue;
|
|
209
|
-
}
|
|
210
|
-
if (Array.isArray(option.enum) && option.enum.length > 0) {
|
|
211
|
-
enumValues.push(...option.enum);
|
|
212
|
-
continue;
|
|
213
|
-
}
|
|
214
|
-
if (option.properties || option.items || option.anyOf || option.oneOf || option.allOf) {
|
|
215
|
-
return null;
|
|
216
|
-
}
|
|
217
|
-
if (typeof option.type === 'string' && option.type) {
|
|
218
|
-
return null;
|
|
219
|
-
}
|
|
220
|
-
return null;
|
|
221
|
-
}
|
|
222
|
-
return enumValues.length > 0 ? enumValues : null;
|
|
223
|
-
}
|
|
224
|
-
function scoreSchemaOption(schema) {
|
|
225
|
-
if (!isPlainRecord(schema))
|
|
226
|
-
return { score: 0, typeName: 'unknown' };
|
|
227
|
-
const type = typeof schema.type === 'string' ? schema.type.toLowerCase() : '';
|
|
228
|
-
// Prefer 'string' to maximize compatibility (can carry arbitrary serialized content),
|
|
229
|
-
// then object/array, then primitives. This mirrors our historical Gemini tool behavior
|
|
230
|
-
// and avoids breaking tools expecting string args (e.g. cmd/command).
|
|
231
|
-
if (type === 'string')
|
|
232
|
-
return { score: 4, typeName: 'string' };
|
|
233
|
-
if (type === 'object' || schema.properties)
|
|
234
|
-
return { score: 3, typeName: 'object' };
|
|
235
|
-
if (type === 'array' || schema.items)
|
|
236
|
-
return { score: 2, typeName: 'array' };
|
|
237
|
-
if (type && type !== 'null')
|
|
238
|
-
return { score: 1, typeName: type };
|
|
239
|
-
return { score: 0, typeName: type || 'null' };
|
|
240
|
-
}
|
|
241
|
-
function flattenAnyOfOneOf(schema) {
|
|
242
|
-
if (!schema || typeof schema !== 'object') {
|
|
243
|
-
return schema;
|
|
244
|
-
}
|
|
245
|
-
if (Array.isArray(schema)) {
|
|
246
|
-
return schema.map((entry) => flattenAnyOfOneOf(entry));
|
|
247
|
-
}
|
|
248
|
-
let out = { ...schema };
|
|
249
|
-
for (const unionKey of ['anyOf', 'oneOf']) {
|
|
250
|
-
const options = out[unionKey];
|
|
251
|
-
if (Array.isArray(options) && options.length > 0) {
|
|
252
|
-
const parentDesc = typeof out.description === 'string' ? out.description : '';
|
|
253
|
-
const mergedEnum = tryMergeEnumFromUnion(options);
|
|
254
|
-
if (mergedEnum) {
|
|
255
|
-
const next = { ...out };
|
|
256
|
-
delete next[unionKey];
|
|
257
|
-
next.type = 'string';
|
|
258
|
-
next.enum = mergedEnum;
|
|
259
|
-
if (parentDesc)
|
|
260
|
-
next.description = parentDesc;
|
|
261
|
-
out = next;
|
|
262
|
-
continue;
|
|
263
|
-
}
|
|
264
|
-
let bestIdx = 0;
|
|
265
|
-
let bestScore = -1;
|
|
266
|
-
const allTypes = [];
|
|
267
|
-
for (let i = 0; i < options.length; i++) {
|
|
268
|
-
const { score, typeName } = scoreSchemaOption(options[i]);
|
|
269
|
-
allTypes.push(typeName);
|
|
270
|
-
if (score > bestScore) {
|
|
271
|
-
bestScore = score;
|
|
272
|
-
bestIdx = i;
|
|
273
|
-
}
|
|
274
|
-
}
|
|
275
|
-
let selected = flattenAnyOfOneOf(options[bestIdx]) || { type: 'string' };
|
|
276
|
-
if (isPlainRecord(selected)) {
|
|
277
|
-
let selectedRecord = selected;
|
|
278
|
-
if (parentDesc) {
|
|
279
|
-
const childDesc = typeof selectedRecord.description === 'string' ? selectedRecord.description : '';
|
|
280
|
-
selectedRecord = childDesc && childDesc !== parentDesc
|
|
281
|
-
? { ...selectedRecord, description: `${parentDesc} (${childDesc})` }
|
|
282
|
-
: childDesc
|
|
283
|
-
? selectedRecord
|
|
284
|
-
: { ...selectedRecord, description: parentDesc };
|
|
285
|
-
}
|
|
286
|
-
const uniqueTypes = Array.from(new Set(allTypes.filter(Boolean)));
|
|
287
|
-
if (uniqueTypes.length > 1) {
|
|
288
|
-
selectedRecord = appendDescriptionHint(selectedRecord, `Accepts: ${uniqueTypes.join(' | ')}`);
|
|
289
|
-
}
|
|
290
|
-
const next = { ...out };
|
|
291
|
-
delete next[unionKey];
|
|
292
|
-
delete next.description;
|
|
293
|
-
out = { ...next, ...selectedRecord };
|
|
294
|
-
}
|
|
295
|
-
else {
|
|
296
|
-
delete out[unionKey];
|
|
297
|
-
}
|
|
298
|
-
}
|
|
299
|
-
}
|
|
300
|
-
for (const [key, value] of Object.entries(out)) {
|
|
301
|
-
if (value && typeof value === 'object') {
|
|
302
|
-
out[key] = flattenAnyOfOneOf(value);
|
|
303
|
-
}
|
|
304
|
-
}
|
|
305
|
-
return out;
|
|
306
|
-
}
|
|
307
|
-
function flattenTypeArrays(schema, nullableFields, currentPath) {
|
|
308
|
-
if (!schema || typeof schema !== 'object') {
|
|
309
|
-
return schema;
|
|
310
|
-
}
|
|
311
|
-
if (Array.isArray(schema)) {
|
|
312
|
-
return schema.map((entry, idx) => flattenTypeArrays(entry, nullableFields, `${currentPath || ''}[${idx}]`));
|
|
313
|
-
}
|
|
314
|
-
let out = { ...schema };
|
|
315
|
-
const localNullableFields = nullableFields || new Map();
|
|
316
|
-
if (Array.isArray(out.type)) {
|
|
317
|
-
const types = out.type.filter((t) => typeof t === 'string' && t.trim().length > 0);
|
|
318
|
-
const hasNull = types.some((t) => t === 'null');
|
|
319
|
-
const nonNull = types.filter((t) => t !== 'null');
|
|
320
|
-
const first = nonNull.length > 0 ? nonNull[0] : 'string';
|
|
321
|
-
out.type = first;
|
|
322
|
-
if (nonNull.length > 1) {
|
|
323
|
-
out = appendDescriptionHint(out, `Accepts: ${Array.from(new Set(nonNull)).join(' | ')}`);
|
|
324
|
-
}
|
|
325
|
-
if (hasNull) {
|
|
326
|
-
out = appendDescriptionHint(out, 'nullable');
|
|
327
|
-
}
|
|
328
|
-
}
|
|
329
|
-
if (isPlainRecord(out.properties)) {
|
|
330
|
-
const nextProps = {};
|
|
331
|
-
for (const [propKey, propValue] of Object.entries(out.properties)) {
|
|
332
|
-
const propPath = currentPath ? `${currentPath}.properties.${propKey}` : `properties.${propKey}`;
|
|
333
|
-
const processed = flattenTypeArrays(propValue, localNullableFields, propPath);
|
|
334
|
-
nextProps[propKey] = processed;
|
|
335
|
-
if (isPlainRecord(processed) &&
|
|
336
|
-
typeof processed.description === 'string' &&
|
|
337
|
-
processed.description.includes('nullable')) {
|
|
338
|
-
const objectPath = currentPath || '';
|
|
339
|
-
const existing = localNullableFields.get(objectPath) || [];
|
|
340
|
-
existing.push(propKey);
|
|
341
|
-
localNullableFields.set(objectPath, existing);
|
|
342
|
-
}
|
|
343
|
-
}
|
|
344
|
-
out.properties = nextProps;
|
|
345
|
-
}
|
|
346
|
-
// Remove nullable fields from required array (root-level cleanup, aligned with opencode).
|
|
347
|
-
if (Array.isArray(out.required) && !nullableFields) {
|
|
348
|
-
const nullableAtRoot = localNullableFields.get(currentPath || '') || [];
|
|
349
|
-
if (nullableAtRoot.length > 0) {
|
|
350
|
-
const nextRequired = out.required.filter((r) => typeof r === 'string' && !nullableAtRoot.includes(r));
|
|
351
|
-
out.required = nextRequired;
|
|
352
|
-
if (nextRequired.length === 0) {
|
|
353
|
-
delete out.required;
|
|
354
|
-
}
|
|
355
|
-
}
|
|
356
|
-
}
|
|
357
|
-
for (const [key, value] of Object.entries(out)) {
|
|
358
|
-
if (key === 'properties')
|
|
359
|
-
continue;
|
|
360
|
-
if (value && typeof value === 'object') {
|
|
361
|
-
out[key] = flattenTypeArrays(value, localNullableFields, `${currentPath || ''}.${key}`);
|
|
362
|
-
}
|
|
363
|
-
}
|
|
364
|
-
return out;
|
|
365
|
-
}
|
|
366
|
-
function removeUnsupportedKeywords(schema, insideProperties = false) {
|
|
367
|
-
if (!schema || typeof schema !== 'object') {
|
|
368
|
-
return schema;
|
|
369
|
-
}
|
|
370
|
-
if (Array.isArray(schema)) {
|
|
371
|
-
return schema.map((entry) => removeUnsupportedKeywords(entry, false));
|
|
372
|
-
}
|
|
373
|
-
const record = schema;
|
|
374
|
-
const out = {};
|
|
375
|
-
for (const [key, value] of Object.entries(record)) {
|
|
376
|
-
if (!insideProperties && UNSUPPORTED_KEYWORDS.has(key)) {
|
|
377
|
-
continue;
|
|
378
|
-
}
|
|
379
|
-
if (value && typeof value === 'object') {
|
|
380
|
-
if (key === 'properties' && isPlainRecord(value)) {
|
|
381
|
-
const propsOut = {};
|
|
382
|
-
for (const [propName, propSchema] of Object.entries(value)) {
|
|
383
|
-
propsOut[propName] = removeUnsupportedKeywords(propSchema, false);
|
|
384
|
-
}
|
|
385
|
-
out[key] = propsOut;
|
|
386
|
-
}
|
|
387
|
-
else {
|
|
388
|
-
out[key] = removeUnsupportedKeywords(value, false);
|
|
389
|
-
}
|
|
390
|
-
}
|
|
391
|
-
else {
|
|
392
|
-
out[key] = value;
|
|
393
|
-
}
|
|
394
|
-
}
|
|
395
|
-
return out;
|
|
396
|
-
}
|
|
397
|
-
function cleanupRequiredFields(schema) {
|
|
398
|
-
if (!schema || typeof schema !== 'object') {
|
|
399
|
-
return schema;
|
|
400
|
-
}
|
|
401
|
-
if (Array.isArray(schema)) {
|
|
402
|
-
return schema.map((entry) => cleanupRequiredFields(entry));
|
|
403
|
-
}
|
|
404
|
-
let out = { ...schema };
|
|
405
|
-
if (Array.isArray(out.required) && isPlainRecord(out.properties)) {
|
|
406
|
-
const props = out.properties;
|
|
407
|
-
const valid = out.required.filter((req) => typeof req === 'string' && Object.prototype.hasOwnProperty.call(props, req));
|
|
408
|
-
if (valid.length === 0) {
|
|
409
|
-
delete out.required;
|
|
410
|
-
}
|
|
411
|
-
else if (valid.length !== out.required.length) {
|
|
412
|
-
out.required = valid;
|
|
413
|
-
}
|
|
414
|
-
}
|
|
415
|
-
for (const [key, value] of Object.entries(out)) {
|
|
416
|
-
if (value && typeof value === 'object') {
|
|
417
|
-
out[key] = cleanupRequiredFields(value);
|
|
418
|
-
}
|
|
419
|
-
}
|
|
420
|
-
return out;
|
|
421
|
-
}
|
|
422
|
-
function addEmptySchemaPlaceholder(schema) {
|
|
423
|
-
if (!schema || typeof schema !== 'object') {
|
|
424
|
-
return schema;
|
|
425
|
-
}
|
|
426
|
-
if (Array.isArray(schema)) {
|
|
427
|
-
return schema.map((entry) => addEmptySchemaPlaceholder(entry));
|
|
428
|
-
}
|
|
429
|
-
let out = { ...schema };
|
|
430
|
-
const typeRaw = typeof out.type === 'string' ? out.type.toLowerCase() : '';
|
|
431
|
-
const isObject = typeRaw === 'object' || isPlainRecord(out.properties);
|
|
432
|
-
if (isObject) {
|
|
433
|
-
const hasProps = isPlainRecord(out.properties) && Object.keys(out.properties).length > 0;
|
|
434
|
-
if (!hasProps) {
|
|
435
|
-
out.type = 'object';
|
|
436
|
-
out.properties = {
|
|
437
|
-
[EMPTY_SCHEMA_PLACEHOLDER_NAME]: {
|
|
438
|
-
type: 'boolean',
|
|
439
|
-
description: EMPTY_SCHEMA_PLACEHOLDER_DESCRIPTION
|
|
440
79
|
}
|
|
441
|
-
|
|
442
|
-
out.required = [EMPTY_SCHEMA_PLACEHOLDER_NAME];
|
|
443
|
-
}
|
|
444
|
-
}
|
|
445
|
-
for (const [key, value] of Object.entries(out)) {
|
|
446
|
-
if (value && typeof value === 'object') {
|
|
447
|
-
out[key] = addEmptySchemaPlaceholder(value);
|
|
448
|
-
}
|
|
449
|
-
}
|
|
450
|
-
return out;
|
|
451
|
-
}
|
|
452
|
-
function cleanJSONSchemaForAntigravity(schema) {
|
|
453
|
-
if (!schema || typeof schema !== 'object') {
|
|
454
|
-
return schema;
|
|
455
|
-
}
|
|
456
|
-
let out = schema;
|
|
457
|
-
// Keep this helper for backward compatibility in other call sites, but avoid
|
|
458
|
-
// aggressive rewriting that can destabilize schema comparison across turns.
|
|
459
|
-
out = removeUnsupportedKeywords(out);
|
|
460
|
-
out = cleanupRequiredFields(out);
|
|
461
|
-
return out;
|
|
462
|
-
}
|
|
463
|
-
function toGeminiSchema(schema) {
|
|
464
|
-
if (schema === null || typeof schema === 'string' || typeof schema === 'number' || typeof schema === 'boolean') {
|
|
465
|
-
return schema;
|
|
466
|
-
}
|
|
467
|
-
if (Array.isArray(schema)) {
|
|
468
|
-
return schema.map((entry) => toGeminiSchema(entry));
|
|
469
|
-
}
|
|
470
|
-
if (!isPlainRecord(schema)) {
|
|
471
|
-
return schema;
|
|
472
|
-
}
|
|
473
|
-
const input = schema;
|
|
474
|
-
const out = {};
|
|
475
|
-
const propertyNames = new Set();
|
|
476
|
-
if (isPlainRecord(input.properties)) {
|
|
477
|
-
for (const key of Object.keys(input.properties)) {
|
|
478
|
-
propertyNames.add(key);
|
|
479
|
-
}
|
|
480
|
-
}
|
|
481
|
-
for (const [key, value] of Object.entries(input)) {
|
|
482
|
-
if (UNSUPPORTED_KEYWORDS.has(key)) {
|
|
483
|
-
continue;
|
|
484
|
-
}
|
|
485
|
-
if (key === 'type' && typeof value === 'string') {
|
|
486
|
-
out[key] = value.toUpperCase();
|
|
487
|
-
continue;
|
|
488
|
-
}
|
|
489
|
-
if (key === 'properties' && isPlainRecord(value)) {
|
|
490
|
-
const props = {};
|
|
491
|
-
for (const [propName, propSchema] of Object.entries(value)) {
|
|
492
|
-
props[propName] = toGeminiSchema(propSchema);
|
|
80
|
+
cloned[key] = cloneParameters(entry);
|
|
493
81
|
}
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
const validRequired = value.filter((entry) => typeof entry === 'string' && propertyNames.has(entry));
|
|
516
|
-
// Filter required array to only include properties that exist.
|
|
517
|
-
if (validRequired.length > 0) {
|
|
518
|
-
out[key] = validRequired;
|
|
519
|
-
}
|
|
82
|
+
return cloned;
|
|
83
|
+
}
|
|
84
|
+
// Gemini function_declarations.parameters only support a subset of JSON Schema.
|
|
85
|
+
// Additionally, recent Antigravity/Gemini backends may produce MALFORMED_FUNCTION_CALL
|
|
86
|
+
// when schemas include combinators (oneOf/anyOf/allOf). Prefer a safe single-variant schema.
|
|
87
|
+
const combinator = Array.isArray(value.oneOf)
|
|
88
|
+
? 'oneOf'
|
|
89
|
+
: Array.isArray(value.anyOf)
|
|
90
|
+
? 'anyOf'
|
|
91
|
+
: Array.isArray(value.allOf)
|
|
92
|
+
? 'allOf'
|
|
93
|
+
: undefined;
|
|
94
|
+
if (combinator) {
|
|
95
|
+
const variants = value[combinator];
|
|
96
|
+
const chosen = pickBestSchemaVariant(Array.isArray(variants) ? variants : []);
|
|
97
|
+
const simplified = cloneParameters(chosen);
|
|
98
|
+
// Preserve description if present on the wrapper node.
|
|
99
|
+
if (isPlainRecord(simplified) &&
|
|
100
|
+
typeof value.description === 'string' &&
|
|
101
|
+
typeof simplified.description !== 'string') {
|
|
102
|
+
simplified.description = String(value.description);
|
|
520
103
|
}
|
|
521
|
-
|
|
522
|
-
|
|
104
|
+
return simplified;
|
|
105
|
+
}
|
|
106
|
+
const cloned = {};
|
|
107
|
+
for (const [key, entry] of Object.entries(value)) {
|
|
108
|
+
// Gemini function_declarations.parameters only support a subset of JSON Schema.
|
|
109
|
+
// Drop meta/unsupported fields that cause INVALID_ARGUMENT, such as $schema/exclusiveMinimum/propertyNames.
|
|
110
|
+
if (typeof key === 'string') {
|
|
111
|
+
if (key.startsWith('$'))
|
|
112
|
+
continue;
|
|
113
|
+
if (key === 'const')
|
|
114
|
+
continue;
|
|
115
|
+
if (key === 'default' || key === 'examples' || key === 'example' || key === 'title')
|
|
116
|
+
continue;
|
|
117
|
+
if (key === 'deprecated' || key === 'readOnly' || key === 'writeOnly')
|
|
118
|
+
continue;
|
|
119
|
+
if (key === 'patternProperties' || key === 'dependentRequired' || key === 'dependentSchemas')
|
|
120
|
+
continue;
|
|
121
|
+
if (key === 'unevaluatedProperties' || key === 'unevaluatedItems')
|
|
122
|
+
continue;
|
|
123
|
+
if (key === 'contains' || key === 'minContains' || key === 'maxContains')
|
|
124
|
+
continue;
|
|
125
|
+
if (key === 'contentEncoding' || key === 'contentMediaType' || key === 'contentSchema')
|
|
126
|
+
continue;
|
|
127
|
+
if (key === 'if' || key === 'then' || key === 'else' || key === 'not')
|
|
128
|
+
continue;
|
|
129
|
+
if (key === 'exclusiveMinimum' || key === 'exclusiveMaximum' || key === 'propertyNames')
|
|
130
|
+
continue;
|
|
131
|
+
// Keep Gemini tool schemas mostly permissive to avoid upstream MALFORMED_FUNCTION_CALL on strict validation.
|
|
132
|
+
// Preserve `required` for Gemini validation (align with gcli2api), but still drop additionalProperties.
|
|
133
|
+
if (key === 'additionalProperties')
|
|
134
|
+
continue;
|
|
135
|
+
// Combinators are handled at the node level above.
|
|
136
|
+
if (key === 'oneOf' || key === 'anyOf' || key === 'allOf')
|
|
137
|
+
continue;
|
|
523
138
|
}
|
|
524
|
-
|
|
139
|
+
cloned[key] = cloneParameters(entry);
|
|
525
140
|
}
|
|
526
|
-
|
|
527
|
-
}
|
|
528
|
-
if (out.type === 'ARRAY' && !('items' in out)) {
|
|
529
|
-
out.items = { type: 'STRING' };
|
|
141
|
+
return cloned;
|
|
530
142
|
}
|
|
531
|
-
return
|
|
532
|
-
}
|
|
533
|
-
function cloneParameters(value, mode = 'default') {
|
|
534
|
-
if (value === null || typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {
|
|
535
|
-
return value;
|
|
536
|
-
}
|
|
537
|
-
const cloned = jsonClone(value);
|
|
538
|
-
if (mode === 'antigravity') {
|
|
539
|
-
// gcli2api-style: minimal, deterministic transform (no schema hint rewriting).
|
|
540
|
-
return toGeminiSchema(cloned);
|
|
541
|
-
}
|
|
542
|
-
// Default Gemini: avoid aggressive schema rewriting (keep original shape where possible).
|
|
543
|
-
return convertConstToEnum(cloned);
|
|
143
|
+
return { type: 'object', properties: {} };
|
|
544
144
|
}
|
|
545
145
|
function declarationToBridge(declaration) {
|
|
546
146
|
if (!declaration || typeof declaration !== 'object') {
|
|
@@ -552,7 +152,7 @@ function declarationToBridge(declaration) {
|
|
|
552
152
|
return null;
|
|
553
153
|
}
|
|
554
154
|
const description = typeof def.description === 'string' ? def.description : undefined;
|
|
555
|
-
const parameters = cloneParameters(def.parameters ?? { type: 'object', properties: {} }
|
|
155
|
+
const parameters = cloneParameters(def.parameters ?? { type: 'object', properties: {} });
|
|
556
156
|
return {
|
|
557
157
|
type: 'function',
|
|
558
158
|
function: {
|
|
@@ -568,7 +168,7 @@ function legacyToolToBridge(entry) {
|
|
|
568
168
|
return null;
|
|
569
169
|
}
|
|
570
170
|
const description = typeof entry.description === 'string' ? entry.description : undefined;
|
|
571
|
-
const parameters = cloneParameters(entry.parameters ?? { type: 'object', properties: {} }
|
|
171
|
+
const parameters = cloneParameters(entry.parameters ?? { type: 'object', properties: {} });
|
|
572
172
|
return {
|
|
573
173
|
type: 'function',
|
|
574
174
|
function: {
|
|
@@ -625,16 +225,15 @@ export function buildGeminiToolsFromBridge(defs, options) {
|
|
|
625
225
|
// Keep all function declarations in a single tool group to match gcli2api snapshots
|
|
626
226
|
// and avoid backend-specific limits on the number of tool groups.
|
|
627
227
|
const functionDeclarations = [];
|
|
228
|
+
const normalizeToolName = (name) => mode === 'antigravity' ? String(name || '').replace(/-/g, '_') : String(name || '');
|
|
628
229
|
const applyFixups = (name, parameters) => {
|
|
629
230
|
if (!parameters || typeof parameters !== 'object' || Array.isArray(parameters)) {
|
|
630
231
|
return parameters;
|
|
631
232
|
}
|
|
632
|
-
if (mode !== 'antigravity') {
|
|
633
|
-
return parameters;
|
|
634
|
-
}
|
|
635
233
|
const params = parameters;
|
|
636
234
|
const propsRaw = params.properties;
|
|
637
235
|
const props = isPlainRecord(propsRaw) ? propsRaw : {};
|
|
236
|
+
const isAntigravity = mode === 'antigravity';
|
|
638
237
|
const lowered = String(name || '').trim().toLowerCase();
|
|
639
238
|
if (lowered === 'exec_command') {
|
|
640
239
|
// Keep Gemini tool schema aligned with both historical Codex and our runtime tool signature:
|
|
@@ -646,20 +245,39 @@ export function buildGeminiToolsFromBridge(defs, options) {
|
|
|
646
245
|
// is disabled (toolConfig.mode=NONE). Force these fields to simple STRING.
|
|
647
246
|
// For Gemini wire, prefer `command` as the canonical key (gcli2api-style).
|
|
648
247
|
// The internal tool governor normalizes `command` → `cmd` before execution.
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
248
|
+
if (isAntigravity) {
|
|
249
|
+
try {
|
|
250
|
+
if (Object.prototype.hasOwnProperty.call(props, 'cmd')) {
|
|
251
|
+
delete props.cmd;
|
|
252
|
+
}
|
|
652
253
|
}
|
|
254
|
+
catch {
|
|
255
|
+
// ignore
|
|
256
|
+
}
|
|
257
|
+
props.command = {
|
|
258
|
+
type: 'STRING',
|
|
259
|
+
description: 'Shell command to execute.'
|
|
260
|
+
};
|
|
261
|
+
// Keep workdir simple (avoid anyOf string|null patterns).
|
|
262
|
+
props.workdir = { type: 'STRING', description: 'Working directory.' };
|
|
653
263
|
}
|
|
654
|
-
|
|
655
|
-
|
|
264
|
+
else {
|
|
265
|
+
if (!Object.prototype.hasOwnProperty.call(props, 'cmd') && Object.prototype.hasOwnProperty.call(props, 'command')) {
|
|
266
|
+
props.cmd = props.command;
|
|
267
|
+
}
|
|
268
|
+
if (!Object.prototype.hasOwnProperty.call(props, 'command') && Object.prototype.hasOwnProperty.call(props, 'cmd')) {
|
|
269
|
+
props.command = props.cmd;
|
|
270
|
+
}
|
|
271
|
+
if (!Object.prototype.hasOwnProperty.call(props, 'cmd')) {
|
|
272
|
+
props.cmd = { type: 'string' };
|
|
273
|
+
}
|
|
274
|
+
if (!Object.prototype.hasOwnProperty.call(props, 'command')) {
|
|
275
|
+
props.command = { type: 'string' };
|
|
276
|
+
}
|
|
277
|
+
if (!Object.prototype.hasOwnProperty.call(props, 'workdir')) {
|
|
278
|
+
props.workdir = { type: 'string' };
|
|
279
|
+
}
|
|
656
280
|
}
|
|
657
|
-
props.command = {
|
|
658
|
-
type: 'STRING',
|
|
659
|
-
description: 'Shell command to execute.'
|
|
660
|
-
};
|
|
661
|
-
// Keep workdir simple (avoid anyOf string|null patterns).
|
|
662
|
-
props.workdir = { type: 'STRING', description: 'Working directory.' };
|
|
663
281
|
params.properties = props;
|
|
664
282
|
// Avoid hard required keys for Gemini: the model may emit either alias (cmd/command),
|
|
665
283
|
// and "required" mismatch surfaces as MALFORMED_FUNCTION_CALL (empty reply) upstream.
|
|
@@ -680,10 +298,10 @@ export function buildGeminiToolsFromBridge(defs, options) {
|
|
|
680
298
|
props.text = props.chars;
|
|
681
299
|
}
|
|
682
300
|
if (!Object.prototype.hasOwnProperty.call(props, 'session_id')) {
|
|
683
|
-
props.session_id = { type: 'NUMBER' };
|
|
301
|
+
props.session_id = { type: isAntigravity ? 'NUMBER' : 'number' };
|
|
684
302
|
}
|
|
685
303
|
if (!Object.prototype.hasOwnProperty.call(props, 'chars')) {
|
|
686
|
-
props.chars = { type: 'STRING' };
|
|
304
|
+
props.chars = { type: isAntigravity ? 'STRING' : 'string' };
|
|
687
305
|
}
|
|
688
306
|
params.properties = props;
|
|
689
307
|
try {
|
|
@@ -701,25 +319,25 @@ export function buildGeminiToolsFromBridge(defs, options) {
|
|
|
701
319
|
// - input/instructions/text: historical aliases containing patch text
|
|
702
320
|
if (!Object.prototype.hasOwnProperty.call(props, 'patch')) {
|
|
703
321
|
props.patch = {
|
|
704
|
-
type: 'STRING',
|
|
322
|
+
type: isAntigravity ? 'STRING' : 'string',
|
|
705
323
|
description: 'Patch text (*** Begin Patch / *** End Patch or GNU unified diff).'
|
|
706
324
|
};
|
|
707
325
|
}
|
|
708
326
|
if (!Object.prototype.hasOwnProperty.call(props, 'input')) {
|
|
709
327
|
props.input = {
|
|
710
|
-
type: 'STRING',
|
|
328
|
+
type: isAntigravity ? 'STRING' : 'string',
|
|
711
329
|
description: 'Alias of patch (patch text). Prefer patch.'
|
|
712
330
|
};
|
|
713
331
|
}
|
|
714
332
|
if (!Object.prototype.hasOwnProperty.call(props, 'instructions')) {
|
|
715
333
|
props.instructions = {
|
|
716
|
-
type: 'STRING',
|
|
334
|
+
type: isAntigravity ? 'STRING' : 'string',
|
|
717
335
|
description: 'Alias of patch (patch text). Prefer patch.'
|
|
718
336
|
};
|
|
719
337
|
}
|
|
720
338
|
if (!Object.prototype.hasOwnProperty.call(props, 'text')) {
|
|
721
339
|
props.text = {
|
|
722
|
-
type: 'STRING',
|
|
340
|
+
type: isAntigravity ? 'STRING' : 'string',
|
|
723
341
|
description: 'Alias of patch (patch text). Prefer patch.'
|
|
724
342
|
};
|
|
725
343
|
}
|
|
@@ -765,16 +383,20 @@ export function buildGeminiToolsFromBridge(defs, options) {
|
|
|
765
383
|
if (!name) {
|
|
766
384
|
return;
|
|
767
385
|
}
|
|
386
|
+
const finalName = normalizeToolName(name);
|
|
768
387
|
const description = typeof fnNode?.description === 'string'
|
|
769
388
|
? fnNode.description
|
|
770
389
|
: typeof def.description === 'string'
|
|
771
390
|
? def.description
|
|
772
391
|
: undefined;
|
|
773
|
-
const parameters = applyFixups(
|
|
392
|
+
const parameters = applyFixups(finalName, cloneParameters(fnNode?.parameters ?? def.parameters ?? { type: 'object', properties: {} }));
|
|
393
|
+
const normalizedParameters = mode === 'antigravity'
|
|
394
|
+
? normalizeSchemaTypes(parameters)
|
|
395
|
+
: parameters;
|
|
774
396
|
functionDeclarations.push({
|
|
775
|
-
name,
|
|
776
|
-
description: rewriteDescription(
|
|
777
|
-
parameters
|
|
397
|
+
name: finalName,
|
|
398
|
+
description: rewriteDescription(finalName, description),
|
|
399
|
+
parameters: normalizedParameters
|
|
778
400
|
});
|
|
779
401
|
});
|
|
780
402
|
if (!functionDeclarations.length) {
|