@dudousxd/nestjs-agent-store-drizzle 0.31.0 → 0.33.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.
package/dist/index.cjs CHANGED
@@ -94,7 +94,9 @@ var agentThread = (0, import_sqlite_core.sqliteTable)("agent_thread", {
94
94
  mode: "timestamp_ms"
95
95
  })
96
96
  }, (table) => [
97
- (0, import_sqlite_core.index)("agent_thread_actor_updated_idx").on(table.actorRef, table.updatedAt)
97
+ (0, import_sqlite_core.index)("agent_thread_actor_updated_idx").on(table.actorRef, table.updatedAt),
98
+ // `ownerOfActiveStream` reads by run id on every stream attach and reconnect.
99
+ (0, import_sqlite_core.index)("agent_thread_active_stream_idx").on(table.activeStreamId)
98
100
  ]);
99
101
  var agentMessage = (0, import_sqlite_core.sqliteTable)("agent_message", {
100
102
  id: (0, import_sqlite_core.text)("id").primaryKey(),
@@ -144,7 +146,9 @@ var agentMessage = (0, import_sqlite_core.sqliteTable)("agent_message", {
144
146
  mode: "timestamp_ms"
145
147
  }).notNull()
146
148
  }, (table) => [
147
- (0, import_sqlite_core.index)("agent_message_thread_created_idx").on(table.threadId, table.createdAt)
149
+ (0, import_sqlite_core.index)("agent_message_thread_created_idx").on(table.threadId, table.createdAt),
150
+ // Transcript reads order a thread's messages by `seq`.
151
+ (0, import_sqlite_core.index)("agent_message_thread_seq_idx").on(table.threadId, table.seq)
148
152
  ]);
