@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
package/dist/conversion/hub/pipeline/stages/req_inbound/req_inbound_stage3_context_capture/index.js
CHANGED
|
@@ -1,7 +1,29 @@
|
|
|
1
1
|
import { captureResponsesContext, buildChatRequestFromResponses } from '../../../../../responses/responses-openai-bridge.js';
|
|
2
2
|
import { captureResponsesRequestContext } from '../../../../../shared/responses-conversation-store.js';
|
|
3
|
+
import { readRuntimeMetadata } from '../../../../../shared/runtime-metadata.js';
|
|
3
4
|
import { recordStage } from '../../../stages/utils.js';
|
|
4
5
|
export async function runReqInboundStage3ContextCapture(options) {
|
|
6
|
+
// 对由 server-side 工具触发的二跳/内部跳转请求(例如 stopMessage followup),
|
|
7
|
+
// 跳过工具输出扫描与 apply_patch 诊断日志,避免在内部流中重复放大客户端已收到的
|
|
8
|
+
// 工具错误信息。此类请求的工具治理在 chat-process 阶段完成,这里仅保留最小快照。
|
|
9
|
+
try {
|
|
10
|
+
const ctx = options.adapterContext;
|
|
11
|
+
const rt = readRuntimeMetadata(ctx);
|
|
12
|
+
const followupFlag = rt?.serverToolFollowup;
|
|
13
|
+
const isFollowup = followupFlag === true ||
|
|
14
|
+
(typeof followupFlag === 'string' && followupFlag.trim().toLowerCase() === 'true');
|
|
15
|
+
if (isFollowup) {
|
|
16
|
+
const snapshot = {
|
|
17
|
+
providerProtocol: options.adapterContext.providerProtocol ?? 'unknown',
|
|
18
|
+
tool_outputs: []
|
|
19
|
+
};
|
|
20
|
+
recordStage(options.stageRecorder, 'chat_process.req.stage3.context_capture', snapshot);
|
|
21
|
+
return snapshot;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
// best-effort: 若检测 serverToolFollowup 失败,则继续走通用路径
|
|
26
|
+
}
|
|
5
27
|
let context;
|
|
6
28
|
if (options.captureContext) {
|
|
7
29
|
try {
|
|
@@ -20,6 +20,43 @@ function shouldSample(rate) {
|
|
|
20
20
|
function isJsonRecord(value) {
|
|
21
21
|
return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
|
|
22
22
|
}
|
|
23
|
+
function hasGeminiCliRequestWrapper(payload) {
|
|
24
|
+
const request = payload.request;
|
|
25
|
+
if (!isJsonRecord(request)) {
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
const req = request;
|
|
29
|
+
return (Object.prototype.hasOwnProperty.call(req, 'contents') ||
|
|
30
|
+
Object.prototype.hasOwnProperty.call(req, 'systemInstruction') ||
|
|
31
|
+
Object.prototype.hasOwnProperty.call(req, 'tools') ||
|
|
32
|
+
Object.prototype.hasOwnProperty.call(req, 'toolConfig') ||
|
|
33
|
+
Object.prototype.hasOwnProperty.call(req, 'generationConfig') ||
|
|
34
|
+
Object.prototype.hasOwnProperty.call(req, 'safetySettings'));
|
|
35
|
+
}
|
|
36
|
+
function isGeminiCliPayload(payload) {
|
|
37
|
+
const record = payload;
|
|
38
|
+
return (typeof record.project === 'string' ||
|
|
39
|
+
typeof record.requestType === 'string' ||
|
|
40
|
+
typeof record.userAgent === 'string' ||
|
|
41
|
+
typeof record.requestId === 'string' ||
|
|
42
|
+
hasGeminiCliRequestWrapper(payload));
|
|
43
|
+
}
|
|
44
|
+
function extendGeminiAllowlistIfNeeded(providerProtocol, payload, allowedTopLevelKeys) {
|
|
45
|
+
if (providerProtocol !== 'gemini-chat') {
|
|
46
|
+
return allowedTopLevelKeys;
|
|
47
|
+
}
|
|
48
|
+
if (!isGeminiCliPayload(payload)) {
|
|
49
|
+
return allowedTopLevelKeys;
|
|
50
|
+
}
|
|
51
|
+
const extended = allowedTopLevelKeys ? new Set(allowedTopLevelKeys) : new Set();
|
|
52
|
+
extended.add('request');
|
|
53
|
+
extended.add('project');
|
|
54
|
+
extended.add('requestId');
|
|
55
|
+
extended.add('requestType');
|
|
56
|
+
extended.add('userAgent');
|
|
57
|
+
extended.add('action');
|
|
58
|
+
return extended;
|
|
59
|
+
}
|
|
23
60
|
function applyProviderOutboundPolicy(providerProtocol, payload) {
|
|
24
61
|
const removedTopLevelKeys = [];
|
|
25
62
|
const flattenedWrappers = [];
|
|
@@ -43,6 +80,8 @@ function applyProviderOutboundPolicy(providerProtocol, payload) {
|
|
|
43
80
|
spec.providerOutbound.enforceAllowedTopLevelKeys === true
|
|
44
81
|
? new Set(spec.providerOutbound.allowedTopLevelKeys)
|
|
45
82
|
: undefined;
|
|
83
|
+
const isGeminiCli = providerProtocol === 'gemini-chat' && isGeminiCliPayload(payload);
|
|
84
|
+
const allowedTopLevelKeysResolved = extendGeminiAllowlistIfNeeded(providerProtocol, payload, allowedTopLevelKeys);
|
|
46
85
|
// Reserved/private keys must never be sent upstream.
|
|
47
86
|
for (const key of Object.keys(payload)) {
|
|
48
87
|
if (spec.providerOutbound.reservedKeyPrefixes.some((prefix) => key.startsWith(prefix))) {
|
|
@@ -57,6 +96,9 @@ function applyProviderOutboundPolicy(providerProtocol, payload) {
|
|
|
57
96
|
if (!wrapperKey || typeof wrapperKey !== 'string') {
|
|
58
97
|
continue;
|
|
59
98
|
}
|
|
99
|
+
if (isGeminiCli && wrapperKey === 'request') {
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
60
102
|
if (!isJsonRecord(out[wrapperKey])) {
|
|
61
103
|
continue;
|
|
62
104
|
}
|
|
@@ -84,9 +126,9 @@ function applyProviderOutboundPolicy(providerProtocol, payload) {
|
|
|
84
126
|
// Enforce protocol allowlist (top-level). Only runs when explicitly enabled
|
|
85
127
|
// for this protocol, and only after wrapper flatten so allowed fields are
|
|
86
128
|
// present at the correct level.
|
|
87
|
-
if (
|
|
129
|
+
if (allowedTopLevelKeysResolved) {
|
|
88
130
|
for (const key of Object.keys(out)) {
|
|
89
|
-
if (
|
|
131
|
+
if (allowedTopLevelKeysResolved.has(key))
|
|
90
132
|
continue;
|
|
91
133
|
ensureOutClone();
|
|
92
134
|
delete out[key];
|
|
@@ -106,13 +148,18 @@ function observeProviderPayload(options) {
|
|
|
106
148
|
// Do NOT modify payload here.
|
|
107
149
|
const spec = resolveHubProtocolSpec(options.providerProtocol);
|
|
108
150
|
const allowlistEnabled = options.phase === 'provider_outbound';
|
|
151
|
+
const isGeminiCli = options.providerProtocol === 'gemini-chat' && isGeminiCliPayload(options.payload);
|
|
109
152
|
const allowedTopLevelKeys = allowlistEnabled && Array.isArray(spec.providerOutbound.allowedTopLevelKeys)
|
|
110
153
|
? new Set(spec.providerOutbound.allowedTopLevelKeys)
|
|
111
154
|
: undefined;
|
|
155
|
+
const allowedTopLevelKeysResolved = extendGeminiAllowlistIfNeeded(options.providerProtocol, options.payload, allowedTopLevelKeys);
|
|
112
156
|
for (const rule of spec.providerOutbound.forbidWrappers) {
|
|
113
157
|
if (rule.code !== 'forbid_wrapper') {
|
|
114
158
|
continue;
|
|
115
159
|
}
|
|
160
|
+
if (isGeminiCli && rule.path === 'request') {
|
|
161
|
+
continue;
|
|
162
|
+
}
|
|
116
163
|
if (rule.path in options.payload && isJsonRecord(options.payload[rule.path])) {
|
|
117
164
|
violations.push({
|
|
118
165
|
code: 'unexpected_wrapper',
|
|
@@ -130,7 +177,7 @@ function observeProviderPayload(options) {
|
|
|
130
177
|
});
|
|
131
178
|
continue;
|
|
132
179
|
}
|
|
133
|
-
if (
|
|
180
|
+
if (allowedTopLevelKeysResolved && !allowedTopLevelKeysResolved.has(key)) {
|
|
134
181
|
violations.push({
|
|
135
182
|
code: 'unexpected_field',
|
|
136
183
|
path: key,
|
|
@@ -112,7 +112,7 @@ async function applyRequestToolGovernance(request, context) {
|
|
|
112
112
|
// Server-side web_search tool injection (config-driven, best-effort).
|
|
113
113
|
merged = applyHubOperations(merged, buildWebSearchOperations(merged, metadata));
|
|
114
114
|
// Server-side clock tool + scheduled reminders injection (config-driven, best-effort).
|
|
115
|
-
merged = applyHubOperations(merged, buildClockOperations(
|
|
115
|
+
merged = applyHubOperations(merged, buildClockOperations(metadata));
|
|
116
116
|
merged = await maybeInjectClockRemindersAndApplyDirectives(merged, metadata, context.requestId);
|
|
117
117
|
const { request: sanitized, summary } = toolGovernanceEngine.governRequest(merged, providerProtocol);
|
|
118
118
|
if (summary.applied) {
|
|
@@ -124,129 +124,12 @@ async function applyRequestToolGovernance(request, context) {
|
|
|
124
124
|
}
|
|
125
125
|
};
|
|
126
126
|
}
|
|
127
|
-
return
|
|
127
|
+
return sanitized;
|
|
128
128
|
}
|
|
129
129
|
function resolveSessionIdForPending(metadata, request) {
|
|
130
130
|
const candidate = readString(metadata.sessionId) ?? readString(request.metadata?.sessionId);
|
|
131
131
|
return candidate && candidate.trim() ? candidate.trim() : null;
|
|
132
132
|
}
|
|
133
|
-
function pruneUndeclaredToolHistory(request) {
|
|
134
|
-
const tools = Array.isArray(request.tools) ? request.tools : [];
|
|
135
|
-
const allowedToolNames = new Set();
|
|
136
|
-
for (const tool of tools) {
|
|
137
|
-
const name = tool && typeof tool === 'object' && tool.function && typeof tool.function.name === 'string'
|
|
138
|
-
? String(tool.function.name).trim()
|
|
139
|
-
: '';
|
|
140
|
-
if (name) {
|
|
141
|
-
allowedToolNames.add(name);
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
// If no tools are declared, do not carry any tool calls/results in history.
|
|
145
|
-
const hasAnyTools = allowedToolNames.size > 0;
|
|
146
|
-
let prunedToolCalls = 0;
|
|
147
|
-
let prunedToolResults = 0;
|
|
148
|
-
const prunedToolNames = new Set();
|
|
149
|
-
const keptToolCallIds = new Set();
|
|
150
|
-
const nextMessages = [];
|
|
151
|
-
for (const msg of request.messages) {
|
|
152
|
-
if (!msg || typeof msg !== 'object')
|
|
153
|
-
continue;
|
|
154
|
-
if (msg.role === 'assistant' && Array.isArray(msg.tool_calls) && msg.tool_calls.length) {
|
|
155
|
-
if (!hasAnyTools) {
|
|
156
|
-
prunedToolCalls += msg.tool_calls.length;
|
|
157
|
-
for (const tc of msg.tool_calls) {
|
|
158
|
-
const n = tc && tc.function && typeof tc.function.name === 'string' ? tc.function.name.trim() : '';
|
|
159
|
-
if (n)
|
|
160
|
-
prunedToolNames.add(n);
|
|
161
|
-
}
|
|
162
|
-
// Drop tool_calls when no tools are declared.
|
|
163
|
-
const keptContent = msg.content;
|
|
164
|
-
const hasContent = typeof keptContent === 'string'
|
|
165
|
-
? keptContent.trim().length > 0
|
|
166
|
-
: Array.isArray(keptContent)
|
|
167
|
-
? keptContent.length > 0
|
|
168
|
-
: false;
|
|
169
|
-
if (hasContent) {
|
|
170
|
-
nextMessages.push({ ...msg, tool_calls: [] });
|
|
171
|
-
}
|
|
172
|
-
continue;
|
|
173
|
-
}
|
|
174
|
-
const keptCalls = [];
|
|
175
|
-
for (const tc of msg.tool_calls) {
|
|
176
|
-
if (!tc || typeof tc !== 'object')
|
|
177
|
-
continue;
|
|
178
|
-
const name = tc.function && typeof tc.function.name === 'string' ? tc.function.name.trim() : '';
|
|
179
|
-
if (!name || !allowedToolNames.has(name)) {
|
|
180
|
-
prunedToolCalls += 1;
|
|
181
|
-
if (name)
|
|
182
|
-
prunedToolNames.add(name);
|
|
183
|
-
continue;
|
|
184
|
-
}
|
|
185
|
-
keptCalls.push(tc);
|
|
186
|
-
if (typeof tc.id === 'string' && tc.id.trim()) {
|
|
187
|
-
keptToolCallIds.add(tc.id.trim());
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
const keptContent = msg.content;
|
|
191
|
-
const hasContent = typeof keptContent === 'string'
|
|
192
|
-
? keptContent.trim().length > 0
|
|
193
|
-
: Array.isArray(keptContent)
|
|
194
|
-
? keptContent.length > 0
|
|
195
|
-
: false;
|
|
196
|
-
if (!hasContent && keptCalls.length === 0) {
|
|
197
|
-
continue;
|
|
198
|
-
}
|
|
199
|
-
if (keptCalls.length === msg.tool_calls.length) {
|
|
200
|
-
nextMessages.push(msg);
|
|
201
|
-
}
|
|
202
|
-
else {
|
|
203
|
-
nextMessages.push({ ...msg, tool_calls: keptCalls.length ? keptCalls : undefined });
|
|
204
|
-
}
|
|
205
|
-
continue;
|
|
206
|
-
}
|
|
207
|
-
if (msg.role === 'tool') {
|
|
208
|
-
const name = typeof msg.name === 'string' ? msg.name.trim() : '';
|
|
209
|
-
const toolCallId = typeof msg.tool_call_id === 'string' ? msg.tool_call_id.trim() : '';
|
|
210
|
-
if (!hasAnyTools) {
|
|
211
|
-
prunedToolResults += 1;
|
|
212
|
-
if (name)
|
|
213
|
-
prunedToolNames.add(name);
|
|
214
|
-
continue;
|
|
215
|
-
}
|
|
216
|
-
if (name && !allowedToolNames.has(name)) {
|
|
217
|
-
prunedToolResults += 1;
|
|
218
|
-
prunedToolNames.add(name);
|
|
219
|
-
continue;
|
|
220
|
-
}
|
|
221
|
-
// If the result references a tool_call_id that no longer exists, drop it to avoid
|
|
222
|
-
// provider-side strict pairing errors (Gemini/Anthropic tool pairing).
|
|
223
|
-
if (toolCallId && !keptToolCallIds.has(toolCallId)) {
|
|
224
|
-
prunedToolResults += 1;
|
|
225
|
-
if (name)
|
|
226
|
-
prunedToolNames.add(name);
|
|
227
|
-
continue;
|
|
228
|
-
}
|
|
229
|
-
nextMessages.push(msg);
|
|
230
|
-
continue;
|
|
231
|
-
}
|
|
232
|
-
nextMessages.push(msg);
|
|
233
|
-
}
|
|
234
|
-
if (prunedToolCalls === 0 && prunedToolResults === 0) {
|
|
235
|
-
return request;
|
|
236
|
-
}
|
|
237
|
-
return {
|
|
238
|
-
...request,
|
|
239
|
-
messages: nextMessages,
|
|
240
|
-
metadata: {
|
|
241
|
-
...request.metadata,
|
|
242
|
-
toolHistoryPrune: {
|
|
243
|
-
prunedToolCalls,
|
|
244
|
-
prunedToolResults,
|
|
245
|
-
prunedToolNames: Array.from(prunedToolNames).sort()
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
};
|
|
249
|
-
}
|
|
250
133
|
function extractToolCallIdsFromToolMessages(messages) {
|
|
251
134
|
const ids = new Set();
|
|
252
135
|
for (const msg of messages) {
|
|
@@ -613,32 +496,8 @@ function buildToolChoiceOperations(toolChoice) {
|
|
|
613
496
|
}
|
|
614
497
|
];
|
|
615
498
|
}
|
|
616
|
-
function isGeminiAntigravityLike(metadata) {
|
|
617
|
-
const providerProtocol = readString(metadata.providerProtocol) ?? readString(metadata.provider) ?? '';
|
|
618
|
-
if (!providerProtocol.toLowerCase().includes('gemini')) {
|
|
619
|
-
return false;
|
|
620
|
-
}
|
|
621
|
-
const rt = readRuntimeMetadata(metadata);
|
|
622
|
-
const providerId = readString(metadata.providerId) ??
|
|
623
|
-
readString(rt?.providerId) ??
|
|
624
|
-
'';
|
|
625
|
-
const providerKey = readString(metadata.providerKey) ??
|
|
626
|
-
readString(rt?.providerKey) ??
|
|
627
|
-
'';
|
|
628
|
-
const loweredId = providerId.toLowerCase();
|
|
629
|
-
const loweredKey = providerKey.toLowerCase();
|
|
630
|
-
return (loweredId.includes('antigravity') ||
|
|
631
|
-
loweredId === 'gemini-cli' ||
|
|
632
|
-
loweredKey.startsWith('antigravity.') ||
|
|
633
|
-
loweredKey.startsWith('gemini-cli.'));
|
|
634
|
-
}
|
|
635
499
|
function buildWebSearchOperations(request, metadata) {
|
|
636
500
|
const rt = readRuntimeMetadata(metadata);
|
|
637
|
-
// gcli2api-style: do not force-inject server-side web_search tools for Gemini/Antigravity.
|
|
638
|
-
// Tools must be present only when supplied by the client payload/config, not auto-appended.
|
|
639
|
-
if (isGeminiAntigravityLike(metadata)) {
|
|
640
|
-
return [];
|
|
641
|
-
}
|
|
642
501
|
// ServerTool 二/三跳(serverToolFollowup=true)不再注入 web_search 工具,
|
|
643
502
|
// 以避免在 web_search 流程内部形成循环命中。
|
|
644
503
|
if (rt?.serverToolFollowup === true) {
|
|
@@ -750,10 +609,10 @@ function buildWebSearchOperations(request, metadata) {
|
|
|
750
609
|
});
|
|
751
610
|
return ops;
|
|
752
611
|
}
|
|
753
|
-
function buildClockOperations(
|
|
612
|
+
function buildClockOperations(metadata) {
|
|
754
613
|
const rt = readRuntimeMetadata(metadata);
|
|
755
|
-
//
|
|
756
|
-
if (
|
|
614
|
+
// Do not inject clock tool into internal servertool followup hops.
|
|
615
|
+
if (rt?.serverToolFollowup === true) {
|
|
757
616
|
return [];
|
|
758
617
|
}
|
|
759
618
|
const rawConfig = rt?.clock;
|
|
@@ -8,7 +8,6 @@ import { OpenAIChatResponseMapper, ResponsesResponseMapper, AnthropicResponseMap
|
|
|
8
8
|
import { runRespInboundStage1SseDecode } from '../pipeline/stages/resp_inbound/resp_inbound_stage1_sse_decode/index.js';
|
|
9
9
|
import { runRespInboundStage2FormatParse } from '../pipeline/stages/resp_inbound/resp_inbound_stage2_format_parse/index.js';
|
|
10
10
|
import { runRespInboundStage3SemanticMap } from '../pipeline/stages/resp_inbound/resp_inbound_stage3_semantic_map/index.js';
|
|
11
|
-
import { runRespInboundStage3ThoughtSignatureCapture } from '../pipeline/stages/resp_inbound/resp_inbound_stage3_thought_signature_capture/index.js';
|
|
12
11
|
import { runRespInboundStageCompatResponse } from '../pipeline/stages/req_outbound/req_outbound_stage3_compat/index.js';
|
|
13
12
|
import { runRespProcessStage1ToolGovernance } from '../pipeline/stages/resp_process/resp_process_stage1_tool_governance/index.js';
|
|
14
13
|
import { runRespProcessStage2Finalize } from '../pipeline/stages/resp_process/resp_process_stage2_finalize/index.js';
|
|
@@ -260,14 +259,21 @@ export async function convertProviderResponse(options) {
|
|
|
260
259
|
// Client protocol remapping happens only on the outermost request.
|
|
261
260
|
const clientProtocol = isFollowup ? 'openai-chat' : resolveClientProtocol(options.entryEndpoint);
|
|
262
261
|
const hasServerToolSupport = Boolean(options.providerInvoker) || Boolean(options.reenterPipeline);
|
|
263
|
-
// ServerTool
|
|
264
|
-
//
|
|
262
|
+
// 是否跳过 ServerTool 编排:
|
|
263
|
+
// - Provider 不支持 ServerTool(没有 invoker/reenterPipeline)时跳过;
|
|
264
|
+
// - 对于 serverToolFollowup=true 的二/三跳内部请求,必须跳过:
|
|
265
|
+
// followup 的语义是“本轮 ServerTool 已决定并发起下一跳”,下一跳响应不应再次触发 ServerTool,
|
|
266
|
+
// 否则会形成嵌套/循环(例如 stop_message_flow 反复触发)。
|
|
267
|
+
const skipServerTools = isFollowup || !hasServerToolSupport;
|
|
268
|
+
// 对于由 server-side 工具触发的内部跳转(二跳/三跳),统一禁用 SSE 聚合输出,
|
|
269
|
+
// 始终返回完整的 ChatCompletion JSON,便于在 llms 内部直接解析,而不是拿到
|
|
270
|
+
// __sse_responses 可读流。
|
|
265
271
|
const wantsStream = isFollowup ? false : options.wantsStream;
|
|
266
272
|
try {
|
|
267
273
|
// eslint-disable-next-line no-console
|
|
268
274
|
console.log(`\x1b[38;5;33m[servertool][orchestrator][debug] requestId=${options.context.requestId} ` +
|
|
269
275
|
`protocol=${options.providerProtocol} endpoint=${options.entryEndpoint} ` +
|
|
270
|
-
`
|
|
276
|
+
`skipServerTools=${skipServerTools} hasInvoker=${Boolean(options.providerInvoker)} ` +
|
|
271
277
|
`hasReenter=${Boolean(options.reenterPipeline)}\x1b[0m`);
|
|
272
278
|
}
|
|
273
279
|
catch {
|
|
@@ -300,11 +306,6 @@ export async function convertProviderResponse(options) {
|
|
|
300
306
|
stageRecorder: options.stageRecorder
|
|
301
307
|
});
|
|
302
308
|
stripInternalPolicyDebugFields(formatEnvelope.payload);
|
|
303
|
-
formatEnvelope.payload = await runRespInboundStage3ThoughtSignatureCapture({
|
|
304
|
-
payload: formatEnvelope.payload,
|
|
305
|
-
adapterContext: options.context,
|
|
306
|
-
stageRecorder: options.stageRecorder
|
|
307
|
-
});
|
|
308
309
|
// Phase 2 (shadow): response tool surface mismatch detection (provider inbound).
|
|
309
310
|
// Only records diffs; does not rewrite payload.
|
|
310
311
|
try {
|
|
@@ -352,7 +353,7 @@ export async function convertProviderResponse(options) {
|
|
|
352
353
|
// 检查是否需要进行 ServerTool 编排
|
|
353
354
|
// 使用新的 ChatEnvelope 级别的 servertool 实现
|
|
354
355
|
let effectiveChatResponse = chatResponse;
|
|
355
|
-
if (
|
|
356
|
+
if (!skipServerTools && options.reenterPipeline) {
|
|
356
357
|
try {
|
|
357
358
|
// eslint-disable-next-line no-console
|
|
358
359
|
console.log(`\x1b[38;5;33m[servertool][orchestrator] start requestId=${options.context.requestId} ` +
|
|
@@ -20,7 +20,5 @@ export declare class AnthropicResponseMapper implements ResponseMapper {
|
|
|
20
20
|
}): ChatCompletionLike;
|
|
21
21
|
}
|
|
22
22
|
export declare class GeminiResponseMapper implements ResponseMapper {
|
|
23
|
-
toChatCompletion(format: FormatEnvelope,
|
|
24
|
-
requestSemantics?: ChatSemantics | JsonObject;
|
|
25
|
-
}): ChatCompletionLike;
|
|
23
|
+
toChatCompletion(format: FormatEnvelope, _ctx: AdapterContext): ChatCompletionLike;
|
|
26
24
|
}
|
|
@@ -18,9 +18,8 @@ export class AnthropicResponseMapper {
|
|
|
18
18
|
}
|
|
19
19
|
}
|
|
20
20
|
export class GeminiResponseMapper {
|
|
21
|
-
toChatCompletion(format,
|
|
22
|
-
|
|
23
|
-
return buildOpenAIChatFromGeminiResponse(format.payload ?? {}, { aliasMap });
|
|
21
|
+
toChatCompletion(format, _ctx) {
|
|
22
|
+
return buildOpenAIChatFromGeminiResponse(format.payload ?? {});
|
|
24
23
|
}
|
|
25
24
|
}
|
|
26
25
|
function extractToolNameAliasMapFromSemantics(semantics) {
|
|
@@ -48,20 +47,3 @@ function extractToolNameAliasMapFromSemantics(semantics) {
|
|
|
48
47
|
}
|
|
49
48
|
return Object.keys(normalized).length ? normalized : undefined;
|
|
50
49
|
}
|
|
51
|
-
function extractGeminiToolNameAliasMapFromContext(ctx) {
|
|
52
|
-
const candidate = ctx?.geminiToolNameAliasMap;
|
|
53
|
-
if (!candidate || typeof candidate !== 'object' || Array.isArray(candidate)) {
|
|
54
|
-
return undefined;
|
|
55
|
-
}
|
|
56
|
-
const normalized = {};
|
|
57
|
-
for (const [key, value] of Object.entries(candidate)) {
|
|
58
|
-
if (typeof key !== 'string' || typeof value !== 'string')
|
|
59
|
-
continue;
|
|
60
|
-
const trimmedKey = key.trim();
|
|
61
|
-
const trimmedValue = value.trim();
|
|
62
|
-
if (!trimmedKey.length || !trimmedValue.length)
|
|
63
|
-
continue;
|
|
64
|
-
normalized[trimmedKey] = trimmedValue;
|
|
65
|
-
}
|
|
66
|
-
return Object.keys(normalized).length ? normalized : undefined;
|
|
67
|
-
}
|
|
@@ -477,7 +477,8 @@ export function applyProviderOutboundToolSurface(args) {
|
|
|
477
477
|
// best-effort only
|
|
478
478
|
}
|
|
479
479
|
const totalDiffCount = (schemaDiff.diffCount || 0) + (historyDiff.diffCount || 0);
|
|
480
|
-
|
|
480
|
+
const shouldRecord = totalDiffCount > 0 || Boolean(candidate?.candidateTools) || Boolean(historyReason);
|
|
481
|
+
if (shouldRecord) {
|
|
481
482
|
const diffHeadMerged = [];
|
|
482
483
|
for (const entry of schemaDiff.diffHead) {
|
|
483
484
|
if (diffHeadMerged.length >= 10)
|
|
@@ -170,9 +170,10 @@ function stripRoutingTagsFromText(text) {
|
|
|
170
170
|
if (typeof text !== 'string') {
|
|
171
171
|
return typeof text === 'string' ? text : String(text ?? '');
|
|
172
172
|
}
|
|
173
|
-
//
|
|
174
|
-
//
|
|
175
|
-
|
|
173
|
+
// NOTE: Do not strip <**...**> here. Routing instructions must reach the
|
|
174
|
+
// virtual router so allowlist/sticky directives can be applied. The router
|
|
175
|
+
// will clean them from messages after parsing.
|
|
176
|
+
return text;
|
|
176
177
|
}
|
|
177
178
|
function sanitizeBridgeHistory(history) {
|
|
178
179
|
if (!history || typeof history !== 'object') {
|
|
@@ -1,12 +1,7 @@
|
|
|
1
1
|
import type { BridgeToolDefinition } from './bridge-message-types.js';
|
|
2
2
|
import type { MissingField } from '../hub/types/chat-envelope.js';
|
|
3
3
|
import type { JsonValue, JsonObject } from '../hub/types/json.js';
|
|
4
|
-
export declare function isGeminiFunctionNameSafe(name: string): boolean;
|
|
5
|
-
export declare function sanitizeGeminiFunctionName(name: string, options?: {
|
|
6
|
-
maxLen?: number;
|
|
7
|
-
}): string;
|
|
8
|
-
export type GeminiToolSchemaMode = 'default' | 'antigravity';
|
|
9
4
|
export declare function prepareGeminiToolsForBridge(rawTools: JsonValue | undefined, missing?: MissingField[]): BridgeToolDefinition[] | undefined;
|
|
10
5
|
export declare function buildGeminiToolsFromBridge(defs: BridgeToolDefinition[] | undefined, options?: {
|
|
11
|
-
mode?:
|
|
6
|
+
mode?: 'antigravity' | 'default';
|
|
12
7
|
}): JsonObject[] | undefined;
|