@mastra/libsql 1.25.2-alpha.1 → 1.26.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 (33) hide show
  1. package/dist/_types/@internal_core/dist/knowledge-compat/index.d.ts +594 -0
  2. package/dist/_types/@internal_core/package.json +1 -0
  3. package/dist/docs/SKILL.md +1 -1
  4. package/dist/docs/assets/SOURCE_MAP.json +1 -1
  5. package/dist/docs/references/docs-memory-message-history.md +15 -8
  6. package/dist/docs/references/reference-core-mastra-class.md +12 -4
  7. package/dist/docs/references/reference-storage-retention.md +21 -18
  8. package/dist/index.cjs +2048 -292
  9. package/dist/index.cjs.map +1 -1
  10. package/dist/index.js +2049 -293
  11. package/dist/index.js.map +1 -1
  12. package/dist/shared/file-transaction-gate.d.ts +28 -0
  13. package/dist/shared/file-transaction-gate.d.ts.map +1 -0
  14. package/dist/shared/reset-after-busy-client.d.ts +34 -0
  15. package/dist/shared/reset-after-busy-client.d.ts.map +1 -0
  16. package/dist/storage/db/index.d.ts +14 -6
  17. package/dist/storage/db/index.d.ts.map +1 -1
  18. package/dist/storage/db/run-fencing.d.ts +41 -0
  19. package/dist/storage/db/run-fencing.d.ts.map +1 -0
  20. package/dist/storage/db/utils.d.ts.map +1 -1
  21. package/dist/storage/domains/knowledge/index.d.ts +30 -7
  22. package/dist/storage/domains/knowledge/index.d.ts.map +1 -1
  23. package/dist/storage/domains/memory/index.d.ts +21 -7
  24. package/dist/storage/domains/memory/index.d.ts.map +1 -1
  25. package/dist/storage/domains/workflows/index.d.ts +21 -6
  26. package/dist/storage/domains/workflows/index.d.ts.map +1 -1
  27. package/dist/storage/factory-storage.d.ts.map +1 -1
  28. package/dist/storage/index.d.ts +6 -0
  29. package/dist/storage/index.d.ts.map +1 -1
  30. package/dist/storage/retention.d.ts +3 -3
  31. package/dist/storage/retention.d.ts.map +1 -1
  32. package/dist/vector/index.d.ts.map +1 -1
  33. package/package.json +5 -4
package/dist/index.cjs CHANGED
@@ -4,13 +4,358 @@ let _mastra_core_error = require("@mastra/core/error");
4
4
  let _mastra_core_storage = require("@mastra/core/storage");
5
5
  let _mastra_core_utils = require("@mastra/core/utils");
6
6
  let _mastra_core_vector = require("@mastra/core/vector");
7
+ let path = require("path");
8
+ let async_hooks = require("async_hooks");
7
9
  let _mastra_core_vector_filter = require("@mastra/core/vector/filter");
8
10
  let fs_promises = require("fs/promises");
9
- let path = require("path");
10
11
  let _mastra_core_base = require("@mastra/core/base");
12
+ let crypto$1 = require("crypto");
13
+ let _mastra_core_features = require("@mastra/core/features");
11
14
  let _mastra_core_agent = require("@mastra/core/agent");
12
15
  let _mastra_core_evals = require("@mastra/core/evals");
13
16
  let _mastra_core_storage_domains_skills = require("@mastra/core/storage/domains/skills");
17
+ //#region src/shared/file-transaction-gate.ts
18
+ /** Interactive transactions open in this process, per database file. */
19
+ const openTransactions = /* @__PURE__ */ new Map();
20
+ /** The database file a local `file:` url points at, or `undefined` for anything else. */
21
+ function localDatabaseFileKey(url) {
22
+ if (!url.startsWith("file:") || url.includes(":memory:")) return void 0;
23
+ const path$2 = url.slice(5).split("?")[0];
24
+ return path$2 ? (0, path.resolve)(path$2.replace(/^\/\/(?=\/)/, "")) : void 0;
25
+ }
26
+ /**
27
+ * Wraps a local `file:` client so its calls wait for interactive transactions
28
+ * that other clients in this process hold open on the same database file,
29
+ * instead of blocking the thread.
30
+ *
31
+ * `@libsql/client` runs local statements synchronously. When one client holds
32
+ * a write transaction open across an `await` and another client writes the same
33
+ * file, SQLite's busy handler spins on the event loop's only thread for
34
+ * `busy_timeout`, so the transaction holding the lock can't resume and the write
35
+ * fails with `SQLITE_BUSY`. Waiting lets the transaction finish first.
36
+ *
37
+ * Only calls on other clients wait; a client already orders its own writes
38
+ * behind its transactions (see `withClientWriteLock`). A call waits at most
39
+ * `maxWaitMs` (the busy timeout), then goes ahead as it would without the gate,
40
+ * so code that awaits another client from inside its own transaction can't hang.
41
+ * Clients in other processes are unaffected and still rely on `busy_timeout`.
42
+ */
43
+ function gateLocalFileClient(client, key, { maxWaitMs = 5e3 } = {}) {
44
+ let gated;
45
+ const othersOpen = () => [...openTransactions.get(key) ?? []].filter((open) => open.owner !== gated);
46
+ const waitForOthers = () => {
47
+ if (othersOpen().length === 0) return;
48
+ return (async () => {
49
+ let timer;
50
+ const gaveUp = new Promise((r) => timer = setTimeout(r, maxWaitMs, "timeout"));
51
+ try {
52
+ for (let open = othersOpen(); open.length > 0; open = othersOpen()) if (await Promise.race([Promise.all(open.map((o) => o.done)), gaveUp]) === "timeout") return false;
53
+ return true;
54
+ } finally {
55
+ clearTimeout(timer);
56
+ }
57
+ })();
58
+ };
59
+ const call = (run) => {
60
+ const waiting = waitForOthers();
61
+ return waiting ? waiting.then(run) : run();
62
+ };
63
+ const transaction = async (open, mode) => {
64
+ const deadline = Date.now() + maxWaitMs;
65
+ for (let waiting = waitForOthers(); waiting; waiting = Date.now() < deadline ? waitForOthers() : void 0) if (!await waiting) break;
66
+ let finish;
67
+ const entry = {
68
+ owner: gated,
69
+ done: new Promise((r) => finish = r)
70
+ };
71
+ let set = openTransactions.get(key);
72
+ if (!set) openTransactions.set(key, set = /* @__PURE__ */ new Set());
73
+ set.add(entry);
74
+ const end = () => {
75
+ if (!set.delete(entry)) return;
76
+ if (set.size === 0 && openTransactions.get(key) === set) openTransactions.delete(key);
77
+ finish();
78
+ };
79
+ let tx;
80
+ try {
81
+ tx = await open(mode);
82
+ } catch (error) {
83
+ end();
84
+ throw error;
85
+ }
86
+ return new Proxy(tx, { get(target, prop) {
87
+ switch (prop) {
88
+ case "execute":
89
+ case "batch":
90
+ case "executeMultiple": {
91
+ const method = Reflect.get(target, prop);
92
+ return async (...args) => {
93
+ try {
94
+ return await method.apply(target, args);
95
+ } finally {
96
+ if (target.closed) end();
97
+ }
98
+ };
99
+ }
100
+ case "commit":
101
+ case "rollback": {
102
+ const method = Reflect.get(target, prop);
103
+ return async () => {
104
+ try {
105
+ await method.call(target);
106
+ } finally {
107
+ end();
108
+ }
109
+ };
110
+ }
111
+ case "close": {
112
+ const method = Reflect.get(target, prop);
113
+ return () => {
114
+ try {
115
+ method.call(target);
116
+ } finally {
117
+ end();
118
+ }
119
+ };
120
+ }
121
+ default: {
122
+ const value = Reflect.get(target, prop);
123
+ return typeof value === "function" ? value.bind(target) : value;
124
+ }
125
+ }
126
+ } });
127
+ };
128
+ gated = new Proxy(client, { get(target, prop) {
129
+ switch (prop) {
130
+ case "transaction": {
131
+ const open = Reflect.get(target, prop);
132
+ return (mode) => transaction(open.bind(target), mode);
133
+ }
134
+ case "execute":
135
+ case "batch":
136
+ case "executeMultiple":
137
+ case "migrate": {
138
+ const method = Reflect.get(target, prop);
139
+ return (...args) => call(() => method.apply(target, args));
140
+ }
141
+ default: {
142
+ const value = Reflect.get(target, prop);
143
+ return typeof value === "function" ? value.bind(target) : value;
144
+ }
145
+ }
146
+ } });
147
+ return gated;
148
+ }
149
+ /** Applies {@link gateLocalFileClient} when `url` is a local database file; returns `client` otherwise. */
150
+ function gateIfLocalFile(client, url, options) {
151
+ const key = localDatabaseFileKey(url);
152
+ return key ? gateLocalFileClient(client, key, options) : client;
153
+ }
154
+ //#endregion
155
+ //#region src/shared/reset-after-busy-client.ts
156
+ function isBusy(error) {
157
+ return error?.code === "SQLITE_BUSY";
158
+ }
159
+ function firstOpen(hold) {
160
+ while (hold && !hold.open) hold = hold.outer;
161
+ return hold;
162
+ }
163
+ /**
164
+ * Wraps a pooled local `file:` client so a statement refused with `SQLITE_BUSY`
165
+ * doesn't leave its connection unusable.
166
+ *
167
+ * libsql leaves a write statement whose run failed busy in progress until it is
168
+ * garbage collected (tursodatabase/libsql-js#237). Until then no transaction on
169
+ * that connection can commit ("cannot commit transaction - SQL statements in
170
+ * progress"), and an autocommit write on it stays uncommitted while holding the
171
+ * database's write lock. The pool hands the same connection out again, so
172
+ * retrying the write doesn't help.
173
+ *
174
+ * After a busy failure the wrapper reopens the client's connections, which
175
+ * finalizes the stuck statement. Reopening closes every connection, including
176
+ * borrowed ones, so it waits until no call or transaction made through the
177
+ * wrapper is in flight. New calls wait for the reset rather than join the calls
178
+ * it waits for, so a client that is never idle still gets reset. A call made
179
+ * while its caller holds an open transaction, or from inside another call,
180
+ * doesn't wait: the reset waits for what its caller holds, so waiting would
181
+ * deadlock. Reopened connections start from the pool's connection options,
182
+ * so `afterReset` runs right after to restore any per-connection settings
183
+ * applied after opening, and calls that waited for the reset go on once it
184
+ * settles, even if it fails. A call waits at most `maxWaitMs` (default 5s, the
185
+ * busy timeout) so a long-held transaction can't stall every later call; after
186
+ * that it goes ahead on the current pool. A transaction SQLite rolled back on its
187
+ * own is released as soon as the failing call returns. It must use the client it was given, not the
188
+ * wrapper: calls through the wrapper would wait for the reset it is part of.
189
+ * Every other member passes through untouched.
190
+ */
191
+ function resetConnectionsAfterBusy(client, { afterReset, maxWaitMs = 5e3 } = {}) {
192
+ let inFlight = 0;
193
+ let pendingReset;
194
+ const holds = new async_hooks.AsyncLocalStorage();
195
+ const finishReset = () => {
196
+ const reset = pendingReset;
197
+ pendingReset = void 0;
198
+ reset?.finish();
199
+ };
200
+ const settle = () => {
201
+ inFlight--;
202
+ if (client.closed) return finishReset();
203
+ if (!pendingReset || inFlight > 0) return;
204
+ client.reconnect();
205
+ if (!afterReset) return finishReset();
206
+ const reset = pendingReset;
207
+ Promise.resolve().then(afterReset).catch(() => {}).finally(() => {
208
+ if (pendingReset === reset) finishReset();
209
+ });
210
+ };
211
+ const noteBusy = (error) => {
212
+ if (!isBusy(error) || pendingReset) return;
213
+ let finish;
214
+ pendingReset = {
215
+ done: new Promise((resolve) => finish = resolve),
216
+ finish
217
+ };
218
+ };
219
+ const enter = (callerHolds) => {
220
+ if (!pendingReset || callerHolds || client.closed) {
221
+ inFlight++;
222
+ return;
223
+ }
224
+ return (async () => {
225
+ let timer;
226
+ const gaveUp = new Promise((resolve) => timer = setTimeout(resolve, maxWaitMs, "timeout"));
227
+ try {
228
+ while (pendingReset && !client.closed) if (await Promise.race([pendingReset.done, gaveUp]) === "timeout") break;
229
+ } finally {
230
+ clearTimeout(timer);
231
+ }
232
+ inFlight++;
233
+ })();
234
+ };
235
+ const run = async (call) => {
236
+ const hold = {
237
+ open: true,
238
+ outer: firstOpen(holds.getStore())
239
+ };
240
+ const waiting = enter(hold.outer !== void 0);
241
+ if (waiting) await waiting;
242
+ try {
243
+ return await holds.run(hold, call);
244
+ } catch (error) {
245
+ noteBusy(error);
246
+ throw error;
247
+ } finally {
248
+ hold.open = false;
249
+ settle();
250
+ }
251
+ };
252
+ const transaction = (open, mode) => {
253
+ const hold = {
254
+ open: true,
255
+ outer: firstOpen(holds.getStore())
256
+ };
257
+ const waiting = enter(hold.outer !== void 0);
258
+ holds.enterWith(hold);
259
+ return openTransaction(open, mode, hold, waiting);
260
+ };
261
+ const openTransaction = async (open, mode, hold, waiting) => {
262
+ let tx;
263
+ try {
264
+ if (waiting) await waiting;
265
+ tx = await open(mode);
266
+ } catch (error) {
267
+ hold.open = false;
268
+ noteBusy(error);
269
+ settle();
270
+ throw error;
271
+ }
272
+ const end = () => {
273
+ if (!hold.open) return;
274
+ hold.open = false;
275
+ settle();
276
+ };
277
+ return new Proxy(tx, { get(target, prop) {
278
+ switch (prop) {
279
+ case "execute":
280
+ case "batch":
281
+ case "executeMultiple": {
282
+ const method = Reflect.get(target, prop);
283
+ return async (...args) => {
284
+ try {
285
+ return await method.apply(target, args);
286
+ } catch (error) {
287
+ noteBusy(error);
288
+ if (target.closed) try {
289
+ target.close();
290
+ } finally {
291
+ end();
292
+ }
293
+ throw error;
294
+ }
295
+ };
296
+ }
297
+ case "commit":
298
+ case "rollback": {
299
+ const method = Reflect.get(target, prop);
300
+ return async () => {
301
+ try {
302
+ await method.call(target);
303
+ } catch (error) {
304
+ noteBusy(error);
305
+ throw error;
306
+ } finally {
307
+ end();
308
+ }
309
+ };
310
+ }
311
+ case "close": {
312
+ const method = Reflect.get(target, prop);
313
+ return () => {
314
+ try {
315
+ method.call(target);
316
+ } finally {
317
+ end();
318
+ }
319
+ };
320
+ }
321
+ default: {
322
+ const value = Reflect.get(target, prop);
323
+ return typeof value === "function" ? value.bind(target) : value;
324
+ }
325
+ }
326
+ } });
327
+ };
328
+ return new Proxy(client, { get(target, prop) {
329
+ switch (prop) {
330
+ case "transaction": {
331
+ const open = Reflect.get(target, prop);
332
+ return (mode) => transaction(open.bind(target), mode);
333
+ }
334
+ case "execute":
335
+ case "batch":
336
+ case "executeMultiple":
337
+ case "migrate": {
338
+ const method = Reflect.get(target, prop);
339
+ return (...args) => run(() => method.apply(target, args));
340
+ }
341
+ case "close": {
342
+ const method = Reflect.get(target, prop);
343
+ return () => {
344
+ try {
345
+ method.call(target);
346
+ } finally {
347
+ finishReset();
348
+ }
349
+ };
350
+ }
351
+ default: {
352
+ const value = Reflect.get(target, prop);
353
+ return typeof value === "function" ? value.bind(target) : value;
354
+ }
355
+ }
356
+ } });
357
+ }
358
+ //#endregion
14
359
  //#region src/shared/single-connection-client.ts
15
360
  /**
16
361
  * Whether `@libsql/client` backs this database with exactly one connection.
@@ -519,7 +864,7 @@ var LibSQLVector = class extends _mastra_core_vector.MastraVector {
519
864
  this.turso = isSingleConnectionDatabase({
520
865
  url,
521
866
  syncUrl
522
- }) ? gateSingleConnectionClient(client) : client;
867
+ }) ? gateSingleConnectionClient(client) : isLocalDb ? gateIfLocalFile(resetConnectionsAfterBusy(client, { maxWaitMs: 5e3 }), url, { maxWaitMs: 5e3 }) : client;
523
868
  this.maxRetries = maxRetries;
524
869
  this.initialBackoffMs = initialBackoffMs;
525
870
  if (!Number.isInteger(vectorTopKOverFetchMultiplier) || vectorTopKOverFetchMultiplier < 1) throw new Error("vectorTopKOverFetchMultiplier must be a positive integer");
@@ -1110,6 +1455,110 @@ var LibSQLVector = class extends _mastra_core_vector.MastraVector {
1110
1455
  }
1111
1456
  };
1112
1457
  //#endregion
1458
+ //#region src/storage/db/write-lock.ts
1459
+ /**
1460
+ * Per-client write serialization.
1461
+ *
1462
+ * `@libsql/client` >= 0.18.0 pools connections for local `file:` databases, but
1463
+ * SQLite still admits one writer at a time: an interactive
1464
+ * `client.transaction('write')` holds `BEGIN` open across every
1465
+ * `await tx.execute(...)`, and any other write on the same database in that
1466
+ * window contends on the file lock and can fail with `SQLITE_BUSY` once
1467
+ * `busy_timeout` expires.
1468
+ *
1469
+ * This is dormant under the default engine but the evented engine runs many
1470
+ * concurrent workflow snapshot writes per agent run, so a write issued by an
1471
+ * unrelated domain (e.g. creating a dataset experiment) can fail spuriously.
1472
+ *
1473
+ * Serializing every write on a given client closes that window: writes — both
1474
+ * autocommit statements and full interactive transactions — run one at a time,
1475
+ * so none can interleave with an open transaction. Reads are intentionally not
1476
+ * gated; WAL readers never observe a partial write and must not queue behind a
1477
+ * long-running writer.
1478
+ *
1479
+ * `:memory:` databases and embedded replicas get a single pooled connection
1480
+ * instead; see `shared/single-connection-client.ts`, which gates *all* calls
1481
+ * (reads included) behind open transactions for those clients.
1482
+ */
1483
+ const clientWriteChains = /* @__PURE__ */ new WeakMap();
1484
+ /**
1485
+ * Runs `fn` after every previously-enqueued write on `client` has settled, and
1486
+ * returns its result. The chain advances regardless of whether `fn` resolves or
1487
+ * rejects, so one failed write never wedges the queue.
1488
+ */
1489
+ function withClientWriteLock(client, fn) {
1490
+ const result = (clientWriteChains.get(client) ?? Promise.resolve()).then(fn, fn);
1491
+ clientWriteChains.set(client, result.then(() => void 0, () => void 0));
1492
+ return result;
1493
+ }
1494
+ //#endregion
1495
+ //#region src/storage/db/run-fencing.ts
1496
+ /**
1497
+ * Epoch milliseconds on the database clock. `julianday('now')` rather than
1498
+ * `unixepoch('subsec')`, which needs SQLite 3.42 on the server.
1499
+ */
1500
+ const DB_NOW_MS = `CAST(ROUND((julianday('now') - 2440587.5) * 86400000) AS INTEGER)`;
1501
+ /**
1502
+ * Runs `fn` in a write transaction, behind every other write on `client`.
1503
+ * `'write'` opens the transaction with `BEGIN IMMEDIATE`, which takes the
1504
+ * database's write lock up front, so nothing written between a read and a
1505
+ * write inside `fn` can come from another connection.
1506
+ */
1507
+ function inWriteTransaction(client, fn) {
1508
+ return withClientWriteLock(client, async () => {
1509
+ const tx = await client.transaction("write");
1510
+ try {
1511
+ const result = await fn(tx);
1512
+ await tx.commit();
1513
+ return result;
1514
+ } catch (error) {
1515
+ if (!tx.closed) await tx.rollback();
1516
+ throw error;
1517
+ } finally {
1518
+ if (!tx.closed) tx.close();
1519
+ }
1520
+ });
1521
+ }
1522
+ /**
1523
+ * The check half of a fenced write; call it first inside the write's
1524
+ * transaction. The transaction already holds the database's write lock, so
1525
+ * a claim can't commit between this check and the write.
1526
+ */
1527
+ async function assertRunFence(tx, { claimsTable, fence, operation }) {
1528
+ const row = (await tx.execute({
1529
+ sql: `SELECT generation, ownerId FROM ${claimsTable} WHERE runId = ?`,
1530
+ args: [fence.runId]
1531
+ })).rows[0];
1532
+ if (!(0, _mastra_core_storage.matchesRunFence)(row ? {
1533
+ generation: Number(row.generation),
1534
+ ownerId: String(row.ownerId)
1535
+ } : null, fence)) throw new _mastra_core_storage.RunFenceConflictError(fence, operation);
1536
+ }
1537
+ /**
1538
+ * Runs `write` behind the client's write lock: directly when there is no
1539
+ * fence to check, or in a write transaction behind the check when there is.
1540
+ * `write` must issue every statement through the writer it is given; on a
1541
+ * single-connection client, a call on the client itself would wait for the
1542
+ * open transaction forever.
1543
+ */
1544
+ function withRunFence(client, check, write) {
1545
+ if (!check) return withClientWriteLock(client, () => write({
1546
+ execute: (statement) => client.execute(statement),
1547
+ batch: (statements) => client.batch(statements, "write")
1548
+ }));
1549
+ return inWriteTransaction(client, async (tx) => {
1550
+ await assertRunFence(tx, check);
1551
+ return write({
1552
+ execute: (statement) => tx.execute(statement),
1553
+ batch: async (statements) => {
1554
+ const results = [];
1555
+ for (const statement of statements) results.push(await tx.execute(statement));
1556
+ return results;
1557
+ }
1558
+ });
1559
+ });
1560
+ }
1561
+ //#endregion
1113
1562
  //#region src/storage/db/utils.ts
