@dudousxd/nestjs-agent-store-drizzle 0.11.1 → 0.12.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/README.md CHANGED
@@ -119,6 +119,10 @@ ALTER TABLE agent_run ADD COLUMN parent_run_id TEXT;
119
119
  ALTER TABLE agent_message ADD COLUMN reasoning TEXT;
120
120
  ALTER TABLE agent_message ADD COLUMN reasoning_ms INTEGER;
121
121
  ALTER TABLE agent_message ADD COLUMN ui TEXT;
122
+ ALTER TABLE agent_tool_call ADD COLUMN approver TEXT;
123
+ ALTER TABLE agent_tool_call ADD COLUMN expires_at INTEGER;
124
+ ALTER TABLE agent_tool_call ADD COLUMN remember INTEGER;
125
+ ALTER TABLE agent_tool_call ADD COLUMN decided_via TEXT;
122
126
  CREATE INDEX agent_tool_call_message_idx ON agent_tool_call (message_id);
123
127
  ```
124
128
 
package/dist/index.cjs CHANGED
@@ -127,7 +127,19 @@ var agentToolCall = (0, import_sqlite_core.sqliteTable)("agent_tool_call", {
127
127
  mode: "timestamp_ms"
128
128
  }),
129
129
  /** The run (turn) this call belongs to, for a trace deep-link; null for a pre-rollout row. */
130
- runId: (0, import_sqlite_core.text)("run_id")
130
+ runId: (0, import_sqlite_core.text)("run_id"),
131
+ /** Who had to approve it (`requester` or a role); null for a call no policy put to anyone. */
132
+ approver: (0, import_sqlite_core.text)("approver"),
133
+ /** When the approval request lapses; null → never. */
134
+ expiresAt: (0, import_sqlite_core.integer)("expires_at", {
135
+ mode: "timestamp_ms"
136
+ }),
137
+ /** The approval covers later calls of this tool in this thread. */
138
+ remember: (0, import_sqlite_core.integer)("remember", {
139
+ mode: "boolean"
140
+ }),
141
+ /** The surface the decision came through (`web`, `slack`, `remembered`, …). */
142
+ decidedVia: (0, import_sqlite_core.text)("decided_via")
131
143
  }, (table) => [
132
144
  (0, import_sqlite_core.index)("agent_tool_call_message_idx").on(table.messageId)
133
145
  ]);
@@ -313,7 +325,11 @@ var statements = [
313
325
  error TEXT,
314
326
  created_at INTEGER NOT NULL,
315
327
  executed_at INTEGER,
316
- run_id TEXT
328
+ run_id TEXT,
329
+ approver TEXT,
330
+ expires_at INTEGER,
331
+ remember INTEGER,
332
+ decided_via TEXT
317
333
  )`,
318
334
  `CREATE INDEX IF NOT EXISTS agent_tool_call_message_idx
319
335
  ON agent_tool_call (message_id)`,
@@ -430,6 +446,26 @@ var additiveColumns = [
430
446
  table: "agent_message",
431
447
  column: "ui",
432
448
  ddl: "ALTER TABLE agent_message ADD COLUMN ui TEXT"
449
+ },
450
+ {
451
+ table: "agent_tool_call",
452
+ column: "approver",
453
+ ddl: "ALTER TABLE agent_tool_call ADD COLUMN approver TEXT"
454
+ },
455
+ {
456
+ table: "agent_tool_call",
457
+ column: "expires_at",
458
+ ddl: "ALTER TABLE agent_tool_call ADD COLUMN expires_at INTEGER"
459
+ },
460
+ {
461
+ table: "agent_tool_call",
462
+ column: "remember",
463
+ ddl: "ALTER TABLE agent_tool_call ADD COLUMN remember INTEGER"
464
+ },
465
+ {
466
+ table: "agent_tool_call",
467
+ column: "decided_via",
468
+ ddl: "ALTER TABLE agent_tool_call ADD COLUMN decided_via TEXT"
433
469
  }
434
470
  ];
