@mastra/duckdb 1.3.2-alpha.0 → 1.4.0-alpha.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.
@@ -5,13 +5,30 @@ var error = require('@mastra/core/error');
5
5
  var storage = require('@mastra/core/storage');
6
6
  var observability = require('@mastra/core/observability');
7
7
  var utils = require('@mastra/core/utils');
8
+ var features = require('@mastra/core/features');
8
9
 
9
10
  // src/storage/domains/observability/ddl.ts
11
+ var SPAN_EVENTS_CURSOR_SEQUENCE_DDL = `
12
+ CREATE SEQUENCE IF NOT EXISTS span_events_cursor_id_seq START 1
13
+ `;
14
+ var METRIC_EVENTS_CURSOR_SEQUENCE_DDL = `
15
+ CREATE SEQUENCE IF NOT EXISTS metric_events_cursor_id_seq START 1
16
+ `;
17
+ var LOG_EVENTS_CURSOR_SEQUENCE_DDL = `
18
+ CREATE SEQUENCE IF NOT EXISTS log_events_cursor_id_seq START 1
19
+ `;
20
+ var SCORE_EVENTS_CURSOR_SEQUENCE_DDL = `
21
+ CREATE SEQUENCE IF NOT EXISTS score_events_cursor_id_seq START 1
22
+ `;
23
+ var FEEDBACK_EVENTS_CURSOR_SEQUENCE_DDL = `
24
+ CREATE SEQUENCE IF NOT EXISTS feedback_events_cursor_id_seq START 1
25
+ `;
10
26
  var SPAN_EVENTS_DDL = `
11
27
  CREATE TABLE IF NOT EXISTS span_events (
12
28
  -- Event metadata
13
29
  eventType VARCHAR NOT NULL,
14
30
  timestamp TIMESTAMP NOT NULL,
31
+ cursorId BIGINT DEFAULT nextval('span_events_cursor_id_seq'),
15
32
 
16
33
  -- IDs
17
34
  traceId VARCHAR NOT NULL,
@@ -58,6 +75,7 @@ var METRIC_EVENTS_DDL = `
58
75
  CREATE TABLE IF NOT EXISTS metric_events (
59
76
  -- Event metadata
60
77
  timestamp TIMESTAMP NOT NULL,
78
+ cursorId BIGINT DEFAULT nextval('metric_events_cursor_id_seq'),
61
79
 
62
80
  -- IDs
63
81
  metricId VARCHAR NOT NULL PRIMARY KEY,
@@ -110,6 +128,7 @@ var LOG_EVENTS_DDL = `
110
128
  CREATE TABLE IF NOT EXISTS log_events (
111
129
  -- Event metadata
112
130
  timestamp TIMESTAMP NOT NULL,
131
+ cursorId BIGINT DEFAULT nextval('log_events_cursor_id_seq'),
113
132
 
114
133
  -- IDs
115
134
  logId VARCHAR NOT NULL PRIMARY KEY,
@@ -157,6 +176,7 @@ var SCORE_EVENTS_DDL = `
157
176
  CREATE TABLE IF NOT EXISTS score_events (
158
177
  -- Event metadata
159
178
  timestamp TIMESTAMP NOT NULL,
179
+ cursorId BIGINT DEFAULT nextval('score_events_cursor_id_seq'),
160
180
 
161
181
  -- IDs
162
182
  scoreId VARCHAR NOT NULL PRIMARY KEY,
@@ -208,6 +228,7 @@ var FEEDBACK_EVENTS_DDL = `
208
228
  CREATE TABLE IF NOT EXISTS feedback_events (
209
229
  -- Event metadata
210
230
  timestamp TIMESTAMP NOT NULL,
231
+ cursorId BIGINT DEFAULT nextval('feedback_events_cursor_id_seq'),
211
232
 
212
233
  -- IDs
213
234
  feedbackId VARCHAR NOT NULL PRIMARY KEY,
@@ -256,11 +277,32 @@ CREATE TABLE IF NOT EXISTS feedback_events (
256
277
  metadata JSON,
257
278
  scope JSON
258
279
  )`;
259
- var ALL_DDL = [SPAN_EVENTS_DDL, METRIC_EVENTS_DDL, LOG_EVENTS_DDL, SCORE_EVENTS_DDL, FEEDBACK_EVENTS_DDL];
280
+ var ALL_DDL = [
281
+ SPAN_EVENTS_CURSOR_SEQUENCE_DDL,
282
+ METRIC_EVENTS_CURSOR_SEQUENCE_DDL,
283
+ LOG_EVENTS_CURSOR_SEQUENCE_DDL,
284
+ SCORE_EVENTS_CURSOR_SEQUENCE_DDL,
285
+ FEEDBACK_EVENTS_CURSOR_SEQUENCE_DDL,
286
+ SPAN_EVENTS_DDL,
287
+ METRIC_EVENTS_DDL,
288
+ LOG_EVENTS_DDL,
289
+ SCORE_EVENTS_DDL,
290
+ FEEDBACK_EVENTS_DDL
291
+ ];
260
292
  var ALL_MIGRATIONS = [
261
- // Span events
293
+ `CREATE SEQUENCE IF NOT EXISTS span_events_cursor_id_seq START 1`,
294
+ `CREATE SEQUENCE IF NOT EXISTS metric_events_cursor_id_seq START 1`,
295
+ `CREATE SEQUENCE IF NOT EXISTS log_events_cursor_id_seq START 1`,
296
+ `CREATE SEQUENCE IF NOT EXISTS score_events_cursor_id_seq START 1`,
297
+ `CREATE SEQUENCE IF NOT EXISTS feedback_events_cursor_id_seq START 1`,
298
+ // Span events. Existing rows intentionally keep NULL cursorId values; delta
299
+ // polling only applies to rows written after this migration path is in place.
300
+ `ALTER TABLE span_events ADD COLUMN IF NOT EXISTS cursorId BIGINT`,
301
+ `ALTER TABLE span_events ALTER COLUMN cursorId SET DEFAULT nextval('span_events_cursor_id_seq')`,
262
302
  `ALTER TABLE span_events ADD COLUMN IF NOT EXISTS entityVersionId VARCHAR`,
263
- // Metrics
303
+ // Metrics. Legacy rows remain page-visible but are not part of delta polling.
304
+ `ALTER TABLE metric_events ADD COLUMN IF NOT EXISTS cursorId BIGINT`,
305
+ `ALTER TABLE metric_events ALTER COLUMN cursorId SET DEFAULT nextval('metric_events_cursor_id_seq')`,
264
306
  `ALTER TABLE metric_events ADD COLUMN IF NOT EXISTS entityVersionId VARCHAR`,
265
307
  `ALTER TABLE metric_events ADD COLUMN IF NOT EXISTS parentEntityVersionId VARCHAR`,
266
308
  `ALTER TABLE metric_events ADD COLUMN IF NOT EXISTS rootEntityVersionId VARCHAR`,
@@ -284,7 +326,9 @@ var ALL_MIGRATIONS = [
284
326
  `ALTER TABLE metric_events ADD COLUMN IF NOT EXISTS costMetadata JSON`,
285
327
  `ALTER TABLE metric_events ADD COLUMN IF NOT EXISTS metadata JSON`,
286
328
  `ALTER TABLE metric_events ADD COLUMN IF NOT EXISTS scope JSON`,
287
- // Logs
329
+ // Logs. Legacy rows remain page-visible but are not part of delta polling.
330
+ `ALTER TABLE log_events ADD COLUMN IF NOT EXISTS cursorId BIGINT`,
331
+ `ALTER TABLE log_events ALTER COLUMN cursorId SET DEFAULT nextval('log_events_cursor_id_seq')`,
288
332
  `ALTER TABLE log_events ADD COLUMN IF NOT EXISTS entityVersionId VARCHAR`,
289
333
  `ALTER TABLE log_events ADD COLUMN IF NOT EXISTS parentEntityVersionId VARCHAR`,
290
334
  `ALTER TABLE log_events ADD COLUMN IF NOT EXISTS rootEntityVersionId VARCHAR`,
@@ -308,7 +352,9 @@ var ALL_MIGRATIONS = [
308
352
  `ALTER TABLE log_events ADD COLUMN IF NOT EXISTS tags JSON`,
309
353
  `ALTER TABLE log_events ADD COLUMN IF NOT EXISTS metadata JSON`,
310
354
  `ALTER TABLE log_events ADD COLUMN IF NOT EXISTS scope JSON`,
311
- // Scores
355
+ // Scores. Legacy rows remain page-visible but are not part of delta polling.
356
+ `ALTER TABLE score_events ADD COLUMN IF NOT EXISTS cursorId BIGINT`,
357
+ `ALTER TABLE score_events ALTER COLUMN cursorId SET DEFAULT nextval('score_events_cursor_id_seq')`,
312
358
  `ALTER TABLE score_events ADD COLUMN IF NOT EXISTS entityVersionId VARCHAR`,
313
359
  `ALTER TABLE score_events ADD COLUMN IF NOT EXISTS parentEntityVersionId VARCHAR`,
314
360
  `ALTER TABLE score_events ADD COLUMN IF NOT EXISTS rootEntityVersionId VARCHAR`,
@@ -336,7 +382,9 @@ var ALL_MIGRATIONS = [
336
382
  `ALTER TABLE score_events ADD COLUMN IF NOT EXISTS source VARCHAR`,
337
383
  `ALTER TABLE score_events ADD COLUMN IF NOT EXISTS scoreSource VARCHAR`,
338
384
  `ALTER TABLE score_events ALTER COLUMN traceId DROP NOT NULL`,
339
- // Feedback
385
+ // Feedback. Legacy rows remain page-visible but are not part of delta polling.
386
+ `ALTER TABLE feedback_events ADD COLUMN IF NOT EXISTS cursorId BIGINT`,
387
+ `ALTER TABLE feedback_events ALTER COLUMN cursorId SET DEFAULT nextval('feedback_events_cursor_id_seq')`,
340
388
  `ALTER TABLE feedback_events ADD COLUMN IF NOT EXISTS entityVersionId VARCHAR`,
341
389
  `ALTER TABLE feedback_events ADD COLUMN IF NOT EXISTS parentEntityVersionId VARCHAR`,
342
390
  `ALTER TABLE feedback_events ADD COLUMN IF NOT EXISTS rootEntityVersionId VARCHAR`,
@@ -600,6 +648,45 @@ function parseJsonArray(value) {
600
648
  const parsed = parseJson(value);
601
649
  return Array.isArray(parsed) ? parsed : null;
602
650
  }
651
+ var OBSERVABILITY_DELTA_POLLING_FEATURE = "observability-delta-polling";
652
+ function deltaPollingFeatureEnabled() {
653
+ return features.coreFeatures.has(OBSERVABILITY_DELTA_POLLING_FEATURE);
654
+ }
655
+ function assertDeltaPollingEnabled() {
656
+ if (deltaPollingFeatureEnabled()) {
657
+ return;
658
+ }
659
+ throw new error.MastraError({
660
+ id: "OBSERVABILITY_DELTA_POLLING_NOT_SUPPORTED",
661
+ domain: error.ErrorDomain.MASTRA_OBSERVABILITY,
662
+ category: error.ErrorCategory.SYSTEM,
663
+ text: "This storage provider does not support observability delta polling"
664
+ });
665
+ }
666
+ function encodeDeltaCursor(value) {
667
+ return String(value ?? 0);
668
+ }
669
+ function validateCursorId(cursor) {
670
+ if (!/^\d+$/.test(cursor)) {
671
+ throw new error.MastraError({
672
+ id: "OBSERVABILITY_INVALID_DELTA_CURSOR",
673
+ domain: error.ErrorDomain.MASTRA_OBSERVABILITY,
674
+ category: error.ErrorCategory.USER,
675
+ text: "Invalid observability delta cursor"
676
+ });
677
+ }
678
+ return cursor;
679
+ }
680
+ function extendWhereClause(baseClause, extraConditions) {
681
+ const conditions = extraConditions.filter(Boolean);
682
+ if (conditions.length === 0) {
683
+ return baseClause;
684
+ }
685
+ if (!baseClause) {
686
+ return `WHERE ${conditions.join(" AND ")}`;
687
+ }
688
+ return `${baseClause} AND ${conditions.join(" AND ")}`;
689
+ }
603
690
 
604
691
  // src/storage/domains/observability/feedback.ts
605
692
  var FEEDBACK_GROUP_BY_COLUMNS = /* @__PURE__ */ new Set([
@@ -899,15 +986,41 @@ async function batchCreateFeedback(db, args) {
899
986
  );
900
987
  }
901
988
  async function listFeedback(db, args) {
902
- const filters = args.filters ?? {};
903
- const page = Number(args.pagination?.page ?? 0);
904
- const perPage = Number(args.pagination?.perPage ?? 10);
905
- const orderBy = { field: args.orderBy?.field ?? "timestamp", direction: args.orderBy?.direction ?? "DESC" };
989
+ const { mode, filters, pagination, orderBy, after, limit } = storage.listFeedbackArgsSchema.parse(args);
990
+ const page = Number(pagination.page);
991
+ const perPage = Number(pagination.perPage);
906
992
  const { clause: filterClause, params: filterParams } = buildWhereClause(filters, {
907
993
  source: "feedbackSource"
908
994
  });
995
+ if (mode === "delta") {
996
+ assertDeltaPollingEnabled();
997
+ const streamHeadCursor = await getStreamHeadCursor(db);
998
+ if (after === void 0) {
999
+ return {
1000
+ feedback: [],
1001
+ delta: { limit, hasMore: false },
1002
+ deltaCursor: streamHeadCursor
1003
+ };
1004
+ }
1005
+ const afterCursorId = validateCursorId(after);
1006
+ const deltaWhereClause = extendWhereClause(filterClause, ["cursorId IS NOT NULL", `cursorId > CAST(? AS BIGINT)`]);
1007
+ const rows2 = await db.query(
1008
+ `SELECT * FROM feedback_events ${deltaWhereClause} ORDER BY cursorId ASC LIMIT ?`,
1009
+ [...filterParams, afterCursorId, limit + 1]
1010
+ );
1011
+ const visibleRows = rows2.slice(0, limit).map((row) => ({
1012
+ cursorId: row.cursorId,
1013
+ feedback: rowToFeedbackRecord(row)
1014
+ }));
1015
+ return {
1016
+ feedback: visibleRows.map((row) => row.feedback),
1017
+ delta: { limit, hasMore: rows2.length > limit },
1018
+ deltaCursor: visibleRows.length > 0 ? encodeDeltaCursor(visibleRows[visibleRows.length - 1]?.cursorId) : streamHeadCursor
1019
+ };
1020
+ }
909
1021
  const orderByClause = buildOrderByClause(orderBy);
910
1022
  const { clause: paginationClause, params: paginationParams } = buildPaginationClause({ page, perPage });
1023
+ const currentDeltaCursor = deltaPollingFeatureEnabled() ? await getDeltaCursor(db, filterClause, filterParams) : void 0;
911
1024
  const countResult = await db.query(
912
1025
  `SELECT COUNT(*) as total FROM feedback_events ${filterClause}`,
913
1026
  filterParams
@@ -919,9 +1032,26 @@ async function listFeedback(db, args) {
919
1032
  );
920
1033
  return {
921
1034
  pagination: { total, page, perPage, hasMore: (page + 1) * perPage < total },
922
- feedback: rows.map((row) => rowToFeedbackRecord(row))
1035
+ feedback: rows.map((row) => rowToFeedbackRecord(row)),
1036
+ ...deltaPollingFeatureEnabled() ? { deltaCursor: currentDeltaCursor } : {}
923
1037
  };
924
1038
  }
1039
+ async function getDeltaCursor(db, filterClause, filterParams) {
1040
+ const rows = await db.query(
1041
+ `SELECT max(cursorId) AS cursorId FROM feedback_events ${filterClause}`,
1042
+ filterParams
1043
+ );
1044
+ const cursorId = rows[0]?.cursorId;
1045
+ if (cursorId !== null && cursorId !== void 0) {
1046
+ return encodeDeltaCursor(cursorId);
1047
+ }
1048
+ const streamRows = await db.query(`SELECT max(cursorId) AS cursorId FROM feedback_events`);
1049
+ return encodeDeltaCursor(streamRows[0]?.cursorId);
1050
+ }
1051
+ async function getStreamHeadCursor(db) {
1052
+ const streamRows = await db.query(`SELECT max(cursorId) AS cursorId FROM feedback_events`);
1053
+ return encodeDeltaCursor(streamRows[0]?.cursorId);
1054
+ }
925
1055
  async function getFeedbackAggregate(db, args) {
926
1056
  const aggSql = getAggregationSql(args.aggregation);
927
1057
  const { clause, params } = buildFeedbackWhereClause(args, true);
@@ -1049,8 +1179,6 @@ async function getFeedbackPercentiles(db, args) {
1049
1179
  }
1050
1180
  return { series };
1051
1181
  }
1052
-
1053
- // src/storage/domains/observability/logs.ts
1054
1182
  var COLUMNS = [
1055
1183
  "logId",
1056
1184
  "timestamp",
@@ -1166,13 +1294,40 @@ async function batchCreateLogs(db, args) {
1166
1294
  await db.execute(`INSERT INTO log_events (${COLUMNS_SQL}) VALUES ${tuples.join(",\n")} ON CONFLICT DO NOTHING`);
1167
1295
  }
1168
1296
  async function listLogs(db, args) {
1169
- const filters = args.filters ?? {};
1170
- const page = Number(args.pagination?.page ?? 0);
1171
- const perPage = Number(args.pagination?.perPage ?? 10);
1172
- const orderBy = { field: args.orderBy?.field ?? "timestamp", direction: args.orderBy?.direction ?? "DESC" };
1173
- const { clause: filterClause, params: filterParams } = buildWhereClause(filters);
1297
+ const { mode, filters, pagination, orderBy, after, limit } = storage.listLogsArgsSchema.parse(args);
1298
+ const filterRecord = filters;
1299
+ const page = Number(pagination.page);
1300
+ const perPage = Number(pagination.perPage);
1301
+ const { clause: filterClause, params: filterParams } = buildWhereClause(filterRecord);
1302
+ if (mode === "delta") {
1303
+ assertDeltaPollingEnabled();
1304
+ const streamHeadCursor = await getStreamHeadCursor2(db);
1305
+ if (after === void 0) {
1306
+ return {
1307
+ logs: [],
1308
+ delta: { limit, hasMore: false },
1309
+ deltaCursor: streamHeadCursor
1310
+ };
1311
+ }
1312
+ const afterCursorId = validateCursorId(after);
1313
+ const deltaWhereClause = extendWhereClause(filterClause, ["cursorId IS NOT NULL", `cursorId > CAST(? AS BIGINT)`]);
1314
+ const rows2 = await db.query(
1315
+ `SELECT * FROM log_events ${deltaWhereClause} ORDER BY cursorId ASC LIMIT ?`,
1316
+ [...filterParams, afterCursorId, limit + 1]
1317
+ );
1318
+ const visibleRows = rows2.slice(0, limit).map((row) => ({
1319
+ cursorId: row.cursorId,
1320
+ log: rowToLogRecord(row)
1321
+ }));
1322
+ return {
1323
+ logs: visibleRows.map((row) => row.log),
1324
+ delta: { limit, hasMore: rows2.length > limit },
1325
+ deltaCursor: visibleRows.length > 0 ? encodeDeltaCursor(visibleRows[visibleRows.length - 1]?.cursorId) : streamHeadCursor
1326
+ };
1327
+ }
1174
1328
  const orderByClause = buildOrderByClause(orderBy);
1175
1329
  const { clause: paginationClause, params: paginationParams } = buildPaginationClause({ page, perPage });
1330
+ const currentDeltaCursor = deltaPollingFeatureEnabled() ? await getDeltaCursor2(db, filterClause, filterParams) : void 0;
1176
1331
  const countResult = await db.query(
1177
1332
  `SELECT COUNT(*) as total FROM log_events ${filterClause}`,
1178
1333
  filterParams
@@ -1185,9 +1340,26 @@ async function listLogs(db, args) {
1185
1340
  const logs = rows.map((row) => rowToLogRecord(row));
1186
1341
  return {
1187
1342
  pagination: { total, page, perPage, hasMore: (page + 1) * perPage < total },
1188
- logs
1343
+ logs,
1344
+ ...deltaPollingFeatureEnabled() ? { deltaCursor: currentDeltaCursor } : {}
1189
1345
  };
1190
1346
  }
1347
+ async function getDeltaCursor2(db, filterClause, filterParams) {
1348
+ const rows = await db.query(
1349
+ `SELECT max(cursorId) AS cursorId FROM log_events ${filterClause}`,
1350
+ filterParams
1351
+ );
1352
+ const cursorId = rows[0]?.cursorId;
1353
+ if (cursorId !== null && cursorId !== void 0) {
1354
+ return encodeDeltaCursor(cursorId);
1355
+ }
1356
+ const streamRows = await db.query(`SELECT max(cursorId) AS cursorId FROM log_events`);
1357
+ return encodeDeltaCursor(streamRows[0]?.cursorId);
1358
+ }
1359
+ async function getStreamHeadCursor2(db) {
1360
+ const streamRows = await db.query(`SELECT max(cursorId) AS cursorId FROM log_events`);
1361
+ return encodeDeltaCursor(streamRows[0]?.cursorId);
1362
+ }
1191
1363
  function resolveDistinctColumnSql(distinctColumn) {
1192
1364
  if (!distinctColumn) {
1193
1365
  throw new Error(`count_distinct aggregation requires a 'distinctColumn' argument`);
@@ -1435,13 +1607,40 @@ async function batchCreateMetrics(db, args) {
1435
1607
  );
1436
1608
  }
1437
1609
  async function listMetrics(db, args) {
1438
- const filters = args.filters ?? {};
1439
- const page = Number(args.pagination?.page ?? 0);
1440
- const perPage = Number(args.pagination?.perPage ?? 10);
1441
- const orderBy = { field: args.orderBy?.field ?? "timestamp", direction: args.orderBy?.direction ?? "DESC" };
1442
- const { clause: filterClause, params: filterParams } = buildWhereClause(filters);
1610
+ const { mode, filters, pagination, orderBy, after, limit } = storage.listMetricsArgsSchema.parse(args);
1611
+ const filterRecord = filters;
1612
+ const page = Number(pagination.page);
1613
+ const perPage = Number(pagination.perPage);
1614
+ const { clause: filterClause, params: filterParams } = buildWhereClause(filterRecord);
1615
+ if (mode === "delta") {
1616
+ assertDeltaPollingEnabled();
1617
+ const streamHeadCursor = await getStreamHeadCursor3(db);
1618
+ if (after === void 0) {
1619
+ return {
1620
+ metrics: [],
1621
+ delta: { limit, hasMore: false },
1622
+ deltaCursor: streamHeadCursor
1623
+ };
1624
+ }
1625
+ const afterCursorId = validateCursorId(after);
1626
+ const deltaWhereClause = extendWhereClause(filterClause, ["cursorId IS NOT NULL", `cursorId > CAST(? AS BIGINT)`]);
1627
+ const rows2 = await db.query(
1628
+ `SELECT * FROM metric_events ${deltaWhereClause} ORDER BY cursorId ASC LIMIT ?`,
1629
+ [...filterParams, afterCursorId, limit + 1]
1630
+ );
1631
+ const visibleRows = rows2.slice(0, limit).map((row) => ({
1632
+ cursorId: row.cursorId,
1633
+ metric: rowToMetricRecord(row)
1634
+ }));
1635
+ return {
1636
+ metrics: visibleRows.map((row) => row.metric),
1637
+ delta: { limit, hasMore: rows2.length > limit },
1638
+ deltaCursor: visibleRows.length > 0 ? encodeDeltaCursor(visibleRows[visibleRows.length - 1]?.cursorId) : streamHeadCursor
1639
+ };
1640
+ }
1443
1641
  const orderByClause = buildOrderByClause(orderBy);
1444
1642
  const { clause: paginationClause, params: paginationParams } = buildPaginationClause({ page, perPage });
1643
+ const currentDeltaCursor = deltaPollingFeatureEnabled() ? await getDeltaCursor3(db, filterClause, filterParams) : void 0;
1445
1644
  const countResult = await db.query(
1446
1645
  `SELECT COUNT(*) AS total FROM metric_events ${filterClause}`,
1447
1646
  filterParams
@@ -1453,9 +1652,26 @@ async function listMetrics(db, args) {
1453
1652
  );
1454
1653
  return {
1455
1654
  pagination: { total, page, perPage, hasMore: (page + 1) * perPage < total },
1456
- metrics: rows.map((row) => rowToMetricRecord(row))
1655
+ metrics: rows.map((row) => rowToMetricRecord(row)),
1656
+ ...deltaPollingFeatureEnabled() ? { deltaCursor: currentDeltaCursor } : {}
1457
1657
  };
1458
1658
  }
1659
+ async function getDeltaCursor3(db, filterClause, filterParams) {
1660
+ const rows = await db.query(
1661
+ `SELECT max(cursorId) AS cursorId FROM metric_events ${filterClause}`,
1662
+ filterParams
1663
+ );
1664
+ const cursorId = rows[0]?.cursorId;
1665
+ if (cursorId !== null && cursorId !== void 0) {
1666
+ return encodeDeltaCursor(cursorId);
1667
+ }
1668
+ const streamRows = await db.query(`SELECT max(cursorId) AS cursorId FROM metric_events`);
1669
+ return encodeDeltaCursor(streamRows[0]?.cursorId);
1670
+ }
1671
+ async function getStreamHeadCursor3(db) {
1672
+ const streamRows = await db.query(`SELECT max(cursorId) AS cursorId FROM metric_events`);
1673
+ return encodeDeltaCursor(streamRows[0]?.cursorId);
1674
+ }
1459
1675
  async function getMetricAggregate(db, args) {
1460
1676
  const aggSql = getAggregationSql2(args.aggregation, "value", args.distinctColumn);
1461
1677
  const { clause: nameClause, params: nameParams } = buildMetricNameFilter(args.name);
@@ -1735,10 +1951,30 @@ async function getMetricLabelValues(db, args) {
1735
1951
  return { values: rows.map((r) => r.val) };
1736
1952
  }
1737
1953
  var SIGNAL_MIGRATIONS = [
1738
- { table: "metric_events", createDDL: METRIC_EVENTS_DDL, idColumn: "metricId" },
1739
- { table: "log_events", createDDL: LOG_EVENTS_DDL, idColumn: "logId" },
1740
- { table: "score_events", createDDL: SCORE_EVENTS_DDL, idColumn: "scoreId" },
1741
- { table: "feedback_events", createDDL: FEEDBACK_EVENTS_DDL, idColumn: "feedbackId" }
1954
+ {
1955
+ table: "metric_events",
1956
+ createDDL: METRIC_EVENTS_DDL,
1957
+ idColumn: "metricId",
1958
+ cursorSequenceDDL: METRIC_EVENTS_CURSOR_SEQUENCE_DDL
1959
+ },
1960
+ {
1961
+ table: "log_events",
1962
+ createDDL: LOG_EVENTS_DDL,
1963
+ idColumn: "logId",
1964
+ cursorSequenceDDL: LOG_EVENTS_CURSOR_SEQUENCE_DDL
1965
+ },
1966
+ {
1967
+ table: "score_events",
1968
+ createDDL: SCORE_EVENTS_DDL,
1969
+ idColumn: "scoreId",
1970
+ cursorSequenceDDL: SCORE_EVENTS_CURSOR_SEQUENCE_DDL
1971
+ },
1972
+ {
1973
+ table: "feedback_events",
1974
+ createDDL: FEEDBACK_EVENTS_DDL,
1975
+ idColumn: "feedbackId",
1976
+ cursorSequenceDDL: FEEDBACK_EVENTS_CURSOR_SEQUENCE_DDL
1977
+ }
1742
1978
  ];
1743
1979
  async function tableExists(db, table) {
1744
1980
  const rows = await db.query(
@@ -1798,7 +2034,7 @@ async function checkSignalTablesMigrationStatus(db) {
1798
2034
  };
1799
2035
  }
1800
2036
  async function migrateSignalTables(db, logger) {
1801
- for (const { table, createDDL, idColumn } of SIGNAL_MIGRATIONS) {
2037
+ for (const { table, createDDL, idColumn, cursorSequenceDDL } of SIGNAL_MIGRATIONS) {
1802
2038
  if (!await tableExists(db, table)) continue;
1803
2039
  if (await hasPrimaryKey(db, table)) continue;
1804
2040
  logger?.info?.(`Migrating ${table} to schema with ${idColumn} PRIMARY KEY`);
@@ -1807,6 +2043,7 @@ async function migrateSignalTables(db, logger) {
1807
2043
  let originalRenamed = false;
1808
2044
  let swapCompleted = false;
1809
2045
  try {
2046
+ await db.execute(cursorSequenceDDL);
1810
2047
  await db.execute(buildTemporaryTableDDL(createDDL, table, temp));
1811
2048
  const newColumns = await getColumns(db, temp);
1812
2049
  const currentColumns = new Set(await getColumns(db, table));
@@ -2138,15 +2375,41 @@ async function batchCreateScores(db, args) {
2138
2375
  );
2139
2376
  }
2140
2377
  async function listScores(db, args) {
2141
- const filters = args.filters ?? {};
2142
- const page = Number(args.pagination?.page ?? 0);
2143
- const perPage = Number(args.pagination?.perPage ?? 10);
2144
- const orderBy = { field: args.orderBy?.field ?? "timestamp", direction: args.orderBy?.direction ?? "DESC" };
2378
+ const { mode, filters, pagination, orderBy, after, limit } = storage.listScoresArgsSchema.parse(args);
2379
+ const page = Number(pagination.page);
2380
+ const perPage = Number(pagination.perPage);
2145
2381
  const { clause: filterClause, params: filterParams } = buildWhereClause(filters, {
2146
2382
  source: "scoreSource"
2147
2383
  });
2384
+ if (mode === "delta") {
2385
+ assertDeltaPollingEnabled();
2386
+ const streamHeadCursor = await getStreamHeadCursor4(db);
2387
+ if (after === void 0) {
2388
+ return {
2389
+ scores: [],
2390
+ delta: { limit, hasMore: false },
2391
+ deltaCursor: streamHeadCursor
2392
+ };
2393
+ }
2394
+ const afterCursorId = validateCursorId(after);
2395
+ const deltaWhereClause = extendWhereClause(filterClause, ["cursorId IS NOT NULL", `cursorId > CAST(? AS BIGINT)`]);
2396
+ const rows2 = await db.query(
2397
+ `SELECT * FROM score_events ${deltaWhereClause} ORDER BY cursorId ASC LIMIT ?`,
2398
+ [...filterParams, afterCursorId, limit + 1]
2399
+ );
2400
+ const visibleRows = rows2.slice(0, limit).map((row) => ({
2401
+ cursorId: row.cursorId,
2402
+ score: rowToScoreRecord(row)
2403
+ }));
2404
+ return {
2405
+ scores: visibleRows.map((row) => row.score),
2406
+ delta: { limit, hasMore: rows2.length > limit },
2407
+ deltaCursor: visibleRows.length > 0 ? encodeDeltaCursor(visibleRows[visibleRows.length - 1]?.cursorId) : streamHeadCursor
2408
+ };
2409
+ }
2148
2410
  const orderByClause = buildOrderByClause(orderBy);
2149
2411
  const { clause: paginationClause, params: paginationParams } = buildPaginationClause({ page, perPage });
2412
+ const currentDeltaCursor = deltaPollingFeatureEnabled() ? await getDeltaCursor4(db, filterClause, filterParams) : void 0;
2150
2413
  const countResult = await db.query(
2151
2414
  `SELECT COUNT(*) as total FROM score_events ${filterClause}`,
2152
2415
  filterParams
@@ -2158,9 +2421,26 @@ async function listScores(db, args) {
2158
2421
  );
2159
2422
  return {
2160
2423
  pagination: { total, page, perPage, hasMore: (page + 1) * perPage < total },
2161
- scores: rows.map((row) => rowToScoreRecord(row))
2424
+ scores: rows.map((row) => rowToScoreRecord(row)),
2425
+ ...deltaPollingFeatureEnabled() ? { deltaCursor: currentDeltaCursor } : {}
2162
2426
  };
2163
2427
  }
2428
+ async function getDeltaCursor4(db, filterClause, filterParams) {
2429
+ const rows = await db.query(
2430
+ `SELECT max(cursorId) AS cursorId FROM score_events ${filterClause}`,
2431
+ filterParams
2432
+ );
2433
+ const cursorId = rows[0]?.cursorId;
2434
+ if (cursorId !== null && cursorId !== void 0) {
2435
+ return encodeDeltaCursor(cursorId);
2436
+ }
2437
+ const streamRows = await db.query(`SELECT max(cursorId) AS cursorId FROM score_events`);
2438
+ return encodeDeltaCursor(streamRows[0]?.cursorId);
2439
+ }
2440
+ async function getStreamHeadCursor4(db) {
2441
+ const streamRows = await db.query(`SELECT max(cursorId) AS cursorId FROM score_events`);
2442
+ return encodeDeltaCursor(streamRows[0]?.cursorId);
2443
+ }
2164
2444
  async function getScoreById(db, scoreId) {
2165
2445
  const rows = await db.query(`SELECT * FROM score_events WHERE scoreId = ? LIMIT 1`, [
2166
2446
  scoreId
@@ -2779,13 +3059,152 @@ async function listTraceRows(db, args, reconstructSelect, mapRow, toSpans) {
2779
3059
  };
2780
3060
  }
2781
3061
  async function listTraces(db, args) {
2782
- return listTraceRows(
2783
- db,
2784
- args,
2785
- SPAN_RECONSTRUCT_SELECT,
2786
- rowToSpanRecord,
2787
- (spans) => storage.toTraceSpans(spans)
2788
- );
3062
+ const { mode, filters, pagination, orderBy, after, limit } = storage.listTracesArgsSchema.parse(args);
3063
+ const filterRecord = filters ?? {};
3064
+ const page = Number(pagination.page);
3065
+ const perPage = Number(pagination.perPage);
3066
+ if (mode === "delta") {
3067
+ assertDeltaPollingEnabled();
3068
+ const streamHeadCursor = await getTraceStreamHeadCursor(db);
3069
+ if (after === void 0) {
3070
+ return {
3071
+ spans: [],
3072
+ delta: { limit, hasMore: false },
3073
+ deltaCursor: streamHeadCursor
3074
+ };
3075
+ }
3076
+ const afterCursorId = validateCursorId(after);
3077
+ const { prefilter: prefilter2, postAgg: postAgg2, hasChildError: hasChildError2 } = partitionAnchorFilters(filterRecord);
3078
+ const { clause: prefilterClause2, params: prefilterParams2 } = buildWhereClause(prefilter2);
3079
+ const prefilterParts2 = [
3080
+ `eventType = 'start'`,
3081
+ `parentSpanId IS NULL`,
3082
+ `cursorId IS NOT NULL`,
3083
+ `cursorId > CAST(? AS BIGINT)`
3084
+ ];
3085
+ if (prefilterClause2) prefilterParts2.push(prefilterClause2.replace(/^WHERE\s+/i, ""));
3086
+ const prefilterWhere2 = `WHERE ${prefilterParts2.join(" AND ")}`;
3087
+ const { clause: postAggClause2, params: postAggParams2 } = buildWhereClause(postAgg2);
3088
+ const postAggParts2 = [];
3089
+ if (postAggClause2) postAggParts2.push(postAggClause2.replace(/^WHERE\s+/i, ""));
3090
+ const childErrorClause2 = buildHasChildErrorClause(hasChildError2, "root_spans");
3091
+ if (childErrorClause2) postAggParts2.push(childErrorClause2);
3092
+ const postAggWhere2 = postAggParts2.length > 0 ? `WHERE ${postAggParts2.join(" AND ")}` : "";
3093
+ const outerAlias2 = "outer_root";
3094
+ const dataSql2 = `
3095
+ WITH candidate_roots AS (
3096
+ SELECT traceId, spanId, cursorId
3097
+ FROM span_events AS ${outerAlias2}
3098
+ ${prefilterWhere2}
3099
+ ),
3100
+ root_spans AS (
3101
+ SELECT reconstructed.*, candidate_roots.cursorId AS anchorCursorId
3102
+ FROM (
3103
+ ${SPAN_RECONSTRUCT_SELECT}
3104
+ WHERE (traceId, spanId) IN (SELECT traceId, spanId FROM candidate_roots)
3105
+ GROUP BY traceId, spanId
3106
+ ) AS reconstructed
3107
+ INNER JOIN candidate_roots USING (traceId, spanId)
3108
+ )
3109
+ SELECT * FROM root_spans ${postAggWhere2} ORDER BY anchorCursorId ASC LIMIT ?
3110
+ `;
3111
+ const rows2 = await db.query(dataSql2, [
3112
+ afterCursorId,
3113
+ ...prefilterParams2,
3114
+ ...postAggParams2,
3115
+ limit + 1
3116
+ ]);
3117
+ const visibleRows = rows2.slice(0, limit).map((row) => ({
3118
+ cursorId: row.anchorCursorId,
3119
+ span: rowToSpanRecord(row)
3120
+ }));
3121
+ return {
3122
+ spans: storage.toTraceSpans(visibleRows.map((row) => row.span)),
3123
+ delta: { limit, hasMore: rows2.length > limit },
3124
+ deltaCursor: visibleRows.length > 0 ? encodeDeltaCursor(visibleRows[visibleRows.length - 1]?.cursorId) : streamHeadCursor
3125
+ };
3126
+ }
3127
+ const { prefilter, postAgg, hasChildError } = partitionAnchorFilters(filterRecord);
3128
+ const { clause: prefilterClause, params: prefilterParams } = buildWhereClause(prefilter);
3129
+ const prefilterParts = [`eventType = 'start'`, `parentSpanId IS NULL`];
3130
+ if (prefilterClause) prefilterParts.push(prefilterClause.replace(/^WHERE\s+/i, ""));
3131
+ const prefilterWhere = `WHERE ${prefilterParts.join(" AND ")}`;
3132
+ const outerAlias = "outer_root";
3133
+ const orderDir = orderBy.direction.toUpperCase();
3134
+ if (orderDir !== "ASC" && orderDir !== "DESC") {
3135
+ throw new Error(`Invalid sort direction: ${orderBy.direction}`);
3136
+ }
3137
+ const currentDeltaCursor = deltaPollingFeatureEnabled() ? await getTraceDeltaCursor(db, filters) : void 0;
3138
+ const canOrderInPrefilter = SAFE_PREFILTER_ORDER_FIELDS.has(orderBy.field);
3139
+ const hasPostAggFilters = Object.keys(postAgg).length > 0 || hasChildError !== void 0 || !canOrderInPrefilter;
3140
+ if (!hasPostAggFilters) {
3141
+ const prefilterOrderBy = `ORDER BY timestamp ${orderDir}`;
3142
+ const offset = page * perPage;
3143
+ const countSql2 = `
3144
+ SELECT COUNT(*) as total
3145
+ FROM span_events AS ${outerAlias}
3146
+ ${prefilterWhere}
3147
+ `;
3148
+ const countResult2 = await db.query(countSql2, prefilterParams);
3149
+ const total2 = Number(countResult2[0]?.total ?? 0);
3150
+ const pageSql = `
3151
+ WITH page_roots AS (
3152
+ SELECT traceId, spanId
3153
+ FROM span_events AS ${outerAlias}
3154
+ ${prefilterWhere}
3155
+ ${prefilterOrderBy}
3156
+ LIMIT ? OFFSET ?
3157
+ )
3158
+ ${SPAN_RECONSTRUCT_SELECT}
3159
+ WHERE (traceId, spanId) IN (SELECT traceId, spanId FROM page_roots)
3160
+ GROUP BY traceId, spanId
3161
+ ${buildOrderByClause(orderBy)}
3162
+ `;
3163
+ const rows2 = await db.query(pageSql, [...prefilterParams, perPage, offset]);
3164
+ const spans2 = rows2.map((row) => rowToSpanRecord(row));
3165
+ return {
3166
+ pagination: { total: total2, page, perPage, hasMore: (page + 1) * perPage < total2 },
3167
+ spans: storage.toTraceSpans(spans2),
3168
+ ...deltaPollingFeatureEnabled() ? { deltaCursor: currentDeltaCursor } : {}
3169
+ };
3170
+ }
3171
+ const { clause: postAggClause, params: postAggParams } = buildWhereClause(postAgg);
3172
+ const postAggParts = [];
3173
+ if (postAggClause) postAggParts.push(postAggClause.replace(/^WHERE\s+/i, ""));
3174
+ const childErrorClause = buildHasChildErrorClause(hasChildError, "root_spans");
3175
+ if (childErrorClause) postAggParts.push(childErrorClause);
3176
+ const postAggWhere = postAggParts.length > 0 ? `WHERE ${postAggParts.join(" AND ")}` : "";
3177
+ const cteSql = `
3178
+ WITH candidate_roots AS (
3179
+ SELECT traceId, spanId
3180
+ FROM span_events AS ${outerAlias}
3181
+ ${prefilterWhere}
3182
+ ),
3183
+ root_spans AS (
3184
+ ${SPAN_RECONSTRUCT_SELECT}
3185
+ WHERE (traceId, spanId) IN (SELECT traceId, spanId FROM candidate_roots)
3186
+ GROUP BY traceId, spanId
3187
+ )
3188
+ `;
3189
+ const orderByClause = buildOrderByClause(orderBy);
3190
+ const { clause: paginationClause, params: paginationParams } = buildPaginationClause({ page, perPage });
3191
+ const countSql = `
3192
+ ${cteSql}
3193
+ SELECT COUNT(*) as total FROM root_spans ${postAggWhere}
3194
+ `;
3195
+ const countResult = await db.query(countSql, [...prefilterParams, ...postAggParams]);
3196
+ const total = Number(countResult[0]?.total ?? 0);
3197
+ const dataSql = `
3198
+ ${cteSql}
3199
+ SELECT * FROM root_spans ${postAggWhere} ${orderByClause} ${paginationClause}
3200
+ `;
3201
+ const rows = await db.query(dataSql, [...prefilterParams, ...postAggParams, ...paginationParams]);
3202
+ const spans = rows.map((row) => rowToSpanRecord(row));
3203
+ return {
3204
+ pagination: { total, page, perPage, hasMore: (page + 1) * perPage < total },
3205
+ spans: storage.toTraceSpans(spans),
3206
+ ...deltaPollingFeatureEnabled() ? { deltaCursor: currentDeltaCursor } : {}
3207
+ };
2789
3208
  }
2790
3209
  async function listTracesLight(db, args) {
2791
3210
  return listTraceRows(
@@ -2814,18 +3233,88 @@ async function getSpans(db, args) {
2814
3233
  };
2815
3234
  }
2816
3235
  async function listBranches(db, args) {
2817
- const filters = args.filters ?? {};
2818
- const page = Number(args.pagination?.page ?? 0);
2819
- const perPage = Number(args.pagination?.perPage ?? 10);
2820
- const orderBy = { field: args.orderBy?.field ?? "startedAt", direction: args.orderBy?.direction ?? "DESC" };
2821
- const userSpanType = filters.spanType;
3236
+ const { mode, filters, pagination, orderBy, after, limit } = storage.listBranchesArgsSchema.parse(args);
3237
+ const filterRecord = filters ?? {};
3238
+ const page = Number(pagination.page);
3239
+ const perPage = Number(pagination.perPage);
3240
+ const userSpanType = filterRecord.spanType;
2822
3241
  if (typeof userSpanType === "string" && !storage.BRANCH_SPAN_TYPES.includes(userSpanType)) {
3242
+ const currentDeltaCursor2 = deltaPollingFeatureEnabled() ? await getBranchDeltaCursor(db, filters) : void 0;
3243
+ if (mode === "delta") {
3244
+ assertDeltaPollingEnabled();
3245
+ return {
3246
+ branches: [],
3247
+ delta: { limit, hasMore: false },
3248
+ deltaCursor: currentDeltaCursor2
3249
+ };
3250
+ }
2823
3251
  return {
2824
3252
  pagination: { total: 0, page, perPage, hasMore: false },
2825
- branches: []
3253
+ branches: [],
3254
+ ...deltaPollingFeatureEnabled() ? { deltaCursor: currentDeltaCursor2 } : {}
3255
+ };
3256
+ }
3257
+ if (mode === "delta") {
3258
+ assertDeltaPollingEnabled();
3259
+ const streamHeadCursor = await getBranchStreamHeadCursor(
3260
+ db,
3261
+ typeof userSpanType === "string" ? userSpanType : null
3262
+ );
3263
+ if (after === void 0) {
3264
+ return {
3265
+ branches: [],
3266
+ delta: { limit, hasMore: false },
3267
+ deltaCursor: streamHeadCursor
3268
+ };
3269
+ }
3270
+ const afterCursorId = validateCursorId(after);
3271
+ const { spanType: _spanType2, ...rest2 } = filterRecord;
3272
+ const { prefilter: prefilter2, postAgg: postAgg2} = partitionAnchorFilters(rest2);
3273
+ const { clause: prefilterClause2, params: prefilterFilterParams2 } = buildWhereClause(prefilter2);
3274
+ const prefilterParts2 = [`eventType = 'start'`, `cursorId IS NOT NULL`, `cursorId > CAST(? AS BIGINT)`];
3275
+ let spanTypeParams2;
3276
+ if (typeof userSpanType === "string") {
3277
+ prefilterParts2.push(`spanType = ?`);
3278
+ spanTypeParams2 = [userSpanType];
3279
+ } else {
3280
+ prefilterParts2.push(`spanType IN (${BRANCH_SPAN_TYPE_PLACEHOLDERS})`);
3281
+ spanTypeParams2 = [...storage.BRANCH_SPAN_TYPES];
3282
+ }
3283
+ if (prefilterClause2) prefilterParts2.push(prefilterClause2.replace(/^WHERE\s+/i, ""));
3284
+ const prefilterWhere2 = `WHERE ${prefilterParts2.join(" AND ")}`;
3285
+ const prefilterParams2 = [afterCursorId, ...spanTypeParams2, ...prefilterFilterParams2];
3286
+ const { clause: postAggClause2, params: postAggParams2 } = buildWhereClause(postAgg2);
3287
+ const postAggWhere2 = postAggClause2 ? postAggClause2 : "";
3288
+ const outerAlias2 = "outer_anchor";
3289
+ const dataSql2 = `
3290
+ WITH candidate_anchors AS (
3291
+ SELECT traceId, spanId, cursorId
3292
+ FROM span_events AS ${outerAlias2}
3293
+ ${prefilterWhere2}
3294
+ ),
3295
+ branch_anchors AS (
3296
+ SELECT reconstructed.*, candidate_anchors.cursorId AS anchorCursorId
3297
+ FROM (
3298
+ ${SPAN_RECONSTRUCT_SELECT}
3299
+ WHERE (traceId, spanId) IN (SELECT traceId, spanId FROM candidate_anchors)
3300
+ GROUP BY traceId, spanId
3301
+ ) AS reconstructed
3302
+ INNER JOIN candidate_anchors USING (traceId, spanId)
3303
+ )
3304
+ SELECT * FROM branch_anchors ${postAggWhere2} ORDER BY anchorCursorId ASC LIMIT ?
3305
+ `;
3306
+ const rows2 = await db.query(dataSql2, [...prefilterParams2, ...postAggParams2, limit + 1]);
3307
+ const visibleRows = rows2.slice(0, limit).map((row) => ({
3308
+ cursorId: row.anchorCursorId,
3309
+ branch: rowToSpanRecord(row)
3310
+ }));
3311
+ return {
3312
+ branches: storage.toTraceSpans(visibleRows.map((row) => row.branch)),
3313
+ delta: { limit, hasMore: rows2.length > limit },
3314
+ deltaCursor: visibleRows.length > 0 ? encodeDeltaCursor(visibleRows[visibleRows.length - 1]?.cursorId) : streamHeadCursor
2826
3315
  };
2827
3316
  }
2828
- const { spanType: _spanType, ...rest } = filters;
3317
+ const { spanType: _spanType, ...rest } = filterRecord;
2829
3318
  const { prefilter, postAgg} = partitionAnchorFilters(rest);
2830
3319
  const { clause: prefilterClause, params: prefilterFilterParams } = buildWhereClause(prefilter);
2831
3320
  const prefilterParts = [`eventType = 'start'`];
@@ -2845,6 +3334,7 @@ async function listBranches(db, args) {
2845
3334
  if (orderDir !== "ASC" && orderDir !== "DESC") {
2846
3335
  throw new Error(`Invalid sort direction: ${orderBy.direction}`);
2847
3336
  }
3337
+ const currentDeltaCursor = deltaPollingFeatureEnabled() ? await getBranchDeltaCursor(db, filters) : void 0;
2848
3338
  const canOrderInPrefilter = SAFE_PREFILTER_ORDER_FIELDS.has(orderBy.field);
2849
3339
  const hasPostAggFilters = Object.keys(postAgg).length > 0 || !canOrderInPrefilter;
2850
3340
  if (!hasPostAggFilters) {
@@ -2860,7 +3350,8 @@ async function listBranches(db, args) {
2860
3350
  if (total2 === 0) {
2861
3351
  return {
2862
3352
  pagination: { total: 0, page, perPage, hasMore: false },
2863
- branches: []
3353
+ branches: [],
3354
+ ...deltaPollingFeatureEnabled() ? { deltaCursor: currentDeltaCursor } : {}
2864
3355
  };
2865
3356
  }
2866
3357
  const pageSql = `
@@ -2880,7 +3371,8 @@ async function listBranches(db, args) {
2880
3371
  const spans2 = rows2.map((row) => rowToSpanRecord(row));
2881
3372
  return {
2882
3373
  pagination: { total: total2, page, perPage, hasMore: (page + 1) * perPage < total2 },
2883
- branches: storage.toTraceSpans(spans2)
3374
+ branches: storage.toTraceSpans(spans2),
3375
+ ...deltaPollingFeatureEnabled() ? { deltaCursor: currentDeltaCursor } : {}
2884
3376
  };
2885
3377
  }
2886
3378
  const { clause: postAggClause, params: postAggParams } = buildWhereClause(postAgg);
@@ -2908,7 +3400,8 @@ async function listBranches(db, args) {
2908
3400
  if (total === 0) {
2909
3401
  return {
2910
3402
  pagination: { total: 0, page, perPage, hasMore: false },
2911
- branches: []
3403
+ branches: [],
3404
+ ...deltaPollingFeatureEnabled() ? { deltaCursor: currentDeltaCursor } : {}
2912
3405
  };
2913
3406
  }
2914
3407
  const dataSql = `
@@ -2919,9 +3412,151 @@ async function listBranches(db, args) {
2919
3412
  const spans = rows.map((row) => rowToSpanRecord(row));
2920
3413
  return {
2921
3414
  pagination: { total, page, perPage, hasMore: (page + 1) * perPage < total },
2922
- branches: storage.toTraceSpans(spans)
3415
+ branches: storage.toTraceSpans(spans),
3416
+ ...deltaPollingFeatureEnabled() ? { deltaCursor: currentDeltaCursor } : {}
2923
3417
  };
2924
3418
  }
3419
+ async function getTraceDeltaCursor(db, filters) {
3420
+ const { prefilter, postAgg, hasChildError } = partitionAnchorFilters(filters ?? {});
3421
+ const { clause: prefilterClause, params: prefilterParams } = buildWhereClause(prefilter);
3422
+ const prefilterParts = [`eventType = 'start'`, `parentSpanId IS NULL`, `cursorId IS NOT NULL`];
3423
+ if (prefilterClause) prefilterParts.push(prefilterClause.replace(/^WHERE\s+/i, ""));
3424
+ const prefilterWhere = `WHERE ${prefilterParts.join(" AND ")}`;
3425
+ const outerAlias = "outer_root";
3426
+ const { clause: postAggClause, params: postAggParams } = buildWhereClause(postAgg);
3427
+ const postAggParts = [];
3428
+ if (postAggClause) postAggParts.push(postAggClause.replace(/^WHERE\s+/i, ""));
3429
+ const childErrorClause = buildHasChildErrorClause(hasChildError, "root_spans");
3430
+ if (childErrorClause) postAggParts.push(childErrorClause);
3431
+ const postAggWhere = postAggParts.length > 0 ? `WHERE ${postAggParts.join(" AND ")}` : "";
3432
+ if (postAggWhere === "") {
3433
+ const rows2 = await db.query(
3434
+ `SELECT max(cursorId) AS cursorId FROM span_events AS ${outerAlias} ${prefilterWhere}`,
3435
+ prefilterParams
3436
+ );
3437
+ const cursorId2 = rows2[0]?.cursorId;
3438
+ if (cursorId2 !== null && cursorId2 !== void 0) {
3439
+ return encodeDeltaCursor(cursorId2);
3440
+ }
3441
+ const streamRows2 = await db.query(
3442
+ `SELECT max(cursorId) AS cursorId FROM span_events WHERE eventType = 'start' AND parentSpanId IS NULL AND cursorId IS NOT NULL`
3443
+ );
3444
+ return encodeDeltaCursor(streamRows2[0]?.cursorId);
3445
+ }
3446
+ const cteSql = `
3447
+ WITH candidate_roots AS (
3448
+ SELECT traceId, spanId, cursorId
3449
+ FROM span_events AS ${outerAlias}
3450
+ ${prefilterWhere}
3451
+ ),
3452
+ root_spans AS (
3453
+ SELECT reconstructed.*, candidate_roots.cursorId AS anchorCursorId
3454
+ FROM (
3455
+ ${SPAN_RECONSTRUCT_SELECT}
3456
+ WHERE (traceId, spanId) IN (SELECT traceId, spanId FROM candidate_roots)
3457
+ GROUP BY traceId, spanId
3458
+ ) AS reconstructed
3459
+ INNER JOIN candidate_roots USING (traceId, spanId)
3460
+ )
3461
+ `;
3462
+ const rows = await db.query(
3463
+ `${cteSql} SELECT max(anchorCursorId) AS cursorId FROM root_spans ${postAggWhere}`,
3464
+ [...prefilterParams, ...postAggParams]
3465
+ );
3466
+ const cursorId = rows[0]?.cursorId;
3467
+ if (cursorId !== null && cursorId !== void 0) {
3468
+ return encodeDeltaCursor(cursorId);
3469
+ }
3470
+ const streamRows = await db.query(
3471
+ `SELECT max(cursorId) AS cursorId FROM span_events WHERE eventType = 'start' AND parentSpanId IS NULL AND cursorId IS NOT NULL`
3472
+ );
3473
+ return encodeDeltaCursor(streamRows[0]?.cursorId);
3474
+ }
3475
+ async function getTraceStreamHeadCursor(db) {
3476
+ const streamRows = await db.query(
3477
+ `SELECT max(cursorId) AS cursorId FROM span_events WHERE eventType = 'start' AND parentSpanId IS NULL AND cursorId IS NOT NULL`
3478
+ );
3479
+ return encodeDeltaCursor(streamRows[0]?.cursorId);
3480
+ }
3481
+ async function getBranchDeltaCursor(db, filters) {
3482
+ const filterRecord = filters ?? {};
3483
+ const userSpanType = filterRecord.spanType;
3484
+ const { spanType: _spanType, ...rest } = filterRecord;
3485
+ const { prefilter, postAgg } = partitionAnchorFilters(rest);
3486
+ const { clause: prefilterClause, params: prefilterFilterParams } = buildWhereClause(prefilter);
3487
+ const prefilterParts = [`eventType = 'start'`, `cursorId IS NOT NULL`];
3488
+ let spanTypeParams;
3489
+ if (typeof userSpanType === "string") {
3490
+ prefilterParts.push(`spanType = ?`);
3491
+ spanTypeParams = [userSpanType];
3492
+ } else {
3493
+ prefilterParts.push(`spanType IN (${BRANCH_SPAN_TYPE_PLACEHOLDERS})`);
3494
+ spanTypeParams = [...storage.BRANCH_SPAN_TYPES];
3495
+ }
3496
+ if (prefilterClause) prefilterParts.push(prefilterClause.replace(/^WHERE\s+/i, ""));
3497
+ const prefilterWhere = `WHERE ${prefilterParts.join(" AND ")}`;
3498
+ const prefilterParams = [...spanTypeParams, ...prefilterFilterParams];
3499
+ const outerAlias = "outer_anchor";
3500
+ const { clause: postAggClause, params: postAggParams } = buildWhereClause(postAgg);
3501
+ if (!postAggClause) {
3502
+ const rows2 = await db.query(
3503
+ `SELECT max(cursorId) AS cursorId FROM span_events AS ${outerAlias} ${prefilterWhere}`,
3504
+ prefilterParams
3505
+ );
3506
+ const cursorId2 = rows2[0]?.cursorId;
3507
+ if (cursorId2 !== null && cursorId2 !== void 0) {
3508
+ return encodeDeltaCursor(cursorId2);
3509
+ }
3510
+ const streamRows2 = await db.query(
3511
+ `SELECT max(cursorId) AS cursorId FROM span_events WHERE eventType = 'start' AND spanType IN (${BRANCH_SPAN_TYPE_PLACEHOLDERS}) AND cursorId IS NOT NULL`,
3512
+ [...storage.BRANCH_SPAN_TYPES]
3513
+ );
3514
+ return encodeDeltaCursor(streamRows2[0]?.cursorId);
3515
+ }
3516
+ const cteSql = `
3517
+ WITH candidate_anchors AS (
3518
+ SELECT traceId, spanId, cursorId
3519
+ FROM span_events AS ${outerAlias}
3520
+ ${prefilterWhere}
3521
+ ),
3522
+ branch_anchors AS (
3523
+ SELECT reconstructed.*, candidate_anchors.cursorId AS anchorCursorId
3524
+ FROM (
3525
+ ${SPAN_RECONSTRUCT_SELECT}
3526
+ WHERE (traceId, spanId) IN (SELECT traceId, spanId FROM candidate_anchors)
3527
+ GROUP BY traceId, spanId
3528
+ ) AS reconstructed
3529
+ INNER JOIN candidate_anchors USING (traceId, spanId)
3530
+ )
3531
+ `;
3532
+ const rows = await db.query(
3533
+ `${cteSql} SELECT max(anchorCursorId) AS cursorId FROM branch_anchors ${postAggClause}`,
3534
+ [...prefilterParams, ...postAggParams]
3535
+ );
3536
+ const cursorId = rows[0]?.cursorId;
3537
+ if (cursorId !== null && cursorId !== void 0) {
3538
+ return encodeDeltaCursor(cursorId);
3539
+ }
3540
+ const streamRows = await db.query(
3541
+ `SELECT max(cursorId) AS cursorId FROM span_events WHERE eventType = 'start' AND spanType IN (${BRANCH_SPAN_TYPE_PLACEHOLDERS}) AND cursorId IS NOT NULL`,
3542
+ [...storage.BRANCH_SPAN_TYPES]
3543
+ );
3544
+ return encodeDeltaCursor(streamRows[0]?.cursorId);
3545
+ }
3546
+ async function getBranchStreamHeadCursor(db, userSpanType) {
3547
+ if (userSpanType) {
3548
+ const rows2 = await db.query(
3549
+ `SELECT max(cursorId) AS cursorId FROM span_events WHERE eventType = 'start' AND spanType = ? AND cursorId IS NOT NULL`,
3550
+ [userSpanType]
3551
+ );
3552
+ return encodeDeltaCursor(rows2[0]?.cursorId);
3553
+ }
3554
+ const rows = await db.query(
3555
+ `SELECT max(cursorId) AS cursorId FROM span_events WHERE eventType = 'start' AND spanType IN (${BRANCH_SPAN_TYPE_PLACEHOLDERS}) AND cursorId IS NOT NULL`,
3556
+ [...storage.BRANCH_SPAN_TYPES]
3557
+ );
3558
+ return encodeDeltaCursor(rows[0]?.cursorId);
3559
+ }
2925
3560
 
2926
3561
  // src/storage/domains/observability/index.ts
2927
3562
  function buildSignalMigrationRequiredMessage(args) {
@@ -3006,6 +3641,12 @@ var ObservabilityStorageDuckDB = class extends storage.ObservabilityStorage {
3006
3641
  supported: ["event-sourced"]
3007
3642
  };
3008
3643
  }
3644
+ getFeatures() {
3645
+ if (!deltaPollingFeatureEnabled()) {
3646
+ return void 0;
3647
+ }
3648
+ return ["delta-polling"];
3649
+ }
3009
3650
  // Tracing
3010
3651
  async createSpan(args) {
3011
3652
  return createSpan(this.db, args);
@@ -3142,5 +3783,5 @@ var ObservabilityStorageDuckDB = class extends storage.ObservabilityStorage {
3142
3783
  };
3143
3784
 
3144
3785
  exports.ObservabilityStorageDuckDB = ObservabilityStorageDuckDB;
3145
- //# sourceMappingURL=observability-J6N3H7W7.cjs.map
3146
- //# sourceMappingURL=observability-J6N3H7W7.cjs.map
3786
+ //# sourceMappingURL=observability-25HVQJC2.cjs.map
3787
+ //# sourceMappingURL=observability-25HVQJC2.cjs.map