@librechat/agents 3.6.9 → 3.6.10
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/cjs/agents/AgentContext.cjs +46 -39
- package/dist/cjs/agents/AgentContext.cjs.map +1 -1
- package/dist/cjs/agents/projection.cjs +2 -2
- package/dist/cjs/agents/projection.cjs.map +1 -1
- package/dist/cjs/graphs/Graph.cjs +28 -18
- package/dist/cjs/graphs/Graph.cjs.map +1 -1
- package/dist/cjs/graphs/MultiAgentGraph.cjs +87 -16
- package/dist/cjs/graphs/MultiAgentGraph.cjs.map +1 -1
- package/dist/cjs/llm/openai/index.cjs +10 -0
- package/dist/cjs/llm/openai/index.cjs.map +1 -1
- package/dist/cjs/main.cjs +14 -0
- package/dist/cjs/messages/alternation.cjs +125 -20
- package/dist/cjs/messages/alternation.cjs.map +1 -1
- package/dist/cjs/messages/core.cjs +195 -18
- package/dist/cjs/messages/core.cjs.map +1 -1
- package/dist/cjs/messages/format.cjs +488 -118
- package/dist/cjs/messages/format.cjs.map +1 -1
- package/dist/cjs/messages/handoffCue.cjs +3 -2
- package/dist/cjs/messages/handoffCue.cjs.map +1 -1
- package/dist/cjs/messages/index.cjs +1 -0
- package/dist/cjs/messages/injected.cjs +5 -2
- package/dist/cjs/messages/injected.cjs.map +1 -1
- package/dist/cjs/messages/provenance.cjs +411 -0
- package/dist/cjs/messages/provenance.cjs.map +1 -0
- package/dist/cjs/messages/prune.cjs +18 -13
- package/dist/cjs/messages/prune.cjs.map +1 -1
- package/dist/cjs/messages/toolResultTypes.cjs +663 -0
- package/dist/cjs/messages/toolResultTypes.cjs.map +1 -0
- package/dist/cjs/run.cjs +7 -4
- package/dist/cjs/run.cjs.map +1 -1
- package/dist/cjs/tools/BashProgrammaticToolCalling.cjs +21 -4
- package/dist/cjs/tools/BashProgrammaticToolCalling.cjs.map +1 -1
- package/dist/cjs/tools/CallerCapabilities.cjs +48 -0
- package/dist/cjs/tools/CallerCapabilities.cjs.map +1 -0
- package/dist/cjs/tools/ProgrammaticCallerPolicy.cjs +34 -0
- package/dist/cjs/tools/ProgrammaticCallerPolicy.cjs.map +1 -0
- package/dist/cjs/tools/ProgrammaticToolCalling.cjs +21 -4
- package/dist/cjs/tools/ProgrammaticToolCalling.cjs.map +1 -1
- package/dist/cjs/tools/ToolNode.cjs +25 -24
- package/dist/cjs/tools/ToolNode.cjs.map +1 -1
- package/dist/cjs/tools/cloudflare/CloudflareProgrammaticToolCalling.cjs +12 -4
- package/dist/cjs/tools/cloudflare/CloudflareProgrammaticToolCalling.cjs.map +1 -1
- package/dist/cjs/tools/local/LocalProgrammaticToolCalling.cjs +13 -4
- package/dist/cjs/tools/local/LocalProgrammaticToolCalling.cjs.map +1 -1
- package/dist/cjs/tools/local/resolveLocalExecutionTools.cjs +47 -11
- package/dist/cjs/tools/local/resolveLocalExecutionTools.cjs.map +1 -1
- package/dist/cjs/tools/ptcTimeout.cjs.map +1 -1
- package/dist/cjs/utils/toolContent.cjs +2 -2
- package/dist/cjs/utils/toolContent.cjs.map +1 -1
- package/dist/esm/agents/AgentContext.mjs +46 -39
- package/dist/esm/agents/AgentContext.mjs.map +1 -1
- package/dist/esm/agents/projection.mjs +2 -2
- package/dist/esm/agents/projection.mjs.map +1 -1
- package/dist/esm/graphs/Graph.mjs +30 -20
- package/dist/esm/graphs/Graph.mjs.map +1 -1
- package/dist/esm/graphs/MultiAgentGraph.mjs +87 -16
- package/dist/esm/graphs/MultiAgentGraph.mjs.map +1 -1
- package/dist/esm/llm/openai/index.mjs +10 -0
- package/dist/esm/llm/openai/index.mjs.map +1 -1
- package/dist/esm/main.mjs +4 -3
- package/dist/esm/messages/alternation.mjs +125 -20
- package/dist/esm/messages/alternation.mjs.map +1 -1
- package/dist/esm/messages/core.mjs +196 -19
- package/dist/esm/messages/core.mjs.map +1 -1
- package/dist/esm/messages/format.mjs +488 -119
- package/dist/esm/messages/format.mjs.map +1 -1
- package/dist/esm/messages/handoffCue.mjs +3 -2
- package/dist/esm/messages/handoffCue.mjs.map +1 -1
- package/dist/esm/messages/index.mjs +1 -0
- package/dist/esm/messages/injected.mjs +5 -2
- package/dist/esm/messages/injected.mjs.map +1 -1
- package/dist/esm/messages/provenance.mjs +401 -0
- package/dist/esm/messages/provenance.mjs.map +1 -0
- package/dist/esm/messages/prune.mjs +18 -13
- package/dist/esm/messages/prune.mjs.map +1 -1
- package/dist/esm/messages/toolResultTypes.mjs +657 -0
- package/dist/esm/messages/toolResultTypes.mjs.map +1 -0
- package/dist/esm/run.mjs +7 -4
- package/dist/esm/run.mjs.map +1 -1
- package/dist/esm/tools/BashProgrammaticToolCalling.mjs +21 -4
- package/dist/esm/tools/BashProgrammaticToolCalling.mjs.map +1 -1
- package/dist/esm/tools/CallerCapabilities.mjs +45 -0
- package/dist/esm/tools/CallerCapabilities.mjs.map +1 -0
- package/dist/esm/tools/ProgrammaticCallerPolicy.mjs +32 -0
- package/dist/esm/tools/ProgrammaticCallerPolicy.mjs.map +1 -0
- package/dist/esm/tools/ProgrammaticToolCalling.mjs +21 -4
- package/dist/esm/tools/ProgrammaticToolCalling.mjs.map +1 -1
- package/dist/esm/tools/ToolNode.mjs +25 -24
- package/dist/esm/tools/ToolNode.mjs.map +1 -1
- package/dist/esm/tools/cloudflare/CloudflareProgrammaticToolCalling.mjs +14 -6
- package/dist/esm/tools/cloudflare/CloudflareProgrammaticToolCalling.mjs.map +1 -1
- package/dist/esm/tools/local/LocalProgrammaticToolCalling.mjs +15 -6
- package/dist/esm/tools/local/LocalProgrammaticToolCalling.mjs.map +1 -1
- package/dist/esm/tools/local/resolveLocalExecutionTools.mjs +47 -12
- package/dist/esm/tools/local/resolveLocalExecutionTools.mjs.map +1 -1
- package/dist/esm/tools/ptcTimeout.mjs.map +1 -1
- package/dist/esm/utils/toolContent.mjs +2 -2
- package/dist/esm/utils/toolContent.mjs.map +1 -1
- package/dist/types/agents/AgentContext.d.ts +8 -13
- package/dist/types/agents/projection.d.ts +3 -1
- package/dist/types/graphs/Graph.d.ts +1 -1
- package/dist/types/llm/openai/index.d.ts +20 -0
- package/dist/types/messages/format.d.ts +5 -0
- package/dist/types/messages/index.d.ts +1 -0
- package/dist/types/messages/provenance.d.ts +85 -0
- package/dist/types/messages/toolResultTypes.d.ts +33 -0
- package/dist/types/tools/CallerCapabilities.d.ts +12 -0
- package/dist/types/tools/ProgrammaticCallerPolicy.d.ts +20 -0
- package/dist/types/tools/ToolNode.d.ts +4 -7
- package/dist/types/tools/local/resolveLocalExecutionTools.d.ts +4 -0
- package/dist/types/tools/ptcTimeout.d.ts +8 -0
- package/dist/types/types/graph.d.ts +3 -1
- package/dist/types/types/tools.d.ts +13 -2
- package/package.json +1 -1
- package/src/agents/AgentContext.ts +109 -67
- package/src/agents/projection.ts +9 -1
- package/src/graphs/Graph.ts +56 -23
- package/src/graphs/MultiAgentGraph.ts +175 -33
- package/src/llm/openai/index.ts +43 -0
- package/src/messages/alternation.ts +217 -34
- package/src/messages/core.ts +358 -38
- package/src/messages/format.ts +958 -137
- package/src/messages/handoffCue.ts +7 -4
- package/src/messages/index.ts +1 -0
- package/src/messages/injected.ts +9 -6
- package/src/messages/provenance.ts +897 -0
- package/src/messages/prune.ts +28 -10
- package/src/messages/toolResultTypes.ts +1174 -0
- package/src/run.ts +9 -4
- package/src/tools/BashProgrammaticToolCalling.ts +46 -12
- package/src/tools/CallerCapabilities.ts +78 -0
- package/src/tools/ProgrammaticCallerPolicy.ts +97 -0
- package/src/tools/ProgrammaticToolCalling.ts +41 -7
- package/src/tools/ToolNode.ts +70 -52
- package/src/tools/cloudflare/CloudflareProgrammaticToolCalling.ts +24 -26
- package/src/tools/local/LocalProgrammaticToolCalling.ts +28 -23
- package/src/tools/local/resolveLocalExecutionTools.ts +132 -13
- package/src/tools/ptcTimeout.ts +6 -0
- package/src/types/graph.ts +3 -0
- package/src/types/tools.ts +14 -3
- package/src/utils/toolContent.ts +2 -2
|
@@ -0,0 +1,1174 @@
|
|
|
1
|
+
import { isProxy } from 'node:util/types';
|
|
2
|
+
|
|
3
|
+
export type ProviderToolCallKind =
|
|
4
|
+
| 'tool'
|
|
5
|
+
| 'server'
|
|
6
|
+
| 'anthropic-server'
|
|
7
|
+
| 'mcp'
|
|
8
|
+
| 'google'
|
|
9
|
+
| 'bedrock';
|
|
10
|
+
|
|
11
|
+
export interface ProviderToolCallPartDescriptor {
|
|
12
|
+
readonly callId: string;
|
|
13
|
+
readonly kind: ProviderToolCallKind;
|
|
14
|
+
readonly name?: string;
|
|
15
|
+
readonly sourceType: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface ProviderToolResultPartDescriptor {
|
|
19
|
+
readonly type: string;
|
|
20
|
+
readonly compatibleCallKinds: readonly ProviderToolCallKind[];
|
|
21
|
+
readonly toolCallId?: string;
|
|
22
|
+
readonly expectedToolNames?: readonly string[];
|
|
23
|
+
readonly requiresPreviousExecutableCode?: boolean;
|
|
24
|
+
readonly allowHumanMessagePairing?: boolean;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface ProviderToolCallIndexEntry {
|
|
28
|
+
readonly descriptor: ProviderToolCallPartDescriptor;
|
|
29
|
+
secondarySourceType?: string;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export type ProviderToolCallIndex = Map<
|
|
33
|
+
string,
|
|
34
|
+
ProviderToolCallIndexEntry | null
|
|
35
|
+
>;
|
|
36
|
+
|
|
37
|
+
type DataProperty =
|
|
38
|
+
| { readonly found: true; readonly value: unknown }
|
|
39
|
+
| { readonly found: false };
|
|
40
|
+
|
|
41
|
+
interface ValidationBudget {
|
|
42
|
+
remaining: number;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export const PROVIDER_TOOL_RESULT_MAX_ARRAY_ENTRIES = 256;
|
|
46
|
+
export const PROVIDER_TOOL_RESULT_MAX_VALIDATION_ENTRIES = 4096;
|
|
47
|
+
export const PROVIDER_TOOL_PAIRING_MAX_IDENTIFIER_CHARS = 512;
|
|
48
|
+
export const PROVIDER_TOOL_PAIRING_MAX_OUTER_PARTS = 4096;
|
|
49
|
+
|
|
50
|
+
const TOOL_CALL_KINDS = ['tool'] as const;
|
|
51
|
+
const SERVER_CALL_KINDS = ['server'] as const;
|
|
52
|
+
const ANTHROPIC_SERVER_CALL_KINDS = ['anthropic-server'] as const;
|
|
53
|
+
const MCP_CALL_KINDS = ['mcp'] as const;
|
|
54
|
+
const GOOGLE_CALL_KINDS = ['google'] as const;
|
|
55
|
+
const BEDROCK_CALL_KINDS = ['bedrock', 'tool'] as const;
|
|
56
|
+
|
|
57
|
+
const ADVISOR_TOOL_NAMES = ['advisor'] as const;
|
|
58
|
+
const BASH_TOOL_NAMES = ['bash_code_execution'] as const;
|
|
59
|
+
const CODE_EXECUTION_TOOL_NAMES = ['code_execution'] as const;
|
|
60
|
+
const TEXT_EDITOR_TOOL_NAMES = ['text_editor_code_execution'] as const;
|
|
61
|
+
const TOOL_SEARCH_TOOL_NAMES = [
|
|
62
|
+
'tool_search_tool_regex',
|
|
63
|
+
'tool_search_tool_bm25',
|
|
64
|
+
] as const;
|
|
65
|
+
const WEB_FETCH_TOOL_NAMES = ['web_fetch'] as const;
|
|
66
|
+
const WEB_SEARCH_TOOL_NAMES = ['web_search'] as const;
|
|
67
|
+
const ANTHROPIC_SERVER_TOOL_ID_PREFIX = 'srvtoolu_';
|
|
68
|
+
|
|
69
|
+
function stringSet(values: readonly string[]): ReadonlySet<string> {
|
|
70
|
+
return new Set(values);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const GOOGLE_CODE_EXECUTION_OUTCOMES = stringSet([
|
|
74
|
+
'OUTCOME_DEADLINE_EXCEEDED',
|
|
75
|
+
'OUTCOME_FAILED',
|
|
76
|
+
'OUTCOME_OK',
|
|
77
|
+
'OUTCOME_UNSPECIFIED',
|
|
78
|
+
'outcome_deadline_exceeded',
|
|
79
|
+
'outcome_failed',
|
|
80
|
+
'outcome_ok',
|
|
81
|
+
'outcome_unspecified',
|
|
82
|
+
]);
|
|
83
|
+
const PROVIDER_TOOL_CALL_KINDS = stringSet([
|
|
84
|
+
'anthropic-server',
|
|
85
|
+
'bedrock',
|
|
86
|
+
'google',
|
|
87
|
+
'mcp',
|
|
88
|
+
'server',
|
|
89
|
+
'tool',
|
|
90
|
+
]);
|
|
91
|
+
const ANTHROPIC_SERVER_TOOL_NAMES = stringSet([
|
|
92
|
+
...ADVISOR_TOOL_NAMES,
|
|
93
|
+
...BASH_TOOL_NAMES,
|
|
94
|
+
...CODE_EXECUTION_TOOL_NAMES,
|
|
95
|
+
...TEXT_EDITOR_TOOL_NAMES,
|
|
96
|
+
...TOOL_SEARCH_TOOL_NAMES,
|
|
97
|
+
...WEB_FETCH_TOOL_NAMES,
|
|
98
|
+
...WEB_SEARCH_TOOL_NAMES,
|
|
99
|
+
]);
|
|
100
|
+
|
|
101
|
+
function getRecord(value: unknown): Record<string, unknown> | undefined {
|
|
102
|
+
if (value == null || typeof value !== 'object') {
|
|
103
|
+
return undefined;
|
|
104
|
+
}
|
|
105
|
+
try {
|
|
106
|
+
if (isProxy(value) || Array.isArray(value)) {
|
|
107
|
+
return undefined;
|
|
108
|
+
}
|
|
109
|
+
const prototype = Object.getPrototypeOf(value);
|
|
110
|
+
return prototype === Object.prototype || prototype === null
|
|
111
|
+
? (value as Record<string, unknown>)
|
|
112
|
+
: undefined;
|
|
113
|
+
} catch {
|
|
114
|
+
return undefined;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function getPropertyContainer(
|
|
119
|
+
value: unknown
|
|
120
|
+
): Record<string, unknown> | undefined {
|
|
121
|
+
if (value == null || typeof value !== 'object') {
|
|
122
|
+
return undefined;
|
|
123
|
+
}
|
|
124
|
+
try {
|
|
125
|
+
return !isProxy(value) && !Array.isArray(value)
|
|
126
|
+
? (value as Record<string, unknown>)
|
|
127
|
+
: undefined;
|
|
128
|
+
} catch {
|
|
129
|
+
return undefined;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function getArray(value: unknown): readonly unknown[] | undefined {
|
|
134
|
+
if (value == null || typeof value !== 'object') {
|
|
135
|
+
return undefined;
|
|
136
|
+
}
|
|
137
|
+
try {
|
|
138
|
+
return !isProxy(value) && Array.isArray(value)
|
|
139
|
+
? (value as readonly unknown[])
|
|
140
|
+
: undefined;
|
|
141
|
+
} catch {
|
|
142
|
+
return undefined;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function readDataProperty(
|
|
147
|
+
record: Record<string, unknown>,
|
|
148
|
+
key: string
|
|
149
|
+
): DataProperty {
|
|
150
|
+
try {
|
|
151
|
+
const descriptor = Object.getOwnPropertyDescriptor(record, key);
|
|
152
|
+
if (
|
|
153
|
+
descriptor == null ||
|
|
154
|
+
descriptor.enumerable !== true ||
|
|
155
|
+
!('value' in descriptor)
|
|
156
|
+
) {
|
|
157
|
+
return { found: false };
|
|
158
|
+
}
|
|
159
|
+
return { found: true, value: descriptor.value };
|
|
160
|
+
} catch {
|
|
161
|
+
return { found: false };
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function hasOnlyOwnDataProperties(
|
|
166
|
+
record: Record<string, unknown>,
|
|
167
|
+
allowed: readonly string[]
|
|
168
|
+
): boolean {
|
|
169
|
+
try {
|
|
170
|
+
const keys = Reflect.ownKeys(record);
|
|
171
|
+
if (keys.length > allowed.length) {
|
|
172
|
+
return false;
|
|
173
|
+
}
|
|
174
|
+
for (let index = 0; index < keys.length; index++) {
|
|
175
|
+
const key = keys[index];
|
|
176
|
+
if (typeof key !== 'string' || !allowed.includes(key)) {
|
|
177
|
+
return false;
|
|
178
|
+
}
|
|
179
|
+
const descriptor = Object.getOwnPropertyDescriptor(record, key);
|
|
180
|
+
if (
|
|
181
|
+
descriptor == null ||
|
|
182
|
+
descriptor.enumerable !== true ||
|
|
183
|
+
!('value' in descriptor)
|
|
184
|
+
) {
|
|
185
|
+
return false;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
return true;
|
|
189
|
+
} catch {
|
|
190
|
+
return false;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
function getBoundedDataArray(
|
|
195
|
+
value: unknown,
|
|
196
|
+
maxEntries: number,
|
|
197
|
+
budget?: ValidationBudget
|
|
198
|
+
): readonly unknown[] | undefined {
|
|
199
|
+
const array = getArray(value);
|
|
200
|
+
if (
|
|
201
|
+
array == null ||
|
|
202
|
+
array.length > maxEntries ||
|
|
203
|
+
(budget != null && array.length > budget.remaining)
|
|
204
|
+
) {
|
|
205
|
+
return undefined;
|
|
206
|
+
}
|
|
207
|
+
if (budget != null) {
|
|
208
|
+
budget.remaining -= array.length;
|
|
209
|
+
}
|
|
210
|
+
for (let index = 0; index < array.length; index++) {
|
|
211
|
+
let descriptor: PropertyDescriptor | undefined;
|
|
212
|
+
try {
|
|
213
|
+
descriptor = Object.getOwnPropertyDescriptor(array, String(index));
|
|
214
|
+
} catch {
|
|
215
|
+
return undefined;
|
|
216
|
+
}
|
|
217
|
+
if (
|
|
218
|
+
descriptor == null ||
|
|
219
|
+
descriptor.enumerable !== true ||
|
|
220
|
+
!('value' in descriptor)
|
|
221
|
+
) {
|
|
222
|
+
return undefined;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
return array;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
export function getBoundedProviderPairingArray(
|
|
229
|
+
value: unknown
|
|
230
|
+
): readonly unknown[] | undefined {
|
|
231
|
+
return getBoundedDataArray(value, PROVIDER_TOOL_PAIRING_MAX_OUTER_PARTS);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
export function getBoundedProviderPairingArrayProperty(
|
|
235
|
+
value: unknown,
|
|
236
|
+
key: string
|
|
237
|
+
): readonly unknown[] | undefined {
|
|
238
|
+
const record = getPropertyContainer(value);
|
|
239
|
+
if (record == null) {
|
|
240
|
+
return undefined;
|
|
241
|
+
}
|
|
242
|
+
const property = readDataProperty(record, key);
|
|
243
|
+
return property.found
|
|
244
|
+
? getBoundedProviderPairingArray(property.value)
|
|
245
|
+
: undefined;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
function readString(
|
|
249
|
+
record: Record<string, unknown>,
|
|
250
|
+
key: string,
|
|
251
|
+
allowEmpty = false
|
|
252
|
+
): string | undefined {
|
|
253
|
+
const property = readDataProperty(record, key);
|
|
254
|
+
return property.found &&
|
|
255
|
+
typeof property.value === 'string' &&
|
|
256
|
+
(allowEmpty || property.value.length > 0)
|
|
257
|
+
? property.value
|
|
258
|
+
: undefined;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
export function isBoundedProviderPairingString(
|
|
262
|
+
value: unknown,
|
|
263
|
+
allowEmpty = false
|
|
264
|
+
): value is string {
|
|
265
|
+
return (
|
|
266
|
+
typeof value === 'string' &&
|
|
267
|
+
(allowEmpty || value.length > 0) &&
|
|
268
|
+
value.length <= PROVIDER_TOOL_PAIRING_MAX_IDENTIFIER_CHARS
|
|
269
|
+
);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
function readPairingString(
|
|
273
|
+
record: Record<string, unknown>,
|
|
274
|
+
key: string,
|
|
275
|
+
allowEmpty = false
|
|
276
|
+
): string | undefined {
|
|
277
|
+
const property = readDataProperty(record, key);
|
|
278
|
+
return property.found &&
|
|
279
|
+
isBoundedProviderPairingString(property.value, allowEmpty)
|
|
280
|
+
? property.value
|
|
281
|
+
: undefined;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
function hasRequiredDataProperties(
|
|
285
|
+
record: Record<string, unknown>,
|
|
286
|
+
keys: readonly string[] | undefined
|
|
287
|
+
): boolean {
|
|
288
|
+
if (keys == null) {
|
|
289
|
+
return true;
|
|
290
|
+
}
|
|
291
|
+
for (let index = 0; index < keys.length; index++) {
|
|
292
|
+
const property = readDataProperty(record, keys[index]);
|
|
293
|
+
if (!property.found || property.value === undefined) {
|
|
294
|
+
return false;
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
return true;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
function hasOptionalNullableString(
|
|
301
|
+
record: Record<string, unknown>,
|
|
302
|
+
key: string
|
|
303
|
+
): boolean {
|
|
304
|
+
const property = readDataProperty(record, key);
|
|
305
|
+
return (
|
|
306
|
+
!property.found ||
|
|
307
|
+
property.value === null ||
|
|
308
|
+
typeof property.value === 'string'
|
|
309
|
+
);
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
function isCacheControl(value: unknown): boolean {
|
|
313
|
+
if (value == null) {
|
|
314
|
+
return true;
|
|
315
|
+
}
|
|
316
|
+
const record = getRecord(value);
|
|
317
|
+
if (
|
|
318
|
+
record == null ||
|
|
319
|
+
!hasOnlyOwnDataProperties(record, ['type', 'ttl']) ||
|
|
320
|
+
readString(record, 'type') !== 'ephemeral'
|
|
321
|
+
) {
|
|
322
|
+
return false;
|
|
323
|
+
}
|
|
324
|
+
const ttl = readDataProperty(record, 'ttl');
|
|
325
|
+
return (
|
|
326
|
+
!ttl.found ||
|
|
327
|
+
ttl.value === null ||
|
|
328
|
+
ttl.value === '5m' ||
|
|
329
|
+
ttl.value === '1h'
|
|
330
|
+
);
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
function hasValidCacheControlProperty(
|
|
334
|
+
record: Record<string, unknown>
|
|
335
|
+
): boolean {
|
|
336
|
+
const property = readDataProperty(record, 'cache_control');
|
|
337
|
+
return !property.found || isCacheControl(property.value);
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
function isServerToolCaller(value: unknown): boolean {
|
|
341
|
+
const record = getRecord(value);
|
|
342
|
+
if (record == null) {
|
|
343
|
+
return false;
|
|
344
|
+
}
|
|
345
|
+
const type = readString(record, 'type');
|
|
346
|
+
if (type === 'direct') {
|
|
347
|
+
return hasOnlyOwnDataProperties(record, ['type']);
|
|
348
|
+
}
|
|
349
|
+
return (
|
|
350
|
+
(type === 'code_execution_20250825' ||
|
|
351
|
+
type === 'code_execution_20260120') &&
|
|
352
|
+
hasOnlyOwnDataProperties(record, ['type', 'tool_id']) &&
|
|
353
|
+
readPairingString(record, 'tool_id') != null
|
|
354
|
+
);
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
function hasValidCallerProperty(record: Record<string, unknown>): boolean {
|
|
358
|
+
const property = readDataProperty(record, 'caller');
|
|
359
|
+
return !property.found || isServerToolCaller(property.value);
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
function hasValidProviderMetadata(
|
|
363
|
+
record: Record<string, unknown>
|
|
364
|
+
): boolean {
|
|
365
|
+
const id = readDataProperty(record, 'id');
|
|
366
|
+
if (id.found && !isBoundedProviderPairingString(id.value)) {
|
|
367
|
+
return false;
|
|
368
|
+
}
|
|
369
|
+
const agentId = readDataProperty(record, 'agentId');
|
|
370
|
+
if (agentId.found && !isBoundedProviderPairingString(agentId.value)) {
|
|
371
|
+
return false;
|
|
372
|
+
}
|
|
373
|
+
const index = readDataProperty(record, 'index');
|
|
374
|
+
if (
|
|
375
|
+
index.found &&
|
|
376
|
+
!(
|
|
377
|
+
(typeof index.value === 'number' && Number.isSafeInteger(index.value)) ||
|
|
378
|
+
isBoundedProviderPairingString(index.value)
|
|
379
|
+
)
|
|
380
|
+
) {
|
|
381
|
+
return false;
|
|
382
|
+
}
|
|
383
|
+
const groupId = readDataProperty(record, 'groupId');
|
|
384
|
+
if (
|
|
385
|
+
groupId.found &&
|
|
386
|
+
!(typeof groupId.value === 'number' && Number.isFinite(groupId.value))
|
|
387
|
+
) {
|
|
388
|
+
return false;
|
|
389
|
+
}
|
|
390
|
+
const thought = readDataProperty(record, 'thought');
|
|
391
|
+
if (thought.found && typeof thought.value !== 'boolean') {
|
|
392
|
+
return false;
|
|
393
|
+
}
|
|
394
|
+
const signature = readDataProperty(record, 'thoughtSignature');
|
|
395
|
+
return (
|
|
396
|
+
!signature.found ||
|
|
397
|
+
isBoundedProviderPairingString(signature.value, true)
|
|
398
|
+
);
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
const ANTHROPIC_STRUCTURED_RESULT_TOOL_NAMES: Readonly<
|
|
402
|
+
Partial<Record<string, readonly string[]>>
|
|
403
|
+
> = {
|
|
404
|
+
advisor_tool_result: ADVISOR_TOOL_NAMES,
|
|
405
|
+
bash_code_execution_tool_result: BASH_TOOL_NAMES,
|
|
406
|
+
code_execution_tool_result: CODE_EXECUTION_TOOL_NAMES,
|
|
407
|
+
text_editor_code_execution_tool_result: TEXT_EDITOR_TOOL_NAMES,
|
|
408
|
+
tool_search_tool_result: TOOL_SEARCH_TOOL_NAMES,
|
|
409
|
+
web_fetch_tool_result: WEB_FETCH_TOOL_NAMES,
|
|
410
|
+
};
|
|
411
|
+
|
|
412
|
+
/** Designated provider output is opaque once its exact envelope and matching
|
|
413
|
+
* call establish authorship. Only its container size/proxy identity is read;
|
|
414
|
+
* nested values are deliberately not walked on the formatting hot path. */
|
|
415
|
+
function isOpaqueBoundedArray(value: unknown): boolean {
|
|
416
|
+
const array = getArray(value);
|
|
417
|
+
return (
|
|
418
|
+
array != null && array.length <= PROVIDER_TOOL_RESULT_MAX_ARRAY_ENTRIES
|
|
419
|
+
);
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
function isOpaqueStringOrBoundedArray(value: unknown): boolean {
|
|
423
|
+
return typeof value === 'string' || isOpaqueBoundedArray(value);
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
function isWebSearchResult(value: unknown): boolean {
|
|
427
|
+
const record = getRecord(value);
|
|
428
|
+
return (
|
|
429
|
+
record != null &&
|
|
430
|
+
hasOnlyOwnDataProperties(record, [
|
|
431
|
+
'type',
|
|
432
|
+
'encrypted_content',
|
|
433
|
+
'title',
|
|
434
|
+
'url',
|
|
435
|
+
'page_age',
|
|
436
|
+
]) &&
|
|
437
|
+
readString(record, 'type') === 'web_search_result' &&
|
|
438
|
+
readString(record, 'encrypted_content', true) != null &&
|
|
439
|
+
readString(record, 'title', true) != null &&
|
|
440
|
+
readString(record, 'url') != null &&
|
|
441
|
+
hasOptionalNullableString(record, 'page_age')
|
|
442
|
+
);
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
function isWireWebSearchContent(
|
|
446
|
+
value: unknown,
|
|
447
|
+
budget: ValidationBudget
|
|
448
|
+
): boolean {
|
|
449
|
+
const error = getRecord(value);
|
|
450
|
+
if (error != null) {
|
|
451
|
+
return (
|
|
452
|
+
hasOnlyOwnDataProperties(error, ['type', 'error_code']) &&
|
|
453
|
+
readString(error, 'type') === 'web_search_tool_result_error' &&
|
|
454
|
+
readString(error, 'error_code') != null
|
|
455
|
+
);
|
|
456
|
+
}
|
|
457
|
+
const results = getBoundedDataArray(
|
|
458
|
+
value,
|
|
459
|
+
PROVIDER_TOOL_RESULT_MAX_ARRAY_ENTRIES,
|
|
460
|
+
budget
|
|
461
|
+
);
|
|
462
|
+
if (results == null) {
|
|
463
|
+
return false;
|
|
464
|
+
}
|
|
465
|
+
for (let index = 0; index < results.length; index++) {
|
|
466
|
+
if (!isWebSearchResult(results[index])) {
|
|
467
|
+
return false;
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
return true;
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
export function hasStructurallyValidAnthropicWebSearchResultContent(
|
|
474
|
+
part: unknown
|
|
475
|
+
): boolean {
|
|
476
|
+
const record = getRecord(part);
|
|
477
|
+
if (record == null || readPairingString(record, 'tool_use_id') == null) {
|
|
478
|
+
return false;
|
|
479
|
+
}
|
|
480
|
+
const content = readDataProperty(record, 'content');
|
|
481
|
+
return (
|
|
482
|
+
content.found &&
|
|
483
|
+
isWireWebSearchContent(content.value, {
|
|
484
|
+
remaining: PROVIDER_TOOL_RESULT_MAX_VALIDATION_ENTRIES,
|
|
485
|
+
})
|
|
486
|
+
);
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
function isGoogleCodeExecutionResult(
|
|
490
|
+
record: Record<string, unknown>
|
|
491
|
+
): boolean {
|
|
492
|
+
const nested = readDataProperty(record, 'codeExecutionResult');
|
|
493
|
+
const result = nested.found ? getRecord(nested.value) : undefined;
|
|
494
|
+
const outcome = result == null ? undefined : readString(result, 'outcome');
|
|
495
|
+
return (
|
|
496
|
+
result != null &&
|
|
497
|
+
hasOnlyOwnDataProperties(result, ['outcome', 'output']) &&
|
|
498
|
+
outcome != null &&
|
|
499
|
+
GOOGLE_CODE_EXECUTION_OUTCOMES.has(outcome) &&
|
|
500
|
+
readString(result, 'output', true) != null
|
|
501
|
+
);
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
function getGoogleToolResponseDescriptor(
|
|
505
|
+
record: Record<string, unknown>,
|
|
506
|
+
type: string
|
|
507
|
+
): ProviderToolResultPartDescriptor | undefined {
|
|
508
|
+
const nested = readDataProperty(record, 'toolResponse');
|
|
509
|
+
const response = nested.found ? getRecord(nested.value) : undefined;
|
|
510
|
+
if (response == null) {
|
|
511
|
+
return undefined;
|
|
512
|
+
}
|
|
513
|
+
const toolCallId = readPairingString(response, 'id');
|
|
514
|
+
const responsePayload = readDataProperty(response, 'response');
|
|
515
|
+
const resultPayload = readDataProperty(response, 'result');
|
|
516
|
+
if (toolCallId == null) {
|
|
517
|
+
return undefined;
|
|
518
|
+
}
|
|
519
|
+
if (
|
|
520
|
+
responsePayload.found &&
|
|
521
|
+
responsePayload.value !== undefined &&
|
|
522
|
+
!resultPayload.found &&
|
|
523
|
+
hasOnlyOwnDataProperties(response, ['id', 'name', 'response'])
|
|
524
|
+
) {
|
|
525
|
+
const name = readPairingString(response, 'name');
|
|
526
|
+
return name == null
|
|
527
|
+
? undefined
|
|
528
|
+
: {
|
|
529
|
+
type,
|
|
530
|
+
toolCallId,
|
|
531
|
+
compatibleCallKinds: GOOGLE_CALL_KINDS,
|
|
532
|
+
expectedToolNames: [name],
|
|
533
|
+
};
|
|
534
|
+
}
|
|
535
|
+
if (
|
|
536
|
+
resultPayload.found &&
|
|
537
|
+
resultPayload.value !== undefined &&
|
|
538
|
+
!responsePayload.found &&
|
|
539
|
+
hasOnlyOwnDataProperties(response, ['id', 'toolType', 'result']) &&
|
|
540
|
+
readPairingString(response, 'toolType') != null
|
|
541
|
+
) {
|
|
542
|
+
return { type, toolCallId, compatibleCallKinds: GOOGLE_CALL_KINDS };
|
|
543
|
+
}
|
|
544
|
+
return undefined;
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
function getBedrockDescriptor(
|
|
548
|
+
record: Record<string, unknown>,
|
|
549
|
+
type: string
|
|
550
|
+
): ProviderToolResultPartDescriptor | undefined {
|
|
551
|
+
const nested = readDataProperty(record, 'toolResult');
|
|
552
|
+
const result = nested.found ? getRecord(nested.value) : undefined;
|
|
553
|
+
if (
|
|
554
|
+
result == null ||
|
|
555
|
+
!hasOnlyOwnDataProperties(result, [
|
|
556
|
+
'toolUseId',
|
|
557
|
+
'content',
|
|
558
|
+
'status',
|
|
559
|
+
'type',
|
|
560
|
+
])
|
|
561
|
+
) {
|
|
562
|
+
return undefined;
|
|
563
|
+
}
|
|
564
|
+
const toolCallId = readPairingString(result, 'toolUseId');
|
|
565
|
+
const content = readDataProperty(result, 'content');
|
|
566
|
+
const status = readDataProperty(result, 'status');
|
|
567
|
+
const nestedType = readDataProperty(result, 'type');
|
|
568
|
+
if (
|
|
569
|
+
toolCallId == null ||
|
|
570
|
+
!content.found ||
|
|
571
|
+
!isOpaqueBoundedArray(content.value) ||
|
|
572
|
+
(status.found && status.value !== 'success' && status.value !== 'error') ||
|
|
573
|
+
(nestedType.found && typeof nestedType.value !== 'string')
|
|
574
|
+
) {
|
|
575
|
+
return undefined;
|
|
576
|
+
}
|
|
577
|
+
return {
|
|
578
|
+
type,
|
|
579
|
+
toolCallId,
|
|
580
|
+
compatibleCallKinds: BEDROCK_CALL_KINDS,
|
|
581
|
+
allowHumanMessagePairing: true,
|
|
582
|
+
};
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
const ANTHROPIC_RESULT_FIELDS = [
|
|
586
|
+
'type',
|
|
587
|
+
'tool_use_id',
|
|
588
|
+
'content',
|
|
589
|
+
'cache_control',
|
|
590
|
+
] as const;
|
|
591
|
+
const ANTHROPIC_GENERIC_RESULT_FIELDS = [
|
|
592
|
+
...ANTHROPIC_RESULT_FIELDS,
|
|
593
|
+
'is_error',
|
|
594
|
+
] as const;
|
|
595
|
+
const ANTHROPIC_SERVER_RESULT_FIELDS = [
|
|
596
|
+
...ANTHROPIC_RESULT_FIELDS,
|
|
597
|
+
'caller',
|
|
598
|
+
] as const;
|
|
599
|
+
const LOCAL_RESULT_METADATA_FIELDS = [
|
|
600
|
+
'id',
|
|
601
|
+
'index',
|
|
602
|
+
'agentId',
|
|
603
|
+
'groupId',
|
|
604
|
+
] as const;
|
|
605
|
+
const TOOL_RESULT_ENVELOPE_FIELDS: Readonly<
|
|
606
|
+
Record<string, readonly string[]>
|
|
607
|
+
> = {
|
|
608
|
+
advisor_tool_result: ANTHROPIC_RESULT_FIELDS,
|
|
609
|
+
bash_code_execution_tool_result: ANTHROPIC_RESULT_FIELDS,
|
|
610
|
+
code_execution_tool_result: ANTHROPIC_RESULT_FIELDS,
|
|
611
|
+
mcp_tool_result: ANTHROPIC_GENERIC_RESULT_FIELDS,
|
|
612
|
+
text_editor_code_execution_tool_result: ANTHROPIC_RESULT_FIELDS,
|
|
613
|
+
tool_search_tool_result: ANTHROPIC_RESULT_FIELDS,
|
|
614
|
+
tool_result: ANTHROPIC_GENERIC_RESULT_FIELDS,
|
|
615
|
+
web_fetch_tool_result: ANTHROPIC_SERVER_RESULT_FIELDS,
|
|
616
|
+
web_search_tool_result: ANTHROPIC_SERVER_RESULT_FIELDS,
|
|
617
|
+
server_tool_call_result: [
|
|
618
|
+
'type',
|
|
619
|
+
'toolCallId',
|
|
620
|
+
'status',
|
|
621
|
+
'output',
|
|
622
|
+
...LOCAL_RESULT_METADATA_FIELDS,
|
|
623
|
+
],
|
|
624
|
+
server_tool_result: [
|
|
625
|
+
'type',
|
|
626
|
+
'tool_call_id',
|
|
627
|
+
'status',
|
|
628
|
+
'output',
|
|
629
|
+
...LOCAL_RESULT_METADATA_FIELDS,
|
|
630
|
+
],
|
|
631
|
+
codeExecutionResult: [
|
|
632
|
+
'type',
|
|
633
|
+
'codeExecutionResult',
|
|
634
|
+
'agentId',
|
|
635
|
+
'groupId',
|
|
636
|
+
],
|
|
637
|
+
toolResponse: [
|
|
638
|
+
'type',
|
|
639
|
+
'toolResponse',
|
|
640
|
+
'thought',
|
|
641
|
+
'thoughtSignature',
|
|
642
|
+
'agentId',
|
|
643
|
+
'groupId',
|
|
644
|
+
],
|
|
645
|
+
toolResult: ['type', 'toolResult', 'agentId', 'groupId'],
|
|
646
|
+
};
|
|
647
|
+
|
|
648
|
+
function anthropicDescriptor(
|
|
649
|
+
record: Record<string, unknown>,
|
|
650
|
+
type: string,
|
|
651
|
+
expectedToolNames: readonly string[]
|
|
652
|
+
): ProviderToolResultPartDescriptor | undefined {
|
|
653
|
+
const toolCallId = readPairingString(record, 'tool_use_id');
|
|
654
|
+
const content = readDataProperty(record, 'content');
|
|
655
|
+
return toolCallId != null &&
|
|
656
|
+
content.found &&
|
|
657
|
+
getRecord(content.value) != null
|
|
658
|
+
? {
|
|
659
|
+
type,
|
|
660
|
+
toolCallId,
|
|
661
|
+
compatibleCallKinds: ANTHROPIC_SERVER_CALL_KINDS,
|
|
662
|
+
expectedToolNames,
|
|
663
|
+
}
|
|
664
|
+
: undefined;
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
export function getProviderToolResultPartDescriptor(
|
|
668
|
+
part: unknown
|
|
669
|
+
): ProviderToolResultPartDescriptor | undefined {
|
|
670
|
+
const record = getRecord(part);
|
|
671
|
+
if (record == null) {
|
|
672
|
+
return undefined;
|
|
673
|
+
}
|
|
674
|
+
const type = readString(record, 'type');
|
|
675
|
+
const allowedFields = type == null ? undefined : TOOL_RESULT_ENVELOPE_FIELDS[type];
|
|
676
|
+
if (
|
|
677
|
+
type == null ||
|
|
678
|
+
allowedFields == null ||
|
|
679
|
+
!hasOnlyOwnDataProperties(record, allowedFields) ||
|
|
680
|
+
!hasValidProviderMetadata(record) ||
|
|
681
|
+
!hasValidCacheControlProperty(record) ||
|
|
682
|
+
!hasValidCallerProperty(record)
|
|
683
|
+
) {
|
|
684
|
+
return undefined;
|
|
685
|
+
}
|
|
686
|
+
const structuredToolNames = ANTHROPIC_STRUCTURED_RESULT_TOOL_NAMES[type];
|
|
687
|
+
if (structuredToolNames != null) {
|
|
688
|
+
return anthropicDescriptor(
|
|
689
|
+
record,
|
|
690
|
+
type,
|
|
691
|
+
structuredToolNames
|
|
692
|
+
);
|
|
693
|
+
}
|
|
694
|
+
if (type === 'web_search_tool_result') {
|
|
695
|
+
const toolCallId = readPairingString(record, 'tool_use_id');
|
|
696
|
+
const content = readDataProperty(record, 'content');
|
|
697
|
+
return toolCallId != null &&
|
|
698
|
+
content.found &&
|
|
699
|
+
(getRecord(content.value) != null ||
|
|
700
|
+
isOpaqueBoundedArray(content.value))
|
|
701
|
+
? {
|
|
702
|
+
type,
|
|
703
|
+
toolCallId,
|
|
704
|
+
compatibleCallKinds: ANTHROPIC_SERVER_CALL_KINDS,
|
|
705
|
+
expectedToolNames: WEB_SEARCH_TOOL_NAMES,
|
|
706
|
+
}
|
|
707
|
+
: undefined;
|
|
708
|
+
}
|
|
709
|
+
if (type === 'mcp_tool_result') {
|
|
710
|
+
const toolCallId = readPairingString(record, 'tool_use_id');
|
|
711
|
+
const content = readDataProperty(record, 'content');
|
|
712
|
+
const isError = readDataProperty(record, 'is_error');
|
|
713
|
+
return toolCallId != null &&
|
|
714
|
+
content.found &&
|
|
715
|
+
isOpaqueStringOrBoundedArray(content.value) &&
|
|
716
|
+
isError.found &&
|
|
717
|
+
typeof isError.value === 'boolean'
|
|
718
|
+
? { type, toolCallId, compatibleCallKinds: MCP_CALL_KINDS }
|
|
719
|
+
: undefined;
|
|
720
|
+
}
|
|
721
|
+
if (type === 'server_tool_call_result' || type === 'server_tool_result') {
|
|
722
|
+
const toolCallId = readPairingString(
|
|
723
|
+
record,
|
|
724
|
+
type === 'server_tool_call_result' ? 'toolCallId' : 'tool_call_id'
|
|
725
|
+
);
|
|
726
|
+
const status = readString(record, 'status');
|
|
727
|
+
const output = readDataProperty(record, 'output');
|
|
728
|
+
return toolCallId != null &&
|
|
729
|
+
(status === 'success' || status === 'error') &&
|
|
730
|
+
output.found &&
|
|
731
|
+
output.value !== undefined
|
|
732
|
+
? { type, toolCallId, compatibleCallKinds: SERVER_CALL_KINDS }
|
|
733
|
+
: undefined;
|
|
734
|
+
}
|
|
735
|
+
if (type === 'codeExecutionResult') {
|
|
736
|
+
return isGoogleCodeExecutionResult(record)
|
|
737
|
+
? {
|
|
738
|
+
type,
|
|
739
|
+
compatibleCallKinds: GOOGLE_CALL_KINDS,
|
|
740
|
+
requiresPreviousExecutableCode: true,
|
|
741
|
+
}
|
|
742
|
+
: undefined;
|
|
743
|
+
}
|
|
744
|
+
if (type === 'toolResponse') {
|
|
745
|
+
return getGoogleToolResponseDescriptor(record, type);
|
|
746
|
+
}
|
|
747
|
+
if (type === 'toolResult') {
|
|
748
|
+
return getBedrockDescriptor(record, type);
|
|
749
|
+
}
|
|
750
|
+
if (type === 'tool_result') {
|
|
751
|
+
const toolCallId = readPairingString(record, 'tool_use_id');
|
|
752
|
+
const content = readDataProperty(record, 'content');
|
|
753
|
+
const isError = readDataProperty(record, 'is_error');
|
|
754
|
+
return toolCallId != null &&
|
|
755
|
+
content.found &&
|
|
756
|
+
isOpaqueStringOrBoundedArray(content.value) &&
|
|
757
|
+
(!isError.found || typeof isError.value === 'boolean')
|
|
758
|
+
? {
|
|
759
|
+
type,
|
|
760
|
+
toolCallId,
|
|
761
|
+
compatibleCallKinds: TOOL_CALL_KINDS,
|
|
762
|
+
allowHumanMessagePairing: true,
|
|
763
|
+
}
|
|
764
|
+
: undefined;
|
|
765
|
+
}
|
|
766
|
+
return undefined;
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
function optionalName(
|
|
770
|
+
record: Record<string, unknown>
|
|
771
|
+
): { readonly name?: string } | undefined {
|
|
772
|
+
const property = readDataProperty(record, 'name');
|
|
773
|
+
if (!property.found || property.value === undefined) {
|
|
774
|
+
return {};
|
|
775
|
+
}
|
|
776
|
+
return isBoundedProviderPairingString(property.value)
|
|
777
|
+
? { name: property.value }
|
|
778
|
+
: undefined;
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
function hasExactCallShape(
|
|
782
|
+
record: Record<string, unknown>,
|
|
783
|
+
fields: readonly string[],
|
|
784
|
+
required: readonly string[]
|
|
785
|
+
): boolean {
|
|
786
|
+
return (
|
|
787
|
+
hasOnlyOwnDataProperties(record, fields) &&
|
|
788
|
+
hasRequiredDataProperties(record, required) &&
|
|
789
|
+
hasValidProviderMetadata(record)
|
|
790
|
+
);
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
function hasOptionalCallType(
|
|
794
|
+
record: Record<string, unknown>,
|
|
795
|
+
expected: string
|
|
796
|
+
): boolean {
|
|
797
|
+
const type = readDataProperty(record, 'type');
|
|
798
|
+
return !type.found || type.value === expected;
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
function isAnthropicServerToolCall(
|
|
802
|
+
callId: string,
|
|
803
|
+
name: string | undefined
|
|
804
|
+
): boolean {
|
|
805
|
+
return (
|
|
806
|
+
callId.startsWith(ANTHROPIC_SERVER_TOOL_ID_PREFIX) &&
|
|
807
|
+
name != null &&
|
|
808
|
+
ANTHROPIC_SERVER_TOOL_NAMES.has(name)
|
|
809
|
+
);
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
function getAnthropicCallKind(
|
|
813
|
+
type: 'tool_use' | 'server_tool_use' | 'mcp_tool_use',
|
|
814
|
+
callId: string,
|
|
815
|
+
name: string | undefined
|
|
816
|
+
): ProviderToolCallKind {
|
|
817
|
+
if (type === 'mcp_tool_use') {
|
|
818
|
+
return 'mcp';
|
|
819
|
+
}
|
|
820
|
+
if (
|
|
821
|
+
type === 'server_tool_use' ||
|
|
822
|
+
isAnthropicServerToolCall(callId, name)
|
|
823
|
+
) {
|
|
824
|
+
return 'anthropic-server';
|
|
825
|
+
}
|
|
826
|
+
return 'tool';
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
const AI_TOOL_CALL_FIELDS = ['type', 'id', 'name', 'args'] as const;
|
|
830
|
+
const LOCAL_CALL_METADATA_FIELDS = ['agentId', 'groupId'] as const;
|
|
831
|
+
const STANDARD_CALL_FIELDS = [
|
|
832
|
+
'type',
|
|
833
|
+
'id',
|
|
834
|
+
'name',
|
|
835
|
+
'args',
|
|
836
|
+
'index',
|
|
837
|
+
...LOCAL_CALL_METADATA_FIELDS,
|
|
838
|
+
] as const;
|
|
839
|
+
const LIBRECHAT_CALL_FIELDS = [
|
|
840
|
+
'type',
|
|
841
|
+
'id',
|
|
842
|
+
'name',
|
|
843
|
+
'args',
|
|
844
|
+
'output',
|
|
845
|
+
'outcome',
|
|
846
|
+
'auth',
|
|
847
|
+
'expires_at',
|
|
848
|
+
] as const;
|
|
849
|
+
const ANTHROPIC_CALL_FIELDS = [
|
|
850
|
+
'type',
|
|
851
|
+
'id',
|
|
852
|
+
'name',
|
|
853
|
+
'input',
|
|
854
|
+
'caller',
|
|
855
|
+
'cache_control',
|
|
856
|
+
'index',
|
|
857
|
+
...LOCAL_CALL_METADATA_FIELDS,
|
|
858
|
+
] as const;
|
|
859
|
+
const ANTHROPIC_MCP_CALL_FIELDS = [
|
|
860
|
+
'type',
|
|
861
|
+
'id',
|
|
862
|
+
'name',
|
|
863
|
+
'input',
|
|
864
|
+
'server_name',
|
|
865
|
+
'cache_control',
|
|
866
|
+
'index',
|
|
867
|
+
...LOCAL_CALL_METADATA_FIELDS,
|
|
868
|
+
] as const;
|
|
869
|
+
const GOOGLE_CALL_FIELDS = [
|
|
870
|
+
'type',
|
|
871
|
+
'toolCall',
|
|
872
|
+
'thought',
|
|
873
|
+
'thoughtSignature',
|
|
874
|
+
...LOCAL_CALL_METADATA_FIELDS,
|
|
875
|
+
] as const;
|
|
876
|
+
|
|
877
|
+
export function getProviderAIMessageToolCallDescriptor(
|
|
878
|
+
toolCall: unknown
|
|
879
|
+
): ProviderToolCallPartDescriptor | undefined {
|
|
880
|
+
const record = getRecord(toolCall);
|
|
881
|
+
if (
|
|
882
|
+
record == null ||
|
|
883
|
+
!hasExactCallShape(
|
|
884
|
+
record,
|
|
885
|
+
AI_TOOL_CALL_FIELDS,
|
|
886
|
+
['id', 'name', 'args']
|
|
887
|
+
) ||
|
|
888
|
+
!hasOptionalCallType(record, 'tool_call')
|
|
889
|
+
) {
|
|
890
|
+
return undefined;
|
|
891
|
+
}
|
|
892
|
+
const callId = readPairingString(record, 'id');
|
|
893
|
+
const name = optionalName(record);
|
|
894
|
+
return callId == null || name == null
|
|
895
|
+
? undefined
|
|
896
|
+
: {
|
|
897
|
+
callId,
|
|
898
|
+
kind: isAnthropicServerToolCall(callId, name.name)
|
|
899
|
+
? 'anthropic-server'
|
|
900
|
+
: 'tool',
|
|
901
|
+
sourceType: 'ai_tool_calls',
|
|
902
|
+
...name,
|
|
903
|
+
};
|
|
904
|
+
}
|
|
905
|
+
|
|
906
|
+
export function getProviderToolCallPartDescriptor(
|
|
907
|
+
part: unknown
|
|
908
|
+
): ProviderToolCallPartDescriptor | undefined {
|
|
909
|
+
const record = getRecord(part);
|
|
910
|
+
if (record == null) {
|
|
911
|
+
return undefined;
|
|
912
|
+
}
|
|
913
|
+
const type = readString(record, 'type');
|
|
914
|
+
if (type === 'tool_call') {
|
|
915
|
+
const nested = readDataProperty(record, 'tool_call');
|
|
916
|
+
const call = nested.found ? getRecord(nested.value) : record;
|
|
917
|
+
const outerIsValid = nested.found
|
|
918
|
+
? hasExactCallShape(
|
|
919
|
+
record,
|
|
920
|
+
['type', 'tool_call', 'agentId', 'groupId'],
|
|
921
|
+
['tool_call']
|
|
922
|
+
)
|
|
923
|
+
: hasExactCallShape(
|
|
924
|
+
record,
|
|
925
|
+
STANDARD_CALL_FIELDS,
|
|
926
|
+
['id', 'name', 'args']
|
|
927
|
+
);
|
|
928
|
+
const callIsValid = nested.found
|
|
929
|
+
? call != null &&
|
|
930
|
+
hasExactCallShape(
|
|
931
|
+
call,
|
|
932
|
+
LIBRECHAT_CALL_FIELDS,
|
|
933
|
+
['id', 'name', 'args']
|
|
934
|
+
) &&
|
|
935
|
+
hasOptionalCallType(call, 'tool_call')
|
|
936
|
+
: call != null;
|
|
937
|
+
if (!outerIsValid || !callIsValid) {
|
|
938
|
+
return undefined;
|
|
939
|
+
}
|
|
940
|
+
const callId = call == null ? undefined : readPairingString(call, 'id');
|
|
941
|
+
const name = call == null ? undefined : optionalName(call);
|
|
942
|
+
return callId == null || name == null
|
|
943
|
+
? undefined
|
|
944
|
+
: {
|
|
945
|
+
callId,
|
|
946
|
+
kind: isAnthropicServerToolCall(callId, name.name)
|
|
947
|
+
? 'anthropic-server'
|
|
948
|
+
: 'tool',
|
|
949
|
+
sourceType: type,
|
|
950
|
+
...name,
|
|
951
|
+
};
|
|
952
|
+
}
|
|
953
|
+
if (
|
|
954
|
+
type === 'tool_use' ||
|
|
955
|
+
type === 'server_tool_use' ||
|
|
956
|
+
type === 'mcp_tool_use'
|
|
957
|
+
) {
|
|
958
|
+
const fields =
|
|
959
|
+
type === 'mcp_tool_use'
|
|
960
|
+
? ANTHROPIC_MCP_CALL_FIELDS
|
|
961
|
+
: ANTHROPIC_CALL_FIELDS;
|
|
962
|
+
const required =
|
|
963
|
+
type === 'mcp_tool_use'
|
|
964
|
+
? ['id', 'name', 'input', 'server_name']
|
|
965
|
+
: ['id', 'name', 'input'];
|
|
966
|
+
if (
|
|
967
|
+
!hasExactCallShape(record, fields, required) ||
|
|
968
|
+
!hasValidCacheControlProperty(record) ||
|
|
969
|
+
!hasValidCallerProperty(record) ||
|
|
970
|
+
(type === 'mcp_tool_use' &&
|
|
971
|
+
readPairingString(record, 'server_name') == null)
|
|
972
|
+
) {
|
|
973
|
+
return undefined;
|
|
974
|
+
}
|
|
975
|
+
const callId = readPairingString(record, 'id');
|
|
976
|
+
const name = optionalName(record);
|
|
977
|
+
if (
|
|
978
|
+
type === 'server_tool_use' &&
|
|
979
|
+
(name?.name == null || !ANTHROPIC_SERVER_TOOL_NAMES.has(name.name))
|
|
980
|
+
) {
|
|
981
|
+
return undefined;
|
|
982
|
+
}
|
|
983
|
+
return callId == null || name == null
|
|
984
|
+
? undefined
|
|
985
|
+
: {
|
|
986
|
+
callId,
|
|
987
|
+
kind: getAnthropicCallKind(type, callId, name.name),
|
|
988
|
+
sourceType: type,
|
|
989
|
+
...name,
|
|
990
|
+
};
|
|
991
|
+
}
|
|
992
|
+
if (type === 'server_tool_call') {
|
|
993
|
+
if (
|
|
994
|
+
!hasExactCallShape(
|
|
995
|
+
record,
|
|
996
|
+
STANDARD_CALL_FIELDS,
|
|
997
|
+
['id', 'name', 'args']
|
|
998
|
+
)
|
|
999
|
+
) {
|
|
1000
|
+
return undefined;
|
|
1001
|
+
}
|
|
1002
|
+
const callId = readPairingString(record, 'id');
|
|
1003
|
+
const name = optionalName(record);
|
|
1004
|
+
return callId == null || name == null
|
|
1005
|
+
? undefined
|
|
1006
|
+
: {
|
|
1007
|
+
callId,
|
|
1008
|
+
kind: isAnthropicServerToolCall(callId, name.name)
|
|
1009
|
+
? 'anthropic-server'
|
|
1010
|
+
: 'server',
|
|
1011
|
+
sourceType: type,
|
|
1012
|
+
...name,
|
|
1013
|
+
};
|
|
1014
|
+
}
|
|
1015
|
+
if (type === 'toolCall' || type === 'toolUse') {
|
|
1016
|
+
const outerFields =
|
|
1017
|
+
type === 'toolCall'
|
|
1018
|
+
? GOOGLE_CALL_FIELDS
|
|
1019
|
+
: ['type', 'toolUse', 'agentId', 'groupId'];
|
|
1020
|
+
if (
|
|
1021
|
+
!hasExactCallShape(record, outerFields, [type]) ||
|
|
1022
|
+
!hasValidProviderMetadata(record)
|
|
1023
|
+
) {
|
|
1024
|
+
return undefined;
|
|
1025
|
+
}
|
|
1026
|
+
const nested = readDataProperty(record, type);
|
|
1027
|
+
const call = nested.found ? getRecord(nested.value) : undefined;
|
|
1028
|
+
if (call == null) {
|
|
1029
|
+
return undefined;
|
|
1030
|
+
}
|
|
1031
|
+
if (type === 'toolCall') {
|
|
1032
|
+
const isNamedVariant = hasExactCallShape(
|
|
1033
|
+
call,
|
|
1034
|
+
['id', 'name', 'args'],
|
|
1035
|
+
['id', 'name', 'args']
|
|
1036
|
+
);
|
|
1037
|
+
const isTypedVariant = hasExactCallShape(
|
|
1038
|
+
call,
|
|
1039
|
+
['id', 'toolType', 'args'],
|
|
1040
|
+
['id', 'toolType', 'args']
|
|
1041
|
+
);
|
|
1042
|
+
if (
|
|
1043
|
+
(!isNamedVariant && !isTypedVariant) ||
|
|
1044
|
+
(isTypedVariant && readPairingString(call, 'toolType') == null)
|
|
1045
|
+
) {
|
|
1046
|
+
return undefined;
|
|
1047
|
+
}
|
|
1048
|
+
} else {
|
|
1049
|
+
const nestedType = readDataProperty(call, 'type');
|
|
1050
|
+
if (
|
|
1051
|
+
!hasExactCallShape(
|
|
1052
|
+
call,
|
|
1053
|
+
['toolUseId', 'name', 'input', 'type'],
|
|
1054
|
+
['toolUseId', 'name', 'input']
|
|
1055
|
+
) ||
|
|
1056
|
+
(nestedType.found && typeof nestedType.value !== 'string')
|
|
1057
|
+
) {
|
|
1058
|
+
return undefined;
|
|
1059
|
+
}
|
|
1060
|
+
}
|
|
1061
|
+
const callId = readPairingString(
|
|
1062
|
+
call,
|
|
1063
|
+
type === 'toolCall' ? 'id' : 'toolUseId'
|
|
1064
|
+
);
|
|
1065
|
+
const name = optionalName(call);
|
|
1066
|
+
return callId == null || name == null
|
|
1067
|
+
? undefined
|
|
1068
|
+
: {
|
|
1069
|
+
callId,
|
|
1070
|
+
kind: type === 'toolCall' ? 'google' : 'bedrock',
|
|
1071
|
+
sourceType: type,
|
|
1072
|
+
...name,
|
|
1073
|
+
};
|
|
1074
|
+
}
|
|
1075
|
+
return undefined;
|
|
1076
|
+
}
|
|
1077
|
+
|
|
1078
|
+
export function appendProviderToolCallDescriptor(
|
|
1079
|
+
index: ProviderToolCallIndex,
|
|
1080
|
+
descriptor: ProviderToolCallPartDescriptor
|
|
1081
|
+
): void {
|
|
1082
|
+
if (
|
|
1083
|
+
!PROVIDER_TOOL_CALL_KINDS.has(descriptor.kind) ||
|
|
1084
|
+
!isBoundedProviderPairingString(descriptor.callId) ||
|
|
1085
|
+
!isBoundedProviderPairingString(descriptor.sourceType) ||
|
|
1086
|
+
(descriptor.name != null &&
|
|
1087
|
+
!isBoundedProviderPairingString(descriptor.name))
|
|
1088
|
+
) {
|
|
1089
|
+
return;
|
|
1090
|
+
}
|
|
1091
|
+
const existing = index.get(descriptor.callId);
|
|
1092
|
+
if (existing === undefined) {
|
|
1093
|
+
if (index.size >= PROVIDER_TOOL_PAIRING_MAX_OUTER_PARTS) {
|
|
1094
|
+
return;
|
|
1095
|
+
}
|
|
1096
|
+
index.set(descriptor.callId, { descriptor });
|
|
1097
|
+
return;
|
|
1098
|
+
}
|
|
1099
|
+
if (existing === null) {
|
|
1100
|
+
return;
|
|
1101
|
+
}
|
|
1102
|
+
const isDualRepresentation =
|
|
1103
|
+
existing.secondarySourceType == null &&
|
|
1104
|
+
existing.descriptor.sourceType !== descriptor.sourceType &&
|
|
1105
|
+
(existing.descriptor.sourceType === 'ai_tool_calls' ||
|
|
1106
|
+
descriptor.sourceType === 'ai_tool_calls');
|
|
1107
|
+
if (
|
|
1108
|
+
existing.descriptor.kind === descriptor.kind &&
|
|
1109
|
+
existing.descriptor.name === descriptor.name &&
|
|
1110
|
+
isDualRepresentation
|
|
1111
|
+
) {
|
|
1112
|
+
existing.secondarySourceType = descriptor.sourceType;
|
|
1113
|
+
return;
|
|
1114
|
+
}
|
|
1115
|
+
index.set(descriptor.callId, null);
|
|
1116
|
+
}
|
|
1117
|
+
|
|
1118
|
+
function isExecutableCodePart(part: unknown): boolean {
|
|
1119
|
+
const record = getRecord(part);
|
|
1120
|
+
if (
|
|
1121
|
+
record == null ||
|
|
1122
|
+
readString(record, 'type') !== 'executableCode' ||
|
|
1123
|
+
!hasOnlyOwnDataProperties(record, [
|
|
1124
|
+
'type',
|
|
1125
|
+
'executableCode',
|
|
1126
|
+
'agentId',
|
|
1127
|
+
'groupId',
|
|
1128
|
+
]) ||
|
|
1129
|
+
!hasValidProviderMetadata(record)
|
|
1130
|
+
) {
|
|
1131
|
+
return false;
|
|
1132
|
+
}
|
|
1133
|
+
const nested = readDataProperty(record, 'executableCode');
|
|
1134
|
+
const executable = nested.found ? getRecord(nested.value) : undefined;
|
|
1135
|
+
return (
|
|
1136
|
+
executable != null &&
|
|
1137
|
+
hasOnlyOwnDataProperties(executable, ['language', 'code']) &&
|
|
1138
|
+
readString(executable, 'language') != null &&
|
|
1139
|
+
readString(executable, 'code', true) != null
|
|
1140
|
+
);
|
|
1141
|
+
}
|
|
1142
|
+
|
|
1143
|
+
export function consumeProviderToolResultPair(
|
|
1144
|
+
descriptor: ProviderToolResultPartDescriptor,
|
|
1145
|
+
calls: ProviderToolCallIndex,
|
|
1146
|
+
previousPart?: unknown
|
|
1147
|
+
): boolean {
|
|
1148
|
+
if (descriptor.requiresPreviousExecutableCode === true) {
|
|
1149
|
+
return isExecutableCodePart(previousPart);
|
|
1150
|
+
}
|
|
1151
|
+
if (
|
|
1152
|
+
descriptor.toolCallId == null ||
|
|
1153
|
+
!isBoundedProviderPairingString(descriptor.toolCallId)
|
|
1154
|
+
) {
|
|
1155
|
+
return false;
|
|
1156
|
+
}
|
|
1157
|
+
const entry = calls.get(descriptor.toolCallId);
|
|
1158
|
+
if (entry == null) {
|
|
1159
|
+
return false;
|
|
1160
|
+
}
|
|
1161
|
+
const candidate = entry.descriptor;
|
|
1162
|
+
if (!descriptor.compatibleCallKinds.includes(candidate.kind)) {
|
|
1163
|
+
return false;
|
|
1164
|
+
}
|
|
1165
|
+
if (
|
|
1166
|
+
descriptor.expectedToolNames != null &&
|
|
1167
|
+
(candidate.name == null ||
|
|
1168
|
+
!descriptor.expectedToolNames.includes(candidate.name))
|
|
1169
|
+
) {
|
|
1170
|
+
return false;
|
|
1171
|
+
}
|
|
1172
|
+
calls.delete(descriptor.toolCallId);
|
|
1173
|
+
return true;
|
|
1174
|
+
}
|