435
471
  async function ensureAgentSchema(db) {
@@ -446,6 +482,7 @@ async function ensureAgentSchema(db) {
446
482
  __name(ensureAgentSchema, "ensureAgentSchema");
447
483
 
448
484
  // src/drizzle-agent-store.ts
485
+ var import_nestjs_agent_core = require("@dudousxd/nestjs-agent-core");
449
486
  var import_drizzle_orm2 = require("drizzle-orm");
450
487
  var TURN_MESSAGE_COLUMNS = {
451
488
  id: agentMessage.id,
@@ -489,9 +526,17 @@ var DrizzleAgentStore = class {
489
526
  }
490
527
  const messages = await this.db.select().from(agentMessage).where((0, import_drizzle_orm2.eq)(agentMessage.threadId, threadId)).orderBy((0, import_drizzle_orm2.asc)(agentMessage.createdAt), (0, import_drizzle_orm2.asc)(agentMessage.id));
491
528
  const last = messages[messages.length - 1];
529
+ const approvals = await this.approvalsFor(messages.map((message) => message.id));
492
530
  return {
493
531
  ...this.toSummary(thread, last?.content),
494
- messages: messages.map((message) => this.toStoredMessage(message)),
532
+ messages: messages.map((message) => {
533
+ const stored = this.toStoredMessage(message);
534
+ const onMessage = approvals.get(message.id);
535
+ return onMessage !== void 0 ? {
536
+ ...stored,
537
+ approvals: onMessage
538
+ } : stored;
539
+ }),
495
540
  ...thread.activeStreamId != null ? {
496
541
  activeStreamId: thread.activeStreamId
497
542
  } : {}
@@ -620,6 +665,63 @@ var DrizzleAgentStore = class {
620
665
  }).from(agentToolCall).innerJoin(agentMessage, (0, import_drizzle_orm2.eq)(agentToolCall.messageId, agentMessage.id)).innerJoin(agentThread, (0, import_drizzle_orm2.eq)(agentMessage.threadId, agentThread.id)).where((0, import_drizzle_orm2.eq)(agentToolCall.id, toolCallId));
621
666
  return row?.runId ?? row?.activeStreamId ?? null;
622
667
  }
668
+ /**
669
+ * The approval record of every call on `messageIds` that a policy put to a person, grouped by
670
+ * message. One query over the calls that carry an approver — a thread whose calls never asked
671
+ * anyone reads nothing back.
672
+ */
673
+ async approvalsFor(messageIds) {
674
+ const byMessage = /* @__PURE__ */ new Map();
675
+ if (messageIds.length === 0) {
676
+ return byMessage;
677
+ }
678
+ const rows = await this.db.select({
679
+ id: agentToolCall.id,
680
+ messageId: agentToolCall.messageId,
681
+ status: agentToolCall.status,
682
+ approver: agentToolCall.approver,
683
+ expiresAt: agentToolCall.expiresAt,
684
+ remember: agentToolCall.remember,
685
+ executedByRef: agentToolCall.executedByRef,
686
+ decidedVia: agentToolCall.decidedVia,
687
+ error: agentToolCall.error
688
+ }).from(agentToolCall).where((0, import_drizzle_orm2.and)((0, import_drizzle_orm2.inArray)(agentToolCall.messageId, messageIds), (0, import_drizzle_orm2.isNotNull)(agentToolCall.approver))).orderBy((0, import_drizzle_orm2.asc)(agentToolCall.createdAt), (0, import_drizzle_orm2.asc)(agentToolCall.id));
689
+ for (const row of rows) {
690
+ const approval = (0, import_nestjs_agent_core.toolCallApprovalFromRow)({
691
+ ...row,
692
+ toolCallId: row.id
693
+ });
694
+ if (approval === null) {
695
+ continue;
696
+ }
697
+ const list = byMessage.get(row.messageId) ?? [];
698
+ list.push(approval);
699
+ byMessage.set(row.messageId, list);
700
+ }
701
+ return byMessage;
702
+ }
703
+ /** Tools whose approval someone asked to remember in this thread. */
704
+ async rememberedApprovals(threadId) {
705
+ const rows = await this.db.selectDistinct({
706
+ toolName: agentToolCall.toolName
707
+ }).from(agentToolCall).innerJoin(agentMessage, (0, import_drizzle_orm2.eq)(agentToolCall.messageId, agentMessage.id)).where((0, import_drizzle_orm2.and)((0, import_drizzle_orm2.eq)(agentMessage.threadId, threadId), (0, import_drizzle_orm2.eq)(agentToolCall.remember, true)));
708
+ return rows.map((row) => row.toolName);
709
+ }
710
+ async toolCallApproval(toolCallId) {
711
+ const [row] = await this.db.select({
712
+ status: agentToolCall.status,
713
+ approver: agentToolCall.approver,
714
+ expiresAt: agentToolCall.expiresAt
715
+ }).from(agentToolCall).where((0, import_drizzle_orm2.eq)(agentToolCall.id, toolCallId));
716
+ if (row === void 0) {
717
+ return null;
718
+ }
719
+ return {
720
+ status: row.status,
721
+ approver: row.approver,
722
+ expiresAt: row.expiresAt?.toISOString() ?? null
723
+ };
724
+ }
623
725
  async ownerOfActiveStream(runId) {
624
726
  const [thread] = await this.db.select({
625
727
  actorRef: agentThread.actorRef
@@ -779,7 +881,11 @@ var DrizzleAgentStore = class {
779
881
  error: null,
780
882
  createdAt: /* @__PURE__ */ new Date(),
781
883
  executedAt: null,
782
- runId: input.runId ?? null
884
+ runId: input.runId ?? null,
885
+ approver: input.approver ?? null,
886
+ expiresAt: input.expiresAt !== void 0 ? new Date(input.expiresAt) : null,
887
+ remember: null,
888
+ decidedVia: null
783
889
  });
784
890
  }
785
891
  async updateToolCall(input) {
@@ -798,6 +904,12 @@ var DrizzleAgentStore = class {
798
904
  if (input.executedByRef !== void 0) {
799
905
  updates.executedByRef = input.executedByRef;
800
906
  }
907
+ if (input.remember !== void 0) {
908
+ updates.remember = input.remember;
909
+ }
910
+ if (input.decidedVia !== void 0) {
911
+ updates.decidedVia = input.decidedVia;
912
+ }
801
913
  if (input.status === "executed" || input.status === "auto_executed") {
802
914
  updates.executedAt = /* @__PURE__ */ new Date();
803
915
  }
@@ -917,7 +1029,7 @@ var DrizzleAgentStore = class {
917
1029
  };
918
1030
 
919
1031
  // src/drizzle-governance-queries.ts
920
- var import_nestjs_agent_core = require("@dudousxd/nestjs-agent-core");
1032
+ var import_nestjs_agent_core2 = require("@dudousxd/nestjs-agent-core");
921
1033
  var import_drizzle_orm3 = require("drizzle-orm");
922
1034
  var UNCLASSIFIED_ERROR_CODE = "unknown";
923
1035
  var DEFAULT_AGENT_BUCKET = "(default)";
@@ -926,7 +1038,8 @@ var TOOL_CALL_STATUSES = [
926
1038
  "pending_approval",
927
1039
  "executed",
928
1040
  "rejected",
929
- "failed"
1041
+ "failed",
1042
+ "expired"
930
1043
  ];
931
1044
  function isToolCallStatus(value) {
932
1045
  return TOOL_CALL_STATUSES.includes(value);
@@ -952,14 +1065,14 @@ function isRunStatus(value) {
952
1065
  }
953
1066
  __name(isRunStatus, "isRunStatus");
954
1067
  function dayStartUtc(day) {
955
- return (0, import_nestjs_agent_core.dayBoundsUtc)({
1068
+ return (0, import_nestjs_agent_core2.dayBoundsUtc)({
956
1069
  fromDay: day,
957
1070
  toDay: day
958
1071
  }).start;
959
1072
  }
960
1073
  __name(dayStartUtc, "dayStartUtc");
961
1074
  function dayEndUtc(day) {
962
- return (0, import_nestjs_agent_core.dayBoundsUtc)({
1075
+ return (0, import_nestjs_agent_core2.dayBoundsUtc)({
963
1076
  fromDay: day,
964
1077
  toDay: day
965
1078
  }).end;
@@ -1050,17 +1163,17 @@ var DrizzleGovernanceQueries = class {
1050
1163
  return pricing;
1051
1164
  }
1052
1165
  async usageInRange(range) {
1053
- const { start, end } = (0, import_nestjs_agent_core.dayBoundsUtc)(range);
1166
+ const { start, end } = (0, import_nestjs_agent_core2.dayBoundsUtc)(range);
1054
1167
  const rows = await this.db.select().from(agentTokenUsage).where((0, import_drizzle_orm3.and)((0, import_drizzle_orm3.gte)(agentTokenUsage.createdAt, start), (0, import_drizzle_orm3.lte)(agentTokenUsage.createdAt, end)));
1055
1168
  return rows.map(toUsageInput);
1056
1169
  }
1057
1170
  async spendByModel(range) {
1058
1171
  const pricing = await this.loadPricing();
1059
- return (0, import_nestjs_agent_core.bucketByModel)(await this.usageInRange(range), pricing);
1172
+ return (0, import_nestjs_agent_core2.bucketByModel)(await this.usageInRange(range), pricing);
1060
1173
  }
1061
1174
  async spendByActor(range) {
1062
1175
  const pricing = await this.loadPricing();
1063
- return (0, import_nestjs_agent_core.bucketByActor)(await this.usageInRange(range), pricing);
1176
+ return (0, import_nestjs_agent_core2.bucketByActor)(await this.usageInRange(range), pricing);
1064
1177
  }
1065
1178
  /**
1066
1179
  * Top threads by spend within the range, highest cost first, capped at `limit`. Usage rows for a
@@ -1084,14 +1197,14 @@ var DrizzleGovernanceQueries = class {
1084
1197
  actorRef: thread.actorRef
1085
1198
  }
1086
1199
  ]));
1087
- return (0, import_nestjs_agent_core.bucketByThread)(rows, pricing, threadsById, {
1200
+ return (0, import_nestjs_agent_core2.bucketByThread)(rows, pricing, threadsById, {
1088
1201
  limit,
1089
1202
  includeUnknownThreads: false
1090
1203
  });
1091
1204
  }
1092
1205
  async usageTrend(range) {
1093
1206
  const pricing = await this.loadPricing();
1094
- return (0, import_nestjs_agent_core.bucketUsageTrend)(await this.usageInRange(range), pricing);
1207
+ return (0, import_nestjs_agent_core2.bucketUsageTrend)(await this.usageInRange(range), pricing);
1095
1208
  }
1096
1209
  async recentToolCalls(limit) {
1097
1210
  const rows = await this.db.select({
@@ -1172,7 +1285,7 @@ var DrizzleGovernanceQueries = class {
1172
1285
  return this.toThreadActivityRows(threads);
1173
1286
  }
1174
1287
  async runsInRange(range) {
1175
- const { start, end } = (0, import_nestjs_agent_core.dayBoundsUtc)(range);
1288
+ const { start, end } = (0, import_nestjs_agent_core2.dayBoundsUtc)(range);
1176
1289
  return this.db.select().from(agentRun).where((0, import_drizzle_orm3.and)((0, import_drizzle_orm3.gte)(agentRun.startedAt, start), (0, import_drizzle_orm3.lte)(agentRun.startedAt, end)));
1177
1290
  }
1178
1291
  async runMetrics(range) {
@@ -1229,7 +1342,7 @@ var DrizzleGovernanceQueries = class {
1229
1342
  return result;
1230
1343
  }
1231
1344
  async runErrors(range) {
1232
- const { start, end } = (0, import_nestjs_agent_core.dayBoundsUtc)(range);
1345
+ const { start, end } = (0, import_nestjs_agent_core2.dayBoundsUtc)(range);
1233
1346
  const runs = await this.db.select().from(agentRun).where((0, import_drizzle_orm3.and)((0, import_drizzle_orm3.gte)(agentRun.startedAt, start), (0, import_drizzle_orm3.lte)(agentRun.startedAt, end), (0, import_drizzle_orm3.eq)(agentRun.status, "failed")));
1234
1347
  const byError = /* @__PURE__ */ new Map();
1235
1348
  for (const run of runs) {
@@ -1310,7 +1423,7 @@ var DrizzleGovernanceQueries = class {
1310
1423
  * `PERCENTILE_CONT` and one portable implementation beats three dialect-specific ones.
1311
1424
  */
1312
1425
  async toolStats(range) {
1313
- const { start, end } = (0, import_nestjs_agent_core.dayBoundsUtc)(range);
1426
+ const { start, end } = (0, import_nestjs_agent_core2.dayBoundsUtc)(range);
1314
1427
  const calls = await this.db.select().from(agentToolCall).where((0, import_drizzle_orm3.and)((0, import_drizzle_orm3.gte)(agentToolCall.createdAt, start), (0, import_drizzle_orm3.lte)(agentToolCall.createdAt, end)));
1315
1428
  const byTool = /* @__PURE__ */ new Map();
1316
1429
  for (const call of calls) {
@@ -1513,7 +1626,7 @@ var DrizzleGovernanceQueries = class {
1513
1626
  }
1514
1627
  const pricing = await this.loadPricing();
1515
1628
  const usageRows = await this.db.select().from(agentTokenUsage).where((0, import_drizzle_orm3.eq)(agentTokenUsage.threadId, thread.id));
1516
- const usage = (0, import_nestjs_agent_core.rollupThreadUsage)(usageRows.map(toUsageInput), pricing);
1629
+ const usage = (0, import_nestjs_agent_core2.rollupThreadUsage)(usageRows.map(toUsageInput), pricing);
1517
1630
  const [messageCountRow] = await this.db.select({
1518
1631
  value: (0, import_drizzle_orm3.count)()
1519
1632
  }).from(agentMessage).where((0, import_drizzle_orm3.eq)(agentMessage.threadId, thread.id));
@@ -1537,7 +1650,7 @@ var DrizzleGovernanceQueries = class {
1537
1650
  runs: runs.map(toRecentRunRow),
1538
1651
  runTotal: runTotalRow?.value ?? 0,
1539
1652
  messages: messages.map((message) => {
1540
- const { content, truncated } = (0, import_nestjs_agent_core.truncateDetailContent)(message.content);
1653
+ const { content, truncated } = (0, import_nestjs_agent_core2.truncateDetailContent)(message.content);
1541
1654
  return {
1542
1655
  messageId: message.id,
1543
1656
  role: message.role,
@@ -1568,7 +1681,7 @@ var DrizzleGovernanceQueries = class {
1568
1681
  };
1569
1682
 
1570
1683
  // src/drizzle-memory-provider.ts
1571
- var import_nestjs_agent_core2 = require("@dudousxd/nestjs-agent-core");
1684
+ var import_nestjs_agent_core3 = require("@dudousxd/nestjs-agent-core");
1572
1685
  var import_drizzle_orm4 = require("drizzle-orm");
1573
1686
  function toRecord(row) {
1574
1687
  return {
@@ -1629,7 +1742,7 @@ var DrizzleMemoryProvider = class {
1629
1742
  * answer cannot be used to find out which memories exist about other people.
1630
1743
  */
1631
1744
  async forget({ id, ctx }) {
1632
- const deleted = await this.db.delete(agentMemory).where((0, import_drizzle_orm4.and)((0, import_drizzle_orm4.eq)(agentMemory.id, id), (0, import_drizzle_orm4.eq)(agentMemory.scope, (0, import_nestjs_agent_core2.actorScope)(ctx.actor)))).returning({
1745
+ const deleted = await this.db.delete(agentMemory).where((0, import_drizzle_orm4.and)((0, import_drizzle_orm4.eq)(agentMemory.id, id), (0, import_drizzle_orm4.eq)(agentMemory.scope, (0, import_nestjs_agent_core3.actorScope)(ctx.actor)))).returning({
1633
1746
  id: agentMemory.id
1634
1747
  });
1635
1748
  return deleted.length > 0;
@@ -1702,7 +1815,7 @@ var DrizzleMemoryProvider = class {
1702
1815
  }
1703
1816
  };
1704
1817
  function assertAuthorMayWriteScope({ scope, origin, ctx }) {
1705
- const own = (0, import_nestjs_agent_core2.actorScope)(ctx.actor);
1818
+ const own = (0, import_nestjs_agent_core3.actorScope)(ctx.actor);
1706
1819
  if (origin.author !== "human" && scope !== own) {
1707
1820
  throw new Error(`DrizzleMemoryProvider: refusing to write an agent-authored memory at "${scope}"; an agent may write only at "${own}"`);
1708
1821
  }
@@ -2081,7 +2194,7 @@ DrizzleRagIngestionLog = _ts_decorate([
2081
2194
  ], DrizzleRagIngestionLog);
2082
2195
 
2083
2196
  // src/drizzle-agent-store.module.ts
2084
- var import_nestjs_agent_core3 = require("@dudousxd/nestjs-agent-core");
2197
+ var import_nestjs_agent_core4 = require("@dudousxd/nestjs-agent-core");
2085
2198
  var import_common2 = require("@nestjs/common");
2086
2199
  function _ts_decorate2(decorators, target, key, desc4) {
2087
2200
  var c = arguments.length, r = c < 3 ? target : desc4 === null ? desc4 = Object.getOwnPropertyDescriptor(target, key) : desc4, d;
@@ -2118,7 +2231,7 @@ var DrizzleAgentStoreModule = class _DrizzleAgentStoreModule {
2118
2231
  useFactory: /* @__PURE__ */ __name(() => new DrizzleAgentStore(options.db), "useFactory")
2119
2232
  },
2120
2233
  {
2121
- provide: import_nestjs_agent_core3.AGENT_STORE,
2234
+ provide: import_nestjs_agent_core4.AGENT_STORE,
2122
2235
  useExisting: DrizzleAgentStore
2123
2236
  },
2124
2237
  // Exported, never bound to AGENT_MEMORY: that token's PRESENCE is what turns memory on, so
@@ -2135,21 +2248,21 @@ var DrizzleAgentStoreModule = class _DrizzleAgentStoreModule {
2135
2248
  // AGENT_PRICING_STORE: the host's own store when supplied, else the default Drizzle one.
2136
2249
  // DrizzleGovernanceQueries prices usage against whatever is bound here.
2137
2250
  options.pricingStore ? {
2138
- provide: import_nestjs_agent_core3.AGENT_PRICING_STORE,
2251
+ provide: import_nestjs_agent_core4.AGENT_PRICING_STORE,
2139
2252
  useClass: options.pricingStore
2140
2253
  } : {
2141
- provide: import_nestjs_agent_core3.AGENT_PRICING_STORE,
2254
+ provide: import_nestjs_agent_core4.AGENT_PRICING_STORE,
2142
2255
  useExisting: DrizzlePricingStore
2143
2256
  },
2144
2257
  {
2145
2258
  provide: DrizzleGovernanceQueries,
2146
2259
  useFactory: /* @__PURE__ */ __name((pricingStore) => new DrizzleGovernanceQueries(options.db, pricingStore), "useFactory"),
2147
2260
  inject: [
2148
- import_nestjs_agent_core3.AGENT_PRICING_STORE
2261
+ import_nestjs_agent_core4.AGENT_PRICING_STORE
2149
2262
  ]
2150
2263
  },
2151
2264
  {
2152
- provide: import_nestjs_agent_core3.AGENT_GOVERNANCE_QUERIES,
2265
+ provide: import_nestjs_agent_core4.AGENT_GOVERNANCE_QUERIES,
2153
2266
  useExisting: DrizzleGovernanceQueries
2154
2267
  }
2155
2268
  ],
@@ -2158,12 +2271,12 @@ var DrizzleAgentStoreModule = class _DrizzleAgentStoreModule {
2158
2271
  DrizzleRagIngestionLog
2159
2272
  ],
2160
2273
  DrizzleAgentStore,
2161
- import_nestjs_agent_core3.AGENT_STORE,
2274
+ import_nestjs_agent_core4.AGENT_STORE,
2162
2275
  DrizzleMemoryProvider,
2163
2276
  DrizzleGovernanceQueries,
2164
- import_nestjs_agent_core3.AGENT_GOVERNANCE_QUERIES,
2277
+ import_nestjs_agent_core4.AGENT_GOVERNANCE_QUERIES,
2165
2278
  DrizzlePricingStore,
2166
- import_nestjs_agent_core3.AGENT_PRICING_STORE
2279
+ import_nestjs_agent_core4.AGENT_PRICING_STORE
2167
2280
  ]
2168
2281
  };
2169
2282
  }