1114
1563
  const MAX_DEPTH = 100;
1115
1564
  /**
@@ -1202,6 +1651,7 @@ function createExecuteWriteOperationWithRetry({ logger, maxRetries, initialBacko
1202
1651
  while (attempts < maxRetries) try {
1203
1652
  return await operationFn();
1204
1653
  } catch (error) {
1654
+ if ((0, _mastra_core_storage.isRunFenceConflictError)(error)) throw error;
1205
1655
  logger.debug(`LibSQLStore: Error caught in retry loop for ${operationDescription}`, {
1206
1656
  errorType: error.constructor.name,
1207
1657
  errorCode: error.code,
@@ -1362,43 +1812,6 @@ function transformFromSqlRow({ tableName, sqlRow }) {
1362
1812
  return result;
1363
1813
  }
1364
1814
  //#endregion
1365
- //#region src/storage/db/write-lock.ts
1366
- /**
1367
- * Per-client write serialization.
1368
- *
1369
- * `@libsql/client` >= 0.18.0 pools connections for local `file:` databases, but
1370
- * SQLite still admits one writer at a time: an interactive
1371
- * `client.transaction('write')` holds `BEGIN` open across every
1372
- * `await tx.execute(...)`, and any other write on the same database in that
1373
- * window contends on the file lock and can fail with `SQLITE_BUSY` once
1374
- * `busy_timeout` expires.
1375
- *
1376
- * This is dormant under the default engine but the evented engine runs many
1377
- * concurrent workflow snapshot writes per agent run, so a write issued by an
1378
- * unrelated domain (e.g. creating a dataset experiment) can fail spuriously.
1379
- *
1380
- * Serializing every write on a given client closes that window: writes — both
1381
- * autocommit statements and full interactive transactions — run one at a time,
1382
- * so none can interleave with an open transaction. Reads are intentionally not
1383
- * gated; WAL readers never observe a partial write and must not queue behind a
1384
- * long-running writer.
1385
- *
1386
- * `:memory:` databases and embedded replicas get a single pooled connection
1387
- * instead; see `shared/single-connection-client.ts`, which gates *all* calls
1388
- * (reads included) behind open transactions for those clients.
1389
- */
1390
- const clientWriteChains = /* @__PURE__ */ new WeakMap();
1391
- /**
1392
- * Runs `fn` after every previously-enqueued write on `client` has settled, and
1393
- * returns its result. The chain advances regardless of whether `fn` resolves or
1394
- * rejects, so one failed write never wedges the queue.
1395
- */
1396
- function withClientWriteLock(client, fn) {
1397
- const result = (clientWriteChains.get(client) ?? Promise.resolve()).then(fn, fn);
1398
- clientWriteChains.set(client, result.then(() => void 0, () => void 0));
1399
- return result;
1400
- }
1401
- //#endregion
1402
1815
  //#region src/storage/db/index.ts
1403
1816
  /**
1404
1817
  * Default SQLite `busy_timeout` (ms) for local LibSQL connections. Chosen to
@@ -1499,10 +1912,10 @@ var LibSQLDB = class extends _mastra_core_base.MastraBase {
1499
1912
  /**
1500
1913
  * Internal insert implementation without retry logic.
1501
1914
  */
