@mastra/duckdb 1.15.0-alpha.2 → 1.15.0-alpha.4

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 (32) hide show
  1. package/dist/docs/SKILL.md +1 -1
  2. package/dist/docs/assets/SOURCE_MAP.json +1 -1
  3. package/dist/{features-BAGbFa2n.cjs → features-D7U4aB_V.cjs} +25 -1
  4. package/dist/{features-BAGbFa2n.cjs.map → features-D7U4aB_V.cjs.map} +1 -1
  5. package/dist/{features-vjVgmb36.js → features-gvUXKO_o.js} +8 -2
  6. package/dist/{features-vjVgmb36.js.map → features-gvUXKO_o.js.map} +1 -1
  7. package/dist/index.cjs +10 -4
  8. package/dist/index.cjs.map +1 -1
  9. package/dist/index.js +10 -4
  10. package/dist/index.js.map +1 -1
  11. package/dist/{observability-HUYFWVy-.js → observability-DWPWPNlL.js} +341 -59
  12. package/dist/observability-DWPWPNlL.js.map +1 -0
  13. package/dist/{observability-DJs6G-BH.cjs → observability-TeHp5JpN.cjs} +341 -59
  14. package/dist/observability-TeHp5JpN.cjs.map +1 -0
  15. package/dist/storage/domains/observability/index.d.ts +4 -1
  16. package/dist/storage/domains/observability/index.d.ts.map +1 -1
  17. package/dist/storage/domains/observability/preview-compat.d.ts +1 -0
  18. package/dist/storage/domains/observability/preview-compat.d.ts.map +1 -1
  19. package/dist/storage/domains/observability/span-query.d.ts +1 -0
  20. package/dist/storage/domains/observability/span-query.d.ts.map +1 -1
  21. package/dist/storage/domains/observability/thread-query.d.ts +49 -0
  22. package/dist/storage/domains/observability/thread-query.d.ts.map +1 -0
  23. package/dist/storage/domains/observability/trace-aggregate.d.ts.map +1 -1
  24. package/dist/storage/domains/observability/trace-query.d.ts +1 -0
  25. package/dist/storage/domains/observability/trace-query.d.ts.map +1 -1
  26. package/dist/storage/features.d.ts +4 -0
  27. package/dist/storage/features.d.ts.map +1 -1
  28. package/dist/storage/index.d.ts +4 -2
  29. package/dist/storage/index.d.ts.map +1 -1
  30. package/package.json +5 -6
  31. package/dist/observability-DJs6G-BH.cjs.map +0 -1
  32. package/dist/observability-HUYFWVy-.js.map +0 -1
@@ -1,4 +1,4 @@
1
- const require_features = require("./features-BAGbFa2n.cjs");
1
+ const require_features = require("./features-D7U4aB_V.cjs");
2
2
  let _mastra_core_error = require("@mastra/core/error");
3
3
  let _mastra_core_storage = require("@mastra/core/storage");
4
4
  _mastra_core_storage = require_features.__toESM(_mastra_core_storage, 1);
@@ -2876,6 +2876,10 @@ const TRACE_FIELDS = {
2876
2876
  sql: durationMsSql("r.startedAt", "r.endedAt"),
2877
2877
  parameterType: "scalar"
2878
2878
  },
