@agent-native/core 0.203.0-nightly-20261007064822 → 0.203.0
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/agent/engine/builder-engine.d.ts +1 -1
- package/dist/agent/model-config.d.ts +2 -2
- package/dist/agent/model-config.js +0 -2
- package/dist/agent/thread-data-builder.d.ts +6 -0
- package/dist/agent/thread-data-builder.js +747 -25
- package/dist/agent/thread-message-projection.d.ts +20 -0
- package/dist/agent/thread-message-projection.js +300 -0
- package/dist/automation/index.d.ts +1 -1
- package/dist/chat-threads/store.d.ts +4 -1
- package/dist/chat-threads/store.js +92 -17
- package/dist/cli/skills-content/turn-into-app-skill.d.ts +10 -4
- package/dist/cli/skills-content/turn-into-app-skill.js +2454 -705
- package/dist/cli/skills.d.ts +6 -0
- package/dist/cli/skills.js +8 -2
- package/dist/client/chat/agentkit-agent-native.js +839 -89
- package/dist/client/chat/agentkit-protocol.js +67 -17
- package/dist/collab/routes.d.ts +2 -2
- package/dist/jobs/actions/manage-recurring-job.d.ts +1 -1
- package/dist/mcp/build-server.d.ts +5 -1
- package/dist/mcp/build-server.js +56 -13
- package/dist/mcp/embed-app.js +6 -1
- package/dist/notifications/routes.d.ts +1 -1
- package/dist/observability/routes.d.ts +3 -3
- package/dist/org/index.d.ts +2 -2
- package/dist/provider-api/actions/provider-api.d.ts +4 -4
- package/dist/resources/handlers.d.ts +1 -1
- package/dist/server/agent-chat/mcp-options.d.ts +4 -0
- package/dist/server/agent-chat-plugin.js +97 -22
- package/dist/server/agent-engine-default-model-route.d.ts +2 -2
- package/dist/server/onboarding-html.js +1 -1
- package/dist/server/realtime-token.d.ts +1 -1
- package/dist/server/transcribe-voice.d.ts +1 -1
- package/dist/shared/embed-auth.js +5 -4
- package/dist/sharing/actions/list-resource-shares.d.ts +1 -1
- package/dist/templates/factory/.agents/skills/review-latest-feedback/SKILL.md +26 -24
- package/dist/templates/factory/.agents/skills/review-latest-feedback/references/deployment-recovery.md +53 -0
- package/dist/templates/factory/.agents/skills/review-latest-feedback/references/documentation-proof.md +7 -0
- package/dist/templates/workspace-core/.agents/skills/turn-into-app/SKILL.md +284 -461
- package/dist/templates/workspace-core/.agents/skills/turn-into-app/agents/openai.yaml +2 -2
- package/dist/templates/workspace-core/.agents/skills/turn-into-app/references/fresh-project.md +65 -90
- package/dist/templates/workspace-core/.agents/skills/turn-into-app/references/local-run-and-deploy.md +161 -0
- package/dist/templates/workspace-core/.agents/skills/turn-into-app/references/review-loop.md +384 -0
- package/dist/templates/workspace-core/.agents/skills/turn-into-app/references/source-brief.md +116 -0
- package/dist/templates/workspace-core/.agents/skills/turn-into-app/references/spreadsheet-source.md +206 -190
- package/dist/templates/workspace-core/.agents/skills/turn-into-app/references/ui-archetypes.md +381 -0
- package/dist/templates/workspace-core/.agents/skills/turn-into-app/references/ui-direction.md +323 -0
- package/dist/templates/workspace-core/.agents/skills/turn-into-app/references/ui-palettes.md +564 -0
- package/dist/triggers/actions/manage-automation.d.ts +7 -7
- package/docs/content/observability.mdx +136 -0
- package/package.json +4 -4
- package/src/templates/factory/.agents/skills/review-latest-feedback/SKILL.md +26 -24
- package/src/templates/factory/.agents/skills/review-latest-feedback/references/deployment-recovery.md +53 -0
- package/src/templates/factory/.agents/skills/review-latest-feedback/references/documentation-proof.md +7 -0
- package/src/templates/workspace-core/.agents/skills/turn-into-app/SKILL.md +284 -461
- package/src/templates/workspace-core/.agents/skills/turn-into-app/agents/openai.yaml +2 -2
- package/src/templates/workspace-core/.agents/skills/turn-into-app/references/fresh-project.md +65 -90
- package/src/templates/workspace-core/.agents/skills/turn-into-app/references/local-run-and-deploy.md +161 -0
- package/src/templates/workspace-core/.agents/skills/turn-into-app/references/review-loop.md +384 -0
- package/src/templates/workspace-core/.agents/skills/turn-into-app/references/source-brief.md +116 -0
- package/src/templates/workspace-core/.agents/skills/turn-into-app/references/spreadsheet-source.md +206 -190
- package/src/templates/workspace-core/.agents/skills/turn-into-app/references/ui-archetypes.md +381 -0
- package/src/templates/workspace-core/.agents/skills/turn-into-app/references/ui-direction.md +323 -0
- package/src/templates/workspace-core/.agents/skills/turn-into-app/references/ui-palettes.md +564 -0
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { AgentKitRunSlotBusyError } from "@agent-native/agentkit/client";
|
|
2
2
|
import { isAgentKitProtocolVersion, parseAgentRunOptions, parseAgentThreadSnapshot, } from "@agent-native/agentkit/protocol";
|
|
3
|
+
import { projectRootAssistantMessages } from "../../agent/thread-message-projection.js";
|
|
3
4
|
import { RUN_NOT_STARTED_METADATA_KEY, retryContextFromRequest, } from "../../shared/agent-chat-run-not-started.js";
|
|
4
5
|
import { agentNativePath } from "../api-path.js";
|
|
5
6
|
import { CHAT_REQUEST_TOO_LARGE_MESSAGE } from "../error-format.js";
|
|
@@ -11,11 +12,48 @@ import { runOutcomeForCode } from "./run-outcome.js";
|
|
|
11
12
|
import { createAgentNativeChatRuntime, isAgentNativeChatRuntime, } from "./runtime.js";
|
|
12
13
|
const RUN_SLOT_POLL_INTERVAL_MS = 150;
|
|
13
14
|
const RUN_SLOT_STABLE_POLLS = 2;
|
|
15
|
+
const MAX_THREAD_SNAPSHOT_REQUEST_BYTES = 4 * 1024 * 1024;
|
|
16
|
+
const MAX_THREAD_SNAPSHOT_RETRIES = 2;
|
|
17
|
+
const MAX_THREAD_SNAPSHOT_ANNOTATION_REPLACEMENT_BYTES = 64 * 1024;
|
|
14
18
|
function asRecord(value) {
|
|
15
19
|
return value && typeof value === "object" && !Array.isArray(value)
|
|
16
20
|
? value
|
|
17
21
|
: null;
|
|
18
22
|
}
|
|
23
|
+
function mergeStoredReasoningParts(messageParts, storedParts) {
|
|
24
|
+
const storedReasoning = storedParts.filter((part) => part.type === "reasoning");
|
|
25
|
+
if (storedReasoning.length === 0)
|
|
26
|
+
return messageParts;
|
|
27
|
+
const reasoningText = (parts) => parts
|
|
28
|
+
.filter((part) => part.type === "reasoning")
|
|
29
|
+
.map((part) => part.text)
|
|
30
|
+
.join("");
|
|
31
|
+
if (reasoningText(messageParts) === reasoningText(storedReasoning)) {
|
|
32
|
+
return messageParts;
|
|
33
|
+
}
|
|
34
|
+
const withoutReasoning = messageParts.filter((part) => part.type !== "reasoning");
|
|
35
|
+
const firstTextIndex = withoutReasoning.findIndex((part) => part.type === "text");
|
|
36
|
+
const insertionIndex = firstTextIndex < 0 ? withoutReasoning.length : firstTextIndex;
|
|
37
|
+
return [
|
|
38
|
+
...withoutReasoning.slice(0, insertionIndex),
|
|
39
|
+
...storedReasoning,
|
|
40
|
+
...withoutReasoning.slice(insertionIndex),
|
|
41
|
+
];
|
|
42
|
+
}
|
|
43
|
+
function latestSnapshotRunRecord(runs) {
|
|
44
|
+
if (!Array.isArray(runs))
|
|
45
|
+
return undefined;
|
|
46
|
+
return runs.reduce((latest, run) => {
|
|
47
|
+
const record = asRecord(run);
|
|
48
|
+
if (typeof record?.id !== "string")
|
|
49
|
+
return latest;
|
|
50
|
+
if (!latest)
|
|
51
|
+
return record;
|
|
52
|
+
const latestTime = latest.startedAt ?? latest.completedAt;
|
|
53
|
+
const runTime = record.startedAt ?? record.completedAt;
|
|
54
|
+
return latestTime && runTime && runTime < latestTime ? latest : record;
|
|
55
|
+
}, undefined);
|
|
56
|
+
}
|
|
19
57
|
function protocolTurnId(metadata) {
|
|
20
58
|
const native = asRecord(asRecord(metadata)?.[AGENT_NATIVE_PROTOCOL_METADATA_KEY]);
|
|
21
59
|
const observability = asRecord(native?.observability);
|
|
@@ -23,6 +61,13 @@ function protocolTurnId(metadata) {
|
|
|
23
61
|
? observability.turnId
|
|
24
62
|
: undefined;
|
|
25
63
|
}
|
|
64
|
+
function protocolInterruptedRunId(metadata) {
|
|
65
|
+
const native = asRecord(asRecord(metadata)?.[AGENT_NATIVE_PROTOCOL_METADATA_KEY]);
|
|
66
|
+
const observability = asRecord(native?.observability);
|
|
67
|
+
return typeof observability?.interruptedRunId === "string"
|
|
68
|
+
? observability.interruptedRunId
|
|
69
|
+
: undefined;
|
|
70
|
+
}
|
|
26
71
|
function scopeObject(scope) {
|
|
27
72
|
const value = asRecord(scope);
|
|
28
73
|
if (typeof value?.id !== "string" || typeof value.type !== "string") {
|
|
@@ -367,7 +412,7 @@ function withRefusedTurnMetadata(message, stored) {
|
|
|
367
412
|
},
|
|
368
413
|
};
|
|
369
414
|
}
|
|
370
|
-
function reconcileDurableMessages(messages, durable, events, runs) {
|
|
415
|
+
function reconcileDurableMessages(messages, durable, events, runs, toolCalls) {
|
|
371
416
|
const submittedRunId = (message) => {
|
|
372
417
|
const value = asRecord(asRecord(message.metadata)?.custom)?.submittedRunId;
|
|
373
418
|
return typeof value === "string" ? value : undefined;
|
|
@@ -442,13 +487,22 @@ function reconcileDurableMessages(messages, durable, events, runs) {
|
|
|
442
487
|
durableByFoldedRun.set(runId, durableByFoldedRun.has(runId) ? null : message);
|
|
443
488
|
}
|
|
444
489
|
}
|
|
490
|
+
const rootAssistantProjection = projectRootAssistantMessages({
|
|
491
|
+
rootMessages: durable,
|
|
492
|
+
snapshotMessages: messages,
|
|
493
|
+
events,
|
|
494
|
+
runs,
|
|
495
|
+
toolCalls,
|
|
496
|
+
});
|
|
497
|
+
const representedDurableAssistantIds = rootAssistantProjection.representedRootMessageIds;
|
|
498
|
+
const rootMessageIdsBySnapshotMessageId = new Map([...rootAssistantProjection.snapshotMessageIdsByRootMessageId].map(([rootId, snapshotId]) => [snapshotId, rootId]));
|
|
499
|
+
const deduplicatedMessages = messages.filter((message) => !representedDurableAssistantIds.has(message.id));
|
|
445
500
|
const representedSubmittedUserIds = new Set();
|
|
446
501
|
const storedUserBySnapshotId = new Map();
|
|
447
502
|
const unmatchedSnapshotUsers = [];
|
|
448
|
-
const representedAssistantIds = new Set(
|
|
449
|
-
const representedAssistantRunIds = new Set();
|
|
503
|
+
const representedAssistantIds = new Set(deduplicatedMessages.flatMap((message) => message.role === "assistant" ? [message.id] : []));
|
|
450
504
|
const snapshotAssistantRunIds = new Set();
|
|
451
|
-
for (const message of
|
|
505
|
+
for (const message of deduplicatedMessages) {
|
|
452
506
|
if (message.role === "user" &&
|
|
453
507
|
asRecord(message.metadata)?.hideUserMessage !== true) {
|
|
454
508
|
const runId = submittedRunId(message);
|
|
@@ -473,9 +527,6 @@ function reconcileDurableMessages(messages, durable, events, runs) {
|
|
|
473
527
|
const runId = typeof metadataRunId === "string"
|
|
474
528
|
? metadataRunId
|
|
475
529
|
: runByAssistantId.get(message.id);
|
|
476
|
-
const stored = runId ? durableByRun.get(runId) : undefined;
|
|
477
|
-
if (runId && stored)
|
|
478
|
-
representedAssistantRunIds.add(runId);
|
|
479
530
|
if (runId)
|
|
480
531
|
snapshotAssistantRunIds.add(runId);
|
|
481
532
|
}
|
|
@@ -538,6 +589,7 @@ function reconcileDurableMessages(messages, durable, events, runs) {
|
|
|
538
589
|
]);
|
|
539
590
|
for (const message of durable) {
|
|
540
591
|
if (message.role !== "assistant" ||
|
|
592
|
+
representedDurableAssistantIds.has(message.id) ||
|
|
541
593
|
representedAssistantIds.has(message.id)) {
|
|
542
594
|
continue;
|
|
543
595
|
}
|
|
@@ -545,18 +597,11 @@ function reconcileDurableMessages(messages, durable, events, runs) {
|
|
|
545
597
|
if (typeof runId !== "string" || !recoverableAssistantRunIds.has(runId)) {
|
|
546
598
|
continue;
|
|
547
599
|
}
|
|
548
|
-
if (representedAssistantRunIds.has(runId))
|
|
549
|
-
continue;
|
|
550
|
-
// The snapshot holds an earlier run of this folded reply; the final pass
|
|
551
|
-
// completes that message instead of adding a second copy.
|
|
552
|
-
if (durableRunIds(message).some((id) => id !== runId && snapshotAssistantRunIds.has(id))) {
|
|
553
|
-
continue;
|
|
554
|
-
}
|
|
555
600
|
missingMessages.push(message);
|
|
556
601
|
representedAssistantIds.add(message.id);
|
|
557
602
|
}
|
|
558
603
|
const projectedMessages = [
|
|
559
|
-
...
|
|
604
|
+
...deduplicatedMessages,
|
|
560
605
|
...missingMessages.sort((left, right) => (durableIndexById.get(left.id) ?? 0) -
|
|
561
606
|
(durableIndexById.get(right.id) ?? 0)),
|
|
562
607
|
]
|
|
@@ -591,6 +636,24 @@ function reconcileDurableMessages(messages, durable, events, runs) {
|
|
|
591
636
|
}
|
|
592
637
|
if (message.role !== "assistant")
|
|
593
638
|
return message;
|
|
639
|
+
const representedRootId = rootMessageIdsBySnapshotMessageId.get(message.id);
|
|
640
|
+
const representedRoot = representedRootId
|
|
641
|
+
? durableById.get(representedRootId)
|
|
642
|
+
: undefined;
|
|
643
|
+
if (representedRoot?.role === "assistant") {
|
|
644
|
+
const parts = mergeStoredReasoningParts(message.parts, representedRoot.parts);
|
|
645
|
+
const terminalStatus = representedRoot.status === "complete" ||
|
|
646
|
+
representedRoot.status === "error"
|
|
647
|
+
? representedRoot.status
|
|
648
|
+
: undefined;
|
|
649
|
+
if (parts !== message.parts || terminalStatus !== undefined) {
|
|
650
|
+
message = {
|
|
651
|
+
...message,
|
|
652
|
+
...(parts !== message.parts ? { parts } : {}),
|
|
653
|
+
...(terminalStatus ? { status: terminalStatus } : {}),
|
|
654
|
+
};
|
|
655
|
+
}
|
|
656
|
+
}
|
|
594
657
|
const runId = snapshotRunId(message);
|
|
595
658
|
const matched = durableById.get(message.id) ??
|
|
596
659
|
(runId ? durableByRun.get(runId) : undefined);
|
|
@@ -606,6 +669,13 @@ function reconcileDurableMessages(messages, durable, events, runs) {
|
|
|
606
669
|
: folded);
|
|
607
670
|
if (stored?.role !== "assistant")
|
|
608
671
|
return message;
|
|
672
|
+
const foldedRunIds = durableRunIds(stored);
|
|
673
|
+
const spansRuns = foldedRunIds.length > 1;
|
|
674
|
+
const foldedGroup = spansRuns
|
|
675
|
+
? projectedMessages.filter((candidate) => candidate.role === "assistant" &&
|
|
676
|
+
foldedRunIds.includes(snapshotRunId(candidate) ?? ""))
|
|
677
|
+
: [message];
|
|
678
|
+
const isFoldedTerminalProjection = !spansRuns || foldedGroup.at(-1) === message;
|
|
609
679
|
const messageMetadata = asRecord(message.metadata);
|
|
610
680
|
const storedMetadata = asRecord(stored.metadata);
|
|
611
681
|
const messageCustom = asRecord(messageMetadata?.custom);
|
|
@@ -619,30 +689,62 @@ function reconcileDurableMessages(messages, durable, events, runs) {
|
|
|
619
689
|
: {}),
|
|
620
690
|
}
|
|
621
691
|
: message.metadata;
|
|
692
|
+
const reconciledParts = isFoldedTerminalProjection
|
|
693
|
+
? mergeStoredReasoningParts(message.parts, stored.parts)
|
|
694
|
+
: message.parts;
|
|
622
695
|
const reconciled = stored === matched
|
|
623
696
|
? {
|
|
624
697
|
...message,
|
|
698
|
+
...(reconciledParts !== message.parts
|
|
699
|
+
? { parts: reconciledParts }
|
|
700
|
+
: {}),
|
|
625
701
|
...(stored.status === "complete" || stored.status === "error"
|
|
626
702
|
? { status: stored.status }
|
|
627
703
|
: {}),
|
|
628
704
|
...(metadata ? { metadata } : {}),
|
|
629
705
|
}
|
|
630
|
-
: message
|
|
706
|
+
: reconciledParts !== message.parts
|
|
707
|
+
? { ...message, parts: reconciledParts }
|
|
708
|
+
: message;
|
|
631
709
|
const lastPart = reconciled.parts.at(-1);
|
|
632
|
-
const foldedRunIds = durableRunIds(stored);
|
|
633
|
-
const spansRuns = foldedRunIds.length > 1;
|
|
634
710
|
if (lastPart && lastPart.type !== "text" && !spansRuns)
|
|
635
711
|
return reconciled;
|
|
636
712
|
// Only the last message the page saved for a folded reply takes the
|
|
637
713
|
// continuation, and it is measured against everything the page saved.
|
|
638
|
-
const foldedGroup = spansRuns
|
|
639
|
-
? projectedMessages.filter((candidate) => candidate.role === "assistant" &&
|
|
640
|
-
foldedRunIds.includes(snapshotRunId(candidate) ?? ""))
|
|
641
|
-
: [reconciled];
|
|
642
714
|
if (spansRuns && foldedGroup.at(-1) !== message)
|
|
643
715
|
return reconciled;
|
|
644
716
|
const currentText = textOf(foldedGroup.flatMap((entry) => entry.parts));
|
|
645
717
|
const storedText = textOf(stored.parts);
|
|
718
|
+
const orderedFoldedRunIds = [
|
|
719
|
+
...new Set([
|
|
720
|
+
...(Array.isArray(storedCustom?.foldedRunIds)
|
|
721
|
+
? storedCustom.foldedRunIds
|
|
722
|
+
: []),
|
|
723
|
+
asRecord(stored.metadata)?.runId,
|
|
724
|
+
].filter((id) => typeof id === "string")),
|
|
725
|
+
];
|
|
726
|
+
const hasEarlierFoldedRun = orderedFoldedRunIds
|
|
727
|
+
.slice(0, -1)
|
|
728
|
+
.some((id) => snapshotAssistantRunIds.has(id));
|
|
729
|
+
if (spansRuns &&
|
|
730
|
+
runId === asRecord(stored.metadata)?.runId &&
|
|
731
|
+
!hasEarlierFoldedRun &&
|
|
732
|
+
currentText.length > 0 &&
|
|
733
|
+
storedText.endsWith(currentText) &&
|
|
734
|
+
storedText.length > currentText.length) {
|
|
735
|
+
let replacedText = false;
|
|
736
|
+
const parts = reconciled.parts.map((part) => {
|
|
737
|
+
if (part.type !== "text")
|
|
738
|
+
return part;
|
|
739
|
+
if (replacedText)
|
|
740
|
+
return { ...part, text: "" };
|
|
741
|
+
replacedText = true;
|
|
742
|
+
return { ...part, text: storedText };
|
|
743
|
+
});
|
|
744
|
+
if (!replacedText)
|
|
745
|
+
parts.push({ type: "text", text: storedText });
|
|
746
|
+
return { ...reconciled, parts };
|
|
747
|
+
}
|
|
646
748
|
if (!storedText.startsWith(currentText) ||
|
|
647
749
|
storedText.length <= currentText.length) {
|
|
648
750
|
return reconciled;
|
|
@@ -777,6 +879,26 @@ function persistedAnnotations(annotations = [], messageIds) {
|
|
|
777
879
|
? [{ messageId, annotation: persistedAnnotation(annotation) }]
|
|
778
880
|
: []);
|
|
779
881
|
}
|
|
882
|
+
function assistantMessageIdsByRun(events) {
|
|
883
|
+
const messageIdsByRun = new Map();
|
|
884
|
+
for (const event of events ?? []) {
|
|
885
|
+
if ((event.type !== "message.created" &&
|
|
886
|
+
event.type !== "message.completed") ||
|
|
887
|
+
event.message.role !== "assistant") {
|
|
888
|
+
continue;
|
|
889
|
+
}
|
|
890
|
+
const messageIds = messageIdsByRun.get(event.runId) ?? new Set();
|
|
891
|
+
messageIds.add(event.message.id);
|
|
892
|
+
messageIdsByRun.set(event.runId, messageIds);
|
|
893
|
+
}
|
|
894
|
+
const uniqueMessageIdsByRun = new Map();
|
|
895
|
+
for (const [runId, messageIds] of messageIdsByRun) {
|
|
896
|
+
if (messageIds.size === 1) {
|
|
897
|
+
uniqueMessageIdsByRun.set(runId, [...messageIds][0]);
|
|
898
|
+
}
|
|
899
|
+
}
|
|
900
|
+
return uniqueMessageIdsByRun;
|
|
901
|
+
}
|
|
780
902
|
function persistedFileUrl(url) {
|
|
781
903
|
return url && !/^\s*data:/i.test(url) ? url : undefined;
|
|
782
904
|
}
|
|
@@ -940,6 +1062,23 @@ function mergeStoredAndIncomingMessages(stored, incoming) {
|
|
|
940
1062
|
}
|
|
941
1063
|
return [...messages.values()];
|
|
942
1064
|
}
|
|
1065
|
+
function mergeLegacyRootMessages(snapshotMessages, legacyMessages) {
|
|
1066
|
+
const merged = [...snapshotMessages];
|
|
1067
|
+
for (const message of legacyMessages) {
|
|
1068
|
+
const createdAt = Date.parse(message.createdAt ?? "");
|
|
1069
|
+
const index = merged.findIndex((candidate) => {
|
|
1070
|
+
const candidateCreatedAt = Date.parse(candidate.createdAt ?? "");
|
|
1071
|
+
return (Number.isFinite(createdAt) &&
|
|
1072
|
+
Number.isFinite(candidateCreatedAt) &&
|
|
1073
|
+
candidateCreatedAt > createdAt);
|
|
1074
|
+
});
|
|
1075
|
+
if (index === -1)
|
|
1076
|
+
merged.push(message);
|
|
1077
|
+
else
|
|
1078
|
+
merged.splice(index, 0, message);
|
|
1079
|
+
}
|
|
1080
|
+
return merged;
|
|
1081
|
+
}
|
|
943
1082
|
function mergeStoredAndIncomingToolCalls(stored, incoming) {
|
|
944
1083
|
const toolCalls = new Map(stored.map((toolCall) => [toolCall.id, toolCall]));
|
|
945
1084
|
for (const toolCall of incoming) {
|
|
@@ -948,12 +1087,9 @@ function mergeStoredAndIncomingToolCalls(stored, incoming) {
|
|
|
948
1087
|
return [...toolCalls.values()];
|
|
949
1088
|
}
|
|
950
1089
|
function mergeStoredAndIncomingWidgets(stored, incoming) {
|
|
951
|
-
const widgets = new Map(stored.map((snapshot) => [
|
|
952
|
-
JSON.stringify([snapshot.messageId, snapshot.widget.id]),
|
|
953
|
-
snapshot,
|
|
954
|
-
]));
|
|
1090
|
+
const widgets = new Map(stored.map((snapshot) => [snapshot.widget.id, snapshot]));
|
|
955
1091
|
for (const snapshot of incoming) {
|
|
956
|
-
widgets.set(
|
|
1092
|
+
widgets.set(snapshot.widget.id, snapshot);
|
|
957
1093
|
}
|
|
958
1094
|
return [...widgets.values()];
|
|
959
1095
|
}
|
|
@@ -1007,6 +1143,52 @@ function persistedToolCalls(toolCalls = []) {
|
|
|
1007
1143
|
];
|
|
1008
1144
|
});
|
|
1009
1145
|
}
|
|
1146
|
+
function changedSnapshotEntries(incoming, previous, getId) {
|
|
1147
|
+
const previousById = new Map();
|
|
1148
|
+
for (const entry of Array.isArray(previous) ? previous : []) {
|
|
1149
|
+
const id = getId(entry);
|
|
1150
|
+
if (id)
|
|
1151
|
+
previousById.set(id, entry);
|
|
1152
|
+
}
|
|
1153
|
+
return incoming.filter((entry) => {
|
|
1154
|
+
const id = getId(entry);
|
|
1155
|
+
if (!id || !previousById.has(id))
|
|
1156
|
+
return true;
|
|
1157
|
+
return JSON.stringify(previousById.get(id)) !== JSON.stringify(entry);
|
|
1158
|
+
});
|
|
1159
|
+
}
|
|
1160
|
+
function samePersistedMessageContent(left, right) {
|
|
1161
|
+
return (left.role === right.role &&
|
|
1162
|
+
JSON.stringify(left.parts) === JSON.stringify(right.parts) &&
|
|
1163
|
+
JSON.stringify(left.status) === JSON.stringify(right.status) &&
|
|
1164
|
+
JSON.stringify(left.metadata) === JSON.stringify(right.metadata));
|
|
1165
|
+
}
|
|
1166
|
+
function snapshotEntryId(value) {
|
|
1167
|
+
const entry = asRecord(value);
|
|
1168
|
+
return typeof entry?.id === "string" ? entry.id : undefined;
|
|
1169
|
+
}
|
|
1170
|
+
function snapshotWidgetId(value) {
|
|
1171
|
+
const entry = asRecord(value);
|
|
1172
|
+
const widget = asRecord(entry?.widget);
|
|
1173
|
+
return typeof widget?.id === "string" ? widget.id : undefined;
|
|
1174
|
+
}
|
|
1175
|
+
function snapshotAnnotationKey(value) {
|
|
1176
|
+
const entry = asRecord(value);
|
|
1177
|
+
const annotation = asRecord(entry?.annotation);
|
|
1178
|
+
return typeof annotation?.id === "string"
|
|
1179
|
+
? JSON.stringify(["id", annotation.id])
|
|
1180
|
+
: JSON.stringify(["value", value]);
|
|
1181
|
+
}
|
|
1182
|
+
function threadSnapshotBody(input) {
|
|
1183
|
+
const repository = { messages: [], agentKit: input.agentKit };
|
|
1184
|
+
return JSON.stringify({
|
|
1185
|
+
threadData: JSON.stringify(repository),
|
|
1186
|
+
title: input.title ??
|
|
1187
|
+
(typeof input.stored.title === "string" ? input.stored.title : ""),
|
|
1188
|
+
preview: typeof input.stored.preview === "string" ? input.stored.preview : "",
|
|
1189
|
+
messageCount: input.snapshotMessages.length,
|
|
1190
|
+
});
|
|
1191
|
+
}
|
|
1010
1192
|
function storedMessageId(value) {
|
|
1011
1193
|
const outer = asRecord(value);
|
|
1012
1194
|
const message = asRecord(outer?.message ?? outer);
|
|
@@ -1148,6 +1330,33 @@ function httpErrorCode(status) {
|
|
|
1148
1330
|
function isRetryableHttpStatus(status) {
|
|
1149
1331
|
return status === 408 || status === 429 || status >= 500;
|
|
1150
1332
|
}
|
|
1333
|
+
function snapshotAnnotationConflicts(value) {
|
|
1334
|
+
const response = asRecord(value);
|
|
1335
|
+
if (!response || response.ok !== true) {
|
|
1336
|
+
throw new TypeError("Agent chat thread save response must be an object.");
|
|
1337
|
+
}
|
|
1338
|
+
if (response.annotationConflicts === undefined)
|
|
1339
|
+
return [];
|
|
1340
|
+
if (!Array.isArray(response.annotationConflicts)) {
|
|
1341
|
+
throw new TypeError("Agent chat annotation conflicts must be an array.");
|
|
1342
|
+
}
|
|
1343
|
+
return response.annotationConflicts.map((value) => {
|
|
1344
|
+
const conflict = asRecord(value);
|
|
1345
|
+
if (typeof conflict?.messageId !== "string" ||
|
|
1346
|
+
(conflict.operation !== "upsert" && conflict.operation !== "remove") ||
|
|
1347
|
+
(conflict.annotationId !== undefined &&
|
|
1348
|
+
typeof conflict.annotationId !== "string")) {
|
|
1349
|
+
throw new TypeError("Agent chat annotation conflict is invalid.");
|
|
1350
|
+
}
|
|
1351
|
+
return {
|
|
1352
|
+
messageId: conflict.messageId,
|
|
1353
|
+
...(typeof conflict.annotationId === "string"
|
|
1354
|
+
? { annotationId: conflict.annotationId }
|
|
1355
|
+
: {}),
|
|
1356
|
+
operation: conflict.operation,
|
|
1357
|
+
};
|
|
1358
|
+
});
|
|
1359
|
+
}
|
|
1151
1360
|
function scopedThreadEndpoint(endpoint, options) {
|
|
1152
1361
|
if (!options.isolateHistoryByScope)
|
|
1153
1362
|
return endpoint;
|
|
@@ -1168,6 +1377,8 @@ export function createAgentNativeAgentKitTransport(options = {}) {
|
|
|
1168
1377
|
const fetcher = options.fetch ?? fetch;
|
|
1169
1378
|
const now = options.adapter?.now ?? (() => new Date().toISOString());
|
|
1170
1379
|
const promotionClaimIds = new Map();
|
|
1380
|
+
const durableAssistantMessageIdsByRun = new Map();
|
|
1381
|
+
const assistantHistoryMessageIdsByRun = new Map();
|
|
1171
1382
|
let transport;
|
|
1172
1383
|
function promotionClaimId(threadId, messageId) {
|
|
1173
1384
|
const key = JSON.stringify([threadId, messageId]);
|
|
@@ -1218,6 +1429,33 @@ export function createAgentNativeAgentKitTransport(options = {}) {
|
|
|
1218
1429
|
}));
|
|
1219
1430
|
const storedMessageProjection = storedMessages(repository.messages, now, options.adapter?.textFormat).filter((message) => !isRenderedRunNotStartedNotice(message, failedRunIds));
|
|
1220
1431
|
const queuedMessages = storedQueue(repository.queuedMessages, threadId, updatedAt);
|
|
1432
|
+
const agentKitMessages = Array.isArray(agentKit?.messages)
|
|
1433
|
+
? agentKit.messages
|
|
1434
|
+
: undefined;
|
|
1435
|
+
const agentKitMessageIds = new Set(agentKitMessages?.map((message) => message.id) ?? []);
|
|
1436
|
+
const legacyRootMessages = storedMessageProjection.filter((message) => {
|
|
1437
|
+
if (agentKitMessageIds.has(message.id))
|
|
1438
|
+
return false;
|
|
1439
|
+
const metadata = asRecord(message.metadata);
|
|
1440
|
+
const custom = asRecord(metadata?.custom);
|
|
1441
|
+
if (typeof metadata?.runId === "string" ||
|
|
1442
|
+
typeof custom?.submittedRunId === "string" ||
|
|
1443
|
+
typeof custom?.agentKitMessageId === "string") {
|
|
1444
|
+
return false;
|
|
1445
|
+
}
|
|
1446
|
+
return !message.parts.some((part) => {
|
|
1447
|
+
if (part.type !== "data")
|
|
1448
|
+
return false;
|
|
1449
|
+
const repositoryPart = asRecord(part.data);
|
|
1450
|
+
return repositoryPart?.type === "tool-call";
|
|
1451
|
+
});
|
|
1452
|
+
});
|
|
1453
|
+
const mergeAllStoredMessages = agentKit?._mergeRootMessages === true || agentKitMessages?.length === 0;
|
|
1454
|
+
const messagesWithLegacyRoot = agentKitMessages &&
|
|
1455
|
+
agentKit?._mergeRootMessages !== true &&
|
|
1456
|
+
agentKitMessages.length > 0
|
|
1457
|
+
? mergeLegacyRootMessages(agentKitMessages, legacyRootMessages)
|
|
1458
|
+
: undefined;
|
|
1221
1459
|
const protocolSnapshot = agentKit
|
|
1222
1460
|
? parseAgentThreadSnapshot({
|
|
1223
1461
|
id: threadId,
|
|
@@ -1225,8 +1463,10 @@ export function createAgentNativeAgentKitTransport(options = {}) {
|
|
|
1225
1463
|
createdAt,
|
|
1226
1464
|
updatedAt,
|
|
1227
1465
|
metadata: asRecord(stored.metadata) ?? undefined,
|
|
1228
|
-
messages:
|
|
1229
|
-
?
|
|
1466
|
+
messages: agentKitMessages
|
|
1467
|
+
? mergeAllStoredMessages
|
|
1468
|
+
? mergeStoredAndIncomingMessages(storedMessageProjection, agentKitMessages)
|
|
1469
|
+
: (messagesWithLegacyRoot ?? agentKitMessages)
|
|
1230
1470
|
: storedMessageProjection,
|
|
1231
1471
|
events: agentKit.events,
|
|
1232
1472
|
runs: agentKit.runs,
|
|
@@ -1240,7 +1480,7 @@ export function createAgentNativeAgentKitTransport(options = {}) {
|
|
|
1240
1480
|
: undefined;
|
|
1241
1481
|
const durableMessages = storedMessageProjection;
|
|
1242
1482
|
const messages = protocolSnapshot?.messages
|
|
1243
|
-
? reconcileDurableMessages(protocolSnapshot.messages, durableMessages, protocolSnapshot.events, protocolSnapshot.runs)
|
|
1483
|
+
? reconcileDurableMessages(protocolSnapshot.messages, durableMessages, protocolSnapshot.events, protocolSnapshot.runs, protocolSnapshot.toolCalls ?? [])
|
|
1244
1484
|
: durableMessages;
|
|
1245
1485
|
const actionWidgets = storedActionWidgets(repository.messages);
|
|
1246
1486
|
const canonicalToolCallMessageIds = new Map((protocolSnapshot?.toolCalls ?? []).flatMap((toolCall) => toolCall.messageId ? [[toolCall.id, toolCall.messageId]] : []));
|
|
@@ -1407,8 +1647,12 @@ export function createAgentNativeAgentKitTransport(options = {}) {
|
|
|
1407
1647
|
else {
|
|
1408
1648
|
throw new TypeError("Agent chat active-run response has an invalid status.");
|
|
1409
1649
|
}
|
|
1650
|
+
const localRun = await protocolTransport.getRun?.({
|
|
1651
|
+
threadId,
|
|
1652
|
+
runId: value.runId,
|
|
1653
|
+
});
|
|
1410
1654
|
return {
|
|
1411
|
-
id: value.runId,
|
|
1655
|
+
id: localRun?.id ?? value.runId,
|
|
1412
1656
|
threadId,
|
|
1413
1657
|
status: runStatus,
|
|
1414
1658
|
// The durable SSE endpoint replays from its first event when a browser
|
|
@@ -1422,10 +1666,32 @@ export function createAgentNativeAgentKitTransport(options = {}) {
|
|
|
1422
1666
|
const thread = stored ? projectThread(threadId, stored) : null;
|
|
1423
1667
|
if (!stored || !thread)
|
|
1424
1668
|
return thread;
|
|
1669
|
+
const repository = storedRepository(stored);
|
|
1670
|
+
const durableMessages = storedMessages(repository.messages, now, options.adapter?.textFormat);
|
|
1671
|
+
for (const message of durableMessages) {
|
|
1672
|
+
if (message.role !== "assistant")
|
|
1673
|
+
continue;
|
|
1674
|
+
const foldedRunIds = durableRunIds(message);
|
|
1675
|
+
if (foldedRunIds.length < 2)
|
|
1676
|
+
continue;
|
|
1677
|
+
for (const runId of foldedRunIds) {
|
|
1678
|
+
const previous = durableAssistantMessageIdsByRun.get(runId);
|
|
1679
|
+
durableAssistantMessageIdsByRun.set(runId, previous === undefined || previous === message.id ? message.id : null);
|
|
1680
|
+
}
|
|
1681
|
+
}
|
|
1682
|
+
for (const [runId, messageId] of assistantMessageIdsByRun(thread.events)) {
|
|
1683
|
+
assistantHistoryMessageIdsByRun.set(runId, messageId);
|
|
1684
|
+
}
|
|
1685
|
+
const assistantMessageIds = new Set(thread.messages.flatMap((message) => message.role === "assistant" ? [message.id] : []));
|
|
1686
|
+
for (const run of thread.runs ?? []) {
|
|
1687
|
+
if (typeof run.activeMessageId === "string" &&
|
|
1688
|
+
assistantMessageIds.has(run.activeMessageId)) {
|
|
1689
|
+
assistantHistoryMessageIdsByRun.set(run.id, run.activeMessageId);
|
|
1690
|
+
}
|
|
1691
|
+
}
|
|
1425
1692
|
if (options.runtime && !isAgentNativeChatRuntime(options.runtime)) {
|
|
1426
1693
|
return thread;
|
|
1427
1694
|
}
|
|
1428
|
-
const durableMessages = storedMessages(storedRepository(stored).messages, now, options.adapter?.textFormat);
|
|
1429
1695
|
const completedRunIds = completedDurableRunIds(durableMessages);
|
|
1430
1696
|
const userStoppedRunIds = userStoppedDurableRunIds(durableMessages);
|
|
1431
1697
|
const durableFailures = durableRunFailures(durableMessages);
|
|
@@ -1493,7 +1759,7 @@ export function createAgentNativeAgentKitTransport(options = {}) {
|
|
|
1493
1759
|
.map((run) => run.id)
|
|
1494
1760
|
: [];
|
|
1495
1761
|
const failedRunIds = new Set(runs.flatMap((run) => run.status === "failed" && run.error ? [run.id] : []));
|
|
1496
|
-
const messages = reconcileDurableMessages(thread.messages, durableMessages.filter((message) => !isRenderedRunNotStartedNotice(message, failedRunIds)), thread.events, runs);
|
|
1762
|
+
const messages = reconcileDurableMessages(thread.messages, durableMessages.filter((message) => !isRenderedRunNotStartedNotice(message, failedRunIds)), thread.events, runs, thread.toolCalls);
|
|
1497
1763
|
const replayFromStart = [
|
|
1498
1764
|
"running",
|
|
1499
1765
|
"awaiting_approval",
|
|
@@ -1512,10 +1778,10 @@ export function createAgentNativeAgentKitTransport(options = {}) {
|
|
|
1512
1778
|
? messages.filter((message) => {
|
|
1513
1779
|
if (message.role !== "assistant")
|
|
1514
1780
|
return true;
|
|
1515
|
-
|
|
1516
|
-
return !(runId === discoveredRun?.id ||
|
|
1781
|
+
return !(durableRunIds(message).includes(discoveredRun?.id ?? "") ||
|
|
1517
1782
|
replayedMessageIds.has(message.id) ||
|
|
1518
|
-
(message.status === "streaming" &&
|
|
1783
|
+
(message.status === "streaming" &&
|
|
1784
|
+
typeof asRecord(message.metadata)?.runId !== "string"));
|
|
1519
1785
|
})
|
|
1520
1786
|
: messages;
|
|
1521
1787
|
return {
|
|
@@ -1575,22 +1841,6 @@ export function createAgentNativeAgentKitTransport(options = {}) {
|
|
|
1575
1841
|
: [], input.snapshot.widgets ?? [])
|
|
1576
1842
|
: input.snapshot.widgets;
|
|
1577
1843
|
const compactEvents = persistedHistoryEvents(input.snapshot.events);
|
|
1578
|
-
const compactRunIds = new Set(compactEvents.map((event) => event.runId));
|
|
1579
|
-
const eventsById = new Map();
|
|
1580
|
-
const previousEvents = Array.isArray(previousAgentKit.events)
|
|
1581
|
-
? previousAgentKit.events
|
|
1582
|
-
: [];
|
|
1583
|
-
for (const event of previousEvents) {
|
|
1584
|
-
const record = asRecord(event);
|
|
1585
|
-
if (typeof record?.id === "string" &&
|
|
1586
|
-
typeof record.runId === "string" &&
|
|
1587
|
-
!compactRunIds.has(record.runId)) {
|
|
1588
|
-
eventsById.set(record.id, event);
|
|
1589
|
-
}
|
|
1590
|
-
}
|
|
1591
|
-
for (const event of compactEvents) {
|
|
1592
|
-
eventsById.set(event.id, event);
|
|
1593
|
-
}
|
|
1594
1844
|
const runsById = new Map();
|
|
1595
1845
|
const previousRuns = Array.isArray(previousAgentKit.runs)
|
|
1596
1846
|
? previousAgentKit.runs
|
|
@@ -1601,43 +1851,445 @@ export function createAgentNativeAgentKitTransport(options = {}) {
|
|
|
1601
1851
|
runsById.set(record.id, run);
|
|
1602
1852
|
}
|
|
1603
1853
|
const snapshotMessageIds = new Set(snapshotMessages.map((message) => message.id));
|
|
1854
|
+
const previousAgentKitMessageIds = new Set((Array.isArray(previousAgentKit.messages)
|
|
1855
|
+
? previousAgentKit.messages
|
|
1856
|
+
: []).flatMap((message) => {
|
|
1857
|
+
const id = snapshotEntryId(message);
|
|
1858
|
+
return id ? [id] : [];
|
|
1859
|
+
}));
|
|
1860
|
+
const topLevelMessageProjections = new Map(persistedMessages(Array.isArray(repository.messages)
|
|
1861
|
+
? storedMessages(repository.messages, now, options.adapter?.textFormat)
|
|
1862
|
+
: []).map((message) => [message.id, message]));
|
|
1863
|
+
const persistedSnapshotMessages = persistedMessages(snapshotMessages);
|
|
1864
|
+
const changedMessages = changedSnapshotEntries(persistedSnapshotMessages, previousAgentKit.messages, snapshotEntryId).filter((message) => {
|
|
1865
|
+
if (previousAgentKitMessageIds.has(message.id))
|
|
1866
|
+
return true;
|
|
1867
|
+
const topLevelMessage = topLevelMessageProjections.get(message.id);
|
|
1868
|
+
return (!topLevelMessage ||
|
|
1869
|
+
!samePersistedMessageContent(topLevelMessage, message));
|
|
1870
|
+
});
|
|
1871
|
+
const mergeRootMessages = previousAgentKit._mergeRootMessages === true ||
|
|
1872
|
+
persistedSnapshotMessages.some((message) => {
|
|
1873
|
+
const topLevelMessage = topLevelMessageProjections.get(message.id);
|
|
1874
|
+
return (!previousAgentKitMessageIds.has(message.id) &&
|
|
1875
|
+
topLevelMessage !== undefined &&
|
|
1876
|
+
samePersistedMessageContent(topLevelMessage, message));
|
|
1877
|
+
});
|
|
1878
|
+
const changedWidgets = changedSnapshotEntries(persistedActionWidgets(snapshotWidgets, snapshotMessageIds), previousAgentKit.widgets, snapshotWidgetId);
|
|
1879
|
+
const changedToolCalls = changedSnapshotEntries(persistedToolCalls(snapshotToolCalls), previousAgentKit.toolCalls, snapshotEntryId);
|
|
1880
|
+
const previousEventsByRun = new Map();
|
|
1881
|
+
for (const event of Array.isArray(previousAgentKit.events)
|
|
1882
|
+
? previousAgentKit.events
|
|
1883
|
+
: []) {
|
|
1884
|
+
const runId = asRecord(event)?.runId;
|
|
1885
|
+
if (typeof runId !== "string")
|
|
1886
|
+
continue;
|
|
1887
|
+
const events = previousEventsByRun.get(runId);
|
|
1888
|
+
if (events)
|
|
1889
|
+
events.push(event);
|
|
1890
|
+
else
|
|
1891
|
+
previousEventsByRun.set(runId, [event]);
|
|
1892
|
+
}
|
|
1893
|
+
const incomingEventsByRun = new Map();
|
|
1894
|
+
for (const event of compactEvents) {
|
|
1895
|
+
const events = incomingEventsByRun.get(event.runId);
|
|
1896
|
+
if (events)
|
|
1897
|
+
events.push(event);
|
|
1898
|
+
else
|
|
1899
|
+
incomingEventsByRun.set(event.runId, [event]);
|
|
1900
|
+
}
|
|
1901
|
+
const changedEventRunIds = new Set();
|
|
1902
|
+
for (const [runId, events] of incomingEventsByRun) {
|
|
1903
|
+
if (JSON.stringify(previousEventsByRun.get(runId) ?? []) ===
|
|
1904
|
+
JSON.stringify(events)) {
|
|
1905
|
+
continue;
|
|
1906
|
+
}
|
|
1907
|
+
changedEventRunIds.add(runId);
|
|
1908
|
+
}
|
|
1909
|
+
const changedEvents = compactEvents.filter((event) => changedEventRunIds.has(event.runId));
|
|
1910
|
+
const eventRunLastSequenceById = new Map();
|
|
1911
|
+
for (const run of input.snapshot.runs ?? []) {
|
|
1912
|
+
if (typeof run.id === "string" &&
|
|
1913
|
+
Number.isSafeInteger(run.lastSequence) &&
|
|
1914
|
+
run.lastSequence >= 0) {
|
|
1915
|
+
eventRunLastSequenceById.set(run.id, run.lastSequence);
|
|
1916
|
+
}
|
|
1917
|
+
}
|
|
1918
|
+
for (const event of input.snapshot.events ?? []) {
|
|
1919
|
+
const previousSequence = eventRunLastSequenceById.get(event.runId) ?? 0;
|
|
1920
|
+
if (Number.isSafeInteger(event.sequence)) {
|
|
1921
|
+
eventRunLastSequenceById.set(event.runId, Math.max(previousSequence, event.sequence));
|
|
1922
|
+
}
|
|
1923
|
+
}
|
|
1924
|
+
const changedEventsByRun = new Map();
|
|
1925
|
+
for (const event of changedEvents) {
|
|
1926
|
+
const runEvents = changedEventsByRun.get(event.runId);
|
|
1927
|
+
if (runEvents)
|
|
1928
|
+
runEvents.push(event);
|
|
1929
|
+
else
|
|
1930
|
+
changedEventsByRun.set(event.runId, [event]);
|
|
1931
|
+
}
|
|
1932
|
+
const eventRunSnapshotBatches = new Map();
|
|
1933
|
+
for (const [runId, events] of changedEventsByRun) {
|
|
1934
|
+
const lastSequence = eventRunLastSequenceById.get(runId) ?? events.length;
|
|
1935
|
+
const snapshotId = typeof crypto !== "undefined" && crypto.randomUUID
|
|
1936
|
+
? crypto.randomUUID()
|
|
1937
|
+
: `${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
1938
|
+
eventRunSnapshotBatches.set(runId, {
|
|
1939
|
+
runId,
|
|
1940
|
+
snapshotId,
|
|
1941
|
+
lastSequence,
|
|
1942
|
+
expectedEventCount: new Set(events.map((event) => event.id)).size,
|
|
1943
|
+
});
|
|
1944
|
+
}
|
|
1945
|
+
const lastChangedEventIndexByRun = new Map();
|
|
1946
|
+
changedEvents.forEach((event, index) => {
|
|
1947
|
+
lastChangedEventIndexByRun.set(event.runId, index);
|
|
1948
|
+
});
|
|
1949
|
+
const changedEventUpdates = changedEvents.map((event, index) => {
|
|
1950
|
+
return {
|
|
1951
|
+
key: "events",
|
|
1952
|
+
entry: event,
|
|
1953
|
+
eventRunSnapshotBatch: eventRunSnapshotBatches.get(event.runId),
|
|
1954
|
+
completesEventRunSnapshot: lastChangedEventIndexByRun.get(event.runId) === index,
|
|
1955
|
+
};
|
|
1956
|
+
});
|
|
1957
|
+
const persistedRuns = [...runsById.values()].map((run) => {
|
|
1958
|
+
const record = asRecord(run);
|
|
1959
|
+
return record && record.error !== undefined
|
|
1960
|
+
? { ...record, error: persistedError(record.error) }
|
|
1961
|
+
: run;
|
|
1962
|
+
});
|
|
1963
|
+
const changedRuns = changedSnapshotEntries(persistedRuns, previousAgentKit.runs, snapshotEntryId);
|
|
1964
|
+
const latestRun = latestSnapshotRunRecord(persistedRuns);
|
|
1965
|
+
const latestRunId = latestRun?.id;
|
|
1966
|
+
const previousLatestRun = latestSnapshotRunRecord(previousAgentKit.runs);
|
|
1967
|
+
const latestRunSequence = typeof latestRun?.lastSequence === "number" &&
|
|
1968
|
+
Number.isSafeInteger(latestRun.lastSequence) &&
|
|
1969
|
+
latestRun.lastSequence >= 0
|
|
1970
|
+
? latestRun.lastSequence
|
|
1971
|
+
: undefined;
|
|
1972
|
+
const previousLatestRunSequence = typeof previousLatestRun?.lastSequence === "number" &&
|
|
1973
|
+
Number.isSafeInteger(previousLatestRun.lastSequence) &&
|
|
1974
|
+
previousLatestRun.lastSequence >= 0
|
|
1975
|
+
? previousLatestRun.lastSequence
|
|
1976
|
+
: undefined;
|
|
1977
|
+
const latestRunIsNew = latestRun?.id !== previousLatestRun?.id;
|
|
1978
|
+
const latestRunJustCompleted = latestRun?.id === previousLatestRun?.id &&
|
|
1979
|
+
previousLatestRun?.status !== "completed" &&
|
|
1980
|
+
latestRunSequence !== undefined &&
|
|
1981
|
+
previousLatestRunSequence !== undefined &&
|
|
1982
|
+
latestRunSequence >= previousLatestRunSequence;
|
|
1983
|
+
const latestRunSuggestionsChanged = input.snapshot.suggestions !== undefined &&
|
|
1984
|
+
JSON.stringify(input.snapshot.suggestions) !==
|
|
1985
|
+
JSON.stringify(previousAgentKit.suggestions);
|
|
1986
|
+
const shouldPersistLatestRunSuggestions = latestRun?.status === "completed" &&
|
|
1987
|
+
(latestRunIsNew ||
|
|
1988
|
+
latestRunJustCompleted ||
|
|
1989
|
+
(latestRunSuggestionsChanged &&
|
|
1990
|
+
latestRunSequence !== undefined &&
|
|
1991
|
+
previousLatestRunSequence !== undefined &&
|
|
1992
|
+
latestRunSequence > previousLatestRunSequence));
|
|
1604
1993
|
const annotations = input.snapshot.annotations ??
|
|
1605
1994
|
(Array.isArray(previousAgentKit.annotations)
|
|
1606
1995
|
? previousAgentKit.annotations
|
|
1607
1996
|
: []);
|
|
1608
|
-
const
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1997
|
+
const persistedSnapshotAnnotations = persistedAnnotations(annotations, snapshotMessageIds);
|
|
1998
|
+
const previousAnnotationsByMessage = new Map();
|
|
1999
|
+
for (const annotation of Array.isArray(previousAgentKit.annotations)
|
|
2000
|
+
? previousAgentKit.annotations
|
|
2001
|
+
: []) {
|
|
2002
|
+
const messageId = asRecord(annotation)?.messageId;
|
|
2003
|
+
if (typeof messageId !== "string")
|
|
2004
|
+
continue;
|
|
2005
|
+
const messageAnnotations = previousAnnotationsByMessage.get(messageId);
|
|
2006
|
+
if (messageAnnotations)
|
|
2007
|
+
messageAnnotations.push(annotation);
|
|
2008
|
+
else
|
|
2009
|
+
previousAnnotationsByMessage.set(messageId, [annotation]);
|
|
2010
|
+
}
|
|
2011
|
+
const incomingAnnotationsByMessage = new Map();
|
|
2012
|
+
for (const annotation of persistedSnapshotAnnotations) {
|
|
2013
|
+
const messageAnnotations = incomingAnnotationsByMessage.get(annotation.messageId);
|
|
2014
|
+
if (messageAnnotations)
|
|
2015
|
+
messageAnnotations.push(annotation);
|
|
2016
|
+
else
|
|
2017
|
+
incomingAnnotationsByMessage.set(annotation.messageId, [annotation]);
|
|
2018
|
+
}
|
|
2019
|
+
const annotationMessageIdsToReplace = [];
|
|
2020
|
+
const changedAnnotations = [];
|
|
2021
|
+
const annotationMessageIds = new Set([
|
|
2022
|
+
...previousAnnotationsByMessage.keys(),
|
|
2023
|
+
...incomingAnnotationsByMessage.keys(),
|
|
2024
|
+
]);
|
|
2025
|
+
for (const messageId of annotationMessageIds) {
|
|
2026
|
+
const previous = previousAnnotationsByMessage.get(messageId) ?? [];
|
|
2027
|
+
const incoming = incomingAnnotationsByMessage.get(messageId) ?? [];
|
|
2028
|
+
const previousByKey = new Map(previous.map((annotation) => [
|
|
2029
|
+
snapshotAnnotationKey(annotation),
|
|
2030
|
+
annotation,
|
|
2031
|
+
]));
|
|
2032
|
+
const incomingKeys = new Set(incoming.map(snapshotAnnotationKey));
|
|
2033
|
+
const annotationsToRemove = previous.flatMap((annotation) => {
|
|
2034
|
+
const key = snapshotAnnotationKey(annotation);
|
|
2035
|
+
return incomingKeys.has(key) ? [] : [{ key, baseline: annotation }];
|
|
2036
|
+
});
|
|
2037
|
+
const changedIncoming = incoming.flatMap((annotation) => {
|
|
2038
|
+
const previousAnnotation = previousByKey.get(snapshotAnnotationKey(annotation));
|
|
2039
|
+
if (previousAnnotation !== undefined &&
|
|
2040
|
+
JSON.stringify(previousAnnotation) === JSON.stringify(annotation)) {
|
|
2041
|
+
return [];
|
|
2042
|
+
}
|
|
2043
|
+
return [
|
|
2044
|
+
{
|
|
2045
|
+
entry: annotation,
|
|
2046
|
+
baseline: previousAnnotation ?? null,
|
|
2047
|
+
},
|
|
2048
|
+
];
|
|
2049
|
+
});
|
|
2050
|
+
if (annotationsToRemove.length === 0 && changedIncoming.length === 0) {
|
|
2051
|
+
continue;
|
|
2052
|
+
}
|
|
2053
|
+
if (annotationsToRemove.length > 0) {
|
|
2054
|
+
let removals = [];
|
|
2055
|
+
for (const removal of annotationsToRemove) {
|
|
2056
|
+
const candidate = {
|
|
2057
|
+
messageId,
|
|
2058
|
+
annotationsToRemove: [...removals, removal],
|
|
2059
|
+
};
|
|
2060
|
+
if (new TextEncoder().encode(JSON.stringify(candidate)).byteLength <=
|
|
2061
|
+
MAX_THREAD_SNAPSHOT_ANNOTATION_REPLACEMENT_BYTES) {
|
|
2062
|
+
removals.push(removal);
|
|
2063
|
+
continue;
|
|
2064
|
+
}
|
|
2065
|
+
if (removals.length === 0) {
|
|
2066
|
+
throw new RangeError("An annotation removal exceeds the persistence request size limit.");
|
|
2067
|
+
}
|
|
2068
|
+
annotationMessageIdsToReplace.push({
|
|
2069
|
+
messageId,
|
|
2070
|
+
annotationsToRemove: removals,
|
|
2071
|
+
});
|
|
2072
|
+
removals = [removal];
|
|
2073
|
+
const singleRemoval = { messageId, annotationsToRemove: removals };
|
|
2074
|
+
if (new TextEncoder().encode(JSON.stringify(singleRemoval)).byteLength >
|
|
2075
|
+
MAX_THREAD_SNAPSHOT_ANNOTATION_REPLACEMENT_BYTES) {
|
|
2076
|
+
throw new RangeError("An annotation removal exceeds the persistence request size limit.");
|
|
2077
|
+
}
|
|
2078
|
+
}
|
|
2079
|
+
if (removals.length > 0) {
|
|
2080
|
+
annotationMessageIdsToReplace.push({
|
|
2081
|
+
messageId,
|
|
2082
|
+
annotationsToRemove: removals,
|
|
2083
|
+
});
|
|
2084
|
+
}
|
|
2085
|
+
}
|
|
2086
|
+
changedAnnotations.push(...changedIncoming);
|
|
2087
|
+
}
|
|
2088
|
+
const updates = [
|
|
2089
|
+
...changedMessages.map((entry) => ({ key: "messages", entry })),
|
|
2090
|
+
...changedWidgets.map((entry) => ({ key: "widgets", entry })),
|
|
2091
|
+
...changedToolCalls.map((entry) => ({ key: "toolCalls", entry })),
|
|
2092
|
+
...changedEventUpdates,
|
|
2093
|
+
...changedAnnotations.map((entry) => ({
|
|
2094
|
+
key: "annotationUpserts",
|
|
2095
|
+
entry,
|
|
2096
|
+
})),
|
|
2097
|
+
...annotationMessageIdsToReplace.map((entry) => ({
|
|
2098
|
+
key: "annotationMessageIdsToReplace",
|
|
2099
|
+
entry,
|
|
2100
|
+
})),
|
|
2101
|
+
...changedRuns.map((entry) => ({
|
|
2102
|
+
key: "runs",
|
|
2103
|
+
entry,
|
|
2104
|
+
...(shouldPersistLatestRunSuggestions &&
|
|
2105
|
+
asRecord(entry)?.id === latestRunId
|
|
2106
|
+
? { includeSuggestions: true }
|
|
2107
|
+
: {}),
|
|
2108
|
+
})),
|
|
2109
|
+
];
|
|
2110
|
+
const fixedAgentKit = {
|
|
2111
|
+
_snapshotDelta: true,
|
|
2112
|
+
...(mergeRootMessages ? { _mergeRootMessages: true } : {}),
|
|
2113
|
+
messages: [],
|
|
2114
|
+
widgets: [],
|
|
2115
|
+
toolCalls: [],
|
|
2116
|
+
events: [],
|
|
2117
|
+
eventRunSnapshotBatches: [],
|
|
2118
|
+
annotationMessageIdsToReplace: [],
|
|
2119
|
+
annotations: [],
|
|
2120
|
+
annotationUpserts: [],
|
|
2121
|
+
runs: [],
|
|
1623
2122
|
};
|
|
1624
2123
|
const requestHeaders = await headers({ sessionId: input.threadId });
|
|
1625
2124
|
requestHeaders.set("content-type", "application/json");
|
|
1626
|
-
const
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
2125
|
+
const bodyFor = (entries) => {
|
|
2126
|
+
const agentKit = { ...fixedAgentKit };
|
|
2127
|
+
for (const key of [
|
|
2128
|
+
"messages",
|
|
2129
|
+
"widgets",
|
|
2130
|
+
"toolCalls",
|
|
2131
|
+
"events",
|
|
2132
|
+
"eventRunSnapshotBatches",
|
|
2133
|
+
"annotationMessageIdsToReplace",
|
|
2134
|
+
"annotations",
|
|
2135
|
+
"annotationUpserts",
|
|
2136
|
+
"runs",
|
|
2137
|
+
]) {
|
|
2138
|
+
agentKit[key] = [];
|
|
2139
|
+
}
|
|
2140
|
+
const eventSnapshotBatchRunIds = new Set();
|
|
2141
|
+
const completedEventRunIds = new Set(entries.flatMap((update) => update.key === "events" &&
|
|
2142
|
+
update.completesEventRunSnapshot &&
|
|
2143
|
+
update.eventRunSnapshotBatch
|
|
2144
|
+
? [update.eventRunSnapshotBatch.runId]
|
|
2145
|
+
: []));
|
|
2146
|
+
for (const { key, entry, eventRunSnapshotBatch, includeSuggestions, } of entries) {
|
|
2147
|
+
if (key === "events") {
|
|
2148
|
+
agentKit.events.push(entry);
|
|
2149
|
+
if (eventRunSnapshotBatch &&
|
|
2150
|
+
!eventSnapshotBatchRunIds.has(eventRunSnapshotBatch.runId)) {
|
|
2151
|
+
eventSnapshotBatchRunIds.add(eventRunSnapshotBatch.runId);
|
|
2152
|
+
agentKit.eventRunSnapshotBatches.push({
|
|
2153
|
+
...eventRunSnapshotBatch,
|
|
2154
|
+
complete: completedEventRunIds.has(eventRunSnapshotBatch.runId),
|
|
2155
|
+
});
|
|
2156
|
+
}
|
|
2157
|
+
continue;
|
|
2158
|
+
}
|
|
2159
|
+
if (key === "runs") {
|
|
2160
|
+
agentKit.runs.push(entry);
|
|
2161
|
+
if (includeSuggestions && input.snapshot.suggestions !== undefined) {
|
|
2162
|
+
agentKit.suggestions = input.snapshot.suggestions;
|
|
2163
|
+
}
|
|
2164
|
+
}
|
|
2165
|
+
else {
|
|
2166
|
+
agentKit[key].push(entry);
|
|
2167
|
+
}
|
|
2168
|
+
}
|
|
2169
|
+
return threadSnapshotBody({
|
|
2170
|
+
stored,
|
|
2171
|
+
title: input.snapshot.title,
|
|
2172
|
+
snapshotMessages,
|
|
2173
|
+
agentKit,
|
|
2174
|
+
});
|
|
2175
|
+
};
|
|
2176
|
+
const chunks = [];
|
|
2177
|
+
let chunk = [];
|
|
2178
|
+
const encoder = new TextEncoder();
|
|
2179
|
+
const baseBodyBytes = encoder.encode(bodyFor([])).byteLength;
|
|
2180
|
+
let chunkBytes = baseBodyBytes;
|
|
2181
|
+
const chunkKeyCounts = new Map();
|
|
2182
|
+
for (const update of updates) {
|
|
2183
|
+
const duplicateAnnotationRemoval = update.key === "annotationMessageIdsToReplace" &&
|
|
2184
|
+
chunk.some((entry) => entry.key === "annotationMessageIdsToReplace" &&
|
|
2185
|
+
asRecord(entry.entry)?.messageId ===
|
|
2186
|
+
asRecord(update.entry)?.messageId);
|
|
2187
|
+
if (duplicateAnnotationRemoval) {
|
|
2188
|
+
chunks.push(chunk);
|
|
2189
|
+
chunk = [];
|
|
2190
|
+
chunkBytes = baseBodyBytes;
|
|
2191
|
+
chunkKeyCounts.clear();
|
|
2192
|
+
}
|
|
2193
|
+
const entryBytes = encoder.encode(bodyFor([update])).byteLength - baseBodyBytes;
|
|
2194
|
+
const addition = entryBytes + (chunkKeyCounts.has(update.key) ? 1 : 0);
|
|
2195
|
+
if (chunkBytes + addition > MAX_THREAD_SNAPSHOT_REQUEST_BYTES) {
|
|
2196
|
+
if (chunk.length === 0) {
|
|
2197
|
+
throw new RangeError("An Agent chat snapshot entry exceeds the persistence request size limit.");
|
|
2198
|
+
}
|
|
2199
|
+
chunks.push(chunk);
|
|
2200
|
+
chunk = [];
|
|
2201
|
+
chunkBytes = baseBodyBytes;
|
|
2202
|
+
chunkKeyCounts.clear();
|
|
2203
|
+
}
|
|
2204
|
+
const firstEntryBytes = entryBytes + (chunkKeyCounts.has(update.key) ? 1 : 0);
|
|
2205
|
+
if (chunkBytes + firstEntryBytes > MAX_THREAD_SNAPSHOT_REQUEST_BYTES) {
|
|
2206
|
+
throw new RangeError("An Agent chat snapshot entry exceeds the persistence request size limit.");
|
|
2207
|
+
}
|
|
2208
|
+
chunk.push(update);
|
|
2209
|
+
chunkBytes += firstEntryBytes;
|
|
2210
|
+
chunkKeyCounts.set(update.key, (chunkKeyCounts.get(update.key) ?? 0) + 1);
|
|
2211
|
+
}
|
|
2212
|
+
if (chunk.length > 0 || chunks.length === 0)
|
|
2213
|
+
chunks.push(chunk);
|
|
2214
|
+
const threadUrl = scopedThreadEndpoint(apiUrl + "/threads/" + encodeURIComponent(input.threadId), options);
|
|
2215
|
+
const persistChunk = async (entries) => {
|
|
2216
|
+
const body = bodyFor(entries);
|
|
2217
|
+
if (encoder.encode(body).byteLength > MAX_THREAD_SNAPSHOT_REQUEST_BYTES) {
|
|
2218
|
+
if (entries.length > 1) {
|
|
2219
|
+
const middle = Math.floor(entries.length / 2);
|
|
2220
|
+
return [
|
|
2221
|
+
...(await persistChunk(entries.slice(0, middle))),
|
|
2222
|
+
...(await persistChunk(entries.slice(middle))),
|
|
2223
|
+
];
|
|
2224
|
+
}
|
|
2225
|
+
throw new RangeError("An Agent chat snapshot entry exceeds the persistence request size limit.");
|
|
2226
|
+
}
|
|
2227
|
+
for (let attempt = 0;; attempt += 1) {
|
|
2228
|
+
const retryAfterReadFailure = async (error) => {
|
|
2229
|
+
if (asRecord(error)?.name === "AbortError" ||
|
|
2230
|
+
attempt >= MAX_THREAD_SNAPSHOT_RETRIES) {
|
|
2231
|
+
throw error;
|
|
2232
|
+
}
|
|
2233
|
+
await new Promise((resolve) => setTimeout(resolve, 100 * 2 ** attempt));
|
|
2234
|
+
};
|
|
2235
|
+
let response;
|
|
2236
|
+
try {
|
|
2237
|
+
response = await fetcher(threadUrl, {
|
|
2238
|
+
method: "PUT",
|
|
2239
|
+
headers: requestHeaders,
|
|
2240
|
+
body,
|
|
2241
|
+
});
|
|
2242
|
+
}
|
|
2243
|
+
catch (error) {
|
|
2244
|
+
await retryAfterReadFailure(error);
|
|
2245
|
+
continue;
|
|
2246
|
+
}
|
|
2247
|
+
if (response.status === 413 && entries.length > 1) {
|
|
2248
|
+
const middle = Math.floor(entries.length / 2);
|
|
2249
|
+
return [
|
|
2250
|
+
...(await persistChunk(entries.slice(0, middle))),
|
|
2251
|
+
...(await persistChunk(entries.slice(middle))),
|
|
2252
|
+
];
|
|
2253
|
+
}
|
|
2254
|
+
if (!response.ok) {
|
|
2255
|
+
let error;
|
|
2256
|
+
try {
|
|
2257
|
+
error = await responseError(response);
|
|
2258
|
+
}
|
|
2259
|
+
catch (readError) {
|
|
2260
|
+
await retryAfterReadFailure(readError);
|
|
2261
|
+
continue;
|
|
2262
|
+
}
|
|
2263
|
+
if (asRecord(error)?.retryable !== true ||
|
|
2264
|
+
attempt >= MAX_THREAD_SNAPSHOT_RETRIES) {
|
|
2265
|
+
throw error;
|
|
2266
|
+
}
|
|
2267
|
+
await new Promise((resolve) => setTimeout(resolve, 100 * 2 ** attempt));
|
|
2268
|
+
continue;
|
|
2269
|
+
}
|
|
2270
|
+
try {
|
|
2271
|
+
return snapshotAnnotationConflicts(await response.json());
|
|
2272
|
+
}
|
|
2273
|
+
catch (readError) {
|
|
2274
|
+
await retryAfterReadFailure(readError);
|
|
2275
|
+
}
|
|
2276
|
+
}
|
|
2277
|
+
};
|
|
2278
|
+
const annotationConflicts = new Map();
|
|
2279
|
+
for (const entries of chunks) {
|
|
2280
|
+
for (const conflict of await persistChunk(entries)) {
|
|
2281
|
+
annotationConflicts.set(JSON.stringify([
|
|
2282
|
+
conflict.messageId,
|
|
2283
|
+
conflict.annotationId ?? null,
|
|
2284
|
+
conflict.operation,
|
|
2285
|
+
]), conflict);
|
|
2286
|
+
}
|
|
2287
|
+
}
|
|
2288
|
+
if (annotationConflicts.size > 0) {
|
|
2289
|
+
const error = new Error("An annotation changed in another save. The latest saved value was preserved; reload the thread before retrying.");
|
|
2290
|
+
error.name = "AgentAnnotationSaveConflictError";
|
|
2291
|
+
throw error;
|
|
2292
|
+
}
|
|
1641
2293
|
}
|
|
1642
2294
|
async function persistQueueMutation(threadId, mutation) {
|
|
1643
2295
|
const requestHeaders = await headers({ sessionId: threadId });
|
|
@@ -1707,7 +2359,11 @@ export function createAgentNativeAgentKitTransport(options = {}) {
|
|
|
1707
2359
|
}
|
|
1708
2360
|
if (lastError)
|
|
1709
2361
|
throw lastError;
|
|
1710
|
-
|
|
2362
|
+
const activeRunId = typeof status?.runId === "string" ? status.runId : undefined;
|
|
2363
|
+
const localRun = activeRunId
|
|
2364
|
+
? await protocolTransport.getRun?.({ threadId, runId: activeRunId })
|
|
2365
|
+
: null;
|
|
2366
|
+
throw new AgentKitRunSlotBusyError(localRun?.id ?? activeRunId);
|
|
1711
2367
|
}
|
|
1712
2368
|
async function startRunTrackingRunningState(input, context) {
|
|
1713
2369
|
dispatchAgentChatRunning({
|
|
@@ -1777,8 +2433,6 @@ export function createAgentNativeAgentKitTransport(options = {}) {
|
|
|
1777
2433
|
connectionRequests: true,
|
|
1778
2434
|
},
|
|
1779
2435
|
operations: {
|
|
1780
|
-
...options.operations,
|
|
1781
|
-
persistThreadSnapshot: options.operations?.persistThreadSnapshot ?? persistThreadSnapshot,
|
|
1782
2436
|
getThread: async ({ threadId }) => {
|
|
1783
2437
|
const thread = await snapshot(threadId);
|
|
1784
2438
|
if (!thread)
|
|
@@ -1933,8 +2587,8 @@ export function createAgentNativeAgentKitTransport(options = {}) {
|
|
|
1933
2587
|
if (fromMessageId) {
|
|
1934
2588
|
const repository = storedRepository(source);
|
|
1935
2589
|
const agentKit = asRecord(repository.agentKit);
|
|
1936
|
-
const sourceMessages =
|
|
1937
|
-
?
|
|
2590
|
+
const sourceMessages = agentKit
|
|
2591
|
+
? projectThread(threadId, source).messages
|
|
1938
2592
|
: repository.messages;
|
|
1939
2593
|
if (!Array.isArray(sourceMessages)) {
|
|
1940
2594
|
throw new Error("The Agent-Native thread cannot be forked from a message without durable history.");
|
|
@@ -2026,6 +2680,7 @@ export function createAgentNativeAgentKitTransport(options = {}) {
|
|
|
2026
2680
|
trackRunFeedback({ runId, threadId, positive: value === "positive" });
|
|
2027
2681
|
},
|
|
2028
2682
|
...options.operations,
|
|
2683
|
+
persistThreadSnapshot: options.operations?.persistThreadSnapshot ?? persistThreadSnapshot,
|
|
2029
2684
|
},
|
|
2030
2685
|
});
|
|
2031
2686
|
const protocolStartRun = protocolTransport.startRun.bind(protocolTransport);
|
|
@@ -2066,7 +2721,16 @@ export function createAgentNativeAgentKitTransport(options = {}) {
|
|
|
2066
2721
|
}
|
|
2067
2722
|
catch (error) {
|
|
2068
2723
|
const record = asRecord(error);
|
|
2069
|
-
const
|
|
2724
|
+
const serverActiveRunId = typeof record?.activeRunId === "string"
|
|
2725
|
+
? record.activeRunId
|
|
2726
|
+
: undefined;
|
|
2727
|
+
const localRun = serverActiveRunId
|
|
2728
|
+
? await protocolTransport.getRun?.({
|
|
2729
|
+
threadId: input.threadId,
|
|
2730
|
+
runId: serverActiveRunId,
|
|
2731
|
+
})
|
|
2732
|
+
: null;
|
|
2733
|
+
const activeRunId = localRun?.id ?? serverActiveRunId;
|
|
2070
2734
|
const explicitNonSlotCode = typeof record?.code === "string" &&
|
|
2071
2735
|
record.code !== "run_slot_busy" &&
|
|
2072
2736
|
record.code !== "http_409";
|
|
@@ -2095,9 +2759,95 @@ export function createAgentNativeAgentKitTransport(options = {}) {
|
|
|
2095
2759
|
runId: input.runId,
|
|
2096
2760
|
});
|
|
2097
2761
|
const assistantMessageIds = new Set();
|
|
2762
|
+
// Continuation runs keep the durable identity of their folded reply.
|
|
2763
|
+
let durableAssistantMessageId = durableAssistantMessageIdsByRun.get(input.runId) ?? undefined;
|
|
2764
|
+
let foldedAssistantSourceMessageId;
|
|
2765
|
+
const foldedMessageIdFor = (sourceMessageId) => {
|
|
2766
|
+
if (!durableAssistantMessageId || !sourceMessageId)
|
|
2767
|
+
return undefined;
|
|
2768
|
+
foldedAssistantSourceMessageId ??= sourceMessageId;
|
|
2769
|
+
return foldedAssistantSourceMessageId === sourceMessageId
|
|
2770
|
+
? durableAssistantMessageId
|
|
2771
|
+
: undefined;
|
|
2772
|
+
};
|
|
2098
2773
|
let responseStarted = false;
|
|
2099
2774
|
let turnId;
|
|
2100
|
-
for await (const
|
|
2775
|
+
for await (const sourceEvent of subscribeToRun(input)) {
|
|
2776
|
+
const interruptedRunId = protocolInterruptedRunId(sourceEvent.metadata);
|
|
2777
|
+
const inheritedMessageId = interruptedRunId
|
|
2778
|
+
? (durableAssistantMessageIdsByRun.get(interruptedRunId) ??
|
|
2779
|
+
assistantHistoryMessageIdsByRun.get(interruptedRunId))
|
|
2780
|
+
: undefined;
|
|
2781
|
+
if (inheritedMessageId) {
|
|
2782
|
+
if (durableAssistantMessageId !== inheritedMessageId) {
|
|
2783
|
+
foldedAssistantSourceMessageId = undefined;
|
|
2784
|
+
}
|
|
2785
|
+
durableAssistantMessageId = inheritedMessageId;
|
|
2786
|
+
durableAssistantMessageIdsByRun.set(input.runId, inheritedMessageId);
|
|
2787
|
+
}
|
|
2788
|
+
let event = sourceEvent;
|
|
2789
|
+
if (isAgentNativeChatRuntime(runtime)) {
|
|
2790
|
+
switch (sourceEvent.type) {
|
|
2791
|
+
case "message.created":
|
|
2792
|
+
case "message.completed":
|
|
2793
|
+
if (sourceEvent.message.role === "assistant") {
|
|
2794
|
+
const messageId = foldedMessageIdFor(sourceEvent.message.id);
|
|
2795
|
+
if (messageId && messageId !== sourceEvent.message.id) {
|
|
2796
|
+
event = {
|
|
2797
|
+
...sourceEvent,
|
|
2798
|
+
message: { ...sourceEvent.message, id: messageId },
|
|
2799
|
+
};
|
|
2800
|
+
}
|
|
2801
|
+
}
|
|
2802
|
+
break;
|
|
2803
|
+
case "message.delta":
|
|
2804
|
+
case "reasoning.delta": {
|
|
2805
|
+
const messageId = foldedMessageIdFor(sourceEvent.messageId);
|
|
2806
|
+
if (messageId && messageId !== sourceEvent.messageId) {
|
|
2807
|
+
event = { ...sourceEvent, messageId };
|
|
2808
|
+
}
|
|
2809
|
+
break;
|
|
2810
|
+
}
|
|
2811
|
+
case "tool.started":
|
|
2812
|
+
case "tool.updated": {
|
|
2813
|
+
if (sourceEvent.toolCall.messageId) {
|
|
2814
|
+
const messageId = foldedMessageIdFor(sourceEvent.toolCall.messageId);
|
|
2815
|
+
if (messageId && messageId !== sourceEvent.toolCall.messageId) {
|
|
2816
|
+
event = {
|
|
2817
|
+
...sourceEvent,
|
|
2818
|
+
toolCall: { ...sourceEvent.toolCall, messageId },
|
|
2819
|
+
};
|
|
2820
|
+
}
|
|
2821
|
+
}
|
|
2822
|
+
break;
|
|
2823
|
+
}
|
|
2824
|
+
case "widget.created":
|
|
2825
|
+
case "widget.updated":
|
|
2826
|
+
case "annotation.created":
|
|
2827
|
+
case "annotation.updated": {
|
|
2828
|
+
if (sourceEvent.messageId) {
|
|
2829
|
+
const messageId = foldedMessageIdFor(sourceEvent.messageId);
|
|
2830
|
+
if (messageId && messageId !== sourceEvent.messageId) {
|
|
2831
|
+
event = { ...sourceEvent, messageId };
|
|
2832
|
+
}
|
|
2833
|
+
}
|
|
2834
|
+
break;
|
|
2835
|
+
}
|
|
2836
|
+
case "action.started": {
|
|
2837
|
+
if (sourceEvent.invocation.messageId) {
|
|
2838
|
+
const messageId = foldedMessageIdFor(sourceEvent.invocation.messageId);
|
|
2839
|
+
if (messageId &&
|
|
2840
|
+
messageId !== sourceEvent.invocation.messageId) {
|
|
2841
|
+
event = {
|
|
2842
|
+
...sourceEvent,
|
|
2843
|
+
invocation: { ...sourceEvent.invocation, messageId },
|
|
2844
|
+
};
|
|
2845
|
+
}
|
|
2846
|
+
}
|
|
2847
|
+
break;
|
|
2848
|
+
}
|
|
2849
|
+
}
|
|
2850
|
+
}
|
|
2101
2851
|
turnId ??= protocolTurnId(event.metadata);
|
|
2102
2852
|
if (event.type === "run.started" && turnId) {
|
|
2103
2853
|
dispatchAgentChatRunning({
|