1502
- async doInsert({ tableName, record }) {
1915
+ async doInsert({ tableName, record, check }) {
1503
1916
  const filteredRecord = await this.filterRecordToKnownColumns(tableName, record);
1504
1917
  if (Object.keys(filteredRecord).length === 0) return;
1505
- await withClientWriteLock(this.client, () => this.client.execute(prepareStatement({
1918
+ await withRunFence(this.client, check, (writer) => writer.execute(prepareStatement({
1506
1919
  tableName,
1507
1920
  record: filteredRecord
1508
1921
  })));
@@ -1513,6 +1926,7 @@ var LibSQLDB = class extends _mastra_core_base.MastraBase {
1513
1926
  * @param args - The insert arguments
1514
1927
  * @param args.tableName - The name of the table to insert into
1515
1928
  * @param args.record - The record to insert (key-value pairs)
1929
+ * @param args.check - The run fence the insert must carry, if any
1516
1930
  */
1517
1931
  insert(args) {
1518
1932
  return this.executeWriteOperationWithRetry(() => this.doInsert(args), `insert into table ${args.tableName}`);
@@ -1787,7 +2201,7 @@ var LibSQLDB = class extends _mastra_core_base.MastraBase {
1787
2201
  * @param args.tableName - The name of the table to create
1788
2202
  * @param args.schema - The schema definition for the table columns
1789
2203
  */
1790
- async createTable({ tableName, schema, compositePrimaryKey }) {
2204
+ async createTable({ tableName, schema, compositePrimaryKey, executor = this.client }) {
1791
2205
  try {
1792
2206
  const parsedTableName = (0, _mastra_core_utils.parseSqlIdentifier)(tableName, "table name");
1793
2207
  if (compositePrimaryKey) {
@@ -1805,7 +2219,7 @@ var LibSQLDB = class extends _mastra_core_base.MastraBase {
1805
2219
  if (tableName === _mastra_core_storage.TABLE_WORKFLOW_SNAPSHOT) tableConstraints.push("UNIQUE (workflow_name, run_id)");
1806
2220
  if (tableName === _mastra_core_storage.TABLE_SPANS) tableConstraints.push("UNIQUE (spanId, traceId)");
1807
2221
  const sql = `CREATE TABLE IF NOT EXISTS ${parsedTableName} (\n ${[...columnDefinitions, ...tableConstraints].join(",\n ")}\n)`;
1808
- await this.client.execute(sql);
2222
+ await executor.execute(sql);
1809
2223
  this.logger.debug(`LibSQLDB: Created table ${tableName}`);
1810
2224
  if (tableName === _mastra_core_storage.TABLE_SPANS) await this.migrateSpansTable();
1811
2225
  } catch (error) {
@@ -6048,20 +6462,678 @@ var HarnessLibSQL = class HarnessLibSQL extends _mastra_core_storage.HarnessStor
6048
6462
  }
6049
6463
  };
6050
6464
  //#endregion
6465
+ //#region ../../packages/_internal-core/dist/knowledge-compat/index.js
6466
+ const KNOWLEDGE_V2_CORE_FEATURE = "knowledge-v2";
6467
+ /** Hard cap on scope nodes returned by one `listScopeNodes` read. */
6468
+ const MAX_KNOWLEDGE_SCOPE_NODES = 1e3;
6469
+ function knowledgeScopeNodeFilterKey(input) {
6470
+ return JSON.stringify([
6471
+ input.withinAddress ?? null,
6472
+ input.addresses ? [...input.addresses].sort() : null,
6473
+ input.ids ? [...input.ids].sort() : null
6474
+ ]);
6475
+ }
6476
+ function createKnowledgeScopeNodeCursor(scope, input) {
6477
+ return encodeURIComponent(JSON.stringify({
6478
+ version: 1,
6479
+ type: "scope",
6480
+ name: scope.name,
6481
+ id: scope.id,
6482
+ filter: knowledgeScopeNodeFilterKey(input)
6483
+ }));
6484
+ }
6485
+ /** Validates the page size and cursor of a `listScopeNodes` query; throws on a cursor from a different query. */
6486
+ function parseListKnowledgeScopeNodesInput(input = {}) {
6487
+ const requested = Number.isFinite(input.limit) ? Math.trunc(input.limit) : MAX_KNOWLEDGE_SCOPE_NODES;
6488
+ const limit = Math.min(Math.max(requested, 1), MAX_KNOWLEDGE_SCOPE_NODES);
6489
+ if (!input.cursor) return {
6490
+ limit,
6491
+ after: null
6492
+ };
6493
+ let value;
6494
+ try {
6495
+ value = JSON.parse(decodeURIComponent(input.cursor));
6496
+ } catch {
6497
+ throw new Error("Invalid Knowledge scope node cursor.");
6498
+ }
6499
+ const parsed = value && typeof value === "object" ? value : {};
6500
+ if (parsed.version !== 1 || parsed.type !== "scope" || typeof parsed.name !== "string" || typeof parsed.id !== "string" || parsed.filter !== knowledgeScopeNodeFilterKey(input)) throw new Error("Knowledge scope node cursor does not match this query.");
6501
+ return {
6502
+ limit,
6503
+ after: {
6504
+ name: parsed.name,
6505
+ id: parsed.id
6506
+ }
6507
+ };
6508
+ }
6509
+ /** Builds a page from up to `limit + 1` name/id-ordered scope nodes. */
6510
+ function pageKnowledgeScopeNodes(rows, limit, input) {
6511
+ const scopes = rows.slice(0, limit);
6512
+ const last = scopes.at(-1);
6513
+ return {
6514
+ scopes,
6515
+ nextCursor: rows.length > limit && last ? createKnowledgeScopeNodeCursor(last, input) : null
6516
+ };
6517
+ }
6518
+ /** Clamps a requested member limit to 1–{@link MAX_KNOWLEDGE_SCOPE_MEMBERS_LIMIT}. */
6519
+ function knowledgeScopeMembersLimit(limit) {
6520
+ if (limit === void 0 || !Number.isFinite(limit)) return 500;
6521
+ return Math.min(Math.max(Math.trunc(limit), 1), 500);
6522
+ }
6523
+ /** Validates the page size and cursor of a `listScopeMembers` query; throws on a cursor from another scope. */
6524
+ function parseListKnowledgeScopeMembersInput(input) {
6525
+ const limit = knowledgeScopeMembersLimit(input.limit);
6526
+ if (!input.cursor) return {
6527
+ limit,
6528
+ after: null
6529
+ };
6530
+ let value;
6531
+ try {
6532
+ value = JSON.parse(decodeURIComponent(input.cursor));
6533
+ } catch {
6534
+ throw new Error("Invalid Knowledge scope member cursor.");
6535
+ }
6536
+ const parsed = value && typeof value === "object" ? value : {};
6537
+ const updatedAt = typeof parsed.updatedAt === "string" ? new Date(parsed.updatedAt) : /* @__PURE__ */ new Date(NaN);
6538
+ if (parsed.version !== 1 || parsed.type !== "scope-member" || parsed.scopeNodeId !== input.scopeNodeId || typeof parsed.name !== "string" || typeof parsed.id !== "string" || Number.isNaN(updatedAt.getTime())) throw new Error("Knowledge scope member cursor does not match this query.");
6539
+ return {
6540
+ limit,
6541
+ after: {
6542
+ updatedAt,
6543
+ name: parsed.name,
6544
+ id: parsed.id
6545
+ }
6546
+ };
6547
+ }
6548
+ /** Builds a page from up to `limit + 1` members in `listScopeMembers` order. */
6549
+ function pageKnowledgeScopeMembers(rows, limit, input) {
6550
+ const members = rows.slice(0, limit);
6551
+ const last = members.at(-1);
6552
+ const hasMore = rows.length > limit;
6553
+ return {
6554
+ members,
6555
+ hasMore,
6556
+ nextCursor: hasMore && last ? encodeURIComponent(JSON.stringify({
6557
+ version: 1,
6558
+ type: "scope-member",
6559
+ scopeNodeId: input.scopeNodeId,
6560
+ updatedAt: last.updatedAt.toISOString(),
6561
+ name: last.name,
6562
+ id: last.id
6563
+ })) : null
6564
+ };
6565
+ }
6566
+ const TABLE_KNOWLEDGE_NODES$1 = "mastra_knowledge_nodes";
6567
+ const TABLE_KNOWLEDGE_RECORDS$1 = "mastra_knowledge_records";
6568
+ const TABLE_KNOWLEDGE_MENTIONS$1 = "mastra_knowledge_mentions";
6569
+ const TABLE_KNOWLEDGE_ACTIVITY$1 = "mastra_knowledge_activity";
6570
+ const TABLE_KNOWLEDGE_SEMANTIC_OUTBOX$1 = "mastra_knowledge_semantic_outbox";
6571
+ const TABLE_KNOWLEDGE_NODE_SCOPES = "mastra_knowledge_node_scopes";
6572
+ const TABLE_KNOWLEDGE_RECORD_SCOPES = "mastra_knowledge_record_scopes";
6573
+ const TABLE_KNOWLEDGE_SCOPE_GRANTS = "mastra_knowledge_scope_grants";
6574
+ const TABLE_KNOWLEDGE_ACCESS_STATE = "mastra_knowledge_access_state";
6575
+ const TABLE_KNOWLEDGE_SCOPE_ADDRESSES = "mastra_knowledge_scope_addresses";
6576
+ const TABLE_KNOWLEDGE_NODE_ADDRESSES = "mastra_knowledge_node_addresses";
6577
+ const TABLE_KNOWLEDGE_IMPORT_STATE = "mastra_knowledge_import_state";
6578
+ const TABLE_KNOWLEDGE_IMPORT_RUNS = "mastra_knowledge_import_runs";
6579
+ const TABLE_KNOWLEDGE_PROPOSALS = "mastra_knowledge_proposals";
6580
+ const KNOWLEDGE_TABLE_NAMES = [
6581
+ TABLE_KNOWLEDGE_NODES$1,
6582
+ TABLE_KNOWLEDGE_RECORDS$1,
6583
+ TABLE_KNOWLEDGE_MENTIONS$1,
6584
+ TABLE_KNOWLEDGE_ACTIVITY$1,
6585
+ TABLE_KNOWLEDGE_SEMANTIC_OUTBOX$1,
6586
+ TABLE_KNOWLEDGE_NODE_SCOPES,
6587
+ TABLE_KNOWLEDGE_RECORD_SCOPES,
6588
+ TABLE_KNOWLEDGE_SCOPE_GRANTS,
6589
+ TABLE_KNOWLEDGE_ACCESS_STATE,
6590
+ TABLE_KNOWLEDGE_SCOPE_ADDRESSES,
6591
+ TABLE_KNOWLEDGE_NODE_ADDRESSES,
6592
+ TABLE_KNOWLEDGE_IMPORT_STATE,
6593
+ TABLE_KNOWLEDGE_IMPORT_RUNS,
6594
+ TABLE_KNOWLEDGE_PROPOSALS
6595
+ ];
6596
+ /** Knowledge v1 tables with no v2 equivalent; an explicit schema reset drops them. */
6597
+ const RETIRED_KNOWLEDGE_TABLE_NAMES = ["mastra_knowledge_cursors"];
6598
+ /** Tables and indexes published v1 adapters created for every app, whether or not it used Knowledge. */
6599
+ const PUBLISHED_KNOWLEDGE_V1_TABLE_NAMES = /* @__PURE__ */ new Set([
6600
+ TABLE_KNOWLEDGE_NODES$1,
6601
+ TABLE_KNOWLEDGE_RECORDS$1,
6602
+ TABLE_KNOWLEDGE_MENTIONS$1,
6603
+ TABLE_KNOWLEDGE_ACTIVITY$1,
6604
+ TABLE_KNOWLEDGE_SEMANTIC_OUTBOX$1,
6605
+ ...RETIRED_KNOWLEDGE_TABLE_NAMES
6606
+ ]);
6607
+ const PUBLISHED_KNOWLEDGE_V1_INDEX_NAMES = /* @__PURE__ */ new Set([
6608
+ "idx_knowledge_nodes_identity",
6609
+ "idx_knowledge_nodes_scope",
6610
+ "idx_knowledge_records_node_latest",
6611
+ "idx_knowledge_records_thread_latest",
6612
+ "idx_knowledge_mentions_record",
6613
+ "idx_knowledge_activity_latest",
6614
+ "idx_knowledge_outbox_idempotency",
6615
+ "idx_knowledge_outbox_claim"
6616
+ ]);
6617
+ const KNOWLEDGE_V2_NODES_SCHEMA = {
6618
+ id: {
6619
+ type: "text",
6620
+ nullable: false,
6621
+ primaryKey: true
6622
+ },
6623
+ name: {
6624
+ type: "text",
6625
+ nullable: false
6626
+ },
6627
+ kind: {
6628
+ type: "text",
6629
+ nullable: true
6630
+ },
6631
+ isScope: {
6632
+ type: "boolean",
6633
+ nullable: false
6634
+ },
6635
+ metadata: {
6636
+ type: "jsonb",
6637
+ nullable: true
6638
+ },
6639
+ version: {
6640
+ type: "integer",
6641
+ nullable: false
6642
+ },
6643
+ createdAt: {
6644
+ type: "timestamp",
6645
+ nullable: false
6646
+ },
6647
+ updatedAt: {
6648
+ type: "timestamp",
6649
+ nullable: false
6650
+ },
6651
+ deletedAt: {
6652
+ type: "timestamp",
6653
+ nullable: true
6654
+ },
6655
+ deletedBy: {
6656
+ type: "text",
6657
+ nullable: true
6658
+ },
6659
+ type: {
6660
+ type: "text",
6661
+ nullable: true
6662
+ },
6663
+ canonicalName: {
6664
+ type: "text",
6665
+ nullable: true
6666
+ },
6667
+ content: {
6668
+ type: "text",
6669
+ nullable: true
6670
+ },
6671
+ description: {
6672
+ type: "text",
6673
+ nullable: true
6674
+ },
6675
+ scope: {
6676
+ type: "jsonb",
6677
+ nullable: true
6678
+ },
6679
+ scopeKey: {
6680
+ type: "text",
6681
+ nullable: true
6682
+ },
6683
+ mergedInto: {
6684
+ type: "text",
6685
+ nullable: true
6686
+ }
6687
+ };
6688
+ const KNOWLEDGE_V2_RECORDS_SCHEMA = {
6689
+ id: {
6690
+ type: "text",
6691
+ nullable: false,
6692
+ primaryKey: true
6693
+ },
6694
+ node: {
6695
+ type: "text",
6696
+ nullable: false,
6697
+ references: {
6698
+ table: TABLE_KNOWLEDGE_NODES$1,
6699
+ column: "id"
6700
+ }
6701
+ },
6702
+ text: {
6703
+ type: "text",
6704
+ nullable: false
6705
+ },
6706
+ metadata: {
6707
+ type: "jsonb",
6708
+ nullable: true
6709
+ },
6710
+ version: {
6711
+ type: "integer",
6712
+ nullable: false
6713
+ },
6714
+ capturedAt: {
6715
+ type: "timestamp",
6716
+ nullable: false
6717
+ },
6718
+ updatedAt: {
6719
+ type: "timestamp",
6720
+ nullable: false
6721
+ },
6722
+ deletedAt: {
6723
+ type: "timestamp",
6724
+ nullable: true
6725
+ },
6726
+ deletedBy: {
6727
+ type: "text",
6728
+ nullable: true
6729
+ },
6730
+ scope: {
6731
+ type: "jsonb",
6732
+ nullable: true
6733
+ },
6734
+ scopeKey: {
6735
+ type: "text",
6736
+ nullable: true
6737
+ },
6738
+ sourceThreadId: {
6739
+ type: "text",
6740
+ nullable: true
6741
+ },
6742
+ when: {
6743
+ type: "timestamp",
6744
+ nullable: true
6745
+ }
6746
+ };
6747
+ const KNOWLEDGE_V2_MENTIONS_SCHEMA = {
6748
+ recordId: {
6749
+ type: "text",
6750
+ nullable: false,
6751
+ references: {
6752
+ table: TABLE_KNOWLEDGE_RECORDS$1,
6753
+ column: "id"
6754
+ }
6755
+ },
6756
+ targetNodeId: {
6757
+ type: "text",
6758
+ nullable: true,
6759
+ references: {
6760
+ table: TABLE_KNOWLEDGE_NODES$1,
6761
+ column: "id"
6762
+ }
6763
+ },
6764
+ sourceType: {
6765
+ type: "text",
6766
+ nullable: true
6767
+ },
6768
+ sourceId: {
6769
+ type: "text",
6770
+ nullable: true
6771
+ }
6772
+ };
6773
+ const KNOWLEDGE_NODE_SCOPES_SCHEMA = {
6774
+ nodeId: {
6775
+ type: "text",
6776
+ nullable: false,
6777
+ references: {
6778
+ table: TABLE_KNOWLEDGE_NODES$1,
6779
+ column: "id"
6780
+ }
6781
+ },
6782
+ scopeNodeId: {
6783
+ type: "text",
6784
+ nullable: false,
6785
+ references: {
6786
+ table: TABLE_KNOWLEDGE_NODES$1,
6787
+ column: "id"
6788
+ }
6789
+ },
6790
+ addedAt: {
6791
+ type: "timestamp",
6792
+ nullable: false
6793
+ }
6794
+ };
6795
+ const KNOWLEDGE_RECORD_SCOPES_SCHEMA = {
6796
+ recordId: {
6797
+ type: "text",
6798
+ nullable: false,
6799
+ references: {
6800
+ table: TABLE_KNOWLEDGE_RECORDS$1,
6801
+ column: "id"
6802
+ }
6803
+ },
6804
+ scopeNodeId: {
6805
+ type: "text",
6806
+ nullable: false,
6807
+ references: {
6808
+ table: TABLE_KNOWLEDGE_NODES$1,
6809
+ column: "id"
6810
+ }
6811
+ },
6812
+ addedAt: {
6813
+ type: "timestamp",
6814
+ nullable: false
6815
+ }
6816
+ };
6817
+ const KNOWLEDGE_SCOPE_GRANTS_SCHEMA = {
6818
+ scopeNodeId: {
6819
+ type: "text",
6820
+ nullable: false,
6821
+ references: {
6822
+ table: TABLE_KNOWLEDGE_NODES$1,
6823
+ column: "id"
6824
+ }
6825
+ },
6826
+ scopeRefId: {
6827
+ type: "text",
6828
+ nullable: false,
6829
+ references: {
6830
+ table: TABLE_KNOWLEDGE_NODES$1,
6831
+ column: "id"
6832
+ }
6833
+ },
6834
+ role: {
6835
+ type: "text",
6836
+ nullable: false
6837
+ },
6838
+ canSuggest: {
6839
+ type: "boolean",
6840
+ nullable: true
6841
+ }
6842
+ };
6843
+ const KNOWLEDGE_ACCESS_STATE_SCHEMA = {
6844
+ id: {
6845
+ type: "text",
6846
+ nullable: false,
6847
+ primaryKey: true
6848
+ },
6849
+ epoch: {
6850
+ type: "integer",
6851
+ nullable: false
6852
+ },
6853
+ schemaVersion: {
6854
+ type: "integer",
6855
+ nullable: false
6856
+ }
6857
+ };
6858
+ const KNOWLEDGE_SCOPE_ADDRESSES_SCHEMA = {
6859
+ address: {
6860
+ type: "text",
6861
+ nullable: false,
6862
+ primaryKey: true
6863
+ },
6864
+ scopeNodeId: {
6865
+ type: "text",
6866
+ nullable: false,
6867
+ references: {
6868
+ table: TABLE_KNOWLEDGE_NODES$1,
6869
+ column: "id"
6870
+ }
6871
+ }
6872
+ };
6873
+ const KNOWLEDGE_NODE_ADDRESSES_SCHEMA = {
6874
+ source: {
6875
+ type: "text",
6876
+ nullable: false
6877
+ },
6878
+ address: {
6879
+ type: "text",
6880
+ nullable: false
6881
+ },
6882
+ nodeId: {
6883
+ type: "text",
6884
+ nullable: false,
6885
+ references: {
6886
+ table: TABLE_KNOWLEDGE_NODES$1,
6887
+ column: "id"
6888
+ }
6889
+ }
6890
+ };
6891
+ const KNOWLEDGE_IMPORT_STATE_SCHEMA = {
6892
+ importerId: {
6893
+ type: "text",
6894
+ nullable: false
6895
+ },
6896
+ binding: {
6897
+ type: "text",
6898
+ nullable: false
6899
+ },
6900
+ key: {
6901
+ type: "text",
6902
+ nullable: false
6903
+ },
6904
+ value: {
6905
+ type: "text",
6906
+ nullable: false
6907
+ }
6908
+ };
6909
+ const KNOWLEDGE_IMPORT_RUNS_SCHEMA = {
6910
+ id: {
6911
+ type: "text",
6912
+ nullable: false,
6913
+ primaryKey: true
6914
+ },
6915
+ importerId: {
6916
+ type: "text",
6917
+ nullable: false
6918
+ },
6919
+ binding: {
6920
+ type: "text",
6921
+ nullable: false
6922
+ },
6923
+ importKind: {
6924
+ type: "text",
6925
+ nullable: false
6926
+ },
6927
+ triggerKind: {
6928
+ type: "text",
6929
+ nullable: false
6930
+ },
6931
+ status: {
6932
+ type: "text",
6933
+ nullable: false
6934
+ },
6935
+ error: {
6936
+ type: "text",
6937
+ nullable: true
6938
+ },
6939
+ transcriptThreadId: {
6940
+ type: "text",
6941
+ nullable: true
6942
+ },
6943
+ traceId: {
6944
+ type: "text",
6945
+ nullable: true
6946
+ },
6947
+ queuedAt: {
6948
+ type: "timestamp",
6949
+ nullable: false
6950
+ },
6951
+ startedAt: {
6952
+ type: "timestamp",
6953
+ nullable: true
6954
+ },
6955
+ completedAt: {
6956
+ type: "timestamp",
6957
+ nullable: true
6958
+ }
6959
+ };
6960
+ const KNOWLEDGE_V2_ACTIVITY_SCHEMA = {
6961
+ id: {
6962
+ type: "text",
6963
+ nullable: false,
6964
+ primaryKey: true
6965
+ },
6966
+ action: {
6967
+ type: "text",
6968
+ nullable: false
6969
+ },
6970
+ targetType: {
6971
+ type: "text",
6972
+ nullable: true
6973
+ },
6974
+ targetId: {
6975
+ type: "text",
6976
+ nullable: true
6977
+ },
6978
+ contextScopeId: {
6979
+ type: "text",
6980
+ nullable: true,
6981
+ references: {
6982
+ table: TABLE_KNOWLEDGE_NODES$1,
6983
+ column: "id"
6984
+ }
6985
+ },
6986
+ importRunId: {
6987
+ type: "text",
6988
+ nullable: true,
6989
+ references: {
6990
+ table: TABLE_KNOWLEDGE_IMPORT_RUNS,
6991
+ column: "id"
6992
+ }
6993
+ },
6994
+ details: {
6995
+ type: "jsonb",
6996
+ nullable: true
6997
+ },
6998
+ createdAt: {
6999
+ type: "timestamp",
7000
+ nullable: false
7001
+ },
7002
+ recordType: {
7003
+ type: "text",
7004
+ nullable: true
7005
+ },
7006
+ recordId: {
7007
+ type: "text",
7008
+ nullable: true
7009
+ },
7010
+ scope: {
7011
+ type: "jsonb",
7012
+ nullable: true
7013
+ },
7014
+ scopeKey: {
7015
+ type: "text",
7016
+ nullable: true
7017
+ },
7018
+ sourceThreadId: {
7019
+ type: "text",
7020
+ nullable: true
7021
+ }
7022
+ };
7023
+ const KNOWLEDGE_PROPOSALS_SCHEMA = {
7024
+ id: {
7025
+ type: "text",
7026
+ nullable: false,
7027
+ primaryKey: true
7028
+ },
7029
+ targetType: {
7030
+ type: "text",
7031
+ nullable: false
7032
+ },
7033
+ targetId: {
7034
+ type: "text",
7035
+ nullable: false
7036
+ },
7037
+ action: {
7038
+ type: "text",
7039
+ nullable: false
7040
+ },
7041
+ changes: {
7042
+ type: "jsonb",
7043
+ nullable: false
7044
+ },
7045
+ reason: {
7046
+ type: "text",
7047
+ nullable: true
7048
+ },
7049
+ proposerContextScopeId: {
7050
+ type: "text",
7051
+ nullable: false
7052
+ },
7053
+ expectedVersion: {
7054
+ type: "integer",
7055
+ nullable: false
7056
+ },
7057
+ status: {
7058
+ type: "text",
7059
+ nullable: false
7060
+ },
7061
+ reviewerContextScopeId: {
7062
+ type: "text",
7063
+ nullable: true
7064
+ },
7065
+ reviewedAt: {
7066
+ type: "timestamp",
7067
+ nullable: true
7068
+ },
7069
+ createdAt: {
7070
+ type: "timestamp",
7071
+ nullable: false
7072
+ }
7073
+ };
7074
+ function assertKnowledgeV2CoreSupport(features) {
7075
+ if (!features.has("knowledge-v2")) throw new Error(`Knowledge v2 requires a @mastra/core release with the "${KNOWLEDGE_V2_CORE_FEATURE}" feature. Upgrade @mastra/core to use Knowledge; other storage domains keep working on this version.`);
7076
+ }
7077
+ function resolveKnowledgeV2Core(storageModule) {
7078
+ if (typeof storageModule !== "object" || storageModule === null) throw new Error("@mastra/core advertises Knowledge v2 without the required storage API");
7079
+ const { assertKnowledgeDescriptionWithinBound: assertDescription, assertKnowledgeRecordTextWithinBound: assertRecordText, assertKnowledgeSchemaCompatible: assertCompatible, inspectKnowledgeSchema: inspectSchema } = storageModule;
7080
+ if (typeof assertDescription !== "function" || typeof assertRecordText !== "function" || typeof assertCompatible !== "function" || typeof inspectSchema !== "function") throw new Error("@mastra/core advertises Knowledge v2 without the required storage API");
7081
+ return {
7082
+ assertKnowledgeDescriptionWithinBound: (description) => assertDescription(description),
7083
+ assertKnowledgeRecordTextWithinBound: (text) => assertRecordText(text),
7084
+ assertKnowledgeSchemaCompatible: (inspection) => assertCompatible(inspection),
7085
+ inspectKnowledgeSchema: (snapshot) => inspectSchema(snapshot)
7086
+ };
7087
+ }
7088
+ function createKnowledgeV2CoreLoader(features, loadStorageModule) {
7089
+ let knowledgeV2Core;
7090
+ return async () => {
7091
+ assertKnowledgeV2CoreSupport(features);
7092
+ knowledgeV2Core ??= loadStorageModule().then(resolveKnowledgeV2Core).catch((error) => {
7093
+ knowledgeV2Core = void 0;
7094
+ throw error;
7095
+ });
7096
+ return knowledgeV2Core;
7097
+ };
7098
+ }
7099
+ //#endregion
6051
7100
  //#region src/storage/domains/knowledge/index.ts
6052
- let assertDescriptionWithinBound;
7101
+ const loadKnowledgeV2Core = createKnowledgeV2CoreLoader(_mastra_core_features.coreFeatures, () => import("@mastra/core/storage"));
6053
7102
  async function assertKnowledgeDescriptionWithinBoundCompat(description) {
6054
- let assertWithinBound = assertDescriptionWithinBound;
6055
- if (!assertWithinBound) {
6056
- const resolvedAssert = (await import("@mastra/core/storage")).assertKnowledgeDescriptionWithinBound ?? ((value) => {
6057
- if (value !== void 0 && value.length > 400) throw new Error("Knowledge node description exceeds the 400 UTF-16 code unit limit");
6058
- });
6059
- assertDescriptionWithinBound = resolvedAssert;
6060
- assertWithinBound = resolvedAssert;
6061
- }
6062
- assertWithinBound(description);
7103
+ const { assertKnowledgeDescriptionWithinBound } = await loadKnowledgeV2Core();
7104
+ assertKnowledgeDescriptionWithinBound(description);
7105
+ }
7106
+ async function assertKnowledgeRecordTextWithinBoundCompat(text) {
7107
+ const { assertKnowledgeRecordTextWithinBound } = await loadKnowledgeV2Core();
7108
+ assertKnowledgeRecordTextWithinBound(text);
7109
+ }
7110
+ const reconcileChains = /* @__PURE__ */ new Map();
7111
+ const unidentifiedClientReconcileKey = {};
7112
+ function withReconcileLock(key, operation) {
7113
+ const lockKey = typeof key === "string" ? key : unidentifiedClientReconcileKey;
7114
+ const result = (reconcileChains.get(lockKey) ?? Promise.resolve()).then(operation, operation);
7115
+ const tail = result.then(() => void 0, () => void 0);
7116
+ reconcileChains.set(lockKey, tail);
7117
+ tail.finally(() => {
7118
+ if (reconcileChains.get(lockKey) === tail) reconcileChains.delete(lockKey);
7119
+ });
7120
+ return result;
6063
7121
  }
6064
- const visibleSql = `(scopeKey = ? OR substr(?, 1, length(scopeKey) + 1) = scopeKey || char(31))`;
7122
+ /**
7123
+ * Scope keys visible to a caller holding `scope`: a row is visible when its scope is a subset of the
7124
+ * caller's (companion entries such as `resource:x:uncurated` make this more than a prefix match).
7125
+ * Matching rows by `scopeKey` keeps visibility filters on an index; a caller scope has only a few
7126
+ * entries, so the list stays small.
7127
+ */
7128
+ function visibleScopeKeys(scope) {
7129
+ const canonical = (0, _mastra_core_storage.canonicalizeKnowledgeScope)(scope);
7130
+ const keys = /* @__PURE__ */ new Set();
7131
+ for (let mask = 1; mask < 1 << canonical.length; mask++) try {
7132
+ keys.add((0, _mastra_core_storage.knowledgeScopeKey)(canonical.filter((_, index) => mask & 1 << index)));
7133
+ } catch {}
7134
+ return [...keys];
7135
+ }
7136
+ const visibleSql = (keys, scopeKeyColumn = "scopeKey") => `${scopeKeyColumn} IN (${keys.map(() => "?").join(",")})`;
6065
7137
  function parseJson$3(value) {
6066
7138
  if (typeof value === "string") return JSON.parse(value);
6067
7139
  if (value instanceof Uint8Array) return JSON.parse(new TextDecoder().decode(value));
@@ -6091,6 +7163,7 @@ function parseNode(row) {
6091
7163
  kind: row.kind == null ? "" : String(row.kind),
6092
7164
  content: row.content == null ? void 0 : String(row.content),
6093
7165
  description: row.description == null ? void 0 : String(row.description),
7166
+ isScope: row.isScope === true || row.isScope === 1 ? true : void 0,
6094
7167
  scope: parseJson$3(row.scopeJson ?? row.scope),
6095
7168
  version: Number(row.version),
6096
7169
  mergedInto: row.mergedInto == null ? void 0 : String(row.mergedInto),
@@ -6107,7 +7180,6 @@ function parseKnowledge(row) {
6107
7180
  sourceThreadId: String(row.sourceThreadId),
6108
7181
  capturedAt: toDate(row.capturedAt),
6109
7182
  when: optionalDate(row.when),
6110
- maxScope: row.maxScope == null ? void 0 : String(row.maxScope),
6111
7183
  metadata: (row.metadataJson ?? row.metadata) == null ? void 0 : parseJson$3(row.metadataJson ?? row.metadata),
6112
7184
  deletedAt: optionalDate(row.deletedAt),
6113
7185
  deletedBy: row.deletedBy == null ? void 0 : String(row.deletedBy)
@@ -6130,100 +7202,458 @@ function parseOutbox(row) {
6130
7202
  completedAt: optionalDate(row.completedAt)
6131
7203
  };
6132
7204
  }
7205
+ const KNOWLEDGE_CURATION_CURSOR_REMOVED_MESSAGE = "Knowledge curation cursors were removed: observation-time curate is the only Knowledge writer and needs no cursor.";
7206
+ const knowledgeV2TableSchemas = /* @__PURE__ */ new Map([
7207
+ [_mastra_core_storage.TABLE_KNOWLEDGE_NODES, KNOWLEDGE_V2_NODES_SCHEMA],
7208
+ [_mastra_core_storage.TABLE_KNOWLEDGE_RECORDS, KNOWLEDGE_V2_RECORDS_SCHEMA],
7209
+ [_mastra_core_storage.TABLE_KNOWLEDGE_MENTIONS, KNOWLEDGE_V2_MENTIONS_SCHEMA],
7210
+ [_mastra_core_storage.TABLE_KNOWLEDGE_ACTIVITY, KNOWLEDGE_V2_ACTIVITY_SCHEMA],
7211
+ [_mastra_core_storage.TABLE_KNOWLEDGE_SEMANTIC_OUTBOX, _mastra_core_storage.KNOWLEDGE_SEMANTIC_OUTBOX_SCHEMA],
7212
+ [TABLE_KNOWLEDGE_NODE_SCOPES, KNOWLEDGE_NODE_SCOPES_SCHEMA],
7213
+ [TABLE_KNOWLEDGE_RECORD_SCOPES, KNOWLEDGE_RECORD_SCOPES_SCHEMA],
7214
+ [TABLE_KNOWLEDGE_SCOPE_GRANTS, KNOWLEDGE_SCOPE_GRANTS_SCHEMA],
7215
+ [TABLE_KNOWLEDGE_ACCESS_STATE, KNOWLEDGE_ACCESS_STATE_SCHEMA],
7216
+ [TABLE_KNOWLEDGE_SCOPE_ADDRESSES, KNOWLEDGE_SCOPE_ADDRESSES_SCHEMA],
7217
+ [TABLE_KNOWLEDGE_NODE_ADDRESSES, KNOWLEDGE_NODE_ADDRESSES_SCHEMA],
7218
+ [TABLE_KNOWLEDGE_IMPORT_STATE, KNOWLEDGE_IMPORT_STATE_SCHEMA],
7219
+ [TABLE_KNOWLEDGE_IMPORT_RUNS, KNOWLEDGE_IMPORT_RUNS_SCHEMA],
7220
+ [TABLE_KNOWLEDGE_PROPOSALS, KNOWLEDGE_PROPOSALS_SCHEMA]
7221
+ ]);
7222
+ function canonicalizeLibSQLUrl(url) {
7223
+ if (url.includes(":memory:")) return /[?&]cache=shared(?:&|$)/.test(url) ? "file::memory:?cache=shared" : void 0;
7224
+ if (url.startsWith("file:")) {
7225
+ const [path$1] = url.slice(5).split("?");
7226
+ if (url.startsWith("file://")) {
7227
+ const parsed = new URL(url);
7228
+ const host = parsed.hostname.toLocaleLowerCase();
7229
+ const filePath = host && host !== "localhost" ? `//${host}${parsed.pathname}` : parsed.pathname;
7230
+ return `file:${(0, path.resolve)(decodeURIComponent(filePath))}`;
7231
+ }
7232
+ return `file:${(0, path.resolve)(decodeURIComponent(path$1))}`;
7233
+ }
7234
+ try {
7235
+ const parsed = new URL(url);
7236
+ const port = parsed.port || (parsed.protocol === "libsql:" || parsed.protocol === "https:" ? "443" : "80");
7237
+ return `${parsed.protocol}//${parsed.hostname.toLocaleLowerCase()}:${port}${parsed.pathname}`;
7238
+ } catch {
7239
+ return url;
7240
+ }
7241
+ }
7242
+ const unidentifiedClientIsolationKey = {};
7243
+ function getLibSQLKnowledgeIsolationKey(config, client) {
7244
+ if (config.storageIsolationKey !== void 0) return config.storageIsolationKey;
7245
+ if (!config.url) return unidentifiedClientIsolationKey;
7246
+ const urlKey = canonicalizeLibSQLUrl(config.url);
7247
+ return urlKey ? `libsql:${urlKey}` : client ?? config.client ?? config;
7248
+ }
6133
7249
  var KnowledgeLibSQL = class extends _mastra_core_storage.KnowledgeStorage {
6134
7250
  #client;
6135
7251
  #db;
6136
7252
  constructor(config) {
6137
- super();
6138
- this.#client = resolveClient(config);
7253
+ const client = resolveClient(config);
7254
+ const storageIsolationKey = config.storageIsolationKey ?? getLibSQLKnowledgeIsolationKey(config, client);
7255
+ super({ storageIsolationKey });
7256
+ this.#client = client;
6139
7257
  this.#db = new LibSQLDB({
6140
7258
  client: this.#client,
6141
7259
  maxRetries: config.maxRetries,
6142
7260
  initialBackoffMs: config.initialBackoffMs
6143
7261
  });
6144
7262
  }
7263
+ getCapabilities() {
7264
+ return {
7265
+ contractVersion: 2,
7266
+ schemaVersion: 2,
7267
+ supportsV2: true,
7268
+ supportsSchemaInspection: true,
7269
+ supportsExplicitReset: true
7270
+ };
7271
+ }
7272
+ async inspectSchema() {
7273
+ return this.#inspectSchema(this.#client);
7274
+ }
7275
+ async #inspectSchema(executor) {
7276
+ const { inspectKnowledgeSchema } = await loadKnowledgeV2Core();
7277
+ try {
7278
+ const placeholders = KNOWLEDGE_TABLE_NAMES.map(() => "?").join(",");
7279
+ const tableNames = (await executor.execute({
7280
+ sql: `SELECT name FROM sqlite_master WHERE type = 'table' AND name IN (${placeholders})`,
7281
+ args: [...KNOWLEDGE_TABLE_NAMES]
7282
+ })).rows.map((row) => String(row.name));
7283
+ if (tableNames.length === 0) return inspectKnowledgeSchema({
7284
+ available: true,
7285
+ tableNames
7286
+ });
7287
+ const missingTables = KNOWLEDGE_TABLE_NAMES.filter((table) => !tableNames.includes(table));
7288
+ if (missingTables.length > 0) return inspectKnowledgeSchema({
7289
+ available: true,
7290
+ tableNames,
7291
+ reason: `Missing Knowledge v2 tables: ${missingTables.join(", ")}`
7292
+ });
7293
+ for (const [table, schema] of knowledgeV2TableSchemas) {
7294
+ const columns = await executor.execute(`PRAGMA table_info("${table}")`);
7295
+ const actual = new Set(columns.rows.map((row) => String(row.name)));
7296
+ const missing = Object.keys(schema).filter((column) => !actual.has(column));
7297
+ if (missing.length > 0) return inspectKnowledgeSchema({
7298
+ available: true,
7299
+ tableNames,
7300
+ reason: `Knowledge table ${table} is missing v2 columns: ${missing.join(", ")}`
7301
+ });
7302
+ }
7303
+ const accessState = await executor.execute(`SELECT schemaVersion FROM "${TABLE_KNOWLEDGE_ACCESS_STATE}" WHERE id='global'`);
7304
+ if (Number(accessState.rows[0]?.schemaVersion) !== 2) return inspectKnowledgeSchema({
7305
+ available: true,
7306
+ tableNames,
7307
+ reason: "Knowledge schema version marker is missing or incompatible"
7308
+ });
7309
+ return inspectKnowledgeSchema({
7310
+ available: true,
7311
+ tableNames,
7312
+ schemaVersion: 2
7313
+ });
7314
+ } catch (error) {
7315
+ return inspectKnowledgeSchema({
7316
+ available: false,
7317
+ tableNames: [],
7318
+ reason: error instanceof Error ? error.message : String(error)
7319
+ });
7320
+ }
7321
+ }
6145
7322
  async init() {
6146
- await this.#db.createTable({
6147
- tableName: _mastra_core_storage.TABLE_KNOWLEDGE_NODES,
6148
- schema: _mastra_core_storage.KNOWLEDGE_NODES_SCHEMA
7323
+ await this.#transaction((tx) => this.#initializeSchema(tx));
7324
+ }
7325
+ async #initializeSchema(tx) {
7326
+ const { assertKnowledgeSchemaCompatible } = await loadKnowledgeV2Core();
7327
+ let inspection = await this.#inspectSchema(tx);
7328
+ if (inspection.status === "incompatible-reset-required" && await this.#isPublishedV1Layout(tx)) {
7329
+ for (const table of [...RETIRED_KNOWLEDGE_TABLE_NAMES, ...[...KNOWLEDGE_TABLE_NAMES].reverse()]) await tx.execute(`DROP TABLE IF EXISTS "${table}"`);
7330
+ inspection = await this.#inspectSchema(tx);
7331
+ }
7332
+ assertKnowledgeSchemaCompatible(inspection);
7333
+ const createTable = (input) => this.#db.createTable({
7334
+ ...input,
7335
+ executor: tx
6149
7336
  });
6150
- await this.#db.alterTable({
7337
+ await createTable({
6151
7338
  tableName: _mastra_core_storage.TABLE_KNOWLEDGE_NODES,
6152
- schema: _mastra_core_storage.KNOWLEDGE_NODES_SCHEMA,
6153
- ifNotExists: ["description"]
7339
+ schema: KNOWLEDGE_V2_NODES_SCHEMA
6154
7340
  });
6155
- await this.#db.createTable({
7341
+ await createTable({
6156
7342
  tableName: _mastra_core_storage.TABLE_KNOWLEDGE_RECORDS,
6157
- schema: _mastra_core_storage.KNOWLEDGE_RECORDS_SCHEMA
7343
+ schema: KNOWLEDGE_V2_RECORDS_SCHEMA
6158
7344
  });
6159
- await this.#db.createTable({
7345
+ await createTable({
6160
7346
  tableName: _mastra_core_storage.TABLE_KNOWLEDGE_MENTIONS,
6161
- schema: _mastra_core_storage.KNOWLEDGE_MENTIONS_SCHEMA,
7347
+ schema: KNOWLEDGE_V2_MENTIONS_SCHEMA,
7348
+ compositePrimaryKey: ["recordId", "sourceId"]
7349
+ });
7350
+ await createTable({
7351
+ tableName: TABLE_KNOWLEDGE_NODE_SCOPES,
7352
+ schema: KNOWLEDGE_NODE_SCOPES_SCHEMA,
7353
+ compositePrimaryKey: ["nodeId", "scopeNodeId"]
7354
+ });
7355
+ await createTable({
7356
+ tableName: TABLE_KNOWLEDGE_RECORD_SCOPES,
7357
+ schema: KNOWLEDGE_RECORD_SCOPES_SCHEMA,
7358
+ compositePrimaryKey: ["recordId", "scopeNodeId"]
7359
+ });
7360
+ await createTable({
7361
+ tableName: TABLE_KNOWLEDGE_SCOPE_GRANTS,
7362
+ schema: KNOWLEDGE_SCOPE_GRANTS_SCHEMA,
7363
+ compositePrimaryKey: ["scopeNodeId", "scopeRefId"]
7364
+ });
7365
+ await createTable({
7366
+ tableName: TABLE_KNOWLEDGE_ACCESS_STATE,
7367
+ schema: KNOWLEDGE_ACCESS_STATE_SCHEMA
7368
+ });
7369
+ await createTable({
7370
+ tableName: TABLE_KNOWLEDGE_SCOPE_ADDRESSES,
7371
+ schema: KNOWLEDGE_SCOPE_ADDRESSES_SCHEMA
7372
+ });
7373
+ await createTable({
7374
+ tableName: TABLE_KNOWLEDGE_NODE_ADDRESSES,
7375
+ schema: KNOWLEDGE_NODE_ADDRESSES_SCHEMA,
7376
+ compositePrimaryKey: ["source", "address"]
7377
+ });
7378
+ await createTable({
7379
+ tableName: TABLE_KNOWLEDGE_IMPORT_STATE,
7380
+ schema: KNOWLEDGE_IMPORT_STATE_SCHEMA,
6162
7381
  compositePrimaryKey: [
6163
- "sourceType",
6164
- "sourceId",
6165
- "recordId"
7382
+ "importerId",
7383
+ "binding",
7384
+ "key"
6166
7385
  ]
6167
7386
  });
6168
- await this.#db.createTable({
6169
- tableName: _mastra_core_storage.TABLE_KNOWLEDGE_CURSORS,
6170
- schema: _mastra_core_storage.KNOWLEDGE_CURSORS_SCHEMA,
6171
- compositePrimaryKey: ["sourceThreadId", "agent"]
7387
+ await createTable({
7388
+ tableName: TABLE_KNOWLEDGE_IMPORT_RUNS,
7389
+ schema: KNOWLEDGE_IMPORT_RUNS_SCHEMA
6172
7390
  });
6173
- await this.#db.createTable({
6174
- tableName: _mastra_core_storage.TABLE_KNOWLEDGE_ACTIVITY,
6175
- schema: _mastra_core_storage.KNOWLEDGE_ACTIVITY_SCHEMA
7391
+ await createTable({
7392
+ tableName: TABLE_KNOWLEDGE_PROPOSALS,
7393
+ schema: KNOWLEDGE_PROPOSALS_SCHEMA
6176
7394
  });
6177
- await this.#db.createTable({
6178
- tableName: _mastra_core_storage.TABLE_KNOWLEDGE_SEMANTIC_OUTBOX,
6179
- schema: _mastra_core_storage.KNOWLEDGE_SEMANTIC_OUTBOX_SCHEMA
7395
+ await createTable({
7396
+ tableName: _mastra_core_storage.TABLE_KNOWLEDGE_ACTIVITY,
7397
+ schema: KNOWLEDGE_V2_ACTIVITY_SCHEMA
6180
7398
  });
6181
- await this.#client.batch([
6182
- {
6183
- sql: `CREATE UNIQUE INDEX IF NOT EXISTS idx_knowledge_nodes_identity ON "${_mastra_core_storage.TABLE_KNOWLEDGE_NODES}" (type, scopeKey, canonicalName)`,
6184
- args: []
6185
- },
6186
- {
6187
- sql: `CREATE INDEX IF NOT EXISTS idx_knowledge_nodes_scope ON "${_mastra_core_storage.TABLE_KNOWLEDGE_NODES}" (scopeKey, type)`,
6188
- args: []
6189
- },
6190
- {
6191
- sql: `CREATE INDEX IF NOT EXISTS idx_knowledge_records_node_latest ON "${_mastra_core_storage.TABLE_KNOWLEDGE_RECORDS}" (node, id DESC)`,
6192
- args: []
6193
- },
6194
- {
6195
- sql: `CREATE INDEX IF NOT EXISTS idx_knowledge_records_thread_latest ON "${_mastra_core_storage.TABLE_KNOWLEDGE_RECORDS}" (sourceThreadId, id DESC)`,
6196
- args: []
6197
- },
6198
- {
6199
- sql: `CREATE INDEX IF NOT EXISTS idx_knowledge_mentions_record ON "${_mastra_core_storage.TABLE_KNOWLEDGE_MENTIONS}" (recordId, sourceType, sourceId)`,
6200
- args: []
6201
- },
6202
- {
6203
- sql: `CREATE INDEX IF NOT EXISTS idx_knowledge_activity_latest ON "${_mastra_core_storage.TABLE_KNOWLEDGE_ACTIVITY}" (id DESC)`,
6204
- args: []
6205
- },
6206
- {
6207
- sql: `CREATE UNIQUE INDEX IF NOT EXISTS idx_knowledge_outbox_idempotency ON "${_mastra_core_storage.TABLE_KNOWLEDGE_SEMANTIC_OUTBOX}" (idempotencyKey)`,
6208
- args: []
6209
- },
6210
- {
6211
- sql: `CREATE INDEX IF NOT EXISTS idx_knowledge_outbox_claim ON "${_mastra_core_storage.TABLE_KNOWLEDGE_SEMANTIC_OUTBOX}" (status, availableAt, createdAt)`,
6212
- args: []
6213
- }
6214
- ], "write");
7399
+ await createTable({
7400
+ tableName: _mastra_core_storage.TABLE_KNOWLEDGE_SEMANTIC_OUTBOX,
7401
+ schema: _mastra_core_storage.KNOWLEDGE_SEMANTIC_OUTBOX_SCHEMA
7402
+ });
7403
+ for (const statement of [
7404
+ `CREATE UNIQUE INDEX IF NOT EXISTS idx_knowledge_nodes_identity ON "${_mastra_core_storage.TABLE_KNOWLEDGE_NODES}" (type, scopeKey, canonicalName)`,
7405
+ `CREATE INDEX IF NOT EXISTS idx_knowledge_nodes_scope ON "${_mastra_core_storage.TABLE_KNOWLEDGE_NODES}" (scopeKey, type)`,
7406
+ `CREATE INDEX IF NOT EXISTS idx_knowledge_nodes_name ON "${_mastra_core_storage.TABLE_KNOWLEDGE_NODES}" (type, canonicalName)`,
7407
+ `CREATE INDEX IF NOT EXISTS idx_knowledge_records_node_latest ON "${_mastra_core_storage.TABLE_KNOWLEDGE_RECORDS}" (node, id DESC)`,
7408
+ `CREATE INDEX IF NOT EXISTS idx_knowledge_records_thread_latest ON "${_mastra_core_storage.TABLE_KNOWLEDGE_RECORDS}" (sourceThreadId, id DESC)`,
7409
+ `CREATE INDEX IF NOT EXISTS idx_knowledge_mentions_record ON "${_mastra_core_storage.TABLE_KNOWLEDGE_MENTIONS}" (recordId, sourceType, sourceId)`,
7410
+ `CREATE INDEX IF NOT EXISTS idx_knowledge_activity_latest ON "${_mastra_core_storage.TABLE_KNOWLEDGE_ACTIVITY}" (id DESC)`,
7411
+ `CREATE INDEX IF NOT EXISTS idx_knowledge_records_scope ON "${_mastra_core_storage.TABLE_KNOWLEDGE_RECORDS}" (scopeKey, id)`,
7412
+ `CREATE INDEX IF NOT EXISTS idx_knowledge_activity_scope ON "${_mastra_core_storage.TABLE_KNOWLEDGE_ACTIVITY}" (scopeKey, id DESC)`,
7413
+ `CREATE UNIQUE INDEX IF NOT EXISTS idx_knowledge_outbox_idempotency ON "${_mastra_core_storage.TABLE_KNOWLEDGE_SEMANTIC_OUTBOX}" (idempotencyKey)`,
7414
+ `CREATE INDEX IF NOT EXISTS idx_knowledge_outbox_claim ON "${_mastra_core_storage.TABLE_KNOWLEDGE_SEMANTIC_OUTBOX}" (status, availableAt, createdAt)`,
7415
+ `CREATE INDEX IF NOT EXISTS idx_knowledge_node_scopes_scope ON "${TABLE_KNOWLEDGE_NODE_SCOPES}" (scopeNodeId, nodeId)`,
7416
+ `CREATE INDEX IF NOT EXISTS idx_knowledge_record_scopes_scope ON "${TABLE_KNOWLEDGE_RECORD_SCOPES}" (scopeNodeId, recordId)`,
7417
+ `CREATE INDEX IF NOT EXISTS idx_knowledge_scope_grants_ref ON "${TABLE_KNOWLEDGE_SCOPE_GRANTS}" (scopeRefId, scopeNodeId)`,
7418
+ `CREATE INDEX IF NOT EXISTS idx_knowledge_node_addresses_node ON "${TABLE_KNOWLEDGE_NODE_ADDRESSES}" (nodeId)`,
7419
+ `CREATE INDEX IF NOT EXISTS idx_knowledge_import_runs_lookup ON "${TABLE_KNOWLEDGE_IMPORT_RUNS}" (importerId, binding, queuedAt DESC)`,
7420
+ `CREATE INDEX IF NOT EXISTS idx_knowledge_activity_import_run ON "${_mastra_core_storage.TABLE_KNOWLEDGE_ACTIVITY}" (importRunId, id DESC)`
7421
+ ]) await tx.execute(statement);
7422
+ await tx.execute({
7423
+ sql: `INSERT OR IGNORE INTO "${TABLE_KNOWLEDGE_ACCESS_STATE}" (id, epoch, schemaVersion) VALUES ('global', 0, ?)`,
7424
+ args: [2]
7425
+ });
7426
+ }
7427
+ /**
7428
+ * True when the only Knowledge objects are the tables and indexes published v1 adapters created,
7429
+ * with or without rows. Anything else (v2 tables, unknown tables, views, triggers) needs an
7430
+ * explicit reset.
7431
+ */
7432
+ async #isPublishedV1Layout(executor) {
7433
+ const objects = await executor.execute("SELECT type, name FROM sqlite_master WHERE name LIKE 'mastra\\_knowledge\\_%' ESCAPE '\\' OR tbl_name LIKE 'mastra\\_knowledge\\_%' ESCAPE '\\' OR sql LIKE '%mastra\\_knowledge\\_%' ESCAPE '\\'");
7434
+ for (const row of objects.rows) {
7435
+ const type = String(row.type);
7436
+ const name = String(row.name);
7437
+ if (type === "table" && PUBLISHED_KNOWLEDGE_V1_TABLE_NAMES.has(name)) continue;
7438
+ const knownIndex = PUBLISHED_KNOWLEDGE_V1_INDEX_NAMES.has(name) || name.startsWith("sqlite_autoindex_");
7439
+ if (type !== "index" || !knownIndex) return false;
7440
+ }
7441
+ return true;
6215
7442
  }
6216
7443
  async dangerouslyClearAll() {
6217
7444
  await this.#transaction(async (tx) => {
6218
7445
  for (const table of [
7446
+ TABLE_KNOWLEDGE_RECORD_SCOPES,
7447
+ TABLE_KNOWLEDGE_NODE_SCOPES,
7448
+ TABLE_KNOWLEDGE_SCOPE_GRANTS,
7449
+ TABLE_KNOWLEDGE_SCOPE_ADDRESSES,
7450
+ TABLE_KNOWLEDGE_NODE_ADDRESSES,
6219
7451
  _mastra_core_storage.TABLE_KNOWLEDGE_MENTIONS,
6220
- _mastra_core_storage.TABLE_KNOWLEDGE_RECORDS,
6221
- _mastra_core_storage.TABLE_KNOWLEDGE_NODES,
6222
- _mastra_core_storage.TABLE_KNOWLEDGE_CURSORS,
7452
+ TABLE_KNOWLEDGE_PROPOSALS,
6223
7453
  _mastra_core_storage.TABLE_KNOWLEDGE_ACTIVITY,
6224
- _mastra_core_storage.TABLE_KNOWLEDGE_SEMANTIC_OUTBOX
7454
+ TABLE_KNOWLEDGE_IMPORT_STATE,
7455
+ TABLE_KNOWLEDGE_IMPORT_RUNS,
7456
+ _mastra_core_storage.TABLE_KNOWLEDGE_SEMANTIC_OUTBOX,
7457
+ _mastra_core_storage.TABLE_KNOWLEDGE_RECORDS,
7458
+ _mastra_core_storage.TABLE_KNOWLEDGE_NODES
6225
7459
  ]) await tx.execute(`DELETE FROM "${table}"`);
7460
+ await tx.execute(`UPDATE "${TABLE_KNOWLEDGE_ACCESS_STATE}" SET epoch = 0 WHERE id = 'global'`);
7461
+ });
7462
+ }
7463
+ async dangerouslyReset() {
7464
+ await withClientWriteLock(this.#client, async () => {
7465
+ await this.#client.batch([...RETIRED_KNOWLEDGE_TABLE_NAMES, ...[...KNOWLEDGE_TABLE_NAMES].reverse()].map((table) => ({
7466
+ sql: `DROP TABLE IF EXISTS "${table}"`,
7467
+ args: []
7468
+ })), "write");
7469
+ });
7470
+ await this.init();
7471
+ }
7472
+ async reconcileStructure(plan) {
7473
+ return withReconcileLock(this.getStorageIsolationKey(), () => this.#transaction(async (tx) => {
7474
+ await tx.execute(`UPDATE "${TABLE_KNOWLEDGE_ACCESS_STATE}" SET epoch=epoch WHERE id='global'`);
7475
+ const scopes = {};
7476
+ const createdScopeIds = [];
7477
+ const createdAddresses = /* @__PURE__ */ new Set();
7478
+ const retrofit = plan.retrofit ?? true;
7479
+ let structureChanged = false;
7480
+ const deletedScopeAddresses = /* @__PURE__ */ new Set();
7481
+ const resolveAddress = async (address) => {
7482
+ if (scopes[address]) return scopes[address];
7483
+ const row = (await tx.execute({
7484
+ sql: `SELECT a.scopeNodeId,n.isScope,n.deletedAt FROM "${TABLE_KNOWLEDGE_SCOPE_ADDRESSES}" a JOIN "${_mastra_core_storage.TABLE_KNOWLEDGE_NODES}" n ON n.id=a.scopeNodeId WHERE a.address=?`,
7485
+ args: [address]
7486
+ })).rows[0];
7487
+ if (!row) return void 0;
7488
+ if (!row.isScope) throw new Error(`Knowledge address ${address} does not reference a scope`);
7489
+ if (row.deletedAt) deletedScopeAddresses.add(address);
7490
+ scopes[address] = String(row.scopeNodeId);
7491
+ return scopes[address];
7492
+ };
7493
+ for (const scope of plan.scopes) {
7494
+ if (await resolveAddress(scope.address)) continue;
7495
+ const id = (0, crypto$1.randomUUID)();
7496
+ const now = (/* @__PURE__ */ new Date()).toISOString();
7497
+ await tx.execute({
7498
+ sql: `INSERT INTO "${_mastra_core_storage.TABLE_KNOWLEDGE_NODES}" (id,type,name,canonicalName,kind,description,isScope,metadata,version,createdAt,updatedAt) VALUES (?,'node',?,?,?,?,TRUE,jsonb(?),1,?,?)`,
7499
+ args: [
7500
+ id,
7501
+ scope.name,
7502
+ canonicalName(scope.name),
7503
+ scope.kind ?? null,
7504
+ scope.description ?? null,
7505
+ scope.metadata ? JSON.stringify(scope.metadata) : null,
7506
+ now,
7507
+ now
7508
+ ]
7509
+ });
7510
+ await tx.execute({
7511
+ sql: `INSERT INTO "${TABLE_KNOWLEDGE_SCOPE_ADDRESSES}" (address,scopeNodeId) VALUES (?,?)`,
7512
+ args: [scope.address, id]
7513
+ });
7514
+ scopes[scope.address] = id;
7515
+ createdAddresses.add(scope.address);
7516
+ createdScopeIds.push(id);
7517
+ structureChanged = true;
7518
+ }
7519
+ for (const scope of plan.scopes) {
7520
+ if (!createdAddresses.has(scope.address) && (!retrofit || deletedScopeAddresses.has(scope.address))) continue;
7521
+ const scopeNodeId = scopes[scope.address];
7522
+ for (const parentAddress of scope.parentAddresses ?? []) {
7523
+ const parentId = await resolveAddress(parentAddress);
7524
+ if (!parentId || deletedScopeAddresses.has(parentAddress)) {
7525
+ const deletedParentId = scopes[parentAddress];
7526
+ if ((deletedParentId ? await tx.execute({
7527
+ sql: `SELECT 1 FROM "mastra_knowledge_node_scopes" WHERE nodeId=? AND scopeNodeId=?`,
7528
+ args: [scopeNodeId, deletedParentId]
7529
+ }) : void 0)?.rows.length) continue;
7530
+ throw new Error(`Knowledge parent scope does not exist: ${parentAddress}`);
7531
+ }
7532
+ if ((await tx.execute({
7533
+ sql: `SELECT 1 FROM "mastra_knowledge_node_scopes" WHERE nodeId=? AND scopeNodeId=?`,
7534
+ args: [scopeNodeId, parentId]
7535
+ })).rows.length) continue;
7536
+ if ((await tx.execute({
7537
+ sql: `SELECT n.id FROM "mastra_knowledge_node_scopes" ns JOIN "${_mastra_core_storage.TABLE_KNOWLEDGE_NODES}" n ON n.id=ns.nodeId WHERE ns.scopeNodeId=? AND n.isScope AND n.canonicalName=? AND n.deletedAt IS NULL AND n.id<>? LIMIT 1`,
7538
+ args: [
7539
+ parentId,
7540
+ canonicalName(scope.name),
7541
+ scopeNodeId
7542
+ ]
7543
+ })).rows.length) throw new Error(`Knowledge scope name ${scope.name} already exists under ${parentAddress}`);
7544
+ const inserted = await tx.execute({
7545
+ sql: `INSERT OR IGNORE INTO "${TABLE_KNOWLEDGE_NODE_SCOPES}" (nodeId,scopeNodeId,addedAt) VALUES (?,?,?)`,
7546
+ args: [
7547
+ scopeNodeId,
7548
+ parentId,
7549
+ (/* @__PURE__ */ new Date()).toISOString()
7550
+ ]
7551
+ });
7552
+ structureChanged ||= inserted.rowsAffected > 0;
7553
+ }
7554
+ for (const grant of scope.grants ?? []) {
7555
+ const scopeRefId = await resolveAddress(grant.scopeRefAddress);
7556
+ if (!scopeRefId || deletedScopeAddresses.has(grant.scopeRefAddress)) {
7557
+ const deletedScopeRefId = scopes[grant.scopeRefAddress];
7558
+ if ((deletedScopeRefId ? await tx.execute({
7559
+ sql: `SELECT 1 FROM "mastra_knowledge_scope_grants" WHERE scopeNodeId=? AND scopeRefId=?`,
7560
+ args: [scopeNodeId, deletedScopeRefId]
7561
+ }) : void 0)?.rows.length) continue;
7562
+ throw new Error(`Knowledge grant scope does not exist: ${grant.scopeRefAddress}`);
7563
+ }
7564
+ const inserted = await tx.execute({
7565
+ sql: `INSERT OR IGNORE INTO "${TABLE_KNOWLEDGE_SCOPE_GRANTS}" (scopeNodeId,scopeRefId,role,canSuggest) VALUES (?,?,?,?)`,
7566
+ args: [
7567
+ scopeNodeId,
7568
+ scopeRefId,
7569
+ grant.role,
7570
+ grant.canSuggest ?? null
7571
+ ]
7572
+ });
7573
+ structureChanged ||= inserted.rowsAffected > 0;
7574
+ }
7575
+ }
7576
+ if (structureChanged) await tx.execute(`UPDATE "${TABLE_KNOWLEDGE_ACCESS_STATE}" SET epoch=epoch+1 WHERE id='global'`);
7577
+ const state = await tx.execute(`SELECT epoch FROM "${TABLE_KNOWLEDGE_ACCESS_STATE}" WHERE id='global'`);
7578
+ return {
7579
+ scopes,
7580
+ createdScopeIds,
7581
+ deletedScopeAddresses: [...deletedScopeAddresses],
7582
+ changed: structureChanged,
7583
+ accessEpoch: Number(state.rows[0]?.epoch ?? 0)
7584
+ };
7585
+ }));
7586
+ }
7587
+ async listScopeNodes(input = {}) {
7588
+ const { limit, after } = parseListKnowledgeScopeNodesInput(input);
7589
+ if (input.addresses?.length === 0 || input.ids?.length === 0) return {
7590
+ scopes: [],
7591
+ nextCursor: null
7592
+ };
7593
+ const args = [];
7594
+ const where = [`n.isScope`, `n.deletedAt IS NULL`];
7595
+ let within = "";
7596
+ if (input.withinAddress !== void 0) {
7597
+ within = `WITH RECURSIVE within_scope(id) AS (SELECT r.id FROM "${_mastra_core_storage.TABLE_KNOWLEDGE_NODES}" r JOIN "${TABLE_KNOWLEDGE_SCOPE_ADDRESSES}" ra ON ra.scopeNodeId=r.id WHERE ra.address=? AND r.isScope AND r.deletedAt IS NULL UNION SELECT ns.nodeId FROM "${TABLE_KNOWLEDGE_NODE_SCOPES}" ns JOIN within_scope w ON ns.scopeNodeId=w.id JOIN "${_mastra_core_storage.TABLE_KNOWLEDGE_NODES}" c ON c.id=ns.nodeId WHERE c.isScope AND c.deletedAt IS NULL) `;
7598
+ args.push(input.withinAddress);
7599
+ where.push("n.id IN (SELECT id FROM within_scope)");
7600
+ }
7601
+ if (input.addresses) {
7602
+ where.push(`a.address IN (${input.addresses.map(() => "?").join(",")})`);
7603
+ args.push(...input.addresses);
7604
+ }
7605
+ if (input.ids) {
7606
+ where.push(`n.id IN (${input.ids.map(() => "?").join(",")})`);
7607
+ args.push(...input.ids);
7608
+ }
7609
+ if (after) {
7610
+ where.push("(n.name > ? OR (n.name = ? AND n.id > ?))");
7611
+ args.push(after.name, after.name, after.id);
7612
+ }
7613
+ args.push(limit + 1);
7614
+ const rows = (await this.#client.execute({
7615
+ sql: `${within}SELECT n.id,n.name,n.kind,n.description,a.address FROM "${_mastra_core_storage.TABLE_KNOWLEDGE_NODES}" n LEFT JOIN "${TABLE_KNOWLEDGE_SCOPE_ADDRESSES}" a ON a.scopeNodeId=n.id WHERE ${where.join(" AND ")} ORDER BY n.name, n.id LIMIT ?`,
7616
+ args
7617
+ })).rows;
7618
+ if (rows.length === 0) return {
7619
+ scopes: [],
7620
+ nextCursor: null
7621
+ };
7622
+ const parents = await this.#client.execute({
7623
+ sql: `SELECT ns.nodeId,ns.scopeNodeId FROM "${TABLE_KNOWLEDGE_NODE_SCOPES}" ns WHERE ns.nodeId IN (${rows.map(() => "?").join(",")}) ORDER BY ns.scopeNodeId`,
7624
+ args: rows.map((row) => String(row.id))
6226
7625
  });