2879
+ timeToFirstChunkMs: {
2880
+ sql: "r.timeToFirstChunkMs",
2881
+ parameterType: "scalar"
2882
+ },
2879
2883
  entityName: {
2880
2884
  sql: "r.entityName",
2881
2885
  parameterType: "scalar"
@@ -2884,6 +2888,14 @@ const TRACE_FIELDS = {
2884
2888
  sql: "r.entityType",
2885
2889
  parameterType: "scalar"
2886
2890
  },
2891
+ name: {
2892
+ sql: "r.name",
2893
+ parameterType: "scalar"
2894
+ },
2895
+ spanType: {
2896
+ sql: "r.spanType",
2897
+ parameterType: "scalar"
2898
+ },
2887
2899
  environment: {
2888
2900
  sql: "r.environment",
2889
2901
  parameterType: "scalar"
@@ -2926,6 +2938,10 @@ const SPAN_FIELDS = {
2926
2938
  sql: "s.durationMs",
2927
2939
  parameterType: "scalar"
2928
2940
  },
2941
+ timeToFirstChunkMs: {
2942
+ sql: "s.timeToFirstChunkMs",
2943
+ parameterType: "scalar"
2944
+ },
2929
2945
  status: {
2930
2946
  sql: "s.status",
2931
2947
  parameterType: "scalar"
@@ -3135,6 +3151,7 @@ const TRACE_SELECT = `
3135
3151
  r.resourceId AS resourceId,
3136
3152
  r.startedAt AS startedAt,
3137
3153
  r.endedAt AS endedAt,
3154
+ r.timeToFirstChunkMs AS timeToFirstChunkMs,
3138
3155
  r.entityName AS entityName,
3139
3156
  r.entityType AS entityType,
3140
3157
  r.environment AS environment,
@@ -3370,8 +3387,11 @@ const ROOT_PUSHDOWN_FIELDS = /* @__PURE__ */ new Set([
3370
3387
  "userId",
3371
3388
  "organizationId",
3372
3389
  "endedAt",
3390
+ "timeToFirstChunkMs",
3373
3391
  "entityName",
3374
3392
  "entityType",
3393
+ "name",
3394
+ "spanType",
3375
3395
  "environment"
3376
3396
  ]);
3377
3397
  function isRootPushdownPredicate(predicate) {
@@ -3497,6 +3517,7 @@ function compileDuckDBTraceScope(relatedCollections, scope, timeRange, rootPredi
3497
3517
  startedAt,
3498
3518
  endedAt,
3499
3519
  ${durationMsSql("startedAt", "endedAt")} AS durationMs,
3520
+ timeToFirstChunkMs,
3500
3521
  CASE WHEN error IS NOT NULL THEN 'error' ELSE 'success' END AS status,
3501
3522
  error,
3502
3523
  entityType,
@@ -3596,15 +3617,15 @@ ORDER BY delta_rows.deltaWatermark ASC NULLS LAST, delta_rows.traceId ASC`,
3596
3617
  };
3597
3618
  }
3598
3619
  const orderField = plan.orderBy.field;
3599
- const direction = plan.orderBy.direction === "asc" ? "ASC" : "DESC";
3620
+ const order = `${orderField} ${plan.orderBy.direction === "asc" ? "ASC" : "DESC"} NULLS LAST, traceId ASC`;
3600
3621
  if (plan.paginationMode === "page") {
3601
3622
  values.push(plan.perPage, plan.page * plan.perPage);
3602
3623
  return {
3603
3624
  sql: `${candidates},
3604
3625
  page_rows AS (
3605
- SELECT *, row_number() OVER (ORDER BY ${orderField} ${direction}, traceId ASC) AS __row_position
3626
+ SELECT *, row_number() OVER (ORDER BY ${order}) AS __row_position
3606
3627
  FROM candidates
3607
- ORDER BY ${orderField} ${direction}, traceId ASC
3628
+ ORDER BY ${order}
3608
3629
  LIMIT ? OFFSET ?
3609
3630
  ),
3610
3631
  page_total AS (
@@ -3619,8 +3640,13 @@ ORDER BY page_rows.__row_position ASC NULLS LAST`,
3619
3640
  };
3620
3641
  }
3621
3642
  let pageCondition = "";
3622
- if (plan.cursor) {
3623
- pageCondition = `WHERE (${orderField} ${plan.orderBy.direction === "asc" ? ">" : "<"} CAST(? AS TIMESTAMP) OR (${orderField} = CAST(? AS TIMESTAMP) AND traceId > ?))`;
3643
+ if (plan.cursor?.sortValue === null) {
3644
+ pageCondition = `WHERE ${orderField} IS NULL AND traceId > ?`;
3645
+ values.push(plan.cursor.traceId);
3646
+ } else if (plan.cursor) {
3647
+ const comparison = plan.orderBy.direction === "asc" ? ">" : "<";
3648
+ const value = `CAST(? AS ${typeof plan.cursor.sortValue === "number" ? "DOUBLE" : "TIMESTAMP"})`;
3649
+ pageCondition = `WHERE (${orderField} ${comparison} ${value} OR ${orderField} IS NULL OR (${orderField} = ${value} AND traceId > ?))`;
3624
3650
  values.push(plan.cursor.sortValue, plan.cursor.sortValue, plan.cursor.traceId);
3625
3651
  }
3626
3652
  values.push(plan.limit + 1);
@@ -3629,7 +3655,7 @@ ORDER BY page_rows.__row_position ASC NULLS LAST`,
3629
3655
  SELECT *
3630
3656
  FROM candidates
3631
3657
  ${pageCondition}
3632
- ORDER BY ${orderField} ${direction}, traceId ASC
3658
+ ORDER BY ${order}
3633
3659
  LIMIT ?`,
3634
3660
  values
3635
3661
  };
@@ -3812,6 +3838,29 @@ async function fillMissingTracePreviews(db, rows) {
3812
3838
  for (const [preview, column] of TRACE_PREVIEW_PAYLOADS) if (needsPreview(row[preview])) row[preview] = previewFromPayload(payload?.[column] ?? null);
3813
3839
  }
3814
3840
  }
