@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.
Files changed (141) hide show
  1. package/dist/cjs/agents/AgentContext.cjs +46 -39
  2. package/dist/cjs/agents/AgentContext.cjs.map +1 -1
  3. package/dist/cjs/agents/projection.cjs +2 -2
  4. package/dist/cjs/agents/projection.cjs.map +1 -1
  5. package/dist/cjs/graphs/Graph.cjs +28 -18
  6. package/dist/cjs/graphs/Graph.cjs.map +1 -1
  7. package/dist/cjs/graphs/MultiAgentGraph.cjs +87 -16
  8. package/dist/cjs/graphs/MultiAgentGraph.cjs.map +1 -1
  9. package/dist/cjs/llm/openai/index.cjs +10 -0
  10. package/dist/cjs/llm/openai/index.cjs.map +1 -1
  11. package/dist/cjs/main.cjs +14 -0
  12. package/dist/cjs/messages/alternation.cjs +125 -20
  13. package/dist/cjs/messages/alternation.cjs.map +1 -1
  14. package/dist/cjs/messages/core.cjs +195 -18
  15. package/dist/cjs/messages/core.cjs.map +1 -1
  16. package/dist/cjs/messages/format.cjs +488 -118
  17. package/dist/cjs/messages/format.cjs.map +1 -1
  18. package/dist/cjs/messages/handoffCue.cjs +3 -2
  19. package/dist/cjs/messages/handoffCue.cjs.map +1 -1
  20. package/dist/cjs/messages/index.cjs +1 -0
  21. package/dist/cjs/messages/injected.cjs +5 -2
  22. package/dist/cjs/messages/injected.cjs.map +1 -1
  23. package/dist/cjs/messages/provenance.cjs +411 -0
  24. package/dist/cjs/messages/provenance.cjs.map +1 -0
  25. package/dist/cjs/messages/prune.cjs +18 -13
  26. package/dist/cjs/messages/prune.cjs.map +1 -1
  27. package/dist/cjs/messages/toolResultTypes.cjs +663 -0
  28. package/dist/cjs/messages/toolResultTypes.cjs.map +1 -0
  29. package/dist/cjs/run.cjs +7 -4
  30. package/dist/cjs/run.cjs.map +1 -1
  31. package/dist/cjs/tools/BashProgrammaticToolCalling.cjs +21 -4
  32. package/dist/cjs/tools/BashProgrammaticToolCalling.cjs.map +1 -1
  33. package/dist/cjs/tools/CallerCapabilities.cjs +48 -0
  34. package/dist/cjs/tools/CallerCapabilities.cjs.map +1 -0
  35. package/dist/cjs/tools/ProgrammaticCallerPolicy.cjs +34 -0
  36. package/dist/cjs/tools/ProgrammaticCallerPolicy.cjs.map +1 -0
  37. package/dist/cjs/tools/ProgrammaticToolCalling.cjs +21 -4
  38. package/dist/cjs/tools/ProgrammaticToolCalling.cjs.map +1 -1
  39. package/dist/cjs/tools/ToolNode.cjs +25 -24
  40. package/dist/cjs/tools/ToolNode.cjs.map +1 -1
  41. package/dist/cjs/tools/cloudflare/CloudflareProgrammaticToolCalling.cjs +12 -4
  42. package/dist/cjs/tools/cloudflare/CloudflareProgrammaticToolCalling.cjs.map +1 -1
  43. package/dist/cjs/tools/local/LocalProgrammaticToolCalling.cjs +13 -4
  44. package/dist/cjs/tools/local/LocalProgrammaticToolCalling.cjs.map +1 -1
  45. package/dist/cjs/tools/local/resolveLocalExecutionTools.cjs +47 -11
  46. package/dist/cjs/tools/local/resolveLocalExecutionTools.cjs.map +1 -1
  47. package/dist/cjs/tools/ptcTimeout.cjs.map +1 -1
  48. package/dist/cjs/utils/toolContent.cjs +2 -2
  49. package/dist/cjs/utils/toolContent.cjs.map +1 -1
  50. package/dist/esm/agents/AgentContext.mjs +46 -39
  51. package/dist/esm/agents/AgentContext.mjs.map +1 -1
  52. package/dist/esm/agents/projection.mjs +2 -2
  53. package/dist/esm/agents/projection.mjs.map +1 -1
  54. package/dist/esm/graphs/Graph.mjs +30 -20
  55. package/dist/esm/graphs/Graph.mjs.map +1 -1
  56. package/dist/esm/graphs/MultiAgentGraph.mjs +87 -16
  57. package/dist/esm/graphs/MultiAgentGraph.mjs.map +1 -1
  58. package/dist/esm/llm/openai/index.mjs +10 -0
  59. package/dist/esm/llm/openai/index.mjs.map +1 -1
  60. package/dist/esm/main.mjs +4 -3
  61. package/dist/esm/messages/alternation.mjs +125 -20
  62. package/dist/esm/messages/alternation.mjs.map +1 -1
  63. package/dist/esm/messages/core.mjs +196 -19
  64. package/dist/esm/messages/core.mjs.map +1 -1
  65. package/dist/esm/messages/format.mjs +488 -119
  66. package/dist/esm/messages/format.mjs.map +1 -1
  67. package/dist/esm/messages/handoffCue.mjs +3 -2
  68. package/dist/esm/messages/handoffCue.mjs.map +1 -1
  69. package/dist/esm/messages/index.mjs +1 -0
  70. package/dist/esm/messages/injected.mjs +5 -2
  71. package/dist/esm/messages/injected.mjs.map +1 -1
  72. package/dist/esm/messages/provenance.mjs +401 -0
  73. package/dist/esm/messages/provenance.mjs.map +1 -0
  74. package/dist/esm/messages/prune.mjs +18 -13
  75. package/dist/esm/messages/prune.mjs.map +1 -1
  76. package/dist/esm/messages/toolResultTypes.mjs +657 -0
  77. package/dist/esm/messages/toolResultTypes.mjs.map +1 -0
  78. package/dist/esm/run.mjs +7 -4
  79. package/dist/esm/run.mjs.map +1 -1
  80. package/dist/esm/tools/BashProgrammaticToolCalling.mjs +21 -4
  81. package/dist/esm/tools/BashProgrammaticToolCalling.mjs.map +1 -1
  82. package/dist/esm/tools/CallerCapabilities.mjs +45 -0
  83. package/dist/esm/tools/CallerCapabilities.mjs.map +1 -0
  84. package/dist/esm/tools/ProgrammaticCallerPolicy.mjs +32 -0
  85. package/dist/esm/tools/ProgrammaticCallerPolicy.mjs.map +1 -0
  86. package/dist/esm/tools/ProgrammaticToolCalling.mjs +21 -4
  87. package/dist/esm/tools/ProgrammaticToolCalling.mjs.map +1 -1
  88. package/dist/esm/tools/ToolNode.mjs +25 -24
  89. package/dist/esm/tools/ToolNode.mjs.map +1 -1
  90. package/dist/esm/tools/cloudflare/CloudflareProgrammaticToolCalling.mjs +14 -6
  91. package/dist/esm/tools/cloudflare/CloudflareProgrammaticToolCalling.mjs.map +1 -1
  92. package/dist/esm/tools/local/LocalProgrammaticToolCalling.mjs +15 -6
  93. package/dist/esm/tools/local/LocalProgrammaticToolCalling.mjs.map +1 -1
  94. package/dist/esm/tools/local/resolveLocalExecutionTools.mjs +47 -12
  95. package/dist/esm/tools/local/resolveLocalExecutionTools.mjs.map +1 -1
  96. package/dist/esm/tools/ptcTimeout.mjs.map +1 -1
  97. package/dist/esm/utils/toolContent.mjs +2 -2
  98. package/dist/esm/utils/toolContent.mjs.map +1 -1
  99. package/dist/types/agents/AgentContext.d.ts +8 -13
  100. package/dist/types/agents/projection.d.ts +3 -1
  101. package/dist/types/graphs/Graph.d.ts +1 -1
  102. package/dist/types/llm/openai/index.d.ts +20 -0
  103. package/dist/types/messages/format.d.ts +5 -0
  104. package/dist/types/messages/index.d.ts +1 -0
  105. package/dist/types/messages/provenance.d.ts +85 -0
  106. package/dist/types/messages/toolResultTypes.d.ts +33 -0
  107. package/dist/types/tools/CallerCapabilities.d.ts +12 -0
  108. package/dist/types/tools/ProgrammaticCallerPolicy.d.ts +20 -0
  109. package/dist/types/tools/ToolNode.d.ts +4 -7
  110. package/dist/types/tools/local/resolveLocalExecutionTools.d.ts +4 -0
  111. package/dist/types/tools/ptcTimeout.d.ts +8 -0
  112. package/dist/types/types/graph.d.ts +3 -1
  113. package/dist/types/types/tools.d.ts +13 -2
  114. package/package.json +1 -1
  115. package/src/agents/AgentContext.ts +109 -67
  116. package/src/agents/projection.ts +9 -1
  117. package/src/graphs/Graph.ts +56 -23
  118. package/src/graphs/MultiAgentGraph.ts +175 -33
  119. package/src/llm/openai/index.ts +43 -0
  120. package/src/messages/alternation.ts +217 -34
  121. package/src/messages/core.ts +358 -38
  122. package/src/messages/format.ts +958 -137
  123. package/src/messages/handoffCue.ts +7 -4
  124. package/src/messages/index.ts +1 -0
  125. package/src/messages/injected.ts +9 -6
  126. package/src/messages/provenance.ts +897 -0
  127. package/src/messages/prune.ts +28 -10
  128. package/src/messages/toolResultTypes.ts +1174 -0
  129. package/src/run.ts +9 -4
  130. package/src/tools/BashProgrammaticToolCalling.ts +46 -12
  131. package/src/tools/CallerCapabilities.ts +78 -0
  132. package/src/tools/ProgrammaticCallerPolicy.ts +97 -0
  133. package/src/tools/ProgrammaticToolCalling.ts +41 -7
  134. package/src/tools/ToolNode.ts +70 -52
  135. package/src/tools/cloudflare/CloudflareProgrammaticToolCalling.ts +24 -26
  136. package/src/tools/local/LocalProgrammaticToolCalling.ts +28 -23
  137. package/src/tools/local/resolveLocalExecutionTools.ts +132 -13
  138. package/src/tools/ptcTimeout.ts +6 -0
  139. package/src/types/graph.ts +3 -0
  140. package/src/types/tools.ts +14 -3
  141. 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
+ }