@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.
- package/dist/_types/@internal_core/dist/knowledge-compat/index.d.ts +594 -0
- package/dist/_types/@internal_core/package.json +1 -0
- package/dist/docs/SKILL.md +1 -1
- package/dist/docs/assets/SOURCE_MAP.json +1 -1
- package/dist/docs/references/docs-memory-message-history.md +15 -8
- package/dist/docs/references/reference-core-mastra-class.md +12 -4
- package/dist/docs/references/reference-storage-retention.md +21 -18
- package/dist/index.cjs +2048 -292
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +2049 -293
- package/dist/index.js.map +1 -1
- package/dist/shared/file-transaction-gate.d.ts +28 -0
- package/dist/shared/file-transaction-gate.d.ts.map +1 -0
- package/dist/shared/reset-after-busy-client.d.ts +34 -0
- package/dist/shared/reset-after-busy-client.d.ts.map +1 -0
- package/dist/storage/db/index.d.ts +14 -6
- package/dist/storage/db/index.d.ts.map +1 -1
- package/dist/storage/db/run-fencing.d.ts +41 -0
- package/dist/storage/db/run-fencing.d.ts.map +1 -0
- package/dist/storage/db/utils.d.ts.map +1 -1
- package/dist/storage/domains/knowledge/index.d.ts +30 -7
- package/dist/storage/domains/knowledge/index.d.ts.map +1 -1
- package/dist/storage/domains/memory/index.d.ts +21 -7
- package/dist/storage/domains/memory/index.d.ts.map +1 -1
- package/dist/storage/domains/workflows/index.d.ts +21 -6
- package/dist/storage/domains/workflows/index.d.ts.map +1 -1
- package/dist/storage/factory-storage.d.ts.map +1 -1
- package/dist/storage/index.d.ts +6 -0
- package/dist/storage/index.d.ts.map +1 -1
- package/dist/storage/retention.d.ts +3 -3
- package/dist/storage/retention.d.ts.map +1 -1
- package/dist/vector/index.d.ts.map +1 -1
- 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
|
|
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
|
|
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
|
-
|
|
7101
|
+
const loadKnowledgeV2Core = createKnowledgeV2CoreLoader(_mastra_core_features.coreFeatures, () => import("@mastra/core/storage"));
|
|
6053
7102
|
async function assertKnowledgeDescriptionWithinBoundCompat(description) {
|
|
6054
|
-
|
|
6055
|
-
|
|
6056
|
-
|
|
6057
|
-
|
|
6058
|
-
|
|
6059
|
-
|
|
6060
|
-
|
|
6061
|
-
|
|
6062
|
-
|
|
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
|
-
|
|
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
|
-
|
|
6138
|
-
|
|
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.#
|
|
6147
|
-
|
|
6148
|
-
|
|
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
|
|
7337
|
+
await createTable({
|
|
6151
7338
|
tableName: _mastra_core_storage.TABLE_KNOWLEDGE_NODES,
|
|
6152
|
-
schema:
|
|
6153
|
-
ifNotExists: ["description"]
|
|
7339
|
+
schema: KNOWLEDGE_V2_NODES_SCHEMA
|
|
6154
7340
|
});
|
|
6155
|
-
await
|
|
7341
|
+
await createTable({
|
|
6156
7342
|
tableName: _mastra_core_storage.TABLE_KNOWLEDGE_RECORDS,
|
|
6157
|
-
schema:
|
|
7343
|
+
schema: KNOWLEDGE_V2_RECORDS_SCHEMA
|
|
6158
7344
|
});
|
|
6159
|
-
await
|
|
7345
|
+
await createTable({
|
|
6160
7346
|
tableName: _mastra_core_storage.TABLE_KNOWLEDGE_MENTIONS,
|
|
6161
|
-
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
|
-
"
|
|
6164
|
-
"
|
|
6165
|
-
"
|
|
7382
|
+
"importerId",
|
|
7383
|
+
"binding",
|
|
7384
|
+
"key"
|
|
6166
7385
|
]
|
|
6167
7386
|
});
|
|
6168
|
-
await
|
|
6169
|
-
tableName:
|
|
6170
|
-
schema:
|
|
6171
|
-
compositePrimaryKey: ["sourceThreadId", "agent"]
|
|
7387
|
+
await createTable({
|
|
7388
|
+
tableName: TABLE_KNOWLEDGE_IMPORT_RUNS,
|
|
7389
|
+
schema: KNOWLEDGE_IMPORT_RUNS_SCHEMA
|
|
6172
7390
|
});
|
|
6173
|
-
await
|
|
6174
|
-
tableName:
|
|
6175
|
-
schema:
|
|
7391
|
+
await createTable({
|
|
7392
|
+
tableName: TABLE_KNOWLEDGE_PROPOSALS,
|
|
7393
|
+
schema: KNOWLEDGE_PROPOSALS_SCHEMA
|
|
6176
7394
|
});
|
|
6177
|
-
await
|
|
6178
|
-
tableName: _mastra_core_storage.
|
|
6179
|
-
schema:
|
|
7395
|
+
await createTable({
|
|
7396
|
+
tableName: _mastra_core_storage.TABLE_KNOWLEDGE_ACTIVITY,
|
|
7397
|
+
schema: KNOWLEDGE_V2_ACTIVITY_SCHEMA
|
|
6180
7398
|
});
|
|
6181
|
-
await
|
|
6182
|
-
|
|
6183
|
-
|
|
6184
|
-
|
|
6185
|
-
|
|
6186
|
-
{
|
|
6187
|
-
|
|
6188
|
-
|
|
6189
|
-
},
|
|
6190
|
-
{
|
|
6191
|
-
|
|
6192
|
-
|
|
6193
|
-
},
|
|
6194
|
-
{
|
|
6195
|
-
|
|
6196
|
-
|
|
6197
|
-
},
|
|
6198
|
-
{
|
|
6199
|
-
|
|
6200
|
-
|
|
6201
|
-
},
|
|
6202
|
-
{
|
|
6203
|
-
|
|
6204
|
-
|
|
6205
|
-
},
|
|
6206
|
-
|
|
6207
|
-
|
|
6208
|
-
|
|
6209
|
-
|
|
6210
|
-
|
|
6211
|
-
|
|
6212
|
-
|
|
6213
|
-
|
|
6214
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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 = [
|
|
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: [
|
|
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",
|
|
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.
|
|
6468
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
6632
|
-
|
|
6633
|
-
|
|
6634
|
-
|
|
6635
|
-
|
|
6636
|
-
|
|
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
|
-
|
|
6642
|
-
|
|
6643
|
-
|
|
6644
|
-
|
|
6645
|
-
|
|
6646
|
-
|
|
6647
|
-
|
|
6648
|
-
|
|
6649
|
-
|
|
6650
|
-
|
|
6651
|
-
|
|
6652
|
-
|
|
6653
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
6698
|
-
clauses.push(visibleSql);
|
|
6699
|
-
args.push(
|
|
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
|
|
6719
|
-
clauses.push(visibleSql);
|
|
6720
|
-
args.push(
|
|
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
|
-
|
|
6798
|
-
|
|
6799
|
-
|
|
6800
|
-
|
|
6801
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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 (
|
|
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
|
-
|
|
8373
|
-
|
|
8374
|
-
|
|
8375
|
-
|
|
8376
|
-
|
|
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
|
|
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
|
|
8443
|
-
return (await
|
|
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
|
-
|
|
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
|
-
|
|
8455
|
-
|
|
8456
|
-
|
|
8457
|
-
|
|
8458
|
-
|
|
8459
|
-
|
|
8460
|
-
|
|
8461
|
-
|
|
8462
|
-
|
|
8463
|
-
|
|
8464
|
-
})
|
|
8465
|
-
|
|
8466
|
-
|
|
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
|
|
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.#
|
|
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.#
|
|
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.#
|
|
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.#
|
|
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 = {
|
|
13369
|
-
|
|
13370
|
-
|
|
13371
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
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
|
|
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.
|
|
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();
|