149
153
  var agentToolCall = (0, import_sqlite_core.sqliteTable)("agent_tool_call", {
150
154
  proposalId: (0, import_sqlite_core.text)("proposal_id"),
@@ -189,7 +193,11 @@ var agentToolCall = (0, import_sqlite_core.sqliteTable)("agent_tool_call", {
189
193
  /** The surface the decision came through (`web`, `slack`, `remembered`, …). */
190
194
  decidedVia: (0, import_sqlite_core.text)("decided_via")
191
195
  }, (table) => [
192
- (0, import_sqlite_core.index)("agent_tool_call_message_idx").on(table.messageId)
196
+ (0, import_sqlite_core.index)("agent_tool_call_message_idx").on(table.messageId),
197
+ // The approvals inbox: `pending_approval`, oldest first.
198
+ (0, import_sqlite_core.index)("agent_tool_call_status_created_idx").on(table.status, table.createdAt),
199
+ (0, import_sqlite_core.index)("agent_tool_call_run_idx").on(table.runId),
200
+ (0, import_sqlite_core.index)("agent_tool_call_created_idx").on(table.createdAt)
193
201
  ]);
194
202
  var agentQueuedMessage = (0, import_sqlite_core.sqliteTable)("agent_queued_message", {
195
203
  uiCapabilities: (0, import_sqlite_core.text)("ui_capabilities", {
@@ -253,7 +261,9 @@ var agentTokenUsage = (0, import_sqlite_core.sqliteTable)("agent_token_usage", {
253
261
  mode: "timestamp_ms"
254
262
  }).notNull()
255
263
  }, (table) => [
256
- (0, import_sqlite_core.index)("agent_token_usage_actor_created_idx").on(table.actorRef, table.createdAt)
264
+ (0, import_sqlite_core.index)("agent_token_usage_actor_created_idx").on(table.actorRef, table.createdAt),
265
+ (0, import_sqlite_core.index)("agent_token_usage_created_idx").on(table.createdAt),
266
+ (0, import_sqlite_core.index)("agent_token_usage_thread_idx").on(table.threadId)
257
267
  ]);
258
268
  var agentGenuiCall = (0, import_sqlite_core.sqliteTable)("agent_genui_call", {
259
269
  id: (0, import_sqlite_core.text)("id").primaryKey(),
@@ -311,6 +321,12 @@ var agentRun = (0, import_sqlite_core.sqliteTable)("agent_run", {
311
321
  agentName: (0, import_sqlite_core.text)("agent_name"),
312
322
  status: (0, import_sqlite_core.text)("status").$type().notNull(),
313
323
  durationMs: (0, import_sqlite_core.integer)("duration_ms"),
324
+ /** Turn start to the first `text` frame; null for a run that streamed none or did not complete. */
325
+ ttftMs: (0, import_sqlite_core.integer)("ttft_ms"),
326
+ /** The run's known spend (the sum of its model calls with a known cost); null when none had one. */
327
+ costUsd: (0, import_sqlite_core.real)("cost_usd"),
328
+ /** The model the run's last model call resolved to. */
329
+ modelId: (0, import_sqlite_core.text)("model_id"),
314
330
  errorCode: (0, import_sqlite_core.text)("error_code"),
315
331
  errorMessage: (0, import_sqlite_core.text)("error_message"),
316
332
  retries: (0, import_sqlite_core.integer)("retries").notNull().default(0),
@@ -590,7 +606,9 @@ var agentThread2 = (0, import_mysql_core.mysqlTable)("agent_thread", {
590
606
  updatedAt: ts("updated_at").notNull(),
591
607
  deletedAt: ts("deleted_at")
592
608
  }, (table) => [
593
- (0, import_mysql_core.index)("agent_thread_actor_updated_idx").on(table.actorRef, table.updatedAt)
609
+ (0, import_mysql_core.index)("agent_thread_actor_updated_idx").on(table.actorRef, table.updatedAt),
610
+ // `ownerOfActiveStream` reads by run id on every stream attach and reconnect.
611
+ (0, import_mysql_core.index)("agent_thread_active_stream_idx").on(table.activeStreamId)
594
612
  ]);
595
613
  var agentMessage2 = (0, import_mysql_core.mysqlTable)("agent_message", {
596
614
  id: key("id").primaryKey(),
@@ -616,7 +634,9 @@ var agentMessage2 = (0, import_mysql_core.mysqlTable)("agent_message", {
616
634
  seq: (0, import_mysql_core.int)("seq"),
617
635
  createdAt: ts("created_at").notNull()
618
636
  }, (table) => [
619
- (0, import_mysql_core.index)("agent_message_thread_created_idx").on(table.threadId, table.createdAt)
637
+ (0, import_mysql_core.index)("agent_message_thread_created_idx").on(table.threadId, table.createdAt),
638
+ // Transcript reads order a thread's messages by `seq`.
639
+ (0, import_mysql_core.index)("agent_message_thread_seq_idx").on(table.threadId, table.seq)
620
640
  ]);
621
641
  var agentToolCall2 = (0, import_mysql_core.mysqlTable)("agent_tool_call", {
622
642
  proposalId: key("proposal_id"),
@@ -641,7 +661,11 @@ var agentToolCall2 = (0, import_mysql_core.mysqlTable)("agent_tool_call", {
641
661
  remember: (0, import_mysql_core.boolean)("remember"),
642
662
  decidedVia: key("decided_via")
643
663
  }, (table) => [
644
- (0, import_mysql_core.index)("agent_tool_call_message_idx").on(table.messageId)
664
+ (0, import_mysql_core.index)("agent_tool_call_message_idx").on(table.messageId),
665
+ // The approvals inbox: `pending_approval`, oldest first.
666
+ (0, import_mysql_core.index)("agent_tool_call_status_created_idx").on(table.status, table.createdAt),
667
+ (0, import_mysql_core.index)("agent_tool_call_run_idx").on(table.runId),
668
+ (0, import_mysql_core.index)("agent_tool_call_created_idx").on(table.createdAt)
645
669
  ]);
646
670
  var agentQueuedMessage2 = (0, import_mysql_core.mysqlTable)("agent_queued_message", {
647
671
  uiCapabilities: (0, import_mysql_core.json)("ui_capabilities").$type(),
@@ -683,7 +707,9 @@ var agentTokenUsage2 = (0, import_mysql_core.mysqlTable)("agent_token_usage", {
683
707
  costSource: key("cost_source").$type(),
684
708
  createdAt: ts("created_at").notNull()
685
709
  }, (table) => [
686
- (0, import_mysql_core.index)("agent_token_usage_actor_created_idx").on(table.actorRef, table.createdAt)
710
+ (0, import_mysql_core.index)("agent_token_usage_actor_created_idx").on(table.actorRef, table.createdAt),
711
+ (0, import_mysql_core.index)("agent_token_usage_created_idx").on(table.createdAt),
712
+ (0, import_mysql_core.index)("agent_token_usage_thread_idx").on(table.threadId)
687
713
  ]);
688
714
  var agentGenuiCall2 = (0, import_mysql_core.mysqlTable)("agent_genui_call", {
689
715
  id: key("id").primaryKey(),
@@ -743,6 +769,12 @@ var agentRun2 = (0, import_mysql_core.mysqlTable)("agent_run", {
743
769
  agentName: key("agent_name"),
744
770
  status: key("status").$type().notNull(),
745
771
  durationMs: (0, import_mysql_core.int)("duration_ms"),
772
+ /** Turn start to the first `text` frame; null for a run that streamed none or did not complete. */
773
+ ttftMs: (0, import_mysql_core.int)("ttft_ms"),
774
+ /** The run's known spend (the sum of its model calls with a known cost); null when none had one. */
775
+ costUsd: (0, import_mysql_core.double)("cost_usd"),
776
+ /** The model the run's last model call resolved to. */
777
+ modelId: key("model_id"),
746
778
  errorCode: key("error_code"),
747
779
  errorMessage: (0, import_mysql_core.longtext)("error_message"),
748
780
  retries: (0, import_mysql_core.int)("retries").notNull().default(0),
@@ -1036,7 +1068,9 @@ var agentThread3 = (0, import_pg_core.pgTable)("agent_thread", {
1036
1068
  updatedAt: ts2("updated_at").notNull(),
1037
1069
  deletedAt: ts2("deleted_at")
1038
1070
  }, (table) => [
1039
- (0, import_pg_core.index)("agent_thread_actor_updated_idx").on(table.actorRef, table.updatedAt)
1071
+ (0, import_pg_core.index)("agent_thread_actor_updated_idx").on(table.actorRef, table.updatedAt),
1072
+ // `ownerOfActiveStream` reads by run id on every stream attach and reconnect.
1073
+ (0, import_pg_core.index)("agent_thread_active_stream_idx").on(table.activeStreamId)
1040
1074
  ]);
1041
1075
  var agentMessage3 = (0, import_pg_core.pgTable)("agent_message", {
1042
1076
  id: (0, import_pg_core.text)("id").primaryKey(),
@@ -1062,7 +1096,9 @@ var agentMessage3 = (0, import_pg_core.pgTable)("agent_message", {
1062
1096
  seq: (0, import_pg_core.integer)("seq"),
1063
1097
  createdAt: ts2("created_at").notNull()
1064
1098
  }, (table) => [
1065
- (0, import_pg_core.index)("agent_message_thread_created_idx").on(table.threadId, table.createdAt)
1099
+ (0, import_pg_core.index)("agent_message_thread_created_idx").on(table.threadId, table.createdAt),
1100
+ // Transcript reads order a thread's messages by `seq`.
1101
+ (0, import_pg_core.index)("agent_message_thread_seq_idx").on(table.threadId, table.seq)
1066
1102
  ]);
1067
1103
  var agentToolCall3 = (0, import_pg_core.pgTable)("agent_tool_call", {
1068
1104
  proposalId: (0, import_pg_core.text)("proposal_id"),
@@ -1087,7 +1123,11 @@ var agentToolCall3 = (0, import_pg_core.pgTable)("agent_tool_call", {
1087
1123
  remember: (0, import_pg_core.boolean)("remember"),
1088
1124
  decidedVia: (0, import_pg_core.text)("decided_via")
1089
1125
  }, (table) => [
1090
- (0, import_pg_core.index)("agent_tool_call_message_idx").on(table.messageId)
1126
+ (0, import_pg_core.index)("agent_tool_call_message_idx").on(table.messageId),
1127
+ // The approvals inbox: `pending_approval`, oldest first.
1128
+ (0, import_pg_core.index)("agent_tool_call_status_created_idx").on(table.status, table.createdAt),
1129
+ (0, import_pg_core.index)("agent_tool_call_run_idx").on(table.runId),
1130
+ (0, import_pg_core.index)("agent_tool_call_created_idx").on(table.createdAt)
1091
1131
  ]);
1092
1132
  var agentQueuedMessage3 = (0, import_pg_core.pgTable)("agent_queued_message", {
1093
1133
  uiCapabilities: (0, import_pg_core.jsonb)("ui_capabilities").$type(),
@@ -1129,7 +1169,9 @@ var agentTokenUsage3 = (0, import_pg_core.pgTable)("agent_token_usage", {
1129
1169
  costSource: (0, import_pg_core.text)("cost_source").$type(),
1130
1170
  createdAt: ts2("created_at").notNull()
1131
1171
  }, (table) => [
1132
- (0, import_pg_core.index)("agent_token_usage_actor_created_idx").on(table.actorRef, table.createdAt)
1172
+ (0, import_pg_core.index)("agent_token_usage_actor_created_idx").on(table.actorRef, table.createdAt),
1173
+ (0, import_pg_core.index)("agent_token_usage_created_idx").on(table.createdAt),
1174
+ (0, import_pg_core.index)("agent_token_usage_thread_idx").on(table.threadId)
1133
1175
  ]);
1134
1176
  var agentGenuiCall3 = (0, import_pg_core.pgTable)("agent_genui_call", {
1135
1177
  id: (0, import_pg_core.text)("id").primaryKey(),
@@ -1189,6 +1231,12 @@ var agentRun3 = (0, import_pg_core.pgTable)("agent_run", {
1189
1231
  agentName: (0, import_pg_core.text)("agent_name"),
1190
1232
  status: (0, import_pg_core.text)("status").$type().notNull(),
1191
1233
  durationMs: (0, import_pg_core.integer)("duration_ms"),
1234
+ /** Turn start to the first `text` frame; null for a run that streamed none or did not complete. */
1235
+ ttftMs: (0, import_pg_core.integer)("ttft_ms"),
1236
+ /** The run's known spend (the sum of its model calls with a known cost); null when none had one. */
1237
+ costUsd: (0, import_pg_core.doublePrecision)("cost_usd"),
1238
+ /** The model the run's last model call resolved to. */
1239
+ modelId: (0, import_pg_core.text)("model_id"),
1192
1240
  errorCode: (0, import_pg_core.text)("error_code"),
1193
1241
  errorMessage: (0, import_pg_core.text)("error_message"),
1194
1242
  retries: (0, import_pg_core.integer)("retries").notNull().default(0),
@@ -2402,12 +2450,49 @@ var DrizzleAgentStore = class {
2402
2450
  }
2403
2451
  async listThreads(actorRef, limit = 50) {
2404
2452
  const threads = await this.db.select().from(this.t.agentThread).where((0, import_drizzle_orm4.and)((0, import_drizzle_orm4.eq)(this.t.agentThread.actorRef, actorRef), (0, import_drizzle_orm4.eq)(this.t.agentThread.transient, false), (0, import_drizzle_orm4.isNull)(this.t.agentThread.deletedAt))).orderBy((0, import_drizzle_orm4.desc)(this.t.agentThread.updatedAt)).limit(limit);
2405
- const summaries = [];
2406
- for (const thread of threads) {
2407
- const [last] = await this.db.select().from(this.t.agentMessage).where((0, import_drizzle_orm4.eq)(this.t.agentMessage.threadId, thread.id)).orderBy(...this.messageOrderNewestFirst()).limit(1);
2408
- summaries.push(this.toSummary(thread, last?.content));
2453
+ const previews = await this.lastMessageContents(threads.map((thread) => thread.id));
2454
+ return threads.map((thread) => this.toSummary(thread, previews.get(thread.id)));
2455
+ }
2456
+ /**
2457
+ * The content of each thread's newest message ({@link messageOrderNewestFirst}), in a fixed two
2458
+ * reads for any number of threads: each thread's highest `seq`, then the rows holding it. Only a
2459
+ * thread whose every message predates `seq` is read on its own, by `created_at`.
2460
+ */
2461
+ async lastMessageContents(threadIds) {
2462
+ const contents = /* @__PURE__ */ new Map();
2463
+ if (threadIds.length === 0) {
2464
+ return contents;
2465
+ }
2466
+ const message = this.t.agentMessage;
2467
+ const highest = await this.db.select({
2468
+ threadId: message.threadId,
2469
+ seq: (0, import_drizzle_orm4.max)(message.seq)
2470
+ }).from(message).where((0, import_drizzle_orm4.inArray)(message.threadId, [
2471
+ ...threadIds
2472
+ ])).groupBy(message.threadId);
2473
+ const numbered = highest.filter((row) => row.seq !== null);
2474
+ if (numbered.length > 0) {
2475
+ const rows = await this.db.select({
2476
+ threadId: message.threadId,
2477
+ content: message.content
2478
+ }).from(message).where((0, import_drizzle_orm4.or)(...numbered.map((row) => (0, import_drizzle_orm4.and)((0, import_drizzle_orm4.eq)(message.threadId, row.threadId), (0, import_drizzle_orm4.eq)(message.seq, Number(row.seq)))))).orderBy(...this.messageOrderNewestFirst());
2479
+ for (const row of rows) {
2480
+ if (!contents.has(row.threadId)) {
2481
+ contents.set(row.threadId, row.content);
2482
+ }
2483
+ }
2409
2484
  }
2410
- return summaries;
2485
+ for (const row of highest) {
2486
+ if (row.seq === null) {
2487
+ const [last] = await this.db.select({
2488
+ content: message.content
2489
+ }).from(message).where((0, import_drizzle_orm4.eq)(message.threadId, row.threadId)).orderBy(...this.messageOrderNewestFirst()).limit(1);
2490
+ if (last !== void 0) {
2491
+ contents.set(row.threadId, last.content);
2492
+ }
2493
+ }
2494
+ }
2495
+ return contents;
2411
2496
  }
2412
2497
  async softDeleteThread(threadId) {
2413
2498
  await this.db.update(this.t.agentThread).set({
@@ -2784,6 +2869,24 @@ var DrizzleAgentStore = class {
2784
2869
  }).from(agentThread).where((0, import_drizzle_orm4.and)((0, import_drizzle_orm4.eq)(this.t.agentThread.id, threadId), (0, import_drizzle_orm4.isNull)(this.t.agentThread.deletedAt)));
2785
2870
  return thread?.persona ?? null;
2786
2871
  }
2872
+ async threadSettings(threadId) {
2873
+ const thread = this.t.agentThread;
2874
+ const [row] = await this.db.select({
2875
+ actorRef: thread.actorRef,
2876
+ defaultAgent: thread.defaultAgent,
2877
+ persona: thread.persona,
2878
+ model: thread.model
2879
+ }).from(thread).where((0, import_drizzle_orm4.and)((0, import_drizzle_orm4.eq)(thread.id, threadId), (0, import_drizzle_orm4.isNull)(thread.deletedAt)));
2880
+ if (row === void 0) {
2881
+ return null;
2882
+ }
2883
+ return {
2884
+ actorRef: row.actorRef,
2885
+ defaultAgent: row.defaultAgent ?? null,
2886
+ persona: row.persona ?? null,
2887
+ model: row.model ?? null
2888
+ };
2889
+ }
2787
2890
  async defaultAgentForThread(threadId) {
2788
2891
  const [thread] = await this.db.select({
2789
2892
  defaultAgent: this.t.agentThread.defaultAgent
@@ -2804,6 +2907,9 @@ var DrizzleAgentStore = class {
2804
2907
  agentName: run.agentName ?? null,
2805
2908
  status: "running",
2806
2909
  durationMs: null,
2910
+ ttftMs: null,
2911
+ costUsd: null,
2912
+ modelId: null,
2807
2913
  errorCode: null,
2808
2914
  errorMessage: null,
2809
2915
  retries: 0,
@@ -2823,6 +2929,15 @@ var DrizzleAgentStore = class {
2823
2929
  if (end.durationMs !== void 0) {
2824
2930
  updates.durationMs = end.durationMs;
2825
2931
  }
2932
+ if (end.ttftMs !== void 0) {
2933
+ updates.ttftMs = end.ttftMs;
2934
+ }
2935
+ if (end.costUsd !== void 0) {
2936
+ updates.costUsd = end.costUsd;
2937
+ }
2938
+ if (end.modelId !== void 0) {
2939
+ updates.modelId = end.modelId;
2940
+ }
2826
2941
  if (end.errorCode !== void 0) {
2827
2942
  updates.errorCode = end.errorCode;
2828
2943
  }
@@ -3087,10 +3202,43 @@ var DrizzleAgentStore = class {
3087
3202
  return this.usageBetween(actorRef, day, day);
3088
3203
  }
3089
3204
  async usageBetween(actorRef, fromDay, toDay) {
3090
- const start = /* @__PURE__ */ new Date(`${fromDay}T00:00:00.000Z`);
3091
- const end = /* @__PURE__ */ new Date(`${toDay}T23:59:59.999Z`);
3092
- const rows = await this.db.select().from(this.t.agentTokenUsage).where((0, import_drizzle_orm4.and)((0, import_drizzle_orm4.eq)(this.t.agentTokenUsage.actorRef, actorRef), (0, import_drizzle_orm4.gte)(this.t.agentTokenUsage.createdAt, start), (0, import_drizzle_orm4.lte)(this.t.agentTokenUsage.createdAt, end)));
3093
- return (0, import_nestjs_agent_core4.sumUsage)(rows);
3205
+ const [totals] = await this.usageWindows(actorRef, [
3206
+ {
3207
+ fromDay,
3208
+ toDay
3209
+ }
3210
+ ]);
3211
+ return totals ?? (0, import_nestjs_agent_core4.sumUsage)([]);
3212
+ }
3213
+ /**
3214
+ * Every range summed by the database in one statement, each as a conditional aggregate over the
3215
+ * rows of the smallest span covering them all — the actor's `(actor_ref, created_at)` index serves
3216
+ * it, and no usage row crosses the wire.
3217
+ */
3218
+ async usageWindows(actorRef, ranges) {
3219
+ if (ranges.length === 0) {
3220
+ return [];
3221
+ }
3222
+ const usage = this.t.agentTokenUsage;
3223
+ const bounds = ranges.map(({ fromDay, toDay }) => ({
3224
+ start: /* @__PURE__ */ new Date(`${fromDay}T00:00:00.000Z`),
3225
+ end: /* @__PURE__ */ new Date(`${toDay}T23:59:59.999Z`)
3226
+ }));
3227
+ const within = /* @__PURE__ */ __name((start, end) => (0, import_drizzle_orm4.and)((0, import_drizzle_orm4.gte)(usage.createdAt, start), (0, import_drizzle_orm4.lte)(usage.createdAt, end)), "within");
3228
+ const columns = {};
3229
+ bounds.forEach(({ start, end }, index4) => {
3230
+ columns[`tokens${index4}`] = import_drizzle_orm4.sql`sum(case when ${within(start, end)} then ${usage.inputTokens} + ${usage.outputTokens} else 0 end)`;
3231
+ columns[`cost${index4}`] = import_drizzle_orm4.sql`sum(case when ${within(start, end)} then ${usage.costUsd} else 0 end)`;
3232
+ columns[`estimated${index4}`] = import_drizzle_orm4.sql`sum(case when ${within(start, end)} and ${usage.costSource} = 'estimate' then ${usage.costUsd} else 0 end)`;
3233
+ });
3234
+ const from = new Date(Math.min(...bounds.map(({ start }) => start.getTime())));
3235
+ const to = new Date(Math.max(...bounds.map(({ end }) => end.getTime())));
3236
+ const [row] = await this.db.select(columns).from(usage).where((0, import_drizzle_orm4.and)((0, import_drizzle_orm4.eq)(usage.actorRef, actorRef), within(from, to)));
3237
+ return bounds.map((_, index4) => ({
3238
+ usedTokens: Number(row?.[`tokens${index4}`] ?? 0),
3239
+ costUsd: Number(row?.[`cost${index4}`] ?? 0),
3240
+ estimatedCostUsd: Number(row?.[`estimated${index4}`] ?? 0)
3241
+ }));
3094
3242
  }
3095
3243
  toSummary(thread, lastContent) {
3096
3244
  return {
@@ -3308,6 +3456,11 @@ function toRecentRunRow(run) {
3308
3456
  agentName: run.agentName ?? null,
3309
3457
  status: run.status,
3310
3458
  durationMs: run.durationMs ?? null,
3459
+ ttftMs: run.ttftMs ?? null,
3460
+ // MySQL's DOUBLE and Postgres' double precision come back as numbers; a driver that hands a
3461
+ // decimal back as a string is normalized here so the SPI row is always a number.
3462
+ costUsd: run.costUsd == null ? null : Number(run.costUsd),
3463
+ modelId: run.modelId ?? null,
3311
3464
  errorCode: run.errorCode ?? null,
3312
3465
  errorMessage: run.errorMessage ?? null,
3313
3466
  retries: run.retries,
@@ -3381,7 +3534,18 @@ var DrizzleGovernanceQueries = class {
3381
3534
  }
3382
3535
  async usageInRange(range) {
3383
3536
  const { start, end } = (0, import_nestjs_agent_core5.dayBoundsUtc)(range);
3384
- const rows = await this.db.select().from(this.t.agentTokenUsage).where((0, import_drizzle_orm5.and)((0, import_drizzle_orm5.gte)(this.t.agentTokenUsage.createdAt, start), (0, import_drizzle_orm5.lte)(this.t.agentTokenUsage.createdAt, end)));
3537
+ const usage = this.t.agentTokenUsage;
3538
+ const rows = await this.db.select({
3539
+ modelId: usage.modelId,
3540
+ actorRef: usage.actorRef,
3541
+ threadId: usage.threadId,
3542
+ createdAt: usage.createdAt,
3543
+ inputTokens: usage.inputTokens,
3544
+ outputTokens: usage.outputTokens,
3545
+ cacheWriteTokens: usage.cacheWriteTokens,
3546
+ cacheReadTokens: usage.cacheReadTokens,
3547
+ costUsd: usage.costUsd
3548
+ }).from(usage).where((0, import_drizzle_orm5.and)((0, import_drizzle_orm5.gte)(this.t.agentTokenUsage.createdAt, start), (0, import_drizzle_orm5.lte)(this.t.agentTokenUsage.createdAt, end)));
3385
3549
  return rows.map(toUsageInput);
3386
3550
  }
3387
3551
  async spendByModel(range) {
@@ -3406,7 +3570,11 @@ var DrizzleGovernanceQueries = class {
3406
3570
  if (threadIds.length === 0) {
3407
3571
  return [];
3408
3572
  }
3409
- const threads = await this.db.select().from(this.t.agentThread).where((0, import_drizzle_orm5.and)((0, import_drizzle_orm5.inArray)(this.t.agentThread.id, threadIds), (0, import_drizzle_orm5.isNull)(this.t.agentThread.deletedAt)));
3573
+ const threads = await this.db.select({
3574
+ id: this.t.agentThread.id,
3575
+ title: this.t.agentThread.title,
3576
+ actorRef: this.t.agentThread.actorRef
3577
+ }).from(this.t.agentThread).where((0, import_drizzle_orm5.and)((0, import_drizzle_orm5.inArray)(this.t.agentThread.id, threadIds), (0, import_drizzle_orm5.isNull)(this.t.agentThread.deletedAt)));
3410
3578
  const threadsById = new Map(threads.map((thread) => [
3411
3579
  thread.id,
3412
3580
  {
@@ -3501,9 +3669,19 @@ var DrizzleGovernanceQueries = class {
3501
3669
  const threads = await this.db.select().from(this.t.agentThread).where((0, import_drizzle_orm5.isNull)(this.t.agentThread.deletedAt)).orderBy((0, import_drizzle_orm5.desc)(this.t.agentThread.updatedAt), (0, import_drizzle_orm5.desc)(this.t.agentThread.id)).limit(limit);
3502
3670
  return this.toThreadActivityRows(threads);
3503
3671
  }
3672
+ /** The range's runs, with only the columns the rollups read. */
3504
3673
  async runsInRange(range) {
3505
3674
  const { start, end } = (0, import_nestjs_agent_core5.dayBoundsUtc)(range);
3506
- return this.db.select().from(this.t.agentRun).where((0, import_drizzle_orm5.and)((0, import_drizzle_orm5.gte)(this.t.agentRun.startedAt, start), (0, import_drizzle_orm5.lte)(this.t.agentRun.startedAt, end)));
3675
+ const run = this.t.agentRun;
3676
+ return this.db.select({
3677
+ agentName: run.agentName,
3678
+ status: run.status,
3679
+ retries: run.retries,
3680
+ durationMs: run.durationMs,
3681
+ ttftMs: run.ttftMs,
3682
+ parentRunId: run.parentRunId,
3683
+ startedAt: run.startedAt
3684
+ }).from(run).where((0, import_drizzle_orm5.and)((0, import_drizzle_orm5.gte)(run.startedAt, start), (0, import_drizzle_orm5.lte)(run.startedAt, end)));
3507
3685
  }
3508
3686
  async runMetrics(range) {
3509
3687
  const runs = await this.runsInRange(range);
@@ -3511,6 +3689,7 @@ var DrizzleGovernanceQueries = class {
3511
3689
  let failed = 0;
3512
3690
  let retries = 0;
3513
3691
  const settledDurationsMs = [];
3692
+ const ttftsMs = [];
3514
3693
  for (const run of runs) {
3515
3694
  if (run.status === "completed") {
3516
3695
  completed += 1;
@@ -3521,8 +3700,12 @@ var DrizzleGovernanceQueries = class {
3521
3700
  if ((run.status === "completed" || run.status === "failed") && run.durationMs != null) {
3522
3701
  settledDurationsMs.push(run.durationMs);
3523
3702
  }
3703
+ if (run.status === "completed" && run.parentRunId == null && run.ttftMs != null) {
3704
+ ttftsMs.push(run.ttftMs);
3705
+ }
3524
3706
  }
3525
3707
  settledDurationsMs.sort((left, right) => left - right);
3708
+ ttftsMs.sort((left, right) => left - right);
3526
3709
  return {
3527
3710
  runs: runs.length,
3528
3711
  completed,
@@ -3530,7 +3713,9 @@ var DrizzleGovernanceQueries = class {
3530
3713
  successRate: runs.length === 0 ? 0 : completed / runs.length,
3531
3714
  retries,
3532
3715
  durationP50Ms: percentileMs(settledDurationsMs, 0.5),
3533
- durationP95Ms: percentileMs(settledDurationsMs, 0.95)
3716
+ durationP95Ms: percentileMs(settledDurationsMs, 0.95),
3717
+ ttftP50Ms: percentileMs(ttftsMs, 0.5),
3718
+ ttftP95Ms: percentileMs(ttftsMs, 0.95)
3534
3719
  };
3535
3720
  }
3536
3721
  async runsByAgent(range) {
@@ -3560,7 +3745,9 @@ var DrizzleGovernanceQueries = class {
3560
3745
  }
3561
3746
  async runErrors(range) {
3562
3747
  const { start, end } = (0, import_nestjs_agent_core5.dayBoundsUtc)(range);
3563
- const runs = await this.db.select().from(this.t.agentRun).where((0, import_drizzle_orm5.and)((0, import_drizzle_orm5.gte)(this.t.agentRun.startedAt, start), (0, import_drizzle_orm5.lte)(this.t.agentRun.startedAt, end), (0, import_drizzle_orm5.eq)(this.t.agentRun.status, "failed")));
3748
+ const runs = await this.db.select({
3749
+ errorCode: this.t.agentRun.errorCode
3750
+ }).from(this.t.agentRun).where((0, import_drizzle_orm5.and)((0, import_drizzle_orm5.gte)(this.t.agentRun.startedAt, start), (0, import_drizzle_orm5.lte)(this.t.agentRun.startedAt, end), (0, import_drizzle_orm5.eq)(this.t.agentRun.status, "failed")));
3564
3751
  const byError = /* @__PURE__ */ new Map();
3565
3752
  for (const run of runs) {
3566
3753
  const errorCode = run.errorCode ?? UNCLASSIFIED_ERROR_CODE;
@@ -3641,7 +3828,12 @@ var DrizzleGovernanceQueries = class {
3641
3828
  */
3642
3829
  async toolStats(range) {
3643
3830
  const { start, end } = (0, import_nestjs_agent_core5.dayBoundsUtc)(range);
3644
- const calls = await this.db.select().from(this.t.agentToolCall).where((0, import_drizzle_orm5.and)((0, import_drizzle_orm5.gte)(this.t.agentToolCall.createdAt, start), (0, import_drizzle_orm5.lte)(this.t.agentToolCall.createdAt, end)));
3831
+ const calls = await this.db.select({
3832
+ toolName: this.t.agentToolCall.toolName,
3833
+ toolType: this.t.agentToolCall.toolType,
3834
+ status: this.t.agentToolCall.status,
3835
+ executionMs: this.t.agentToolCall.executionMs
3836
+ }).from(this.t.agentToolCall).where((0, import_drizzle_orm5.and)((0, import_drizzle_orm5.gte)(this.t.agentToolCall.createdAt, start), (0, import_drizzle_orm5.lte)(this.t.agentToolCall.createdAt, end)));
3645
3837
  const byTool = /* @__PURE__ */ new Map();
3646
3838
  for (const call of calls) {
3647
3839
  const key2 = `${call.toolName} ${call.toolType}`;