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

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-vjVgmb36.js → features-Clv6RO_w.js} +6 -2
  4. package/dist/{features-vjVgmb36.js.map → features-Clv6RO_w.js.map} +1 -1
  5. package/dist/{features-BAGbFa2n.cjs → features-DxaUwIC2.cjs} +23 -1
  6. package/dist/{features-BAGbFa2n.cjs.map → features-DxaUwIC2.cjs.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-C1o-hqrj.js} +321 -47
  12. package/dist/observability-C1o-hqrj.js.map +1 -0
  13. package/dist/{observability-DJs6G-BH.cjs → observability-D70dyGYt.cjs} +321 -47
  14. package/dist/observability-D70dyGYt.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 +4 -5
  31. package/dist/observability-DJs6G-BH.cjs.map +0 -1
  32. package/dist/observability-HUYFWVy-.js.map +0 -1
@@ -1,4 +1,4 @@
1
- import { n as DuckDBConnection, r as DuckDBQueryTimeoutError, t as spanQueryFeatures } from "./features-vjVgmb36.js";
1
+ import { a as DuckDBConnection, i as timeToFirstChunkSupported, o as DuckDBQueryTimeoutError, r as timeToFirstChunkFeatures, t as spanQueryFeatures } from "./features-Clv6RO_w.js";
2
2
  import { ErrorCategory, ErrorDomain, MastraError } from "@mastra/core/error";
3
3
  import * as coreStorage from "@mastra/core/storage";
4
4
  import { BRANCH_SPAN_TYPES, METRIC_DISTINCT_COLUMNS, ObservabilityStorage, computeTraceStatus, createStorageErrorId, executeRetentionPrune, feedbackRecordSchema, listBranchesArgsSchema, listFeedbackArgsSchema, listLogsArgsSchema, listMetricsArgsSchema, listScoresArgsSchema, listTracesArgsSchema, resolveRetentionTargets, retentionCutoffMs, toTraceSpans } from "@mastra/core/storage";
@@ -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,
@@ -3812,6 +3833,29 @@ async function fillMissingTracePreviews(db, rows) {
3812
3833
  for (const [preview, column] of TRACE_PREVIEW_PAYLOADS) if (needsPreview(row[preview])) row[preview] = previewFromPayload(payload?.[column] ?? null);
3813
3834
  }
3814
3835
  }
3836
+ function traceFromRow(row) {
3837
+ return {
3838
+ traceId: String(row.traceId),
3839
+ rootSpanId: String(row.rootSpanId),
3840
+ name: row.name,
3841
+ entityId: row.entityId ?? null,
3842
+ parentSpanId: row.parentSpanId ?? null,
3843
+ createdAt: asIsoTimestamp(row.startedAt),
3844
+ metadata: parseJson(row.metadata) ?? null,
3845
+ inputPreview: apiPreview(row.inputPreview),
3846
+ outputPreview: apiPreview(row.outputPreview),
3847
+ errorPreview: apiPreview(row.errorPreview),
3848
+ threadId: row.threadId == null ? null : String(row.threadId),
3849
+ resourceId: row.resourceId == null ? null : String(row.resourceId),
3850
+ startedAt: asIsoTimestamp(row.startedAt),
3851
+ endedAt: asIsoTimestamp(row.endedAt),
3852
+ entityName: row.entityName == null ? null : String(row.entityName),
3853
+ entityType: row.entityType == null ? null : String(row.entityType),
3854
+ environment: row.environment == null ? null : String(row.environment),
3855
+ status: row.status,
3856
+ ...timeToFirstChunkSupported && { timeToFirstChunkMs: row.timeToFirstChunkMs == null ? null : Number(row.timeToFirstChunkMs) }
3857
+ };
3858
+ }
3815
3859
  async function queryTraces(db, plan) {
3816
3860
  if (plan.paginationMode === "delta") assertDeltaPollingEnabled();
3817
3861
  if (plan.paginationMode === "page") {
@@ -3819,26 +3863,7 @@ async function queryTraces(db, plan) {
3819
3863
  const rows = await db.query(query.sql, query.values);
3820
3864
  const total = Number(rows[0]?.total ?? 0);
3821
3865
  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
- }));
3866
+ const traces = rows.filter((row) => row.traceId != null).map(traceFromRow);
3842
3867
  return coreStorage.traceQueryResponseSchema.parse({
3843
3868
  traces,
3844
3869
  ...deltaPollingFeatureEnabled() && typeof coreStorage.encodeTraceQueryDeltaCursor === "function" ? { deltaCursor: coreStorage.encodeTraceQueryDeltaCursor(plan, "duckdb", String(rows[0]?.streamHead ?? 0)) } : {},
@@ -3866,26 +3891,7 @@ async function queryTraces(db, plan) {
3866
3891
  });
3867
3892
  }