7626
+ const parentsByScopeId = /* @__PURE__ */ new Map();
7627
+ for (const row of parents.rows) {
7628
+ const scopeId = String(row.nodeId);
7629
+ const parentId = String(row.scopeNodeId);
7630
+ const existing = parentsByScopeId.get(scopeId);
7631
+ if (existing) existing.push(parentId);
7632
+ else parentsByScopeId.set(scopeId, [parentId]);
7633
+ }
7634
+ return pageKnowledgeScopeNodes(rows.map((row) => ({
7635
+ id: String(row.id),
7636
+ address: String(row.address),
7637
+ name: String(row.name),
7638
+ ...row.kind == null ? {} : { kind: String(row.kind) },
7639
+ ...row.description == null ? {} : { description: String(row.description) },
7640
+ parentIds: parentsByScopeId.get(String(row.id)) ?? []
7641
+ })), limit, input);
7642
+ }
7643
+ async listScopeMembers(input) {
7644
+ const { limit, after } = parseListKnowledgeScopeMembersInput(input);
7645
+ const args = [input.scopeNodeId];
7646
+ let keyset = "";
7647
+ if (after) {
7648
+ const updatedAt = after.updatedAt.toISOString();
7649
+ keyset = ` AND (n.updatedAt < ? OR (n.updatedAt = ? AND (n.name > ? OR (n.name = ? AND n.id > ?))))`;
7650
+ args.push(updatedAt, updatedAt, after.name, after.name, after.id);
7651
+ }
7652
+ args.push(limit + 1);
7653
+ return pageKnowledgeScopeMembers((await this.#client.execute({
7654
+ sql: `SELECT *, json(scope) AS scopeJson FROM "${_mastra_core_storage.TABLE_KNOWLEDGE_NODES}" n WHERE EXISTS (SELECT 1 FROM "${TABLE_KNOWLEDGE_NODE_SCOPES}" ns WHERE ns.nodeId=n.id AND ns.scopeNodeId=?) AND n.deletedAt IS NULL AND n.mergedInto IS NULL${keyset} ORDER BY n.updatedAt DESC, n.name ASC, n.id ASC LIMIT ?`,
7655
+ args
7656
+ })).rows.map(parseNode), limit, input);
6227
7657
  }
