@mastra/clickhouse 1.27.0-alpha.2 → 1.28.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.
@@ -3,7 +3,7 @@ name: mastra-clickhouse
3
3
  description: Documentation for @mastra/clickhouse. Use when working with @mastra/clickhouse APIs, configuration, or implementation.
4
4
  metadata:
5
5
  package: "@mastra/clickhouse"
6
- version: "1.27.0-alpha.2"
6
+ version: "1.28.0-alpha.0"
7
7
  ---
8
8
 
9
9
  ## When to use
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "1.27.0-alpha.2",
2
+ "version": "1.28.0-alpha.0",
3
3
  "package": "@mastra/clickhouse",
4
4
  "exports": {},
5
5
  "modules": {}
package/dist/index.cjs CHANGED
@@ -323,7 +323,8 @@ const JSON_FIELDS = [
323
323
  "output",
324
324
  "error",
325
325
  "scope",
326
- "links"
326
+ "links",
327
+ "stopReasonContext"
327
328
  ];
328
329
  const NULLABLE_STRING_FIELDS = ["parentSpanId", "error"];
329
330
  function transformRow(row) {
@@ -2738,6 +2739,17 @@ CREATE TABLE IF NOT EXISTS ${TABLE_SPAN_EVENTS} (
2738
2739
  endedAt DateTime64(3, 'UTC'),
2739
2740
  timeToFirstChunkMs Nullable(Float64),
2740
2741
 
2742
+ -- Native span fields, stored as supplied (NULL when the producer set none)
2743
+ externalParentSpanId Nullable(String),
2744
+ resumedFromSpanId Nullable(String),
2745
+ model LowCardinality(Nullable(String)),
2746
+ provider LowCardinality(Nullable(String)),
2747
+ outcome LowCardinality(Nullable(String)),
2748
+ stopReason LowCardinality(Nullable(String)),
2749
+ stopReasonContext Nullable(String),
2750
+ timeToFirstTokenMs Nullable(Float64),
2751
+ errorCount Nullable(UInt32),
2752
+
2741
2753
  -- Query-relevant flexible fields
2742
2754
  tags Array(LowCardinality(String)) DEFAULT [],
2743
2755
  metadataSearch Map(LowCardinality(String), String) DEFAULT map(),
@@ -2812,6 +2824,17 @@ CREATE TABLE IF NOT EXISTS ${TABLE_TRACE_ROOTS} (
2812
2824
  endedAt DateTime64(3, 'UTC'),
2813
2825
  timeToFirstChunkMs Nullable(Float64),
2814
2826
 
2827
+ -- Native span fields, stored as supplied (NULL when the producer set none)
2828
+ externalParentSpanId Nullable(String),
2829
+ resumedFromSpanId Nullable(String),
2830
+ model LowCardinality(Nullable(String)),
2831
+ provider LowCardinality(Nullable(String)),
2832
+ outcome LowCardinality(Nullable(String)),
2833
+ stopReason LowCardinality(Nullable(String)),
2834
+ stopReasonContext Nullable(String),
2835
+ timeToFirstTokenMs Nullable(Float64),
2836
+ errorCount Nullable(UInt32),
2837
+
2815
2838
  -- Query-relevant flexible fields
2816
2839
  tags Array(LowCardinality(String)) DEFAULT [],
2817
2840
  metadataSearch Map(LowCardinality(String), String) DEFAULT map(),
@@ -2898,6 +2921,17 @@ CREATE TABLE IF NOT EXISTS ${TABLE_TRACE_BRANCHES} (
2898
2921
  endedAt DateTime64(3, 'UTC'),
2899
2922
  timeToFirstChunkMs Nullable(Float64),
2900
2923
 
2924
+ -- Native span fields, stored as supplied (NULL when the producer set none)
2925
+ externalParentSpanId Nullable(String),
2926
+ resumedFromSpanId Nullable(String),
2927
+ model LowCardinality(Nullable(String)),
2928
+ provider LowCardinality(Nullable(String)),
2929
+ outcome LowCardinality(Nullable(String)),
2930
+ stopReason LowCardinality(Nullable(String)),
2931
+ stopReasonContext Nullable(String),
2932
+ timeToFirstTokenMs Nullable(Float64),
2933
+ errorCount Nullable(UInt32),
2934
+
2901
2935
  -- Query-relevant flexible fields
2902
2936
  tags Array(LowCardinality(String)) DEFAULT [],
2903
2937
  metadataSearch Map(LowCardinality(String), String) DEFAULT map(),
@@ -3633,6 +3667,25 @@ function buildAllMvDDL(strategy) {
3633
3667
  }
3634
3668
  /** Discovery-specific refreshable MVs — created separately from core MVs. */
3635
3669
  const DISCOVERY_MV_DDL = [DISCOVERY_VALUES_MV_DDL, DISCOVERY_PAIRS_MV_DDL];
3670
+ /**
3671
+ * Span columns added after the first span tables shipped, with their types.
3672
+ * Present on span events, trace roots and trace branches.
3673
+ */
3674
+ const ADDED_SPAN_COLUMNS = [
3675
+ ["inputPreview", "Nullable(String)"],
3676
+ ["outputPreview", "Nullable(String)"],
3677
+ ["errorPreview", "Nullable(String)"],
3678
+ ["timeToFirstChunkMs", "Nullable(Float64)"],
3679
+ ["externalParentSpanId", "Nullable(String)"],
3680
+ ["resumedFromSpanId", "Nullable(String)"],
3681
+ ["model", "LowCardinality(Nullable(String))"],
3682
+ ["provider", "LowCardinality(Nullable(String))"],
3683
+ ["outcome", "LowCardinality(Nullable(String))"],
3684
+ ["stopReason", "LowCardinality(Nullable(String))"],
3685
+ ["stopReasonContext", "Nullable(String)"],
3686
+ ["timeToFirstTokenMs", "Nullable(Float64)"],
3687
+ ["errorCount", "Nullable(UInt32)"]
3688
+ ];
3636
3689
  const addColumn = (table, name, type) => ({
3637
3690
  kind: "column",
3638
3691
  table,
@@ -3657,14 +3710,11 @@ const LOG_SKIP_INDEX_COLUMNS = [
3657
3710
  "requestId"
3658
3711
  ];
3659
3712
  const ALL_MIGRATIONS = [
3660
- ...[
3661
- "inputPreview",
3662
- "outputPreview",
3663
- "errorPreview"
3664
- ].flatMap((column) => [addColumn(TABLE_TRACE_ROOTS, column, "Nullable(String)"), addColumn(TABLE_TRACE_BRANCHES, column, "Nullable(String)")]),
3665
- addColumn(TABLE_SPAN_EVENTS, "inputPreview", "Nullable(String)"),
3666
- addColumn(TABLE_SPAN_EVENTS, "outputPreview", "Nullable(String)"),
3667
- addColumn(TABLE_SPAN_EVENTS, "errorPreview", "Nullable(String)"),
3713
+ ...ADDED_SPAN_COLUMNS.flatMap(([column, type]) => [
3714
+ addColumn(TABLE_TRACE_ROOTS, column, type),
3715
+ addColumn(TABLE_TRACE_BRANCHES, column, type),
3716
+ addColumn(TABLE_SPAN_EVENTS, column, type)
3717
+ ]),
3668
3718
  addColumn(TABLE_TRACE_BRANCHES, "entityVersionId", "Nullable(String)"),
3669
3719
  addColumn(TABLE_TRACE_BRANCHES, "parentEntityVersionId", "Nullable(String)"),
3670
3720
  addColumn(TABLE_TRACE_BRANCHES, "rootEntityVersionId", "Nullable(String)"),
@@ -3705,10 +3755,7 @@ const ALL_MIGRATIONS = [
3705
3755
  addBloomIndex(TABLE_METRIC_EVENTS, "idx_sessionId", "sessionId"),
3706
3756
  addBloomIndex(TABLE_METRIC_EVENTS, "idx_requestId", "requestId"),
3707
3757
  ...LOG_SKIP_INDEX_COLUMNS.map((column) => addBloomIndex(TABLE_LOG_EVENTS, `idx_${column}`, column)),
3708
- addBloomIndex(TABLE_DELETION_REQUESTS, "idx_predicateValues", "predicateValues"),
3709
- addColumn(TABLE_TRACE_ROOTS, "timeToFirstChunkMs", "Nullable(Float64)"),
3710
- addColumn(TABLE_TRACE_BRANCHES, "timeToFirstChunkMs", "Nullable(Float64)"),
3711
- addColumn(TABLE_SPAN_EVENTS, "timeToFirstChunkMs", "Nullable(Float64)")
3758
+ addBloomIndex(TABLE_DELETION_REQUESTS, "idx_predicateValues", "predicateValues")
3712
3759
  ];
3713
3760
  const ALL_TABLE_NAMES = [
3714
3761
  TABLE_SPAN_EVENTS,
@@ -3959,6 +4006,11 @@ const PROMOTED_KEYS = /* @__PURE__ */ new Set([
3959
4006
  "executionSource",
3960
4007
  "serviceName"
3961
4008
  ]);
4009
+ function nullableNumber$1(value) {
4010
+ if (value == null || value === "") return null;
4011
+ const n = Number(value);
4012
+ return Number.isNaN(n) ? null : n;
4013
+ }
3962
4014
  function nullableString(value) {
3963
4015
  return typeof value === "string" ? value === "" ? null : value : value == null ? null : String(value);
3964
4016
  }
@@ -4040,6 +4092,7 @@ function rowToSpanRecord(row) {
4040
4092
  const startedAt = toDate(row.startedAt);
4041
4093
  const endedAt = row.isEvent ? startedAt : toDateOrNull(row.endedAt);
4042
4094
  const error = parseJson(row.error);
4095
+ const attributes = parseJson(row.attributes);
4043
4096
  return {
4044
4097
  traceId: row.traceId,
4045
4098
  spanId: row.spanId,
@@ -4075,12 +4128,21 @@ function rowToSpanRecord(row) {
4075
4128
  tags: normalizeTags(row.tags),
4076
4129
  metadata: parseJson(row.metadataRaw) ?? void 0,
4077
4130
  scope: parseJson(row.scope) ?? void 0,
4078
- attributes: parseJson(row.attributes) ?? void 0,
4131
+ attributes: attributes ?? void 0,
4079
4132
  links: parseJson(row.links) ?? void 0,
4080
4133
  input: parseJson(row.input) ?? void 0,
4081
4134
  output: parseJson(row.output) ?? void 0,
4082
4135
  error: error ?? void 0,
4083
4136
  requestContext: parseJson(row.requestContext) ?? void 0,
4137
+ externalParentSpanId: nullableString(row.externalParentSpanId),
4138
+ resumedFromSpanId: nullableString(row.resumedFromSpanId),
4139
+ model: nullableString(row.model) ?? (0, _mastra_core_storage.getStringAttribute)(attributes, "model"),
4140
+ provider: nullableString(row.provider) ?? (0, _mastra_core_storage.getStringAttribute)(attributes, "provider"),
4141
+ outcome: nullableString(row.outcome),
4142
+ stopReason: nullableString(row.stopReason),
4143
+ stopReasonContext: parseJson(row.stopReasonContext) ?? null,
4144
+ timeToFirstTokenMs: nullableNumber$1(row.timeToFirstTokenMs),
4145
+ errorCount: nullableNumber$1(row.errorCount),
4084
4146
  createdAt: startedAt,
4085
4147
  updatedAt: null
4086
4148
  };
@@ -4154,6 +4216,15 @@ function spanRecordToRow(span) {
4154
4216
  startedAt: toISOString(span.startedAt),
4155
4217
  endedAt: toISOString(endedAt),
4156
4218
  timeToFirstChunkMs: span.timeToFirstChunkMs ?? null,
4219
+ externalParentSpanId: span.externalParentSpanId ?? null,
4220
+ resumedFromSpanId: span.resumedFromSpanId ?? null,
4221
+ model: span.model ?? null,
4222
+ provider: span.provider ?? null,
4223
+ outcome: span.outcome ?? null,
4224
+ stopReason: span.stopReason ?? null,
4225
+ stopReasonContext: jsonEncode(span.stopReasonContext),
4226
+ timeToFirstTokenMs: span.timeToFirstTokenMs ?? null,
4227
+ errorCount: span.errorCount ?? null,
4157
4228
  tags: normalizeTags(span.tags),
4158
4229
  metadataSearch: buildMetadataSearch(metadata),
4159
4230
  metadataRaw: jsonEncode(metadata),
@@ -6857,7 +6928,17 @@ const TRACE_STATUS_SQL = `if(isNotNull(r.error), 'error', 'success')`;
6857
6928
  const timeToFirstChunkSupported = "timeToFirstChunkMs" in (_mastra_core_storage.TRACE_QUERY_FIELD_REGISTRY?.trace ?? {});
6858
6929
  /** Core before the sort rejects `orderBy` on the field, so the sort is only declared when core plans it. */
6859
6930
  const timeToFirstChunkSortSupported = _mastra_core_storage.TRACE_QUERY_ORDER_FIELDS?.includes("timeToFirstChunkMs") === true;
6860
- const timeToFirstChunkFeatures = timeToFirstChunkSupported ? timeToFirstChunkSortSupported ? ["trace-query-time-to-first-chunk", "trace-query-time-to-first-chunk-sort"] : ["trace-query-time-to-first-chunk"] : [];
6931
+ /** Same rule for time to first token: returned and sorted only when core knows the field. */
6932
+ const timeToFirstTokenSupported = "timeToFirstTokenMs" in (_mastra_core_storage.TRACE_QUERY_FIELD_REGISTRY?.trace ?? {});
6933
+ const timeToFirstTokenSortSupported = _mastra_core_storage.TRACE_QUERY_ORDER_FIELDS?.includes("timeToFirstTokenMs") === true;
6934
+ const timingFeatures = [
6935
+ ...timeToFirstChunkSupported ? ["trace-query-time-to-first-chunk"] : [],
6936
+ ...timeToFirstChunkSupported && timeToFirstChunkSortSupported ? ["trace-query-time-to-first-chunk-sort"] : [],
6937
+ ...timeToFirstTokenSupported ? ["trace-query-time-to-first-token"] : [],
6938
+ ...timeToFirstTokenSupported && timeToFirstTokenSortSupported ? ["trace-query-time-to-first-token-sort"] : []
6939
+ ];
6940
+ /** Order fields holding numbers that can be empty; the rest are timestamps. */
6941
+ const NUMERIC_ORDER_FIELDS = /* @__PURE__ */ new Set(["timeToFirstChunkMs", "timeToFirstTokenMs"]);
6861
6942
  function durationMsSql(startedAt, endedAt) {
6862
6943
  return `dateDiff('millisecond', ${startedAt}, ${endedAt})`;
6863
6944
  }
@@ -6910,6 +6991,10 @@ const TRACE_FIELDS = {
6910
6991
  sql: "r.timeToFirstChunkMs",
6911
6992
  parameterType: "Float64"
6912
6993
  },
6994
+ timeToFirstTokenMs: {
6995
+ sql: "r.timeToFirstTokenMs",
6996
+ parameterType: "Float64"
6997
+ },
6913
6998
  entityId: {
6914
6999
  sql: "r.entityId",
6915
7000
  parameterType: "String"
@@ -6976,6 +7061,10 @@ const SPAN_FIELDS = {
6976
7061
  sql: "s.timeToFirstChunkMs",
6977
7062
  parameterType: "Float64"
6978
7063
  },
7064
+ timeToFirstTokenMs: {
7065
+ sql: "s.timeToFirstTokenMs",
7066
+ parameterType: "Float64"
7067
+ },
6979
7068
  status: {
6980
7069
  sql: "s.status",
6981
7070
  parameterType: "String"
@@ -7188,6 +7277,7 @@ const TRACE_SELECT = `
7188
7277
  r.entityType AS entityType,
7189
7278
  r.environment AS environment,
7190
7279
  r.timeToFirstChunkMs AS timeToFirstChunkMs,
7280
+ r.timeToFirstTokenMs AS timeToFirstTokenMs,
7191
7281
  ${TRACE_STATUS_SQL} AS status`;
7192
7282
  var ParameterBuilder = class {
7193
7283
  params = {};
@@ -7204,7 +7294,7 @@ function fieldDefinition(registry, field) {
7204
7294
  return definition;
7205
7295
  }
7206
7296
  function resolveOrderField(field) {
7207
- if (field === "startedAt" || field === "endedAt" || field === "timeToFirstChunkMs") return field;
7297
+ if (field === "startedAt" || field === "endedAt" || NUMERIC_ORDER_FIELDS.has(field)) return field;
7208
7298
  throw new Error(`Unsupported trusted trace-query field: ${field}`);
7209
7299
  }
7210
7300
  function isMetadataField(field) {
@@ -7394,6 +7484,7 @@ function compileClickHouseTraceScope(selection, relationCollections, parameters,
7394
7484
  endedAt,
7395
7485
  ${durationMsSql("startedAt", "endedAt")} AS durationMs,
7396
7486
  timeToFirstChunkMs,
7487
+ timeToFirstTokenMs,
7397
7488
  if(isNotNull(error), 'error', 'success') AS status,
7398
7489
  error,
7399
7490
  entityType,
@@ -7509,7 +7600,7 @@ function traceOrder(plan) {
7509
7600
  function keysetCursorCondition(plan, order, parameters) {
7510
7601
  if (plan.paginationMode !== "keyset" || !plan.cursor) return "";
7511
7602
  if (plan.cursor.sortValue === null) return `(isNull(sortValue) AND traceId > ${parameters.add(plan.cursor.traceId, "String")})`;
7512
- const numeric = order.field === "timeToFirstChunkMs";
7603
+ const numeric = NUMERIC_ORDER_FIELDS.has(order.field);
7513
7604
  const comparison = order.direction === "ASC" ? ">" : "<";
7514
7605
  const sortValue = parameters.add(plan.cursor.sortValue, numeric ? "Float64" : "DateTime64(3, 'UTC')");
7515
7606
  return `(sortValue ${comparison} ${sortValue} OR (sortValue = ${sortValue} AND traceId > ${parameters.add(plan.cursor.traceId, "String")})${numeric ? " OR isNull(sortValue)" : ""})`;
@@ -7523,7 +7614,7 @@ function compileClickHouseTraceQueryKeys(plan) {
7523
7614
  if (plan.paginationMode === "delta") throw new Error("Delta trace queries have no keys statement");
7524
7615
  const parameters = new ParameterBuilder();
7525
7616
  const order = traceOrder(plan);
7526
- const ctes = compileClickHouseTraceCandidates(plan, "r.traceId AS traceId, r.dedupeKey AS dedupeKey, r.startedAt AS startedAt, r.endedAt AS endedAt, r.timeToFirstChunkMs AS timeToFirstChunkMs", parameters, QUERY_TRACES_SCOPE);
7617
+ const ctes = compileClickHouseTraceCandidates(plan, "r.traceId AS traceId, r.dedupeKey AS dedupeKey, r.startedAt AS startedAt, r.endedAt AS endedAt, r.timeToFirstChunkMs AS timeToFirstChunkMs, r.timeToFirstTokenMs AS timeToFirstTokenMs", parameters, QUERY_TRACES_SCOPE);
7527
7618
  const cursor = keysetCursorCondition(plan, order, parameters);
7528
7619
  const limit = plan.paginationMode === "page" ? `LIMIT ${parameters.add(plan.perPage, "UInt64")} OFFSET ${parameters.add(plan.page * plan.perPage, "UInt64")}` : `LIMIT ${parameters.add(plan.limit + 1, "UInt64")}`;
7529
7620
  return {
@@ -7924,7 +8015,8 @@ async function queryTraces(client, plan, timeoutMs, strategy = null) {
7924
8015
  entityType: row.entityType == null ? null : String(row.entityType),
7925
8016
  environment: row.environment == null ? null : String(row.environment),
7926
8017
  status: row.status,
7927
- ...timeToFirstChunkSupported && { timeToFirstChunkMs: row.timeToFirstChunkMs == null ? null : Number(row.timeToFirstChunkMs) }
8018
+ ...timeToFirstChunkSupported && { timeToFirstChunkMs: row.timeToFirstChunkMs == null ? null : Number(row.timeToFirstChunkMs) },
8019
+ ...timeToFirstTokenSupported && { timeToFirstTokenMs: row.timeToFirstTokenMs == null ? null : Number(row.timeToFirstTokenMs) }
7928
8020
  });
7929
8021
  if (plan.paginationMode === "delta") {
7930
8022
  const rows = await run(compileClickHouseTraceQuery(plan, deltaHead));
@@ -7947,7 +8039,7 @@ async function queryTraces(client, plan, timeoutMs, strategy = null) {
7947
8039
  });
7948
8040
  }
7949
8041
  const keys = await run(compileClickHouseTraceQueryKeys(plan));
7950
- const sortValue = (value) => plan.orderBy.field !== "timeToFirstChunkMs" ? asIsoTimestamp(value) : value == null ? null : Number(value);
8042
+ const sortValue = (value) => !NUMERIC_ORDER_FIELDS.has(plan.orderBy.field) ? asIsoTimestamp(value) : value == null ? null : Number(value);
7951
8043
  let total = Number(keys[0]?.__total ?? 0);
7952
8044
  if (plan.paginationMode === "page" && keys.length === 0 && plan.page > 0) {
7953
8045
  const [stats] = await run(compileClickHouseTraceQueryTotal(plan));
@@ -8050,6 +8142,7 @@ const displayFields = [
8050
8142
  "provider",
8051
8143
  "durationMs",
8052
8144
  "timeToFirstChunkMs",
8145
+ "timeToFirstTokenMs",
8053
8146
  "status"
8054
8147
  ];
8055
8148
  var Parameters = class {
@@ -8078,7 +8171,7 @@ function afterCursor$1(plan, parameters) {
8078
8171
  const equal = [];
8079
8172
  if (cursor.sortValue === null) equal.push(`isNull(${field})`);
8080
8173
  else {
8081
- const numeric = plan.orderBy.field === "timeToFirstChunkMs";
8174
+ const numeric = plan.orderBy.field === "timeToFirstChunkMs" || plan.orderBy.field === "timeToFirstTokenMs";
8082
8175
  const value = parameters.add(cursor.sortValue, numeric ? "Float64" : "DateTime64(3, 'UTC')");
8083
8176
  terms.push(`${field} ${plan.orderBy.direction === "asc" ? ">" : "<"} ${value}`);
8084
8177
  if (numeric) terms.push(`isNull(${field})`);
@@ -8174,6 +8267,7 @@ const TRACE_BRANCHES_SPAN_FIELD_COLUMNS = {
8174
8267
  "startedAt",
8175
8268
  "endedAt",
8176
8269
  "timeToFirstChunkMs",
8270
+ "timeToFirstTokenMs",
8177
8271
  "threadId",
8178
8272
  "runId",
8179
8273
  "sessionId",
@@ -8334,12 +8428,13 @@ async function querySpans(client, plan, timeoutMs) {
8334
8428
  const hydrated = page.flatMap((identity) => {
8335
8429
  const row = byIdentity.get(_mastra_core_storage.spanQueryIdentityKey(identity));
8336
8430
  if (!row) return [];
8337
- const { inputPreview: _input, outputPreview: _output, errorPreview: _error, timeToFirstChunkMs, ...metadata } = row;
8431
+ const { inputPreview: _input, outputPreview: _output, errorPreview: _error, timeToFirstChunkMs, timeToFirstTokenMs, ...metadata } = row;
8338
8432
  return [{
8339
8433
  ...metadata,
8340
8434
  ...identity,
8341
8435
  durationMs: Number(row.durationMs),
8342
- ...timeToFirstChunkSupported && { timeToFirstChunkMs: timeToFirstChunkMs == null ? null : Number(timeToFirstChunkMs) }
8436
+ ...timeToFirstChunkSupported && { timeToFirstChunkMs: timeToFirstChunkMs == null ? null : Number(timeToFirstChunkMs) },
8437
+ ...timeToFirstTokenSupported && { timeToFirstTokenMs: timeToFirstTokenMs == null ? null : Number(timeToFirstTokenMs) }
8343
8438
  }];
8344
8439
  });
8345
8440
  let metrics = [];
@@ -8462,7 +8557,11 @@ function compileClickHouseThreadRowsQuery(plan, strategy = "full") {
8462
8557
  const after = afterCursor(plan, parameters);
8463
8558
  const qualify = plan.timeRange.mode === "traces" ? "s.inWindow > 0" : `s.${plan.timeRange.field} >= ${from} AND s.${plan.timeRange.field} < ${to}`;
8464
8559
  const filtered = predicateFields(plan.where);
8465
- const firstContext = [...contextFields, "timeToFirstChunkMs"].filter((field) => field === "resourceId" || filtered.has(field));
8560
+ const firstContext = [
8561
+ ...contextFields,
8562
+ "timeToFirstChunkMs",
8563
+ "timeToFirstTokenMs"
8564
+ ].filter((field) => field === "resourceId" || filtered.has(field));
8466
8565
  const firstFields = [
8467
8566
  "startedAt",
8468
8567
  "traceId",
@@ -8486,7 +8585,7 @@ function compileClickHouseThreadRowsQuery(plan, strategy = "full") {
8486
8585
  ...firstContext,
8487
8586
  ...filtered.has("status") ? ["status"] : []
8488
8587
  ];
8489
- const rootExpression = (field) => field === "timeToFirstChunkMs" ? "if(spanType = 'agent_run', timeToFirstChunkMs, NULL)" : field === "status" ? "if(isNull(error), 'success', 'error')" : field;
8588
+ const rootExpression = (field) => field === "timeToFirstChunkMs" || field === "timeToFirstTokenMs" ? `if(spanType = 'agent_run', ${field}, NULL)` : field === "status" ? "if(isNull(error), 'success', 'error')" : field;
8490
8589
  const sortedRoots = `roots AS (
8491
8590
  SELECT organizationId, traceId, ${rootDataFields.map((field) => `${rootExpression(field)} AS ${field}`).join(", ")}
8492
8591
  FROM ${TABLE_TRACE_ROOTS} ${scope.length || strategy === "candidates" ? `WHERE ${[...scope, ...strategy === "candidates" ? [candidateTracesFilter] : []].join(" AND ")}` : ""}
@@ -8590,6 +8689,7 @@ async function queryThreadRows(client, plan, timeoutMs, strategy = "auto") {
8590
8689
  for (const batch of batches([...roots.values()])) stored.push(...await run({
8591
8690
  query: `SELECT organizationId, traceId, spanId, startedAt, endedAt,
8592
8691
  ${contextFields.join(", ")}, if(spanType = 'agent_run', timeToFirstChunkMs, NULL) AS timeToFirstChunkMs,
8692
+ if(spanType = 'agent_run', timeToFirstTokenMs, NULL) AS timeToFirstTokenMs,
8593
8693
  if(isNull(error), 'success', 'error') AS status, ${PREVIEW_COLUMNS.join(", ")}
8594
8694
  FROM ${TABLE_TRACE_ROOTS} WHERE ${rootWhere}
8595
8695
  ORDER BY ${ROOT_VERSION_ORDER} LIMIT 1 BY organizationId, traceId, spanId`,
@@ -8623,6 +8723,7 @@ async function queryThreadRows(client, plan, timeoutMs, strategy = "auto") {
8623
8723
  endedAt: iso(row.endedAt),
8624
8724
  traceCount: Number(row.traceCount),
8625
8725
  timeToFirstChunkMs: first.timeToFirstChunkMs,
8726
+ ...timeToFirstTokenSupported && { timeToFirstTokenMs: first.timeToFirstTokenMs },
8626
8727
  inputPreview: resolveStoredPreviews(first).inputPreview,
8627
8728
  outputPreview: resolveStoredPreviews(last).outputPreview,
8628
8729
  errorPreview: resolveStoredPreviews(last).errorPreview
@@ -10279,7 +10380,7 @@ var ObservabilityStorageClickhouseVNext = class extends _mastra_core_storage.Obs
10279
10380
  "trace-query-tenant-scope",
10280
10381
  "feedback",
10281
10382
  "trace-query-context-ids",
10282
- ...timeToFirstChunkFeatures,
10383
+ ...timingFeatures,
10283
10384
  "trace-query-root-span-fields",
10284
10385
  "trace-query-root-entity-id",
10285
10386
  "trace-aggregate",
@@ -10304,7 +10405,7 @@ var ObservabilityStorageClickhouseVNext = class extends _mastra_core_storage.Obs
10304
10405
  "trace-query-tenant-scope",
10305
10406
  "feedback",
10306
10407
  "trace-query-context-ids",
10307
- ...timeToFirstChunkFeatures,
10408
+ ...timingFeatures,
10308
10409
  "trace-query-root-span-fields",
10309
10410
  "trace-query-root-entity-id",
10310
10411
  "trace-aggregate",