@mastra/libsql 1.14.3 → 1.15.0-alpha.1
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 +165 -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 +578 -61
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +579 -62
- 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/datasets/index.d.ts +7 -7
- package/dist/storage/domains/datasets/index.d.ts.map +1 -1
- package/dist/storage/domains/experiments/index.d.ts +24 -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 +15 -5
- 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/utils.d.ts +34 -0
- package/dist/storage/domains/utils.d.ts.map +1 -0
- 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,
|
|
@@ -3601,6 +3826,30 @@ var ChannelsLibSQL = class extends storage.ChannelsStorage {
|
|
|
3601
3826
|
};
|
|
3602
3827
|
}
|
|
3603
3828
|
};
|
|
3829
|
+
|
|
3830
|
+
// src/storage/domains/utils.ts
|
|
3831
|
+
function tenancyWhere(filters) {
|
|
3832
|
+
const conditions = [];
|
|
3833
|
+
const params = [];
|
|
3834
|
+
if (filters?.organizationId !== void 0) {
|
|
3835
|
+
conditions.push("organizationId = ?");
|
|
3836
|
+
params.push(filters.organizationId);
|
|
3837
|
+
}
|
|
3838
|
+
if (filters?.projectId !== void 0) {
|
|
3839
|
+
conditions.push("projectId = ?");
|
|
3840
|
+
params.push(filters.projectId);
|
|
3841
|
+
}
|
|
3842
|
+
return { conditions, params };
|
|
3843
|
+
}
|
|
3844
|
+
function buildScopedWhere(idColumn, idValue, filters) {
|
|
3845
|
+
const { conditions, params } = tenancyWhere(filters);
|
|
3846
|
+
return {
|
|
3847
|
+
sql: [`${idColumn} = ?`, ...conditions].join(" AND "),
|
|
3848
|
+
args: [idValue, ...params]
|
|
3849
|
+
};
|
|
3850
|
+
}
|
|
3851
|
+
|
|
3852
|
+
// src/storage/domains/datasets/index.ts
|
|
3604
3853
|
function jsonbArg(value) {
|
|
3605
3854
|
return value === void 0 || value === null ? null : JSON.stringify(value);
|
|
3606
3855
|
}
|
|
@@ -3681,6 +3930,18 @@ var DatasetsLibSQL = class extends storage.DatasetsStorage {
|
|
|
3681
3930
|
await this.#db.deleteData({ tableName: storage.TABLE_DATASET_ITEMS });
|
|
3682
3931
|
await this.#db.deleteData({ tableName: storage.TABLE_DATASETS });
|
|
3683
3932
|
}
|
|
3933
|
+
async experimentTablesExist() {
|
|
3934
|
+
try {
|
|
3935
|
+
const result = await this.#client.execute({
|
|
3936
|
+
sql: `SELECT COUNT(*) AS c FROM sqlite_master WHERE type = 'table' AND name IN (?, ?)`,
|
|
3937
|
+
args: [storage.TABLE_EXPERIMENTS, storage.TABLE_EXPERIMENT_RESULTS]
|
|
3938
|
+
});
|
|
3939
|
+
const row = result.rows?.[0];
|
|
3940
|
+
return Number(row?.c ?? 0) === 2;
|
|
3941
|
+
} catch {
|
|
3942
|
+
return false;
|
|
3943
|
+
}
|
|
3944
|
+
}
|
|
3684
3945
|
// --- Row transformers ---
|
|
3685
3946
|
transformDatasetRow(row) {
|
|
3686
3947
|
return {
|
|
@@ -3767,8 +4028,8 @@ var DatasetsLibSQL = class extends storage.DatasetsStorage {
|
|
|
3767
4028
|
groundTruthSchema: input.groundTruthSchema ?? null,
|
|
3768
4029
|
requestContextSchema: input.requestContextSchema ?? null,
|
|
3769
4030
|
targetType: input.targetType ?? null,
|
|
3770
|
-
targetIds: input.targetIds
|
|
3771
|
-
scorerIds: input.scorerIds
|
|
4031
|
+
targetIds: input.targetIds ?? null,
|
|
4032
|
+
scorerIds: input.scorerIds ?? null,
|
|
3772
4033
|
version: 0,
|
|
3773
4034
|
organizationId: input.organizationId ?? null,
|
|
3774
4035
|
projectId: input.projectId ?? null,
|
|
@@ -3808,11 +4069,16 @@ var DatasetsLibSQL = class extends storage.DatasetsStorage {
|
|
|
3808
4069
|
);
|
|
3809
4070
|
}
|
|
3810
4071
|
}
|
|
3811
|
-
async getDatasetById({
|
|
4072
|
+
async getDatasetById({
|
|
4073
|
+
id,
|
|
4074
|
+
filters
|
|
4075
|
+
}) {
|
|
3812
4076
|
try {
|
|
4077
|
+
const { conditions, params } = tenancyWhere(filters);
|
|
4078
|
+
const whereSql = ["id = ?", ...conditions].join(" AND ");
|
|
3813
4079
|
const result = await this.#client.execute({
|
|
3814
|
-
sql: `SELECT ${buildSelectColumns(storage.TABLE_DATASETS)} FROM ${storage.TABLE_DATASETS} WHERE
|
|
3815
|
-
args: [id]
|
|
4080
|
+
sql: `SELECT ${buildSelectColumns(storage.TABLE_DATASETS)} FROM ${storage.TABLE_DATASETS} WHERE ${whereSql}`,
|
|
4081
|
+
args: [id, ...params]
|
|
3816
4082
|
});
|
|
3817
4083
|
return result.rows?.[0] ? this.transformDatasetRow(result.rows[0]) : null;
|
|
3818
4084
|
} catch (error$1) {
|
|
@@ -3828,7 +4094,7 @@ var DatasetsLibSQL = class extends storage.DatasetsStorage {
|
|
|
3828
4094
|
}
|
|
3829
4095
|
async _doUpdateDataset(args) {
|
|
3830
4096
|
try {
|
|
3831
|
-
const existing = await this.getDatasetById({ id: args.id });
|
|
4097
|
+
const existing = await this.getDatasetById({ id: args.id, filters: args.filters });
|
|
3832
4098
|
if (!existing) {
|
|
3833
4099
|
throw new error.MastraError({
|
|
3834
4100
|
id: storage.createStorageErrorId("LIBSQL", "UPDATE_DATASET", "NOT_FOUND"),
|
|
@@ -3911,30 +4177,31 @@ var DatasetsLibSQL = class extends storage.DatasetsStorage {
|
|
|
3911
4177
|
);
|
|
3912
4178
|
}
|
|
3913
4179
|
}
|
|
3914
|
-
async deleteDataset({ id }) {
|
|
4180
|
+
async deleteDataset({ id, filters }) {
|
|
3915
4181
|
try {
|
|
3916
|
-
|
|
3917
|
-
|
|
4182
|
+
const { conditions, params } = tenancyWhere(filters);
|
|
4183
|
+
const scopedWhere = ["id = ?", ...conditions].join(" AND ");
|
|
4184
|
+
const exists = await this.#client.execute({
|
|
4185
|
+
sql: `SELECT id FROM ${storage.TABLE_DATASETS} WHERE ${scopedWhere}`,
|
|
4186
|
+
args: [id, ...params]
|
|
4187
|
+
});
|
|
4188
|
+
if (!exists.rows?.[0]) return;
|
|
4189
|
+
const experimentTablesExist = await this.experimentTablesExist();
|
|
4190
|
+
const statements = [];
|
|
4191
|
+
if (experimentTablesExist) {
|
|
4192
|
+
statements.push({
|
|
3918
4193
|
sql: `DELETE FROM ${storage.TABLE_EXPERIMENT_RESULTS} WHERE experimentId IN (SELECT id FROM ${storage.TABLE_EXPERIMENTS} WHERE datasetId = ?)`,
|
|
3919
4194
|
args: [id]
|
|
3920
4195
|
});
|
|
3921
|
-
|
|
3922
|
-
}
|
|
3923
|
-
try {
|
|
3924
|
-
await this.#client.execute({
|
|
4196
|
+
statements.push({
|
|
3925
4197
|
sql: `UPDATE ${storage.TABLE_EXPERIMENTS} SET datasetId = NULL, datasetVersion = NULL WHERE datasetId = ?`,
|
|
3926
4198
|
args: [id]
|
|
3927
4199
|
});
|
|
3928
|
-
} catch {
|
|
3929
4200
|
}
|
|
3930
|
-
|
|
3931
|
-
|
|
3932
|
-
|
|
3933
|
-
|
|
3934
|
-
{ sql: `DELETE FROM ${storage.TABLE_DATASETS} WHERE id = ?`, args: [id] }
|
|
3935
|
-
],
|
|
3936
|
-
"write"
|
|
3937
|
-
);
|
|
4201
|
+
statements.push({ sql: `DELETE FROM ${storage.TABLE_DATASET_VERSIONS} WHERE datasetId = ?`, args: [id] });
|
|
4202
|
+
statements.push({ sql: `DELETE FROM ${storage.TABLE_DATASET_ITEMS} WHERE datasetId = ?`, args: [id] });
|
|
4203
|
+
statements.push({ sql: `DELETE FROM ${storage.TABLE_DATASETS} WHERE ${scopedWhere}`, args: [id, ...params] });
|
|
4204
|
+
await this.#client.batch(statements, "write");
|
|
3938
4205
|
} catch (error$1) {
|
|
3939
4206
|
throw new error.MastraError(
|
|
3940
4207
|
{
|
|
@@ -3967,6 +4234,21 @@ var DatasetsLibSQL = class extends storage.DatasetsStorage {
|
|
|
3967
4234
|
filterConditions.push("candidateId = ?");
|
|
3968
4235
|
filterParams.push(args.filters.candidateId);
|
|
3969
4236
|
}
|
|
4237
|
+
if (args.filters?.targetType !== void 0) {
|
|
4238
|
+
filterConditions.push("targetType = ?");
|
|
4239
|
+
filterParams.push(args.filters.targetType);
|
|
4240
|
+
}
|
|
4241
|
+
if (args.filters?.targetIds !== void 0 && args.filters.targetIds.length > 0) {
|
|
4242
|
+
const placeholders = args.filters.targetIds.map(() => "?").join(",");
|
|
4243
|
+
filterConditions.push(
|
|
4244
|
+
`EXISTS (SELECT 1 FROM json_each(${storage.TABLE_DATASETS}.targetIds) WHERE value IN (${placeholders}))`
|
|
4245
|
+
);
|
|
4246
|
+
for (const id of args.filters.targetIds) filterParams.push(id);
|
|
4247
|
+
}
|
|
4248
|
+
if (args.filters?.name !== void 0 && args.filters.name.length > 0) {
|
|
4249
|
+
filterConditions.push("LOWER(name) LIKE ?");
|
|
4250
|
+
filterParams.push(`%${args.filters.name.toLowerCase()}%`);
|
|
4251
|
+
}
|
|
3970
4252
|
const whereClause = filterConditions.length > 0 ? `WHERE ${filterConditions.join(" AND ")}` : "";
|
|
3971
4253
|
const countResult = await this.#client.execute({
|
|
3972
4254
|
sql: `SELECT COUNT(*) as count FROM ${storage.TABLE_DATASETS} ${whereClause}`,
|
|
@@ -4626,7 +4908,18 @@ var DatasetsLibSQL = class extends storage.DatasetsStorage {
|
|
|
4626
4908
|
}
|
|
4627
4909
|
}
|
|
4628
4910
|
};
|
|
4911
|
+
var DEFAULT_PRUNE_BATCH_SIZE = 1e3;
|
|
4629
4912
|
var ExperimentsLibSQL = class extends storage.ExperimentsStorage {
|
|
4913
|
+
/**
|
|
4914
|
+
* An experiment is pruned as a whole unit: when `experiments.completedAt` is
|
|
4915
|
+
* older than the policy, the run and all its `experiment_results` rows are
|
|
4916
|
+
* deleted together (results cascade with their parent, matching
|
|
4917
|
+
* `deleteExperiment`). Results are not an independent retention key. NULL
|
|
4918
|
+
* `completedAt` (still running) is never pruned.
|
|
4919
|
+
*/
|
|
4920
|
+
static retentionTables = {
|
|
4921
|
+
experiments: { table: storage.TABLE_EXPERIMENTS, column: "completedAt", indexed: true }
|
|
4922
|
+
};
|
|
4630
4923
|
#db;
|
|
4631
4924
|
#client;
|
|
4632
4925
|
constructor(config) {
|
|
@@ -4682,6 +4975,58 @@ var ExperimentsLibSQL = class extends storage.ExperimentsStorage {
|
|
|
4682
4975
|
await this.#db.deleteData({ tableName: storage.TABLE_EXPERIMENT_RESULTS });
|
|
4683
4976
|
await this.#db.deleteData({ tableName: storage.TABLE_EXPERIMENTS });
|
|
4684
4977
|
}
|
|
4978
|
+
/**
|
|
4979
|
+
* Prune whole experiments older than the `experiments` policy's `maxAge`.
|
|
4980
|
+
*
|
|
4981
|
+
* Each batch selects up to `batchSize` aged experiments and deletes their
|
|
4982
|
+
* `experiment_results` rows and the experiment rows in one transaction —
|
|
4983
|
+
* mirroring `deleteExperiment` — so hitting `maxBatches`/`maxRows` or the
|
|
4984
|
+
* abort signal between batches never leaves a run hollow (parent kept,
|
|
4985
|
+
* results gone). NULL `completedAt` (still running) is excluded by the
|
|
4986
|
+
* `< cutoff` predicate. Bounds count whole experiments, not rows.
|
|
4987
|
+
*/
|
|
4988
|
+
async prune(policies, options) {
|
|
4989
|
+
const policy = policies["experiments"];
|
|
4990
|
+
if (!policy || options?.signal?.aborted) {
|
|
4991
|
+
return policy ? [
|
|
4992
|
+
{ domain: "experiments", table: storage.TABLE_EXPERIMENT_RESULTS, deleted: 0, done: false },
|
|
4993
|
+
{ domain: "experiments", table: storage.TABLE_EXPERIMENTS, deleted: 0, done: false }
|
|
4994
|
+
] : [];
|
|
4995
|
+
}
|
|
4996
|
+
try {
|
|
4997
|
+
await this.#db.ensureIndex({
|
|
4998
|
+
indexName: `idx_retention_${storage.TABLE_EXPERIMENTS}_completedAt`,
|
|
4999
|
+
tableName: storage.TABLE_EXPERIMENTS,
|
|
5000
|
+
column: "completedAt"
|
|
5001
|
+
});
|
|
5002
|
+
} catch (error) {
|
|
5003
|
+
this.logger?.warn?.(`Failed to ensure retention index on ${storage.TABLE_EXPERIMENTS}(completedAt):`, error);
|
|
5004
|
+
}
|
|
5005
|
+
const cutoff = cutoffFor(policy, "timestamp");
|
|
5006
|
+
const batchSize = policy.batchSize ?? DEFAULT_PRUNE_BATCH_SIZE;
|
|
5007
|
+
let childDeleted = 0;
|
|
5008
|
+
const parent = await runBatchedDelete({
|
|
5009
|
+
deleteBatch: async (limit) => {
|
|
5010
|
+
const { parents, children } = await this.#db.pruneUnitsBatch({
|
|
5011
|
+
parentTable: storage.TABLE_EXPERIMENTS,
|
|
5012
|
+
parentKey: "id",
|
|
5013
|
+
parentColumn: "completedAt",
|
|
5014
|
+
childTable: storage.TABLE_EXPERIMENT_RESULTS,
|
|
5015
|
+
childForeignKey: "experimentId",
|
|
5016
|
+
cutoff,
|
|
5017
|
+
limit
|
|
5018
|
+
});
|
|
5019
|
+
childDeleted += children;
|
|
5020
|
+
return parents;
|
|
5021
|
+
},
|
|
5022
|
+
batchSize,
|
|
5023
|
+
options
|
|
5024
|
+
});
|
|
5025
|
+
return [
|
|
5026
|
+
{ domain: "experiments", table: storage.TABLE_EXPERIMENT_RESULTS, deleted: childDeleted, done: parent.done },
|
|
5027
|
+
{ domain: "experiments", table: storage.TABLE_EXPERIMENTS, deleted: parent.deleted, done: parent.done }
|
|
5028
|
+
];
|
|
5029
|
+
}
|
|
4685
5030
|
// Helper to transform row to Experiment
|
|
4686
5031
|
transformExperimentRow(row) {
|
|
4687
5032
|
return {
|
|
@@ -4869,9 +5214,10 @@ var ExperimentsLibSQL = class extends storage.ExperimentsStorage {
|
|
|
4869
5214
|
}
|
|
4870
5215
|
async getExperimentById(args) {
|
|
4871
5216
|
try {
|
|
5217
|
+
const scoped = buildScopedWhere("id", args.id, args.filters);
|
|
4872
5218
|
const result = await this.#client.execute({
|
|
4873
|
-
sql: `SELECT ${buildSelectColumns(storage.TABLE_EXPERIMENTS)} FROM ${storage.TABLE_EXPERIMENTS} WHERE
|
|
4874
|
-
args:
|
|
5219
|
+
sql: `SELECT ${buildSelectColumns(storage.TABLE_EXPERIMENTS)} FROM ${storage.TABLE_EXPERIMENTS} WHERE ${scoped.sql}`,
|
|
5220
|
+
args: scoped.args
|
|
4875
5221
|
});
|
|
4876
5222
|
return result.rows?.[0] ? this.transformExperimentRow(result.rows[0]) : null;
|
|
4877
5223
|
} catch (error$1) {
|
|
@@ -4963,14 +5309,17 @@ var ExperimentsLibSQL = class extends storage.ExperimentsStorage {
|
|
|
4963
5309
|
}
|
|
4964
5310
|
async deleteExperiment(args) {
|
|
4965
5311
|
try {
|
|
4966
|
-
|
|
4967
|
-
|
|
4968
|
-
|
|
4969
|
-
|
|
4970
|
-
await this.#client.
|
|
4971
|
-
|
|
4972
|
-
|
|
4973
|
-
|
|
5312
|
+
const parentScoped = buildScopedWhere("id", args.id, args.filters);
|
|
5313
|
+
const { conditions, params } = tenancyWhere(args.filters);
|
|
5314
|
+
const cascadeWhere = conditions.length ? `experimentId IN (SELECT id FROM ${storage.TABLE_EXPERIMENTS} WHERE ${["id = ?", ...conditions].join(" AND ")})` : `experimentId = ?`;
|
|
5315
|
+
const cascadeArgs = conditions.length ? [args.id, ...params] : [args.id];
|
|
5316
|
+
await this.#client.batch(
|
|
5317
|
+
[
|
|
5318
|
+
{ sql: `DELETE FROM ${storage.TABLE_EXPERIMENT_RESULTS} WHERE ${cascadeWhere}`, args: cascadeArgs },
|
|
5319
|
+
{ sql: `DELETE FROM ${storage.TABLE_EXPERIMENTS} WHERE ${parentScoped.sql}`, args: parentScoped.args }
|
|
5320
|
+
],
|
|
5321
|
+
"write"
|
|
5322
|
+
);
|
|
4974
5323
|
} catch (error$1) {
|
|
4975
5324
|
throw new error.MastraError(
|
|
4976
5325
|
{
|
|
@@ -5108,9 +5457,10 @@ var ExperimentsLibSQL = class extends storage.ExperimentsStorage {
|
|
|
5108
5457
|
}
|
|
5109
5458
|
async getExperimentResultById(args) {
|
|
5110
5459
|
try {
|
|
5460
|
+
const scoped = buildScopedWhere("id", args.id, args.filters);
|
|
5111
5461
|
const result = await this.#client.execute({
|
|
5112
|
-
sql: `SELECT ${buildSelectColumns(storage.TABLE_EXPERIMENT_RESULTS)} FROM ${storage.TABLE_EXPERIMENT_RESULTS} WHERE
|
|
5113
|
-
args:
|
|
5462
|
+
sql: `SELECT ${buildSelectColumns(storage.TABLE_EXPERIMENT_RESULTS)} FROM ${storage.TABLE_EXPERIMENT_RESULTS} WHERE ${scoped.sql}`,
|
|
5463
|
+
args: scoped.args
|
|
5114
5464
|
});
|
|
5115
5465
|
return result.rows?.[0] ? this.transformExperimentResultRow(result.rows[0]) : null;
|
|
5116
5466
|
} catch (error$1) {
|
|
@@ -5190,6 +5540,14 @@ var ExperimentsLibSQL = class extends storage.ExperimentsStorage {
|
|
|
5190
5540
|
}
|
|
5191
5541
|
async deleteExperimentResults(args) {
|
|
5192
5542
|
try {
|
|
5543
|
+
const { conditions, params } = tenancyWhere(args.filters);
|
|
5544
|
+
if (conditions.length) {
|
|
5545
|
+
await this.#client.execute({
|
|
5546
|
+
sql: `DELETE FROM ${storage.TABLE_EXPERIMENT_RESULTS} WHERE experimentId IN (SELECT id FROM ${storage.TABLE_EXPERIMENTS} WHERE ${["id = ?", ...conditions].join(" AND ")})`,
|
|
5547
|
+
args: [args.experimentId, ...params]
|
|
5548
|
+
});
|
|
5549
|
+
return;
|
|
5550
|
+
}
|
|
5193
5551
|
await this.#client.execute({
|
|
5194
5552
|
sql: `DELETE FROM ${storage.TABLE_EXPERIMENT_RESULTS} WHERE experimentId = ?`,
|
|
5195
5553
|
args: [args.experimentId]
|
|
@@ -5569,7 +5927,11 @@ function sessionToRecord(record) {
|
|
|
5569
5927
|
deletedAt: record.deletedAt ?? null
|
|
5570
5928
|
};
|
|
5571
5929
|
}
|
|
5572
|
-
var HarnessLibSQL = class extends storage.HarnessStorage {
|
|
5930
|
+
var HarnessLibSQL = class _HarnessLibSQL extends storage.HarnessStorage {
|
|
5931
|
+
/** Session records accumulate over time. Single table, anchored on `createdAt`. */
|
|
5932
|
+
static retentionTables = {
|
|
5933
|
+
sessions: { table: storage.TABLE_HARNESS_SESSIONS, column: "createdAt", indexed: true }
|
|
5934
|
+
};
|
|
5573
5935
|
#db;
|
|
5574
5936
|
constructor(config) {
|
|
5575
5937
|
super();
|
|
@@ -5585,6 +5947,15 @@ var HarnessLibSQL = class extends storage.HarnessStorage {
|
|
|
5585
5947
|
async dangerouslyClearAll() {
|
|
5586
5948
|
await this.#db.deleteData({ tableName: storage.TABLE_HARNESS_SESSIONS });
|
|
5587
5949
|
}
|
|
5950
|
+
/** Delete harness sessions older than the `sessions` policy's `maxAge`, batched. */
|
|
5951
|
+
async prune(policies, options) {
|
|
5952
|
+
const targets = resolveTargets({
|
|
5953
|
+
policies,
|
|
5954
|
+
descriptor: _HarnessLibSQL.retentionTables,
|
|
5955
|
+
order: ["sessions"]
|
|
5956
|
+
});
|
|
5957
|
+
return runPrune({ db: this.#db, domain: "harness", targets, options, logger: this.logger });
|
|
5958
|
+
}
|
|
5588
5959
|
async loadSession(sessionId) {
|
|
5589
5960
|
const row = await this.#db.select({
|
|
5590
5961
|
tableName: storage.TABLE_HARNESS_SESSIONS,
|
|
@@ -6570,8 +6941,18 @@ var MCPServersLibSQL = class extends storage.MCPServersStorage {
|
|
|
6570
6941
|
}
|
|
6571
6942
|
};
|
|
6572
6943
|
var OM_TABLE = "mastra_observational_memory";
|
|
6573
|
-
var MemoryLibSQL = class extends storage.MemoryStorage {
|
|
6944
|
+
var MemoryLibSQL = class _MemoryLibSQL extends storage.MemoryStorage {
|
|
6574
6945
|
supportsObservationalMemory = true;
|
|
6946
|
+
/**
|
|
6947
|
+
* Retention-eligible tables. `threads`, `messages`, and `resources` all anchor
|
|
6948
|
+
* on `createdAt` and are indexed for fast batched deletes. Cascade order is
|
|
6949
|
+
* enforced in `prune()` (children before threads), not here.
|
|
6950
|
+
*/
|
|
6951
|
+
static retentionTables = {
|
|
6952
|
+
messages: { table: storage.TABLE_MESSAGES, column: "createdAt", indexed: true },
|
|
6953
|
+
resources: { table: storage.TABLE_RESOURCES, column: "createdAt", indexed: true },
|
|
6954
|
+
threads: { table: storage.TABLE_THREADS, column: "createdAt", indexed: true }
|
|
6955
|
+
};
|
|
6575
6956
|
#client;
|
|
6576
6957
|
#db;
|
|
6577
6958
|
constructor(config) {
|
|
@@ -6646,6 +7027,21 @@ var MemoryLibSQL = class extends storage.MemoryStorage {
|
|
|
6646
7027
|
await this.#db.deleteData({ tableName: OM_TABLE });
|
|
6647
7028
|
}
|
|
6648
7029
|
}
|
|
7030
|
+
/**
|
|
7031
|
+
* Delete rows older than each policy's `maxAge`, batched and cancellable.
|
|
7032
|
+
*
|
|
7033
|
+
* Deletes children before parents (messages/resources before threads) so a
|
|
7034
|
+
* `threads` policy doesn't strand rows behind a deleted parent — there are no
|
|
7035
|
+
* FKs in the LibSQL schema, so cascade ordering is explicit here.
|
|
7036
|
+
*/
|
|
7037
|
+
async prune(policies, options) {
|
|
7038
|
+
const targets = resolveTargets({
|
|
7039
|
+
policies,
|
|
7040
|
+
descriptor: _MemoryLibSQL.retentionTables,
|
|
7041
|
+
order: ["messages", "resources", "threads"]
|
|
7042
|
+
});
|
|
7043
|
+
return runPrune({ db: this.#db, domain: "memory", targets, options, logger: this.logger });
|
|
7044
|
+
}
|
|
6649
7045
|
parseRow(row) {
|
|
6650
7046
|
let content = row.content;
|
|
6651
7047
|
try {
|
|
@@ -8712,7 +9108,11 @@ function addArrayFilter(conditions, args, column, value) {
|
|
|
8712
9108
|
conditions.push(`"${column}" IN (${values.map(() => "?").join(", ")})`);
|
|
8713
9109
|
args.push(...values);
|
|
8714
9110
|
}
|
|
8715
|
-
var NotificationsLibSQL = class extends storage.NotificationsStorage {
|
|
9111
|
+
var NotificationsLibSQL = class _NotificationsLibSQL extends storage.NotificationsStorage {
|
|
9112
|
+
/** The notification feed grows unbounded. Single table, anchored on `createdAt`. */
|
|
9113
|
+
static retentionTables = {
|
|
9114
|
+
notifications: { table: storage.TABLE_NOTIFICATIONS, column: "createdAt", indexed: true }
|
|
9115
|
+
};
|
|
8716
9116
|
#db;
|
|
8717
9117
|
#client;
|
|
8718
9118
|
constructor(config) {
|
|
@@ -8747,6 +9147,15 @@ var NotificationsLibSQL = class extends storage.NotificationsStorage {
|
|
|
8747
9147
|
async dangerouslyClearAll() {
|
|
8748
9148
|
await this.#db.deleteData({ tableName: storage.TABLE_NOTIFICATIONS });
|
|
8749
9149
|
}
|
|
9150
|
+
/** Delete notifications older than the `notifications` policy's `maxAge`, batched. */
|
|
9151
|
+
async prune(policies, options) {
|
|
9152
|
+
const targets = resolveTargets({
|
|
9153
|
+
policies,
|
|
9154
|
+
descriptor: _NotificationsLibSQL.retentionTables,
|
|
9155
|
+
order: ["notifications"]
|
|
9156
|
+
});
|
|
9157
|
+
return runPrune({ db: this.#db, domain: "notifications", targets, options, logger: this.logger });
|
|
9158
|
+
}
|
|
8750
9159
|
async createNotification(input) {
|
|
8751
9160
|
const existing = await this.findCoalescable(input);
|
|
8752
9161
|
if (existing) {
|
|
@@ -8936,7 +9345,14 @@ var NotificationsLibSQL = class extends storage.NotificationsStorage {
|
|
|
8936
9345
|
return row ? rowToNotification(row) : void 0;
|
|
8937
9346
|
}
|
|
8938
9347
|
};
|
|
8939
|
-
var ObservabilityLibSQL = class extends storage.ObservabilityStorage {
|
|
9348
|
+
var ObservabilityLibSQL = class _ObservabilityLibSQL extends storage.ObservabilityStorage {
|
|
9349
|
+
/**
|
|
9350
|
+
* Spans are the only physical table; traces are derived from them. Spans
|
|
9351
|
+
* anchor on `startedAt` (not `createdAt`), stored as an ISO-8601 string.
|
|
9352
|
+
*/
|
|
9353
|
+
static retentionTables = {
|
|
9354
|
+
spans: { table: storage.TABLE_SPANS, column: "startedAt", indexed: true }
|
|
9355
|
+
};
|
|
8940
9356
|
#db;
|
|
8941
9357
|
constructor(config) {
|
|
8942
9358
|
super();
|
|
@@ -8954,6 +9370,15 @@ var ObservabilityLibSQL = class extends storage.ObservabilityStorage {
|
|
|
8954
9370
|
async dangerouslyClearAll() {
|
|
8955
9371
|
await this.#db.deleteData({ tableName: storage.TABLE_SPANS });
|
|
8956
9372
|
}
|
|
9373
|
+
/** Delete spans older than the `spans` policy's `maxAge`, batched. */
|
|
9374
|
+
async prune(policies, options) {
|
|
9375
|
+
const targets = resolveTargets({
|
|
9376
|
+
policies,
|
|
9377
|
+
descriptor: _ObservabilityLibSQL.retentionTables,
|
|
9378
|
+
order: ["spans"]
|
|
9379
|
+
});
|
|
9380
|
+
return runPrune({ db: this.#db, domain: "observability", targets, options, logger: this.logger });
|
|
9381
|
+
}
|
|
8957
9382
|
/**
|
|
8958
9383
|
* Manually run the spans migration to deduplicate and add the unique constraint.
|
|
8959
9384
|
* This is intended to be called from the CLI when duplicates are detected.
|
|
@@ -9962,7 +10387,15 @@ function rowToTrigger(row) {
|
|
|
9962
10387
|
if (metadata !== void 0) trigger.metadata = metadata;
|
|
9963
10388
|
return trigger;
|
|
9964
10389
|
}
|
|
9965
|
-
var SchedulesLibSQL = class extends storage.SchedulesStorage {
|
|
10390
|
+
var SchedulesLibSQL = class _SchedulesLibSQL extends storage.SchedulesStorage {
|
|
10391
|
+
/**
|
|
10392
|
+
* The fire/run history (`schedule_triggers`, one row per fire) is the growth
|
|
10393
|
+
* table; schedule definitions are config and excluded. Anchored on
|
|
10394
|
+
* `actual_fire_at`, a bigint epoch-ms column (numeric comparison, not ISO).
|
|
10395
|
+
*/
|
|
10396
|
+
static retentionTables = {
|
|
10397
|
+
triggers: { table: storage.TABLE_SCHEDULE_TRIGGERS, column: "actual_fire_at", indexed: true, anchorType: "epoch-ms" }
|
|
10398
|
+
};
|
|
9966
10399
|
#db;
|
|
9967
10400
|
#client;
|
|
9968
10401
|
constructor(config) {
|
|
@@ -9998,6 +10431,15 @@ var SchedulesLibSQL = class extends storage.SchedulesStorage {
|
|
|
9998
10431
|
await this.#db.deleteData({ tableName: storage.TABLE_SCHEDULE_TRIGGERS });
|
|
9999
10432
|
await this.#db.deleteData({ tableName: storage.TABLE_SCHEDULES });
|
|
10000
10433
|
}
|
|
10434
|
+
/** Delete schedule fire history older than the `triggers` policy's `maxAge`, batched. */
|
|
10435
|
+
async prune(policies, options) {
|
|
10436
|
+
const targets = resolveTargets({
|
|
10437
|
+
policies,
|
|
10438
|
+
descriptor: _SchedulesLibSQL.retentionTables,
|
|
10439
|
+
order: ["triggers"]
|
|
10440
|
+
});
|
|
10441
|
+
return runPrune({ db: this.#db, domain: "schedules", targets, options, logger: this.logger });
|
|
10442
|
+
}
|
|
10001
10443
|
async createSchedule(schedule) {
|
|
10002
10444
|
const existing = await this.getSchedule(schedule.id);
|
|
10003
10445
|
if (existing) {
|
|
@@ -10672,7 +11114,23 @@ var ScorerDefinitionsLibSQL = class extends storage.ScorerDefinitionsStorage {
|
|
|
10672
11114
|
};
|
|
10673
11115
|
}
|
|
10674
11116
|
};
|
|
10675
|
-
|
|
11117
|
+
function appendTenancyConditions(conditions, args, filters) {
|
|
11118
|
+
if (filters?.organizationId !== void 0) {
|
|
11119
|
+
conditions.push(`organizationId = ?`);
|
|
11120
|
+
args.push(filters.organizationId);
|
|
11121
|
+
}
|
|
11122
|
+
if (filters?.projectId !== void 0) {
|
|
11123
|
+
conditions.push(`projectId = ?`);
|
|
11124
|
+
args.push(filters.projectId);
|
|
11125
|
+
}
|
|
11126
|
+
}
|
|
11127
|
+
var ScoresLibSQL = class _ScoresLibSQL extends storage.ScoresStorage {
|
|
11128
|
+
/**
|
|
11129
|
+
* Scorer results accumulate as evals run. Single table, anchored on `createdAt`.
|
|
11130
|
+
*/
|
|
11131
|
+
static retentionTables = {
|
|
11132
|
+
scorers: { table: storage.TABLE_SCORERS, column: "createdAt", indexed: true }
|
|
11133
|
+
};
|
|
10676
11134
|
#db;
|
|
10677
11135
|
#client;
|
|
10678
11136
|
constructor(config) {
|
|
@@ -10686,21 +11144,35 @@ var ScoresLibSQL = class extends storage.ScoresStorage {
|
|
|
10686
11144
|
await this.#db.alterTable({
|
|
10687
11145
|
tableName: storage.TABLE_SCORERS,
|
|
10688
11146
|
schema: storage.SCORERS_SCHEMA,
|
|
10689
|
-
ifNotExists: ["spanId", "requestContext"]
|
|
11147
|
+
ifNotExists: ["spanId", "requestContext", "organizationId", "projectId", "batchId", "datasetId", "datasetItemId"]
|
|
10690
11148
|
});
|
|
10691
11149
|
}
|
|
10692
11150
|
async dangerouslyClearAll() {
|
|
10693
11151
|
await this.#db.deleteData({ tableName: storage.TABLE_SCORERS });
|
|
10694
11152
|
}
|
|
11153
|
+
/** Delete scorer results older than the `scorers` policy's `maxAge`, batched. */
|
|
11154
|
+
async prune(policies, options) {
|
|
11155
|
+
const targets = resolveTargets({
|
|
11156
|
+
policies,
|
|
11157
|
+
descriptor: _ScoresLibSQL.retentionTables,
|
|
11158
|
+
order: ["scorers"]
|
|
11159
|
+
});
|
|
11160
|
+
return runPrune({ db: this.#db, domain: "scores", targets, options, logger: this.logger });
|
|
11161
|
+
}
|
|
10695
11162
|
async listScoresByRunId({
|
|
10696
11163
|
runId,
|
|
10697
|
-
pagination
|
|
11164
|
+
pagination,
|
|
11165
|
+
filters
|
|
10698
11166
|
}) {
|
|
10699
11167
|
try {
|
|
10700
11168
|
const { page, perPage: perPageInput } = pagination;
|
|
11169
|
+
const conditions = [`runId = ?`];
|
|
11170
|
+
const queryParams = [runId];
|
|
11171
|
+
appendTenancyConditions(conditions, queryParams, filters);
|
|
11172
|
+
const whereClause = `WHERE ${conditions.join(" AND ")}`;
|
|
10701
11173
|
const countResult = await this.#client.execute({
|
|
10702
|
-
sql: `SELECT COUNT(*) as count FROM ${storage.TABLE_SCORERS}
|
|
10703
|
-
args:
|
|
11174
|
+
sql: `SELECT COUNT(*) as count FROM ${storage.TABLE_SCORERS} ${whereClause}`,
|
|
11175
|
+
args: queryParams
|
|
10704
11176
|
});
|
|
10705
11177
|
const total = Number(countResult.rows?.[0]?.count ?? 0);
|
|
10706
11178
|
if (total === 0) {
|
|
@@ -10719,8 +11191,8 @@ var ScoresLibSQL = class extends storage.ScoresStorage {
|
|
|
10719
11191
|
const limitValue = perPageInput === false ? total : perPage;
|
|
10720
11192
|
const end = perPageInput === false ? total : start + perPage;
|
|
10721
11193
|
const result = await this.#client.execute({
|
|
10722
|
-
sql: `SELECT ${buildSelectColumns(storage.TABLE_SCORERS)} FROM ${storage.TABLE_SCORERS}
|
|
10723
|
-
args: [
|
|
11194
|
+
sql: `SELECT ${buildSelectColumns(storage.TABLE_SCORERS)} FROM ${storage.TABLE_SCORERS} ${whereClause} ORDER BY createdAt DESC LIMIT ? OFFSET ?`,
|
|
11195
|
+
args: [...queryParams, limitValue, start]
|
|
10724
11196
|
});
|
|
10725
11197
|
const scores = result.rows?.map((row) => this.transformScoreRow(row)) ?? [];
|
|
10726
11198
|
return {
|
|
@@ -10748,7 +11220,8 @@ var ScoresLibSQL = class extends storage.ScoresStorage {
|
|
|
10748
11220
|
entityId,
|
|
10749
11221
|
entityType,
|
|
10750
11222
|
source,
|
|
10751
|
-
pagination
|
|
11223
|
+
pagination,
|
|
11224
|
+
filters
|
|
10752
11225
|
}) {
|
|
10753
11226
|
try {
|
|
10754
11227
|
const { page, perPage: perPageInput } = pagination;
|
|
@@ -10770,6 +11243,7 @@ var ScoresLibSQL = class extends storage.ScoresStorage {
|
|
|
10770
11243
|
conditions.push(`source = ?`);
|
|
10771
11244
|
queryParams.push(source);
|
|
10772
11245
|
}
|
|
11246
|
+
appendTenancyConditions(conditions, queryParams, filters);
|
|
10773
11247
|
const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
10774
11248
|
const countResult = await this.#client.execute({
|
|
10775
11249
|
sql: `SELECT COUNT(*) as count FROM ${storage.TABLE_SCORERS} ${whereClause}`,
|
|
@@ -10877,13 +11351,18 @@ var ScoresLibSQL = class extends storage.ScoresStorage {
|
|
|
10877
11351
|
async listScoresByEntityId({
|
|
10878
11352
|
entityId,
|
|
10879
11353
|
entityType,
|
|
10880
|
-
pagination
|
|
11354
|
+
pagination,
|
|
11355
|
+
filters
|
|
10881
11356
|
}) {
|
|
10882
11357
|
try {
|
|
10883
11358
|
const { page, perPage: perPageInput } = pagination;
|
|
11359
|
+
const conditions = [`entityId = ?`, `entityType = ?`];
|
|
11360
|
+
const queryParams = [entityId, entityType];
|
|
11361
|
+
appendTenancyConditions(conditions, queryParams, filters);
|
|
11362
|
+
const whereClause = `WHERE ${conditions.join(" AND ")}`;
|
|
10884
11363
|
const countResult = await this.#client.execute({
|
|
10885
|
-
sql: `SELECT COUNT(*) as count FROM ${storage.TABLE_SCORERS}
|
|
10886
|
-
args:
|
|
11364
|
+
sql: `SELECT COUNT(*) as count FROM ${storage.TABLE_SCORERS} ${whereClause}`,
|
|
11365
|
+
args: queryParams
|
|
10887
11366
|
});
|
|
10888
11367
|
const total = Number(countResult.rows?.[0]?.count ?? 0);
|
|
10889
11368
|
if (total === 0) {
|
|
@@ -10902,8 +11381,8 @@ var ScoresLibSQL = class extends storage.ScoresStorage {
|
|
|
10902
11381
|
const limitValue = perPageInput === false ? total : perPage;
|
|
10903
11382
|
const end = perPageInput === false ? total : start + perPage;
|
|
10904
11383
|
const result = await this.#client.execute({
|
|
10905
|
-
sql: `SELECT ${buildSelectColumns(storage.TABLE_SCORERS)} FROM ${storage.TABLE_SCORERS}
|
|
10906
|
-
args: [
|
|
11384
|
+
sql: `SELECT ${buildSelectColumns(storage.TABLE_SCORERS)} FROM ${storage.TABLE_SCORERS} ${whereClause} ORDER BY createdAt DESC LIMIT ? OFFSET ?`,
|
|
11385
|
+
args: [...queryParams, limitValue, start]
|
|
10907
11386
|
});
|
|
10908
11387
|
const scores = result.rows?.map((row) => this.transformScoreRow(row)) ?? [];
|
|
10909
11388
|
return {
|
|
@@ -10929,22 +11408,27 @@ var ScoresLibSQL = class extends storage.ScoresStorage {
|
|
|
10929
11408
|
async listScoresBySpan({
|
|
10930
11409
|
traceId,
|
|
10931
11410
|
spanId,
|
|
10932
|
-
pagination
|
|
11411
|
+
pagination,
|
|
11412
|
+
filters
|
|
10933
11413
|
}) {
|
|
10934
11414
|
try {
|
|
10935
11415
|
const { page, perPage: perPageInput } = pagination;
|
|
10936
11416
|
const perPage = storage.normalizePerPage(perPageInput, 100);
|
|
10937
11417
|
const { offset: start, perPage: perPageForResponse } = storage.calculatePagination(page, perPageInput, perPage);
|
|
11418
|
+
const conditions = [`traceId = ?`, `spanId = ?`];
|
|
11419
|
+
const queryParams = [traceId, spanId];
|
|
11420
|
+
appendTenancyConditions(conditions, queryParams, filters);
|
|
11421
|
+
const whereClause = `WHERE ${conditions.join(" AND ")}`;
|
|
10938
11422
|
const countSQLResult = await this.#client.execute({
|
|
10939
|
-
sql: `SELECT COUNT(*) as count FROM ${storage.TABLE_SCORERS}
|
|
10940
|
-
args:
|
|
11423
|
+
sql: `SELECT COUNT(*) as count FROM ${storage.TABLE_SCORERS} ${whereClause}`,
|
|
11424
|
+
args: queryParams
|
|
10941
11425
|
});
|
|
10942
11426
|
const total = Number(countSQLResult.rows?.[0]?.count ?? 0);
|
|
10943
11427
|
const limitValue = perPageInput === false ? total : perPage;
|
|
10944
11428
|
const end = perPageInput === false ? total : start + perPage;
|
|
10945
11429
|
const result = await this.#client.execute({
|
|
10946
|
-
sql: `SELECT ${buildSelectColumns(storage.TABLE_SCORERS)} FROM ${storage.TABLE_SCORERS}
|
|
10947
|
-
args: [
|
|
11430
|
+
sql: `SELECT ${buildSelectColumns(storage.TABLE_SCORERS)} FROM ${storage.TABLE_SCORERS} ${whereClause} ORDER BY createdAt DESC LIMIT ? OFFSET ?`,
|
|
11431
|
+
args: [...queryParams, limitValue, start]
|
|
10948
11432
|
});
|
|
10949
11433
|
const scores = result.rows?.map((row) => this.transformScoreRow(row)) ?? [];
|
|
10950
11434
|
return {
|
|
@@ -11575,7 +12059,15 @@ var SkillsLibSQL = class extends storage.SkillsStorage {
|
|
|
11575
12059
|
};
|
|
11576
12060
|
}
|
|
11577
12061
|
};
|
|
11578
|
-
var ThreadStateLibSQL = class extends storage.ThreadStateStorage {
|
|
12062
|
+
var ThreadStateLibSQL = class _ThreadStateLibSQL extends storage.ThreadStateStorage {
|
|
12063
|
+
/**
|
|
12064
|
+
* `thread_state` grows as a side effect of thread activity (one row per
|
|
12065
|
+
* thread per state type). It anchors on `updatedAt` (last activity), so state
|
|
12066
|
+
* for a thread that is still being appended to is not pruned by creation age.
|
|
12067
|
+
*/
|
|
12068
|
+
static retentionTables = {
|
|
12069
|
+
threadState: { table: storage.TABLE_THREAD_STATE, column: "updatedAt", indexed: true }
|
|
12070
|
+
};
|
|
11579
12071
|
#db;
|
|
11580
12072
|
#client;
|
|
11581
12073
|
constructor(config) {
|
|
@@ -11591,6 +12083,15 @@ var ThreadStateLibSQL = class extends storage.ThreadStateStorage {
|
|
|
11591
12083
|
compositePrimaryKey: ["threadId", "type"]
|
|
11592
12084
|
});
|
|
11593
12085
|
}
|
|
12086
|
+
/** Delete thread state older than the `threadState` policy's `maxAge`, batched. */
|
|
12087
|
+
async prune(policies, options) {
|
|
12088
|
+
const targets = resolveTargets({
|
|
12089
|
+
policies,
|
|
12090
|
+
descriptor: _ThreadStateLibSQL.retentionTables,
|
|
12091
|
+
order: ["threadState"]
|
|
12092
|
+
});
|
|
12093
|
+
return runPrune({ db: this.#db, domain: "threadState", targets, options, logger: this.logger });
|
|
12094
|
+
}
|
|
11594
12095
|
async dangerouslyClearAll() {
|
|
11595
12096
|
try {
|
|
11596
12097
|
await this.#client.execute(`DELETE FROM "${storage.TABLE_THREAD_STATE}"`);
|
|
@@ -11859,7 +12360,14 @@ var ToolProviderConnectionsLibSQL = class extends storage.ToolProviderConnection
|
|
|
11859
12360
|
}
|
|
11860
12361
|
}
|
|
11861
12362
|
};
|
|
11862
|
-
var WorkflowsLibSQL = class extends storage.WorkflowsStorage {
|
|
12363
|
+
var WorkflowsLibSQL = class _WorkflowsLibSQL extends storage.WorkflowsStorage {
|
|
12364
|
+
/**
|
|
12365
|
+
* Workflow run snapshots accumulate as runs execute. Anchored on `updatedAt`
|
|
12366
|
+
* (last activity) so suspended/long-running runs are not pruned by start age.
|
|
12367
|
+
*/
|
|
12368
|
+
static retentionTables = {
|
|
12369
|
+
workflowSnapshot: { table: storage.TABLE_WORKFLOW_SNAPSHOT, column: "updatedAt", indexed: true }
|
|
12370
|
+
};
|
|
11863
12371
|
#db;
|
|
11864
12372
|
#client;
|
|
11865
12373
|
executeWithRetry;
|
|
@@ -11912,6 +12420,15 @@ var WorkflowsLibSQL = class extends storage.WorkflowsStorage {
|
|
|
11912
12420
|
async dangerouslyClearAll() {
|
|
11913
12421
|
await this.#db.deleteData({ tableName: storage.TABLE_WORKFLOW_SNAPSHOT });
|
|
11914
12422
|
}
|
|
12423
|
+
/** Delete workflow snapshots older than the `workflowSnapshot` policy's `maxAge`, batched. */
|
|
12424
|
+
async prune(policies, options) {
|
|
12425
|
+
const targets = resolveTargets({
|
|
12426
|
+
policies,
|
|
12427
|
+
descriptor: _WorkflowsLibSQL.retentionTables,
|
|
12428
|
+
order: ["workflowSnapshot"]
|
|
12429
|
+
});
|
|
12430
|
+
return runPrune({ db: this.#db, domain: "workflows", targets, options, logger: this.logger });
|
|
12431
|
+
}
|
|
11915
12432
|
async setupPragmaSettings() {
|
|
11916
12433
|
try {
|
|
11917
12434
|
await this.#client.execute("PRAGMA busy_timeout = 10000;");
|
|
@@ -12767,7 +13284,7 @@ var LibSQLStore = class extends storage.MastraCompositeStore {
|
|
|
12767
13284
|
if (!config.id || typeof config.id !== "string" || config.id.trim() === "") {
|
|
12768
13285
|
throw new Error("LibSQLStore: id must be provided and cannot be empty.");
|
|
12769
13286
|
}
|
|
12770
|
-
super({ id: config.id, name: `LibSQLStore`, disableInit: config.disableInit });
|
|
13287
|
+
super({ id: config.id, name: `LibSQLStore`, disableInit: config.disableInit, retention: config.retention });
|
|
12771
13288
|
this.maxRetries = config.maxRetries ?? 5;
|
|
12772
13289
|
this.initialBackoffMs = config.initialBackoffMs ?? 100;
|
|
12773
13290
|
this.connectionTimeoutMs = config.connectionTimeoutMs ?? DEFAULT_CONNECTION_TIMEOUT_MS;
|