6228
7658
  async createNode(input) {
6229
7659
  await assertKnowledgeDescriptionWithinBoundCompat(input.description);
@@ -6233,6 +7663,7 @@ var KnowledgeLibSQL = class extends _mastra_core_storage.KnowledgeStorage {
6233
7663
  if (existing) {
6234
7664
  const terminal = await this.#resolveTerminalNode(tx, existing.id);
6235
7665
  if (!(0, _mastra_core_storage.isKnowledgeScopeVisible)(terminal.scope, scope)) throw new Error(`Merged knowledge node is not visible from scope: ${input.name}`);
7666
+ await this.#placeNodeInScopes(tx, terminal.id, input.scopeAddresses, /* @__PURE__ */ new Date());
6236
7667
  return terminal;
6237
7668
  }
6238
7669
  const now = /* @__PURE__ */ new Date();
@@ -6249,7 +7680,7 @@ var KnowledgeLibSQL = class extends _mastra_core_storage.KnowledgeStorage {
6249
7680
  updatedAt: now
6250
7681
  };
6251
7682
  await tx.execute({
6252
- sql: `INSERT INTO "${_mastra_core_storage.TABLE_KNOWLEDGE_NODES}" (id,type,name,canonicalName,kind,content,description,scope,scopeKey,version,mergedInto,createdAt,updatedAt) VALUES (?,?,?,?,?,?,?,jsonb(?),?,?,NULL,?,?)`,
7683
+ sql: `INSERT INTO "${_mastra_core_storage.TABLE_KNOWLEDGE_NODES}" (id,type,name,canonicalName,kind,content,description,isScope,scope,scopeKey,version,mergedInto,createdAt,updatedAt) VALUES (?,?,?,?,?,?,?,FALSE,jsonb(?),?,?,NULL,?,?)`,
6253
7684
  args: [
6254
7685
  node.id,
6255
7686
  "node",
@@ -6265,6 +7696,8 @@ var KnowledgeLibSQL = class extends _mastra_core_storage.KnowledgeStorage {
6265
7696
  now.toISOString()
6266
7697
  ]
6267
7698
  });
7699
+ await this.#replaceNodeScopes(tx, node.id, scope, now);
7700
+ await this.#placeNodeInScopes(tx, node.id, input.scopeAddresses, now);
6268
7701
  await this.#replaceMentions(tx, "node", node.id, node.content ?? "", input.resolutionScope ?? scope, scope);
6269
7702
  await this.#activity(tx, "node-created", "node", node.id, scope);
6270
7703
  await this.#outbox(tx, "node", node.id, "upsert", node.version, scope);
@@ -6281,13 +7714,13 @@ var KnowledgeLibSQL = class extends _mastra_core_storage.KnowledgeStorage {
6281
7714
  return this.#resolveNode(this.#client, input.name, (0, _mastra_core_storage.canonicalizeKnowledgeScope)(input.scope));
6282
7715
  }
6283
7716
  async listNodes(input) {
6284
- const key = (0, _mastra_core_storage.knowledgeScopeKey)((0, _mastra_core_storage.canonicalizeKnowledgeScope)(input.scope));
7717
+ const keys = visibleScopeKeys((0, _mastra_core_storage.canonicalizeKnowledgeScope)(input.scope));
6285
7718
  const clauses = [
6286
7719
  `type = 'node'`,
6287
7720
  "mergedInto IS NULL",
6288
- visibleSql
7721
+ visibleSql(keys)
6289
7722
  ];
6290
- const args = [key, key];
7723
+ const args = [...keys];
6291
7724
  if (input.namePrefix) {
6292
7725
  clauses.push("canonicalName LIKE ? ESCAPE '='");
6293
7726
  args.push(`${escapeLikePattern(canonicalName(input.namePrefix))}%`);
@@ -6339,6 +7772,7 @@ var KnowledgeLibSQL = class extends _mastra_core_storage.KnowledgeStorage {
6339
7772
  input.version
6340
7773
  ]
6341
7774
  })).rowsAffected === 0) throw new _mastra_core_storage.KnowledgeConflictError(input.id);
7775
+ if ((0, _mastra_core_storage.knowledgeScopeKey)(existing.scope) !== (0, _mastra_core_storage.knowledgeScopeKey)(scope)) await this.#swapIdentityScopes(tx, input.id, existing.scope, scope, now);
6342
7776
  if (input.content !== void 0 || input.name !== void 0 || input.scope !== void 0) await this.#replaceMentions(tx, "node", input.id, content ?? "", input.resolutionScope ?? scope, scope);
6343
7777
  await this.#activity(tx, "node-updated", "node", input.id, scope);
6344
7778
  if ((0, _mastra_core_storage.knowledgeScopeKey)(existing.scope) !== (0, _mastra_core_storage.knowledgeScopeKey)(scope)) {
@@ -6393,8 +7827,12 @@ var KnowledgeLibSQL = class extends _mastra_core_storage.KnowledgeStorage {
6393
7827
  ]
6394
7828
  })).rowsAffected === 0) throw new _mastra_core_storage.KnowledgeConflictError(source.id);
6395
7829
  await tx.execute({
6396
- sql: `UPDATE "${_mastra_core_storage.TABLE_KNOWLEDGE_RECORDS}" SET node=? WHERE node=?`,
6397
- args: [target.id, source.id]
7830
+ sql: `UPDATE "${_mastra_core_storage.TABLE_KNOWLEDGE_RECORDS}" SET node=?,version=version+1,updatedAt=? WHERE node=?`,
7831
+ args: [
7832
+ target.id,
7833
+ (/* @__PURE__ */ new Date()).toISOString(),
7834
+ source.id
7835
+ ]
6398
7836
  });
6399
7837
  await tx.execute({
6400
7838
  sql: `DELETE FROM "${_mastra_core_storage.TABLE_KNOWLEDGE_MENTIONS}" WHERE recordId=? AND EXISTS (SELECT 1 FROM "${_mastra_core_storage.TABLE_KNOWLEDGE_MENTIONS}" target WHERE target.sourceType="${_mastra_core_storage.TABLE_KNOWLEDGE_MENTIONS}".sourceType AND target.sourceId="${_mastra_core_storage.TABLE_KNOWLEDGE_MENTIONS}".sourceId AND target.recordId=?)`,
@@ -6434,10 +7872,10 @@ var KnowledgeLibSQL = class extends _mastra_core_storage.KnowledgeStorage {
6434
7872
  });
6435
7873
  }
6436
7874
  async appendKnowledge(input) {
7875
+ await assertKnowledgeRecordTextWithinBoundCompat(input.text);
6437
7876
  const scope = (0, _mastra_core_storage.canonicalizeKnowledgeScope)(input.scope);
6438
7877
  const resolutionScope = (0, _mastra_core_storage.canonicalizeKnowledgeScope)(input.resolutionScope);
6439
7878
  const defaultScope = (0, _mastra_core_storage.canonicalizeKnowledgeScope)(input.defaultScope);
6440
- (0, _mastra_core_storage.assertKnowledgeScopeWithinCeiling)(scope, input.maxScope);
6441
7879
  return this.#transaction(async (tx) => {
6442
7880
  const node = nodeReferenceId(input.node);
6443
7881
  const parent = await this.#resolveTerminalNode(tx, node);
@@ -6450,11 +7888,10 @@ var KnowledgeLibSQL = class extends _mastra_core_storage.KnowledgeStorage {
6450
7888
  sourceThreadId: input.sourceThreadId,
6451
7889
  capturedAt: /* @__PURE__ */ new Date(),
6452
7890
  when: input.when ? new Date(input.when) : void 0,
6453
- maxScope: input.maxScope,
6454
7891
  metadata: input.metadata
6455
7892
  };
6456
7893
  await tx.execute({
6457
- sql: `INSERT INTO "${_mastra_core_storage.TABLE_KNOWLEDGE_RECORDS}" (id,node,text,scope,scopeKey,sourceThreadId,capturedAt,"when",maxScope,metadata,deletedAt,deletedBy) VALUES (?,?,?,jsonb(?),?,?,?,?,?,jsonb(?),NULL,NULL)`,
7894
+ sql: `INSERT INTO "${_mastra_core_storage.TABLE_KNOWLEDGE_RECORDS}" (id,node,text,scope,scopeKey,sourceThreadId,capturedAt,"when",metadata,version,updatedAt,deletedAt,deletedBy) VALUES (?,?,?,jsonb(?),?,?,?,?,jsonb(?),?,?,NULL,NULL)`,
6458
7895
  args: [
6459
7896
  record.id,
6460
7897
  record.node,
@@ -6464,10 +7901,12 @@ var KnowledgeLibSQL = class extends _mastra_core_storage.KnowledgeStorage {
6464
7901
  record.sourceThreadId,
6465
7902
  record.capturedAt.toISOString(),
6466
7903
  record.when?.toISOString() ?? null,
6467
- record.maxScope ?? null,
6468
- record.metadata ? JSON.stringify(record.metadata) : null
7904
+ record.metadata ? JSON.stringify(record.metadata) : null,
7905
+ 1,
7906
+ record.capturedAt.toISOString()
6469
7907
  ]
6470
7908
  });
7909
+ await this.#replaceRecordScopes(tx, record.id, scope, record.capturedAt);
6471
7910
  await this.#replaceMentions(tx, "record", record.id, record.text, resolutionScope, defaultScope);
6472
7911
  await this.#activity(tx, "record-created", "record", record.id, scope, record.sourceThreadId);
6473
7912
  await this.#outbox(tx, "record", record.id, "upsert", record.id, scope);
@@ -6491,17 +7930,13 @@ var KnowledgeLibSQL = class extends _mastra_core_storage.KnowledgeStorage {
6491
7930
  return this.#queryKnowledge(input, "related");
6492
7931
  }
6493
7932
  async knowledgeBySource(input) {
6494
- const key = (0, _mastra_core_storage.knowledgeScopeKey)((0, _mastra_core_storage.canonicalizeKnowledgeScope)(input.scope));
6495
- const args = [
6496
- input.sourceThreadId,
6497
- key,
6498
- key
6499
- ];
7933
+ const keys = visibleScopeKeys((0, _mastra_core_storage.canonicalizeKnowledgeScope)(input.scope));
7934
+ const args = [input.sourceThreadId, ...keys];
6500
7935
  if (input.after) args.push(input.after);
6501
7936
  const limit = input.limit ?? 100;
6502
7937
  args.push(limit + 1);
6503
7938
  const records = (await this.#client.execute({
6504
- sql: `SELECT *,json(scope) AS scopeJson,json(metadata) AS metadataJson FROM "${_mastra_core_storage.TABLE_KNOWLEDGE_RECORDS}" WHERE sourceThreadId=? AND ${visibleSql}${input.includeDeleted ? "" : " AND deletedAt IS NULL"}${input.after ? " AND id > ?" : ""} ORDER BY id ASC LIMIT ?`,
7939
+ sql: `SELECT *,json(scope) AS scopeJson,json(metadata) AS metadataJson FROM "${_mastra_core_storage.TABLE_KNOWLEDGE_RECORDS}" WHERE sourceThreadId=? AND ${visibleSql(keys)}${input.includeDeleted ? "" : " AND deletedAt IS NULL"}${input.after ? " AND id > ?" : ""} ORDER BY id ASC LIMIT ?`,
6505
7940
  args
6506
7941
  })).rows.map(parseKnowledge);
6507
7942
  return {
@@ -6516,10 +7951,11 @@ var KnowledgeLibSQL = class extends _mastra_core_storage.KnowledgeStorage {
6516
7951
  if (record.deletedAt) return record;
6517
7952
  const deletedAt = /* @__PURE__ */ new Date();
6518
7953
  await tx.execute({
6519
- sql: `UPDATE "${_mastra_core_storage.TABLE_KNOWLEDGE_RECORDS}" SET deletedAt=?,deletedBy=? WHERE id=? AND deletedAt IS NULL`,
7954
+ sql: `UPDATE "${_mastra_core_storage.TABLE_KNOWLEDGE_RECORDS}" SET deletedAt=?,deletedBy=?,version=version+1,updatedAt=? WHERE id=? AND deletedAt IS NULL`,
6520
7955
  args: [
6521
7956
  deletedAt.toISOString(),
6522
7957
  input.deletedBy,
7958
+ deletedAt.toISOString(),
6523
7959
  input.id
6524
7960
  ]
6525
7961
  });
@@ -6538,8 +7974,8 @@ var KnowledgeLibSQL = class extends _mastra_core_storage.KnowledgeStorage {
6538
7974
  if (!record) throw new _mastra_core_storage.KnowledgeNotFoundError("record", input.id);
6539
7975
  if (!record.deletedAt) return record;
6540
7976
  await tx.execute({
6541
- sql: `UPDATE "${_mastra_core_storage.TABLE_KNOWLEDGE_RECORDS}" SET deletedAt=NULL,deletedBy=NULL WHERE id=?`,
6542
- args: [input.id]
7977
+ sql: `UPDATE "${_mastra_core_storage.TABLE_KNOWLEDGE_RECORDS}" SET deletedAt=NULL,deletedBy=NULL,version=version+1,updatedAt=? WHERE id=?`,
7978
+ args: [(/* @__PURE__ */ new Date()).toISOString(), input.id]
6543
7979
  });
6544
7980
  await this.#activity(tx, "record-restored", "record", input.id, record.scope, record.sourceThreadId);
6545
7981
  await this.#outbox(tx, "record", input.id, "upsert", (0, _mastra_core_storage.createKnowledgeUlid)(), record.scope);
@@ -6555,15 +7991,17 @@ var KnowledgeLibSQL = class extends _mastra_core_storage.KnowledgeStorage {
6555
7991
  return this.#transaction(async (tx) => {
6556
7992
  const record = await this.#getKnowledge(tx, input.id, true);
6557
7993
  if (!record) throw new _mastra_core_storage.KnowledgeNotFoundError("record", input.id);
6558
- (0, _mastra_core_storage.assertKnowledgeScopeWithinCeiling)(scope, record.maxScope);
7994
+ const updatedAt = /* @__PURE__ */ new Date();
6559
7995
  await tx.execute({
6560
- sql: `UPDATE "${_mastra_core_storage.TABLE_KNOWLEDGE_RECORDS}" SET scope=jsonb(?),scopeKey=? WHERE id=?`,
7996
+ sql: `UPDATE "${_mastra_core_storage.TABLE_KNOWLEDGE_RECORDS}" SET scope=jsonb(?),scopeKey=?,version=version+1,updatedAt=? WHERE id=?`,
6561
7997
  args: [
6562
7998
  JSON.stringify(scope),
6563
7999
  (0, _mastra_core_storage.knowledgeScopeKey)(scope),
8000
+ updatedAt.toISOString(),
6564
8001
  input.id
6565
8002
  ]
6566
8003
  });
8004
+ await this.#replaceRecordScopes(tx, input.id, scope, updatedAt);
6567
8005
  await this.#activity(tx, "record-rescoped", "record", input.id, scope, record.sourceThreadId);
6568
8006
  if ((0, _mastra_core_storage.knowledgeScopeKey)(record.scope) !== (0, _mastra_core_storage.knowledgeScopeKey)(scope)) await this.#outbox(tx, "record", input.id, "delete", (0, _mastra_core_storage.createKnowledgeUlid)(), record.scope);
6569
8007
  if (!record.deletedAt) await this.#outbox(tx, "record", input.id, "upsert", (0, _mastra_core_storage.createKnowledgeUlid)(), scope);
@@ -6573,32 +8011,16 @@ var KnowledgeLibSQL = class extends _mastra_core_storage.KnowledgeStorage {
6573
8011
  };
6574
8012
  });
6575
8013
  }
6576
- async raiseKnowledgeCeiling(input) {
6577
- return this.#transaction(async (tx) => {
6578
- const record = await this.#getKnowledge(tx, input.id, true);
6579
- if (!record) throw new _mastra_core_storage.KnowledgeNotFoundError("record", input.id);
6580
- (0, _mastra_core_storage.assertKnowledgeScopeWithinCeiling)(record.scope, input.maxScope);
6581
- (0, _mastra_core_storage.assertKnowledgeCeilingRaised)(record.maxScope, input.maxScope);
6582
- await tx.execute({
6583
- sql: `UPDATE "${_mastra_core_storage.TABLE_KNOWLEDGE_RECORDS}" SET maxScope=? WHERE id=?`,
6584
- args: [input.maxScope ?? null, input.id]
6585
- });
6586
- return {
6587
- ...record,
6588
- maxScope: input.maxScope
6589
- };
6590
- });
6591
- }
6592
8014
  async search(input) {
6593
- const key = (0, _mastra_core_storage.knowledgeScopeKey)((0, _mastra_core_storage.canonicalizeKnowledgeScope)(input.scope));
8015
+ const scope = (0, _mastra_core_storage.canonicalizeKnowledgeScope)(input.scope);
8016
+ const keys = visibleScopeKeys(scope);
6594
8017
  const normalizedQuery = input.query.trim().toLocaleLowerCase();
6595
8018
  if (!normalizedQuery) return [];
6596
8019
  const query = `%${escapeLikePattern(normalizedQuery)}%`;
6597
8020
  const results = (await this.#client.execute({
6598
- sql: `SELECT *,json(scope) AS scopeJson FROM "${_mastra_core_storage.TABLE_KNOWLEDGE_NODES}" WHERE mergedInto IS NULL AND ${visibleSql} AND (canonicalName LIKE ? ESCAPE '=' OR lower(COALESCE(kind,'')) LIKE ? ESCAPE '=' OR lower(COALESCE(content,'')) LIKE ? ESCAPE '=' OR lower(COALESCE(description,'')) LIKE ? ESCAPE '=') ORDER BY updatedAt DESC LIMIT ?`,
8021
+ sql: `SELECT *,json(scope) AS scopeJson FROM "${_mastra_core_storage.TABLE_KNOWLEDGE_NODES}" WHERE mergedInto IS NULL AND ${visibleSql(keys)} AND (canonicalName LIKE ? ESCAPE '=' OR lower(COALESCE(kind,'')) LIKE ? ESCAPE '=' OR lower(COALESCE(content,'')) LIKE ? ESCAPE '=' OR lower(COALESCE(description,'')) LIKE ? ESCAPE '=') ORDER BY updatedAt DESC LIMIT ?`,
6599
8022
  args: [
6600
- key,
6601
- key,
8023
+ ...keys,
6602
8024
  query,
6603
8025
  query,
6604
8026
  query,
@@ -6619,60 +8041,47 @@ var KnowledgeLibSQL = class extends _mastra_core_storage.KnowledgeStorage {
6619
8041
  }));
6620
8042
  if (results.length < (input.limit ?? 20)) {
6621
8043
  const knowledge = await this.#client.execute({
6622
- sql: `SELECT k.*,json(k.scope) AS scopeJson,json(k.metadata) AS metadataJson,n.name FROM "${_mastra_core_storage.TABLE_KNOWLEDGE_RECORDS}" k JOIN "${_mastra_core_storage.TABLE_KNOWLEDGE_NODES}" n ON n.id=k.node AND n.type='node' AND n.mergedInto IS NULL WHERE k.deletedAt IS NULL AND ${visibleSql.replaceAll("scopeKey", "k.scopeKey")} AND lower(k.text) LIKE ? ESCAPE '=' ORDER BY k.id DESC LIMIT ?`,
8044
+ sql: `SELECT k.*,json(k.scope) AS scopeJson,json(k.metadata) AS metadataJson,n.name,json(n.scope) AS parentScopeJson FROM "${_mastra_core_storage.TABLE_KNOWLEDGE_RECORDS}" k CROSS JOIN "${_mastra_core_storage.TABLE_KNOWLEDGE_NODES}" n ON n.id=k.node AND n.type='node' AND n.mergedInto IS NULL WHERE k.deletedAt IS NULL AND ${visibleSql(keys, "k.scopeKey")} AND lower(k.text) LIKE ? ESCAPE '=' ORDER BY k.id DESC LIMIT ?`,
6623
8045
  args: [
6624
- key,
6625
- key,
8046
+ ...keys,
6626
8047
  query,
6627
8048
  (input.limit ?? 20) - results.length
6628
8049
  ]
6629
8050
  });
