@mastra/libsql 1.14.3 → 1.15.0-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +26 -0
- package/dist/docs/SKILL.md +2 -1
- package/dist/docs/assets/SOURCE_MAP.json +1 -1
- package/dist/docs/references/docs-agent-builder-deploying.md +2 -0
- package/dist/docs/references/docs-agent-builder-overview.md +2 -0
- package/dist/docs/references/docs-agents-agent-approval.md +2 -0
- package/dist/docs/references/docs-agents-networks.md +2 -0
- package/dist/docs/references/docs-memory-memory-processors.md +4 -0
- package/dist/docs/references/docs-memory-message-history.md +2 -0
- package/dist/docs/references/docs-memory-multi-user-threads.md +2 -0
- package/dist/docs/references/docs-memory-overview.md +5 -1
- package/dist/docs/references/docs-memory-semantic-recall.md +8 -0
- package/dist/docs/references/docs-memory-storage.md +6 -0
- package/dist/docs/references/docs-memory-working-memory.md +2 -0
- package/dist/docs/references/docs-rag-retrieval.md +2 -0
- package/dist/docs/references/docs-workflows-snapshots.md +2 -0
- package/dist/docs/references/guides-agent-frameworks-ai-sdk.md +2 -0
- package/dist/docs/references/reference-core-getMemory.md +2 -0
- package/dist/docs/references/reference-core-listMemory.md +2 -0
- package/dist/docs/references/reference-core-mastra-class.md +2 -0
- package/dist/docs/references/reference-memory-memory-class.md +3 -0
- package/dist/docs/references/reference-storage-composite.md +2 -0
- package/dist/docs/references/reference-storage-dynamodb.md +2 -0
- package/dist/docs/references/reference-storage-libsql.md +2 -0
- package/dist/docs/references/reference-storage-retention.md +180 -0
- package/dist/docs/references/reference-vectors-libsql.md +2 -0
- package/dist/index.cjs +430 -10
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +431 -11
- package/dist/index.js.map +1 -1
- package/dist/storage/db/index.d.ts +51 -0
- package/dist/storage/db/index.d.ts.map +1 -1
- package/dist/storage/domains/background-tasks/index.d.ts +9 -0
- package/dist/storage/domains/background-tasks/index.d.ts.map +1 -1
- package/dist/storage/domains/experiments/index.d.ts +20 -1
- package/dist/storage/domains/experiments/index.d.ts.map +1 -1
- package/dist/storage/domains/harness/index.d.ts +5 -1
- package/dist/storage/domains/harness/index.d.ts.map +1 -1
- package/dist/storage/domains/memory/index.d.ts +15 -1
- package/dist/storage/domains/memory/index.d.ts.map +1 -1
- package/dist/storage/domains/notifications/index.d.ts +5 -1
- package/dist/storage/domains/notifications/index.d.ts.map +1 -1
- package/dist/storage/domains/observability/index.d.ts +8 -1
- package/dist/storage/domains/observability/index.d.ts.map +1 -1
- package/dist/storage/domains/schedules/index.d.ts +9 -1
- package/dist/storage/domains/schedules/index.d.ts.map +1 -1
- package/dist/storage/domains/scores/index.d.ts +7 -1
- package/dist/storage/domains/scores/index.d.ts.map +1 -1
- package/dist/storage/domains/thread-state/index.d.ts +9 -0
- package/dist/storage/domains/thread-state/index.d.ts.map +1 -1
- package/dist/storage/domains/workflows/index.d.ts +8 -1
- package/dist/storage/domains/workflows/index.d.ts.map +1 -1
- package/dist/storage/index.d.ts +8 -1
- package/dist/storage/index.d.ts.map +1 -1
- package/dist/storage/retention.d.ts +77 -0
- package/dist/storage/retention.d.ts.map +1 -0
- package/package.json +4 -4
package/dist/index.cjs
CHANGED
|
@@ -1458,6 +1458,11 @@ function resolveClient(config) {
|
|
|
1458
1458
|
...isLocal ? { timeout } : {}
|
|
1459
1459
|
});
|
|
1460
1460
|
}
|
|
1461
|
+
function assertPositiveLimit(limit) {
|
|
1462
|
+
if (!Number.isSafeInteger(limit) || limit <= 0) {
|
|
1463
|
+
throw new Error(`prune limit must be a positive integer; received ${limit}`);
|
|
1464
|
+
}
|
|
1465
|
+
}
|
|
1461
1466
|
var LibSQLDB = class extends base.MastraBase {
|
|
1462
1467
|
client;
|
|
1463
1468
|
maxRetries;
|
|
@@ -2278,6 +2283,102 @@ Note: This migration may take some time for large tables.
|
|
|
2278
2283
|
this.logger?.error?.(mastraError.toString());
|
|
2279
2284
|
}
|
|
2280
2285
|
}
|
|
2286
|
+
// ---------------------------------------------------------------------------
|
|
2287
|
+
// Retention helpers (prune)
|
|
2288
|
+
//
|
|
2289
|
+
// Low-level SQL primitives the memory and observability domains build their
|
|
2290
|
+
// `prune()` on, plus a plain user-invoked `VACUUM`. Keeping the SQL here
|
|
2291
|
+
// reuses the existing write-lock + retry machinery and keeps identifier
|
|
2292
|
+
// parsing in one place.
|
|
2293
|
+
// ---------------------------------------------------------------------------
|
|
2294
|
+
/**
|
|
2295
|
+
* Delete up to `limit` of the oldest rows whose `column` timestamp is strictly
|
|
2296
|
+
* before `cutoffMs`, in a single bounded statement. Returns the number of rows
|
|
2297
|
+
* removed so the caller can decide whether more batches remain.
|
|
2298
|
+
*
|
|
2299
|
+
* Deletes by `rowid` so the range scan is bounded by LIMIT and the write set
|
|
2300
|
+
* stays small — short locks, bounded WAL growth. Requires an index on
|
|
2301
|
+
* `column` to be fast at scale (see ensureIndex).
|
|
2302
|
+
*/
|
|
2303
|
+
async pruneBatch({
|
|
2304
|
+
tableName,
|
|
2305
|
+
column,
|
|
2306
|
+
cutoff,
|
|
2307
|
+
limit
|
|
2308
|
+
}) {
|
|
2309
|
+
assertPositiveLimit(limit);
|
|
2310
|
+
const parsedTable = utils.parseSqlIdentifier(tableName, "table name");
|
|
2311
|
+
const parsedColumn = utils.parseSqlIdentifier(column, "column name");
|
|
2312
|
+
const sql = `DELETE FROM "${parsedTable}" WHERE rowid IN (SELECT rowid FROM "${parsedTable}" WHERE "${parsedColumn}" < ? LIMIT ?)`;
|
|
2313
|
+
const result = await this.executeWriteOperationWithRetry(
|
|
2314
|
+
() => withClientWriteLock(this.client, () => this.client.execute({ sql, args: [cutoff, limit] })),
|
|
2315
|
+
`prune ${tableName}`
|
|
2316
|
+
);
|
|
2317
|
+
return Number(result.rowsAffected ?? 0);
|
|
2318
|
+
}
|
|
2319
|
+
/**
|
|
2320
|
+
* Delete up to `limit` aged parent rows *and* their child rows together, in a
|
|
2321
|
+
* single transaction. Used for whole-unit (parent-driven) pruning where
|
|
2322
|
+
* children must die with their parent (e.g. an aged experiment and its
|
|
2323
|
+
* experiment_results) — deleting per unit means a bound or abort between
|
|
2324
|
+
* batches never leaves a parent hollow (kept, but with its children gone) or
|
|
2325
|
+
* children orphaned.
|
|
2326
|
+
*
|
|
2327
|
+
* Both deletes target the same parent set via an identical deterministic
|
|
2328
|
+
* subquery (`ORDER BY rowid LIMIT ?`); the batch runs as one write
|
|
2329
|
+
* transaction, so the set cannot change between the two statements.
|
|
2330
|
+
*/
|
|
2331
|
+
async pruneUnitsBatch({
|
|
2332
|
+
parentTable,
|
|
2333
|
+
parentKey,
|
|
2334
|
+
parentColumn,
|
|
2335
|
+
childTable,
|
|
2336
|
+
childForeignKey,
|
|
2337
|
+
cutoff,
|
|
2338
|
+
limit
|
|
2339
|
+
}) {
|
|
2340
|
+
assertPositiveLimit(limit);
|
|
2341
|
+
const parsedChild = utils.parseSqlIdentifier(childTable, "table name");
|
|
2342
|
+
const parsedChildFk = utils.parseSqlIdentifier(childForeignKey, "column name");
|
|
2343
|
+
const parsedParent = utils.parseSqlIdentifier(parentTable, "table name");
|
|
2344
|
+
const parsedParentKey = utils.parseSqlIdentifier(parentKey, "column name");
|
|
2345
|
+
const parsedParentColumn = utils.parseSqlIdentifier(parentColumn, "column name");
|
|
2346
|
+
const agedParents = `SELECT "${parsedParentKey}" FROM "${parsedParent}" WHERE "${parsedParentColumn}" < ? ORDER BY rowid LIMIT ?`;
|
|
2347
|
+
const results = await this.executeWriteOperationWithRetry(
|
|
2348
|
+
() => withClientWriteLock(
|
|
2349
|
+
this.client,
|
|
2350
|
+
() => this.client.batch(
|
|
2351
|
+
[
|
|
2352
|
+
{
|
|
2353
|
+
sql: `DELETE FROM "${parsedChild}" WHERE "${parsedChildFk}" IN (${agedParents})`,
|
|
2354
|
+
args: [cutoff, limit]
|
|
2355
|
+
},
|
|
2356
|
+
{
|
|
2357
|
+
sql: `DELETE FROM "${parsedParent}" WHERE "${parsedParentKey}" IN (${agedParents})`,
|
|
2358
|
+
args: [cutoff, limit]
|
|
2359
|
+
}
|
|
2360
|
+
],
|
|
2361
|
+
"write"
|
|
2362
|
+
)
|
|
2363
|
+
),
|
|
2364
|
+
`prune units ${parentTable}`
|
|
2365
|
+
);
|
|
2366
|
+
return {
|
|
2367
|
+
children: Number(results[0]?.rowsAffected ?? 0),
|
|
2368
|
+
parents: Number(results[1]?.rowsAffected ?? 0)
|
|
2369
|
+
};
|
|
2370
|
+
}
|
|
2371
|
+
/** Create an index if it does not already exist. */
|
|
2372
|
+
async ensureIndex({
|
|
2373
|
+
indexName,
|
|
2374
|
+
tableName,
|
|
2375
|
+
column
|
|
2376
|
+
}) {
|
|
2377
|
+
const parsedTable = utils.parseSqlIdentifier(tableName, "table name");
|
|
2378
|
+
const parsedColumn = utils.parseSqlIdentifier(column, "column name");
|
|
2379
|
+
const parsedIndex = utils.parseSqlIdentifier(indexName, "index name");
|
|
2380
|
+
await this.client.execute(`CREATE INDEX IF NOT EXISTS "${parsedIndex}" ON "${parsedTable}" ("${parsedColumn}")`);
|
|
2381
|
+
}
|
|
2281
2382
|
};
|
|
2282
2383
|
var AgentsLibSQL = class extends storage.AgentsStorage {
|
|
2283
2384
|
#db;
|
|
@@ -3115,6 +3216,113 @@ var AgentsLibSQL = class extends storage.AgentsStorage {
|
|
|
3115
3216
|
};
|
|
3116
3217
|
}
|
|
3117
3218
|
};
|
|
3219
|
+
var DEFAULT_BATCH_SIZE = 1e3;
|
|
3220
|
+
async function ensureAnchorIndex(db, target, logger) {
|
|
3221
|
+
if (!target.indexed) return;
|
|
3222
|
+
try {
|
|
3223
|
+
await db.ensureIndex({
|
|
3224
|
+
indexName: `idx_retention_${target.table}_${target.column}`,
|
|
3225
|
+
tableName: target.table,
|
|
3226
|
+
column: target.column
|
|
3227
|
+
});
|
|
3228
|
+
} catch (error) {
|
|
3229
|
+
logger?.warn?.(`Failed to ensure retention index on ${target.table}(${target.column}):`, error);
|
|
3230
|
+
}
|
|
3231
|
+
}
|
|
3232
|
+
async function sleep(ms, signal) {
|
|
3233
|
+
if (ms <= 0 || signal?.aborted) return;
|
|
3234
|
+
await new Promise((resolve) => {
|
|
3235
|
+
const timer = setTimeout(() => {
|
|
3236
|
+
signal?.removeEventListener("abort", onAbort);
|
|
3237
|
+
resolve();
|
|
3238
|
+
}, ms);
|
|
3239
|
+
const onAbort = () => {
|
|
3240
|
+
clearTimeout(timer);
|
|
3241
|
+
resolve();
|
|
3242
|
+
};
|
|
3243
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
3244
|
+
});
|
|
3245
|
+
}
|
|
3246
|
+
function cutoffFor(policy, anchorType, now = Date.now()) {
|
|
3247
|
+
const cutoffMs = now - storage.parseDuration(policy.maxAge);
|
|
3248
|
+
return anchorType === "epoch-ms" ? cutoffMs : new Date(cutoffMs).toISOString();
|
|
3249
|
+
}
|
|
3250
|
+
async function runBatchedDelete({
|
|
3251
|
+
deleteBatch,
|
|
3252
|
+
batchSize,
|
|
3253
|
+
options
|
|
3254
|
+
}) {
|
|
3255
|
+
if (!Number.isSafeInteger(batchSize) || batchSize <= 0) {
|
|
3256
|
+
throw new Error(`retention batchSize must be a positive integer; received ${batchSize}`);
|
|
3257
|
+
}
|
|
3258
|
+
let deleted = 0;
|
|
3259
|
+
let batches = 0;
|
|
3260
|
+
while (true) {
|
|
3261
|
+
if (options?.signal?.aborted) return { deleted, done: false };
|
|
3262
|
+
if (options?.maxBatches !== void 0 && batches >= options.maxBatches) return { deleted, done: false };
|
|
3263
|
+
let limit = batchSize;
|
|
3264
|
+
if (options?.maxRows !== void 0) {
|
|
3265
|
+
const remaining = options.maxRows - deleted;
|
|
3266
|
+
if (remaining <= 0) return { deleted, done: false };
|
|
3267
|
+
limit = Math.min(limit, remaining);
|
|
3268
|
+
}
|
|
3269
|
+
const affected = await deleteBatch(limit);
|
|
3270
|
+
deleted += affected;
|
|
3271
|
+
batches += 1;
|
|
3272
|
+
if (affected < limit) return { deleted, done: true };
|
|
3273
|
+
if (options?.pauseMs) {
|
|
3274
|
+
await sleep(options.pauseMs, options.signal);
|
|
3275
|
+
}
|
|
3276
|
+
}
|
|
3277
|
+
}
|
|
3278
|
+
async function runPrune({
|
|
3279
|
+
db,
|
|
3280
|
+
domain,
|
|
3281
|
+
targets,
|
|
3282
|
+
options,
|
|
3283
|
+
logger
|
|
3284
|
+
}) {
|
|
3285
|
+
const results = [];
|
|
3286
|
+
const now = Date.now();
|
|
3287
|
+
for (const target of targets) {
|
|
3288
|
+
if (options?.signal?.aborted) {
|
|
3289
|
+
results.push({ domain, table: target.table, deleted: 0, done: false });
|
|
3290
|
+
continue;
|
|
3291
|
+
}
|
|
3292
|
+
await ensureAnchorIndex(db, target, logger);
|
|
3293
|
+
const cutoff = cutoffFor(target.policy, target.anchorType, now);
|
|
3294
|
+
const batchSize = target.policy.batchSize ?? DEFAULT_BATCH_SIZE;
|
|
3295
|
+
const { deleted, done } = await runBatchedDelete({
|
|
3296
|
+
deleteBatch: (limit) => db.pruneBatch({ tableName: target.table, column: target.column, cutoff, limit }),
|
|
3297
|
+
batchSize,
|
|
3298
|
+
options
|
|
3299
|
+
});
|
|
3300
|
+
results.push({ domain, table: target.table, deleted, done });
|
|
3301
|
+
}
|
|
3302
|
+
return results;
|
|
3303
|
+
}
|
|
3304
|
+
function resolveTargets({
|
|
3305
|
+
policies,
|
|
3306
|
+
descriptor,
|
|
3307
|
+
order
|
|
3308
|
+
}) {
|
|
3309
|
+
const targets = [];
|
|
3310
|
+
for (const key of order) {
|
|
3311
|
+
const policy = policies[key];
|
|
3312
|
+
const entry = descriptor[key];
|
|
3313
|
+
if (!policy || !entry) continue;
|
|
3314
|
+
targets.push({
|
|
3315
|
+
table: entry.table,
|
|
3316
|
+
column: entry.column,
|
|
3317
|
+
anchorType: entry.anchorType ?? "timestamp",
|
|
3318
|
+
indexed: entry.indexed ?? true,
|
|
3319
|
+
policy
|
|
3320
|
+
});
|
|
3321
|
+
}
|
|
3322
|
+
return targets;
|
|
3323
|
+
}
|
|
3324
|
+
|
|
3325
|
+
// src/storage/domains/background-tasks/index.ts
|
|
3118
3326
|
function serializeJson(v) {
|
|
3119
3327
|
if (typeof v === "object" && v != null) return JSON.stringify(v);
|
|
3120
3328
|
return v ?? null;
|
|
@@ -3153,7 +3361,15 @@ function rowToTask(row) {
|
|
|
3153
3361
|
completedAt: row.completedAt ? new Date(String(row.completedAt)) : void 0
|
|
3154
3362
|
};
|
|
3155
3363
|
}
|
|
3156
|
-
var BackgroundTasksLibSQL = class extends storage.BackgroundTasksStorage {
|
|
3364
|
+
var BackgroundTasksLibSQL = class _BackgroundTasksLibSQL extends storage.BackgroundTasksStorage {
|
|
3365
|
+
/**
|
|
3366
|
+
* Completed/failed task records accumulate. Anchored on `completedAt`, which
|
|
3367
|
+
* is NULL while a task is in-flight (pending/running/suspended) — so `completedAt
|
|
3368
|
+
* < cutoff` never prunes a live task, no explicit status filter needed.
|
|
3369
|
+
*/
|
|
3370
|
+
static retentionTables = {
|
|
3371
|
+
backgroundTasks: { table: storage.TABLE_BACKGROUND_TASKS, column: "completedAt", indexed: true }
|
|
3372
|
+
};
|
|
3157
3373
|
#db;
|
|
3158
3374
|
#client;
|
|
3159
3375
|
constructor(config) {
|
|
@@ -3176,6 +3392,15 @@ var BackgroundTasksLibSQL = class extends storage.BackgroundTasksStorage {
|
|
|
3176
3392
|
async dangerouslyClearAll() {
|
|
3177
3393
|
await this.#db.deleteData({ tableName: storage.TABLE_BACKGROUND_TASKS });
|
|
3178
3394
|
}
|
|
3395
|
+
/** Delete completed tasks older than the `backgroundTasks` policy's `maxAge`, batched. */
|
|
3396
|
+
async prune(policies, options) {
|
|
3397
|
+
const targets = resolveTargets({
|
|
3398
|
+
policies,
|
|
3399
|
+
descriptor: _BackgroundTasksLibSQL.retentionTables,
|
|
3400
|
+
order: ["backgroundTasks"]
|
|
3401
|
+
});
|
|
3402
|
+
return runPrune({ db: this.#db, domain: "backgroundTasks", targets, options, logger: this.logger });
|
|
3403
|
+
}
|
|
3179
3404
|
async createTask(task) {
|
|
3180
3405
|
await this.#db.insert({
|
|
3181
3406
|
tableName: storage.TABLE_BACKGROUND_TASKS,
|
|
@@ -4626,7 +4851,18 @@ var DatasetsLibSQL = class extends storage.DatasetsStorage {
|
|
|
4626
4851
|
}
|
|
4627
4852
|
}
|
|
4628
4853
|
};
|
|
4854
|
+
var DEFAULT_PRUNE_BATCH_SIZE = 1e3;
|
|
4629
4855
|
var ExperimentsLibSQL = class extends storage.ExperimentsStorage {
|
|
4856
|
+
/**
|
|
4857
|
+
* An experiment is pruned as a whole unit: when `experiments.completedAt` is
|
|
4858
|
+
* older than the policy, the run and all its `experiment_results` rows are
|
|
4859
|
+
* deleted together (results cascade with their parent, matching
|
|
4860
|
+
* `deleteExperiment`). Results are not an independent retention key. NULL
|
|
4861
|
+
* `completedAt` (still running) is never pruned.
|
|
4862
|
+
*/
|
|
4863
|
+
static retentionTables = {
|
|
4864
|
+
experiments: { table: storage.TABLE_EXPERIMENTS, column: "completedAt", indexed: true }
|
|
4865
|
+
};
|
|
4630
4866
|
#db;
|
|
4631
4867
|
#client;
|
|
4632
4868
|
constructor(config) {
|
|
@@ -4682,6 +4918,58 @@ var ExperimentsLibSQL = class extends storage.ExperimentsStorage {
|
|
|
4682
4918
|
await this.#db.deleteData({ tableName: storage.TABLE_EXPERIMENT_RESULTS });
|
|
4683
4919
|
await this.#db.deleteData({ tableName: storage.TABLE_EXPERIMENTS });
|
|
4684
4920
|
}
|
|
4921
|
+
/**
|
|
4922
|
+
* Prune whole experiments older than the `experiments` policy's `maxAge`.
|
|
4923
|
+
*
|
|
4924
|
+
* Each batch selects up to `batchSize` aged experiments and deletes their
|
|
4925
|
+
* `experiment_results` rows and the experiment rows in one transaction —
|
|
4926
|
+
* mirroring `deleteExperiment` — so hitting `maxBatches`/`maxRows` or the
|
|
4927
|
+
* abort signal between batches never leaves a run hollow (parent kept,
|
|
4928
|
+
* results gone). NULL `completedAt` (still running) is excluded by the
|
|
4929
|
+
* `< cutoff` predicate. Bounds count whole experiments, not rows.
|
|
4930
|
+
*/
|
|
4931
|
+
async prune(policies, options) {
|
|
4932
|
+
const policy = policies["experiments"];
|
|
4933
|
+
if (!policy || options?.signal?.aborted) {
|
|
4934
|
+
return policy ? [
|
|
4935
|
+
{ domain: "experiments", table: storage.TABLE_EXPERIMENT_RESULTS, deleted: 0, done: false },
|
|
4936
|
+
{ domain: "experiments", table: storage.TABLE_EXPERIMENTS, deleted: 0, done: false }
|
|
4937
|
+
] : [];
|
|
4938
|
+
}
|
|
4939
|
+
try {
|
|
4940
|
+
await this.#db.ensureIndex({
|
|
4941
|
+
indexName: `idx_retention_${storage.TABLE_EXPERIMENTS}_completedAt`,
|
|
4942
|
+
tableName: storage.TABLE_EXPERIMENTS,
|
|
4943
|
+
column: "completedAt"
|
|
4944
|
+
});
|
|
4945
|
+
} catch (error) {
|
|
4946
|
+
this.logger?.warn?.(`Failed to ensure retention index on ${storage.TABLE_EXPERIMENTS}(completedAt):`, error);
|
|
4947
|
+
}
|
|
4948
|
+
const cutoff = cutoffFor(policy, "timestamp");
|
|
4949
|
+
const batchSize = policy.batchSize ?? DEFAULT_PRUNE_BATCH_SIZE;
|
|
4950
|
+
let childDeleted = 0;
|
|
4951
|
+
const parent = await runBatchedDelete({
|
|
4952
|
+
deleteBatch: async (limit) => {
|
|
4953
|
+
const { parents, children } = await this.#db.pruneUnitsBatch({
|
|
4954
|
+
parentTable: storage.TABLE_EXPERIMENTS,
|
|
4955
|
+
parentKey: "id",
|
|
4956
|
+
parentColumn: "completedAt",
|
|
4957
|
+
childTable: storage.TABLE_EXPERIMENT_RESULTS,
|
|
4958
|
+
childForeignKey: "experimentId",
|
|
4959
|
+
cutoff,
|
|
4960
|
+
limit
|
|
4961
|
+
});
|
|
4962
|
+
childDeleted += children;
|
|
4963
|
+
return parents;
|
|
4964
|
+
},
|
|
4965
|
+
batchSize,
|
|
4966
|
+
options
|
|
4967
|
+
});
|
|
4968
|
+
return [
|
|
4969
|
+
{ domain: "experiments", table: storage.TABLE_EXPERIMENT_RESULTS, deleted: childDeleted, done: parent.done },
|
|
4970
|
+
{ domain: "experiments", table: storage.TABLE_EXPERIMENTS, deleted: parent.deleted, done: parent.done }
|
|
4971
|
+
];
|
|
4972
|
+
}
|
|
4685
4973
|
// Helper to transform row to Experiment
|
|
4686
4974
|
transformExperimentRow(row) {
|
|
4687
4975
|
return {
|
|
@@ -5569,7 +5857,11 @@ function sessionToRecord(record) {
|
|
|
5569
5857
|
deletedAt: record.deletedAt ?? null
|
|
5570
5858
|
};
|
|
5571
5859
|
}
|
|
5572
|
-
var HarnessLibSQL = class extends storage.HarnessStorage {
|
|
5860
|
+
var HarnessLibSQL = class _HarnessLibSQL extends storage.HarnessStorage {
|
|
5861
|
+
/** Session records accumulate over time. Single table, anchored on `createdAt`. */
|
|
5862
|
+
static retentionTables = {
|
|
5863
|
+
sessions: { table: storage.TABLE_HARNESS_SESSIONS, column: "createdAt", indexed: true }
|
|
5864
|
+
};
|
|
5573
5865
|
#db;
|
|
5574
5866
|
constructor(config) {
|
|
5575
5867
|
super();
|
|
@@ -5585,6 +5877,15 @@ var HarnessLibSQL = class extends storage.HarnessStorage {
|
|
|
5585
5877
|
async dangerouslyClearAll() {
|
|
5586
5878
|
await this.#db.deleteData({ tableName: storage.TABLE_HARNESS_SESSIONS });
|
|
5587
5879
|
}
|
|
5880
|
+
/** Delete harness sessions older than the `sessions` policy's `maxAge`, batched. */
|
|
5881
|
+
async prune(policies, options) {
|
|
5882
|
+
const targets = resolveTargets({
|
|
5883
|
+
policies,
|
|
5884
|
+
descriptor: _HarnessLibSQL.retentionTables,
|
|
5885
|
+
order: ["sessions"]
|
|
5886
|
+
});
|
|
5887
|
+
return runPrune({ db: this.#db, domain: "harness", targets, options, logger: this.logger });
|
|
5888
|
+
}
|
|
5588
5889
|
async loadSession(sessionId) {
|
|
5589
5890
|
const row = await this.#db.select({
|
|
5590
5891
|
tableName: storage.TABLE_HARNESS_SESSIONS,
|
|
@@ -6570,8 +6871,18 @@ var MCPServersLibSQL = class extends storage.MCPServersStorage {
|
|
|
6570
6871
|
}
|
|
6571
6872
|
};
|
|
6572
6873
|
var OM_TABLE = "mastra_observational_memory";
|
|
6573
|
-
var MemoryLibSQL = class extends storage.MemoryStorage {
|
|
6874
|
+
var MemoryLibSQL = class _MemoryLibSQL extends storage.MemoryStorage {
|
|
6574
6875
|
supportsObservationalMemory = true;
|
|
6876
|
+
/**
|
|
6877
|
+
* Retention-eligible tables. `threads`, `messages`, and `resources` all anchor
|
|
6878
|
+
* on `createdAt` and are indexed for fast batched deletes. Cascade order is
|
|
6879
|
+
* enforced in `prune()` (children before threads), not here.
|
|
6880
|
+
*/
|
|
6881
|
+
static retentionTables = {
|
|
6882
|
+
messages: { table: storage.TABLE_MESSAGES, column: "createdAt", indexed: true },
|
|
6883
|
+
resources: { table: storage.TABLE_RESOURCES, column: "createdAt", indexed: true },
|
|
6884
|
+
threads: { table: storage.TABLE_THREADS, column: "createdAt", indexed: true }
|
|
6885
|
+
};
|
|
6575
6886
|
#client;
|
|
6576
6887
|
#db;
|
|
6577
6888
|
constructor(config) {
|
|
@@ -6646,6 +6957,21 @@ var MemoryLibSQL = class extends storage.MemoryStorage {
|
|
|
6646
6957
|
await this.#db.deleteData({ tableName: OM_TABLE });
|
|
6647
6958
|
}
|
|
6648
6959
|
}
|
|
6960
|
+
/**
|
|
6961
|
+
* Delete rows older than each policy's `maxAge`, batched and cancellable.
|
|
6962
|
+
*
|
|
6963
|
+
* Deletes children before parents (messages/resources before threads) so a
|
|
6964
|
+
* `threads` policy doesn't strand rows behind a deleted parent — there are no
|
|
6965
|
+
* FKs in the LibSQL schema, so cascade ordering is explicit here.
|
|
6966
|
+
*/
|
|
6967
|
+
async prune(policies, options) {
|
|
6968
|
+
const targets = resolveTargets({
|
|
6969
|
+
policies,
|
|
6970
|
+
descriptor: _MemoryLibSQL.retentionTables,
|
|
6971
|
+
order: ["messages", "resources", "threads"]
|
|
6972
|
+
});
|
|
6973
|
+
return runPrune({ db: this.#db, domain: "memory", targets, options, logger: this.logger });
|
|
6974
|
+
}
|
|
6649
6975
|
parseRow(row) {
|
|
6650
6976
|
let content = row.content;
|
|
6651
6977
|
try {
|
|
@@ -8712,7 +9038,11 @@ function addArrayFilter(conditions, args, column, value) {
|
|
|
8712
9038
|
conditions.push(`"${column}" IN (${values.map(() => "?").join(", ")})`);
|
|
8713
9039
|
args.push(...values);
|
|
8714
9040
|
}
|
|
8715
|
-
var NotificationsLibSQL = class extends storage.NotificationsStorage {
|
|
9041
|
+
var NotificationsLibSQL = class _NotificationsLibSQL extends storage.NotificationsStorage {
|
|
9042
|
+
/** The notification feed grows unbounded. Single table, anchored on `createdAt`. */
|
|
9043
|
+
static retentionTables = {
|
|
9044
|
+
notifications: { table: storage.TABLE_NOTIFICATIONS, column: "createdAt", indexed: true }
|
|
9045
|
+
};
|
|
8716
9046
|
#db;
|
|
8717
9047
|
#client;
|
|
8718
9048
|
constructor(config) {
|
|
@@ -8747,6 +9077,15 @@ var NotificationsLibSQL = class extends storage.NotificationsStorage {
|
|
|
8747
9077
|
async dangerouslyClearAll() {
|
|
8748
9078
|
await this.#db.deleteData({ tableName: storage.TABLE_NOTIFICATIONS });
|
|
8749
9079
|
}
|
|
9080
|
+
/** Delete notifications older than the `notifications` policy's `maxAge`, batched. */
|
|
9081
|
+
async prune(policies, options) {
|
|
9082
|
+
const targets = resolveTargets({
|
|
9083
|
+
policies,
|
|
9084
|
+
descriptor: _NotificationsLibSQL.retentionTables,
|
|
9085
|
+
order: ["notifications"]
|
|
9086
|
+
});
|
|
9087
|
+
return runPrune({ db: this.#db, domain: "notifications", targets, options, logger: this.logger });
|
|
9088
|
+
}
|
|
8750
9089
|
async createNotification(input) {
|
|
8751
9090
|
const existing = await this.findCoalescable(input);
|
|
8752
9091
|
if (existing) {
|
|
@@ -8936,7 +9275,14 @@ var NotificationsLibSQL = class extends storage.NotificationsStorage {
|
|
|
8936
9275
|
return row ? rowToNotification(row) : void 0;
|
|
8937
9276
|
}
|
|
8938
9277
|
};
|
|
8939
|
-
var ObservabilityLibSQL = class extends storage.ObservabilityStorage {
|
|
9278
|
+
var ObservabilityLibSQL = class _ObservabilityLibSQL extends storage.ObservabilityStorage {
|
|
9279
|
+
/**
|
|
9280
|
+
* Spans are the only physical table; traces are derived from them. Spans
|
|
9281
|
+
* anchor on `startedAt` (not `createdAt`), stored as an ISO-8601 string.
|
|
9282
|
+
*/
|
|
9283
|
+
static retentionTables = {
|
|
9284
|
+
spans: { table: storage.TABLE_SPANS, column: "startedAt", indexed: true }
|
|
9285
|
+
};
|
|
8940
9286
|
#db;
|
|
8941
9287
|
constructor(config) {
|
|
8942
9288
|
super();
|
|
@@ -8954,6 +9300,15 @@ var ObservabilityLibSQL = class extends storage.ObservabilityStorage {
|
|
|
8954
9300
|
async dangerouslyClearAll() {
|
|
8955
9301
|
await this.#db.deleteData({ tableName: storage.TABLE_SPANS });
|
|
8956
9302
|
}
|
|
9303
|
+
/** Delete spans older than the `spans` policy's `maxAge`, batched. */
|
|
9304
|
+
async prune(policies, options) {
|
|
9305
|
+
const targets = resolveTargets({
|
|
9306
|
+
policies,
|
|
9307
|
+
descriptor: _ObservabilityLibSQL.retentionTables,
|
|
9308
|
+
order: ["spans"]
|
|
9309
|
+
});
|
|
9310
|
+
return runPrune({ db: this.#db, domain: "observability", targets, options, logger: this.logger });
|
|
9311
|
+
}
|
|
8957
9312
|
/**
|
|
8958
9313
|
* Manually run the spans migration to deduplicate and add the unique constraint.
|
|
8959
9314
|
* This is intended to be called from the CLI when duplicates are detected.
|
|
@@ -9962,7 +10317,15 @@ function rowToTrigger(row) {
|
|
|
9962
10317
|
if (metadata !== void 0) trigger.metadata = metadata;
|
|
9963
10318
|
return trigger;
|
|
9964
10319
|
}
|
|
9965
|
-
var SchedulesLibSQL = class extends storage.SchedulesStorage {
|
|
10320
|
+
var SchedulesLibSQL = class _SchedulesLibSQL extends storage.SchedulesStorage {
|
|
10321
|
+
/**
|
|
10322
|
+
* The fire/run history (`schedule_triggers`, one row per fire) is the growth
|
|
10323
|
+
* table; schedule definitions are config and excluded. Anchored on
|
|
10324
|
+
* `actual_fire_at`, a bigint epoch-ms column (numeric comparison, not ISO).
|
|
10325
|
+
*/
|
|
10326
|
+
static retentionTables = {
|
|
10327
|
+
triggers: { table: storage.TABLE_SCHEDULE_TRIGGERS, column: "actual_fire_at", indexed: true, anchorType: "epoch-ms" }
|
|
10328
|
+
};
|
|
9966
10329
|
#db;
|
|
9967
10330
|
#client;
|
|
9968
10331
|
constructor(config) {
|
|
@@ -9998,6 +10361,15 @@ var SchedulesLibSQL = class extends storage.SchedulesStorage {
|
|
|
9998
10361
|
await this.#db.deleteData({ tableName: storage.TABLE_SCHEDULE_TRIGGERS });
|
|
9999
10362
|
await this.#db.deleteData({ tableName: storage.TABLE_SCHEDULES });
|
|
10000
10363
|
}
|
|
10364
|
+
/** Delete schedule fire history older than the `triggers` policy's `maxAge`, batched. */
|
|
10365
|
+
async prune(policies, options) {
|
|
10366
|
+
const targets = resolveTargets({
|
|
10367
|
+
policies,
|
|
10368
|
+
descriptor: _SchedulesLibSQL.retentionTables,
|
|
10369
|
+
order: ["triggers"]
|
|
10370
|
+
});
|
|
10371
|
+
return runPrune({ db: this.#db, domain: "schedules", targets, options, logger: this.logger });
|
|
10372
|
+
}
|
|
10001
10373
|
async createSchedule(schedule) {
|
|
10002
10374
|
const existing = await this.getSchedule(schedule.id);
|
|
10003
10375
|
if (existing) {
|
|
@@ -10672,7 +11044,13 @@ var ScorerDefinitionsLibSQL = class extends storage.ScorerDefinitionsStorage {
|
|
|
10672
11044
|
};
|
|
10673
11045
|
}
|
|
10674
11046
|
};
|
|
10675
|
-
var ScoresLibSQL = class extends storage.ScoresStorage {
|
|
11047
|
+
var ScoresLibSQL = class _ScoresLibSQL extends storage.ScoresStorage {
|
|
11048
|
+
/**
|
|
11049
|
+
* Scorer results accumulate as evals run. Single table, anchored on `createdAt`.
|
|
11050
|
+
*/
|
|
11051
|
+
static retentionTables = {
|
|
11052
|
+
scorers: { table: storage.TABLE_SCORERS, column: "createdAt", indexed: true }
|
|
11053
|
+
};
|
|
10676
11054
|
#db;
|
|
10677
11055
|
#client;
|
|
10678
11056
|
constructor(config) {
|
|
@@ -10692,6 +11070,15 @@ var ScoresLibSQL = class extends storage.ScoresStorage {
|
|
|
10692
11070
|
async dangerouslyClearAll() {
|
|
10693
11071
|
await this.#db.deleteData({ tableName: storage.TABLE_SCORERS });
|
|
10694
11072
|
}
|
|
11073
|
+
/** Delete scorer results older than the `scorers` policy's `maxAge`, batched. */
|
|
11074
|
+
async prune(policies, options) {
|
|
11075
|
+
const targets = resolveTargets({
|
|
11076
|
+
policies,
|
|
11077
|
+
descriptor: _ScoresLibSQL.retentionTables,
|
|
11078
|
+
order: ["scorers"]
|
|
11079
|
+
});
|
|
11080
|
+
return runPrune({ db: this.#db, domain: "scores", targets, options, logger: this.logger });
|
|
11081
|
+
}
|
|
10695
11082
|
async listScoresByRunId({
|
|
10696
11083
|
runId,
|
|
10697
11084
|
pagination
|
|
@@ -11575,7 +11962,15 @@ var SkillsLibSQL = class extends storage.SkillsStorage {
|
|
|
11575
11962
|
};
|
|
11576
11963
|
}
|
|
11577
11964
|
};
|
|
11578
|
-
var ThreadStateLibSQL = class extends storage.ThreadStateStorage {
|
|
11965
|
+
var ThreadStateLibSQL = class _ThreadStateLibSQL extends storage.ThreadStateStorage {
|
|
11966
|
+
/**
|
|
11967
|
+
* `thread_state` grows as a side effect of thread activity (one row per
|
|
11968
|
+
* thread per state type). It anchors on `updatedAt` (last activity), so state
|
|
11969
|
+
* for a thread that is still being appended to is not pruned by creation age.
|
|
11970
|
+
*/
|
|
11971
|
+
static retentionTables = {
|
|
11972
|
+
threadState: { table: storage.TABLE_THREAD_STATE, column: "updatedAt", indexed: true }
|
|
11973
|
+
};
|
|
11579
11974
|
#db;
|
|
11580
11975
|
#client;
|
|
11581
11976
|
constructor(config) {
|
|
@@ -11591,6 +11986,15 @@ var ThreadStateLibSQL = class extends storage.ThreadStateStorage {
|
|
|
11591
11986
|
compositePrimaryKey: ["threadId", "type"]
|
|
11592
11987
|
});
|
|
11593
11988
|
}
|
|
11989
|
+
/** Delete thread state older than the `threadState` policy's `maxAge`, batched. */
|
|
11990
|
+
async prune(policies, options) {
|
|
11991
|
+
const targets = resolveTargets({
|
|
11992
|
+
policies,
|
|
11993
|
+
descriptor: _ThreadStateLibSQL.retentionTables,
|
|
11994
|
+
order: ["threadState"]
|
|
11995
|
+
});
|
|
11996
|
+
return runPrune({ db: this.#db, domain: "threadState", targets, options, logger: this.logger });
|
|
11997
|
+
}
|
|
11594
11998
|
async dangerouslyClearAll() {
|
|
11595
11999
|
try {
|
|
11596
12000
|
await this.#client.execute(`DELETE FROM "${storage.TABLE_THREAD_STATE}"`);
|
|
@@ -11859,7 +12263,14 @@ var ToolProviderConnectionsLibSQL = class extends storage.ToolProviderConnection
|
|
|
11859
12263
|
}
|
|
11860
12264
|
}
|
|
11861
12265
|
};
|
|
11862
|
-
var WorkflowsLibSQL = class extends storage.WorkflowsStorage {
|
|
12266
|
+
var WorkflowsLibSQL = class _WorkflowsLibSQL extends storage.WorkflowsStorage {
|
|
12267
|
+
/**
|
|
12268
|
+
* Workflow run snapshots accumulate as runs execute. Anchored on `updatedAt`
|
|
12269
|
+
* (last activity) so suspended/long-running runs are not pruned by start age.
|
|
12270
|
+
*/
|
|
12271
|
+
static retentionTables = {
|
|
12272
|
+
workflowSnapshot: { table: storage.TABLE_WORKFLOW_SNAPSHOT, column: "updatedAt", indexed: true }
|
|
12273
|
+
};
|
|
11863
12274
|
#db;
|
|
11864
12275
|
#client;
|
|
11865
12276
|
executeWithRetry;
|
|
@@ -11912,6 +12323,15 @@ var WorkflowsLibSQL = class extends storage.WorkflowsStorage {
|
|
|
11912
12323
|
async dangerouslyClearAll() {
|
|
11913
12324
|
await this.#db.deleteData({ tableName: storage.TABLE_WORKFLOW_SNAPSHOT });
|
|
11914
12325
|
}
|
|
12326
|
+
/** Delete workflow snapshots older than the `workflowSnapshot` policy's `maxAge`, batched. */
|
|
12327
|
+
async prune(policies, options) {
|
|
12328
|
+
const targets = resolveTargets({
|
|
12329
|
+
policies,
|
|
12330
|
+
descriptor: _WorkflowsLibSQL.retentionTables,
|
|
12331
|
+
order: ["workflowSnapshot"]
|
|
12332
|
+
});
|
|
12333
|
+
return runPrune({ db: this.#db, domain: "workflows", targets, options, logger: this.logger });
|
|
12334
|
+
}
|
|
11915
12335
|
async setupPragmaSettings() {
|
|
11916
12336
|
try {
|
|
11917
12337
|
await this.#client.execute("PRAGMA busy_timeout = 10000;");
|
|
@@ -12767,7 +13187,7 @@ var LibSQLStore = class extends storage.MastraCompositeStore {
|
|
|
12767
13187
|
if (!config.id || typeof config.id !== "string" || config.id.trim() === "") {
|
|
12768
13188
|
throw new Error("LibSQLStore: id must be provided and cannot be empty.");
|
|
12769
13189
|
}
|
|
12770
|
-
super({ id: config.id, name: `LibSQLStore`, disableInit: config.disableInit });
|
|
13190
|
+
super({ id: config.id, name: `LibSQLStore`, disableInit: config.disableInit, retention: config.retention });
|
|
12771
13191
|
this.maxRetries = config.maxRetries ?? 5;
|
|
12772
13192
|
this.initialBackoffMs = config.initialBackoffMs ?? 100;
|
|
12773
13193
|
this.connectionTimeoutMs = config.connectionTimeoutMs ?? DEFAULT_CONNECTION_TIMEOUT_MS;
|