@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.
Files changed (63) hide show
  1. package/dist/agent/engine/builder-engine.d.ts +1 -1
  2. package/dist/agent/model-config.d.ts +2 -2
  3. package/dist/agent/model-config.js +0 -2
  4. package/dist/agent/thread-data-builder.d.ts +6 -0
  5. package/dist/agent/thread-data-builder.js +747 -25
  6. package/dist/agent/thread-message-projection.d.ts +20 -0
  7. package/dist/agent/thread-message-projection.js +300 -0
  8. package/dist/automation/index.d.ts +1 -1
  9. package/dist/chat-threads/store.d.ts +4 -1
  10. package/dist/chat-threads/store.js +92 -17
  11. package/dist/cli/skills-content/turn-into-app-skill.d.ts +10 -4
  12. package/dist/cli/skills-content/turn-into-app-skill.js +2454 -705
  13. package/dist/cli/skills.d.ts +6 -0
  14. package/dist/cli/skills.js +8 -2
  15. package/dist/client/chat/agentkit-agent-native.js +839 -89
  16. package/dist/client/chat/agentkit-protocol.js +67 -17
  17. package/dist/collab/routes.d.ts +2 -2
  18. package/dist/jobs/actions/manage-recurring-job.d.ts +1 -1
  19. package/dist/mcp/build-server.d.ts +5 -1
  20. package/dist/mcp/build-server.js +56 -13
  21. package/dist/mcp/embed-app.js +6 -1
  22. package/dist/notifications/routes.d.ts +1 -1
  23. package/dist/observability/routes.d.ts +3 -3
  24. package/dist/org/index.d.ts +2 -2
  25. package/dist/provider-api/actions/provider-api.d.ts +4 -4
  26. package/dist/resources/handlers.d.ts +1 -1
  27. package/dist/server/agent-chat/mcp-options.d.ts +4 -0
  28. package/dist/server/agent-chat-plugin.js +97 -22
  29. package/dist/server/agent-engine-default-model-route.d.ts +2 -2
  30. package/dist/server/onboarding-html.js +1 -1
  31. package/dist/server/realtime-token.d.ts +1 -1
  32. package/dist/server/transcribe-voice.d.ts +1 -1
  33. package/dist/shared/embed-auth.js +5 -4
  34. package/dist/sharing/actions/list-resource-shares.d.ts +1 -1
  35. package/dist/templates/factory/.agents/skills/review-latest-feedback/SKILL.md +26 -24
  36. package/dist/templates/factory/.agents/skills/review-latest-feedback/references/deployment-recovery.md +53 -0
  37. package/dist/templates/factory/.agents/skills/review-latest-feedback/references/documentation-proof.md +7 -0
  38. package/dist/templates/workspace-core/.agents/skills/turn-into-app/SKILL.md +284 -461
  39. package/dist/templates/workspace-core/.agents/skills/turn-into-app/agents/openai.yaml +2 -2
  40. package/dist/templates/workspace-core/.agents/skills/turn-into-app/references/fresh-project.md +65 -90
  41. package/dist/templates/workspace-core/.agents/skills/turn-into-app/references/local-run-and-deploy.md +161 -0
  42. package/dist/templates/workspace-core/.agents/skills/turn-into-app/references/review-loop.md +384 -0
  43. package/dist/templates/workspace-core/.agents/skills/turn-into-app/references/source-brief.md +116 -0
  44. package/dist/templates/workspace-core/.agents/skills/turn-into-app/references/spreadsheet-source.md +206 -190
  45. package/dist/templates/workspace-core/.agents/skills/turn-into-app/references/ui-archetypes.md +381 -0
  46. package/dist/templates/workspace-core/.agents/skills/turn-into-app/references/ui-direction.md +323 -0
  47. package/dist/templates/workspace-core/.agents/skills/turn-into-app/references/ui-palettes.md +564 -0
  48. package/dist/triggers/actions/manage-automation.d.ts +7 -7
  49. package/docs/content/observability.mdx +136 -0
  50. package/package.json +4 -4
  51. package/src/templates/factory/.agents/skills/review-latest-feedback/SKILL.md +26 -24
  52. package/src/templates/factory/.agents/skills/review-latest-feedback/references/deployment-recovery.md +53 -0
  53. package/src/templates/factory/.agents/skills/review-latest-feedback/references/documentation-proof.md +7 -0
  54. package/src/templates/workspace-core/.agents/skills/turn-into-app/SKILL.md +284 -461
  55. package/src/templates/workspace-core/.agents/skills/turn-into-app/agents/openai.yaml +2 -2
  56. package/src/templates/workspace-core/.agents/skills/turn-into-app/references/fresh-project.md +65 -90
  57. package/src/templates/workspace-core/.agents/skills/turn-into-app/references/local-run-and-deploy.md +161 -0
  58. package/src/templates/workspace-core/.agents/skills/turn-into-app/references/review-loop.md +384 -0
  59. package/src/templates/workspace-core/.agents/skills/turn-into-app/references/source-brief.md +116 -0
  60. package/src/templates/workspace-core/.agents/skills/turn-into-app/references/spreadsheet-source.md +206 -190
  61. package/src/templates/workspace-core/.agents/skills/turn-into-app/references/ui-archetypes.md +381 -0
  62. package/src/templates/workspace-core/.agents/skills/turn-into-app/references/ui-direction.md +323 -0
  63. package/src/templates/workspace-core/.agents/skills/turn-into-app/references/ui-palettes.md +564 -0
@@ -1165,11 +1165,8 @@ function snapshotEntryId(entry, kind) {
1165
1165
  if (!entry || typeof entry !== "object")
1166
1166
  return undefined;
1167
1167
  if (kind === "widget") {
1168
- const messageId = entry.messageId;
1169
1168
  const widgetId = entry.widget?.id;
1170
- return typeof messageId === "string" && typeof widgetId === "string"
1171
- ? JSON.stringify([messageId, widgetId])
1172
- : undefined;
1169
+ return typeof widgetId === "string" ? widgetId : undefined;
1173
1170
  }
1174
1171
  return typeof entry.id === "string" ? entry.id : undefined;
1175
1172
  }