6630
- results.push(...knowledge.rows.map((row) => ({
6631
- type: "record",
6632
- id: String(row.id),
6633
- recordId: String(row.node),
6634
- name: String(row.name),
6635
- text: String(row.text),
6636
- scope: parseJson$3(row.scopeJson)
6637
- })));
8051
+ results.push(...knowledge.rows.map((row) => {
8052
+ const parentVisible = (0, _mastra_core_storage.isKnowledgeScopeVisible)(parseJson$3(row.parentScopeJson), scope);
8053
+ return {
8054
+ type: "record",
8055
+ id: String(row.id),
8056
+ recordId: parentVisible ? String(row.node) : String(row.id),
8057
+ name: parentVisible ? String(row.name) : "(private node)",
8058
+ text: String(row.text),
8059
+ scope: parseJson$3(row.scopeJson)
8060
+ };
8061
+ }));
6638
8062
  }
6639
8063
  return results;
6640
8064
  }
6641
- async getCurationCursor(input) {
6642
- const row = (await this.#client.execute({
6643
- sql: `SELECT * FROM "${_mastra_core_storage.TABLE_KNOWLEDGE_CURSORS}" WHERE sourceThreadId=? AND agent=?`,
6644
- args: [input.sourceThreadId, input.agent]
6645
- })).rows[0];
6646
- return row ? {
6647
- sourceThreadId: String(row.sourceThreadId),
6648
- agent: String(row.agent),
6649
- lastKnowledgeId: String(row.lastKnowledgeId),
6650
- updatedAt: toDate(row.updatedAt)
6651
- } : null;
6652
- }
6653
- async advanceCurationCursor(input) {
6654
- const updatedAt = /* @__PURE__ */ new Date();
6655
- if ((await this.#db.executeWriteOperationWithRetry(() => withClientWriteLock(this.#client, () => this.#client.execute({
6656
- sql: `INSERT INTO "${_mastra_core_storage.TABLE_KNOWLEDGE_CURSORS}" (sourceThreadId,agent,lastKnowledgeId,updatedAt) VALUES (?,?,?,?) ON CONFLICT(sourceThreadId,agent) DO UPDATE SET lastKnowledgeId=excluded.lastKnowledgeId,updatedAt=excluded.updatedAt WHERE excluded.lastKnowledgeId >= "${_mastra_core_storage.TABLE_KNOWLEDGE_CURSORS}".lastKnowledgeId`,
6657
- args: [
6658
- input.sourceThreadId,
6659
- input.agent,
6660
- input.lastKnowledgeId,
6661
- updatedAt.toISOString()
6662
- ]
6663
- })), "advance knowledge curation cursor")).rowsAffected === 0) throw new Error("Knowledge curation cursor cannot move backwards");
6664
- return {
6665
- ...input,
6666
- updatedAt
6667
- };
8065
+ /**
8066
+ * @deprecated Curation cursors were removed. Observation-time curate is the only Knowledge writer and needs no
8067
+ * cursor. Always throws.
8068
+ */
8069
+ async getCurationCursor(_input) {
8070
+ throw new Error(KNOWLEDGE_CURATION_CURSOR_REMOVED_MESSAGE);
8071
+ }
8072
+ /**
8073
+ * @deprecated Curation cursors were removed. Observation-time curate is the only Knowledge writer and needs no
8074
+ * cursor. Always throws.
8075
+ */
8076
+ async advanceCurationCursor(_input) {
8077
+ throw new Error(KNOWLEDGE_CURATION_CURSOR_REMOVED_MESSAGE);
6668
8078
  }
6669
8079
  async listActivity(input) {
6670
- const key = (0, _mastra_core_storage.knowledgeScopeKey)((0, _mastra_core_storage.canonicalizeKnowledgeScope)(input.scope));
8080
+ const keys = visibleScopeKeys((0, _mastra_core_storage.canonicalizeKnowledgeScope)(input.scope));
6671
8081
  return (await this.#client.execute({
6672
- sql: `SELECT *,json(scope) AS scopeJson FROM "${_mastra_core_storage.TABLE_KNOWLEDGE_ACTIVITY}" WHERE ${visibleSql}${input.after ? " AND id < ?" : ""} ORDER BY id DESC LIMIT ?`,
8082
+ sql: `SELECT *,json(scope) AS scopeJson FROM "${_mastra_core_storage.TABLE_KNOWLEDGE_ACTIVITY}" WHERE ${visibleSql(keys)}${input.after ? " AND id < ?" : ""} ORDER BY id DESC LIMIT ?`,
6673
8083
  args: [
6674
- key,
6675
- key,
8084
+ ...keys,
6676
8085
  ...input.after ? [input.after] : [],
6677
8086
  input.limit ?? 100
6678
8087
  ]
@@ -6694,9 +8103,9 @@ var KnowledgeLibSQL = class extends _mastra_core_storage.KnowledgeStorage {
6694
8103
  args.push(input.status);
6695
8104
  }
6696
8105
  if (input.scope) {
6697
- const key = (0, _mastra_core_storage.knowledgeScopeKey)((0, _mastra_core_storage.canonicalizeKnowledgeScope)(input.scope));
6698
- clauses.push(visibleSql);
6699
- args.push(key, key);
8106
+ const keys = visibleScopeKeys(input.scope);
8107
+ clauses.push(visibleSql(keys));
8108
+ args.push(...keys);
6700
8109
  }
6701
8110
  args.push(input.limit ?? 100);
6702
8111
  return (await this.#client.execute({
@@ -6715,9 +8124,9 @@ var KnowledgeLibSQL = class extends _mastra_core_storage.KnowledgeStorage {
6715
8124
  ];
6716
8125
  const args = [now.toISOString(), stale.toISOString()];
6717
8126
  if (input.scope) {
6718
- const key = (0, _mastra_core_storage.knowledgeScopeKey)((0, _mastra_core_storage.canonicalizeKnowledgeScope)(input.scope));
6719
- clauses.push(visibleSql);
6720
- args.push(key, key);
8127
+ const keys = visibleScopeKeys(input.scope);
8128
+ clauses.push(visibleSql(keys));
8129
+ args.push(...keys);
6721
8130
  }
6722
8131
  args.push(input.limit ?? 100);
6723
8132
  const ids = (await tx.execute({
@@ -6794,12 +8203,14 @@ var KnowledgeLibSQL = class extends _mastra_core_storage.KnowledgeStorage {
6794
8203
  return result.rows[0] ? parseNode(result.rows[0]) : null;
6795
8204
  }
6796
8205
  async #resolveNode(executor, name, scope) {
6797
- for (let length = scope.length; length > 0; length--) {
6798
- const node = await this.#getNodeByName(executor, name, scope.slice(0, length));
6799
- if (node) {
6800
- const terminal = await this.#resolveTerminalNode(executor, node.id);
6801
- if (terminal && (0, _mastra_core_storage.isKnowledgeScopeVisible)(terminal.scope, scope)) return terminal;
6802
- }
8206
+ const keys = visibleScopeKeys(scope);
8207
+ const candidates = (await executor.execute({
8208
+ sql: `SELECT *,json(scope) AS scopeJson FROM "${_mastra_core_storage.TABLE_KNOWLEDGE_NODES}" WHERE type='node' AND canonicalName=? AND (scopeKey IN (${keys.map(() => "?").join(",")}) OR mergedInto IS NOT NULL)`,
8209
+ args: [canonicalName(name), ...keys]
8210
+ })).rows.map(parseNode).sort((left, right) => right.scope.length - left.scope.length);
8211
+ for (const candidate of candidates) {
8212
+ const terminal = await this.#resolveTerminalNode(executor, candidate.id);
8213
+ if (terminal && (0, _mastra_core_storage.isKnowledgeScopeVisible)(terminal.scope, scope)) return terminal;
6803
8214
  }
6804
8215
  return null;
6805
8216
  }
@@ -6824,19 +8235,18 @@ var KnowledgeLibSQL = class extends _mastra_core_storage.KnowledgeStorage {
6824
8235
  const scope = (0, _mastra_core_storage.canonicalizeKnowledgeScope)(input.scope);
6825
8236
  const node = await this.#resolveTerminalNode(this.#client, nodeReferenceId(input.node));
6826
8237
  if (!node) return { records: [] };
6827
- const key = (0, _mastra_core_storage.knowledgeScopeKey)(scope);
8238
+ const keys = visibleScopeKeys(scope);
6828
8239
  const includesMentions = relationship !== "about";
6829
8240
  const relationSql = relationship === "about" ? "k.node=?" : relationship === "mentioning" ? "m.recordId=?" : "(k.node=? OR m.recordId=?)";
6830
8241
  const args = [
6831
8242
  node.id,
6832
8243
  ...relationship === "related" ? [node.id] : [],
6833
- key,
6834
- key
8244
+ ...keys
6835
8245
  ];
6836
8246
  if (input.after) args.push(input.after);
6837
8247
  args.push((input.limit ?? 100) + 1);
6838
8248
  const result = await this.#client.execute({
6839
- sql: `SELECT DISTINCT k.*,json(k.scope) AS scopeJson,json(k.metadata) AS metadataJson FROM "${_mastra_core_storage.TABLE_KNOWLEDGE_RECORDS}" k${includesMentions ? ` LEFT JOIN "${_mastra_core_storage.TABLE_KNOWLEDGE_MENTIONS}" m ON m.sourceType='record' AND m.sourceId=k.id` : ""} WHERE ${relationSql} AND ${visibleSql.replaceAll("scopeKey", "k.scopeKey")}${input.includeDeleted ? "" : " AND k.deletedAt IS NULL"}${input.after ? " AND k.id < ?" : ""} ORDER BY k.id DESC LIMIT ?`,
8249
+ sql: `SELECT DISTINCT k.*,json(k.scope) AS scopeJson,json(k.metadata) AS metadataJson FROM "${_mastra_core_storage.TABLE_KNOWLEDGE_RECORDS}" k${includesMentions ? ` LEFT JOIN "${_mastra_core_storage.TABLE_KNOWLEDGE_MENTIONS}" m ON m.sourceType='record' AND m.sourceId=k.id` : ""} WHERE ${relationSql} AND ${visibleSql(keys, "k.scopeKey")}${input.includeDeleted ? "" : " AND k.deletedAt IS NULL"}${input.after ? " AND k.id < ?" : ""} ORDER BY k.id DESC LIMIT ?`,
6840
8250
  args
6841
8251
  });
6842
8252
  const limit = input.limit ?? 100;
@@ -6846,6 +8256,90 @@ var KnowledgeLibSQL = class extends _mastra_core_storage.KnowledgeStorage {
6846
8256
  nextCursor: result.rows.length > limit ? String(rows.at(-1)?.id) : void 0
6847
8257
  };
6848
8258
  }
8259
+ async #resolveScopeNodeIds(executor, addresses) {
8260
+ const scopeNodeIds = /* @__PURE__ */ new Set();
8261
+ for (const address of addresses) {
8262
+ const result = await executor.execute({
8263
+ sql: `SELECT scopeNodeId FROM "${TABLE_KNOWLEDGE_SCOPE_ADDRESSES}" WHERE address=?`,
8264
+ args: [address]
8265
+ });
8266
+ if (result.rows[0]?.scopeNodeId != null) scopeNodeIds.add(String(result.rows[0].scopeNodeId));
8267
+ }
8268
+ return [...scopeNodeIds];
8269
+ }
8270
+ async #replaceNodeScopes(executor, nodeId, addresses, addedAt) {
8271
+ await executor.execute({
8272
+ sql: `DELETE FROM "${TABLE_KNOWLEDGE_NODE_SCOPES}" WHERE nodeId=?`,
8273
+ args: [nodeId]
8274
+ });
8275
+ for (const scopeNodeId of await this.#resolveScopeNodeIds(executor, addresses)) await executor.execute({
8276
+ sql: `INSERT OR IGNORE INTO "${TABLE_KNOWLEDGE_NODE_SCOPES}" (nodeId,scopeNodeId,addedAt) VALUES (?,?,?)`,
8277
+ args: [
8278
+ nodeId,
8279
+ scopeNodeId,
8280
+ addedAt.toISOString()
8281
+ ]
8282
+ });
8283
+ }
8284
+ /**
8285
+ * Move a node's identity memberships from `from` to `to` without touching structural
8286
+ * placements added by `#placeNodeInScopes`, which a scope change must not erase.
8287
+ */
8288
+ async #swapIdentityScopes(executor, nodeId, from, to, addedAt) {
8289
+ const next = await this.#resolveScopeNodeIds(executor, to);
8290
+ for (const scopeNodeId of await this.#resolveScopeNodeIds(executor, from)) {
8291
+ if (next.includes(scopeNodeId)) continue;
8292
+ await executor.execute({
8293
+ sql: `DELETE FROM "${TABLE_KNOWLEDGE_NODE_SCOPES}" WHERE nodeId=? AND scopeNodeId=?`,
8294
+ args: [nodeId, scopeNodeId]
8295
+ });
8296
+ }
8297
+ for (const scopeNodeId of next) await executor.execute({
8298
+ sql: `INSERT OR IGNORE INTO "${TABLE_KNOWLEDGE_NODE_SCOPES}" (nodeId,scopeNodeId,addedAt) VALUES (?,?,?)`,
8299
+ args: [
8300
+ nodeId,
8301
+ scopeNodeId,
8302
+ addedAt.toISOString()
8303
+ ]
8304
+ });
8305
+ }
8306
+ /**
8307
+ * Additive structural placement: every address must resolve to a live scope node
8308
+ * (unlike identity membership, an unknown address is a caller error, not a lazy
8309
+ * materialization miss). Runs inside the caller's transaction, so a throw aborts
8310
+ * the whole mutation.
8311
+ */
8312
+ async #placeNodeInScopes(executor, nodeId, addresses, addedAt) {
8313
+ for (const address of addresses ?? []) {
8314
+ const scopeNodeId = (await executor.execute({
8315
+ sql: `SELECT sa.scopeNodeId AS scopeNodeId FROM "${TABLE_KNOWLEDGE_SCOPE_ADDRESSES}" sa JOIN "${_mastra_core_storage.TABLE_KNOWLEDGE_NODES}" n ON n.id=sa.scopeNodeId WHERE sa.address=? AND n.isScope AND n.deletedAt IS NULL`,
8316
+ args: [address]
8317
+ })).rows[0]?.scopeNodeId;
8318
+ if (scopeNodeId == null) throw new _mastra_core_storage.KnowledgeNotFoundError("scope", address);
8319
+ await executor.execute({
8320
+ sql: `INSERT OR IGNORE INTO "${TABLE_KNOWLEDGE_NODE_SCOPES}" (nodeId,scopeNodeId,addedAt) VALUES (?,?,?)`,
8321
+ args: [
8322
+ nodeId,
8323
+ String(scopeNodeId),
8324
+ addedAt.toISOString()
8325
+ ]
8326
+ });
8327
+ }
8328
+ }
8329
+ async #replaceRecordScopes(executor, recordId, addresses, addedAt) {
8330
+ await executor.execute({
8331
+ sql: `DELETE FROM "${TABLE_KNOWLEDGE_RECORD_SCOPES}" WHERE recordId=?`,
8332
+ args: [recordId]
8333
+ });
8334
+ for (const scopeNodeId of await this.#resolveScopeNodeIds(executor, addresses)) await executor.execute({
8335
+ sql: `INSERT OR IGNORE INTO "${TABLE_KNOWLEDGE_RECORD_SCOPES}" (recordId,scopeNodeId,addedAt) VALUES (?,?,?)`,
8336
+ args: [
8337
+ recordId,
8338
+ scopeNodeId,
8339
+ addedAt.toISOString()
8340
+ ]
8341
+ });
8342
+ }
6849
8343
  async #replaceMentions(tx, sourceType, sourceId, text, resolutionScope, defaultScope) {
6850
8344
  await tx.execute({
6851
8345
  sql: `DELETE FROM "${_mastra_core_storage.TABLE_KNOWLEDGE_MENTIONS}" WHERE sourceType=? AND sourceId=?`,
@@ -6869,7 +8363,7 @@ var KnowledgeLibSQL = class extends _mastra_core_storage.KnowledgeStorage {
6869
8363
  updatedAt: now
6870
8364
  };
6871
8365
  await tx.execute({
6872
- sql: `INSERT INTO "${_mastra_core_storage.TABLE_KNOWLEDGE_NODES}" (id,type,name,canonicalName,kind,content,scope,scopeKey,version,mergedInto,createdAt,updatedAt) VALUES (?,?,?,?,?,NULL,jsonb(?),?,?,NULL,?,?)`,
8366
+ sql: `INSERT INTO "${_mastra_core_storage.TABLE_KNOWLEDGE_NODES}" (id,type,name,canonicalName,kind,content,isScope,scope,scopeKey,version,mergedInto,createdAt,updatedAt) VALUES (?,?,?,?,?,NULL,FALSE,jsonb(?),?,?,NULL,?,?)`,
6873
8367
  args: [
6874
8368
  node.id,
6875
8369
  "node",
@@ -6883,6 +8377,7 @@ var KnowledgeLibSQL = class extends _mastra_core_storage.KnowledgeStorage {
6883
8377
  now.toISOString()
6884
8378
  ]
6885
8379
  });
8380
+ await this.#replaceNodeScopes(tx, node.id, defaultScope, now);
6886
8381
  await this.#activity(tx, "node-created", "node", node.id, defaultScope);
6887
8382
  await this.#outbox(tx, "node", node.id, "upsert", 1, defaultScope);
6888
8383
  }
@@ -6899,13 +8394,17 @@ var KnowledgeLibSQL = class extends _mastra_core_storage.KnowledgeStorage {
6899
8394
  }
6900
8395
  async #activity(executor, action, recordType, recordId, scope, sourceThreadId) {
6901
8396
  const now = /* @__PURE__ */ new Date();
8397
+ const [contextScopeId] = await this.#resolveScopeNodeIds(executor, scope);
6902
8398
  await executor.execute({
6903
- sql: `INSERT INTO "${_mastra_core_storage.TABLE_KNOWLEDGE_ACTIVITY}" (id,action,recordType,recordId,scope,scopeKey,sourceThreadId,createdAt) VALUES (?,?,?,?,jsonb(?),?,?,?)`,
8399
+ sql: `INSERT INTO "${_mastra_core_storage.TABLE_KNOWLEDGE_ACTIVITY}" (id,action,targetType,targetId,contextScopeId,recordType,recordId,scope,scopeKey,sourceThreadId,createdAt) VALUES (?,?,?,?,?,?,?,jsonb(?),?,?,?)`,
6904
8400
  args: [
6905
8401
  (0, _mastra_core_storage.createKnowledgeUlid)(),
6906
8402
  action,
6907
8403
  recordType,
6908
8404
  recordId,
8405
+ contextScopeId ?? null,
8406
+ recordType,
8407
+ recordId,
6909
8408
  JSON.stringify(scope),
6910
8409
  (0, _mastra_core_storage.knowledgeScopeKey)(scope),
6911
8410
  sourceThreadId ?? null,
@@ -7832,6 +9331,10 @@ var MemoryLibSQL = class MemoryLibSQL extends _mastra_core_storage.MemoryStorage
7832
9331
  * Retention-eligible tables. `threads`, `messages`, and `resources` all anchor
7833
9332
  * on `createdAt` and are indexed for fast batched deletes. Cascade order is
7834
9333
  * enforced in `prune()` (children before threads), not here.
9334
+ *
9335
+ * Run fences anchor on `retiredAt` (epoch ms), which stays NULL until the
9336
+ * execution holding the fence settles, so a running execution's fence is
9337
+ * never pruned.
7835
9338
  */
7836
9339
  static retentionTables = {
7837
9340
  messages: {
@@ -7848,6 +9351,12 @@ var MemoryLibSQL = class MemoryLibSQL extends _mastra_core_storage.MemoryStorage
7848
9351
  table: _mastra_core_storage.TABLE_THREADS,
7849
9352
  column: "createdAt",
7850
9353
  indexed: true
9354
+ },
9355
+ runFences: {
9356
+ table: _mastra_core_storage.TABLE_MEMORY_RUN_FENCES,
9357
+ column: "retiredAt",
9358
+ indexed: true,
9359
+ anchorType: "epoch-ms"
7851
9360
  }
7852
9361
  };
7853
9362
  #client;
@@ -7870,6 +9379,13 @@ var MemoryLibSQL = class MemoryLibSQL extends _mastra_core_storage.MemoryStorage
7870
9379
  #write(operation, fn) {
7871
9380
  return this.#db.executeWriteOperationWithRetry(() => withClientWriteLock(this.#client, fn), operation);
7872
9381
  }
9382
+ /**
9383
+ * `#write` for a write that may carry a run fence: with a check, it runs in a write
9384
+ * transaction behind the check, and a retry reruns the whole transaction.
9385
+ */
9386
+ #fencedWrite(operation, check, write) {
9387
+ return this.#db.executeWriteOperationWithRetry(() => withRunFence(this.#client, check, write), operation);
9388
+ }
7873
9389
  async init() {
7874
9390
  await this.#db.createTable({
7875
9391
  tableName: _mastra_core_storage.TABLE_THREADS,
@@ -7883,6 +9399,10 @@ var MemoryLibSQL = class MemoryLibSQL extends _mastra_core_storage.MemoryStorage
7883
9399
  tableName: _mastra_core_storage.TABLE_RESOURCES,
7884
9400
  schema: _mastra_core_storage.TABLE_SCHEMAS[_mastra_core_storage.TABLE_RESOURCES]
7885
9401
  });
9402
+ await this.#db.createTable({
9403
+ tableName: _mastra_core_storage.TABLE_MEMORY_RUN_FENCES,
9404
+ schema: _mastra_core_storage.RUN_FENCING_TABLE_SCHEMAS[_mastra_core_storage.TABLE_MEMORY_RUN_FENCES]
9405
+ });
7886
9406
  const omSchema = _mastra_core_storage.OBSERVATIONAL_MEMORY_TABLE_SCHEMA?.[OM_TABLE];
