@agent-native/core 0.207.0 → 0.207.1-nightly-20261012011809
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/thread-data-builder.d.ts +1 -0
- package/dist/agent/thread-data-builder.js +220 -45
- package/dist/agent/thread-message-projection.d.ts +3 -0
- package/dist/agent/thread-message-projection.js +136 -3
- package/dist/automation/index.d.ts +1 -1
- package/dist/chat-threads/store.d.ts +1 -0
- package/dist/chat-threads/store.js +14 -4
- package/dist/cli/connect.d.ts +1 -0
- package/dist/cli/connect.js +8 -3
- package/dist/client/chat/agentkit-agent-native.js +151 -77
- package/dist/client/chat/runtime.js +61 -2
- package/dist/client/onboarding/first-run-status.js +0 -4
- package/dist/collab/routes.d.ts +1 -1
- package/dist/file-upload/actions/upload-image.d.ts +1 -1
- package/dist/jobs/actions/manage-recurring-job.d.ts +1 -1
- package/dist/mcp/agent-connected.d.ts +13 -0
- package/dist/mcp/agent-connected.js +63 -0
- package/dist/mcp/connect-app-label.d.ts +11 -0
- package/dist/mcp/connect-app-label.js +33 -0
- package/dist/mcp/connect-route.js +41 -33
- package/dist/mcp/connect-store.d.ts +6 -1
- package/dist/mcp/connect-store.js +10 -2
- package/dist/mcp/oauth-route.js +17 -5
- package/dist/mcp/oauth-store.d.ts +2 -0
- package/dist/mcp/oauth-store.js +5 -1
- package/dist/notifications/routes.d.ts +1 -1
- package/dist/org/index.d.ts +2 -2
- package/dist/progress/routes.d.ts +1 -1
- package/dist/provider-api/actions/custom-provider-registration.d.ts +9 -9
- package/dist/provider-api/actions/provider-api.d.ts +11 -11
- package/dist/provider-api/corpus-jobs.d.ts +2 -2
- package/dist/resources/handlers.d.ts +1 -1
- package/dist/secrets/routes.d.ts +5 -5
- package/dist/server/agent-chat-plugin.js +1 -1
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/server/agent-engine-default-model-route.d.ts +2 -2
- package/dist/server/core-routes-plugin.d.ts +1 -1
- package/dist/shared/mcp-connect-content.d.ts +1 -0
- package/dist/shared/mcp-connect-content.js +3 -0
- package/dist/sharing/actions/list-resource-shares.d.ts +1 -1
- package/dist/tracking/registry.d.ts +1 -0
- package/dist/tracking/registry.js +1 -1
- package/dist/triggers/actions/manage-automation.d.ts +7 -7
- package/docs/content/agent-native-toolkit.mdx +42 -38
- package/docs/content/components.mdx +29 -18
- package/docs/content/notifications.mdx +1 -1
- package/docs/content/progress.mdx +1 -1
- package/docs/content/sharing.mdx +1 -1
- package/docs/content/toolkit-agent-ux.mdx +17 -2
- package/docs/content/toolkit-command-navigation.mdx +4 -2
- package/docs/content/toolkit-comments-review.mdx +1 -1
- package/docs/content/toolkit-context-knowledge.mdx +14 -16
- package/docs/content/toolkit-editors-canvases.mdx +24 -7
- package/docs/content/toolkit-history.mdx +13 -26
- package/docs/content/toolkit-org-team.mdx +4 -4
- package/docs/content/toolkit-resources.mdx +3 -1
- package/docs/content/toolkit-settings.mdx +1 -1
- package/docs/content/toolkit-setup-connections.mdx +1 -1
- package/docs/content/toolkit-sharing.mdx +6 -6
- package/docs/content/toolkit-ui.mdx +11 -0
- package/package.json +4 -4
|
@@ -105,6 +105,7 @@ export interface MergeThreadDataOptions {
|
|
|
105
105
|
preserveExistingQueuedMessages?: boolean;
|
|
106
106
|
preserveExistingTopLevelKeys?: boolean;
|
|
107
107
|
onAnnotationConflict?: (conflict: ThreadAnnotationSnapshotConflict) => void;
|
|
108
|
+
trustedRunSequenceResetIds?: ReadonlySet<string>;
|
|
108
109
|
}
|
|
109
110
|
export interface ThreadAnnotationSnapshotConflict {
|
|
110
111
|
messageId: string;
|
|
@@ -1582,13 +1582,18 @@ export function foldThreadRunSuggestions(repo, run) {
|
|
|
1582
1582
|
? previous.runs
|
|
1583
1583
|
: [];
|
|
1584
1584
|
const oldRun = runs.find((entry) => entry.id === run.runId);
|
|
1585
|
-
const
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1585
|
+
const committedSnapshots = previous._eventRunSnapshotCommits?.[run.runId];
|
|
1586
|
+
const protocolSequences = [
|
|
1587
|
+
previous._eventRunWatermarks?.[run.runId],
|
|
1588
|
+
...(Array.isArray(committedSnapshots)
|
|
1589
|
+
? committedSnapshots.map((snapshot) => snapshot?.lastSequence)
|
|
1590
|
+
: []),
|
|
1591
|
+
];
|
|
1592
|
+
const lastSequence = protocolSequences.reduce((maximum, sequence) => typeof sequence === "number" &&
|
|
1593
|
+
Number.isSafeInteger(sequence) &&
|
|
1594
|
+
sequence >= 0
|
|
1595
|
+
? Math.max(maximum, sequence)
|
|
1596
|
+
: maximum, 0);
|
|
1592
1597
|
const snapshot = {
|
|
1593
1598
|
...oldRun,
|
|
1594
1599
|
id: run.runId,
|
|
@@ -1653,6 +1658,7 @@ function mergeAgentKitEvents(existing, incoming, replacedRunIds = new Set(), sta
|
|
|
1653
1658
|
Number.isFinite(merged[index].sequence)
|
|
1654
1659
|
? merged[index].sequence
|
|
1655
1660
|
: Number.NaN,
|
|
1661
|
+
sequenceEnd: getAgentKitEventSequenceEnd(merged[index]),
|
|
1656
1662
|
}));
|
|
1657
1663
|
runEvents.sort((left, right) => {
|
|
1658
1664
|
if (Number.isFinite(left.sequence) &&
|
|
@@ -1662,12 +1668,142 @@ function mergeAgentKitEvents(existing, incoming, replacedRunIds = new Set(), sta
|
|
|
1662
1668
|
}
|
|
1663
1669
|
return left.order - right.order;
|
|
1664
1670
|
});
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1671
|
+
let nextSequence = 1;
|
|
1672
|
+
for (const [order, { event, sequenceEnd, sequence },] of runEvents.entries()) {
|
|
1673
|
+
const index = runPositions[order];
|
|
1674
|
+
if (index === undefined)
|
|
1675
|
+
continue;
|
|
1676
|
+
const span = Number.isSafeInteger(sequenceEnd) &&
|
|
1677
|
+
Number.isSafeInteger(sequence) &&
|
|
1678
|
+
sequenceEnd >= sequence
|
|
1679
|
+
? sequenceEnd - sequence + 1
|
|
1680
|
+
: 1;
|
|
1681
|
+
if (event?.type === "x-agent-native.persisted-event-omission-range") {
|
|
1682
|
+
const payload = event.payload &&
|
|
1683
|
+
typeof event.payload === "object" &&
|
|
1684
|
+
!Array.isArray(event.payload)
|
|
1685
|
+
? event.payload
|
|
1686
|
+
: {};
|
|
1687
|
+
merged[index] = {
|
|
1688
|
+
...event,
|
|
1689
|
+
sequence: nextSequence,
|
|
1690
|
+
payload: {
|
|
1691
|
+
...payload,
|
|
1692
|
+
lastSequence: nextSequence + span - 1,
|
|
1693
|
+
},
|
|
1694
|
+
};
|
|
1695
|
+
}
|
|
1696
|
+
else {
|
|
1697
|
+
merged[index] = { ...event, sequence: nextSequence };
|
|
1698
|
+
}
|
|
1699
|
+
nextSequence += span;
|
|
1700
|
+
}
|
|
1668
1701
|
}
|
|
1669
1702
|
return merged;
|
|
1670
1703
|
}
|
|
1704
|
+
function mergeAgentKitEventSnapshotBatch(existing, incoming) {
|
|
1705
|
+
const merged = Array.isArray(existing) ? [...existing] : [];
|
|
1706
|
+
const positions = new Map();
|
|
1707
|
+
merged.forEach((event, index) => {
|
|
1708
|
+
if (typeof event?.id === "string" && !positions.has(event.id)) {
|
|
1709
|
+
positions.set(event.id, index);
|
|
1710
|
+
}
|
|
1711
|
+
});
|
|
1712
|
+
for (const event of Array.isArray(incoming) ? incoming : []) {
|
|
1713
|
+
if (typeof event?.id !== "string") {
|
|
1714
|
+
merged.push(event);
|
|
1715
|
+
continue;
|
|
1716
|
+
}
|
|
1717
|
+
const index = positions.get(event.id);
|
|
1718
|
+
if (index === undefined) {
|
|
1719
|
+
positions.set(event.id, merged.length);
|
|
1720
|
+
merged.push(event);
|
|
1721
|
+
}
|
|
1722
|
+
else {
|
|
1723
|
+
merged[index] = event;
|
|
1724
|
+
}
|
|
1725
|
+
}
|
|
1726
|
+
return merged
|
|
1727
|
+
.map((event, order) => ({
|
|
1728
|
+
event,
|
|
1729
|
+
order,
|
|
1730
|
+
sequence: typeof event?.sequence === "number" && Number.isFinite(event.sequence)
|
|
1731
|
+
? event.sequence
|
|
1732
|
+
: Number.NaN,
|
|
1733
|
+
}))
|
|
1734
|
+
.sort((left, right) => {
|
|
1735
|
+
if (Number.isFinite(left.sequence) &&
|
|
1736
|
+
Number.isFinite(right.sequence) &&
|
|
1737
|
+
left.sequence !== right.sequence) {
|
|
1738
|
+
return left.sequence - right.sequence;
|
|
1739
|
+
}
|
|
1740
|
+
return left.order - right.order;
|
|
1741
|
+
})
|
|
1742
|
+
.map(({ event }) => event);
|
|
1743
|
+
}
|
|
1744
|
+
const MAX_AGENT_NATIVE_PERSISTED_EVENT_OMISSION_RANGE = 256;
|
|
1745
|
+
function getAgentKitEventSequence(event) {
|
|
1746
|
+
if (!event || typeof event !== "object" || Array.isArray(event)) {
|
|
1747
|
+
return undefined;
|
|
1748
|
+
}
|
|
1749
|
+
const sequence = event.sequence;
|
|
1750
|
+
return typeof sequence === "number" && Number.isSafeInteger(sequence)
|
|
1751
|
+
? sequence
|
|
1752
|
+
: undefined;
|
|
1753
|
+
}
|
|
1754
|
+
function getAgentKitEventSequenceEnd(event) {
|
|
1755
|
+
const sequence = getAgentKitEventSequence(event);
|
|
1756
|
+
if (sequence === undefined)
|
|
1757
|
+
return undefined;
|
|
1758
|
+
if (!event ||
|
|
1759
|
+
typeof event !== "object" ||
|
|
1760
|
+
event.type !==
|
|
1761
|
+
"x-agent-native.persisted-event-omission-range") {
|
|
1762
|
+
return sequence;
|
|
1763
|
+
}
|
|
1764
|
+
const payload = event.payload;
|
|
1765
|
+
if (!payload || typeof payload !== "object" || Array.isArray(payload)) {
|
|
1766
|
+
return undefined;
|
|
1767
|
+
}
|
|
1768
|
+
const lastSequence = payload.lastSequence;
|
|
1769
|
+
return typeof lastSequence === "number" &&
|
|
1770
|
+
Number.isSafeInteger(lastSequence) &&
|
|
1771
|
+
lastSequence >= sequence &&
|
|
1772
|
+
lastSequence - sequence < MAX_AGENT_NATIVE_PERSISTED_EVENT_OMISSION_RANGE
|
|
1773
|
+
? lastSequence
|
|
1774
|
+
: undefined;
|
|
1775
|
+
}
|
|
1776
|
+
function hasContiguousAgentKitEventCoverage(events, runId, lastSequence, firstSequence = 1) {
|
|
1777
|
+
const runEvents = events
|
|
1778
|
+
.filter((event) => event !== null &&
|
|
1779
|
+
typeof event === "object" &&
|
|
1780
|
+
!Array.isArray(event) &&
|
|
1781
|
+
event.runId === runId)
|
|
1782
|
+
.map((event, order) => ({
|
|
1783
|
+
event,
|
|
1784
|
+
order,
|
|
1785
|
+
sequence: getAgentKitEventSequence(event),
|
|
1786
|
+
sequenceEnd: getAgentKitEventSequenceEnd(event),
|
|
1787
|
+
}))
|
|
1788
|
+
.sort((left, right) => {
|
|
1789
|
+
if (left.sequence !== undefined &&
|
|
1790
|
+
right.sequence !== undefined &&
|
|
1791
|
+
left.sequence !== right.sequence) {
|
|
1792
|
+
return left.sequence - right.sequence;
|
|
1793
|
+
}
|
|
1794
|
+
return left.order - right.order;
|
|
1795
|
+
});
|
|
1796
|
+
let nextSequence = firstSequence;
|
|
1797
|
+
for (const event of runEvents) {
|
|
1798
|
+
if (event.sequence !== nextSequence ||
|
|
1799
|
+
event.sequenceEnd === undefined ||
|
|
1800
|
+
event.sequenceEnd < event.sequence) {
|
|
1801
|
+
return false;
|
|
1802
|
+
}
|
|
1803
|
+
nextSequence = event.sequenceEnd + 1;
|
|
1804
|
+
}
|
|
1805
|
+
return nextSequence === lastSequence + 1;
|
|
1806
|
+
}
|
|
1671
1807
|
function mergeAgentKitFullEventSnapshot(existingEvents, incomingEvents, incomingRuns, committedSnapshots) {
|
|
1672
1808
|
if (!Array.isArray(incomingEvents))
|
|
1673
1809
|
return undefined;
|
|
@@ -1715,16 +1851,7 @@ function mergeAgentKitFullEventSnapshot(existingEvents, incomingEvents, incoming
|
|
|
1715
1851
|
if (committedSequence < 0)
|
|
1716
1852
|
continue;
|
|
1717
1853
|
const runEvents = incomingEventsByRun.get(runId) ?? [];
|
|
1718
|
-
const incomingSequence = Math.max(incomingRunSequences.get(runId) ?? 0, ...runEvents.map((event) =>
|
|
1719
|
-
const sequence = event && typeof event === "object"
|
|
1720
|
-
? event.sequence
|
|
1721
|
-
: undefined;
|
|
1722
|
-
return typeof sequence === "number" &&
|
|
1723
|
-
Number.isSafeInteger(sequence) &&
|
|
1724
|
-
sequence >= 0
|
|
1725
|
-
? sequence
|
|
1726
|
-
: 0;
|
|
1727
|
-
}));
|
|
1854
|
+
const incomingSequence = Math.max(incomingRunSequences.get(runId) ?? 0, ...runEvents.map((event) => getAgentKitEventSequenceEnd(event) ?? 0));
|
|
1728
1855
|
if (runEvents.length === 0 || incomingSequence <= committedSequence) {
|
|
1729
1856
|
protectedRunIds.add(runId);
|
|
1730
1857
|
}
|
|
@@ -1807,11 +1934,24 @@ function mergeAgentKitEventRunSnapshots(input) {
|
|
|
1807
1934
|
const hasCommittedSnapshot = (commits[runId]?.length ?? 0) > 0;
|
|
1808
1935
|
const committedSequence = Math.max(0, ...(commits[runId] ?? []).map((entry) => entry.lastSequence));
|
|
1809
1936
|
const knownSequence = Math.max(storedWatermark ?? 0, storedRunSequence ?? 0, committedSequence);
|
|
1937
|
+
if (!input.batches.has(runId) && lastSequence > knownSequence) {
|
|
1938
|
+
const incomingEvents = Array.isArray(input.incomingEvents)
|
|
1939
|
+
? input.incomingEvents
|
|
1940
|
+
: [];
|
|
1941
|
+
const completeFromStart = hasContiguousAgentKitEventCoverage(incomingEvents, runId, lastSequence);
|
|
1942
|
+
const nextChunk = hasContiguousAgentKitEventCoverage(incomingEvents, runId, lastSequence, knownSequence + 1);
|
|
1943
|
+
if (!completeFromStart && !nextChunk) {
|
|
1944
|
+
throw new TypeError("Agent chat event snapshot did not contain a contiguous sequence range.");
|
|
1945
|
+
}
|
|
1946
|
+
}
|
|
1810
1947
|
const hasKnownSequence = storedWatermark !== undefined ||
|
|
1811
1948
|
storedRunSequence !== undefined ||
|
|
1812
1949
|
hasCommittedSnapshot;
|
|
1813
1950
|
const hasStoredEvents = previousEvents.some((event) => event?.runId === runId);
|
|
1814
1951
|
if ((hasKnownSequence && lastSequence < knownSequence) ||
|
|
1952
|
+
(hasKnownSequence &&
|
|
1953
|
+
lastSequence === knownSequence &&
|
|
1954
|
+
!input.incomingReplacements.has(runId)) ||
|
|
1815
1955
|
(hasCommittedSnapshot && lastSequence <= committedSequence)) {
|
|
1816
1956
|
staleRunIds.add(runId);
|
|
1817
1957
|
continue;
|
|
@@ -1896,8 +2036,7 @@ function mergeAgentKitEventRunSnapshots(input) {
|
|
|
1896
2036
|
const matchingCurrent = current?.lastSequence === batch.lastSequence &&
|
|
1897
2037
|
current.expectedEventCount === batch.expectedEventCount;
|
|
1898
2038
|
const batchEvents = incomingEvents.filter((event) => event?.runId === runId);
|
|
1899
|
-
const stagedEvents =
|
|
1900
|
-
[];
|
|
2039
|
+
const stagedEvents = mergeAgentKitEventSnapshotBatch(matchingCurrent ? current.events : [], batchEvents);
|
|
1901
2040
|
if (stagedEvents.length > batch.expectedEventCount) {
|
|
1902
2041
|
throw new TypeError("Agent chat event snapshot exceeded its event count.");
|
|
1903
2042
|
}
|
|
@@ -1905,6 +2044,18 @@ function mergeAgentKitEventRunSnapshots(input) {
|
|
|
1905
2044
|
throw new TypeError("Agent chat event snapshot ended before all events arrived.");
|
|
1906
2045
|
}
|
|
1907
2046
|
if (stagedEvents.length === batch.expectedEventCount) {
|
|
2047
|
+
const lastEventSequence = stagedEvents.reduce((maximum, event) => {
|
|
2048
|
+
const sequence = getAgentKitEventSequenceEnd(event);
|
|
2049
|
+
return sequence !== undefined && sequence >= 0
|
|
2050
|
+
? Math.max(maximum, sequence)
|
|
2051
|
+
: maximum;
|
|
2052
|
+
}, 0);
|
|
2053
|
+
if (batch.lastSequence !== lastEventSequence) {
|
|
2054
|
+
throw new TypeError(`Agent chat event snapshot cursor ${batch.lastSequence} did not match its last event sequence ${lastEventSequence}.`);
|
|
2055
|
+
}
|
|
2056
|
+
if (!hasContiguousAgentKitEventCoverage(stagedEvents, runId, batch.lastSequence)) {
|
|
2057
|
+
throw new TypeError("Agent chat event snapshot did not include every sequence through its cursor.");
|
|
2058
|
+
}
|
|
1908
2059
|
events = mergeAgentKitEvents(events, stagedEvents, new Set([runId]));
|
|
1909
2060
|
watermarks.set(runId, Math.max(knownSequence, batch.lastSequence));
|
|
1910
2061
|
commits[runId] = [
|
|
@@ -2051,7 +2202,7 @@ function mergeAgentKitAnnotations(existing, incoming, replaceMessageIds, removal
|
|
|
2051
2202
|
}
|
|
2052
2203
|
return merged;
|
|
2053
2204
|
}
|
|
2054
|
-
function mergeAgentKitHistory(existing, incoming, promptRunIds, onAnnotationConflict) {
|
|
2205
|
+
function mergeAgentKitHistory(existing, incoming, promptRunIds, onAnnotationConflict, trustedRunSequenceResetIds = new Set()) {
|
|
2055
2206
|
const incomingIsRecord = incoming !== null &&
|
|
2056
2207
|
typeof incoming === "object" &&
|
|
2057
2208
|
!Array.isArray(incoming);
|
|
@@ -2062,10 +2213,27 @@ function mergeAgentKitHistory(existing, incoming, promptRunIds, onAnnotationConf
|
|
|
2062
2213
|
typeof existing === "object" &&
|
|
2063
2214
|
!Array.isArray(existing);
|
|
2064
2215
|
const snapshotDelta = next._snapshotDelta === true;
|
|
2065
|
-
const
|
|
2066
|
-
|
|
2216
|
+
const incomingEvents = Array.isArray(next.events) ? next.events : [];
|
|
2217
|
+
for (const event of incomingEvents) {
|
|
2218
|
+
if (event?.type === "x-agent-native.persisted-event-omission-range" &&
|
|
2219
|
+
getAgentKitEventSequenceEnd(event) === undefined) {
|
|
2220
|
+
throw new TypeError(`Agent chat persisted omission ranges must cover at most ${MAX_AGENT_NATIVE_PERSISTED_EVENT_OMISSION_RANGE} event sequences.`);
|
|
2221
|
+
}
|
|
2222
|
+
}
|
|
2223
|
+
const incomingEventRunIds = new Set(incomingEvents.flatMap((event) => typeof event?.runId === "string" ? [event.runId] : []));
|
|
2224
|
+
const incomingRunStarts = new Set(incomingEvents.flatMap((event) => typeof event?.runId === "string" && event.sequence === 1
|
|
2067
2225
|
? [event.runId]
|
|
2068
2226
|
: []));
|
|
2227
|
+
const incomingEventSequences = new Map();
|
|
2228
|
+
for (const event of incomingEvents) {
|
|
2229
|
+
if (typeof event?.runId !== "string") {
|
|
2230
|
+
continue;
|
|
2231
|
+
}
|
|
2232
|
+
const sequenceEnd = getAgentKitEventSequenceEnd(event);
|
|
2233
|
+
if (sequenceEnd === undefined || sequenceEnd < 0)
|
|
2234
|
+
continue;
|
|
2235
|
+
incomingEventSequences.set(event.runId, Math.max(incomingEventSequences.get(event.runId) ?? 0, sequenceEnd));
|
|
2236
|
+
}
|
|
2069
2237
|
const eventRunSnapshotBatches = new Map();
|
|
2070
2238
|
for (const batch of Array.isArray(next.eventRunSnapshotBatches)
|
|
2071
2239
|
? next.eventRunSnapshotBatches
|
|
@@ -2091,27 +2259,25 @@ function mergeAgentKitHistory(existing, incoming, promptRunIds, onAnnotationConf
|
|
|
2091
2259
|
}
|
|
2092
2260
|
}
|
|
2093
2261
|
const eventRunSnapshotWatermarks = new Map();
|
|
2094
|
-
for (const
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
if (typeof watermark?.runId === "string" &&
|
|
2098
|
-
typeof watermark.lastSequence === "number" &&
|
|
2099
|
-
Number.isSafeInteger(watermark.lastSequence) &&
|
|
2100
|
-
watermark.lastSequence >= 0 &&
|
|
2101
|
-
incomingEventRunIds.has(watermark.runId) &&
|
|
2102
|
-
!eventRunSnapshotBatches.has(watermark.runId)) {
|
|
2103
|
-
eventRunSnapshotWatermarks.set(watermark.runId, Math.max(eventRunSnapshotWatermarks.get(watermark.runId) ?? 0, watermark.lastSequence));
|
|
2262
|
+
for (const [runId, lastSequence] of incomingEventSequences) {
|
|
2263
|
+
if (incomingEventRunIds.has(runId) && !eventRunSnapshotBatches.has(runId)) {
|
|
2264
|
+
eventRunSnapshotWatermarks.set(runId, lastSequence);
|
|
2104
2265
|
}
|
|
2105
2266
|
}
|
|
2106
2267
|
const eventRunReplacements = new Map();
|
|
2107
2268
|
for (const replacement of Array.isArray(next.eventRunReplacements)
|
|
2108
2269
|
? next.eventRunReplacements
|
|
2109
2270
|
: []) {
|
|
2271
|
+
const eventSequence = typeof replacement?.runId === "string"
|
|
2272
|
+
? incomingEventSequences.get(replacement.runId)
|
|
2273
|
+
: undefined;
|
|
2110
2274
|
if (typeof replacement?.runId === "string" &&
|
|
2275
|
+
incomingRunStarts.has(replacement.runId) &&
|
|
2111
2276
|
typeof replacement.lastSequence === "number" &&
|
|
2112
2277
|
Number.isSafeInteger(replacement.lastSequence) &&
|
|
2113
2278
|
replacement.lastSequence >= 1 &&
|
|
2114
|
-
|
|
2279
|
+
eventSequence === replacement.lastSequence &&
|
|
2280
|
+
hasContiguousAgentKitEventCoverage(incomingEvents, replacement.runId, replacement.lastSequence) &&
|
|
2115
2281
|
!eventRunSnapshotBatches.has(replacement.runId)) {
|
|
2116
2282
|
eventRunReplacements.set(replacement.runId, Math.max(eventRunReplacements.get(replacement.runId) ?? 0, replacement.lastSequence));
|
|
2117
2283
|
}
|
|
@@ -2214,20 +2380,29 @@ function mergeAgentKitHistory(existing, incoming, promptRunIds, onAnnotationConf
|
|
|
2214
2380
|
runs.set(run.id, run);
|
|
2215
2381
|
continue;
|
|
2216
2382
|
}
|
|
2217
|
-
const
|
|
2218
|
-
const
|
|
2219
|
-
?
|
|
2220
|
-
: newer.lastSequence
|
|
2383
|
+
const trustedReset = trustedRunSequenceResetIds.has(run.id) && newer.lastSequence === 0;
|
|
2384
|
+
const richer = trustedReset
|
|
2385
|
+
? newer
|
|
2386
|
+
: newer.lastSequence >= run.lastSequence
|
|
2221
2387
|
? newer
|
|
2222
|
-
:
|
|
2223
|
-
|
|
2224
|
-
|
|
2388
|
+
: run;
|
|
2389
|
+
const statusRun = trustedReset
|
|
2390
|
+
? newer
|
|
2391
|
+
: run.lastSequence > newer.lastSequence
|
|
2392
|
+
? run
|
|
2393
|
+
: newer.lastSequence > run.lastSequence
|
|
2394
|
+
? newer
|
|
2395
|
+
: isTerminalRunStatus(run.status)
|
|
2396
|
+
? run
|
|
2397
|
+
: newer;
|
|
2225
2398
|
runs.set(run.id, {
|
|
2226
2399
|
...run,
|
|
2227
2400
|
...newer,
|
|
2228
2401
|
...richer,
|
|
2229
2402
|
metadata: { ...run.metadata, ...newer.metadata, ...richer.metadata },
|
|
2230
|
-
lastSequence:
|
|
2403
|
+
lastSequence: trustedReset
|
|
2404
|
+
? newer.lastSequence
|
|
2405
|
+
: Math.max(newer.lastSequence, run.lastSequence),
|
|
2231
2406
|
status: statusRun.status,
|
|
2232
2407
|
});
|
|
2233
2408
|
}
|
|
@@ -2437,7 +2612,7 @@ export function mergeThreadDataForClientSave(existingRepo, incomingRepo, options
|
|
|
2437
2612
|
}
|
|
2438
2613
|
const promptRunIds = submittedPromptRunIds(existingNormalized?.messages, incomingNormalized?.messages);
|
|
2439
2614
|
if (merged.agentKit !== undefined) {
|
|
2440
|
-
merged.agentKit = mergeAgentKitHistory(existingNormalized?.agentKit, merged.agentKit, promptRunIds, options.onAnnotationConflict);
|
|
2615
|
+
merged.agentKit = mergeAgentKitHistory(existingNormalized?.agentKit, merged.agentKit, promptRunIds, options.onAnnotationConflict, options.trustedRunSequenceResetIds);
|
|
2441
2616
|
}
|
|
2442
2617
|
const existingMessages = Array.isArray(existingNormalized?.messages)
|
|
2443
2618
|
? existingNormalized.messages
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
export declare function failedRunIdsWithError(runs: unknown): Set<string>;
|
|
2
|
+
export declare function isRenderedRunNotStartedNotice(value: unknown, failedRunIds: ReadonlySet<string>): boolean;
|
|
1
3
|
export declare function threadMessageRecord(value: unknown): Record<string, unknown> | undefined;
|
|
2
4
|
/** AgentKit messages that represent durable root assistant messages. */
|
|
3
5
|
export declare function projectRootAssistantMessages(input: {
|
|
@@ -10,6 +12,7 @@ export declare function projectRootAssistantMessages(input: {
|
|
|
10
12
|
representedRootMessageIds: Set<string>;
|
|
11
13
|
snapshotMessageIdsByRootMessageId: Map<string, string>;
|
|
12
14
|
snapshotRunIdsByMessageId: Map<string, string>;
|
|
15
|
+
supersededSnapshotMessageIds: Set<string>;
|
|
13
16
|
};
|
|
14
17
|
/** Root assistant IDs represented by the AgentKit snapshot. */
|
|
15
18
|
export declare function representedRootAssistantMessageIds(input: {
|
|
@@ -1,9 +1,28 @@
|
|
|
1
|
+
import { RUN_NOT_STARTED_METADATA_KEY } from "../shared/agent-chat-run-not-started.js";
|
|
1
2
|
const REPOSITORY_PART_MEDIA_TYPE = "application/x-agent-native-repository-part";
|
|
2
3
|
function record(value) {
|
|
3
4
|
return value && typeof value === "object" && !Array.isArray(value)
|
|
4
5
|
? value
|
|
5
6
|
: undefined;
|
|
6
7
|
}
|
|
8
|
+
export function failedRunIdsWithError(runs) {
|
|
9
|
+
return new Set((Array.isArray(runs) ? runs : []).flatMap((value) => {
|
|
10
|
+
const run = record(value);
|
|
11
|
+
return run?.status === "failed" &&
|
|
12
|
+
record(run.error) &&
|
|
13
|
+
typeof run.id === "string"
|
|
14
|
+
? [run.id]
|
|
15
|
+
: [];
|
|
16
|
+
}));
|
|
17
|
+
}
|
|
18
|
+
export function isRenderedRunNotStartedNotice(value, failedRunIds) {
|
|
19
|
+
const entry = message(value);
|
|
20
|
+
const metadata = record(entry?.metadata);
|
|
21
|
+
return (entry?.role === "assistant" &&
|
|
22
|
+
record(metadata?.custom)?.[RUN_NOT_STARTED_METADATA_KEY] === true &&
|
|
23
|
+
typeof metadata?.runId === "string" &&
|
|
24
|
+
failedRunIds.has(metadata.runId));
|
|
25
|
+
}
|
|
7
26
|
function message(value) {
|
|
8
27
|
const outer = record(value);
|
|
9
28
|
return record(outer?.message) ?? outer;
|
|
@@ -37,6 +56,15 @@ function explicitRunId(value) {
|
|
|
37
56
|
const runId = record(value.metadata)?.runId;
|
|
38
57
|
return typeof runId === "string" ? runId : undefined;
|
|
39
58
|
}
|
|
59
|
+
function isTerminalAssistant(value) {
|
|
60
|
+
const status = value.status;
|
|
61
|
+
const type = record(status)?.type;
|
|
62
|
+
return (status === "complete" ||
|
|
63
|
+
status === "error" ||
|
|
64
|
+
type === "complete" ||
|
|
65
|
+
type === "error" ||
|
|
66
|
+
type === "incomplete");
|
|
67
|
+
}
|
|
40
68
|
function foldedRunIds(value) {
|
|
41
69
|
const custom = record(record(value.metadata)?.custom);
|
|
42
70
|
const folded = Array.isArray(custom?.foldedRunIds)
|
|
@@ -115,6 +143,26 @@ function reasoning(value) {
|
|
|
115
143
|
function isPrefixCompatible(left, right) {
|
|
116
144
|
return left.startsWith(right) || right.startsWith(left);
|
|
117
145
|
}
|
|
146
|
+
function isRepeatedAnswerSnapshot(answer, candidateText) {
|
|
147
|
+
if (!answer || !candidateText.startsWith(answer))
|
|
148
|
+
return false;
|
|
149
|
+
let offset = answer.length;
|
|
150
|
+
let repeatedAnswers = 0;
|
|
151
|
+
while (offset < candidateText.length) {
|
|
152
|
+
while (offset < candidateText.length &&
|
|
153
|
+
/\s/u.test(candidateText[offset])) {
|
|
154
|
+
offset += 1;
|
|
155
|
+
}
|
|
156
|
+
if (offset === candidateText.length)
|
|
157
|
+
return repeatedAnswers > 0;
|
|
158
|
+
if (!candidateText.startsWith(answer, offset)) {
|
|
159
|
+
return false;
|
|
160
|
+
}
|
|
161
|
+
offset += answer.length;
|
|
162
|
+
repeatedAnswers += 1;
|
|
163
|
+
}
|
|
164
|
+
return repeatedAnswers > 0;
|
|
165
|
+
}
|
|
118
166
|
function rootToolCalls(value) {
|
|
119
167
|
return parts(value).flatMap((part) => {
|
|
120
168
|
if (part.type !== "data" || part.mediaType !== REPOSITORY_PART_MEDIA_TYPE) {
|
|
@@ -124,6 +172,39 @@ function rootToolCalls(value) {
|
|
|
124
172
|
return data?.type === "tool-call" ? [data] : [];
|
|
125
173
|
});
|
|
126
174
|
}
|
|
175
|
+
function sameToolCallIdentity(left, right) {
|
|
176
|
+
const id = (call) => typeof call.toolCallId === "string" ? call.toolCallId : call.id;
|
|
177
|
+
const name = (call) => typeof call.toolName === "string" ? call.toolName : call.name;
|
|
178
|
+
const args = (call) => Object.prototype.hasOwnProperty.call(call, "args") ? call.args : call.input;
|
|
179
|
+
const hasArgs = (call) => Object.prototype.hasOwnProperty.call(call, "args") ||
|
|
180
|
+
Object.prototype.hasOwnProperty.call(call, "input");
|
|
181
|
+
return (typeof id(left) === "string" &&
|
|
182
|
+
id(left) === id(right) &&
|
|
183
|
+
typeof name(left) === "string" &&
|
|
184
|
+
name(left) === name(right) &&
|
|
185
|
+
hasArgs(left) &&
|
|
186
|
+
hasArgs(right) &&
|
|
187
|
+
sameValue(args(left), args(right)));
|
|
188
|
+
}
|
|
189
|
+
function snapshotAdditionalContentIsRepresented(root, candidate) {
|
|
190
|
+
const candidateReasoning = reasoning(candidate);
|
|
191
|
+
if (candidateReasoning && candidateReasoning !== reasoning(root)) {
|
|
192
|
+
return false;
|
|
193
|
+
}
|
|
194
|
+
const rootCalls = rootToolCalls(root);
|
|
195
|
+
return rootToolCalls(candidate).every((candidateCall) => rootCalls.some((rootCall) => sameToolCallIdentity(rootCall, candidateCall)));
|
|
196
|
+
}
|
|
197
|
+
function snapshotRepresentsRootContent(root, candidate, toolMessageIds) {
|
|
198
|
+
const rootCalls = rootToolCalls(root);
|
|
199
|
+
const candidateCalls = rootToolCalls(candidate);
|
|
200
|
+
if (rootCalls.every((rootCall) => candidateCalls.some((candidateCall) => sameToolCallIdentity(rootCall, candidateCall)))) {
|
|
201
|
+
return true;
|
|
202
|
+
}
|
|
203
|
+
return (rootCalls.length > 0 &&
|
|
204
|
+
typeof candidate.id === "string" &&
|
|
205
|
+
toolMessageIds?.size === 1 &&
|
|
206
|
+
toolMessageIds.has(candidate.id));
|
|
207
|
+
}
|
|
127
208
|
function sameValue(left, right) {
|
|
128
209
|
if (Object.is(left, right))
|
|
129
210
|
return true;
|
|
@@ -294,17 +375,68 @@ export function projectRootAssistantMessages(input) {
|
|
|
294
375
|
}
|
|
295
376
|
const represented = new Set();
|
|
296
377
|
const directMatches = new Map();
|
|
378
|
+
const rootAssistantCountsByRun = new Map();
|
|
379
|
+
for (const root of rootMessages) {
|
|
380
|
+
const runId = assistantRunId(root, runIdsByMessageId);
|
|
381
|
+
if (!runId)
|
|
382
|
+
continue;
|
|
383
|
+
rootAssistantCountsByRun.set(runId, (rootAssistantCountsByRun.get(runId) ?? 0) + 1);
|
|
384
|
+
}
|
|
385
|
+
const supersededSnapshotMessageIds = new Set();
|
|
297
386
|
for (const root of rootMessages) {
|
|
298
387
|
if (root.role !== "assistant" || typeof root.id !== "string")
|
|
299
388
|
continue;
|
|
300
|
-
const runId =
|
|
389
|
+
const runId = assistantRunId(root, runIdsByMessageId);
|
|
301
390
|
if (!runId)
|
|
302
391
|
continue;
|
|
303
392
|
const toolMessageIds = representedToolCallMessageIds(root, input.toolCalls);
|
|
304
393
|
const sameRunCandidates = (snapshotMessagesByRunId.get(runId) ?? []).filter((candidate) => candidate.id !== root.id);
|
|
305
394
|
const sameRunMatches = sameRunCandidates.filter((candidate) => contentMatches(root, candidate, toolMessageIds));
|
|
306
395
|
const rootText = text(root);
|
|
307
|
-
const
|
|
396
|
+
const exactTextMatches = sameRunMatches.filter((candidate) => rootText !== undefined && text(candidate) === rootText);
|
|
397
|
+
const duplicateCandidates = rootText && isTerminalAssistant(root)
|
|
398
|
+
? sameRunCandidates.filter((candidate) => {
|
|
399
|
+
const candidateText = text(candidate);
|
|
400
|
+
return (candidateText === rootText ||
|
|
401
|
+
Boolean(candidateText &&
|
|
402
|
+
isRepeatedAnswerSnapshot(rootText, candidateText)));
|
|
403
|
+
})
|
|
404
|
+
: [];
|
|
405
|
+
if (rootText &&
|
|
406
|
+
isTerminalAssistant(root) &&
|
|
407
|
+
rootAssistantCountsByRun.get(runId) === 1 &&
|
|
408
|
+
duplicateCandidates.length > 0) {
|
|
409
|
+
const matchingCandidate = exactTextMatches.find((candidate) => snapshotRepresentsRootContent(root, candidate, toolMessageIds) &&
|
|
410
|
+
snapshotAdditionalContentIsRepresented(root, candidate));
|
|
411
|
+
if (matchingCandidate && typeof matchingCandidate.id === "string") {
|
|
412
|
+
represented.add(root.id);
|
|
413
|
+
directMatches.set(root.id, matchingCandidate.id);
|
|
414
|
+
}
|
|
415
|
+
for (const candidate of duplicateCandidates) {
|
|
416
|
+
if (matchingCandidate && candidate.id === matchingCandidate.id) {
|
|
417
|
+
continue;
|
|
418
|
+
}
|
|
419
|
+
const candidateText = text(candidate);
|
|
420
|
+
if (candidateText &&
|
|
421
|
+
snapshotAdditionalContentIsRepresented(root, candidate) &&
|
|
422
|
+
typeof candidate.id === "string") {
|
|
423
|
+
supersededSnapshotMessageIds.add(candidate.id);
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
continue;
|
|
427
|
+
}
|
|
428
|
+
const matchingCandidates = rootText && isTerminalAssistant(root)
|
|
429
|
+
? sameRunMatches.filter((candidate) => {
|
|
430
|
+
const candidateText = text(candidate);
|
|
431
|
+
return (!candidateText ||
|
|
432
|
+
!isRepeatedAnswerSnapshot(rootText, candidateText));
|
|
433
|
+
})
|
|
434
|
+
: sameRunMatches;
|
|
435
|
+
const fallbackExactMatches = matchingCandidates.filter((candidate) => rootText !== undefined && text(candidate) === rootText);
|
|
436
|
+
const candidateMatches = fallbackExactMatches.length > 0
|
|
437
|
+
? fallbackExactMatches
|
|
438
|
+
: matchingCandidates;
|
|
439
|
+
const matchingCandidate = candidateMatches[0];
|
|
308
440
|
const exactTextMatch = matchingCandidate !== undefined &&
|
|
309
441
|
rootText !== undefined &&
|
|
310
442
|
text(matchingCandidate) === rootText;
|
|
@@ -314,7 +446,7 @@ export function projectRootAssistantMessages(input) {
|
|
|
314
446
|
(snapshotRunIdsByMessageId.get(matchingCandidate.id) === runId ||
|
|
315
447
|
(runIdsByMessageId.get(matchingCandidate.id)?.size === 1 &&
|
|
316
448
|
messageIdsByRun.get(runId)?.size === 1))));
|
|
317
|
-
if (
|
|
449
|
+
if (candidateMatches.length === 1 &&
|
|
318
450
|
(exactTextMatch ||
|
|
319
451
|
(sameRunCandidates.length === 1 && clientCanMatchPartial))) {
|
|
320
452
|
represented.add(root.id);
|
|
@@ -344,6 +476,7 @@ export function projectRootAssistantMessages(input) {
|
|
|
344
476
|
representedRootMessageIds: represented,
|
|
345
477
|
snapshotMessageIdsByRootMessageId,
|
|
346
478
|
snapshotRunIdsByMessageId,
|
|
479
|
+
supersededSnapshotMessageIds,
|
|
347
480
|
};
|
|
348
481
|
}
|
|
349
482
|
/** Root assistant IDs represented by the AgentKit snapshot. */
|
|
@@ -139,8 +139,8 @@ export declare function createAutomationCallbackHandler(runtime: AutomationRunti
|
|
|
139
139
|
duplicate?: undefined;
|
|
140
140
|
eventId?: undefined;
|
|
141
141
|
} | {
|
|
142
|
-
error?: undefined;
|
|
143
142
|
accepted: boolean;
|
|
144
143
|
duplicate: boolean;
|
|
145
144
|
eventId: string;
|
|
145
|
+
error?: undefined;
|
|
146
146
|
}>;
|
|
@@ -189,6 +189,7 @@ export interface UpdateThreadDataOptions {
|
|
|
189
189
|
preserveExistingTopLevelKeys?: boolean;
|
|
190
190
|
preserveCurrentMetadata?: boolean;
|
|
191
191
|
preserveCurrentTitleAndPreview?: boolean;
|
|
192
|
+
trustedRunSequenceResetIds?: ReadonlySet<string>;
|
|
192
193
|
onAnnotationConflict?: (conflict: ThreadAnnotationSnapshotConflict) => void;
|
|
193
194
|
transformThreadData?: (currentThreadData: string) => string | {
|
|
194
195
|
threadData: string;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import crypto from "node:crypto";
|
|
2
2
|
import { parseQueueMessageInput, } from "@agent-native/agentkit/protocol";
|
|
3
3
|
import { extractThreadMeta, mergeThreadDataForClientSave, normalizeThreadRepository, normalizeThreadTitle, } from "../agent/thread-data-builder.js";
|
|
4
|
-
import {
|
|
4
|
+
import { failedRunIdsWithError, isRenderedRunNotStartedNotice, projectRootAssistantMessages, threadMessageRecord, } from "../agent/thread-message-projection.js";
|
|
5
5
|
import { getDbExec } from "../db/client.js";
|
|
6
6
|
import { createGetDb } from "../db/create-get-db.js";
|
|
7
7
|
import { ensureColumnExists, ensureIndexExists, ensureTableExists, } from "../db/ddl-guard.js";
|
|
@@ -293,17 +293,20 @@ function countThreadMessages(value, fallback) {
|
|
|
293
293
|
if (repoMessageCount === undefined && agentKitMessageCount === undefined) {
|
|
294
294
|
return fallback;
|
|
295
295
|
}
|
|
296
|
-
const
|
|
296
|
+
const failedRunIds = failedRunIdsWithError(repo.agentKit?.runs);
|
|
297
|
+
const rootMessages = (Array.isArray(repo.messages) ? repo.messages : []).filter((entry) => !isRenderedRunNotStartedNotice(entry, failedRunIds));
|
|
297
298
|
const agentKitMessages = Array.isArray(repo.agentKit?.messages)
|
|
298
299
|
? repo.agentKit.messages
|
|
299
300
|
: [];
|
|
300
|
-
const
|
|
301
|
+
const assistantProjection = projectRootAssistantMessages({
|
|
301
302
|
rootMessages,
|
|
302
303
|
snapshotMessages: agentKitMessages,
|
|
303
304
|
events: repo.agentKit?.events,
|
|
304
305
|
runs: repo.agentKit?.runs,
|
|
305
306
|
toolCalls: repo.agentKit?.toolCalls,
|
|
306
307
|
});
|
|
308
|
+
const mirroredRootIds = assistantProjection.representedRootMessageIds;
|
|
309
|
+
const supersededSnapshotMessageIds = assistantProjection.supersededSnapshotMessageIds;
|
|
307
310
|
const agentKitUserMessageId = (entry) => {
|
|
308
311
|
const message = threadMessageRecord(entry);
|
|
309
312
|
if (message?.role !== "user")
|
|
@@ -362,8 +365,14 @@ function countThreadMessages(value, fallback) {
|
|
|
362
365
|
}
|
|
363
366
|
countMessage(entry);
|
|
364
367
|
}
|
|
365
|
-
for (const entry of agentKitMessages)
|
|
368
|
+
for (const entry of agentKitMessages) {
|
|
369
|
+
const message = threadMessageRecord(entry);
|
|
370
|
+
if (typeof message?.id === "string" &&
|
|
371
|
+
supersededSnapshotMessageIds.has(message.id)) {
|
|
372
|
+
continue;
|
|
373
|
+
}
|
|
366
374
|
countMessage(entry);
|
|
375
|
+
}
|
|
367
376
|
return messageIds.size + unkeyedMessages;
|
|
368
377
|
}
|
|
369
378
|
function forkThreadData(threadData, forkId, fromMessageId) {
|
|
@@ -1124,6 +1133,7 @@ export async function updateThreadData(id, threadData, title, preview, messageCo
|
|
|
1124
1133
|
const merged = mergeThreadDataForClientSave(safeCurrentThreadData, parseThreadData(incomingThreadData), {
|
|
1125
1134
|
preserveExistingQueuedMessages: options.preserveExistingQueuedMessages ?? true,
|
|
1126
1135
|
preserveExistingTopLevelKeys: options.preserveExistingTopLevelKeys ?? true,
|
|
1136
|
+
trustedRunSequenceResetIds: options.trustedRunSequenceResetIds,
|
|
1127
1137
|
onAnnotationConflict: (conflict) => annotationConflicts.push(conflict),
|
|
1128
1138
|
});
|
|
1129
1139
|
// Client snapshots can predate their upload URL, so inline bytes become a
|