@@ -1384,6 +1381,9 @@ function latestSnapshotRun(runs) {
1384
1381
  return latestTime && runTime && runTime < latestTime ? latest : run;
1385
1382
  }, undefined);
1386
1383
  }
1384
+ function isTerminalRunStatus(status) {
1385
+ return (status === "completed" || status === "failed" || status === "cancelled");
1386
+ }
1387
1387
  function latestStoredUser(repo) {
1388
1388
  return repo?.messages
1389
1389
  ?.map((entry) => getStoredMessage(entry))
@@ -1453,13 +1453,19 @@ export function foldThreadRunSuggestions(repo, run) {
1453
1453
  ? previous.runs
1454
1454
  : [];
1455
1455
  const oldRun = runs.find((entry) => entry.id === run.runId);
1456
+ const previousSequence = oldRun &&
1457
+ Number.isSafeInteger(oldRun.lastSequence) &&
1458
+ oldRun.lastSequence >= 0
1459
+ ? oldRun.lastSequence
1460
+ : 0;
1461
+ const lastSequence = run.events.reduce((maximum, { seq }) => Number.isSafeInteger(seq) && seq >= 0 ? Math.max(maximum, seq) : maximum, previousSequence);
1456
1462
  const snapshot = {
1457
1463
  ...oldRun,
1458
1464
  id: run.runId,
1459
1465
  threadId: run.threadId,
1460
1466
  status,
1461
1467
  startedAt,
1462
- lastSequence: oldRun?.lastSequence ?? 0,
1468
+ lastSequence,
1463
1469
  };
1464
1470
  return {
1465
1471
  ...repo,
@@ -1471,17 +1477,600 @@ export function foldThreadRunSuggestions(repo, run) {
1471
1477
  },
1472
1478
  };
1473
1479
  }
1474
- function mergeAgentKitHistory(existing, incoming, promptRunIds) {
1475
- if (!existing ||
1476
- typeof existing !== "object" ||
1477
- Array.isArray(existing) ||
1478
- !incoming ||
1479
- typeof incoming !== "object" ||
1480
- Array.isArray(incoming)) {
1480
+ function mergeAgentKitEvents(existing, incoming, replacedRunIds = new Set(), staleRunIds = new Set()) {
1481
+ if (!Array.isArray(existing) && !Array.isArray(incoming))
1482
+ return undefined;
1483
+ const incomingEvents = Array.isArray(incoming)
1484
+ ? incoming.filter((event) => typeof event?.runId !== "string" || !staleRunIds.has(event.runId))
1485
+ : [];
1486
+ const merged = Array.isArray(existing)
1487
+ ? existing.filter((event) => typeof event?.runId !== "string" || !replacedRunIds.has(event.runId))
1488
+ : [];
1489
+ const positions = new Map();
1490
+ merged.forEach((event, index) => {
1491
+ if (typeof event?.id === "string" && !positions.has(event.id)) {
1492
+ positions.set(event.id, index);
1493
+ }
1494
+ });
1495
+ for (const event of incomingEvents) {
1496
+ if (typeof event?.id !== "string") {
1497
+ merged.push(event);
1498
+ continue;
1499
+ }
1500
+ const index = positions.get(event.id);
1501
+ if (index === undefined) {
1502
+ positions.set(event.id, merged.length);
1503
+ merged.push(event);
1504
+ }
1505
+ else {
1506
+ merged[index] = event;
1507
+ }
1508
+ }
1509
+ const positionsByRun = new Map();
1510
+ merged.forEach((event, index) => {
1511
+ if (typeof event?.runId !== "string")
1512
+ return;
1513
+ const runPositions = positionsByRun.get(event.runId) ?? [];
1514
+ runPositions.push(index);
1515
+ positionsByRun.set(event.runId, runPositions);
1516
+ });
1517
+ for (const runPositions of positionsByRun.values()) {
1518
+ const runEvents = runPositions.map((index, order) => ({
1519
+ event: merged[index],
1520
+ index,
1521
+ order,
1522
+ sequence: typeof merged[index]?.sequence === "number" &&
1523
+ Number.isFinite(merged[index].sequence)
1524
+ ? merged[index].sequence
1525
+ : Number.NaN,
1526
+ }));
1527
+ runEvents.sort((left, right) => {
1528
+ if (Number.isFinite(left.sequence) &&
1529
+ Number.isFinite(right.sequence) &&
1530
+ left.sequence !== right.sequence) {
1531
+ return left.sequence - right.sequence;
1532
+ }
1533
+ return left.order - right.order;
1534
+ });
1535
+ runPositions.forEach((index, order) => {
1536
+ merged[index] = { ...runEvents[order].event, sequence: order + 1 };
1537
+ });
1538
+ }
1539
+ return merged;
1540
+ }
1541
+ function mergeAgentKitFullEventSnapshot(existingEvents, incomingEvents, incomingRuns, committedSnapshots) {
1542
+ if (!Array.isArray(incomingEvents))
1543
+ return undefined;
1544
+ const previousEvents = Array.isArray(existingEvents) ? existingEvents : [];
1545
+ const incomingRunSequences = new Map();
1546
+ for (const run of Array.isArray(incomingRuns) ? incomingRuns : []) {
1547
+ if (typeof run?.id === "string" &&
1548
+ typeof run.lastSequence === "number" &&
1549
+ Number.isSafeInteger(run.lastSequence) &&
1550
+ run.lastSequence >= 0) {
1551
+ incomingRunSequences.set(run.id, run.lastSequence);
1552
+ }
1553
+ }
1554
+ const existingEventsByRun = new Map();
1555
+ for (const event of previousEvents) {
1556
+ if (typeof event?.runId !== "string")
1557
+ continue;
1558
+ const events = existingEventsByRun.get(event.runId) ?? [];
1559
+ events.push(event);
1560
+ existingEventsByRun.set(event.runId, events);
1561
+ }
1562
+ const incomingEventsByRun = new Map();
1563
+ for (const event of incomingEvents) {
1564
+ if (typeof event?.runId !== "string")
1565
+ continue;
1566
+ const events = incomingEventsByRun.get(event.runId) ?? [];
1567
+ events.push(event);
1568
+ incomingEventsByRun.set(event.runId, events);
1569
+ }
1570
+ const protectedRunIds = new Set();
1571
+ if (committedSnapshots &&
1572
+ typeof committedSnapshots === "object" &&
1573
+ !Array.isArray(committedSnapshots)) {
1574
+ for (const [runId, value] of Object.entries(committedSnapshots)) {
1575
+ if (!Array.isArray(value))
1576
+ continue;
1577
+ const committedSequence = value.reduce((maxSequence, entry) => {
1578
+ const sequence = entry?.lastSequence;
1579
+ return typeof sequence === "number" &&
1580
+ Number.isSafeInteger(sequence) &&
1581
+ sequence >= 0
1582
+ ? Math.max(maxSequence, sequence)
1583
+ : maxSequence;
1584
+ }, -1);
1585
+ if (committedSequence < 0)
1586
+ continue;
1587
+ const runEvents = incomingEventsByRun.get(runId) ?? [];
1588
+ const incomingSequence = Math.max(incomingRunSequences.get(runId) ?? 0, ...runEvents.map((event) => {
1589
+ const sequence = event && typeof event === "object"
1590
+ ? event.sequence
1591
+ : undefined;
1592
+ return typeof sequence === "number" &&
1593
+ Number.isSafeInteger(sequence) &&
1594
+ sequence >= 0
1595
+ ? sequence
1596
+ : 0;
1597
+ }));
1598
+ if (runEvents.length === 0 || incomingSequence <= committedSequence) {
1599
+ protectedRunIds.add(runId);
1600
+ }
1601
+ }
1602
+ }
1603
+ if (protectedRunIds.size === 0)
1604
+ return incomingEvents;
1605
+ const merged = [];
1606
+ const insertedProtectedRuns = new Set();
1607
+ for (const event of incomingEvents) {
1608
+ const runId = event?.runId;
1609
+ if (typeof runId === "string" && protectedRunIds.has(runId)) {
1610
+ if (!insertedProtectedRuns.has(runId)) {
1611
+ merged.push(...(existingEventsByRun.get(runId) ?? []));
1612
+ insertedProtectedRuns.add(runId);
1613
+ }
1614
+ continue;
1615
+ }
1616
+ merged.push(event);
1617
+ }
1618
+ for (const runId of protectedRunIds) {
1619
+ if (insertedProtectedRuns.has(runId))
1620
+ continue;
1621
+ merged.push(...(existingEventsByRun.get(runId) ?? []));
1622
+ }
1623
+ return merged;
1624
+ }
1625
+ const MAX_PENDING_EVENT_RUN_SNAPSHOTS_PER_RUN = 2;
1626
+ const MAX_COMMITTED_EVENT_RUN_SNAPSHOTS_PER_RUN = 8;
1627
+ function mergeAgentKitEventRunSnapshots(input) {
1628
+ const incomingEvents = Array.isArray(input.incomingEvents)
1629
+ ? input.incomingEvents
1630
+ : [];
1631
+ const batchedRunIds = new Set(input.batches.keys());
1632
+ const unbatchedEvents = Array.isArray(input.incomingEvents)
1633
+ ? incomingEvents.filter((event) => !batchedRunIds.has(event?.runId))
1634
+ : undefined;
1635
+ const previousEvents = Array.isArray(input.existingEvents)
1636
+ ? input.existingEvents
1637
+ : [];
1638
+ const commits = {};
1639
+ if (input.existingCommits &&
1640
+ typeof input.existingCommits === "object" &&
1641
+ !Array.isArray(input.existingCommits)) {
1642
+ for (const [runId, value] of Object.entries(input.existingCommits)) {
1643
+ if (!Array.isArray(value))
1644
+ continue;
1645
+ const entries = value.flatMap((candidate) => {
1646
+ if (!candidate || typeof candidate !== "object")
1647
+ return [];
1648
+ const commit = candidate;
1649
+ if (typeof commit.snapshotId !== "string" ||
1650
+ typeof commit.lastSequence !== "number" ||
1651
+ !Number.isSafeInteger(commit.lastSequence) ||
1652
+ commit.lastSequence < 0 ||
1653
+ typeof commit.expectedEventCount !== "number" ||
1654
+ !Number.isSafeInteger(commit.expectedEventCount) ||
1655
+ commit.expectedEventCount < 1) {
1656
+ return [];
1657
+ }
1658
+ return [
1659
+ {
1660
+ snapshotId: commit.snapshotId,
1661
+ lastSequence: commit.lastSequence,
1662
+ expectedEventCount: commit.expectedEventCount,
1663
+ },
1664
+ ];
1665
+ });
1666
+ if (entries.length > 0) {
1667
+ commits[runId] = entries.slice(-MAX_COMMITTED_EVENT_RUN_SNAPSHOTS_PER_RUN);
1668
+ }
1669
+ }
1670
+ }
1671
+ const watermarks = new Map(input.existingWatermarks);
1672
+ const replacedRunIds = new Set();
1673
+ const staleRunIds = new Set();
1674
+ for (const [runId, lastSequence] of input.incomingWatermarks) {
1675
+ const storedWatermark = watermarks.get(runId);
1676
+ const storedRunSequence = input.existingRunSequences.get(runId);
1677
+ const hasCommittedSnapshot = (commits[runId]?.length ?? 0) > 0;
1678
+ const committedSequence = Math.max(0, ...(commits[runId] ?? []).map((entry) => entry.lastSequence));
1679
+ const knownSequence = Math.max(storedWatermark ?? 0, storedRunSequence ?? 0, committedSequence);
1680
+ const hasKnownSequence = storedWatermark !== undefined ||
1681
+ storedRunSequence !== undefined ||
1682
+ hasCommittedSnapshot;
1683
+ const hasStoredEvents = previousEvents.some((event) => event?.runId === runId);
1684
+ if ((hasKnownSequence && lastSequence < knownSequence) ||
1685
+ (hasCommittedSnapshot && lastSequence <= committedSequence)) {
1686
+ staleRunIds.add(runId);
1687
+ continue;
1688
+ }
1689
+ if (input.incomingReplacements.has(runId) &&
1690
+ (hasKnownSequence || !hasStoredEvents)) {
1691
+ replacedRunIds.add(runId);
1692
+ }
1693
+ watermarks.set(runId, Math.max(knownSequence, lastSequence));
1694
+ }
1695
+ let events = mergeAgentKitEvents(input.existingEvents, unbatchedEvents, replacedRunIds, staleRunIds);
1696
+ const pending = {};
1697
+ if (input.existingPending &&
1698
+ typeof input.existingPending === "object" &&
1699
+ !Array.isArray(input.existingPending)) {
1700
+ for (const [runId, value] of Object.entries(input.existingPending)) {
1701
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
1702
+ continue;
1703
+ }
1704
+ const batches = {};
1705
+ for (const [snapshotId, candidate] of Object.entries(value)) {
1706
+ if (!candidate || typeof candidate !== "object")
1707
+ continue;
1708
+ const candidateRecord = candidate;
1709
+ if (Array.isArray(candidate) ||
1710
+ typeof candidateRecord.lastSequence !== "number" ||
1711
+ !Number.isSafeInteger(candidateRecord.lastSequence) ||
1712
+ candidateRecord.lastSequence < 0 ||
1713
+ typeof candidateRecord.expectedEventCount !== "number" ||
1714
+ !Number.isSafeInteger(candidateRecord.expectedEventCount) ||
1715
+ candidateRecord.expectedEventCount < 1 ||
1716
+ !Array.isArray(candidateRecord.events)) {
1717
+ continue;
1718
+ }
1719
+ batches[snapshotId] = {
1720
+ lastSequence: candidateRecord.lastSequence,
1721
+ expectedEventCount: candidateRecord.expectedEventCount,
1722
+ events: candidateRecord.events.filter((event) => event?.runId === runId),
1723
+ };
1724
+ }
1725
+ if (Object.keys(batches).length > 0)
1726
+ pending[runId] = batches;
1727
+ }
1728
+ }
1729
+ for (const [runId, batches] of Object.entries(pending)) {
1730
+ const committedSequence = Math.max(watermarks.get(runId) ?? 0, input.existingRunSequences.get(runId) ?? 0);
1731
+ if (!watermarks.has(runId) && !input.existingRunSequences.has(runId)) {
1732
+ continue;
1733
+ }
1734
+ for (const [snapshotId, batch] of Object.entries(batches)) {
1735
+ const supersededByLegacySnapshot = (input.incomingWatermarks.get(runId) ?? -1) >= batch.lastSequence;
1736
+ if (batch.lastSequence < committedSequence ||
1737
+ supersededByLegacySnapshot) {
1738
+ delete batches[snapshotId];
1739
+ }
1740
+ }
1741
+ if (Object.keys(batches).length === 0)
1742
+ delete pending[runId];
1743
+ }
1744
+ for (const [runId, batch] of input.batches) {
1745
+ const committed = commits[runId]?.find((entry) => entry.snapshotId === batch.snapshotId);
1746
+ if (committed) {
1747
+ if (committed.lastSequence !== batch.lastSequence ||
1748
+ committed.expectedEventCount !== batch.expectedEventCount) {
1749
+ throw new TypeError("Agent chat event snapshot id was reused.");
1750
+ }
1751
+ continue;
1752
+ }
1753
+ const knownSequence = Math.max(watermarks.get(runId) ?? 0, input.existingRunSequences.get(runId) ?? 0);
1754
+ const hasKnownSequence = watermarks.has(runId) || input.existingRunSequences.has(runId);
1755
+ const alreadyCommittedAtOrAfter = (commits[runId] ?? []).some((entry) => entry.lastSequence >= batch.lastSequence);
1756
+ if (hasKnownSequence &&
1757
+ (batch.lastSequence < knownSequence || alreadyCommittedAtOrAfter)) {
1758
+ if (pending[runId]) {
1759
+ delete pending[runId][batch.snapshotId];
1760
+ if (Object.keys(pending[runId]).length === 0)
1761
+ delete pending[runId];
1762
+ }
1763
+ continue;
1764
+ }
1765
+ const current = pending[runId]?.[batch.snapshotId];
1766
+ const matchingCurrent = current?.lastSequence === batch.lastSequence &&
1767
+ current.expectedEventCount === batch.expectedEventCount;
1768
+ const batchEvents = incomingEvents.filter((event) => event?.runId === runId);
1769
+ const stagedEvents = mergeAgentKitEvents(matchingCurrent ? current.events : [], batchEvents) ??
1770
+ [];
1771
+ if (stagedEvents.length > batch.expectedEventCount) {
1772
+ throw new TypeError("Agent chat event snapshot exceeded its event count.");
1773
+ }
1774
+ if (batch.complete && stagedEvents.length < batch.expectedEventCount) {
1775
+ throw new TypeError("Agent chat event snapshot ended before all events arrived.");
1776
+ }
1777
+ if (stagedEvents.length === batch.expectedEventCount) {
1778
+ events = mergeAgentKitEvents(events, stagedEvents, new Set([runId]));
1779
+ watermarks.set(runId, Math.max(knownSequence, batch.lastSequence));
1780
+ commits[runId] = [
1781
+ ...(commits[runId] ?? []).filter((entry) => entry.snapshotId !== batch.snapshotId),
1782
+ {
1783
+ snapshotId: batch.snapshotId,
1784
+ lastSequence: batch.lastSequence,
1785
+ expectedEventCount: batch.expectedEventCount,
1786
+ },
1787
+ ].slice(-MAX_COMMITTED_EVENT_RUN_SNAPSHOTS_PER_RUN);
1788
+ if (pending[runId]) {
1789
+ for (const [snapshotId, pendingBatch] of Object.entries(pending[runId])) {
1790
+ if (pendingBatch.lastSequence <= batch.lastSequence) {
1791
+ delete pending[runId][snapshotId];
1792
+ }
1793
+ }
1794
+ if (Object.keys(pending[runId]).length === 0)
1795
+ delete pending[runId];
1796
+ }
1797
+ continue;
1798
+ }
1799
+ const runPending = { ...(pending[runId] ?? {}) };
1800
+ if (!matchingCurrent) {
1801
+ while (Object.keys(runPending).length >=
1802
+ MAX_PENDING_EVENT_RUN_SNAPSHOTS_PER_RUN) {
1803
+ const oldestSnapshotId = Object.keys(runPending)[0];
1804
+ if (oldestSnapshotId === undefined)
1805
+ break;
1806
+ delete runPending[oldestSnapshotId];
1807
+ }
1808
+ }
1809
+ runPending[batch.snapshotId] = {
1810
+ lastSequence: batch.lastSequence,
1811
+ expectedEventCount: batch.expectedEventCount,
1812
+ events: stagedEvents,
1813
+ };
1814
+ pending[runId] = runPending;
1815
+ }
1816
+ return { events, watermarks, pending, commits };
1817
+ }
1818
+ function mergeAgentKitAnnotations(existing, incoming, replaceMessageIds, removalValuesByMessage, conditionalUpserts, onConflict) {
1819
+ if (!Array.isArray(existing) && !Array.isArray(incoming))
1820
+ return undefined;
1821
+ const annotationKey = (entry) => {
1822
+ const id = entry?.annotation?.id;
1823
+ return typeof id === "string"
1824
+ ? JSON.stringify(["id", id])
1825
+ : JSON.stringify(["value", entry]);
1826
+ };
1827
+ const merged = (Array.isArray(existing) ? existing : []).filter((entry) => {
1828
+ const messageId = entry?.messageId;
1829
+ if (replaceMessageIds.has(messageId))
1830
+ return false;
1831
+ const removalValues = removalValuesByMessage.get(messageId);
1832
+ const baseline = removalValues?.get(annotationKey(entry));
1833
+ if (baseline === undefined)
1834
+ return true;
1835
+ if (JSON.stringify(entry) === baseline)
1836
+ return false;
1837
+ if (typeof messageId === "string") {
1838
+ const annotationId = entry?.annotation?.id;
1839
+ onConflict?.({
1840
+ messageId,
1841
+ ...(typeof annotationId === "string" ? { annotationId } : {}),
1842
+ operation: "remove",
1843
+ });
1844
+ }
1845
+ return true;
1846
+ });
1847
+ const positions = new Map();
1848
+ merged.forEach((entry, index) => {
1849
+ if (typeof entry?.messageId !== "string")
1850
+ return;
1851
+ const key = JSON.stringify([entry.messageId, annotationKey(entry)]);
1852
+ if (!positions.has(key))
1853
+ positions.set(key, index);
1854
+ });
1855
+ if (Array.isArray(conditionalUpserts)) {
1856
+ for (const update of conditionalUpserts) {
1857
+ if (!update ||
1858
+ typeof update !== "object" ||
1859
+ !update.entry ||
1860
+ typeof update.entry !== "object" ||
1861
+ Array.isArray(update.entry) ||
1862
+ (update.baseline !== null &&
1863
+ (!update.baseline ||
1864
+ typeof update.baseline !== "object" ||
1865
+ Array.isArray(update.baseline)))) {
1866
+ continue;
1867
+ }
1868
+ const entry = update.entry;
1869
+ if (typeof entry.messageId !== "string") {
1870
+ merged.push(entry);
1871
+ continue;
1872
+ }
1873
+ const key = JSON.stringify([entry.messageId, annotationKey(entry)]);
1874
+ const index = positions.get(key);
1875
+ if (index === undefined) {
1876
+ if (update.baseline === null) {
1877
+ positions.set(key, merged.length);
1878
+ merged.push(entry);
1879
+ }
1880
+ else {
1881
+ const annotationId = entry.annotation?.id;
1882
+ onConflict?.({
1883
+ messageId: entry.messageId,
1884
+ ...(typeof annotationId === "string" ? { annotationId } : {}),
1885
+ operation: "upsert",
1886
+ });
1887
+ }
1888
+ continue;
1889
+ }
1890
+ if (JSON.stringify(merged[index]) === JSON.stringify(entry))
1891
+ continue;
1892
+ if (update.baseline !== null &&
1893
+ JSON.stringify(merged[index]) === JSON.stringify(update.baseline)) {
1894
+ merged[index] = entry;
1895
+ }
1896
+ else {
1897
+ const annotationId = entry.annotation?.id;
1898
+ onConflict?.({
1899
+ messageId: entry.messageId,
1900
+ ...(typeof annotationId === "string" ? { annotationId } : {}),
1901
+ operation: "upsert",
1902
+ });
1903
+ }
1904
+ }
1905
+ }
1906
+ else
1907
+ for (const entry of Array.isArray(incoming) ? incoming : []) {
1908
+ if (typeof entry?.messageId !== "string") {
1909
+ merged.push(entry);
1910
+ continue;
1911
+ }
1912
+ const key = JSON.stringify([entry.messageId, annotationKey(entry)]);
1913
+ const index = positions.get(key);
1914
+ if (index === undefined) {
1915
+ positions.set(key, merged.length);
1916
+ merged.push(entry);
1917
+ }
1918
+ else {
1919
+ merged[index] = entry;
1920
+ }
1921
+ }
1922
+ return merged;
1923
+ }
1924
+ function mergeAgentKitHistory(existing, incoming, promptRunIds, onAnnotationConflict) {
1925
+ const incomingIsRecord = incoming !== null &&
1926
+ typeof incoming === "object" &&
1927
+ !Array.isArray(incoming);
1928
+ if (!incomingIsRecord)
1481
1929
  return incoming ?? existing;
1930
+ const next = incoming;
1931
+ const existingIsRecord = existing !== null &&
1932
+ typeof existing === "object" &&
1933
+ !Array.isArray(existing);
1934
+ const snapshotDelta = next._snapshotDelta === true;
1935
+ const incomingEventRunIds = new Set((Array.isArray(next.events) ? next.events : []).flatMap((event) => typeof event?.runId === "string" ? [event.runId] : []));
1936
+ const incomingRunStarts = new Set((Array.isArray(next.events) ? next.events : []).flatMap((event) => typeof event?.runId === "string" && event.sequence === 1
1937
+ ? [event.runId]
1938
+ : []));
1939
+ const eventRunSnapshotBatches = new Map();
1940
+ for (const batch of Array.isArray(next.eventRunSnapshotBatches)
1941
+ ? next.eventRunSnapshotBatches
1942
+ : []) {
1943
+ if (typeof batch?.runId === "string" &&
1944
+ typeof batch.snapshotId === "string" &&
1945
+ batch.snapshotId.length > 0 &&
1946
+ typeof batch.lastSequence === "number" &&
1947
+ Number.isSafeInteger(batch.lastSequence) &&
1948
+ batch.lastSequence >= 0 &&
1949
+ typeof batch.expectedEventCount === "number" &&
1950
+ Number.isSafeInteger(batch.expectedEventCount) &&
1951
+ batch.expectedEventCount > 0 &&
1952
+ typeof batch.complete === "boolean" &&
1953
+ incomingEventRunIds.has(batch.runId)) {
1954
+ eventRunSnapshotBatches.set(batch.runId, {
1955
+ runId: batch.runId,
1956
+ snapshotId: batch.snapshotId,
1957
+ lastSequence: batch.lastSequence,
1958
+ expectedEventCount: batch.expectedEventCount,
1959
+ complete: batch.complete,
1960
+ });
1961
+ }
1962
+ }
1963
+ const eventRunSnapshotWatermarks = new Map();
1964
+ for (const watermark of Array.isArray(next.eventRunSnapshotWatermarks)
1965
+ ? next.eventRunSnapshotWatermarks
1966
+ : []) {
1967
+ if (typeof watermark?.runId === "string" &&
1968
+ typeof watermark.lastSequence === "number" &&
1969
+ Number.isSafeInteger(watermark.lastSequence) &&
1970
+ watermark.lastSequence >= 0 &&
1971
+ incomingEventRunIds.has(watermark.runId) &&
1972
+ !eventRunSnapshotBatches.has(watermark.runId)) {
1973
+ eventRunSnapshotWatermarks.set(watermark.runId, Math.max(eventRunSnapshotWatermarks.get(watermark.runId) ?? 0, watermark.lastSequence));
1974
+ }
1975
+ }
1976
+ const eventRunReplacements = new Map();
1977
+ for (const replacement of Array.isArray(next.eventRunReplacements)
1978
+ ? next.eventRunReplacements
1979
+ : []) {
1980
+ if (typeof replacement?.runId === "string" &&
1981
+ typeof replacement.lastSequence === "number" &&
1982
+ Number.isSafeInteger(replacement.lastSequence) &&
1983
+ replacement.lastSequence >= 1 &&
1984
+ incomingRunStarts.has(replacement.runId) &&
1985
+ !eventRunSnapshotBatches.has(replacement.runId)) {
1986
+ eventRunReplacements.set(replacement.runId, Math.max(eventRunReplacements.get(replacement.runId) ?? 0, replacement.lastSequence));
1987
+ }
1988
+ }
1989
+ for (const [runId, lastSequence] of eventRunReplacements) {
1990
+ eventRunSnapshotWatermarks.set(runId, Math.max(eventRunSnapshotWatermarks.get(runId) ?? 0, lastSequence));
1991
+ }
1992
+ const annotationMessageIdsToReplace = new Set();
1993
+ const annotationRemovalValuesByMessage = new Map();
1994
+ for (const replacement of Array.isArray(next.annotationMessageIdsToReplace)
1995
+ ? next.annotationMessageIdsToReplace
1996
+ : []) {
1997
+ if (typeof replacement === "string") {
1998
+ annotationMessageIdsToReplace.add(replacement);
1999
+ continue;
2000
+ }
2001
+ if (!replacement ||
2002
+ typeof replacement !== "object" ||
2003
+ typeof replacement.messageId !== "string" ||
2004
+ !Array.isArray(replacement.annotationsToRemove)) {
2005
+ continue;
2006
+ }
2007
+ const removalValues = new Map();
2008
+ for (const removal of replacement.annotationsToRemove) {
2009
+ if (!removal ||
2010
+ typeof removal !== "object" ||
2011
+ typeof removal.key !== "string" ||
2012
+ !removal.baseline ||
2013
+ typeof removal.baseline !== "object" ||
2014
+ Array.isArray(removal.baseline)) {
2015
+ continue;
2016
+ }
2017
+ const baseline = JSON.stringify(removal.baseline);
2018
+ if (typeof baseline === "string") {
2019
+ removalValues.set(removal.key, baseline);
2020
+ }
2021
+ }
2022
+ if (removalValues.size > 0) {
2023
+ annotationRemovalValuesByMessage.set(replacement.messageId, removalValues);
2024
+ }
2025
+ }
2026
+ if (!existingIsRecord) {
2027
+ const initial = { ...next };
2028
+ delete initial._eventRunWatermarks;
2029
+ if (snapshotDelta) {
2030
+ if (Array.isArray(initial.messages) && initial.messages.length === 0) {
2031
+ delete initial.messages;
2032
+ }
2033
+ const eventSnapshot = mergeAgentKitEventRunSnapshots({
2034
+ existingEvents: undefined,
2035
+ incomingEvents: next.events,
2036
+ existingWatermarks: new Map(),
2037
+ existingRunSequences: new Map(),
2038
+ incomingWatermarks: eventRunSnapshotWatermarks,
2039
+ incomingReplacements: eventRunReplacements,
2040
+ batches: eventRunSnapshotBatches,
2041
+ existingPending: undefined,
2042
+ existingCommits: undefined,
2043
+ });
2044
+ if (eventSnapshot.events)
2045
+ initial.events = eventSnapshot.events;
2046
+ if (eventSnapshot.watermarks.size > 0) {
2047
+ initial._eventRunWatermarks = Object.fromEntries(eventSnapshot.watermarks);
2048
+ }
2049
+ if (Object.keys(eventSnapshot.pending).length > 0) {
2050
+ initial._pendingEventRunSnapshots = eventSnapshot.pending;
2051
+ }
2052
+ else {
2053
+ delete initial._pendingEventRunSnapshots;
2054
+ }
2055
+ if (Object.keys(eventSnapshot.commits).length > 0) {
2056
+ initial._eventRunSnapshotCommits = eventSnapshot.commits;
2057
+ }
2058
+ else {
2059
+ delete initial._eventRunSnapshotCommits;
2060
+ }
2061
+ const annotations = mergeAgentKitAnnotations(undefined, next.annotations, annotationMessageIdsToReplace, annotationRemovalValuesByMessage, next.annotationUpserts, onAnnotationConflict);
2062
+ if (annotations)
2063
+ initial.annotations = annotations;
2064
+ }
2065
+ delete initial._snapshotDelta;
2066
+ delete initial.eventRunReplacements;
2067
+ delete initial.eventRunSnapshotWatermarks;
2068
+ delete initial.eventRunSnapshotBatches;
2069
+ delete initial.annotationMessageIdsToReplace;
2070
+ delete initial.annotationUpserts;
2071
+ return initial;
1482
2072
  }
1483
2073
  const previous = existing;
1484
- const next = incoming;
1485
2074
  const merged = { ...previous, ...next };
1486
2075
  const runs = new Map();
1487
2076
  for (const run of [
@@ -1496,29 +2085,76 @@ function mergeAgentKitHistory(existing, incoming, promptRunIds) {
1496
2085
  continue;
1497
2086
  }
1498
2087
  const richer = newer.lastSequence >= run.lastSequence ? newer : run;
2088
+ const statusRun = run.lastSequence > newer.lastSequence
2089
+ ? run
2090
+ : newer.lastSequence > run.lastSequence
2091
+ ? newer
2092
+ : isTerminalRunStatus(run.status)
2093
+ ? run
2094
+ : newer;
1499
2095
  runs.set(run.id, {
1500
2096
  ...run,
1501
2097
  ...newer,
1502
2098
  ...richer,
1503
2099
  metadata: { ...run.metadata, ...newer.metadata, ...richer.metadata },
1504
2100
  lastSequence: Math.max(newer.lastSequence, run.lastSequence),
1505
- status: ["completed", "failed", "cancelled"].includes(run.status)
1506
- ? run.status
1507
- : newer.status,
2101
+ status: statusRun.status,
1508
2102
  });
1509
2103
  }
1510
2104
  if (runs.size > 0) {
1511
2105
  merged.runs = [...runs.values()];
1512
2106
  const latest = latestSnapshotRun(merged.runs);
2107
+ const incomingLatest = latestSnapshotRun(next.runs);
1513
2108
  const previousLatest = latestSnapshotRun(previous.runs);
1514
- merged.suggestions =
1515
- latest?.status !== "completed"
1516
- ? []
1517
- : latest.id === previousLatest?.id &&
1518
- previousLatest.status === "completed" &&
1519
- Array.isArray(previous.suggestions)
1520
- ? previous.suggestions
1521
- : next.suggestions;
2109
+ const incomingLatestCompleted = incomingLatest?.status === "completed";
2110
+ const incomingLatestIsMergedLatest = incomingLatest?.id === latest?.id && incomingLatestCompleted;
2111
+ const newLatestRunCompletion = incomingLatestIsMergedLatest && latest?.id !== previousLatest?.id;
2112
+ const latestRunJustCompleted = incomingLatestIsMergedLatest &&
2113
+ incomingLatest !== undefined &&
2114
+ latest !== undefined &&
2115
+ previousLatest !== undefined &&
2116
+ previousLatest.id === latest.id &&
2117
+ previousLatest.status !== "completed" &&
2118
+ Number.isSafeInteger(incomingLatest.lastSequence) &&
2119
+ incomingLatest.lastSequence >= 0 &&
2120
+ Number.isSafeInteger(previousLatest.lastSequence) &&
2121
+ previousLatest.lastSequence >= 0 &&
2122
+ incomingLatest.lastSequence >= previousLatest.lastSequence;
2123
+ const sameCompletedRunAdvanced = incomingLatestIsMergedLatest &&
2124
+ incomingLatest !== undefined &&
2125
+ latest !== undefined &&
2126
+ previousLatest !== undefined &&
2127
+ incomingLatest.id === latest.id &&
2128
+ previousLatest.id === latest.id &&
2129
+ previousLatest.status === "completed" &&
2130
+ Number.isSafeInteger(incomingLatest.lastSequence) &&
2131
+ incomingLatest.lastSequence >= 0 &&
2132
+ Number.isSafeInteger(previousLatest.lastSequence) &&
2133
+ previousLatest.lastSequence >= 0 &&
2134
+ incomingLatest.lastSequence > previousLatest.lastSequence;
2135
+ const incomingSuggestionsAreCurrent = Object.prototype.hasOwnProperty.call(next, "suggestions") &&
2136
+ (newLatestRunCompletion ||
2137
+ latestRunJustCompleted ||
2138
+ sameCompletedRunAdvanced);
2139
+ if (latest?.status !== "completed") {
2140
+ merged.suggestions = [];
2141
+ }
2142
+ else if (incomingSuggestionsAreCurrent) {
2143
+ merged.suggestions = next.suggestions;
2144
+ }
2145
+ else if (previousLatest !== undefined &&
2146
+ latest.id === previousLatest.id &&
2147
+ previousLatest.status === "completed") {
2148
+ if (Object.prototype.hasOwnProperty.call(previous, "suggestions")) {
2149
+ merged.suggestions = previous.suggestions;
2150
+ }
2151
+ else {
2152
+ delete merged.suggestions;
2153
+ }
2154
+ }
2155
+ else {
2156
+ merged.suggestions = [];
2157
+ }
1522
2158
  if (Array.isArray(merged.suggestions)) {
1523
2159
  merged.suggestions = merged.suggestions.filter((suggestion) => suggestion?.runId === latest?.id);
1524
2160
  }
@@ -1537,6 +2173,92 @@ function mergeAgentKitHistory(existing, incoming, promptRunIds) {
1537
2173
  if (entries)
1538
2174
  merged[key] = entries;
1539
2175
  }
2176
+ if (snapshotDelta &&
2177
+ Array.isArray(next.messages) &&
2178
+ next.messages.length === 0 &&
2179
+ !Array.isArray(previous.messages)) {
2180
+ delete merged.messages;
2181
+ }
2182
+ if (snapshotDelta) {
2183
+ const previousWatermarks = new Map();
2184
+ const previousWatermarkRecord = previous._eventRunWatermarks;
2185
+ if (previousWatermarkRecord &&
2186
+ typeof previousWatermarkRecord === "object" &&
2187
+ !Array.isArray(previousWatermarkRecord)) {
2188
+ for (const [runId, value] of Object.entries(previousWatermarkRecord)) {
2189
+ if (typeof value === "number" &&
2190
+ Number.isSafeInteger(value) &&
2191
+ value >= 0) {
2192
+ previousWatermarks.set(runId, value);
2193
+ }
2194
+ }
2195
+ }
2196
+ const previousRunSequences = new Map();
2197
+ for (const run of Array.isArray(previous.runs) ? previous.runs : []) {
2198
+ if (typeof run?.id === "string" &&
2199
+ typeof run.lastSequence === "number" &&
2200
+ Number.isSafeInteger(run.lastSequence) &&
2201
+ run.lastSequence >= 0) {
2202
+ previousRunSequences.set(run.id, run.lastSequence);
2203
+ }
2204
+ }
2205
+ const eventSnapshot = mergeAgentKitEventRunSnapshots({
2206
+ existingEvents: previous.events,
2207
+ incomingEvents: next.events,
2208
+ existingWatermarks: previousWatermarks,
2209
+ existingRunSequences: previousRunSequences,
2210
+ incomingWatermarks: eventRunSnapshotWatermarks,
2211
+ incomingReplacements: eventRunReplacements,
2212
+ batches: eventRunSnapshotBatches,
2213
+ existingPending: previous._pendingEventRunSnapshots,
2214
+ existingCommits: previous._eventRunSnapshotCommits,
2215
+ });
2216
+ if (eventSnapshot.events)
2217
+ merged.events = eventSnapshot.events;
2218
+ delete merged._eventRunWatermarks;
2219
+ if (eventSnapshot.watermarks.size > 0) {
2220
+ merged._eventRunWatermarks = Object.fromEntries(eventSnapshot.watermarks);
2221
+ }
2222
+ if (Object.keys(eventSnapshot.pending).length > 0) {
2223
+ merged._pendingEventRunSnapshots = eventSnapshot.pending;
2224
+ }
2225
+ else {
2226
+ delete merged._pendingEventRunSnapshots;
2227
+ }
2228
+ if (Object.keys(eventSnapshot.commits).length > 0) {
2229
+ merged._eventRunSnapshotCommits = eventSnapshot.commits;
2230
+ }
2231
+ else {
2232
+ delete merged._eventRunSnapshotCommits;
2233
+ }
2234
+ }
2235
+ else {
2236
+ const events = mergeAgentKitFullEventSnapshot(previous.events, next.events, next.runs, previous._eventRunSnapshotCommits);
2237
+ if (events)
2238
+ merged.events = events;
2239
+ if (previous._eventRunWatermarks === undefined) {
2240
+ delete merged._eventRunWatermarks;
2241
+ }
2242
+ else {
2243
+ merged._eventRunWatermarks = previous._eventRunWatermarks;
2244
+ }
2245
+ }
2246
+ delete merged._snapshotDelta;
2247
+ delete merged.eventRunReplacements;
2248
+ delete merged.eventRunSnapshotWatermarks;
2249
+ delete merged.eventRunSnapshotBatches;
2250
+ if (snapshotDelta ||
2251
+ annotationMessageIdsToReplace.size > 0 ||
2252
+ annotationRemovalValuesByMessage.size > 0) {
2253
+ const annotations = mergeAgentKitAnnotations(previous.annotations, next.annotations, annotationMessageIdsToReplace, annotationRemovalValuesByMessage, next.annotationUpserts, onAnnotationConflict);
2254
+ if (annotations)
2255
+ merged.annotations = annotations;
2256
+ }
2257
+ else if (Array.isArray(next.annotations)) {
2258
+ merged.annotations = next.annotations;
2259
+ }
2260
+ delete merged.annotationMessageIdsToReplace;
2261
+ delete merged.annotationUpserts;
1540
2262
  return merged;
1541
2263
  }
1542
2264
  function pruneClaimedQueuedMessages(repo) {
@@ -1579,7 +2301,7 @@ export function mergeThreadDataForClientSave(existingRepo, incomingRepo, options
1579
2301
  }
1580
2302
  const promptRunIds = submittedPromptRunIds(existingNormalized?.messages, incomingNormalized?.messages);
1581
2303
  if (merged.agentKit !== undefined) {
1582
- merged.agentKit = mergeAgentKitHistory(existingNormalized?.agentKit, merged.agentKit, promptRunIds);
2304
+ merged.agentKit = mergeAgentKitHistory(existingNormalized?.agentKit, merged.agentKit, promptRunIds, options.onAnnotationConflict);
1583
2305
  }
1584
2306
  const existingMessages = Array.isArray(existingNormalized?.messages)
1585
2307
  ? existingNormalized.messages