@dudousxd/nestjs-agent-store-drizzle 0.11.1 → 0.13.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,69 @@ 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 toolCallInput(toolCallId) {
711
+ const [row] = await this.db.select({
712
+ input: agentToolCall.input
713
+ }).from(agentToolCall).where((0, import_drizzle_orm2.eq)(agentToolCall.id, toolCallId));
714
+ return row?.input ?? null;
715
+ }
716
+ async toolCallApproval(toolCallId) {
717
+ const [row] = await this.db.select({
718
+ status: agentToolCall.status,
719
+ approver: agentToolCall.approver,
720
+ expiresAt: agentToolCall.expiresAt
721
+ }).from(agentToolCall).where((0, import_drizzle_orm2.eq)(agentToolCall.id, toolCallId));
722
+ if (row === void 0) {
723
+ return null;
724
+ }
725
+ return {
726
+ status: row.status,
727
+ approver: row.approver,
728
+ expiresAt: row.expiresAt?.toISOString() ?? null
729
+ };
730
+ }
623
731
  async ownerOfActiveStream(runId) {
624
732
  const [thread] = await this.db.select({
625
733
  actorRef: agentThread.actorRef
@@ -779,7 +887,11 @@ var DrizzleAgentStore = class {
779
887
  error: null,
780
888
  createdAt: /* @__PURE__ */ new Date(),
781
889
  executedAt: null,
782
- runId: input.runId ?? null
890
+ runId: input.runId ?? null,
891
+ approver: input.approver ?? null,
892
+ expiresAt: input.expiresAt !== void 0 ? new Date(input.expiresAt) : null,
893
+ remember: null,
894
+ decidedVia: null
783
895
  });
784
896
  }
785
897
  async updateToolCall(input) {
@@ -798,6 +910,12 @@ var DrizzleAgentStore = class {
798
910
  if (input.executedByRef !== void 0) {
799
911
  updates.executedByRef = input.executedByRef;
800
912
  }
913
+ if (input.remember !== void 0) {
914
+ updates.remember = input.remember;
915
+ }
916
+ if (input.decidedVia !== void 0) {
917
+ updates.decidedVia = input.decidedVia;
918
+ }
801
919
  if (input.status === "executed" || input.status === "auto_executed") {
802
920
  updates.executedAt = /* @__PURE__ */ new Date();
803
921
  }
@@ -917,7 +1035,7 @@ var DrizzleAgentStore = class {
917
1035
  };
918
1036
 
919
1037
  // src/drizzle-governance-queries.ts
920
- var import_nestjs_agent_core = require("@dudousxd/nestjs-agent-core");
1038
+ var import_nestjs_agent_core2 = require("@dudousxd/nestjs-agent-core");
921
1039
  var import_drizzle_orm3 = require("drizzle-orm");
922
1040
  var UNCLASSIFIED_ERROR_CODE = "unknown";
923
1041
  var DEFAULT_AGENT_BUCKET = "(default)";
@@ -926,7 +1044,8 @@ var TOOL_CALL_STATUSES = [
926
1044
  "pending_approval",
927
1045
  "executed",
928
1046
  "rejected",
929
- "failed"
1047
+ "failed",
1048
+ "expired"
930
1049
  ];
931
1050
  function isToolCallStatus(value) {
932
1051
  return TOOL_CALL_STATUSES.includes(value);
@@ -952,14 +1071,14 @@ function isRunStatus(value) {
952
1071
  }
953
1072
  __name(isRunStatus, "isRunStatus");
954
1073
  function dayStartUtc(day) {
955
- return (0, import_nestjs_agent_core.dayBoundsUtc)({
1074
+ return (0, import_nestjs_agent_core2.dayBoundsUtc)({
956
1075
  fromDay: day,
957
1076
  toDay: day
958
1077
  }).start;
959
1078
  }
960
1079
  __name(dayStartUtc, "dayStartUtc");
961
1080
  function dayEndUtc(day) {
962
- return (0, import_nestjs_agent_core.dayBoundsUtc)({
1081
+ return (0, import_nestjs_agent_core2.dayBoundsUtc)({
963
1082
  fromDay: day,
964
1083
  toDay: day
965
1084
  }).end;
@@ -1050,17 +1169,17 @@ var DrizzleGovernanceQueries = class {
1050
1169
  return pricing;
1051
1170
  }
1052
1171
  async usageInRange(range) {
1053
- const { start, end } = (0, import_nestjs_agent_core.dayBoundsUtc)(range);
1172
+ const { start, end } = (0, import_nestjs_agent_core2.dayBoundsUtc)(range);
1054
1173
  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
1174
  return rows.map(toUsageInput);
1056
1175
  }
1057
1176
  async spendByModel(range) {
1058
1177
  const pricing = await this.loadPricing();
1059
- return (0, import_nestjs_agent_core.bucketByModel)(await this.usageInRange(range), pricing);
1178
+ return (0, import_nestjs_agent_core2.bucketByModel)(await this.usageInRange(range), pricing);
1060
1179
  }
1061
1180
  async spendByActor(range) {
1062
1181
  const pricing = await this.loadPricing();
1063
- return (0, import_nestjs_agent_core.bucketByActor)(await this.usageInRange(range), pricing);
1182
+ return (0, import_nestjs_agent_core2.bucketByActor)(await this.usageInRange(range), pricing);
1064
1183
  }
1065
1184
  /**
1066
1185
  * Top threads by spend within the range, highest cost first, capped at `limit`. Usage rows for a
@@ -1084,14 +1203,14 @@ var DrizzleGovernanceQueries = class {
1084
1203
  actorRef: thread.actorRef
1085
1204
  }
1086
1205
  ]));
1087
- return (0, import_nestjs_agent_core.bucketByThread)(rows, pricing, threadsById, {
1206
+ return (0, import_nestjs_agent_core2.bucketByThread)(rows, pricing, threadsById, {
1088
1207
  limit,
1089
1208
  includeUnknownThreads: false
1090
1209
  });
1091
1210
  }
1092
1211
  async usageTrend(range) {
1093
1212
  const pricing = await this.loadPricing();
1094
- return (0, import_nestjs_agent_core.bucketUsageTrend)(await this.usageInRange(range), pricing);
1213
+ return (0, import_nestjs_agent_core2.bucketUsageTrend)(await this.usageInRange(range), pricing);
1095
1214
  }
1096
1215
  async recentToolCalls(limit) {
1097
1216
  const rows = await this.db.select({
@@ -1172,7 +1291,7 @@ var DrizzleGovernanceQueries = class {
1172
1291
  return this.toThreadActivityRows(threads);
1173
1292
  }
1174
1293
  async runsInRange(range) {
1175
- const { start, end } = (0, import_nestjs_agent_core.dayBoundsUtc)(range);
1294
+ const { start, end } = (0, import_nestjs_agent_core2.dayBoundsUtc)(range);
1176
1295
  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
1296
  }
1178
1297
  async runMetrics(range) {
@@ -1229,7 +1348,7 @@ var DrizzleGovernanceQueries = class {
1229
1348
  return result;
1230
1349
  }
1231
1350
  async runErrors(range) {
1232
- const { start, end } = (0, import_nestjs_agent_core.dayBoundsUtc)(range);
1351
+ const { start, end } = (0, import_nestjs_agent_core2.dayBoundsUtc)(range);
1233
1352
  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
1353
  const byError = /* @__PURE__ */ new Map();
1235
1354
  for (const run of runs) {
@@ -1310,7 +1429,7 @@ var DrizzleGovernanceQueries = class {
1310
1429
  * `PERCENTILE_CONT` and one portable implementation beats three dialect-specific ones.
1311
1430
  */
1312
1431
  async toolStats(range) {
1313
- const { start, end } = (0, import_nestjs_agent_core.dayBoundsUtc)(range);
1432
+ const { start, end } = (0, import_nestjs_agent_core2.dayBoundsUtc)(range);
1314
1433
  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
1434
  const byTool = /* @__PURE__ */ new Map();
1316
1435
  for (const call of calls) {
@@ -1513,7 +1632,7 @@ var DrizzleGovernanceQueries = class {
1513
1632
  }
1514
1633
  const pricing = await this.loadPricing();
1515
1634
  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);
1635
+ const usage = (0, import_nestjs_agent_core2.rollupThreadUsage)(usageRows.map(toUsageInput), pricing);
1517
1636
  const [messageCountRow] = await this.db.select({
1518
1637
  value: (0, import_drizzle_orm3.count)()
1519
1638
  }).from(agentMessage).where((0, import_drizzle_orm3.eq)(agentMessage.threadId, thread.id));
@@ -1537,7 +1656,7 @@ var DrizzleGovernanceQueries = class {
1537
1656
  runs: runs.map(toRecentRunRow),
1538
1657
  runTotal: runTotalRow?.value ?? 0,
1539
1658
  messages: messages.map((message) => {
1540
- const { content, truncated } = (0, import_nestjs_agent_core.truncateDetailContent)(message.content);
1659
+ const { content, truncated } = (0, import_nestjs_agent_core2.truncateDetailContent)(message.content);
1541
1660
  return {
1542
1661
  messageId: message.id,
1543
1662
  role: message.role,
@@ -1568,7 +1687,7 @@ var DrizzleGovernanceQueries = class {
1568
1687
  };
1569
1688
 
1570
1689
  // src/drizzle-memory-provider.ts
1571
- var import_nestjs_agent_core2 = require("@dudousxd/nestjs-agent-core");
1690
+ var import_nestjs_agent_core3 = require("@dudousxd/nestjs-agent-core");
1572
1691
  var import_drizzle_orm4 = require("drizzle-orm");
1573
1692
  function toRecord(row) {
1574
1693
  return {
@@ -1629,7 +1748,7 @@ var DrizzleMemoryProvider = class {
1629
1748
  * answer cannot be used to find out which memories exist about other people.
1630
1749
  */
1631
1750
  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({
1751
+ 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
1752
  id: agentMemory.id
1634
1753
  });
1635
1754
  return deleted.length > 0;
@@ -1702,7 +1821,7 @@ var DrizzleMemoryProvider = class {
1702
1821
  }
1703
1822
  };
1704
1823
  function assertAuthorMayWriteScope({ scope, origin, ctx }) {
1705
- const own = (0, import_nestjs_agent_core2.actorScope)(ctx.actor);
1824
+ const own = (0, import_nestjs_agent_core3.actorScope)(ctx.actor);
1706
1825
  if (origin.author !== "human" && scope !== own) {
1707
1826
  throw new Error(`DrizzleMemoryProvider: refusing to write an agent-authored memory at "${scope}"; an agent may write only at "${own}"`);
1708
1827
  }
@@ -2081,7 +2200,7 @@ DrizzleRagIngestionLog = _ts_decorate([
2081
2200
  ], DrizzleRagIngestionLog);
2082
2201
 
2083
2202
  // src/drizzle-agent-store.module.ts
2084
- var import_nestjs_agent_core3 = require("@dudousxd/nestjs-agent-core");
2203
+ var import_nestjs_agent_core4 = require("@dudousxd/nestjs-agent-core");
2085
2204
  var import_common2 = require("@nestjs/common");
2086
2205
  function _ts_decorate2(decorators, target, key, desc4) {
2087
2206
  var c = arguments.length, r = c < 3 ? target : desc4 === null ? desc4 = Object.getOwnPropertyDescriptor(target, key) : desc4, d;
@@ -2118,7 +2237,7 @@ var DrizzleAgentStoreModule = class _DrizzleAgentStoreModule {
2118
2237
  useFactory: /* @__PURE__ */ __name(() => new DrizzleAgentStore(options.db), "useFactory")
2119
2238
  },
2120
2239
  {
2121
- provide: import_nestjs_agent_core3.AGENT_STORE,
2240
+ provide: import_nestjs_agent_core4.AGENT_STORE,
2122
2241
  useExisting: DrizzleAgentStore
2123
2242
  },
2124
2243
  // Exported, never bound to AGENT_MEMORY: that token's PRESENCE is what turns memory on, so
@@ -2135,21 +2254,21 @@ var DrizzleAgentStoreModule = class _DrizzleAgentStoreModule {
2135
2254
  // AGENT_PRICING_STORE: the host's own store when supplied, else the default Drizzle one.
2136
2255
  // DrizzleGovernanceQueries prices usage against whatever is bound here.
2137
2256
  options.pricingStore ? {
2138
- provide: import_nestjs_agent_core3.AGENT_PRICING_STORE,
2257
+ provide: import_nestjs_agent_core4.AGENT_PRICING_STORE,
2139
2258
  useClass: options.pricingStore
2140
2259
  } : {
2141
- provide: import_nestjs_agent_core3.AGENT_PRICING_STORE,
2260
+ provide: import_nestjs_agent_core4.AGENT_PRICING_STORE,
2142
2261
  useExisting: DrizzlePricingStore
2143
2262
  },
2144
2263
  {
2145
2264
  provide: DrizzleGovernanceQueries,
2146
2265
  useFactory: /* @__PURE__ */ __name((pricingStore) => new DrizzleGovernanceQueries(options.db, pricingStore), "useFactory"),
2147
2266
  inject: [
2148
- import_nestjs_agent_core3.AGENT_PRICING_STORE
2267
+ import_nestjs_agent_core4.AGENT_PRICING_STORE
2149
2268
  ]
2150
2269
  },
2151
2270
  {
2152
- provide: import_nestjs_agent_core3.AGENT_GOVERNANCE_QUERIES,
2271
+ provide: import_nestjs_agent_core4.AGENT_GOVERNANCE_QUERIES,
2153
2272
  useExisting: DrizzleGovernanceQueries
2154
2273
  }
2155
2274
  ],
@@ -2158,12 +2277,12 @@ var DrizzleAgentStoreModule = class _DrizzleAgentStoreModule {
2158
2277
  DrizzleRagIngestionLog
2159
2278
  ],
2160
2279
  DrizzleAgentStore,
2161
- import_nestjs_agent_core3.AGENT_STORE,
2280
+ import_nestjs_agent_core4.AGENT_STORE,
2162
2281
  DrizzleMemoryProvider,
2163
2282
  DrizzleGovernanceQueries,
2164
- import_nestjs_agent_core3.AGENT_GOVERNANCE_QUERIES,
2283
+ import_nestjs_agent_core4.AGENT_GOVERNANCE_QUERIES,
2165
2284
  DrizzlePricingStore,
2166
- import_nestjs_agent_core3.AGENT_PRICING_STORE
2285
+ import_nestjs_agent_core4.AGENT_PRICING_STORE
2167
2286
  ]
2168
2287
  };
2169
2288
  }