7887
9407
  if (omSchema) {
7888
9408
  await this.#db.createTable({
@@ -7934,6 +9454,70 @@ var MemoryLibSQL = class MemoryLibSQL extends _mastra_core_storage.MemoryStorage
7934
9454
  await this.#db.deleteData({ tableName: _mastra_core_storage.TABLE_THREADS });
7935
9455
  await this.#db.deleteData({ tableName: _mastra_core_storage.TABLE_RESOURCES });
7936
9456
  await this.#db.deleteData({ tableName: OM_TABLE });
9457
+ await this.#db.deleteData({ tableName: _mastra_core_storage.TABLE_MEMORY_RUN_FENCES });
9458
+ }
9459
+ supportsRunFencing() {
9460
+ return true;
9461
+ }
9462
+ async raiseRunFence(fence) {
9463
+ try {
9464
+ return await this.#db.executeWriteOperationWithRetry(() => inWriteTransaction(this.#client, async (tx) => {
9465
+ const row = (await tx.execute({
9466
+ sql: `SELECT generation, ownerId FROM ${_mastra_core_storage.TABLE_MEMORY_RUN_FENCES} WHERE runId = ?`,
9467
+ args: [fence.runId]
9468
+ })).rows[0];
9469
+ const generation = row ? Number(row.generation) : void 0;
9470
+ const raises = generation === void 0 || generation < fence.generation;
9471
+ const reaffirms = generation === fence.generation && String(row.ownerId) === fence.ownerId;
9472
+ if (!raises && !reaffirms) return false;
9473
+ await tx.execute({
9474
+ sql: `INSERT INTO ${_mastra_core_storage.TABLE_MEMORY_RUN_FENCES} (runId, generation, ownerId) VALUES (?, ?, ?)
9475
+ ON CONFLICT(runId) DO UPDATE SET generation = excluded.generation, ownerId = excluded.ownerId, retiredAt = NULL`,
9476
+ args: [
9477
+ fence.runId,
9478
+ fence.generation,
9479
+ fence.ownerId
9480
+ ]
9481
+ });
9482
+ return true;
9483
+ }), "raiseRunFence");
9484
+ } catch (error) {
9485
+ throw new _mastra_core_error.MastraError({
9486
+ id: (0, _mastra_core_storage.createStorageErrorId)("LIBSQL", "RAISE_RUN_FENCE", "FAILED"),
9487
+ domain: _mastra_core_error.ErrorDomain.STORAGE,
9488
+ category: _mastra_core_error.ErrorCategory.THIRD_PARTY,
9489
+ details: { runId: fence.runId }
9490
+ }, error);
9491
+ }
9492
+ }
9493
+ async retireRunFence(fence) {
9494
+ try {
9495
+ return (await this.#db.executeWriteOperationWithRetry(() => withClientWriteLock(this.#client, () => this.#client.execute({
9496
+ sql: `UPDATE ${_mastra_core_storage.TABLE_MEMORY_RUN_FENCES} SET retiredAt = ${DB_NOW_MS}
9497
+ WHERE runId = ? AND generation = ? AND ownerId = ?`,
9498
+ args: [
9499
+ fence.runId,
9500
+ fence.generation,
9501
+ fence.ownerId
9502
+ ]
9503
+ })), "retireRunFence")).rowsAffected > 0;
9504
+ } catch (error) {
9505
+ throw new _mastra_core_error.MastraError({
9506
+ id: (0, _mastra_core_storage.createStorageErrorId)("LIBSQL", "RETIRE_RUN_FENCE", "FAILED"),
9507
+ domain: _mastra_core_error.ErrorDomain.STORAGE,
9508
+ category: _mastra_core_error.ErrorCategory.THIRD_PARTY,
9509
+ details: { runId: fence.runId }
9510
+ }, error);
9511
+ }
9512
+ }
9513
+ /** The check a write must pass: its own fence, otherwise the one in scope. */
9514
+ #runFenceCheck(fence, operation) {
9515
+ const resolved = (0, _mastra_core_storage.resolveRunFence)(this, fence);
9516
+ return resolved && {
9517
+ claimsTable: _mastra_core_storage.TABLE_MEMORY_RUN_FENCES,
9518
+ fence: resolved,
9519
+ operation
9520
+ };
7937
9521
  }
7938
9522
  /**
7939
9523
  * Delete rows older than each policy's `maxAge`, batched and cancellable.
@@ -7949,7 +9533,8 @@ var MemoryLibSQL = class MemoryLibSQL extends _mastra_core_storage.MemoryStorage
7949
9533
  order: [
7950
9534
  "messages",
7951
9535
  "resources",
7952
- "threads"
9536
+ "threads",
9537
+ "runFences"
7953
9538
  ]
7954
9539
  });
7955
9540
  return runPrune({
@@ -8331,7 +9916,7 @@ var MemoryLibSQL = class MemoryLibSQL extends _mastra_core_storage.MemoryStorage
8331
9916
  throw mastraError;
8332
9917
  }
8333
9918
  }
8334
- async saveMessages({ messages }) {
9919
+ async saveMessages({ messages, fence }) {
8335
9920
  if (messages.length === 0) return { messages };
8336
9921
  try {
8337
9922
  const threadId = messages[0]?.threadId;
@@ -8369,13 +9954,16 @@ var MemoryLibSQL = class MemoryLibSQL extends _mastra_core_storage.MemoryStorage
8369
9954
  const BATCH_SIZE = 50;
8370
9955
  const messageStatements = batchStatements.slice(0, -1);
8371
9956
  const threadUpdateStatement = batchStatements[batchStatements.length - 1];
8372
- for (let i = 0; i < messageStatements.length; i += BATCH_SIZE) {
8373
- const batch = messageStatements.slice(i, i + BATCH_SIZE);
8374
- if (batch.length > 0) await this.#write("saveMessages", () => this.#client.batch(batch, "write"));
8375
- }
8376
- if (threadUpdateStatement) await this.#write("saveMessages", () => this.#client.execute(threadUpdateStatement));
9957
+ await this.#fencedWrite("saveMessages", this.#runFenceCheck(fence, "saveMessages"), async (writer) => {
9958
+ for (let i = 0; i < messageStatements.length; i += BATCH_SIZE) {
9959
+ const batch = messageStatements.slice(i, i + BATCH_SIZE);
9960
+ if (batch.length > 0) await writer.batch(batch);
9961
+ }
9962
+ if (threadUpdateStatement) await writer.execute(threadUpdateStatement);
9963
+ });
8377
9964
  return { messages: new _mastra_core_agent.MessageList().add(messages, "memory").get.all.db() };
8378
9965
  } catch (error) {
9966
+ if ((0, _mastra_core_storage.isRunFenceConflictError)(error)) throw error;
8379
9967
  throw new _mastra_core_error.MastraError({
8380
9968
  id: (0, _mastra_core_storage.createStorageErrorId)("LIBSQL", "SAVE_MESSAGES", "FAILED"),
8381
9969
  domain: _mastra_core_error.ErrorDomain.STORAGE,
@@ -8383,11 +9971,14 @@ var MemoryLibSQL = class MemoryLibSQL extends _mastra_core_storage.MemoryStorage
8383
9971
  }, error);
8384
9972
  }
8385
9973
  }
8386
- async updateMessages({ messages }) {
9974
+ async updateMessages({ messages, fence }) {
8387
9975
  if (messages.length === 0) return [];
9976
+ return this.#fencedWrite("updateMessages", this.#runFenceCheck(fence, "updateMessages"), (writer) => this.#updateMessages(writer, messages));
9977
+ }
9978
+ async #updateMessages(writer, messages) {
8388
9979
  const messageIds = messages.map((m) => m.id);
8389
9980
  const selectSql = `SELECT * FROM ${_mastra_core_storage.TABLE_MESSAGES} WHERE id IN (${messageIds.map(() => "?").join(",")})`;
8390
- const existingMessages = (await this.#client.execute({
9981
+ const existingMessages = (await writer.execute({
8391
9982
  sql: selectSql,
8392
9983
  args: messageIds
8393
9984
  })).rows.map((row) => this.parseRow(row));
@@ -8439,48 +10030,44 @@ var MemoryLibSQL = class MemoryLibSQL extends _mastra_core_storage.MemoryStorage
8439
10030
  sql: `UPDATE ${_mastra_core_storage.TABLE_THREADS} SET updatedAt = ? WHERE id = ?`,
8440
10031
  args: [now, threadId]
8441
10032
  });
8442
- await this.#write("updateMessages", () => this.#client.batch(batchStatements, "write"));
8443
- return (await this.#client.execute({
10033
+ await writer.batch(batchStatements);
10034
+ return (await writer.execute({
8444
10035
  sql: selectSql,
8445
10036
  args: messageIds
8446
10037
  })).rows.map((row) => this.parseRow(row));
8447
10038
  }
8448
- async deleteMessages(messageIds) {
10039
+ async deleteMessages(messageIds, options) {
8449
10040
  if (!messageIds || messageIds.length === 0) return;
8450
10041
  try {
8451
10042
  const BATCH_SIZE = 100;
8452
- await this.#write("deleteMessages", async () => {
10043
+ const check = this.#runFenceCheck(options?.fence, "deleteMessages");
10044
+ await this.#db.executeWriteOperationWithRetry(() => inWriteTransaction(this.#client, async (tx) => {
10045
+ if (check) await assertRunFence(tx, check);
8453
10046
  const threadIds = /* @__PURE__ */ new Set();
8454
- const tx = await this.#client.transaction("write");
8455
- try {
8456
- for (let i = 0; i < messageIds.length; i += BATCH_SIZE) {
8457
- const batch = messageIds.slice(i, i + BATCH_SIZE);
8458
- const placeholders = batch.map(() => "?").join(",");
8459
- (await tx.execute({
8460
- sql: `SELECT DISTINCT thread_id FROM "${_mastra_core_storage.TABLE_MESSAGES}" WHERE id IN (${placeholders})`,
8461
- args: batch
8462
- })).rows?.forEach((row) => {
8463
- if (row.thread_id) threadIds.add(row.thread_id);
8464
- });
8465
- await tx.execute({
8466
- sql: `DELETE FROM "${_mastra_core_storage.TABLE_MESSAGES}" WHERE id IN (${placeholders})`,
8467
- args: batch
8468
- });
8469
- }
8470
- if (threadIds.size > 0) {
8471
- const now = (/* @__PURE__ */ new Date()).toISOString();
8472
- for (const threadId of threadIds) await tx.execute({
8473
- sql: `UPDATE "${_mastra_core_storage.TABLE_THREADS}" SET "updatedAt" = ? WHERE id = ?`,
8474
- args: [now, threadId]
8475
- });
8476
- }
8477
- await tx.commit();
8478
- } catch (error) {
8479
- await tx.rollback();
8480
- throw error;
10047
+ for (let i = 0; i < messageIds.length; i += BATCH_SIZE) {
10048
+ const batch = messageIds.slice(i, i + BATCH_SIZE);
10049
+ const placeholders = batch.map(() => "?").join(",");
10050
+ (await tx.execute({
10051
+ sql: `SELECT DISTINCT thread_id FROM "${_mastra_core_storage.TABLE_MESSAGES}" WHERE id IN (${placeholders})`,
10052
+ args: batch
10053
+ })).rows?.forEach((row) => {
10054
+ if (row.thread_id) threadIds.add(row.thread_id);
10055
+ });
10056
+ await tx.execute({
10057
+ sql: `DELETE FROM "${_mastra_core_storage.TABLE_MESSAGES}" WHERE id IN (${placeholders})`,
10058
+ args: batch
10059
+ });
8481
10060
  }
8482
- });
10061
+ if (threadIds.size > 0) {
10062
+ const now = (/* @__PURE__ */ new Date()).toISOString();
10063
+ for (const threadId of threadIds) await tx.execute({
10064
+ sql: `UPDATE "${_mastra_core_storage.TABLE_THREADS}" SET "updatedAt" = ? WHERE id = ?`,
10065
+ args: [now, threadId]
10066
+ });
10067
+ }
10068
+ }), "deleteMessages");
8483
10069
  } catch (error) {
10070
+ if ((0, _mastra_core_storage.isRunFenceConflictError)(error)) throw error;
8484
10071
  throw new _mastra_core_error.MastraError({
8485
10072
  id: (0, _mastra_core_storage.createStorageErrorId)("LIBSQL", "DELETE_MESSAGES", "FAILED"),
8486
10073
  domain: _mastra_core_error.ErrorDomain.STORAGE,
@@ -8504,13 +10091,18 @@ var MemoryLibSQL = class MemoryLibSQL extends _mastra_core_storage.MemoryStorage
8504
10091
  };
8505
10092
  }
8506
10093
  async saveResource({ resource }) {
10094
+ return this.#insertResource(resource, void 0);
10095
+ }
10096
+ async #insertResource(resource, check) {
8507
10097
  await this.#db.insert({
8508
10098
  tableName: _mastra_core_storage.TABLE_RESOURCES,
8509
- record: { ...resource }
10099
+ record: { ...resource },
10100
+ check
8510
10101
  });
8511
10102
  return resource;
8512
10103
  }
8513
- async updateResource({ resourceId, workingMemory, metadata }) {
10104
+ async updateResource({ resourceId, workingMemory, metadata, fence }) {
10105
+ const check = this.#runFenceCheck(fence, "updateResource");
8514
10106
  const existingResource = await this.getResourceById({ resourceId });
8515
10107
  if (!existingResource) {
8516
10108
  const newResource = {
@@ -8520,7 +10112,7 @@ var MemoryLibSQL = class MemoryLibSQL extends _mastra_core_storage.MemoryStorage
8520
10112
  createdAt: /* @__PURE__ */ new Date(),
8521
10113
  updatedAt: /* @__PURE__ */ new Date()
8522
10114
  };
8523
- return this.saveResource({ resource: newResource });
10115
+ return this.#insertResource(newResource, check);
8524
10116
  }
8525
10117
  const updatedResource = {
8526
10118
  ...existingResource,
@@ -8544,7 +10136,7 @@ var MemoryLibSQL = class MemoryLibSQL extends _mastra_core_storage.MemoryStorage
8544
10136
  updates.push("updatedAt = ?");
8545
10137
  values.push(updatedResource.updatedAt.toISOString());
8546
10138
  values.push(resourceId);
8547
- await this.#write("updateResource", () => this.#client.execute({
10139
+ await this.#fencedWrite("updateResource", check, (writer) => writer.execute({
8548
10140
  sql: `UPDATE ${_mastra_core_storage.TABLE_RESOURCES} SET ${updates.join(", ")} WHERE id = ?`,
8549
10141
  args: values
8550
10142
  }));
@@ -8748,14 +10340,16 @@ var MemoryLibSQL = class MemoryLibSQL extends _mastra_core_storage.MemoryStorage
8748
10340
  throw mastraError;
8749
10341
  }
8750
10342
  }