3868
3893
  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
- }));
3894
+ const traces = visibleRows.map(traceFromRow);
3889
3895
  if (plan.paginationMode === "delta") {
3890
3896
  const previous = coreStorage.getTraceQueryDeltaWatermark(plan, "duckdb") ?? "0";
3891
3897
  const head = String(rows[0]?.streamHead ?? 0);
@@ -3926,6 +3932,12 @@ async function queryThreads(db, plan) {
3926
3932
  function compileSpanQueryPredicate(predicate) {
3927
3933
  return compileScalarPredicate(predicate, SPAN_QUERY_FIELDS);
3928
3934
  }
3935
+ function compileThreadQueryPredicate(predicate) {
3936
+ return compileScalarPredicate(predicate, Object.fromEntries(Object.entries(coreStorage.THREAD_QUERY_FIELD_REGISTRY).map(([field, rule]) => [field, {
3937
+ sql: `s.${field}`,
3938
+ parameterType: rule.valueKind === "timestamp" ? "timestamp" : "scalar"
3939
+ }])));
3940
+ }
3929
3941
  //#endregion
3930
3942
  //#region src/storage/domains/observability/span-query.ts
3931
3943
  const identityFields = [
@@ -3969,7 +3981,8 @@ const fields = [
3969
3981
  "serviceName",
3970
3982
  "experimentId",
3971
3983
  "environment",
3972
- "executionSource"
3984
+ "executionSource",
3985
+ "timeToFirstChunkMs"
3973
3986
  ];
3974
3987
  function keys(rows, values, alias = "") {
3975
3988
  return rows.map((row) => `(${identityFields.map((field) => {
@@ -4073,10 +4086,11 @@ async function querySpans(db, plan) {
4073
4086
  };
4074
4087
  const compiled = compileDuckDBSpanQuery(plan);
4075
4088
  const rows = await query(compiled.sql, compiled.values);
4076
- const selected = rows.map(({ cursorId: _cursorId, startedAt, endedAt, ...row }) => ({
4089
+ const selected = rows.map(({ cursorId: _cursorId, startedAt, endedAt, timeToFirstChunkMs, ...row }) => ({
4077
4090
  ...row,
4078
4091
  startedAt: startedAt.toISOString(),
4079
- endedAt: endedAt.toISOString()
4092
+ endedAt: endedAt.toISOString(),
4093
+ ...timeToFirstChunkSupported && { timeToFirstChunkMs: timeToFirstChunkMs == null ? null : Number(timeToFirstChunkMs) }
4080
4094
  }));
4081
4095
  const page = rows.slice(0, plan.limit);
4082
4096
  if (!page.length) return buildSpanQueryResponse(plan, [], []);
@@ -4127,11 +4141,264 @@ async function querySpans(db, plan) {
4127
4141
  return buildSpanQueryResponse(plan, hydrated, payloads, metrics);
4128
4142
  }
4129
4143
  //#endregion
4144
+ //#region src/storage/domains/observability/thread-query.ts
4145
+ const contextFields = [
4146
+ "resourceId",
4147
+ "userId",
4148
+ "entityType",
4149
+ "entityId",
4150
+ "entityName",
4151
+ "entityVersionId",
4152
+ "runId",
4153
+ "sessionId",
4154
+ "requestId",
4155
+ "serviceName",
4156
+ "experimentId",
4157
+ "environment",
4158
+ "executionSource"
4159
+ ];
4160
+ function predicateFields(predicate, fields = /* @__PURE__ */ new Set()) {
4161
+ if (!predicate) return fields;
4162
+ if (predicate.type === "boolean") for (const arg of predicate.args) predicateFields(arg, fields);
4163
+ else if (predicate.type === "not") predicateFields(predicate.arg, fields);
4164
+ else fields.add(predicate.field);
4165
+ return fields;
4166
+ }
4167
+ function canUseThreadCandidates(probe) {
4168
+ return Number(probe.threadCount) <= 16384 && Number(probe.traceCount) <= 1e5 && Number(probe.threadBytes) + Number(probe.traceBytes) <= 4 * 1024 * 1024;
4169
+ }
4170
+ function compileDuckDBThreadRowsQuery(plan, strategy = "full") {
4171
+ const values = [];
4172
+ const scope = [];
4173
+ if (plan.scope) {
4174
+ values.push(plan.scope.organizationId);
4175
+ scope.push("organizationId = ?");
4176
+ if (plan.scope.resourceId !== void 0) {
4177
+ values.push(plan.scope.resourceId);
4178
+ scope.push("resourceId = ?");
4179
+ }
4180
+ }
4181
+ const identity = (field) => `(organizationId IS NULL, COALESCE(organizationId, ''), ${field})`;
4182
+ const threadFilter = `${identity("threadId")} IN (SELECT ${identity("threadId")} FROM candidate_threads)`;
4183
+ const endWindow = plan.timeRange.mode === "threads" && plan.timeRange.field === "endedAt";
4184
+ const scopedEvents = `scoped_events AS NOT MATERIALIZED (
4185
+ SELECT * FROM span_events WHERE parentSpanId IS NULL ${scope.length ? `AND ${scope.join(" AND ")}` : ""}
4186
+ )`;
4187
+ const candidates = `window_traces AS MATERIALIZED (
4188
+ SELECT DISTINCT organizationId, traceId FROM scoped_events
4189
+ WHERE ${endWindow ? "endedAt" : "timestamp"} >= CAST(? AS TIMESTAMP) AND ${endWindow ? "endedAt" : "timestamp"} < CAST(? AS TIMESTAMP)
4190
+ ${endWindow ? "" : "AND eventType = 'start'"}
4191
+ ), candidate_threads AS MATERIALIZED (
4192
+ SELECT DISTINCT organizationId, threadId FROM scoped_events
4193
+ WHERE threadId IS NOT NULL AND ${identity("traceId")} IN (SELECT ${identity("traceId")} FROM window_traces)
4194
+ ${strategy === "probe" ? "LIMIT 16385" : ""}
4195
+ ), candidate_traces AS MATERIALIZED (
4196
+ SELECT DISTINCT organizationId, traceId FROM scoped_events WHERE ${threadFilter}
4197
+ ${strategy === "probe" ? "LIMIT 100001" : ""}
4198
+ )`;
4199
+ if (strategy !== "full") values.push(plan.timeRange.from, plan.timeRange.to);
4200
+ if (strategy === "probe") return {
4201
+ sql: `WITH ${scopedEvents}, ${candidates}
4202
+ SELECT (SELECT count(*) FROM candidate_threads) AS threadCount,
4203
+ (SELECT COALESCE(sum(octet_length(encode(COALESCE(organizationId, ''))) + octet_length(encode(threadId))), 0) FROM candidate_threads) AS threadBytes,
4204
+ count(*) AS traceCount, COALESCE(sum(octet_length(encode(COALESCE(organizationId, ''))) + octet_length(encode(traceId))), 0) AS traceBytes
4205
+ FROM candidate_traces`,
4206
+ values
4207
+ };
4208
+ values.push(plan.timeRange.from, plan.timeRange.to);
4209
+ const qualify = plan.timeRange.mode === "traces" ? "s.inWindow" : `s.${plan.timeRange.field} >= CAST(? AS TIMESTAMP) AND s.${plan.timeRange.field} < CAST(? AS TIMESTAMP)`;
4210
+ if (plan.timeRange.mode === "threads") values.push(plan.timeRange.from, plan.timeRange.to);
4211
+ const predicate = plan.where ? compileThreadQueryPredicate(plan.where) : {
4212
+ sql: "TRUE",
4213
+ values: []
4214
+ };
4215
+ values.push(...predicate.values);
4216
+ let after = "TRUE";
4217
+ if (plan.cursor) {
4218
+ const cursor = plan.cursor;
4219
+ const terms = [`s.${plan.orderBy.field} ${plan.orderBy.direction === "asc" ? ">" : "<"} CAST(? AS TIMESTAMP)`];
4220
+ values.push(cursor.sortValue);
4221
+ const ids = ["organizationId", "threadId"];
4222
+ for (let i = 0; i < ids.length; i++) {
4223
+ const field = ids[i];
4224
+ if (cursor[field] === null) continue;
4225
+ const equal = [`s.${plan.orderBy.field} = CAST(? AS TIMESTAMP)`];
4226
+ values.push(cursor.sortValue);
4227
+ for (const prior of ids.slice(0, i)) {
4228
+ equal.push(`s.${prior} IS NOT DISTINCT FROM ?`);
4229
+ values.push(cursor[prior]);
4230
+ }
4231
+ values.push(cursor[field]);
4232
+ terms.push(`(${equal.join(" AND ")} AND (s.${field} > ? OR s.${field} IS NULL))`);
4233
+ }
4234
+ after = `(${terms.join(" OR ")})`;
4235
+ }
4236
+ values.push(plan.limit + 1);
4237
+ const filtered = predicateFields(plan.where);
4238
+ const firstContext = [...contextFields, "timeToFirstChunkMs"].filter((field) => field === "resourceId" || filtered.has(field));
4239
+ const contextExpression = (field) => field === "executionSource" ? "source AS executionSource" : field === "timeToFirstChunkMs" ? "CASE WHEN spanType = 'agent_run' THEN timeToFirstChunkMs END AS timeToFirstChunkMs" : field;
4240
+ const lastFields = [
4241
+ "traceId",
4242
+ "spanId",
4243
+ "cursorId",
4244
+ "resourceId",
4245
+ "endedAt",
4246
+ ...filtered.has("status") ? ["status"] : []
4247
+ ];
4248
+ return {
4249
+ sql: `WITH ${scopedEvents}, ${strategy === "candidates" ? `${candidates}, ` : ""}root_events AS (
4250
+ SELECT organizationId, threadId, traceId, spanId, cursorId, endedAt,
4251
+ ${firstContext.map(contextExpression).join(", ")},
4252
+ min(timestamp) FILTER (WHERE eventType = 'start') OVER (PARTITION BY organizationId, traceId, spanId) AS startedAt
4253
+ ${filtered.has("status") ? ", CASE WHEN error IS NULL THEN 'success' ELSE 'error' END AS status" : ""}
4254
+ FROM scoped_events ${strategy === "candidates" ? `WHERE ${identity("traceId")} IN (SELECT ${identity("traceId")} FROM candidate_traces)` : ""}
4255
+ ), current_roots AS (
4256
+ SELECT *, row_number() OVER (PARTITION BY organizationId, traceId ORDER BY startedAt ASC, spanId, cursorId DESC) AS rootRank
4257
+ FROM root_events
4258
+ ), ranked AS (
4259
+ SELECT *, row_number() OVER first_order AS rank,
4260
+ max(endedAt) OVER population AS lastActivity, count(*) OVER population AS traceCount,
4261
+ bool_or(startedAt >= CAST(? AS TIMESTAMP) AND startedAt < CAST(? AS TIMESTAMP)) OVER population AS inWindow,
4262
+ ${lastFields.map((field) => `first_value(${field}) OVER last_order AS last${field[0].toUpperCase()}${field.slice(1)}`).join(", ")}
4263
+ 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}` : ""}
4264
+ WINDOW population AS (PARTITION BY organizationId, threadId),
4265
+ first_order AS (PARTITION BY organizationId, threadId ORDER BY startedAt, traceId),
4266
+ last_order AS (PARTITION BY organizationId, threadId ORDER BY startedAt DESC, traceId DESC)
4267
+ ), rows AS (
4268
+ SELECT organizationId, threadId, traceId AS firstTraceId, spanId AS firstSpanId, cursorId AS firstCursorId,
4269
+ endedAt AS firstEndedAt, startedAt, lastActivity AS endedAt, traceCount, inWindow,
4270
+ ${firstContext.join(", ")},
4271
+ ${lastFields.map((field) => `last${field[0].toUpperCase()}${field.slice(1)}${field === "status" ? " AS status" : ""}`).join(", ")}
4272
+ FROM ranked WHERE rank = 1
4273
+ ) SELECT * FROM rows s WHERE ${qualify} AND (${predicate.sql}) AND ${after}
4274
+ ORDER BY s.${plan.orderBy.field} ${plan.orderBy.direction === "asc" ? "ASC" : "DESC"}, s.organizationId ASC NULLS LAST, s.threadId ASC LIMIT ?`,
4275
+ values
4276
+ };
4277
+ }
4278
+ async function queryThreadRows(db, plan, strategy = "auto") {
4279
+ const deadline = performance.now() + coreStorage.TRACE_QUERY_DEFAULT_TIMEOUT_MS;
4280
+ const run = async (sql, values) => {
4281
+ const remaining = Math.floor(deadline - performance.now());
4282
+ if (remaining <= 0) throw new coreStorage.TraceQueryExecutionError();
4283
+ try {
4284
+ return await db.query(sql, values, remaining);
4285
+ } catch (error) {
4286
+ if (error instanceof DuckDBQueryTimeoutError) throw new coreStorage.TraceQueryExecutionError();
4287
+ if (error instanceof Error && /out of memory/i.test(error.message)) throw new coreStorage.TraceQueryResourceLimitError();
4288
+ throw error;
4289
+ }
4290
+ };
4291
+ if (strategy === "auto") {
4292
+ const probeQuery = compileDuckDBThreadRowsQuery(plan, "probe");
4293
+ const [probe] = await run(probeQuery.sql, probeQuery.values);
4294
+ if (Number(probe.threadCount) === 0) return {
4295
+ threads: [],
4296
+ page: { next: null }
4297
+ };
4298
+ strategy = canUseThreadCandidates(probe) ? "candidates" : "full";
4299
+ }
4300
+ const compiled = compileDuckDBThreadRowsQuery(plan, strategy);
4301
+ const selected = await run(compiled.sql, compiled.values);
4302
+ const page = selected.slice(0, plan.limit);
4303
+ if (!page.length) return {
4304
+ threads: [],
4305
+ page: { next: null }
4306
+ };
4307
+ const values = [];
4308
+ const keys = page.flatMap((row) => ["first", "last"].map((which) => {
4309
+ const entries = {
4310
+ organizationId: row.organizationId,
4311
+ resourceId: which === "first" ? row.resourceId : row.lastResourceId,
4312
+ traceId: row[`${which}TraceId`],
4313
+ spanId: row[`${which}SpanId`],
4314
+ cursorId: row[`${which}CursorId`],
4315
+ endedAt: row[`${which}EndedAt`]
4316
+ };
4317
+ return `(${Object.entries(entries).map(([field, value]) => {
4318
+ values.push(value);
4319
+ return `${field} ${field === "organizationId" || field === "resourceId" ? "IS NOT DISTINCT FROM" : "="} ?`;
4320
+ }).join(" AND ")})`;
4321
+ }));
4322
+ const payloads = await run(`SELECT organizationId, traceId, cursorId, spanType,
4323
+ ${contextFields.map((field) => field === "executionSource" ? "source AS executionSource" : field).join(", ")},
4324
+ CASE WHEN spanType = 'agent_run' THEN timeToFirstChunkMs END AS timeToFirstChunkMs,
4325
+ CASE WHEN error IS NULL THEN 'success' ELSE 'error' END AS status,
4326
+ ${[
4327
+ "input",
4328
+ "output",
4329
+ "error"
4330
+ ].map((field) => `${field}Preview, CASE WHEN ${missingPreviewSql(`${field}Preview`)} THEN CAST(${field === "error" ? field : payloadColumnSql(field)} AS VARCHAR) END AS ${field}`).join(", ")}
4331
+ FROM span_events WHERE ${keys.join(" OR ")}`, values);
4332
+ const key = (org, trace, version) => JSON.stringify([
4333
+ org,
4334
+ trace,
4335
+ String(version)
4336
+ ]);
4337
+ const byRoot = new Map(payloads.map((row) => [key(row.organizationId, row.traceId, row.cursorId), row]));
4338
+ const agents = page.filter((row) => byRoot.get(key(row.organizationId, row.firstTraceId, row.firstCursorId))?.spanType === "agent_run");
4339
+ const models = /* @__PURE__ */ new Map();
4340
+ if (agents.length) {
4341
+ const modelValues = [];
4342
+ const rows = await run(`WITH generations AS (
4343
+ SELECT organizationId, traceId, spanId, endedAt, cursorId, attributes,
4344
+ min(timestamp) FILTER (WHERE eventType = 'start') OVER (PARTITION BY organizationId, traceId, spanId) AS startedAt
4345
+ FROM span_events WHERE spanType = 'model_generation' AND (${agents.map((row) => `(${Object.entries({
4346
+ organizationId: row.organizationId,
4347
+ resourceId: row.resourceId,
4348
+ traceId: row.firstTraceId,
4349
+ parentSpanId: row.firstSpanId
4350
+ }).map(([field, value]) => {
4351
+ modelValues.push(value);
4352
+ return `${field} ${field === "organizationId" || field === "resourceId" ? "IS NOT DISTINCT FROM" : "="} ?`;
4353
+ }).join(" AND ")})`).join(" OR ")})
4354
+ ) SELECT organizationId, traceId,
4355
+ CASE WHEN json_type(attributes, '$.model') = 'VARCHAR' THEN json_extract_string(attributes, '$.model') END AS model,
4356
+ CASE WHEN json_type(attributes, '$.provider') = 'VARCHAR' THEN json_extract_string(attributes, '$.provider') END AS provider
4357
+ FROM generations WHERE endedAt IS NOT NULL
4358
+ QUALIFY row_number() OVER (PARTITION BY organizationId, traceId ORDER BY startedAt, spanId, cursorId DESC) = 1`, modelValues);
4359
+ for (const row of rows) models.set(JSON.stringify([row.organizationId, row.traceId]), row);
4360
+ }
4361
+ const threads = page.flatMap((row) => {
4362
+ const first = byRoot.get(key(row.organizationId, row.firstTraceId, row.firstCursorId));
4363
+ const last = byRoot.get(key(row.organizationId, row.lastTraceId, row.lastCursorId));
4364
+ if (!first || !last) return [];
4365
+ const model = models.get(JSON.stringify([row.organizationId, row.firstTraceId]));
4366
+ return [coreStorage.threadQueryRowSchema.parse({
4367
+ organizationId: row.organizationId,
4368
+ threadId: row.threadId,
4369
+ firstTraceId: row.firstTraceId,
4370
+ lastTraceId: row.lastTraceId,
4371
+ ...Object.fromEntries(contextFields.map((field) => [field, first[field]])),
4372
+ status: last.status,
4373
+ startedAt: row.startedAt.toISOString(),
4374
+ endedAt: row.endedAt.toISOString(),
4375
+ traceCount: Number(row.traceCount),
4376
+ timeToFirstChunkMs: first.timeToFirstChunkMs,
4377
+ model: model?.model ?? null,
4378
+ provider: model?.provider ?? null,
4379
+ inputPreview: readPreview(first.inputPreview, first.input, true),
4380
+ outputPreview: readPreview(last.outputPreview, last.output, true),
4381
+ errorPreview: readPreview(last.errorPreview, last.error, true)
4382
+ })];
4383
+ });
4384
+ const last = page.at(-1);
4385
+ return {
4386
+ threads,
4387
+ page: { next: selected.length > plan.limit ? coreStorage.encodeThreadQueryCursor(plan, {
4388
+ organizationId: last.organizationId,
4389
+ threadId: last.threadId,
4390
+ sortValue: last[plan.orderBy.field].toISOString()
4391
+ }) : null }
4392
+ };
4393
+ }
4394
+ //#endregion
4130
4395
  //#region src/storage/domains/observability/trace-aggregate.ts
4131
4396
  /** Trace-root column expressions (alias `r`) for each groupable dimension; same values `where` sees. */
4132
4397
  const DIMENSION_SQL = {
4133
4398
  entityType: "r.entityType",
4134
4399
  entityName: "r.entityName",
4400
+ name: "r.name",
4401
+ spanType: "r.spanType",
4135
4402
  environment: "r.environment",
4136
4403
  status: TRACE_STATUS_SQL,
4137
4404
  serviceName: "r.serviceName",
@@ -5900,12 +6167,15 @@ var ObservabilityStorageDuckDB = class ObservabilityStorageDuckDB extends Observ
5900
6167
  "trace-query",
5901
6168
  "trace-aggregate",
5902
6169
  ...spanQueryFeatures,
6170
+ ...typeof coreStorage.planThreadRowsQuery === "function" ? ["thread-query-rows"] : [],
5903
6171
  "trace-query-root-duration",
5904
6172
  "trace-query-discovery",
5905
6173
  "thread-query",
5906
6174
  "trace-query-tenant-scope",
5907
6175
  "feedback",
5908
- "trace-query-context-ids"
6176
+ "trace-query-context-ids",
6177
+ ...timeToFirstChunkFeatures,
6178
+ "trace-query-root-span-fields"
5909
6179
  ];
5910
6180
  return [
5911
6181
  "metrics",
@@ -5920,12 +6190,15 @@ var ObservabilityStorageDuckDB = class ObservabilityStorageDuckDB extends Observ
5920
6190
  "trace-query",
5921
6191
  "trace-aggregate",
5922
6192
  ...spanQueryFeatures,
6193
+ ...typeof coreStorage.planThreadRowsQuery === "function" ? ["thread-query-rows"] : [],
5923
6194
  "trace-query-root-duration",
5924
6195
  "trace-query-discovery",
5925
6196
  "thread-query",
5926
6197
  "trace-query-tenant-scope",
5927
6198
  "feedback",
5928
- "trace-query-context-ids"
6199
+ "trace-query-context-ids",
6200
+ ...timeToFirstChunkFeatures,
6201
+ "trace-query-root-span-fields"
5929
6202
  ];
5930
6203
  }
5931
6204
  async createSpan(args) {
@@ -5971,6 +6244,7 @@ var ObservabilityStorageDuckDB = class ObservabilityStorageDuckDB extends Observ
5971
6244
  return getTraceQueryValues(this.db, plan);
5972
6245
  }
5973
6246
  async queryThreads(plan) {
6247
+ if (plan.result === "threadRows") return queryThreadRows(this.db, plan);
5974
6248
  return queryThreads(this.db, plan);
5975
6249
  }
5976
6250
  async listTracesLight(args) {
@@ -6086,4 +6360,4 @@ var ObservabilityStorageDuckDB = class ObservabilityStorageDuckDB extends Observ
6086
6360
  //#endregion
6087
6361
  export { ObservabilityStorageDuckDB };
6088
6362
 
6089
- //# sourceMappingURL=observability-HUYFWVy-.js.map
6363
+ //# sourceMappingURL=observability-C1o-hqrj.js.map