@agent-native/agentkit 0.202.0 → 0.202.1-nightly-20261007011022
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/client/client.d.ts.map +1 -1
- package/dist/client/client.js +1126 -2
- package/dist/client/client.js.map +1 -1
- package/dist/client/state.d.ts.map +1 -1
- package/dist/client/state.js +21 -2
- package/dist/client/state.js.map +1 -1
- package/dist/protocol/index.d.ts +2 -0
- package/dist/protocol/index.d.ts.map +1 -1
- package/dist/protocol/index.js +2 -0
- package/dist/protocol/index.js.map +1 -1
- package/package.json +5 -1
package/dist/client/client.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AgentProtocolValidationError, AgentKitProtocolError, createRequestAbortedError, createAgentKitProtocolVersionOffer, parseAgentEvent, projectAgentCapabilities, resumeEntryFromApproval, } from "../protocol/index.js";
|
|
1
|
+
import { AGENT_TOOL_CALL_HISTORY_MEDIA_TYPE, AGENT_TOOL_RESULT_HISTORY_MEDIA_TYPE, AgentProtocolValidationError, AgentKitProtocolError, createRequestAbortedError, createAgentKitProtocolVersionOffer, parseAgentEvent, projectAgentCapabilities, resumeEntryFromApproval, } from "../protocol/index.js";
|
|
2
2
|
import { classifyAgentEvent, createAgentThreadState, hasActiveAgentRuns, selectLatestAgentRun, reduceAgentEvent, settleRunProjection, } from "./state.js";
|
|
3
3
|
/**
|
|
4
4
|
* Creates the deterministic AgentKit controller used by every UI binding.
|
|
@@ -229,6 +229,1129 @@ async function defaultUploadDriver(target, file, context) {
|
|
|
229
229
|
throw new Error(`Upload failed with ${response.status}.`);
|
|
230
230
|
}
|
|
231
231
|
}
|
|
232
|
+
const MAX_TOOL_HISTORY_VALUE_BYTES = 64 * 1024;
|
|
233
|
+
const MAX_TOOL_HISTORY_VALUE_DEPTH = 256;
|
|
234
|
+
const MAX_TOOL_HISTORY_VALUE_WORK = 64 * 1024;
|
|
235
|
+
const MAX_TOOL_HISTORY_SIGNATURE_KEYS = 4_096;
|
|
236
|
+
const MAX_ADDED_TOOL_HISTORY_BYTES = 256 * 1024;
|
|
237
|
+
const MAX_TOOL_HISTORY_CALLS = 64;
|
|
238
|
+
const MAX_TOOL_HISTORY_SOURCE_EVENT_WORK = 4_096;
|
|
239
|
+
const MAX_TOOL_HISTORY_SOURCE_EVENTS = MAX_TOOL_HISTORY_SOURCE_EVENT_WORK / 2;
|
|
240
|
+
const MAX_TOOL_HISTORY_SOURCE_TOOLS = 4_096;
|
|
241
|
+
const TOOL_HISTORY_OMISSION_TEXT = "Some tool-call history was omitted to keep the added history under 256 KiB and 64 calls.";
|
|
242
|
+
const TOOL_HISTORY_ORDER_OMISSION_TEXT = "Tool-call history was omitted because its position could not be reconstructed safely.";
|
|
243
|
+
const TOOL_HISTORY_ASSISTANT_GROUP_OVERHEAD = '{"role":"assistant","content":[]}';
|
|
244
|
+
const TOOL_HISTORY_USER_GROUP_OVERHEAD = '{"role":"user","content":[]}';
|
|
245
|
+
const TOOL_HISTORY_OMISSION_PART = {
|
|
246
|
+
type: "text",
|
|
247
|
+
text: TOOL_HISTORY_OMISSION_TEXT,
|
|
248
|
+
};
|
|
249
|
+
const TOOL_HISTORY_OMISSION_PART_BYTES = JSON.stringify(TOOL_HISTORY_OMISSION_PART).length;
|
|
250
|
+
function projectStructuredToolHistoryParts(parts) {
|
|
251
|
+
const content = [];
|
|
252
|
+
const results = [];
|
|
253
|
+
for (const part of parts) {
|
|
254
|
+
const data = part.data;
|
|
255
|
+
if (part.mediaType === AGENT_TOOL_CALL_HISTORY_MEDIA_TYPE) {
|
|
256
|
+
if (typeof data.inputText === "string" && data.inputText.trim()) {
|
|
257
|
+
content.push({ type: "text", text: data.inputText });
|
|
258
|
+
}
|
|
259
|
+
content.push({
|
|
260
|
+
type: "tool-call",
|
|
261
|
+
id: data.id,
|
|
262
|
+
name: data.name,
|
|
263
|
+
...(data.input === undefined ? {} : { input: data.input }),
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
else if (part.mediaType === AGENT_TOOL_RESULT_HISTORY_MEDIA_TYPE) {
|
|
267
|
+
const result = data.result === undefined
|
|
268
|
+
? (data.resultText ??
|
|
269
|
+
"No tool result was recorded.")
|
|
270
|
+
: `${typeof data.result === "string" ? data.result : (JSON.stringify(data.result) ?? "Tool result could not be serialized for history.")}${data.resultText ? `\n${data.resultText}` : ""}`;
|
|
271
|
+
results.push({
|
|
272
|
+
type: "tool-result",
|
|
273
|
+
toolCallId: data.id,
|
|
274
|
+
...(typeof data.name === "string" ? { toolName: data.name } : {}),
|
|
275
|
+
content: result,
|
|
276
|
+
...(data.isError === true ? { isError: true } : {}),
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
return { content, results };
|
|
281
|
+
}
|
|
282
|
+
function projectedToolHistoryPartSizes(parts) {
|
|
283
|
+
try {
|
|
284
|
+
const size = (part) => {
|
|
285
|
+
const serialized = JSON.stringify(part);
|
|
286
|
+
return serialized === undefined
|
|
287
|
+
? undefined
|
|
288
|
+
: utf8ByteLength(serialized, MAX_ADDED_TOOL_HISTORY_BYTES);
|
|
289
|
+
};
|
|
290
|
+
const content = parts.content.map(size);
|
|
291
|
+
const results = parts.results.map(size);
|
|
292
|
+
if (content.some((bytes) => bytes === undefined) ||
|
|
293
|
+
results.some((bytes) => bytes === undefined)) {
|
|
294
|
+
return undefined;
|
|
295
|
+
}
|
|
296
|
+
return {
|
|
297
|
+
content: content,
|
|
298
|
+
results: results,
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
catch {
|
|
302
|
+
// coercion-ok: Unserializable history parts cannot be safely budgeted.
|
|
303
|
+
return undefined;
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
function createProjectedToolHistorySizer(assistantMessageCopies) {
|
|
307
|
+
const byMessageId = new Map();
|
|
308
|
+
const updateGroup = (group, partSizes, direction) => {
|
|
309
|
+
if (partSizes.length === 0)
|
|
310
|
+
return group;
|
|
311
|
+
const next = group ?? { partBytes: 0, partCount: 0 };
|
|
312
|
+
for (const partBytes of partSizes) {
|
|
313
|
+
next.partBytes += direction * partBytes;
|
|
314
|
+
next.partCount += direction;
|
|
315
|
+
}
|
|
316
|
+
return next.partCount === 0 ? undefined : next;
|
|
317
|
+
};
|
|
318
|
+
return {
|
|
319
|
+
update(messageId, sizes, direction) {
|
|
320
|
+
const messageSize = byMessageId.get(messageId) ?? {};
|
|
321
|
+
messageSize.assistant = updateGroup(messageSize.assistant, sizes.content, direction);
|
|
322
|
+
messageSize.user = updateGroup(messageSize.user, sizes.results, direction);
|
|
323
|
+
if (messageSize.assistant || messageSize.user) {
|
|
324
|
+
byMessageId.set(messageId, messageSize);
|
|
325
|
+
}
|
|
326
|
+
else {
|
|
327
|
+
byMessageId.delete(messageId);
|
|
328
|
+
}
|
|
329
|
+
},
|
|
330
|
+
byteLength(omissionMessageId) {
|
|
331
|
+
let groupBytes = 0;
|
|
332
|
+
let groupCount = 0;
|
|
333
|
+
let omittedMessageCopies = 0;
|
|
334
|
+
if (omissionMessageId) {
|
|
335
|
+
omittedMessageCopies =
|
|
336
|
+
assistantMessageCopies.get(omissionMessageId) ?? 0;
|
|
337
|
+
}
|
|
338
|
+
for (const [messageId, messageSize] of byMessageId) {
|
|
339
|
+
const copies = assistantMessageCopies.get(messageId) ?? 0;
|
|
340
|
+
if (copies === 0)
|
|
341
|
+
continue;
|
|
342
|
+
for (const [group, overhead] of [
|
|
343
|
+
[messageSize.assistant, TOOL_HISTORY_ASSISTANT_GROUP_OVERHEAD],
|
|
344
|
+
[messageSize.user, TOOL_HISTORY_USER_GROUP_OVERHEAD],
|
|
345
|
+
]) {
|
|
346
|
+
if (!group)
|
|
347
|
+
continue;
|
|
348
|
+
let partBytes = group.partBytes;
|
|
349
|
+
let partCount = group.partCount;
|
|
350
|
+
if (messageId === omissionMessageId &&
|
|
351
|
+
group === messageSize.assistant) {
|
|
352
|
+
partBytes += TOOL_HISTORY_OMISSION_PART_BYTES;
|
|
353
|
+
partCount += 1;
|
|
354
|
+
}
|
|
355
|
+
const overheadBytes = overhead.length;
|
|
356
|
+
const size = overheadBytes + partBytes + Math.max(0, partCount - 1);
|
|
357
|
+
groupBytes += size * copies;
|
|
358
|
+
groupCount += copies;
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
if (omittedMessageCopies > 0 &&
|
|
362
|
+
!byMessageId.get(omissionMessageId)?.assistant) {
|
|
363
|
+
const overheadBytes = TOOL_HISTORY_ASSISTANT_GROUP_OVERHEAD.length;
|
|
364
|
+
groupBytes +=
|
|
365
|
+
(overheadBytes + TOOL_HISTORY_OMISSION_PART_BYTES) *
|
|
366
|
+
omittedMessageCopies;
|
|
367
|
+
groupCount += omittedMessageCopies;
|
|
368
|
+
}
|
|
369
|
+
const bytes = 2 + groupBytes + Math.max(0, groupCount - 1);
|
|
370
|
+
return Math.min(bytes, MAX_ADDED_TOOL_HISTORY_BYTES + 1);
|
|
371
|
+
},
|
|
372
|
+
};
|
|
373
|
+
}
|
|
374
|
+
function jsonStringByteLength(value, limit) {
|
|
375
|
+
if (value.length > limit)
|
|
376
|
+
return undefined;
|
|
377
|
+
let bytes = 2;
|
|
378
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
379
|
+
const code = value.charCodeAt(index);
|
|
380
|
+
if (code === 0x22 || code === 0x5c) {
|
|
381
|
+
bytes += 2;
|
|
382
|
+
}
|
|
383
|
+
else if (code === 0x08 ||
|
|
384
|
+
code === 0x09 ||
|
|
385
|
+
code === 0x0a ||
|
|
386
|
+
code === 0x0c ||
|
|
387
|
+
code === 0x0d) {
|
|
388
|
+
bytes += 2;
|
|
389
|
+
}
|
|
390
|
+
else if (code < 0x20) {
|
|
391
|
+
bytes += 6;
|
|
392
|
+
}
|
|
393
|
+
else if (code >= 0xd800 && code <= 0xdbff) {
|
|
394
|
+
const next = value.charCodeAt(index + 1);
|
|
395
|
+
if (next >= 0xdc00 && next <= 0xdfff) {
|
|
396
|
+
bytes += 4;
|
|
397
|
+
index += 1;
|
|
398
|
+
}
|
|
399
|
+
else {
|
|
400
|
+
bytes += 6;
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
else if (code >= 0xdc00 && code <= 0xdfff) {
|
|
404
|
+
bytes += 6;
|
|
405
|
+
}
|
|
406
|
+
else if (code <= 0x7f) {
|
|
407
|
+
bytes += 1;
|
|
408
|
+
}
|
|
409
|
+
else if (code <= 0x7ff) {
|
|
410
|
+
bytes += 2;
|
|
411
|
+
}
|
|
412
|
+
else {
|
|
413
|
+
bytes += 3;
|
|
414
|
+
}
|
|
415
|
+
if (bytes > limit)
|
|
416
|
+
return undefined;
|
|
417
|
+
}
|
|
418
|
+
return bytes;
|
|
419
|
+
}
|
|
420
|
+
function utf8ByteLength(value, limit) {
|
|
421
|
+
if (value.length > limit)
|
|
422
|
+
return undefined;
|
|
423
|
+
let bytes = 0;
|
|
424
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
425
|
+
const code = value.charCodeAt(index);
|
|
426
|
+
if (code >= 0xd800 && code <= 0xdbff) {
|
|
427
|
+
const next = value.charCodeAt(index + 1);
|
|
428
|
+
if (next >= 0xdc00 && next <= 0xdfff) {
|
|
429
|
+
bytes += 4;
|
|
430
|
+
index += 1;
|
|
431
|
+
}
|
|
432
|
+
else {
|
|
433
|
+
bytes += 3;
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
else if (code <= 0x7f) {
|
|
437
|
+
bytes += 1;
|
|
438
|
+
}
|
|
439
|
+
else if (code <= 0x7ff) {
|
|
440
|
+
bytes += 2;
|
|
441
|
+
}
|
|
442
|
+
else {
|
|
443
|
+
bytes += 3;
|
|
444
|
+
}
|
|
445
|
+
if (bytes > limit)
|
|
446
|
+
return undefined;
|
|
447
|
+
}
|
|
448
|
+
return bytes;
|
|
449
|
+
}
|
|
450
|
+
function hasUnsupportedToJSON(value, inheritedPrototype) {
|
|
451
|
+
const ownDescriptor = Object.getOwnPropertyDescriptor(value, "toJSON");
|
|
452
|
+
if (ownDescriptor &&
|
|
453
|
+
(!("value" in ownDescriptor) || typeof ownDescriptor.value === "function")) {
|
|
454
|
+
return true;
|
|
455
|
+
}
|
|
456
|
+
if (!inheritedPrototype)
|
|
457
|
+
return false;
|
|
458
|
+
const inheritedDescriptor = Object.getOwnPropertyDescriptor(inheritedPrototype, "toJSON");
|
|
459
|
+
return Boolean(inheritedDescriptor &&
|
|
460
|
+
(!("value" in inheritedDescriptor) ||
|
|
461
|
+
typeof inheritedDescriptor.value === "function"));
|
|
462
|
+
}
|
|
463
|
+
function projectBoundedJsonValue(value, position, ancestors, work, limit, depth) {
|
|
464
|
+
if (value === null)
|
|
465
|
+
return { kind: "value", value, bytes: 4 };
|
|
466
|
+
if (typeof value === "string") {
|
|
467
|
+
const bytes = jsonStringByteLength(value, limit);
|
|
468
|
+
return bytes === undefined
|
|
469
|
+
? { kind: "too-large" }
|
|
470
|
+
: { kind: "value", value, bytes };
|
|
471
|
+
}
|
|
472
|
+
if (typeof value === "boolean") {
|
|
473
|
+
const bytes = value ? 4 : 5;
|
|
474
|
+
return bytes <= limit
|
|
475
|
+
? { kind: "value", value, bytes }
|
|
476
|
+
: { kind: "too-large" };
|
|
477
|
+
}
|
|
478
|
+
if (typeof value === "number") {
|
|
479
|
+
if (!Number.isFinite(value)) {
|
|
480
|
+
return 4 <= limit
|
|
481
|
+
? { kind: "value", value: null, bytes: 4 }
|
|
482
|
+
: { kind: "too-large" };
|
|
483
|
+
}
|
|
484
|
+
const bytes = JSON.stringify(value).length;
|
|
485
|
+
return bytes <= limit
|
|
486
|
+
? { kind: "value", value, bytes }
|
|
487
|
+
: { kind: "too-large" };
|
|
488
|
+
}
|
|
489
|
+
if (typeof value === "undefined" ||
|
|
490
|
+
typeof value === "function" ||
|
|
491
|
+
typeof value === "symbol") {
|
|
492
|
+
if (position === "object")
|
|
493
|
+
return { kind: "omitted" };
|
|
494
|
+
if (position === "array" && limit >= 4) {
|
|
495
|
+
return { kind: "value", value: null, bytes: 4 };
|
|
496
|
+
}
|
|
497
|
+
return position === "array"
|
|
498
|
+
? { kind: "too-large" }
|
|
499
|
+
: { kind: "unsupported" };
|
|
500
|
+
}
|
|
501
|
+
if (typeof value !== "object" || depth >= MAX_TOOL_HISTORY_VALUE_DEPTH) {
|
|
502
|
+
return { kind: "unsupported" };
|
|
503
|
+
}
|
|
504
|
+
if (ancestors.has(value))
|
|
505
|
+
return { kind: "unsupported" };
|
|
506
|
+
try {
|
|
507
|
+
const isArray = Array.isArray(value);
|
|
508
|
+
const prototype = Object.getPrototypeOf(value);
|
|
509
|
+
if (isArray
|
|
510
|
+
? prototype !== Array.prototype && prototype !== null
|
|
511
|
+
: prototype !== Object.prototype && prototype !== null) {
|
|
512
|
+
return { kind: "unsupported" };
|
|
513
|
+
}
|
|
514
|
+
const inheritedPrototype = prototype === null ? null : prototype;
|
|
515
|
+
if (hasUnsupportedToJSON(value, inheritedPrototype)) {
|
|
516
|
+
return { kind: "unsupported" };
|
|
517
|
+
}
|
|
518
|
+
if (limit < 2)
|
|
519
|
+
return { kind: "too-large" };
|
|
520
|
+
ancestors.add(value);
|
|
521
|
+
try {
|
|
522
|
+
if (isArray) {
|
|
523
|
+
const array = value;
|
|
524
|
+
const length = array.length;
|
|
525
|
+
if (length === 0) {
|
|
526
|
+
return limit >= 2
|
|
527
|
+
? {
|
|
528
|
+
kind: "value",
|
|
529
|
+
value: Object.setPrototypeOf([], null),
|
|
530
|
+
bytes: 2,
|
|
531
|
+
}
|
|
532
|
+
: { kind: "too-large" };
|
|
533
|
+
}
|
|
534
|
+
if (length * 2 + 1 > limit)
|
|
535
|
+
return { kind: "too-large" };
|
|
536
|
+
if (length > work.remaining)
|
|
537
|
+
return { kind: "unsupported" };
|
|
538
|
+
const projected = Object.setPrototypeOf(new Array(length), null);
|
|
539
|
+
let bytes = 2;
|
|
540
|
+
for (let index = 0; index < length; index += 1) {
|
|
541
|
+
work.remaining -= 1;
|
|
542
|
+
const separatorBytes = index === 0 ? 0 : 1;
|
|
543
|
+
const available = limit - bytes - separatorBytes;
|
|
544
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, String(index));
|
|
545
|
+
if (!descriptor) {
|
|
546
|
+
if (prototype === Array.prototype &&
|
|
547
|
+
Object.getOwnPropertyDescriptor(Array.prototype, String(index))) {
|
|
548
|
+
return { kind: "unsupported" };
|
|
549
|
+
}
|
|
550
|
+
if (available < 4)
|
|
551
|
+
return { kind: "too-large" };
|
|
552
|
+
projected[index] = null;
|
|
553
|
+
bytes += separatorBytes + 4;
|
|
554
|
+
continue;
|
|
555
|
+
}
|
|
556
|
+
if (!("value" in descriptor))
|
|
557
|
+
return { kind: "unsupported" };
|
|
558
|
+
const item = projectBoundedJsonValue(descriptor.value, "array", ancestors, work, available, depth + 1);
|
|
559
|
+
if (item.kind !== "value")
|
|
560
|
+
return item;
|
|
561
|
+
projected[index] = item.value;
|
|
562
|
+
bytes += separatorBytes + item.bytes;
|
|
563
|
+
}
|
|
564
|
+
return { kind: "value", value: projected, bytes };
|
|
565
|
+
}
|
|
566
|
+
const projected = Object.create(null);
|
|
567
|
+
let bytes = 2;
|
|
568
|
+
let hasEntry = false;
|
|
569
|
+
for (const key in value) {
|
|
570
|
+
if (work.remaining <= 0) {
|
|
571
|
+
return { kind: "unsupported" };
|
|
572
|
+
}
|
|
573
|
+
work.remaining -= 1;
|
|
574
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, key);
|
|
575
|
+
if (!descriptor || !descriptor.enumerable)
|
|
576
|
+
continue;
|
|
577
|
+
if (!("value" in descriptor))
|
|
578
|
+
return { kind: "unsupported" };
|
|
579
|
+
const rawItem = descriptor.value;
|
|
580
|
+
if (rawItem === undefined ||
|
|
581
|
+
typeof rawItem === "function" ||
|
|
582
|
+
typeof rawItem === "symbol") {
|
|
583
|
+
continue;
|
|
584
|
+
}
|
|
585
|
+
const keyBytes = jsonStringByteLength(key, limit);
|
|
586
|
+
if (keyBytes === undefined)
|
|
587
|
+
return { kind: "too-large" };
|
|
588
|
+
const separatorBytes = hasEntry ? 1 : 0;
|
|
589
|
+
const available = limit - bytes - separatorBytes - keyBytes - 1;
|
|
590
|
+
if (available < 0)
|
|
591
|
+
return { kind: "too-large" };
|
|
592
|
+
const item = projectBoundedJsonValue(rawItem, "object", ancestors, work, available, depth + 1);
|
|
593
|
+
if (item.kind === "too-large" || item.kind === "unsupported") {
|
|
594
|
+
return item;
|
|
595
|
+
}
|
|
596
|
+
if (item.kind === "omitted")
|
|
597
|
+
continue;
|
|
598
|
+
projected[key] = item.value;
|
|
599
|
+
bytes += separatorBytes + keyBytes + 1 + item.bytes;
|
|
600
|
+
hasEntry = true;
|
|
601
|
+
}
|
|
602
|
+
return { kind: "value", value: projected, bytes };
|
|
603
|
+
}
|
|
604
|
+
finally {
|
|
605
|
+
ancestors.delete(value);
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
catch {
|
|
609
|
+
return { kind: "unsupported" };
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
function projectToolHistoryValue(value) {
|
|
613
|
+
const projection = projectBoundedJsonValue(value, "root", new Set(), { remaining: MAX_TOOL_HISTORY_VALUE_WORK }, MAX_TOOL_HISTORY_VALUE_BYTES, 0);
|
|
614
|
+
if (projection.kind !== "value") {
|
|
615
|
+
return {
|
|
616
|
+
ok: false,
|
|
617
|
+
omission: projection.kind === "too-large"
|
|
618
|
+
? "it exceeds 64 KiB"
|
|
619
|
+
: "it could not be serialized",
|
|
620
|
+
};
|
|
621
|
+
}
|
|
622
|
+
try {
|
|
623
|
+
const serialized = JSON.stringify(projection.value);
|
|
624
|
+
if (serialized === undefined ||
|
|
625
|
+
new TextEncoder().encode(serialized).byteLength >
|
|
626
|
+
MAX_TOOL_HISTORY_VALUE_BYTES) {
|
|
627
|
+
return { ok: false, omission: "it exceeds 64 KiB" };
|
|
628
|
+
}
|
|
629
|
+
return { ok: true, value: JSON.parse(serialized) };
|
|
630
|
+
}
|
|
631
|
+
catch {
|
|
632
|
+
return { ok: false, omission: "it could not be serialized" };
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
function toolHistoryTextOmission(value) {
|
|
636
|
+
if (value.length > MAX_TOOL_HISTORY_VALUE_BYTES) {
|
|
637
|
+
return "it exceeds 64 KiB";
|
|
638
|
+
}
|
|
639
|
+
return new TextEncoder().encode(value).byteLength >
|
|
640
|
+
MAX_TOOL_HISTORY_VALUE_BYTES
|
|
641
|
+
? "it exceeds 64 KiB"
|
|
642
|
+
: undefined;
|
|
643
|
+
}
|
|
644
|
+
function orderedThreadToolCalls(thread) {
|
|
645
|
+
const eventCount = thread.events.length;
|
|
646
|
+
const indexedEvents = [];
|
|
647
|
+
const startedIds = new Set();
|
|
648
|
+
const settledIds = new Set();
|
|
649
|
+
const updatedIds = new Set();
|
|
650
|
+
const unstartedUpdatesNewestFirst = [];
|
|
651
|
+
const selectedIdsNewestFirst = [];
|
|
652
|
+
const selectedIds = new Set();
|
|
653
|
+
let omitted = false;
|
|
654
|
+
let sourceTruncated = false;
|
|
655
|
+
let omissionMessageId;
|
|
656
|
+
for (let index = eventCount - 1; index >= 0 && indexedEvents.length < MAX_TOOL_HISTORY_SOURCE_EVENTS; index -= 1) {
|
|
657
|
+
const event = thread.events[index];
|
|
658
|
+
const eventType = event.type;
|
|
659
|
+
indexedEvents.push({ event, index });
|
|
660
|
+
if (eventType === "tool.updated" && event.toolCall.status !== "running") {
|
|
661
|
+
const id = event.toolCall.id;
|
|
662
|
+
if (id.length <= MAX_TOOL_HISTORY_VALUE_BYTES) {
|
|
663
|
+
settledIds.add(id);
|
|
664
|
+
if (!updatedIds.has(id)) {
|
|
665
|
+
updatedIds.add(id);
|
|
666
|
+
unstartedUpdatesNewestFirst.push(id);
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
else {
|
|
670
|
+
omitted = true;
|
|
671
|
+
omissionMessageId = event.toolCall.messageId ?? omissionMessageId;
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
else if (eventType === "tool.started") {
|
|
675
|
+
const id = event.toolCall.id;
|
|
676
|
+
if (id.length > MAX_TOOL_HISTORY_VALUE_BYTES) {
|
|
677
|
+
omitted = true;
|
|
678
|
+
omissionMessageId = event.toolCall.messageId ?? omissionMessageId;
|
|
679
|
+
continue;
|
|
680
|
+
}
|
|
681
|
+
startedIds.add(id);
|
|
682
|
+
if (!settledIds.has(id) || selectedIds.has(id))
|
|
683
|
+
continue;
|
|
684
|
+
const toolCall = thread.tools[id];
|
|
685
|
+
if (!toolCall || toolCall.status === "running")
|
|
686
|
+
continue;
|
|
687
|
+
if (selectedIdsNewestFirst.length < MAX_TOOL_HISTORY_CALLS) {
|
|
688
|
+
selectedIds.add(id);
|
|
689
|
+
selectedIdsNewestFirst.push(id);
|
|
690
|
+
}
|
|
691
|
+
else {
|
|
692
|
+
omitted = true;
|
|
693
|
+
omissionMessageId = thread.tools[id]?.messageId ?? omissionMessageId;
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
if (indexedEvents.length < eventCount) {
|
|
698
|
+
omitted = true;
|
|
699
|
+
sourceTruncated = true;
|
|
700
|
+
}
|
|
701
|
+
let selectedIdsChronological = selectedIdsNewestFirst.reverse();
|
|
702
|
+
if (indexedEvents.length === eventCount) {
|
|
703
|
+
const unstartedUpdateIdsNewestFirst = [];
|
|
704
|
+
for (const id of unstartedUpdatesNewestFirst) {
|
|
705
|
+
if (startedIds.has(id) || selectedIds.has(id))
|
|
706
|
+
continue;
|
|
707
|
+
const toolCall = thread.tools[id];
|
|
708
|
+
if (!toolCall || toolCall.status === "running")
|
|
709
|
+
continue;
|
|
710
|
+
unstartedUpdateIdsNewestFirst.push(id);
|
|
711
|
+
}
|
|
712
|
+
const remainingUpdates = MAX_TOOL_HISTORY_CALLS - selectedIdsChronological.length;
|
|
713
|
+
if (unstartedUpdateIdsNewestFirst.length > remainingUpdates) {
|
|
714
|
+
omitted = true;
|
|
715
|
+
const firstSkippedId = unstartedUpdateIdsNewestFirst[remainingUpdates];
|
|
716
|
+
omissionMessageId =
|
|
717
|
+
thread.tools[firstSkippedId]?.messageId ?? omissionMessageId;
|
|
718
|
+
}
|
|
719
|
+
const selectedUnstartedUpdateIds = unstartedUpdateIdsNewestFirst
|
|
720
|
+
.slice(0, remainingUpdates)
|
|
721
|
+
.reverse();
|
|
722
|
+
selectedIdsChronological.push(...selectedUnstartedUpdateIds);
|
|
723
|
+
for (const id of selectedUnstartedUpdateIds)
|
|
724
|
+
selectedIds.add(id);
|
|
725
|
+
if (selectedIdsChronological.length < MAX_TOOL_HISTORY_CALLS) {
|
|
726
|
+
const fallbackIds = [];
|
|
727
|
+
let inspectedTools = 0;
|
|
728
|
+
let fallbackScanComplete = true;
|
|
729
|
+
for (const id in thread.tools) {
|
|
730
|
+
if (inspectedTools >= MAX_TOOL_HISTORY_SOURCE_TOOLS) {
|
|
731
|
+
fallbackScanComplete = false;
|
|
732
|
+
break;
|
|
733
|
+
}
|
|
734
|
+
inspectedTools += 1;
|
|
735
|
+
if (!Object.prototype.hasOwnProperty.call(thread.tools, id))
|
|
736
|
+
continue;
|
|
737
|
+
if (startedIds.has(id) || updatedIds.has(id) || selectedIds.has(id)) {
|
|
738
|
+
continue;
|
|
739
|
+
}
|
|
740
|
+
const toolCall = thread.tools[id];
|
|
741
|
+
if (!toolCall || toolCall.status === "running")
|
|
742
|
+
continue;
|
|
743
|
+
fallbackIds.push(id);
|
|
744
|
+
}
|
|
745
|
+
if (!fallbackScanComplete) {
|
|
746
|
+
omitted = true;
|
|
747
|
+
sourceTruncated = true;
|
|
748
|
+
}
|
|
749
|
+
else {
|
|
750
|
+
const remaining = MAX_TOOL_HISTORY_CALLS - selectedIdsChronological.length;
|
|
751
|
+
if (fallbackIds.length > remaining) {
|
|
752
|
+
omitted = true;
|
|
753
|
+
omissionMessageId =
|
|
754
|
+
thread.tools[fallbackIds[0]]?.messageId ?? omissionMessageId;
|
|
755
|
+
}
|
|
756
|
+
selectedIdsChronological.push(...fallbackIds.slice(-remaining));
|
|
757
|
+
}
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
if (selectedIdsChronological.length > MAX_TOOL_HISTORY_CALLS) {
|
|
761
|
+
omitted = true;
|
|
762
|
+
selectedIdsChronological = selectedIdsChronological.slice(-MAX_TOOL_HISTORY_CALLS);
|
|
763
|
+
}
|
|
764
|
+
const calls = selectedIdsChronological.flatMap((id) => {
|
|
765
|
+
const toolCall = thread.tools[id];
|
|
766
|
+
return toolCall ? [toolCall] : [];
|
|
767
|
+
});
|
|
768
|
+
omissionMessageId ??= omitted
|
|
769
|
+
? calls.find((toolCall) => toolCall.messageId)?.messageId
|
|
770
|
+
: undefined;
|
|
771
|
+
return {
|
|
772
|
+
calls,
|
|
773
|
+
events: indexedEvents,
|
|
774
|
+
omitted,
|
|
775
|
+
sourceTruncated,
|
|
776
|
+
omissionMessageId,
|
|
777
|
+
};
|
|
778
|
+
}
|
|
779
|
+
function representedToolHistoryParts(message) {
|
|
780
|
+
const represented = {
|
|
781
|
+
callIds: new Set(),
|
|
782
|
+
resultIds: new Set(),
|
|
783
|
+
};
|
|
784
|
+
for (const part of message.parts) {
|
|
785
|
+
if (part.type !== "data")
|
|
786
|
+
continue;
|
|
787
|
+
const ids = part.mediaType === AGENT_TOOL_CALL_HISTORY_MEDIA_TYPE
|
|
788
|
+
? represented.callIds
|
|
789
|
+
: part.mediaType === AGENT_TOOL_RESULT_HISTORY_MEDIA_TYPE
|
|
790
|
+
? represented.resultIds
|
|
791
|
+
: undefined;
|
|
792
|
+
if (!ids ||
|
|
793
|
+
typeof part.data !== "object" ||
|
|
794
|
+
part.data === null ||
|
|
795
|
+
Array.isArray(part.data))
|
|
796
|
+
continue;
|
|
797
|
+
const id = part.data.id;
|
|
798
|
+
if (typeof id === "string")
|
|
799
|
+
ids.add(id);
|
|
800
|
+
}
|
|
801
|
+
return represented;
|
|
802
|
+
}
|
|
803
|
+
function safeJsonSignature(value) {
|
|
804
|
+
const ancestors = new Set();
|
|
805
|
+
let work = 0;
|
|
806
|
+
const visit = (current, depth, byteLimit) => {
|
|
807
|
+
work += 1;
|
|
808
|
+
if (work > MAX_TOOL_HISTORY_VALUE_WORK)
|
|
809
|
+
return undefined;
|
|
810
|
+
if (current === null) {
|
|
811
|
+
return byteLimit >= 4 ? { signature: "null", bytes: 4 } : undefined;
|
|
812
|
+
}
|
|
813
|
+
if (typeof current === "string") {
|
|
814
|
+
const bytes = jsonStringByteLength(current, byteLimit);
|
|
815
|
+
if (bytes === undefined)
|
|
816
|
+
return undefined;
|
|
817
|
+
const signature = JSON.stringify(current);
|
|
818
|
+
return signature === undefined ? undefined : { signature, bytes };
|
|
819
|
+
}
|
|
820
|
+
if (typeof current === "boolean") {
|
|
821
|
+
const signature = current ? "true" : "false";
|
|
822
|
+
const bytes = current ? 4 : 5;
|
|
823
|
+
return bytes <= byteLimit ? { signature, bytes } : undefined;
|
|
824
|
+
}
|
|
825
|
+
if (typeof current === "number") {
|
|
826
|
+
if (!Number.isFinite(current))
|
|
827
|
+
return undefined;
|
|
828
|
+
const signature = JSON.stringify(current);
|
|
829
|
+
const bytes = signature
|
|
830
|
+
? jsonStringByteLength(signature, byteLimit)
|
|
831
|
+
: undefined;
|
|
832
|
+
return bytes === undefined ? undefined : { signature, bytes };
|
|
833
|
+
}
|
|
834
|
+
if (typeof current !== "object" ||
|
|
835
|
+
depth >= MAX_TOOL_HISTORY_VALUE_DEPTH ||
|
|
836
|
+
ancestors.has(current)) {
|
|
837
|
+
return undefined;
|
|
838
|
+
}
|
|
839
|
+
try {
|
|
840
|
+
const isArray = Array.isArray(current);
|
|
841
|
+
const prototype = Object.getPrototypeOf(current);
|
|
842
|
+
if (isArray
|
|
843
|
+
? prototype !== Array.prototype
|
|
844
|
+
: prototype !== Object.prototype && prototype !== null) {
|
|
845
|
+
return undefined;
|
|
846
|
+
}
|
|
847
|
+
if (hasUnsupportedToJSON(current, prototype))
|
|
848
|
+
return undefined;
|
|
849
|
+
if (byteLimit < 2)
|
|
850
|
+
return undefined;
|
|
851
|
+
ancestors.add(current);
|
|
852
|
+
try {
|
|
853
|
+
if (isArray) {
|
|
854
|
+
const length = Object.getOwnPropertyDescriptor(current, "length");
|
|
855
|
+
if (!length ||
|
|
856
|
+
!("value" in length) ||
|
|
857
|
+
typeof length.value !== "number" ||
|
|
858
|
+
length.value < 0 ||
|
|
859
|
+
!Number.isSafeInteger(length.value) ||
|
|
860
|
+
length.value * 2 > MAX_TOOL_HISTORY_VALUE_WORK - work ||
|
|
861
|
+
(length.value > 0 && length.value * 2 + 1 > byteLimit)) {
|
|
862
|
+
return undefined;
|
|
863
|
+
}
|
|
864
|
+
const items = [];
|
|
865
|
+
let bytes = 2;
|
|
866
|
+
let index = 0;
|
|
867
|
+
for (const key in current) {
|
|
868
|
+
work += 1;
|
|
869
|
+
if (work > MAX_TOOL_HISTORY_VALUE_WORK ||
|
|
870
|
+
index >= length.value ||
|
|
871
|
+
key !== String(index)) {
|
|
872
|
+
return undefined;
|
|
873
|
+
}
|
|
874
|
+
const descriptor = Object.getOwnPropertyDescriptor(current, key);
|
|
875
|
+
if (!descriptor ||
|
|
876
|
+
!descriptor.enumerable ||
|
|
877
|
+
!("value" in descriptor)) {
|
|
878
|
+
return undefined;
|
|
879
|
+
}
|
|
880
|
+
const separatorBytes = index === 0 ? 0 : 1;
|
|
881
|
+
const item = visit(descriptor.value, depth + 1, byteLimit - bytes - separatorBytes);
|
|
882
|
+
if (!item)
|
|
883
|
+
return undefined;
|
|
884
|
+
items.push(item.signature);
|
|
885
|
+
bytes += separatorBytes + item.bytes;
|
|
886
|
+
index += 1;
|
|
887
|
+
}
|
|
888
|
+
if (index !== length.value)
|
|
889
|
+
return undefined;
|
|
890
|
+
return { signature: `[${items.join(",")}]`, bytes };
|
|
891
|
+
}
|
|
892
|
+
const entries = [];
|
|
893
|
+
for (const key in current) {
|
|
894
|
+
work += 1;
|
|
895
|
+
if (work > MAX_TOOL_HISTORY_VALUE_WORK ||
|
|
896
|
+
entries.length >= MAX_TOOL_HISTORY_SIGNATURE_KEYS ||
|
|
897
|
+
key === "toJSON") {
|
|
898
|
+
return undefined;
|
|
899
|
+
}
|
|
900
|
+
const keyBytes = jsonStringByteLength(key, byteLimit);
|
|
901
|
+
if (keyBytes === undefined)
|
|
902
|
+
return undefined;
|
|
903
|
+
const descriptor = Object.getOwnPropertyDescriptor(current, key);
|
|
904
|
+
if (!descriptor ||
|
|
905
|
+
!descriptor.enumerable ||
|
|
906
|
+
!("value" in descriptor)) {
|
|
907
|
+
return undefined;
|
|
908
|
+
}
|
|
909
|
+
entries.push({ key, keyBytes, value: descriptor.value });
|
|
910
|
+
}
|
|
911
|
+
entries.sort((first, second) => first.key < second.key ? -1 : first.key > second.key ? 1 : 0);
|
|
912
|
+
const serializedEntries = [];
|
|
913
|
+
let bytes = 2;
|
|
914
|
+
for (const [index, entry] of entries.entries()) {
|
|
915
|
+
const separatorBytes = index === 0 ? 0 : 1;
|
|
916
|
+
const available = byteLimit - bytes - separatorBytes - entry.keyBytes - 1;
|
|
917
|
+
if (available < 1)
|
|
918
|
+
return undefined;
|
|
919
|
+
const item = visit(entry.value, depth + 1, available);
|
|
920
|
+
if (!item)
|
|
921
|
+
return undefined;
|
|
922
|
+
serializedEntries.push(`${index === 0 ? "" : ","}${JSON.stringify(entry.key)}:${item.signature}`);
|
|
923
|
+
bytes += separatorBytes + entry.keyBytes + 1 + item.bytes;
|
|
924
|
+
}
|
|
925
|
+
return { signature: `{${serializedEntries.join("")}}`, bytes };
|
|
926
|
+
}
|
|
927
|
+
finally {
|
|
928
|
+
ancestors.delete(current);
|
|
929
|
+
}
|
|
930
|
+
}
|
|
931
|
+
catch {
|
|
932
|
+
// coercion-ok: Uninspectable history payloads fail closed.
|
|
933
|
+
return undefined;
|
|
934
|
+
}
|
|
935
|
+
};
|
|
936
|
+
return visit(value, 0, MAX_TOOL_HISTORY_VALUE_BYTES)?.signature;
|
|
937
|
+
}
|
|
938
|
+
function safeToolHistoryDataSignature(value) {
|
|
939
|
+
if (typeof value !== "object" || value === null)
|
|
940
|
+
return undefined;
|
|
941
|
+
try {
|
|
942
|
+
if (Array.isArray(value))
|
|
943
|
+
return undefined;
|
|
944
|
+
const prototype = Object.getPrototypeOf(value);
|
|
945
|
+
if (prototype !== Object.prototype && prototype !== null)
|
|
946
|
+
return undefined;
|
|
947
|
+
const id = Object.getOwnPropertyDescriptor(value, "id");
|
|
948
|
+
if (!id ||
|
|
949
|
+
!id.enumerable ||
|
|
950
|
+
!("value" in id) ||
|
|
951
|
+
typeof id.value !== "string") {
|
|
952
|
+
return undefined;
|
|
953
|
+
}
|
|
954
|
+
return safeJsonSignature(value);
|
|
955
|
+
}
|
|
956
|
+
catch {
|
|
957
|
+
// coercion-ok: Invalid reserved history data fails closed.
|
|
958
|
+
return undefined;
|
|
959
|
+
}
|
|
960
|
+
}
|
|
961
|
+
function messagePartsOrderSignature(parts) {
|
|
962
|
+
const segments = [];
|
|
963
|
+
for (const part of parts) {
|
|
964
|
+
if (part.type === "text") {
|
|
965
|
+
const previous = segments.at(-1);
|
|
966
|
+
if (previous?.[0] === "text" && previous[2] === part.format) {
|
|
967
|
+
previous[1] += part.text;
|
|
968
|
+
}
|
|
969
|
+
else {
|
|
970
|
+
segments.push(["text", part.text, part.format]);
|
|
971
|
+
}
|
|
972
|
+
}
|
|
973
|
+
else if (part.type === "reasoning") {
|
|
974
|
+
const previous = segments.at(-1);
|
|
975
|
+
if (previous?.[0] === "reasoning" &&
|
|
976
|
+
previous[2] === part.visibility &&
|
|
977
|
+
previous[3] === part.label) {
|
|
978
|
+
previous[1] += part.text;
|
|
979
|
+
}
|
|
980
|
+
else {
|
|
981
|
+
segments.push(["reasoning", part.text, part.visibility, part.label]);
|
|
982
|
+
}
|
|
983
|
+
}
|
|
984
|
+
else if (part.type === "data" &&
|
|
985
|
+
(part.mediaType === AGENT_TOOL_CALL_HISTORY_MEDIA_TYPE ||
|
|
986
|
+
part.mediaType === AGENT_TOOL_RESULT_HISTORY_MEDIA_TYPE)) {
|
|
987
|
+
const dataSignature = safeToolHistoryDataSignature(part.data);
|
|
988
|
+
if (dataSignature === undefined)
|
|
989
|
+
return undefined;
|
|
990
|
+
segments.push(["data", part.mediaType, dataSignature]);
|
|
991
|
+
}
|
|
992
|
+
else {
|
|
993
|
+
return undefined;
|
|
994
|
+
}
|
|
995
|
+
}
|
|
996
|
+
return JSON.stringify(segments);
|
|
997
|
+
}
|
|
998
|
+
function indexToolHistoryEvents(eventsNewestFirst, toolCallIds, messageIds) {
|
|
999
|
+
const eventOrdersByToolCallId = new Map();
|
|
1000
|
+
const messageBodiesByMessageId = new Map();
|
|
1001
|
+
for (let eventOffset = eventsNewestFirst.length - 1; eventOffset >= 0; eventOffset -= 1) {
|
|
1002
|
+
const { event, index } = eventsNewestFirst[eventOffset];
|
|
1003
|
+
const eventType = event.type;
|
|
1004
|
+
if (eventType === "tool.started" && toolCallIds.has(event.toolCall.id)) {
|
|
1005
|
+
const order = eventOrdersByToolCallId.get(event.toolCall.id) ?? {};
|
|
1006
|
+
order.call ??= index;
|
|
1007
|
+
eventOrdersByToolCallId.set(event.toolCall.id, order);
|
|
1008
|
+
}
|
|
1009
|
+
else if (eventType === "tool.updated" &&
|
|
1010
|
+
toolCallIds.has(event.toolCall.id) &&
|
|
1011
|
+
event.toolCall.status !== "running") {
|
|
1012
|
+
const order = eventOrdersByToolCallId.get(event.toolCall.id) ?? {};
|
|
1013
|
+
order.result ??= index;
|
|
1014
|
+
eventOrdersByToolCallId.set(event.toolCall.id, order);
|
|
1015
|
+
}
|
|
1016
|
+
const messageId = eventType === "message.created" || eventType === "message.completed"
|
|
1017
|
+
? event.message.id
|
|
1018
|
+
: eventType === "message.delta" || eventType === "reasoning.delta"
|
|
1019
|
+
? event.messageId
|
|
1020
|
+
: undefined;
|
|
1021
|
+
if (!messageId || !messageIds.has(messageId))
|
|
1022
|
+
continue;
|
|
1023
|
+
const body = messageBodiesByMessageId.get(messageId) ?? {
|
|
1024
|
+
hasMessageEvent: false,
|
|
1025
|
+
parts: [],
|
|
1026
|
+
};
|
|
1027
|
+
if (eventType === "message.created" &&
|
|
1028
|
+
event.message.role === "assistant" &&
|
|
1029
|
+
!body.hasMessageEvent) {
|
|
1030
|
+
for (const part of event.message.parts) {
|
|
1031
|
+
body.parts.push({ part, order: index });
|
|
1032
|
+
}
|
|
1033
|
+
body.hasMessageEvent = true;
|
|
1034
|
+
}
|
|
1035
|
+
else if (eventType === "message.delta") {
|
|
1036
|
+
body.parts.push({
|
|
1037
|
+
part: {
|
|
1038
|
+
type: "text",
|
|
1039
|
+
text: event.text,
|
|
1040
|
+
...(event.format ? { format: event.format } : {}),
|
|
1041
|
+
},
|
|
1042
|
+
order: index,
|
|
1043
|
+
});
|
|
1044
|
+
body.hasMessageEvent = true;
|
|
1045
|
+
}
|
|
1046
|
+
else if (eventType === "reasoning.delta") {
|
|
1047
|
+
body.parts.push({
|
|
1048
|
+
part: { type: "reasoning", text: event.text, visibility: "summary" },
|
|
1049
|
+
order: index,
|
|
1050
|
+
});
|
|
1051
|
+
body.hasMessageEvent = true;
|
|
1052
|
+
}
|
|
1053
|
+
messageBodiesByMessageId.set(messageId, body);
|
|
1054
|
+
}
|
|
1055
|
+
return { eventOrdersByToolCallId, messageBodiesByMessageId };
|
|
1056
|
+
}
|
|
1057
|
+
function messagePartsWithToolHistory(message, body, historyParts) {
|
|
1058
|
+
const reconstructedText = messagePartsOrderSignature(body?.parts.map(({ part }) => part) ?? []);
|
|
1059
|
+
const finalText = messagePartsOrderSignature(message.parts);
|
|
1060
|
+
const hasOrderedBody = body?.hasMessageEvent &&
|
|
1061
|
+
reconstructedText !== undefined &&
|
|
1062
|
+
reconstructedText === finalText &&
|
|
1063
|
+
historyParts.every(({ part, order }) => part.type !== "data" ||
|
|
1064
|
+
(part.mediaType !== AGENT_TOOL_CALL_HISTORY_MEDIA_TYPE &&
|
|
1065
|
+
part.mediaType !== AGENT_TOOL_RESULT_HISTORY_MEDIA_TYPE) ||
|
|
1066
|
+
order !== undefined);
|
|
1067
|
+
if (!hasOrderedBody) {
|
|
1068
|
+
const hasToolHistoryParts = historyParts.some(({ part }) => part.type === "data" &&
|
|
1069
|
+
(part.mediaType === AGENT_TOOL_CALL_HISTORY_MEDIA_TYPE ||
|
|
1070
|
+
part.mediaType === AGENT_TOOL_RESULT_HISTORY_MEDIA_TYPE));
|
|
1071
|
+
return hasToolHistoryParts
|
|
1072
|
+
? undefined
|
|
1073
|
+
: [...message.parts, ...historyParts.map(({ part }) => part)];
|
|
1074
|
+
}
|
|
1075
|
+
const orderedParts = [
|
|
1076
|
+
...(body?.parts ?? []),
|
|
1077
|
+
...historyParts.map((entry) => ({
|
|
1078
|
+
part: entry.part,
|
|
1079
|
+
order: entry.order ?? Number.POSITIVE_INFINITY,
|
|
1080
|
+
preserveBoundary: entry.preserveBoundary,
|
|
1081
|
+
})),
|
|
1082
|
+
].sort((first, second) => (first.order ?? Number.POSITIVE_INFINITY) -
|
|
1083
|
+
(second.order ?? Number.POSITIVE_INFINITY));
|
|
1084
|
+
const parts = [];
|
|
1085
|
+
let preserveNextBoundary = false;
|
|
1086
|
+
for (const entry of orderedParts) {
|
|
1087
|
+
const previous = parts.at(-1);
|
|
1088
|
+
if (!preserveNextBoundary && !entry.preserveBoundary) {
|
|
1089
|
+
if (previous?.type === "text" &&
|
|
1090
|
+
entry.part.type === "text" &&
|
|
1091
|
+
previous.format === entry.part.format) {
|
|
1092
|
+
parts[parts.length - 1] = {
|
|
1093
|
+
...previous,
|
|
1094
|
+
text: previous.text + entry.part.text,
|
|
1095
|
+
};
|
|
1096
|
+
continue;
|
|
1097
|
+
}
|
|
1098
|
+
if (previous?.type === "reasoning" &&
|
|
1099
|
+
entry.part.type === "reasoning" &&
|
|
1100
|
+
previous.visibility === entry.part.visibility &&
|
|
1101
|
+
previous.label === entry.part.label) {
|
|
1102
|
+
parts[parts.length - 1] = {
|
|
1103
|
+
...previous,
|
|
1104
|
+
text: previous.text + entry.part.text,
|
|
1105
|
+
};
|
|
1106
|
+
continue;
|
|
1107
|
+
}
|
|
1108
|
+
}
|
|
1109
|
+
parts.push(entry.part);
|
|
1110
|
+
preserveNextBoundary = Boolean(entry.preserveBoundary);
|
|
1111
|
+
}
|
|
1112
|
+
return parts;
|
|
1113
|
+
}
|
|
1114
|
+
function messagesWithToolCallHistory(messages, selection) {
|
|
1115
|
+
const { calls: toolCalls, events, omitted: sourceOmitted, sourceTruncated, } = selection;
|
|
1116
|
+
const assistantMessageIds = new Set();
|
|
1117
|
+
const assistantMessageCopies = new Map();
|
|
1118
|
+
const existingToolHistoryPartsByMessageId = new Map();
|
|
1119
|
+
let firstAssistantMessageId;
|
|
1120
|
+
for (const message of messages) {
|
|
1121
|
+
if (message.role !== "assistant")
|
|
1122
|
+
continue;
|
|
1123
|
+
firstAssistantMessageId ??= message.id;
|
|
1124
|
+
assistantMessageIds.add(message.id);
|
|
1125
|
+
assistantMessageCopies.set(message.id, (assistantMessageCopies.get(message.id) ?? 0) + 1);
|
|
1126
|
+
existingToolHistoryPartsByMessageId.set(message.id, representedToolHistoryParts(message));
|
|
1127
|
+
}
|
|
1128
|
+
const eligibleCalls = toolCalls.filter((toolCall) => {
|
|
1129
|
+
const messageId = toolCall.messageId;
|
|
1130
|
+
const represented = messageId
|
|
1131
|
+
? existingToolHistoryPartsByMessageId.get(messageId)
|
|
1132
|
+
: undefined;
|
|
1133
|
+
return (toolCall.status !== "running" &&
|
|
1134
|
+
Boolean(messageId &&
|
|
1135
|
+
assistantMessageIds.has(messageId) &&
|
|
1136
|
+
(!represented?.callIds.has(toolCall.id) ||
|
|
1137
|
+
!represented.resultIds.has(toolCall.id))));
|
|
1138
|
+
});
|
|
1139
|
+
const historyPartsByMessageId = new Map();
|
|
1140
|
+
const selectedCalls = [];
|
|
1141
|
+
const historySizer = createProjectedToolHistorySizer(assistantMessageCopies);
|
|
1142
|
+
const recentCalls = eligibleCalls.slice(-MAX_TOOL_HISTORY_CALLS);
|
|
1143
|
+
let omittedHistory = sourceOmitted || recentCalls.length < eligibleCalls.length;
|
|
1144
|
+
let omissionMessageId = sourceTruncated
|
|
1145
|
+
? firstAssistantMessageId
|
|
1146
|
+
: selection.omissionMessageId;
|
|
1147
|
+
if (omissionMessageId && !assistantMessageIds.has(omissionMessageId)) {
|
|
1148
|
+
omissionMessageId = undefined;
|
|
1149
|
+
}
|
|
1150
|
+
if (!omissionMessageId && omittedHistory) {
|
|
1151
|
+
omissionMessageId = sourceOmitted
|
|
1152
|
+
? firstAssistantMessageId
|
|
1153
|
+
: (eligibleCalls[0]?.messageId ?? firstAssistantMessageId);
|
|
1154
|
+
}
|
|
1155
|
+
for (let index = recentCalls.length - 1; index >= 0; index--) {
|
|
1156
|
+
const toolCall = recentCalls[index];
|
|
1157
|
+
if (toolHistoryTextOmission(toolCall.id) ||
|
|
1158
|
+
toolHistoryTextOmission(toolCall.name)) {
|
|
1159
|
+
omittedHistory = true;
|
|
1160
|
+
omissionMessageId ??= toolCall.messageId;
|
|
1161
|
+
continue;
|
|
1162
|
+
}
|
|
1163
|
+
const represented = existingToolHistoryPartsByMessageId.get(toolCall.messageId);
|
|
1164
|
+
const parts = toolCallHistoryParts(toolCall, {
|
|
1165
|
+
call: !represented?.callIds.has(toolCall.id),
|
|
1166
|
+
result: !represented?.resultIds.has(toolCall.id),
|
|
1167
|
+
});
|
|
1168
|
+
const candidate = {
|
|
1169
|
+
messageId: toolCall.messageId,
|
|
1170
|
+
toolCallId: toolCall.id,
|
|
1171
|
+
parts,
|
|
1172
|
+
sizes: projectedToolHistoryPartSizes(projectStructuredToolHistoryParts(parts)),
|
|
1173
|
+
};
|
|
1174
|
+
const sizes = candidate.sizes;
|
|
1175
|
+
if (sizes) {
|
|
1176
|
+
historySizer.update(candidate.messageId, sizes, 1);
|
|
1177
|
+
}
|
|
1178
|
+
if (!sizes ||
|
|
1179
|
+
historySizer.byteLength(omissionMessageId) > MAX_ADDED_TOOL_HISTORY_BYTES) {
|
|
1180
|
+
if (sizes) {
|
|
1181
|
+
historySizer.update(candidate.messageId, sizes, -1);
|
|
1182
|
+
}
|
|
1183
|
+
omittedHistory = true;
|
|
1184
|
+
omissionMessageId ??= candidate.messageId;
|
|
1185
|
+
while (selectedCalls.length > 0 &&
|
|
1186
|
+
historySizer.byteLength(omissionMessageId) >
|
|
1187
|
+
MAX_ADDED_TOOL_HISTORY_BYTES) {
|
|
1188
|
+
const removed = selectedCalls.pop();
|
|
1189
|
+
historySizer.update(removed.messageId, removed.sizes, -1);
|
|
1190
|
+
}
|
|
1191
|
+
continue;
|
|
1192
|
+
}
|
|
1193
|
+
selectedCalls.push({ ...candidate, sizes });
|
|
1194
|
+
}
|
|
1195
|
+
if (selectedCalls.length < recentCalls.length)
|
|
1196
|
+
omittedHistory = true;
|
|
1197
|
+
const eventIndex = selectedCalls.length > 0
|
|
1198
|
+
? indexToolHistoryEvents(events, new Set(selectedCalls.map(({ toolCallId }) => toolCallId)), new Set([
|
|
1199
|
+
...selectedCalls.map(({ messageId }) => messageId),
|
|
1200
|
+
...(omissionMessageId ? [omissionMessageId] : []),
|
|
1201
|
+
]))
|
|
1202
|
+
: {
|
|
1203
|
+
eventOrdersByToolCallId: new Map(),
|
|
1204
|
+
messageBodiesByMessageId: new Map(),
|
|
1205
|
+
};
|
|
1206
|
+
for (const { messageId, toolCallId, parts } of selectedCalls.reverse()) {
|
|
1207
|
+
const historyParts = historyPartsByMessageId.get(messageId) ?? [];
|
|
1208
|
+
const eventOrder = eventIndex.eventOrdersByToolCallId.get(toolCallId);
|
|
1209
|
+
for (const part of parts) {
|
|
1210
|
+
historyParts.push({
|
|
1211
|
+
part,
|
|
1212
|
+
order: part.mediaType === AGENT_TOOL_CALL_HISTORY_MEDIA_TYPE
|
|
1213
|
+
? eventOrder?.call
|
|
1214
|
+
: eventOrder?.result,
|
|
1215
|
+
});
|
|
1216
|
+
}
|
|
1217
|
+
historyPartsByMessageId.set(messageId, historyParts);
|
|
1218
|
+
}
|
|
1219
|
+
if (omittedHistory && omissionMessageId) {
|
|
1220
|
+
const historyParts = historyPartsByMessageId.get(omissionMessageId) ?? [];
|
|
1221
|
+
historyParts.push({
|
|
1222
|
+
part: {
|
|
1223
|
+
type: "text",
|
|
1224
|
+
text: TOOL_HISTORY_OMISSION_TEXT,
|
|
1225
|
+
},
|
|
1226
|
+
preserveBoundary: true,
|
|
1227
|
+
});
|
|
1228
|
+
historyPartsByMessageId.set(omissionMessageId, historyParts);
|
|
1229
|
+
}
|
|
1230
|
+
if (historyPartsByMessageId.size === 0) {
|
|
1231
|
+
if (!omittedHistory)
|
|
1232
|
+
return messages;
|
|
1233
|
+
const messageIds = new Set(messages.map(({ id }) => id));
|
|
1234
|
+
let id = "agentkit-tool-history-omission";
|
|
1235
|
+
for (let suffix = 1; messageIds.has(id); suffix += 1) {
|
|
1236
|
+
id = `agentkit-tool-history-omission-${suffix}`;
|
|
1237
|
+
}
|
|
1238
|
+
const omissionMessage = {
|
|
1239
|
+
id,
|
|
1240
|
+
role: "assistant",
|
|
1241
|
+
parts: [{ type: "text", text: TOOL_HISTORY_OMISSION_TEXT }],
|
|
1242
|
+
status: "complete",
|
|
1243
|
+
};
|
|
1244
|
+
const lastUserMessageIndex = messages.at(-1)?.role === "user" ? messages.length - 1 : messages.length;
|
|
1245
|
+
return [
|
|
1246
|
+
...messages.slice(0, lastUserMessageIndex),
|
|
1247
|
+
omissionMessage,
|
|
1248
|
+
...messages.slice(lastUserMessageIndex),
|
|
1249
|
+
];
|
|
1250
|
+
}
|
|
1251
|
+
let omittedUnorderedHistory = false;
|
|
1252
|
+
const projectedMessages = messages.map((message) => {
|
|
1253
|
+
const historyParts = historyPartsByMessageId.get(message.id);
|
|
1254
|
+
if (!historyParts)
|
|
1255
|
+
return message;
|
|
1256
|
+
const parts = messagePartsWithToolHistory(message, eventIndex.messageBodiesByMessageId.get(message.id), historyParts);
|
|
1257
|
+
if (!parts) {
|
|
1258
|
+
omittedUnorderedHistory = true;
|
|
1259
|
+
return message;
|
|
1260
|
+
}
|
|
1261
|
+
return { ...message, parts };
|
|
1262
|
+
});
|
|
1263
|
+
if (omittedUnorderedHistory) {
|
|
1264
|
+
const messageIds = new Set(projectedMessages.map(({ id }) => id));
|
|
1265
|
+
let id = "agentkit-tool-history-omission";
|
|
1266
|
+
for (let suffix = 1; messageIds.has(id); suffix += 1) {
|
|
1267
|
+
id = `agentkit-tool-history-omission-${suffix}`;
|
|
1268
|
+
}
|
|
1269
|
+
projectedMessages.push({
|
|
1270
|
+
id,
|
|
1271
|
+
role: "assistant",
|
|
1272
|
+
parts: [{ type: "text", text: TOOL_HISTORY_ORDER_OMISSION_TEXT }],
|
|
1273
|
+
status: "complete",
|
|
1274
|
+
});
|
|
1275
|
+
}
|
|
1276
|
+
return projectedMessages;
|
|
1277
|
+
}
|
|
1278
|
+
function toolCallHistoryParts(toolCall, include) {
|
|
1279
|
+
const inputProjection = !include.call || toolCall.input === undefined
|
|
1280
|
+
? undefined
|
|
1281
|
+
: projectToolHistoryValue(toolCall.input);
|
|
1282
|
+
const outputProjection = !include.result || toolCall.output === undefined
|
|
1283
|
+
? undefined
|
|
1284
|
+
: projectToolHistoryValue(toolCall.output);
|
|
1285
|
+
const inputOmission = inputProjection && !inputProjection.ok
|
|
1286
|
+
? inputProjection.omission
|
|
1287
|
+
: undefined;
|
|
1288
|
+
const outputOmission = outputProjection && !outputProjection.ok
|
|
1289
|
+
? outputProjection.omission
|
|
1290
|
+
: undefined;
|
|
1291
|
+
const outputOmissionText = include.result && outputOmission
|
|
1292
|
+
? `Tool output omitted from history because ${outputOmission}.`
|
|
1293
|
+
: undefined;
|
|
1294
|
+
const errorText = toolCall.error?.message
|
|
1295
|
+
? `Tool error: ${toolCall.error.message}`
|
|
1296
|
+
: undefined;
|
|
1297
|
+
const errorTextOmission = errorText
|
|
1298
|
+
? toolHistoryTextOmission(errorText)
|
|
1299
|
+
: undefined;
|
|
1300
|
+
const boundedErrorText = errorText
|
|
1301
|
+
? errorTextOmission
|
|
1302
|
+
? `Tool error omitted from history because ${errorTextOmission}.`
|
|
1303
|
+
: errorText
|
|
1304
|
+
: undefined;
|
|
1305
|
+
const statusText = !include.result
|
|
1306
|
+
? undefined
|
|
1307
|
+
: toolCall.output === undefined
|
|
1308
|
+
? boundedErrorText
|
|
1309
|
+
? undefined
|
|
1310
|
+
: `Tool call ${toolCall.status} without a recorded result.`
|
|
1311
|
+
: toolCall.status === "completed" || toolCall.error
|
|
1312
|
+
? undefined
|
|
1313
|
+
: `Tool call ${toolCall.status} returned partial output without a recorded error.`;
|
|
1314
|
+
const resultText = [outputOmissionText, boundedErrorText, statusText]
|
|
1315
|
+
.filter((text) => Boolean(text))
|
|
1316
|
+
.join("\n");
|
|
1317
|
+
const resultTextOmission = toolHistoryTextOmission(resultText);
|
|
1318
|
+
const boundedResultText = resultTextOmission
|
|
1319
|
+
? `Tool result details omitted from history because ${resultTextOmission}.`
|
|
1320
|
+
: resultText;
|
|
1321
|
+
const parts = [];
|
|
1322
|
+
if (include.call) {
|
|
1323
|
+
parts.push({
|
|
1324
|
+
type: "data",
|
|
1325
|
+
mediaType: AGENT_TOOL_CALL_HISTORY_MEDIA_TYPE,
|
|
1326
|
+
data: {
|
|
1327
|
+
id: toolCall.id,
|
|
1328
|
+
name: toolCall.name,
|
|
1329
|
+
...(inputProjection?.ok ? { input: inputProjection.value } : {}),
|
|
1330
|
+
...(inputOmission !== undefined
|
|
1331
|
+
? {
|
|
1332
|
+
inputText: `Tool input omitted from history because ${inputOmission}.`,
|
|
1333
|
+
}
|
|
1334
|
+
: {}),
|
|
1335
|
+
},
|
|
1336
|
+
});
|
|
1337
|
+
}
|
|
1338
|
+
if (include.result) {
|
|
1339
|
+
parts.push({
|
|
1340
|
+
type: "data",
|
|
1341
|
+
mediaType: AGENT_TOOL_RESULT_HISTORY_MEDIA_TYPE,
|
|
1342
|
+
data: {
|
|
1343
|
+
id: toolCall.id,
|
|
1344
|
+
name: toolCall.name,
|
|
1345
|
+
...(outputProjection?.ok ? { result: outputProjection.value } : {}),
|
|
1346
|
+
...(boundedResultText ? { resultText: boundedResultText } : {}),
|
|
1347
|
+
...(toolCall.status === "completed" && !toolCall.error
|
|
1348
|
+
? {}
|
|
1349
|
+
: { isError: true }),
|
|
1350
|
+
},
|
|
1351
|
+
});
|
|
1352
|
+
}
|
|
1353
|
+
return parts;
|
|
1354
|
+
}
|
|
232
1355
|
export class AgentKitClient {
|
|
233
1356
|
transport;
|
|
234
1357
|
createId;
|
|
@@ -641,9 +1764,10 @@ export class AgentKitClient {
|
|
|
641
1764
|
this.setConnection("connecting");
|
|
642
1765
|
try {
|
|
643
1766
|
input.onLocalSubmit?.();
|
|
1767
|
+
const messages = messagesWithToolCallHistory([...current.messages, message], orderedThreadToolCalls(current));
|
|
644
1768
|
const result = await this.invokeRequest(requestContext, (context) => this.transport.startRun({
|
|
645
1769
|
threadId: input.threadId,
|
|
646
|
-
messages
|
|
1770
|
+
messages,
|
|
647
1771
|
options: input.options,
|
|
648
1772
|
metadata: input.metadata,
|
|
649
1773
|
}, context));
|