8751
- async saveThread({ thread }) {
10343
+ async saveThread({ thread, fence }) {
8752
10344
  try {
8753
10345
  await this.#db.insert({
8754
10346
  tableName: _mastra_core_storage.TABLE_THREADS,
8755
- record: { ...thread }
10347
+ record: { ...thread },
10348
+ check: this.#runFenceCheck(fence, "saveThread")
8756
10349
  });
8757
10350
  return thread;
8758
10351
  } catch (error) {
10352
+ if ((0, _mastra_core_storage.isRunFenceConflictError)(error)) throw error;
8759
10353
  const mastraError = new _mastra_core_error.MastraError({
8760
10354
  id: (0, _mastra_core_storage.createStorageErrorId)("LIBSQL", "SAVE_THREAD", "FAILED"),
8761
10355
  domain: _mastra_core_error.ErrorDomain.STORAGE,
@@ -8767,7 +10361,8 @@ var MemoryLibSQL = class MemoryLibSQL extends _mastra_core_storage.MemoryStorage
8767
10361
  throw mastraError;
8768
10362
  }
8769
10363
  }
8770
- async updateThread({ id, title, metadata }) {
10364
+ async updateThread({ id, title, metadata, fence }) {
10365
+ const check = this.#runFenceCheck(fence, "updateThread");
8771
10366
  const thread = await this.getThreadById({ threadId: id });
8772
10367
  if (!thread) throw new _mastra_core_error.MastraError({
8773
10368
  id: (0, _mastra_core_storage.createStorageErrorId)("LIBSQL", "UPDATE_THREAD", "NOT_FOUND"),
@@ -8790,7 +10385,7 @@ var MemoryLibSQL = class MemoryLibSQL extends _mastra_core_storage.MemoryStorage
8790
10385
  updatedAt: now
8791
10386
  };
8792
10387
  try {
8793
- await this.#write("updateThread", () => this.#client.execute({
10388
+ await this.#fencedWrite("updateThread", check, (writer) => writer.execute({
8794
10389
  sql: `UPDATE ${_mastra_core_storage.TABLE_THREADS} SET title = COALESCE(?, title), metadata = jsonb(?), updatedAt = ? WHERE id = ?`,
8795
10390
  args: [
8796
10391
  title ?? null,
@@ -8801,6 +10396,7 @@ var MemoryLibSQL = class MemoryLibSQL extends _mastra_core_storage.MemoryStorage
8801
10396
  }));
8802
10397
  return updatedThread;
8803
10398
  } catch (error) {
10399
+ if ((0, _mastra_core_storage.isRunFenceConflictError)(error)) throw error;
8804
10400
  throw new _mastra_core_error.MastraError({
8805
10401
  id: (0, _mastra_core_storage.createStorageErrorId)("LIBSQL", "UPDATE_THREAD", "FAILED"),
8806
10402
  domain: _mastra_core_error.ErrorDomain.STORAGE,
@@ -9211,7 +10807,7 @@ var MemoryLibSQL = class MemoryLibSQL extends _mastra_core_storage.MemoryStorage
9211
10807
  try {
9212
10808
  const now = /* @__PURE__ */ new Date();
9213
10809
  const observedMessageIdsJson = input.observedMessageIds ? JSON.stringify(input.observedMessageIds) : null;
9214
- if ((await this.#write("updateActiveObservations", () => this.#client.execute({
10810
+ if ((await this.#fencedWrite("updateActiveObservations", this.#runFenceCheck(void 0, "updateActiveObservations"), (writer) => writer.execute({
9215
10811
  sql: `UPDATE "${OM_TABLE}" SET
9216
10812
  "activeObservations" = ?,
9217
10813
  "lastObservedAt" = ?,
@@ -9249,9 +10845,11 @@ var MemoryLibSQL = class MemoryLibSQL extends _mastra_core_storage.MemoryStorage
9249
10845
  }
9250
10846
  async createReflectionGeneration(input) {
9251
10847
  try {
10848
+ const check = this.#runFenceCheck(void 0, "createReflectionGeneration");
9252
10849
  return await this.#write("createReflectionGeneration", async () => {
9253
10850
  const tx = await this.#client.transaction("write");
9254
10851
  try {
10852
+ if (check) await assertRunFence(tx, check);
9255
10853
  const record = await this.#insertReflectionGeneration(tx, input);
9256
10854
  await tx.commit();
9257
10855
  return record;
@@ -9588,9 +11186,11 @@ var MemoryLibSQL = class MemoryLibSQL extends _mastra_core_storage.MemoryStorage
9588
11186
  async updateBufferedObservations(input) {
9589
11187
  try {
9590
11188
  const nowStr = (/* @__PURE__ */ new Date()).toISOString();
11189
+ const check = this.#runFenceCheck(void 0, "updateBufferedObservations");
9591
11190
  await this.#write("updateBufferedObservations", async () => {
9592
11191
  const tx = await this.#client.transaction("write");
9593
11192
  try {
11193
+ if (check) await assertRunFence(tx, check);
9594
11194
  const current = await tx.execute({
9595
11195
  sql: `SELECT "bufferedObservationChunks" FROM "${OM_TABLE}" WHERE id = ?`,
9596
11196
  args: [input.id]
@@ -9669,9 +11269,11 @@ var MemoryLibSQL = class MemoryLibSQL extends _mastra_core_storage.MemoryStorage
9669
11269
  async swapBufferedToActive(input) {
9670
11270
  try {
9671
11271
  const nowStr = (/* @__PURE__ */ new Date()).toISOString();
11272
+ const check = this.#runFenceCheck(void 0, "swapBufferedToActive");
9672
11273
  return await this.#write("swapBufferedToActive", async () => {
9673
11274
  const tx = await this.#client.transaction("write");
9674
11275
  try {
11276
+ if (check) await assertRunFence(tx, check);
9675
11277
  const current = await tx.execute({
9676
11278
  sql: `SELECT * FROM "${OM_TABLE}" WHERE id = ?`,
9677
11279
  args: [input.id]
@@ -9818,7 +11420,7 @@ var MemoryLibSQL = class MemoryLibSQL extends _mastra_core_storage.MemoryStorage
9818
11420
  async updateBufferedReflection(input) {
9819
11421
  try {
9820
11422
  const nowStr = (/* @__PURE__ */ new Date()).toISOString();
9821
- if ((await this.#write("updateBufferedReflection", () => this.#client.execute({
11423
+ if ((await this.#fencedWrite("updateBufferedReflection", this.#runFenceCheck(void 0, "updateBufferedReflection"), (writer) => writer.execute({
9822
11424
  sql: `UPDATE "${OM_TABLE}" SET
9823
11425
  "bufferedReflection" = CASE
9824
11426
  WHEN "bufferedReflection" IS NOT NULL AND "bufferedReflection" != ''
@@ -9858,9 +11460,11 @@ var MemoryLibSQL = class MemoryLibSQL extends _mastra_core_storage.MemoryStorage
9858
11460
  }
9859
11461
  async swapBufferedReflectionToActive(input) {
9860
11462
  try {
11463
+ const check = this.#runFenceCheck(void 0, "swapBufferedReflectionToActive");
9861
11464
  return await this.#write("swapBufferedReflectionToActive", async () => {
9862
11465
  const tx = await this.#client.transaction("write");
9863
11466
  try {
11467
+ if (check) await assertRunFence(tx, check);
9864
11468
  const current = await tx.execute({
9865
11469
  sql: `SELECT * FROM "${OM_TABLE}" WHERE id = ?`,
9866
11470
  args: [input.currentRecord.id]
@@ -13360,16 +14964,39 @@ var WorkflowDefinitionsLibSQL = class extends _mastra_core_storage.WorkflowDefin
13360
14964
  };
13361
14965
  //#endregion
13362
14966
  //#region src/storage/domains/workflows/index.ts
14967
+ const RUN_OWNER_COLUMNS = `generation, ownerId, leaseExpiresAt, ${DB_NOW_MS} AS nowMs`;
14968
+ function toRunOwnershipRecord(runId, row) {
14969
+ const leaseExpiresAt = row.leaseExpiresAt === null ? null : Number(row.leaseExpiresAt);
14970
+ return {
14971
+ runId,
14972
+ generation: Number(row.generation),
14973
+ ownerId: String(row.ownerId),
14974
+ leaseExpiresAt: leaseExpiresAt === null ? null : new Date(leaseExpiresAt),
14975
+ live: leaseExpiresAt !== null && leaseExpiresAt > Number(row.nowMs)
14976
+ };
14977
+ }
13363
14978
  var WorkflowsLibSQL = class WorkflowsLibSQL extends _mastra_core_storage.WorkflowsStorage {
13364
14979
  /**
13365
14980
  * Workflow run snapshots accumulate as runs execute. Anchored on `updatedAt`
13366
14981
  * (last activity) so suspended/long-running runs are not pruned by start age.
14982
+ *
14983
+ * Run ownership rows are anchored on `updatedAt` (epoch ms), which every
14984
+ * claim, renewal and release refreshes, so a live execution's row is never
14985
+ * older than its renew interval.
13367
14986
  */
13368
- static retentionTables = { workflowSnapshot: {
13369
- table: _mastra_core_storage.TABLE_WORKFLOW_SNAPSHOT,
13370
- column: "updatedAt",
13371
- indexed: true
13372
- } };
14987
+ static retentionTables = {
14988
+ workflowSnapshot: {
14989
+ table: _mastra_core_storage.TABLE_WORKFLOW_SNAPSHOT,
14990
+ column: "updatedAt",
14991
+ indexed: true
14992
+ },
14993
+ runOwnership: {
14994
+ table: _mastra_core_storage.TABLE_WORKFLOW_RUN_OWNERS,
14995
+ column: "updatedAt",
14996
+ indexed: true,
14997
+ anchorType: "epoch-ms"
14998
+ }
14999
+ };
13373
15000
  #db;
13374
15001
  #client;
13375
15002
  executeWithRetry;
@@ -13421,16 +15048,132 @@ var WorkflowsLibSQL = class WorkflowsLibSQL extends _mastra_core_storage.Workflo
13421
15048
  schema,
13422
15049
  ifNotExists: ["resourceId"]
13423
15050
  });
15051
+ await this.#db.createTable({
15052
+ tableName: _mastra_core_storage.TABLE_WORKFLOW_RUN_OWNERS,
15053
+ schema: _mastra_core_storage.RUN_FENCING_TABLE_SCHEMAS[_mastra_core_storage.TABLE_WORKFLOW_RUN_OWNERS]
15054
+ });
13424
15055
  }
13425
15056
  async dangerouslyClearAll() {
13426
15057
  await this.#db.deleteData({ tableName: _mastra_core_storage.TABLE_WORKFLOW_SNAPSHOT });
15058
+ await this.#db.deleteData({ tableName: _mastra_core_storage.TABLE_WORKFLOW_RUN_OWNERS });
15059
+ }
15060
+ supportsRunFencing() {
15061
+ return true;
15062
+ }
15063
+ #runFenceCheck(fence, runId, operation) {
15064
+ const resolved = (0, _mastra_core_storage.resolveRunFence)(this, fence, runId);
15065
+ return resolved && {
15066
+ claimsTable: _mastra_core_storage.TABLE_WORKFLOW_RUN_OWNERS,
15067
+ fence: resolved,
15068
+ operation
15069
+ };
15070
+ }
15071
+ async #readRunOwner(q, runId) {
15072
+ const row = (await q.execute({
15073
+ sql: `SELECT ${RUN_OWNER_COLUMNS} FROM ${_mastra_core_storage.TABLE_WORKFLOW_RUN_OWNERS} WHERE runId = ?`,
15074
+ args: [runId]
15075
+ })).rows[0];
15076
+ return row ? toRunOwnershipRecord(runId, row) : null;
15077
+ }
15078
+ #ownershipError(operation, runId, error) {
15079
+ return new _mastra_core_error.MastraError({
15080
+ id: (0, _mastra_core_storage.createStorageErrorId)("LIBSQL", operation, "FAILED"),
15081
+ domain: _mastra_core_error.ErrorDomain.STORAGE,
15082
+ category: _mastra_core_error.ErrorCategory.THIRD_PARTY,
15083
+ details: { runId }
15084
+ }, error);
15085
+ }
15086
+ async claimRunOwnership({ runId, ownerId, leaseMs, force, expectedGeneration }) {
15087
+ try {
15088
+ return await this.executeWithRetry(() => inWriteTransaction(this.#client, async (tx) => {
15089
+ const current = await this.#readRunOwner(tx, runId);
15090
+ const generation = current?.generation ?? 0;
15091
+ if (expectedGeneration !== void 0 && expectedGeneration !== generation) return {
15092
+ acquired: false,
15093
+ record: current
15094
+ };
15095
+ if (current?.live && !force) return {
15096
+ acquired: false,
15097
+ record: current
15098
+ };
15099
+ await tx.execute({
15100
+ sql: `INSERT INTO ${_mastra_core_storage.TABLE_WORKFLOW_RUN_OWNERS} (runId, generation, ownerId, leaseExpiresAt, updatedAt)
15101
+ VALUES (?, COALESCE(?, MAX(1, ${DB_NOW_MS})), ?, ${DB_NOW_MS} + ?, ${DB_NOW_MS})
15102
+ ON CONFLICT(runId) DO UPDATE SET
15103
+ generation = excluded.generation,
15104
+ ownerId = excluded.ownerId,
15105
+ leaseExpiresAt = excluded.leaseExpiresAt,
15106
+ updatedAt = excluded.updatedAt`,
15107
+ args: [
15108
+ runId,
15109
+ current ? generation + 1 : null,
15110
+ ownerId,
15111
+ leaseMs
15112
+ ]
15113
+ });
15114
+ return {
15115
+ acquired: true,
15116
+ record: await this.#readRunOwner(tx, runId)
15117
+ };
15118
+ }), "claimRunOwnership");
15119
+ } catch (error) {
15120
+ throw this.#ownershipError("CLAIM_RUN_OWNERSHIP", runId, error);
15121
+ }
15122
+ }
15123
+ async renewRunOwnership({ leaseMs, ...fence }) {
15124
+ try {
15125
+ return await this.executeWithRetry(() => inWriteTransaction(this.#client, async (tx) => {
15126
+ const renewed = await tx.execute({
15127
+ sql: `UPDATE ${_mastra_core_storage.TABLE_WORKFLOW_RUN_OWNERS} SET leaseExpiresAt = ${DB_NOW_MS} + ?, updatedAt = ${DB_NOW_MS}
15128
+ WHERE runId = ? AND generation = ? AND ownerId = ? AND leaseExpiresAt IS NOT NULL`,
15129
+ args: [
15130
+ leaseMs,
15131
+ fence.runId,
15132
+ fence.generation,
15133
+ fence.ownerId
15134
+ ]
15135
+ });
15136
+ const record = await this.#readRunOwner(tx, fence.runId);
15137
+ return renewed.rowsAffected > 0 ? {
15138
+ renewed: true,
15139
+ record
15140
+ } : {
15141
+ renewed: false,
15142
+ record
15143
+ };
15144
+ }), "renewRunOwnership");
15145
+ } catch (error) {
15146
+ throw this.#ownershipError("RENEW_RUN_OWNERSHIP", fence.runId, error);
15147
+ }
15148
+ }
15149
+ async releaseRunOwnership(fence) {
15150
+ try {
15151
+ return (await this.executeWithRetry(() => withClientWriteLock(this.#client, () => this.#client.execute({
15152
+ sql: `UPDATE ${_mastra_core_storage.TABLE_WORKFLOW_RUN_OWNERS} SET leaseExpiresAt = NULL, updatedAt = ${DB_NOW_MS}
15153
+ WHERE runId = ? AND generation = ? AND ownerId = ?`,
15154
+ args: [
15155
+ fence.runId,
15156
+ fence.generation,
15157
+ fence.ownerId
15158
+ ]
15159
+ })), "releaseRunOwnership")).rowsAffected > 0;
15160
+ } catch (error) {
15161
+ throw this.#ownershipError("RELEASE_RUN_OWNERSHIP", fence.runId, error);
15162
+ }
13427
15163
  }
13428
- /** Delete workflow snapshots older than the `workflowSnapshot` policy's `maxAge`, batched. */
15164
+ async getRunOwnership({ runId }) {
15165
+ try {
15166
+ return await this.#readRunOwner(this.#client, runId);
15167
+ } catch (error) {
15168
+ throw this.#ownershipError("GET_RUN_OWNERSHIP", runId, error);
15169
+ }
15170
+ }
15171
+ /** Delete workflow run snapshots and run ownership rows older than their policy's `maxAge`, batched. */
13429
15172
  async prune(policies, options) {
13430
15173
  const targets = resolveTargets({
13431
15174
  policies,
13432
15175
  descriptor: WorkflowsLibSQL.retentionTables,
13433
- order: ["workflowSnapshot"]
15176
+ order: ["workflowSnapshot", "runOwnership"]
13434
15177
  });
13435
15178
  return runPrune({
13436
15179
  db: this.#db,
@@ -13460,10 +15203,12 @@ var WorkflowsLibSQL = class WorkflowsLibSQL extends _mastra_core_storage.Workflo
13460
15203
  this.logger.warn("LibSQL Workflows: Failed to set PRAGMA settings.", err);
13461
15204
  }
13462
15205
  }
13463
- async updateWorkflowResults({ workflowName, runId, stepId, result, requestContext, state }) {
15206
+ async updateWorkflowResults({ workflowName, runId, stepId, result, requestContext, state, fence: explicitFence }) {
15207
+ const check = this.#runFenceCheck(explicitFence, runId, "updateWorkflowResults");
13464
15208
  return this.executeWithRetry(() => withClientWriteLock(this.#client, async () => {
13465
15209
  const tx = await this.#client.transaction("write");
13466
15210
  try {
15211
+ if (check) await assertRunFence(tx, check);
13467
15212
  const existingSnapshotResult = await tx.execute({
13468
15213
  sql: `SELECT json(snapshot) as snapshot FROM ${_mastra_core_storage.TABLE_WORKFLOW_SNAPSHOT} WHERE workflow_name = ? AND run_id = ?`,
13469
15214
  args: [workflowName, runId]
@@ -13516,10 +15261,12 @@ var WorkflowsLibSQL = class WorkflowsLibSQL extends _mastra_core_storage.Workflo
13516
15261
  }
13517
15262
  }), "updateWorkflowResults");
13518
15263
  }
13519
- async updateWorkflowState({ workflowName, runId, opts }) {
15264
+ async updateWorkflowState({ workflowName, runId, opts, fence: explicitFence }) {
15265
+ const check = this.#runFenceCheck(explicitFence, runId, "updateWorkflowState");
13520
15266
  return this.executeWithRetry(() => withClientWriteLock(this.#client, async () => {
13521
15267
  const tx = await this.#client.transaction("write");
13522
15268
  try {
15269
+ if (check) await assertRunFence(tx, check);
13523
15270
  const existingSnapshotResult = await tx.execute({
13524
15271
  sql: `SELECT json(snapshot) as snapshot FROM ${_mastra_core_storage.TABLE_WORKFLOW_SNAPSHOT} WHERE workflow_name = ? AND run_id = ?`,
13525
15272
  args: [workflowName, runId]
@@ -13560,7 +15307,7 @@ var WorkflowsLibSQL = class WorkflowsLibSQL extends _mastra_core_storage.Workflo
13560
15307
  }
13561
15308
  }), "updateWorkflowState");
13562
15309
  }
13563
- async persistWorkflowSnapshot({ workflowName, runId, resourceId, snapshot, createdAt, updatedAt }) {
15310
+ async persistWorkflowSnapshot({ workflowName, runId, resourceId, snapshot, createdAt, updatedAt, fence }) {
13564
15311
  const now = /* @__PURE__ */ new Date();
13565
15312
  const createdAtValue = (createdAt ?? now).toISOString();
13566
15313
  const updatedAtValue = (updatedAt ?? now).toISOString();
@@ -13568,7 +15315,8 @@ var WorkflowsLibSQL = class WorkflowsLibSQL extends _mastra_core_storage.Workflo
13568
15315
  workflowName,
13569
15316
  runId
13570
15317
  });
13571
- await this.executeWithRetry(() => withClientWriteLock(this.#client, () => this.#client.execute({
15318
+ const check = this.#runFenceCheck(fence, runId, "persistWorkflowSnapshot");
15319
+ await this.executeWithRetry(() => withRunFence(this.#client, check, (writer) => writer.execute({
13572
15320
  sql: `INSERT INTO ${_mastra_core_storage.TABLE_WORKFLOW_SNAPSHOT} (workflow_name, run_id, resourceId, snapshot, createdAt, updatedAt)
13573
15321
  VALUES (?, ?, ?, jsonb(?), ?, ?)
13574
15322
  ON CONFLICT(workflow_name, run_id)
@@ -13624,14 +15372,16 @@ var WorkflowsLibSQL = class WorkflowsLibSQL extends _mastra_core_storage.Workflo
13624
15372
  }, error);
13625
15373
  }
13626
15374
  }
13627
- async deleteWorkflowRunById({ runId, workflowName }) {
15375
+ async deleteWorkflowRunById({ runId, workflowName, fence }) {
15376
+ const check = this.#runFenceCheck(fence, runId, "deleteWorkflowRunById");
13628
15377
  return this.executeWithRetry(async () => {
13629
15378
  try {
13630
- await this.#client.execute({
15379
+ await withRunFence(this.#client, check, (writer) => writer.execute({
13631
15380
  sql: `DELETE FROM ${_mastra_core_storage.TABLE_WORKFLOW_SNAPSHOT} WHERE workflow_name = ? AND run_id = ?`,
13632
15381
  args: [workflowName, runId]
13633
- });
15382
+ }));
13634
15383
  } catch (error) {
15384
+ if ((0, _mastra_core_storage.isRunFenceConflictError)(error)) throw error;
13635
15385
  throw new _mastra_core_error.MastraError({
13636
15386
  id: (0, _mastra_core_storage.createStorageErrorId)("LIBSQL", "DELETE_WORKFLOW_RUN_BY_ID", "FAILED"),
13637
15387
  domain: _mastra_core_error.ErrorDomain.STORAGE,
@@ -14501,7 +16251,7 @@ var LibSQLFactoryStorage = class extends _mastra_core_storage.FactoryStorage {
14501
16251
  ...config.authToken ? { authToken: config.authToken } : {},
14502
16252
  ...isLocalDb ? { timeout: DEFAULT_CONNECTION_TIMEOUT_MS } : {}
14503
16253
  });
14504
- this.#client = isSingleConnectionDatabase(config) ? gateSingleConnectionClient(client) : client;
16254
+ this.#client = isSingleConnectionDatabase(config) ? gateSingleConnectionClient(client) : isLocalDb ? gateIfLocalFile(resetConnectionsAfterBusy(client, { maxWaitMs: DEFAULT_CONNECTION_TIMEOUT_MS }), config.url, { maxWaitMs: DEFAULT_CONNECTION_TIMEOUT_MS }) : client;
14505
16255
  this.ops = new LibSQLFactoryStorageOps(this.#client, this.#schemas, (fn) => withClientWriteLock(this.#client, fn));
14506
16256
  }
14507
16257
  getMastraStorage() {
@@ -14667,7 +16417,10 @@ var LibSQLStore = class extends _mastra_core_storage.MastraCompositeStore {
14667
16417
  ...config.syncInterval !== void 0 ? { syncInterval: config.syncInterval } : {},
14668
16418
  ...this.isLocalDb ? { timeout: this.connectionTimeoutMs } : {}
14669
16419
  });
14670
- this.client = isSingleConnectionDatabase(config) ? gateSingleConnectionClient(client) : client;
16420
+ this.client = isSingleConnectionDatabase(config) ? gateSingleConnectionClient(client) : this.isLocalDb ? gateIfLocalFile(resetConnectionsAfterBusy(client, {
16421
+ afterReset: () => this.applyLocalPragmas(client),
16422
+ maxWaitMs: this.connectionTimeoutMs
16423
+ }), config.url, { maxWaitMs: this.connectionTimeoutMs }) : client;
14671
16424
  this.pragmasReady = this.isLocalDb ? this.applyLocalPragmas() : Promise.resolve();
14672
16425
  } else {
14673
16426
  this.client = config.client;
@@ -14683,7 +16436,10 @@ var LibSQLStore = class extends _mastra_core_storage.MastraCompositeStore {
14683
16436
  const workflows = new WorkflowsLibSQL(domainConfig);
14684
16437
  const workflowDefinitions = new WorkflowDefinitionsLibSQL(domainConfig);
14685
16438
  const memory = new MemoryLibSQL(domainConfig);
14686
- const knowledge = new KnowledgeLibSQL(domainConfig);
16439
+ const knowledge = new KnowledgeLibSQL({
16440
+ ...domainConfig,
16441
+ storageIsolationKey: config.storageIsolationKey ?? getLibSQLKnowledgeIsolationKey(config, this.client)
16442
+ });
14687
16443
  const observability = new ObservabilityLibSQL(domainConfig);
14688
16444
  const agents = new AgentsLibSQL(domainConfig);
14689
16445
  const channels = new ChannelsLibSQL(domainConfig);
@@ -14730,7 +16486,7 @@ var LibSQLStore = class extends _mastra_core_storage.MastraCompositeStore {
14730
16486
  threadState
14731
16487
  };
14732
16488
  }
14733
- async applyLocalPragmas() {
16489
+ async applyLocalPragmas(client = this.client) {
14734
16490
  const pragmas = [
14735
16491
  ["journal_mode=WAL", "PRAGMA journal_mode=WAL;"],
14736
16492
  [`busy_timeout=${this.connectionTimeoutMs}`, `PRAGMA busy_timeout=${this.connectionTimeoutMs};`],
@@ -14740,14 +16496,14 @@ var LibSQLStore = class extends _mastra_core_storage.MastraCompositeStore {
14740
16496
  [`mmap_size=${this.localPragmas.mmapSize}`, `PRAGMA mmap_size=${this.localPragmas.mmapSize};`]
14741
16497
  ];
14742
16498
  for (const [label, sql] of pragmas) try {
14743
- await this.client.execute(sql);
16499
+ await client.execute(sql);
14744
16500
  this.logger.debug(`LibSQLStore: PRAGMA ${label} set.`);
14745
16501
  } catch (err) {
14746
16502
  this.logger.warn(`LibSQLStore: Failed to set PRAGMA ${label}.`, err);
14747
16503
  }
14748
16504
  }
14749
16505
  getStoresToInit() {
14750
- return Object.values(this.stores).filter(Boolean);
16506
+ return Object.entries(this.stores).filter(([name]) => name !== "knowledge").map(([, store]) => store).filter(Boolean);
14751
16507
  }
14752
16508
  async initDomainsSequentially() {
14753
16509
  for (const store of this.getStoresToInit()) await store.init();