3841
+ function traceFromRow(row) {
3842
+ return {
3843
+ traceId: String(row.traceId),
3844
+ rootSpanId: String(row.rootSpanId),
3845
+ name: row.name,
3846
+ entityId: row.entityId ?? null,
3847
+ parentSpanId: row.parentSpanId ?? null,
3848
+ createdAt: asIsoTimestamp(row.startedAt),
3849
+ metadata: parseJson(row.metadata) ?? null,
3850
+ inputPreview: apiPreview(row.inputPreview),
3851
+ outputPreview: apiPreview(row.outputPreview),
3852
+ errorPreview: apiPreview(row.errorPreview),
3853
+ threadId: row.threadId == null ? null : String(row.threadId),
3854
+ resourceId: row.resourceId == null ? null : String(row.resourceId),
3855
+ startedAt: asIsoTimestamp(row.startedAt),
3856
+ endedAt: asIsoTimestamp(row.endedAt),
3857
+ entityName: row.entityName == null ? null : String(row.entityName),
3858
+ entityType: row.entityType == null ? null : String(row.entityType),
3859
+ environment: row.environment == null ? null : String(row.environment),
3860
+ status: row.status,
3861
+ ...require_features.timeToFirstChunkSupported && { timeToFirstChunkMs: row.timeToFirstChunkMs == null ? null : Number(row.timeToFirstChunkMs) }
3862
+ };
3863
+ }
3815
3864
  async function queryTraces(db, plan) {
3816
3865
  if (plan.paginationMode === "delta") assertDeltaPollingEnabled();
3817
3866
  if (plan.paginationMode === "page") {
@@ -3819,26 +3868,7 @@ async function queryTraces(db, plan) {
3819
3868
  const rows = await db.query(query.sql, query.values);
3820
3869
  const total = Number(rows[0]?.total ?? 0);
3821
3870
  await fillMissingTracePreviews(db, rows);
3822
- const traces = rows.filter((row) => row.traceId != null).map((row) => ({
3823
- traceId: String(row.traceId),
3824
- rootSpanId: String(row.rootSpanId),
3825
- name: row.name,
3826
- entityId: row.entityId ?? null,
3827
- parentSpanId: row.parentSpanId ?? null,
3828
- createdAt: asIsoTimestamp(row.startedAt),
3829
- metadata: parseJson(row.metadata) ?? null,
3830
- inputPreview: apiPreview(row.inputPreview),
3831
- outputPreview: apiPreview(row.outputPreview),
3832
- errorPreview: apiPreview(row.errorPreview),
3833
- threadId: row.threadId == null ? null : String(row.threadId),
3834
- resourceId: row.resourceId == null ? null : String(row.resourceId),
3835
- startedAt: asIsoTimestamp(row.startedAt),
3836
- endedAt: asIsoTimestamp(row.endedAt),
3837
- entityName: row.entityName == null ? null : String(row.entityName),
3838
- entityType: row.entityType == null ? null : String(row.entityType),
3839
- environment: row.environment == null ? null : String(row.environment),
3840
- status: row.status
3841
- }));
3871
+ const traces = rows.filter((row) => row.traceId != null).map(traceFromRow);
3842
3872
  return _mastra_core_storage.traceQueryResponseSchema.parse({
3843
3873
  traces,
3844
3874
  ...deltaPollingFeatureEnabled() && typeof _mastra_core_storage.encodeTraceQueryDeltaCursor === "function" ? { deltaCursor: _mastra_core_storage.encodeTraceQueryDeltaCursor(plan, "duckdb", String(rows[0]?.streamHead ?? 0)) } : {},
@@ -3866,26 +3896,7 @@ async function queryTraces(db, plan) {
3866
3896
  });
3867
3897
  }
3868
3898
  await fillMissingTracePreviews(db, visibleRows);
3869
- const traces = visibleRows.map((row) => ({
3870
- traceId: String(row.traceId),
3871
- rootSpanId: String(row.rootSpanId),
3872
- name: row.name,
3873
- entityId: row.entityId ?? null,
3874
- parentSpanId: row.parentSpanId ?? null,
3875
- createdAt: asIsoTimestamp(row.startedAt),
3876
- metadata: parseJson(row.metadata) ?? null,
3877
- inputPreview: apiPreview(row.inputPreview),
3878
- outputPreview: apiPreview(row.outputPreview),
3879
- errorPreview: apiPreview(row.errorPreview),
3880
- threadId: row.threadId == null ? null : String(row.threadId),
3881
- resourceId: row.resourceId == null ? null : String(row.resourceId),
3882
- startedAt: asIsoTimestamp(row.startedAt),
3883
- endedAt: asIsoTimestamp(row.endedAt),
3884
- entityName: row.entityName == null ? null : String(row.entityName),
3885
- entityType: row.entityType == null ? null : String(row.entityType),
3886
- environment: row.environment == null ? null : String(row.environment),
3887
- status: row.status
3888
- }));
3899
+ const traces = visibleRows.map(traceFromRow);
3889
3900
  if (plan.paginationMode === "delta") {
3890
3901
  const previous = _mastra_core_storage.getTraceQueryDeltaWatermark(plan, "duckdb") ?? "0";
3891
3902
  const head = String(rows[0]?.streamHead ?? 0);
@@ -3904,7 +3915,7 @@ async function queryTraces(db, plan) {
3904
3915
  traces,
3905
3916
  page: { next: rows.length > plan.limit && last ? _mastra_core_storage.encodeTraceQueryCursor(plan, {
3906
3917
  result: "traces",
3907
- sortValue: last[plan.orderBy.field],
3918
+ sortValue: last[plan.orderBy.field] ?? null,
3908
3919
  traceId: last.traceId
3909
3920
  }) : null }
3910
3921
  });
@@ -3926,6 +3937,12 @@ async function queryThreads(db, plan) {
3926
3937
  function compileSpanQueryPredicate(predicate) {
3927
3938
  return compileScalarPredicate(predicate, SPAN_QUERY_FIELDS);
3928
3939
  }
3940
+ function compileThreadQueryPredicate(predicate) {
3941
+ return compileScalarPredicate(predicate, Object.fromEntries(Object.entries(_mastra_core_storage.THREAD_QUERY_FIELD_REGISTRY).map(([field, rule]) => [field, {
3942
+ sql: `s.${field}`,
3943
+ parameterType: rule.valueKind === "timestamp" ? "timestamp" : "scalar"
3944
+ }])));
3945
+ }
3929
3946
  //#endregion
3930
3947
  //#region src/storage/domains/observability/span-query.ts
3931
3948
  const identityFields = [
@@ -3969,7 +3986,8 @@ const fields = [
3969
3986
  "serviceName",
3970
3987
  "experimentId",
3971
3988
  "environment",
3972
- "executionSource"
3989
+ "executionSource",
3990
+ "timeToFirstChunkMs"
3973
3991
  ];
3974
3992
  function keys(rows, values, alias = "") {
3975
3993
  return rows.map((row) => `(${identityFields.map((field) => {
@@ -3983,13 +4001,16 @@ function afterCursor(plan) {
3983
4001
  values: []
3984
4002
  };
3985
4003
  const cursor = plan.cursor;
3986
- const comparisons = [`s.${plan.orderBy.field} ${plan.orderBy.direction === "asc" ? ">" : "<"} CAST(? AS TIMESTAMP)`];
3987
- const values = [cursor.sortValue];
4004
+ const sort = `s.${plan.orderBy.field}`;
4005
+ const empty = cursor.sortValue === null;
4006
+ const value = `CAST(? AS ${typeof cursor.sortValue === "number" ? "DOUBLE" : "TIMESTAMP"})`;
4007
+ const comparisons = empty ? [] : [`${sort} ${plan.orderBy.direction === "asc" ? ">" : "<"} ${value}`, `${sort} IS NULL`];
4008
+ const values = empty ? [] : [cursor.sortValue];
3988
4009
  for (let i = 0; i < identityFields.length; i++) {
3989
4010
  const field = identityFields[i];
3990
4011
  if (cursor[field] === null) continue;
3991
- const equal = [`s.${plan.orderBy.field} = CAST(? AS TIMESTAMP)`];
3992
- values.push(cursor.sortValue);
4012
+ const equal = [empty ? `${sort} IS NULL` : `${sort} = ${value}`];
4013
+ if (!empty) values.push(cursor.sortValue);
3993
4014
  for (const prior of identityFields.slice(0, i)) {
3994
4015
  equal.push(`s.${prior} IS NOT DISTINCT FROM ?`);
3995
4016
  values.push(cursor[prior]);
@@ -4048,7 +4069,7 @@ function compileDuckDBSpanQuery(plan) {
4048
4069
  "cursorId"
4049
4070
  ].map((field) => `s.${field}`).join(", ")}
4050
4071
  FROM ranked s WHERE rank = 1 AND endedAt IS NOT NULL AND startedAt >= CAST(? AS TIMESTAMP) AND startedAt < CAST(? AS TIMESTAMP) AND (${predicate.sql}) AND ${after.sql}
4051
- ORDER BY s.${plan.orderBy.field} ${plan.orderBy.direction === "asc" ? "ASC" : "DESC"}, ${identityFields.map((field) => `s.${field} ASC NULLS LAST`).join(", ")}
4072
+ ORDER BY s.${plan.orderBy.field} ${plan.orderBy.direction === "asc" ? "ASC" : "DESC"} NULLS LAST, ${identityFields.map((field) => `s.${field} ASC NULLS LAST`).join(", ")}
4052
4073
  LIMIT ?`,
4053
4074
  values
4054
4075
  };
@@ -4073,10 +4094,11 @@ async function querySpans(db, plan) {
4073
4094
  };
4074
4095
  const compiled = compileDuckDBSpanQuery(plan);
4075
4096
  const rows = await query(compiled.sql, compiled.values);
4076
- const selected = rows.map(({ cursorId: _cursorId, startedAt, endedAt, ...row }) => ({
4097
+ const selected = rows.map(({ cursorId: _cursorId, startedAt, endedAt, timeToFirstChunkMs, ...row }) => ({
4077
4098
  ...row,
4078
4099
  startedAt: startedAt.toISOString(),
4079
- endedAt: endedAt.toISOString()
4100
+ endedAt: endedAt.toISOString(),
4101
+ ...require_features.timeToFirstChunkSupported && { timeToFirstChunkMs: timeToFirstChunkMs == null ? null : Number(timeToFirstChunkMs) }
4080
4102
  }));
4081
4103
  const page = rows.slice(0, plan.limit);
4082
4104
  if (!page.length) return buildSpanQueryResponse(plan, [], []);
@@ -4127,11 +4149,264 @@ async function querySpans(db, plan) {
4127
4149
  return buildSpanQueryResponse(plan, hydrated, payloads, metrics);
4128
4150
  }
4129
4151
  //#endregion
4152
+ //#region src/storage/domains/observability/thread-query.ts
4153
+ const contextFields = [
4154
+ "resourceId",
4155
+ "userId",
4156
+ "entityType",
4157
+ "entityId",
4158
+ "entityName",
4159
+ "entityVersionId",
4160
+ "runId",
4161
+ "sessionId",
4162
+ "requestId",
4163
+ "serviceName",
4164
+ "experimentId",
4165
+ "environment",
4166
+ "executionSource"
4167
+ ];
4168
+ function predicateFields(predicate, fields = /* @__PURE__ */ new Set()) {
4169
+ if (!predicate) return fields;
4170
+ if (predicate.type === "boolean") for (const arg of predicate.args) predicateFields(arg, fields);
4171
+ else if (predicate.type === "not") predicateFields(predicate.arg, fields);
4172
+ else fields.add(predicate.field);
4173
+ return fields;
4174
+ }
4175
+ function canUseThreadCandidates(probe) {
4176
+ return Number(probe.threadCount) <= 16384 && Number(probe.traceCount) <= 1e5 && Number(probe.threadBytes) + Number(probe.traceBytes) <= 4 * 1024 * 1024;
4177
+ }
4178
+ function compileDuckDBThreadRowsQuery(plan, strategy = "full") {
4179
+ const values = [];
4180
+ const scope = [];
4181
+ if (plan.scope) {
4182
+ values.push(plan.scope.organizationId);
4183
+ scope.push("organizationId = ?");
4184
+ if (plan.scope.resourceId !== void 0) {
4185
+ values.push(plan.scope.resourceId);
4186
+ scope.push("resourceId = ?");
4187
+ }
4188
+ }
4189
+ const identity = (field) => `(organizationId IS NULL, COALESCE(organizationId, ''), ${field})`;
4190
+ const threadFilter = `${identity("threadId")} IN (SELECT ${identity("threadId")} FROM candidate_threads)`;
4191
+ const endWindow = plan.timeRange.mode === "threads" && plan.timeRange.field === "endedAt";
4192
+ const scopedEvents = `scoped_events AS NOT MATERIALIZED (
4193
+ SELECT * FROM span_events WHERE parentSpanId IS NULL ${scope.length ? `AND ${scope.join(" AND ")}` : ""}
4194
+ )`;
4195
+ const candidates = `window_traces AS MATERIALIZED (
4196
+ SELECT DISTINCT organizationId, traceId FROM scoped_events
4197
+ WHERE ${endWindow ? "endedAt" : "timestamp"} >= CAST(? AS TIMESTAMP) AND ${endWindow ? "endedAt" : "timestamp"} < CAST(? AS TIMESTAMP)
4198
+ ${endWindow ? "" : "AND eventType = 'start'"}
4199
+ ), candidate_threads AS MATERIALIZED (
4200
+ SELECT DISTINCT organizationId, threadId FROM scoped_events
4201
+ WHERE threadId IS NOT NULL AND ${identity("traceId")} IN (SELECT ${identity("traceId")} FROM window_traces)
4202
+ ${strategy === "probe" ? "LIMIT 16385" : ""}
4203
+ ), candidate_traces AS MATERIALIZED (
4204
+ SELECT DISTINCT organizationId, traceId FROM scoped_events WHERE ${threadFilter}
4205
+ ${strategy === "probe" ? "LIMIT 100001" : ""}
4206
+ )`;
4207
+ if (strategy !== "full") values.push(plan.timeRange.from, plan.timeRange.to);
4208
+ if (strategy === "probe") return {
4209
+ sql: `WITH ${scopedEvents}, ${candidates}
4210
+ SELECT (SELECT count(*) FROM candidate_threads) AS threadCount,
4211
+ (SELECT COALESCE(sum(octet_length(encode(COALESCE(organizationId, ''))) + octet_length(encode(threadId))), 0) FROM candidate_threads) AS threadBytes,
4212
+ count(*) AS traceCount, COALESCE(sum(octet_length(encode(COALESCE(organizationId, ''))) + octet_length(encode(traceId))), 0) AS traceBytes
4213
+ FROM candidate_traces`,
4214
+ values
4215
+ };
4216
+ values.push(plan.timeRange.from, plan.timeRange.to);
4217
+ const qualify = plan.timeRange.mode === "traces" ? "s.inWindow" : `s.${plan.timeRange.field} >= CAST(? AS TIMESTAMP) AND s.${plan.timeRange.field} < CAST(? AS TIMESTAMP)`;
4218
+ if (plan.timeRange.mode === "threads") values.push(plan.timeRange.from, plan.timeRange.to);
4219
+ const predicate = plan.where ? compileThreadQueryPredicate(plan.where) : {
4220
+ sql: "TRUE",
4221
+ values: []
4222
+ };
4223
+ values.push(...predicate.values);
4224
+ let after = "TRUE";
4225
+ if (plan.cursor) {
4226
+ const cursor = plan.cursor;
4227
+ const terms = [`s.${plan.orderBy.field} ${plan.orderBy.direction === "asc" ? ">" : "<"} CAST(? AS TIMESTAMP)`];
4228
+ values.push(cursor.sortValue);
4229
+ const ids = ["organizationId", "threadId"];
4230
+ for (let i = 0; i < ids.length; i++) {
4231
+ const field = ids[i];
4232
+ if (cursor[field] === null) continue;
4233
+ const equal = [`s.${plan.orderBy.field} = CAST(? AS TIMESTAMP)`];
4234
+ values.push(cursor.sortValue);
4235
+ for (const prior of ids.slice(0, i)) {
4236
+ equal.push(`s.${prior} IS NOT DISTINCT FROM ?`);
4237
+ values.push(cursor[prior]);
4238
+ }
4239
+ values.push(cursor[field]);
4240
+ terms.push(`(${equal.join(" AND ")} AND (s.${field} > ? OR s.${field} IS NULL))`);
4241
+ }
4242
+ after = `(${terms.join(" OR ")})`;
4243
+ }
4244
+ values.push(plan.limit + 1);
4245
+ const filtered = predicateFields(plan.where);
4246
+ const firstContext = [...contextFields, "timeToFirstChunkMs"].filter((field) => field === "resourceId" || filtered.has(field));
4247
+ const contextExpression = (field) => field === "executionSource" ? "source AS executionSource" : field === "timeToFirstChunkMs" ? "CASE WHEN spanType = 'agent_run' THEN timeToFirstChunkMs END AS timeToFirstChunkMs" : field;
4248
+ const lastFields = [
4249
+ "traceId",
4250
+ "spanId",
4251
+ "cursorId",
4252
+ "resourceId",
4253
+ "endedAt",
4254
+ ...filtered.has("status") ? ["status"] : []
4255
+ ];
4256
+ return {
4257
+ sql: `WITH ${scopedEvents}, ${strategy === "candidates" ? `${candidates}, ` : ""}root_events AS (
4258
+ SELECT organizationId, threadId, traceId, spanId, cursorId, endedAt,
4259
+ ${firstContext.map(contextExpression).join(", ")},
4260
+ min(timestamp) FILTER (WHERE eventType = 'start') OVER (PARTITION BY organizationId, traceId, spanId) AS startedAt
4261
+ ${filtered.has("status") ? ", CASE WHEN error IS NULL THEN 'success' ELSE 'error' END AS status" : ""}
4262
+ FROM scoped_events ${strategy === "candidates" ? `WHERE ${identity("traceId")} IN (SELECT ${identity("traceId")} FROM candidate_traces)` : ""}
4263
+ ), current_roots AS (
4264
+ SELECT *, row_number() OVER (PARTITION BY organizationId, traceId ORDER BY startedAt ASC, spanId, cursorId DESC) AS rootRank
4265
+ FROM root_events
4266
+ ), ranked AS (
4267
+ SELECT *, row_number() OVER first_order AS rank,
4268
+ max(endedAt) OVER population AS lastActivity, count(*) OVER population AS traceCount,
4269
+ bool_or(startedAt >= CAST(? AS TIMESTAMP) AND startedAt < CAST(? AS TIMESTAMP)) OVER population AS inWindow,
4270
+ ${lastFields.map((field) => `first_value(${field}) OVER last_order AS last${field[0].toUpperCase()}${field.slice(1)}`).join(", ")}
4271
+ FROM current_roots WHERE rootRank = 1 AND endedAt IS NOT NULL AND startedAt IS NOT NULL AND threadId IS NOT NULL ${strategy === "candidates" ? `AND ${threadFilter}` : ""}
4272
+ WINDOW population AS (PARTITION BY organizationId, threadId),
4273
+ first_order AS (PARTITION BY organizationId, threadId ORDER BY startedAt, traceId),
4274
+ last_order AS (PARTITION BY organizationId, threadId ORDER BY startedAt DESC, traceId DESC)
4275
+ ), rows AS (
4276
+ SELECT organizationId, threadId, traceId AS firstTraceId, spanId AS firstSpanId, cursorId AS firstCursorId,
4277
+ endedAt AS firstEndedAt, startedAt, lastActivity AS endedAt, traceCount, inWindow,
4278
+ ${firstContext.join(", ")},
4279
+ ${lastFields.map((field) => `last${field[0].toUpperCase()}${field.slice(1)}${field === "status" ? " AS status" : ""}`).join(", ")}
4280
+ FROM ranked WHERE rank = 1
4281
+ ) SELECT * FROM rows s WHERE ${qualify} AND (${predicate.sql}) AND ${after}
4282
+ ORDER BY s.${plan.orderBy.field} ${plan.orderBy.direction === "asc" ? "ASC" : "DESC"}, s.organizationId ASC NULLS LAST, s.threadId ASC LIMIT ?`,
4283
+ values
4284
+ };
4285
+ }
4286
+ async function queryThreadRows(db, plan, strategy = "auto") {
4287
+ const deadline = performance.now() + _mastra_core_storage.TRACE_QUERY_DEFAULT_TIMEOUT_MS;
4288
+ const run = async (sql, values) => {
4289
+ const remaining = Math.floor(deadline - performance.now());
4290
+ if (remaining <= 0) throw new _mastra_core_storage.TraceQueryExecutionError();
4291
+ try {
4292
+ return await db.query(sql, values, remaining);
4293
+ } catch (error) {
4294
+ if (error instanceof require_features.DuckDBQueryTimeoutError) throw new _mastra_core_storage.TraceQueryExecutionError();
4295
+ if (error instanceof Error && /out of memory/i.test(error.message)) throw new _mastra_core_storage.TraceQueryResourceLimitError();
4296
+ throw error;
4297
+ }
4298
+ };
4299
+ if (strategy === "auto") {
4300
+ const probeQuery = compileDuckDBThreadRowsQuery(plan, "probe");
4301
+ const [probe] = await run(probeQuery.sql, probeQuery.values);
4302
+ if (Number(probe.threadCount) === 0) return {
4303
+ threads: [],
4304
+ page: { next: null }
4305
+ };
4306
+ strategy = canUseThreadCandidates(probe) ? "candidates" : "full";
4307
+ }
4308
+ const compiled = compileDuckDBThreadRowsQuery(plan, strategy);
4309
+ const selected = await run(compiled.sql, compiled.values);
4310
+ const page = selected.slice(0, plan.limit);
4311
+ if (!page.length) return {
4312
+ threads: [],
4313
+ page: { next: null }
4314
+ };
4315
+ const values = [];
4316
+ const keys = page.flatMap((row) => ["first", "last"].map((which) => {
4317
+ const entries = {
4318
+ organizationId: row.organizationId,
4319
+ resourceId: which === "first" ? row.resourceId : row.lastResourceId,
4320
+ traceId: row[`${which}TraceId`],
4321
+ spanId: row[`${which}SpanId`],
4322
+ cursorId: row[`${which}CursorId`],
4323
+ endedAt: row[`${which}EndedAt`]
4324
+ };
4325
+ return `(${Object.entries(entries).map(([field, value]) => {
4326
+ values.push(value);
4327
+ return `${field} ${field === "organizationId" || field === "resourceId" ? "IS NOT DISTINCT FROM" : "="} ?`;
4328
+ }).join(" AND ")})`;
4329
+ }));
4330
+ const payloads = await run(`SELECT organizationId, traceId, cursorId, spanType,
4331
+ ${contextFields.map((field) => field === "executionSource" ? "source AS executionSource" : field).join(", ")},
4332
+ CASE WHEN spanType = 'agent_run' THEN timeToFirstChunkMs END AS timeToFirstChunkMs,
4333
+ CASE WHEN error IS NULL THEN 'success' ELSE 'error' END AS status,
4334
+ ${[
4335
+ "input",
4336
+ "output",
4337
+ "error"
4338
+ ].map((field) => `${field}Preview, CASE WHEN ${missingPreviewSql(`${field}Preview`)} THEN CAST(${field === "error" ? field : payloadColumnSql(field)} AS VARCHAR) END AS ${field}`).join(", ")}
4339
+ FROM span_events WHERE ${keys.join(" OR ")}`, values);
4340
+ const key = (org, trace, version) => JSON.stringify([
4341
+ org,
4342
+ trace,
4343
+ String(version)
4344
+ ]);
4345
+ const byRoot = new Map(payloads.map((row) => [key(row.organizationId, row.traceId, row.cursorId), row]));
4346
+ const agents = page.filter((row) => byRoot.get(key(row.organizationId, row.firstTraceId, row.firstCursorId))?.spanType === "agent_run");
4347
+ const models = /* @__PURE__ */ new Map();
4348
+ if (agents.length) {
4349
+ const modelValues = [];
4350
+ const rows = await run(`WITH generations AS (
4351
+ SELECT organizationId, traceId, spanId, endedAt, cursorId, attributes,
4352
+ min(timestamp) FILTER (WHERE eventType = 'start') OVER (PARTITION BY organizationId, traceId, spanId) AS startedAt
4353
+ FROM span_events WHERE spanType = 'model_generation' AND (${agents.map((row) => `(${Object.entries({
4354
+ organizationId: row.organizationId,
4355
+ resourceId: row.resourceId,
4356
+ traceId: row.firstTraceId,
4357
+ parentSpanId: row.firstSpanId
4358
+ }).map(([field, value]) => {
4359
+ modelValues.push(value);
4360
+ return `${field} ${field === "organizationId" || field === "resourceId" ? "IS NOT DISTINCT FROM" : "="} ?`;
4361
+ }).join(" AND ")})`).join(" OR ")})
4362
+ ) SELECT organizationId, traceId,
4363
+ CASE WHEN json_type(attributes, '$.model') = 'VARCHAR' THEN json_extract_string(attributes, '$.model') END AS model,
4364
+ CASE WHEN json_type(attributes, '$.provider') = 'VARCHAR' THEN json_extract_string(attributes, '$.provider') END AS provider
4365
+ FROM generations WHERE endedAt IS NOT NULL
4366
+ QUALIFY row_number() OVER (PARTITION BY organizationId, traceId ORDER BY startedAt, spanId, cursorId DESC) = 1`, modelValues);
4367
+ for (const row of rows) models.set(JSON.stringify([row.organizationId, row.traceId]), row);
4368
+ }
4369
+ const threads = page.flatMap((row) => {
4370
+ const first = byRoot.get(key(row.organizationId, row.firstTraceId, row.firstCursorId));
4371
+ const last = byRoot.get(key(row.organizationId, row.lastTraceId, row.lastCursorId));
4372
+ if (!first || !last) return [];
4373
+ const model = models.get(JSON.stringify([row.organizationId, row.firstTraceId]));
4374
+ return [_mastra_core_storage.threadQueryRowSchema.parse({
4375
+ organizationId: row.organizationId,
4376
+ threadId: row.threadId,
4377
+ firstTraceId: row.firstTraceId,
4378
+ lastTraceId: row.lastTraceId,
4379
+ ...Object.fromEntries(contextFields.map((field) => [field, first[field]])),
4380
+ status: last.status,
4381
+ startedAt: row.startedAt.toISOString(),
4382
+ endedAt: row.endedAt.toISOString(),
4383
+ traceCount: Number(row.traceCount),
4384
+ timeToFirstChunkMs: first.timeToFirstChunkMs,
4385
+ model: model?.model ?? null,
4386
+ provider: model?.provider ?? null,
4387
+ inputPreview: readPreview(first.inputPreview, first.input, true),
4388
+ outputPreview: readPreview(last.outputPreview, last.output, true),
4389
+ errorPreview: readPreview(last.errorPreview, last.error, true)
4390
+ })];
4391
+ });
4392
+ const last = page.at(-1);
4393
+ return {
4394
+ threads,
4395
+ page: { next: selected.length > plan.limit ? _mastra_core_storage.encodeThreadQueryCursor(plan, {
4396
+ organizationId: last.organizationId,
4397
+ threadId: last.threadId,
4398
+ sortValue: last[plan.orderBy.field].toISOString()
4399
+ }) : null }
4400
+ };
4401
+ }
4402
+ //#endregion
4130
4403
  //#region src/storage/domains/observability/trace-aggregate.ts
4131
4404
  /** Trace-root column expressions (alias `r`) for each groupable dimension; same values `where` sees. */
4132
4405
  const DIMENSION_SQL = {
4133
4406
  entityType: "r.entityType",
4134
4407
  entityName: "r.entityName",
4408
+ name: "r.name",
4409
+ spanType: "r.spanType",
4135
4410
  environment: "r.environment",
4136
4411
  status: TRACE_STATUS_SQL,
4137
4412
  serviceName: "r.serviceName",
@@ -5900,12 +6175,15 @@ var ObservabilityStorageDuckDB = class ObservabilityStorageDuckDB extends _mastr
5900
6175
  "trace-query",
5901
6176
  "trace-aggregate",
5902
6177
  ...require_features.spanQueryFeatures,
6178
+ ...typeof _mastra_core_storage.planThreadRowsQuery === "function" ? ["thread-query-rows"] : [],
5903
6179
  "trace-query-root-duration",
5904
6180
  "trace-query-discovery",
5905
6181
  "thread-query",
5906
6182
  "trace-query-tenant-scope",
5907
6183
  "feedback",
5908
- "trace-query-context-ids"
6184
+ "trace-query-context-ids",
6185
+ ...require_features.timeToFirstChunkFeatures,
6186
+ "trace-query-root-span-fields"
5909
6187
  ];
5910
6188
  return [
5911
6189
  "metrics",
@@ -5920,12 +6198,15 @@ var ObservabilityStorageDuckDB = class ObservabilityStorageDuckDB extends _mastr
5920
6198
  "trace-query",
5921
6199
  "trace-aggregate",
5922
6200
  ...require_features.spanQueryFeatures,
6201
+ ...typeof _mastra_core_storage.planThreadRowsQuery === "function" ? ["thread-query-rows"] : [],
5923
6202
  "trace-query-root-duration",
5924
6203
  "trace-query-discovery",
5925
6204
  "thread-query",
5926
6205
  "trace-query-tenant-scope",
5927
6206
  "feedback",
5928
- "trace-query-context-ids"
6207
+ "trace-query-context-ids",
6208
+ ...require_features.timeToFirstChunkFeatures,
6209
+ "trace-query-root-span-fields"
5929
6210
  ];
5930
6211
  }
5931
6212
  async createSpan(args) {
@@ -5971,6 +6252,7 @@ var ObservabilityStorageDuckDB = class ObservabilityStorageDuckDB extends _mastr
5971
6252
  return getTraceQueryValues(this.db, plan);
5972
6253
  }
5973
6254
  async queryThreads(plan) {
6255
+ if (plan.result === "threadRows") return queryThreadRows(this.db, plan);
5974
6256
  return queryThreads(this.db, plan);
5975
6257
  }
5976
6258
  async listTracesLight(args) {
@@ -6086,4 +6368,4 @@ var ObservabilityStorageDuckDB = class ObservabilityStorageDuckDB extends _mastr
6086
6368
  //#endregion
6087
6369
  exports.ObservabilityStorageDuckDB = ObservabilityStorageDuckDB;
6088
6370
 
6089
- //# sourceMappingURL=observability-DJs6G-BH.cjs.map
6371
+ //# sourceMappingURL=observability-TeHp5JpN.cjs.map