@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.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createClient } from '@libsql/client';
|
|
2
2
|
import { MastraError, ErrorCategory, ErrorDomain } from '@mastra/core/error';
|
|
3
|
-
import { createVectorErrorId, AgentsStorage, AGENTS_SCHEMA, TABLE_AGENTS, AGENT_VERSIONS_SCHEMA, TABLE_AGENT_VERSIONS, createStorageErrorId, normalizePerPage, calculatePagination, BackgroundTasksStorage,
|
|
3
|
+
import { createVectorErrorId, AgentsStorage, AGENTS_SCHEMA, TABLE_AGENTS, AGENT_VERSIONS_SCHEMA, TABLE_AGENT_VERSIONS, createStorageErrorId, normalizePerPage, calculatePagination, BackgroundTasksStorage, TABLE_BACKGROUND_TASKS, TABLE_SCHEMAS, BlobStore, TABLE_SKILL_BLOBS, SKILL_BLOBS_SCHEMA, ChannelsStorage, TABLE_CHANNEL_INSTALLATIONS, TABLE_CHANNEL_CONFIG, DatasetsStorage, DATASETS_SCHEMA, TABLE_DATASETS, DATASET_ITEMS_SCHEMA, TABLE_DATASET_ITEMS, DATASET_VERSIONS_SCHEMA, TABLE_DATASET_VERSIONS, TABLE_EXPERIMENTS, TABLE_EXPERIMENT_RESULTS, ensureDate, safelyParseJSON, ExperimentsStorage, EXPERIMENTS_SCHEMA, EXPERIMENT_RESULTS_SCHEMA, FavoritesStorage, FAVORITES_SCHEMA, TABLE_FAVORITES, TABLE_SKILLS, HarnessStorage, TABLE_HARNESS_SESSIONS, MCPClientsStorage, MCP_CLIENTS_SCHEMA, TABLE_MCP_CLIENTS, MCP_CLIENT_VERSIONS_SCHEMA, TABLE_MCP_CLIENT_VERSIONS, MCPServersStorage, MCP_SERVERS_SCHEMA, TABLE_MCP_SERVERS, MCP_SERVER_VERSIONS_SCHEMA, TABLE_MCP_SERVER_VERSIONS, MemoryStorage, TABLE_THREADS, TABLE_RESOURCES, TABLE_MESSAGES, OBSERVATIONAL_MEMORY_TABLE_SCHEMA, NotificationsStorage, TABLE_NOTIFICATIONS, ObservabilityStorage, TABLE_SPANS, SPAN_SCHEMA, listTracesArgsSchema, toTraceSpans, PromptBlocksStorage, PROMPT_BLOCKS_SCHEMA, TABLE_PROMPT_BLOCKS, PROMPT_BLOCK_VERSIONS_SCHEMA, TABLE_PROMPT_BLOCK_VERSIONS, SchedulesStorage, TABLE_SCHEDULE_TRIGGERS, TABLE_SCHEDULES, ScorerDefinitionsStorage, SCORER_DEFINITIONS_SCHEMA, TABLE_SCORER_DEFINITIONS, SCORER_DEFINITION_VERSIONS_SCHEMA, TABLE_SCORER_DEFINITION_VERSIONS, ScoresStorage, TABLE_SCORERS, SCORERS_SCHEMA, transformScoreRow, SkillsStorage, SKILLS_SCHEMA, SKILL_VERSIONS_SCHEMA, TABLE_SKILL_VERSIONS, ThreadStateStorage, TABLE_THREAD_STATE, THREAD_STATE_SCHEMA, ToolProviderConnectionsStorage, TOOL_PROVIDER_CONNECTIONS_SCHEMA, TABLE_TOOL_PROVIDER_CONNECTIONS, WorkflowsStorage, TABLE_WORKFLOW_SNAPSHOT, WorkspacesStorage, WORKSPACES_SCHEMA, TABLE_WORKSPACES, WORKSPACE_VERSIONS_SCHEMA, TABLE_WORKSPACE_VERSIONS, MastraCompositeStore, getSqlType, parseDuration, TraceStatus, mergeWorkflowStepResult } from '@mastra/core/storage';
|
|
4
4
|
import { parseSqlIdentifier, parseFieldKey } from '@mastra/core/utils';
|
|
5
5
|
import { MastraVector, validateTopK, validateUpsertInput } from '@mastra/core/vector';
|
|
6
6
|
import { BaseFilterTranslator } from '@mastra/core/vector/filter';
|
|
@@ -1456,6 +1456,11 @@ function resolveClient(config) {
|
|
|
1456
1456
|
...isLocal ? { timeout } : {}
|
|
1457
1457
|
});
|
|
1458
1458
|
}
|
|
1459
|
+
function assertPositiveLimit(limit) {
|
|
1460
|
+
if (!Number.isSafeInteger(limit) || limit <= 0) {
|
|
1461
|
+
throw new Error(`prune limit must be a positive integer; received ${limit}`);
|
|
1462
|
+
}
|
|
1463
|
+
}
|
|
1459
1464
|
var LibSQLDB = class extends MastraBase {
|
|
1460
1465
|
client;
|
|
1461
1466
|
maxRetries;
|
|
@@ -2276,6 +2281,102 @@ Note: This migration may take some time for large tables.
|
|
|
2276
2281
|
this.logger?.error?.(mastraError.toString());
|
|
2277
2282
|
}
|
|
2278
2283
|
}
|
|
2284
|
+
// ---------------------------------------------------------------------------
|
|
2285
|
+
// Retention helpers (prune)
|
|
2286
|
+
//
|
|
2287
|
+
// Low-level SQL primitives the memory and observability domains build their
|
|
2288
|
+
// `prune()` on, plus a plain user-invoked `VACUUM`. Keeping the SQL here
|
|
2289
|
+
// reuses the existing write-lock + retry machinery and keeps identifier
|
|
2290
|
+
// parsing in one place.
|
|
2291
|
+
// ---------------------------------------------------------------------------
|
|
2292
|
+
/**
|
|
2293
|
+
* Delete up to `limit` of the oldest rows whose `column` timestamp is strictly
|
|
2294
|
+
* before `cutoffMs`, in a single bounded statement. Returns the number of rows
|
|
2295
|
+
* removed so the caller can decide whether more batches remain.
|
|
2296
|
+
*
|
|
2297
|
+
* Deletes by `rowid` so the range scan is bounded by LIMIT and the write set
|
|
2298
|
+
* stays small — short locks, bounded WAL growth. Requires an index on
|
|
2299
|
+
* `column` to be fast at scale (see ensureIndex).
|
|
2300
|
+
*/
|
|
2301
|
+
async pruneBatch({
|
|
2302
|
+
tableName,
|
|
2303
|
+
column,
|
|
2304
|
+
cutoff,
|
|
2305
|
+
limit
|
|
2306
|
+
}) {
|
|
2307
|
+
assertPositiveLimit(limit);
|
|
2308
|
+
const parsedTable = parseSqlIdentifier(tableName, "table name");
|
|
2309
|
+
const parsedColumn = parseSqlIdentifier(column, "column name");
|
|
2310
|
+
const sql = `DELETE FROM "${parsedTable}" WHERE rowid IN (SELECT rowid FROM "${parsedTable}" WHERE "${parsedColumn}" < ? LIMIT ?)`;
|
|
2311
|
+
const result = await this.executeWriteOperationWithRetry(
|
|
2312
|
+
() => withClientWriteLock(this.client, () => this.client.execute({ sql, args: [cutoff, limit] })),
|
|
2313
|
+
`prune ${tableName}`
|
|
2314
|
+
);
|
|
2315
|
+
return Number(result.rowsAffected ?? 0);
|
|
2316
|
+
}
|
|
2317
|
+
/**
|
|
2318
|
+
* Delete up to `limit` aged parent rows *and* their child rows together, in a
|
|
2319
|
+
* single transaction. Used for whole-unit (parent-driven) pruning where
|
|
2320
|
+
* children must die with their parent (e.g. an aged experiment and its
|
|
2321
|
+
* experiment_results) — deleting per unit means a bound or abort between
|
|
2322
|
+
* batches never leaves a parent hollow (kept, but with its children gone) or
|
|
2323
|
+
* children orphaned.
|
|
2324
|
+
*
|
|
2325
|
+
* Both deletes target the same parent set via an identical deterministic
|
|
2326
|
+
* subquery (`ORDER BY rowid LIMIT ?`); the batch runs as one write
|
|
2327
|
+
* transaction, so the set cannot change between the two statements.
|
|
2328
|
+
*/
|
|
2329
|
+
async pruneUnitsBatch({
|
|
2330
|
+
parentTable,
|
|
2331
|
+
parentKey,
|
|
2332
|
+
parentColumn,
|
|
2333
|
+
childTable,
|
|
2334
|
+
childForeignKey,
|
|
2335
|
+
cutoff,
|
|
2336
|
+
limit
|
|
2337
|
+
}) {
|
|
2338
|
+
assertPositiveLimit(limit);
|
|
2339
|
+
const parsedChild = parseSqlIdentifier(childTable, "table name");
|
|
2340
|
+
const parsedChildFk = parseSqlIdentifier(childForeignKey, "column name");
|
|
2341
|
+
const parsedParent = parseSqlIdentifier(parentTable, "table name");
|
|
2342
|
+
const parsedParentKey = parseSqlIdentifier(parentKey, "column name");
|
|
2343
|
+
const parsedParentColumn = parseSqlIdentifier(parentColumn, "column name");
|
|
2344
|
+
const agedParents = `SELECT "${parsedParentKey}" FROM "${parsedParent}" WHERE "${parsedParentColumn}" < ? ORDER BY rowid LIMIT ?`;
|
|
2345
|
+
const results = await this.executeWriteOperationWithRetry(
|
|
2346
|
+
() => withClientWriteLock(
|
|
2347
|
+
this.client,
|
|
2348
|
+
() => this.client.batch(
|
|
2349
|
+
[
|
|
2350
|
+
{
|
|
2351
|
+
sql: `DELETE FROM "${parsedChild}" WHERE "${parsedChildFk}" IN (${agedParents})`,
|
|
2352
|
+
args: [cutoff, limit]
|
|
2353
|
+
},
|
|
2354
|
+
{
|
|
2355
|
+
sql: `DELETE FROM "${parsedParent}" WHERE "${parsedParentKey}" IN (${agedParents})`,
|
|
2356
|
+
args: [cutoff, limit]
|
|
2357
|
+
}
|
|
2358
|
+
],
|
|
2359
|
+
"write"
|
|
2360
|
+
)
|
|
2361
|
+
),
|
|
2362
|
+
`prune units ${parentTable}`
|
|
2363
|
+
);
|
|
2364
|
+
return {
|
|
2365
|
+
children: Number(results[0]?.rowsAffected ?? 0),
|
|
2366
|
+
parents: Number(results[1]?.rowsAffected ?? 0)
|
|
2367
|
+
};
|
|
2368
|
+
}
|
|
2369
|
+
/** Create an index if it does not already exist. */
|
|
2370
|
+
async ensureIndex({
|
|
2371
|
+
indexName,
|
|
2372
|
+
tableName,
|
|
2373
|
+
column
|
|
2374
|
+
}) {
|
|
2375
|
+
const parsedTable = parseSqlIdentifier(tableName, "table name");
|
|
2376
|
+
const parsedColumn = parseSqlIdentifier(column, "column name");
|
|
2377
|
+
const parsedIndex = parseSqlIdentifier(indexName, "index name");
|
|
2378
|
+
await this.client.execute(`CREATE INDEX IF NOT EXISTS "${parsedIndex}" ON "${parsedTable}" ("${parsedColumn}")`);
|
|
2379
|
+
}
|
|
2279
2380
|
};
|
|
2280
2381
|
var AgentsLibSQL = class extends AgentsStorage {
|
|
2281
2382
|
#db;
|
|
@@ -3113,6 +3214,113 @@ var AgentsLibSQL = class extends AgentsStorage {
|
|
|
3113
3214
|
};
|
|
3114
3215
|
}
|
|
3115
3216
|
};
|
|
3217
|
+
var DEFAULT_BATCH_SIZE = 1e3;
|
|
3218
|
+
async function ensureAnchorIndex(db, target, logger) {
|
|
3219
|
+
if (!target.indexed) return;
|
|
3220
|
+
try {
|
|
3221
|
+
await db.ensureIndex({
|
|
3222
|
+
indexName: `idx_retention_${target.table}_${target.column}`,
|
|
3223
|
+
tableName: target.table,
|
|
3224
|
+
column: target.column
|
|
3225
|
+
});
|
|
3226
|
+
} catch (error) {
|
|
3227
|
+
logger?.warn?.(`Failed to ensure retention index on ${target.table}(${target.column}):`, error);
|
|
3228
|
+
}
|
|
3229
|
+
}
|
|
3230
|
+
async function sleep(ms, signal) {
|
|
3231
|
+
if (ms <= 0 || signal?.aborted) return;
|
|
3232
|
+
await new Promise((resolve) => {
|
|
3233
|
+
const timer = setTimeout(() => {
|
|
3234
|
+
signal?.removeEventListener("abort", onAbort);
|
|
3235
|
+
resolve();
|
|
3236
|
+
}, ms);
|
|
3237
|
+
const onAbort = () => {
|
|
3238
|
+
clearTimeout(timer);
|
|
3239
|
+
resolve();
|
|
3240
|
+
};
|
|
3241
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
3242
|
+
});
|
|
3243
|
+
}
|
|
3244
|
+
function cutoffFor(policy, anchorType, now = Date.now()) {
|
|
3245
|
+
const cutoffMs = now - parseDuration(policy.maxAge);
|
|
3246
|
+
return anchorType === "epoch-ms" ? cutoffMs : new Date(cutoffMs).toISOString();
|
|
3247
|
+
}
|
|
3248
|
+
async function runBatchedDelete({
|
|
3249
|
+
deleteBatch,
|
|
3250
|
+
batchSize,
|
|
3251
|
+
options
|
|
3252
|
+
}) {
|
|
3253
|
+
if (!Number.isSafeInteger(batchSize) || batchSize <= 0) {
|
|
3254
|
+
throw new Error(`retention batchSize must be a positive integer; received ${batchSize}`);
|
|
3255
|
+
}
|
|
3256
|
+
let deleted = 0;
|
|
3257
|
+
let batches = 0;
|
|
3258
|
+
while (true) {
|
|
3259
|
+
if (options?.signal?.aborted) return { deleted, done: false };
|
|
3260
|
+
if (options?.maxBatches !== void 0 && batches >= options.maxBatches) return { deleted, done: false };
|
|
3261
|
+
let limit = batchSize;
|
|
3262
|
+
if (options?.maxRows !== void 0) {
|
|
3263
|
+
const remaining = options.maxRows - deleted;
|
|
3264
|
+
if (remaining <= 0) return { deleted, done: false };
|
|
3265
|
+
limit = Math.min(limit, remaining);
|
|
3266
|
+
}
|
|
3267
|
+
const affected = await deleteBatch(limit);
|
|
3268
|
+
deleted += affected;
|
|
3269
|
+
batches += 1;
|
|
3270
|
+
if (affected < limit) return { deleted, done: true };
|
|
3271
|
+
if (options?.pauseMs) {
|
|
3272
|
+
await sleep(options.pauseMs, options.signal);
|
|
3273
|
+
}
|
|
3274
|
+
}
|
|
3275
|
+
}
|
|
3276
|
+
async function runPrune({
|
|
3277
|
+
db,
|
|
3278
|
+
domain,
|
|
3279
|
+
targets,
|
|
3280
|
+
options,
|
|
3281
|
+
logger
|
|
3282
|
+
}) {
|
|
3283
|
+
const results = [];
|
|
3284
|
+
const now = Date.now();
|
|
3285
|
+
for (const target of targets) {
|
|
3286
|
+
if (options?.signal?.aborted) {
|
|
3287
|
+
results.push({ domain, table: target.table, deleted: 0, done: false });
|
|
3288
|
+
continue;
|
|
3289
|
+
}
|
|
3290
|
+
await ensureAnchorIndex(db, target, logger);
|
|
3291
|
+
const cutoff = cutoffFor(target.policy, target.anchorType, now);
|
|
3292
|
+
const batchSize = target.policy.batchSize ?? DEFAULT_BATCH_SIZE;
|
|
3293
|
+
const { deleted, done } = await runBatchedDelete({
|
|
3294
|
+
deleteBatch: (limit) => db.pruneBatch({ tableName: target.table, column: target.column, cutoff, limit }),
|
|
3295
|
+
batchSize,
|
|
3296
|
+
options
|
|
3297
|
+
});
|
|
3298
|
+
results.push({ domain, table: target.table, deleted, done });
|
|
3299
|
+
}
|
|
3300
|
+
return results;
|
|
3301
|
+
}
|
|
3302
|
+
function resolveTargets({
|
|
3303
|
+
policies,
|
|
3304
|
+
descriptor,
|
|
3305
|
+
order
|
|
3306
|
+
}) {
|
|
3307
|
+
const targets = [];
|
|
3308
|
+
for (const key of order) {
|
|
3309
|
+
const policy = policies[key];
|
|
3310
|
+
const entry = descriptor[key];
|
|
3311
|
+
if (!policy || !entry) continue;
|
|
3312
|
+
targets.push({
|
|
3313
|
+
table: entry.table,
|
|
3314
|
+
column: entry.column,
|
|
3315
|
+
anchorType: entry.anchorType ?? "timestamp",
|
|
3316
|
+
indexed: entry.indexed ?? true,
|
|
3317
|
+
policy
|
|
3318
|
+
});
|
|
3319
|
+
}
|
|
3320
|
+
return targets;
|
|
3321
|
+
}
|
|
3322
|
+
|
|
3323
|
+
// src/storage/domains/background-tasks/index.ts
|
|
3116
3324
|
function serializeJson(v) {
|
|
3117
3325
|
if (typeof v === "object" && v != null) return JSON.stringify(v);
|
|
3118
3326
|
return v ?? null;
|
|
@@ -3151,7 +3359,15 @@ function rowToTask(row) {
|
|
|
3151
3359
|
completedAt: row.completedAt ? new Date(String(row.completedAt)) : void 0
|
|
3152
3360
|
};
|
|
3153
3361
|
}
|
|
3154
|
-
var BackgroundTasksLibSQL = class extends BackgroundTasksStorage {
|
|
3362
|
+
var BackgroundTasksLibSQL = class _BackgroundTasksLibSQL extends BackgroundTasksStorage {
|
|
3363
|
+
/**
|
|
3364
|
+
* Completed/failed task records accumulate. Anchored on `completedAt`, which
|
|
3365
|
+
* is NULL while a task is in-flight (pending/running/suspended) — so `completedAt
|
|
3366
|
+
* < cutoff` never prunes a live task, no explicit status filter needed.
|
|
3367
|
+
*/
|
|
3368
|
+
static retentionTables = {
|
|
3369
|
+
backgroundTasks: { table: TABLE_BACKGROUND_TASKS, column: "completedAt", indexed: true }
|
|
3370
|
+
};
|
|
3155
3371
|
#db;
|
|
3156
3372
|
#client;
|
|
3157
3373
|
constructor(config) {
|
|
@@ -3174,6 +3390,15 @@ var BackgroundTasksLibSQL = class extends BackgroundTasksStorage {
|
|
|
3174
3390
|
async dangerouslyClearAll() {
|
|
3175
3391
|
await this.#db.deleteData({ tableName: TABLE_BACKGROUND_TASKS });
|
|
3176
3392
|
}
|
|
3393
|
+
/** Delete completed tasks older than the `backgroundTasks` policy's `maxAge`, batched. */
|
|
3394
|
+
async prune(policies, options) {
|
|
3395
|
+
const targets = resolveTargets({
|
|
3396
|
+
policies,
|
|
3397
|
+
descriptor: _BackgroundTasksLibSQL.retentionTables,
|
|
3398
|
+
order: ["backgroundTasks"]
|
|
3399
|
+
});
|
|
3400
|
+
return runPrune({ db: this.#db, domain: "backgroundTasks", targets, options, logger: this.logger });
|
|
3401
|
+
}
|
|
3177
3402
|
async createTask(task) {
|
|
3178
3403
|
await this.#db.insert({
|
|
3179
3404
|
tableName: TABLE_BACKGROUND_TASKS,
|
|
@@ -3599,6 +3824,30 @@ var ChannelsLibSQL = class extends ChannelsStorage {
|
|
|
3599
3824
|
};
|
|
3600
3825
|
}
|
|
3601
3826
|
};
|
|
3827
|
+
|
|
3828
|
+
// src/storage/domains/utils.ts
|
|
3829
|
+
function tenancyWhere(filters) {
|
|
3830
|
+
const conditions = [];
|
|
3831
|
+
const params = [];
|
|
3832
|
+
if (filters?.organizationId !== void 0) {
|
|
3833
|
+
conditions.push("organizationId = ?");
|
|
3834
|
+
params.push(filters.organizationId);
|
|
3835
|
+
}
|
|
3836
|
+
if (filters?.projectId !== void 0) {
|
|
3837
|
+
conditions.push("projectId = ?");
|
|
3838
|
+
params.push(filters.projectId);
|
|
3839
|
+
}
|
|
3840
|
+
return { conditions, params };
|
|
3841
|
+
}
|
|
3842
|
+
function buildScopedWhere(idColumn, idValue, filters) {
|
|
3843
|
+
const { conditions, params } = tenancyWhere(filters);
|
|
3844
|
+
return {
|
|
3845
|
+
sql: [`${idColumn} = ?`, ...conditions].join(" AND "),
|
|
3846
|
+
args: [idValue, ...params]
|
|
3847
|
+
};
|
|
3848
|
+
}
|
|
3849
|
+
|
|
3850
|
+
// src/storage/domains/datasets/index.ts
|
|
3602
3851
|
function jsonbArg(value) {
|
|
3603
3852
|
return value === void 0 || value === null ? null : JSON.stringify(value);
|
|
3604
3853
|
}
|
|
@@ -3679,6 +3928,18 @@ var DatasetsLibSQL = class extends DatasetsStorage {
|
|
|
3679
3928
|
await this.#db.deleteData({ tableName: TABLE_DATASET_ITEMS });
|
|
3680
3929
|
await this.#db.deleteData({ tableName: TABLE_DATASETS });
|
|
3681
3930
|
}
|
|
3931
|
+
async experimentTablesExist() {
|
|
3932
|
+
try {
|
|
3933
|
+
const result = await this.#client.execute({
|
|
3934
|
+
sql: `SELECT COUNT(*) AS c FROM sqlite_master WHERE type = 'table' AND name IN (?, ?)`,
|
|
3935
|
+
args: [TABLE_EXPERIMENTS, TABLE_EXPERIMENT_RESULTS]
|
|
3936
|
+
});
|
|
3937
|
+
const row = result.rows?.[0];
|
|
3938
|
+
return Number(row?.c ?? 0) === 2;
|
|
3939
|
+
} catch {
|
|
3940
|
+
return false;
|
|
3941
|
+
}
|
|
3942
|
+
}
|
|
3682
3943
|
// --- Row transformers ---
|
|
3683
3944
|
transformDatasetRow(row) {
|
|
3684
3945
|
return {
|
|
@@ -3765,8 +4026,8 @@ var DatasetsLibSQL = class extends DatasetsStorage {
|
|
|
3765
4026
|
groundTruthSchema: input.groundTruthSchema ?? null,
|
|
3766
4027
|
requestContextSchema: input.requestContextSchema ?? null,
|
|
3767
4028
|
targetType: input.targetType ?? null,
|
|
3768
|
-
targetIds: input.targetIds
|
|
3769
|
-
scorerIds: input.scorerIds
|
|
4029
|
+
targetIds: input.targetIds ?? null,
|
|
4030
|
+
scorerIds: input.scorerIds ?? null,
|
|
3770
4031
|
version: 0,
|
|
3771
4032
|
organizationId: input.organizationId ?? null,
|
|
3772
4033
|
projectId: input.projectId ?? null,
|
|
@@ -3806,11 +4067,16 @@ var DatasetsLibSQL = class extends DatasetsStorage {
|
|
|
3806
4067
|
);
|
|
3807
4068
|
}
|
|
3808
4069
|
}
|
|
3809
|
-
async getDatasetById({
|
|
4070
|
+
async getDatasetById({
|
|
4071
|
+
id,
|
|
4072
|
+
filters
|
|
4073
|
+
}) {
|
|
3810
4074
|
try {
|
|
4075
|
+
const { conditions, params } = tenancyWhere(filters);
|
|
4076
|
+
const whereSql = ["id = ?", ...conditions].join(" AND ");
|
|
3811
4077
|
const result = await this.#client.execute({
|
|
3812
|
-
sql: `SELECT ${buildSelectColumns(TABLE_DATASETS)} FROM ${TABLE_DATASETS} WHERE
|
|
3813
|
-
args: [id]
|
|
4078
|
+
sql: `SELECT ${buildSelectColumns(TABLE_DATASETS)} FROM ${TABLE_DATASETS} WHERE ${whereSql}`,
|
|
4079
|
+
args: [id, ...params]
|
|
3814
4080
|
});
|
|
3815
4081
|
return result.rows?.[0] ? this.transformDatasetRow(result.rows[0]) : null;
|
|
3816
4082
|
} catch (error) {
|
|
@@ -3826,7 +4092,7 @@ var DatasetsLibSQL = class extends DatasetsStorage {
|
|
|
3826
4092
|
}
|
|
3827
4093
|
async _doUpdateDataset(args) {
|
|
3828
4094
|
try {
|
|
3829
|
-
const existing = await this.getDatasetById({ id: args.id });
|
|
4095
|
+
const existing = await this.getDatasetById({ id: args.id, filters: args.filters });
|
|
3830
4096
|
if (!existing) {
|
|
3831
4097
|
throw new MastraError({
|
|
3832
4098
|
id: createStorageErrorId("LIBSQL", "UPDATE_DATASET", "NOT_FOUND"),
|
|
@@ -3909,30 +4175,31 @@ var DatasetsLibSQL = class extends DatasetsStorage {
|
|
|
3909
4175
|
);
|
|
3910
4176
|
}
|
|
3911
4177
|
}
|
|
3912
|
-
async deleteDataset({ id }) {
|
|
4178
|
+
async deleteDataset({ id, filters }) {
|
|
3913
4179
|
try {
|
|
3914
|
-
|
|
3915
|
-
|
|
4180
|
+
const { conditions, params } = tenancyWhere(filters);
|
|
4181
|
+
const scopedWhere = ["id = ?", ...conditions].join(" AND ");
|
|
4182
|
+
const exists = await this.#client.execute({
|
|
4183
|
+
sql: `SELECT id FROM ${TABLE_DATASETS} WHERE ${scopedWhere}`,
|
|
4184
|
+
args: [id, ...params]
|
|
4185
|
+
});
|
|
4186
|
+
if (!exists.rows?.[0]) return;
|
|
4187
|
+
const experimentTablesExist = await this.experimentTablesExist();
|
|
4188
|
+
const statements = [];
|
|
4189
|
+
if (experimentTablesExist) {
|
|
4190
|
+
statements.push({
|
|
3916
4191
|
sql: `DELETE FROM ${TABLE_EXPERIMENT_RESULTS} WHERE experimentId IN (SELECT id FROM ${TABLE_EXPERIMENTS} WHERE datasetId = ?)`,
|
|
3917
4192
|
args: [id]
|
|
3918
4193
|
});
|
|
3919
|
-
|
|
3920
|
-
}
|
|
3921
|
-
try {
|
|
3922
|
-
await this.#client.execute({
|
|
4194
|
+
statements.push({
|
|
3923
4195
|
sql: `UPDATE ${TABLE_EXPERIMENTS} SET datasetId = NULL, datasetVersion = NULL WHERE datasetId = ?`,
|
|
3924
4196
|
args: [id]
|
|
3925
4197
|
});
|
|
3926
|
-
} catch {
|
|
3927
4198
|
}
|
|
3928
|
-
|
|
3929
|
-
|
|
3930
|
-
|
|
3931
|
-
|
|
3932
|
-
{ sql: `DELETE FROM ${TABLE_DATASETS} WHERE id = ?`, args: [id] }
|
|
3933
|
-
],
|
|
3934
|
-
"write"
|
|
3935
|
-
);
|
|
4199
|
+
statements.push({ sql: `DELETE FROM ${TABLE_DATASET_VERSIONS} WHERE datasetId = ?`, args: [id] });
|
|
4200
|
+
statements.push({ sql: `DELETE FROM ${TABLE_DATASET_ITEMS} WHERE datasetId = ?`, args: [id] });
|
|
4201
|
+
statements.push({ sql: `DELETE FROM ${TABLE_DATASETS} WHERE ${scopedWhere}`, args: [id, ...params] });
|
|
4202
|
+
await this.#client.batch(statements, "write");
|
|
3936
4203
|
} catch (error) {
|
|
3937
4204
|
throw new MastraError(
|
|
3938
4205
|
{
|
|
@@ -3965,6 +4232,21 @@ var DatasetsLibSQL = class extends DatasetsStorage {
|
|
|
3965
4232
|
filterConditions.push("candidateId = ?");
|
|
3966
4233
|
filterParams.push(args.filters.candidateId);
|
|
3967
4234
|
}
|
|
4235
|
+
if (args.filters?.targetType !== void 0) {
|
|
4236
|
+
filterConditions.push("targetType = ?");
|
|
4237
|
+
filterParams.push(args.filters.targetType);
|
|
4238
|
+
}
|
|
4239
|
+
if (args.filters?.targetIds !== void 0 && args.filters.targetIds.length > 0) {
|
|
4240
|
+
const placeholders = args.filters.targetIds.map(() => "?").join(",");
|
|
4241
|
+
filterConditions.push(
|
|
4242
|
+
`EXISTS (SELECT 1 FROM json_each(${TABLE_DATASETS}.targetIds) WHERE value IN (${placeholders}))`
|
|
4243
|
+
);
|
|
4244
|
+
for (const id of args.filters.targetIds) filterParams.push(id);
|
|
4245
|
+
}
|
|
4246
|
+
if (args.filters?.name !== void 0 && args.filters.name.length > 0) {
|
|
4247
|
+
filterConditions.push("LOWER(name) LIKE ?");
|
|
4248
|
+
filterParams.push(`%${args.filters.name.toLowerCase()}%`);
|
|
4249
|
+
}
|
|
3968
4250
|
const whereClause = filterConditions.length > 0 ? `WHERE ${filterConditions.join(" AND ")}` : "";
|
|
3969
4251
|
const countResult = await this.#client.execute({
|
|
3970
4252
|
sql: `SELECT COUNT(*) as count FROM ${TABLE_DATASETS} ${whereClause}`,
|
|
@@ -4624,7 +4906,18 @@ var DatasetsLibSQL = class extends DatasetsStorage {
|
|
|
4624
4906
|
}
|
|
4625
4907
|
}
|
|
4626
4908
|
};
|
|
4909
|
+
var DEFAULT_PRUNE_BATCH_SIZE = 1e3;
|
|
4627
4910
|
var ExperimentsLibSQL = class extends ExperimentsStorage {
|
|
4911
|
+
/**
|
|
4912
|
+
* An experiment is pruned as a whole unit: when `experiments.completedAt` is
|
|
4913
|
+
* older than the policy, the run and all its `experiment_results` rows are
|
|
4914
|
+
* deleted together (results cascade with their parent, matching
|
|
4915
|
+
* `deleteExperiment`). Results are not an independent retention key. NULL
|
|
4916
|
+
* `completedAt` (still running) is never pruned.
|
|
4917
|
+
*/
|
|
4918
|
+
static retentionTables = {
|
|
4919
|
+
experiments: { table: TABLE_EXPERIMENTS, column: "completedAt", indexed: true }
|
|
4920
|
+
};
|
|
4628
4921
|
#db;
|
|
4629
4922
|
#client;
|
|
4630
4923
|
constructor(config) {
|
|
@@ -4680,6 +4973,58 @@ var ExperimentsLibSQL = class extends ExperimentsStorage {
|
|
|
4680
4973
|
await this.#db.deleteData({ tableName: TABLE_EXPERIMENT_RESULTS });
|
|
4681
4974
|
await this.#db.deleteData({ tableName: TABLE_EXPERIMENTS });
|
|
4682
4975
|
}
|
|
4976
|
+
/**
|
|
4977
|
+
* Prune whole experiments older than the `experiments` policy's `maxAge`.
|
|
4978
|
+
*
|
|
4979
|
+
* Each batch selects up to `batchSize` aged experiments and deletes their
|
|
4980
|
+
* `experiment_results` rows and the experiment rows in one transaction —
|
|
4981
|
+
* mirroring `deleteExperiment` — so hitting `maxBatches`/`maxRows` or the
|
|
4982
|
+
* abort signal between batches never leaves a run hollow (parent kept,
|
|
4983
|
+
* results gone). NULL `completedAt` (still running) is excluded by the
|
|
4984
|
+
* `< cutoff` predicate. Bounds count whole experiments, not rows.
|
|
4985
|
+
*/
|
|
4986
|
+
async prune(policies, options) {
|
|
4987
|
+
const policy = policies["experiments"];
|
|
4988
|
+
if (!policy || options?.signal?.aborted) {
|
|
4989
|
+
return policy ? [
|
|
4990
|
+
{ domain: "experiments", table: TABLE_EXPERIMENT_RESULTS, deleted: 0, done: false },
|
|
4991
|
+
{ domain: "experiments", table: TABLE_EXPERIMENTS, deleted: 0, done: false }
|
|
4992
|
+
] : [];
|
|
4993
|
+
}
|
|
4994
|
+
try {
|
|
4995
|
+
await this.#db.ensureIndex({
|
|
4996
|
+
indexName: `idx_retention_${TABLE_EXPERIMENTS}_completedAt`,
|
|
4997
|
+
tableName: TABLE_EXPERIMENTS,
|
|
4998
|
+
column: "completedAt"
|
|
4999
|
+
});
|
|
5000
|
+
} catch (error) {
|
|
5001
|
+
this.logger?.warn?.(`Failed to ensure retention index on ${TABLE_EXPERIMENTS}(completedAt):`, error);
|
|
5002
|
+
}
|
|
5003
|
+
const cutoff = cutoffFor(policy, "timestamp");
|
|
5004
|
+
const batchSize = policy.batchSize ?? DEFAULT_PRUNE_BATCH_SIZE;
|
|
5005
|
+
let childDeleted = 0;
|
|
5006
|
+
const parent = await runBatchedDelete({
|
|
5007
|
+
deleteBatch: async (limit) => {
|
|
5008
|
+
const { parents, children } = await this.#db.pruneUnitsBatch({
|
|
5009
|
+
parentTable: TABLE_EXPERIMENTS,
|
|
5010
|
+
parentKey: "id",
|
|
5011
|
+
parentColumn: "completedAt",
|
|
5012
|
+
childTable: TABLE_EXPERIMENT_RESULTS,
|
|
5013
|
+
childForeignKey: "experimentId",
|
|
5014
|
+
cutoff,
|
|
5015
|
+
limit
|
|
5016
|
+
});
|
|
5017
|
+
childDeleted += children;
|
|
5018
|
+
return parents;
|
|
5019
|
+
},
|
|
5020
|
+
batchSize,
|
|
5021
|
+
options
|
|
5022
|
+
});
|
|
5023
|
+
return [
|
|
5024
|
+
{ domain: "experiments", table: TABLE_EXPERIMENT_RESULTS, deleted: childDeleted, done: parent.done },
|
|
5025
|
+
{ domain: "experiments", table: TABLE_EXPERIMENTS, deleted: parent.deleted, done: parent.done }
|
|
5026
|
+
];
|
|
5027
|
+
}
|
|
4683
5028
|
// Helper to transform row to Experiment
|
|
4684
5029
|
transformExperimentRow(row) {
|
|
4685
5030
|
return {
|
|
@@ -4867,9 +5212,10 @@ var ExperimentsLibSQL = class extends ExperimentsStorage {
|
|
|
4867
5212
|
}
|
|
4868
5213
|
async getExperimentById(args) {
|
|
4869
5214
|
try {
|
|
5215
|
+
const scoped = buildScopedWhere("id", args.id, args.filters);
|
|
4870
5216
|
const result = await this.#client.execute({
|
|
4871
|
-
sql: `SELECT ${buildSelectColumns(TABLE_EXPERIMENTS)} FROM ${TABLE_EXPERIMENTS} WHERE
|
|
4872
|
-
args:
|
|
5217
|
+
sql: `SELECT ${buildSelectColumns(TABLE_EXPERIMENTS)} FROM ${TABLE_EXPERIMENTS} WHERE ${scoped.sql}`,
|
|
5218
|
+
args: scoped.args
|
|
4873
5219
|
});
|
|
4874
5220
|
return result.rows?.[0] ? this.transformExperimentRow(result.rows[0]) : null;
|
|
4875
5221
|
} catch (error) {
|
|
@@ -4961,14 +5307,17 @@ var ExperimentsLibSQL = class extends ExperimentsStorage {
|
|
|
4961
5307
|
}
|
|
4962
5308
|
async deleteExperiment(args) {
|
|
4963
5309
|
try {
|
|
4964
|
-
|
|
4965
|
-
|
|
4966
|
-
|
|
4967
|
-
|
|
4968
|
-
await this.#client.
|
|
4969
|
-
|
|
4970
|
-
|
|
4971
|
-
|
|
5310
|
+
const parentScoped = buildScopedWhere("id", args.id, args.filters);
|
|
5311
|
+
const { conditions, params } = tenancyWhere(args.filters);
|
|
5312
|
+
const cascadeWhere = conditions.length ? `experimentId IN (SELECT id FROM ${TABLE_EXPERIMENTS} WHERE ${["id = ?", ...conditions].join(" AND ")})` : `experimentId = ?`;
|
|
5313
|
+
const cascadeArgs = conditions.length ? [args.id, ...params] : [args.id];
|
|
5314
|
+
await this.#client.batch(
|
|
5315
|
+
[
|
|
5316
|
+
{ sql: `DELETE FROM ${TABLE_EXPERIMENT_RESULTS} WHERE ${cascadeWhere}`, args: cascadeArgs },
|
|
5317
|
+
{ sql: `DELETE FROM ${TABLE_EXPERIMENTS} WHERE ${parentScoped.sql}`, args: parentScoped.args }
|
|
5318
|
+
],
|
|
5319
|
+
"write"
|
|
5320
|
+
);
|
|
4972
5321
|
} catch (error) {
|
|
4973
5322
|
throw new MastraError(
|
|
4974
5323
|
{
|
|
@@ -5106,9 +5455,10 @@ var ExperimentsLibSQL = class extends ExperimentsStorage {
|
|
|
5106
5455
|
}
|
|
5107
5456
|
async getExperimentResultById(args) {
|
|
5108
5457
|
try {
|
|
5458
|
+
const scoped = buildScopedWhere("id", args.id, args.filters);
|
|
5109
5459
|
const result = await this.#client.execute({
|
|
5110
|
-
sql: `SELECT ${buildSelectColumns(TABLE_EXPERIMENT_RESULTS)} FROM ${TABLE_EXPERIMENT_RESULTS} WHERE
|
|
5111
|
-
args:
|
|
5460
|
+
sql: `SELECT ${buildSelectColumns(TABLE_EXPERIMENT_RESULTS)} FROM ${TABLE_EXPERIMENT_RESULTS} WHERE ${scoped.sql}`,
|
|
5461
|
+
args: scoped.args
|
|
5112
5462
|
});
|
|
5113
5463
|
return result.rows?.[0] ? this.transformExperimentResultRow(result.rows[0]) : null;
|
|
5114
5464
|
} catch (error) {
|
|
@@ -5188,6 +5538,14 @@ var ExperimentsLibSQL = class extends ExperimentsStorage {
|
|
|
5188
5538
|
}
|
|
5189
5539
|
async deleteExperimentResults(args) {
|
|
5190
5540
|
try {
|
|
5541
|
+
const { conditions, params } = tenancyWhere(args.filters);
|
|
5542
|
+
if (conditions.length) {
|
|
5543
|
+
await this.#client.execute({
|
|
5544
|
+
sql: `DELETE FROM ${TABLE_EXPERIMENT_RESULTS} WHERE experimentId IN (SELECT id FROM ${TABLE_EXPERIMENTS} WHERE ${["id = ?", ...conditions].join(" AND ")})`,
|
|
5545
|
+
args: [args.experimentId, ...params]
|
|
5546
|
+
});
|
|
5547
|
+
return;
|
|
5548
|
+
}
|
|
5191
5549
|
await this.#client.execute({
|
|
5192
5550
|
sql: `DELETE FROM ${TABLE_EXPERIMENT_RESULTS} WHERE experimentId = ?`,
|
|
5193
5551
|
args: [args.experimentId]
|
|
@@ -5567,7 +5925,11 @@ function sessionToRecord(record) {
|
|
|
5567
5925
|
deletedAt: record.deletedAt ?? null
|
|
5568
5926
|
};
|
|
5569
5927
|
}
|
|
5570
|
-
var HarnessLibSQL = class extends HarnessStorage {
|
|
5928
|
+
var HarnessLibSQL = class _HarnessLibSQL extends HarnessStorage {
|
|
5929
|
+
/** Session records accumulate over time. Single table, anchored on `createdAt`. */
|
|
5930
|
+
static retentionTables = {
|
|
5931
|
+
sessions: { table: TABLE_HARNESS_SESSIONS, column: "createdAt", indexed: true }
|
|
5932
|
+
};
|
|
5571
5933
|
#db;
|
|
5572
5934
|
constructor(config) {
|
|
5573
5935
|
super();
|
|
@@ -5583,6 +5945,15 @@ var HarnessLibSQL = class extends HarnessStorage {
|
|
|
5583
5945
|
async dangerouslyClearAll() {
|
|
5584
5946
|
await this.#db.deleteData({ tableName: TABLE_HARNESS_SESSIONS });
|
|
5585
5947
|
}
|
|
5948
|
+
/** Delete harness sessions older than the `sessions` policy's `maxAge`, batched. */
|
|
5949
|
+
async prune(policies, options) {
|
|
5950
|
+
const targets = resolveTargets({
|
|
5951
|
+
policies,
|
|
5952
|
+
descriptor: _HarnessLibSQL.retentionTables,
|
|
5953
|
+
order: ["sessions"]
|
|
5954
|
+
});
|
|
5955
|
+
return runPrune({ db: this.#db, domain: "harness", targets, options, logger: this.logger });
|
|
5956
|
+
}
|
|
5586
5957
|
async loadSession(sessionId) {
|
|
5587
5958
|
const row = await this.#db.select({
|
|
5588
5959
|
tableName: TABLE_HARNESS_SESSIONS,
|
|
@@ -6568,8 +6939,18 @@ var MCPServersLibSQL = class extends MCPServersStorage {
|
|
|
6568
6939
|
}
|
|
6569
6940
|
};
|
|
6570
6941
|
var OM_TABLE = "mastra_observational_memory";
|
|
6571
|
-
var MemoryLibSQL = class extends MemoryStorage {
|
|
6942
|
+
var MemoryLibSQL = class _MemoryLibSQL extends MemoryStorage {
|
|
6572
6943
|
supportsObservationalMemory = true;
|
|
6944
|
+
/**
|
|
6945
|
+
* Retention-eligible tables. `threads`, `messages`, and `resources` all anchor
|
|
6946
|
+
* on `createdAt` and are indexed for fast batched deletes. Cascade order is
|
|
6947
|
+
* enforced in `prune()` (children before threads), not here.
|
|
6948
|
+
*/
|
|
6949
|
+
static retentionTables = {
|
|
6950
|
+
messages: { table: TABLE_MESSAGES, column: "createdAt", indexed: true },
|
|
6951
|
+
resources: { table: TABLE_RESOURCES, column: "createdAt", indexed: true },
|
|
6952
|
+
threads: { table: TABLE_THREADS, column: "createdAt", indexed: true }
|
|
6953
|
+
};
|
|
6573
6954
|
#client;
|
|
6574
6955
|
#db;
|
|
6575
6956
|
constructor(config) {
|
|
@@ -6644,6 +7025,21 @@ var MemoryLibSQL = class extends MemoryStorage {
|
|
|
6644
7025
|
await this.#db.deleteData({ tableName: OM_TABLE });
|
|
6645
7026
|
}
|
|
6646
7027
|
}
|
|
7028
|
+
/**
|
|
7029
|
+
* Delete rows older than each policy's `maxAge`, batched and cancellable.
|
|
7030
|
+
*
|
|
7031
|
+
* Deletes children before parents (messages/resources before threads) so a
|
|
7032
|
+
* `threads` policy doesn't strand rows behind a deleted parent — there are no
|
|
7033
|
+
* FKs in the LibSQL schema, so cascade ordering is explicit here.
|
|
7034
|
+
*/
|
|
7035
|
+
async prune(policies, options) {
|
|
7036
|
+
const targets = resolveTargets({
|
|
7037
|
+
policies,
|
|
7038
|
+
descriptor: _MemoryLibSQL.retentionTables,
|
|
7039
|
+
order: ["messages", "resources", "threads"]
|
|
7040
|
+
});
|
|
7041
|
+
return runPrune({ db: this.#db, domain: "memory", targets, options, logger: this.logger });
|
|
7042
|
+
}
|
|
6647
7043
|
parseRow(row) {
|
|
6648
7044
|
let content = row.content;
|
|
6649
7045
|
try {
|
|
@@ -8710,7 +9106,11 @@ function addArrayFilter(conditions, args, column, value) {
|
|
|
8710
9106
|
conditions.push(`"${column}" IN (${values.map(() => "?").join(", ")})`);
|
|
8711
9107
|
args.push(...values);
|
|
8712
9108
|
}
|
|
8713
|
-
var NotificationsLibSQL = class extends NotificationsStorage {
|
|
9109
|
+
var NotificationsLibSQL = class _NotificationsLibSQL extends NotificationsStorage {
|
|
9110
|
+
/** The notification feed grows unbounded. Single table, anchored on `createdAt`. */
|
|
9111
|
+
static retentionTables = {
|
|
9112
|
+
notifications: { table: TABLE_NOTIFICATIONS, column: "createdAt", indexed: true }
|
|
9113
|
+
};
|
|
8714
9114
|
#db;
|
|
8715
9115
|
#client;
|
|
8716
9116
|
constructor(config) {
|
|
@@ -8745,6 +9145,15 @@ var NotificationsLibSQL = class extends NotificationsStorage {
|
|
|
8745
9145
|
async dangerouslyClearAll() {
|
|
8746
9146
|
await this.#db.deleteData({ tableName: TABLE_NOTIFICATIONS });
|
|
8747
9147
|
}
|
|
9148
|
+
/** Delete notifications older than the `notifications` policy's `maxAge`, batched. */
|
|
9149
|
+
async prune(policies, options) {
|
|
9150
|
+
const targets = resolveTargets({
|
|
9151
|
+
policies,
|
|
9152
|
+
descriptor: _NotificationsLibSQL.retentionTables,
|
|
9153
|
+
order: ["notifications"]
|
|
9154
|
+
});
|
|
9155
|
+
return runPrune({ db: this.#db, domain: "notifications", targets, options, logger: this.logger });
|
|
9156
|
+
}
|
|
8748
9157
|
async createNotification(input) {
|
|
8749
9158
|
const existing = await this.findCoalescable(input);
|
|
8750
9159
|
if (existing) {
|
|
@@ -8934,7 +9343,14 @@ var NotificationsLibSQL = class extends NotificationsStorage {
|
|
|
8934
9343
|
return row ? rowToNotification(row) : void 0;
|
|
8935
9344
|
}
|
|
8936
9345
|
};
|
|
8937
|
-
var ObservabilityLibSQL = class extends ObservabilityStorage {
|
|
9346
|
+
var ObservabilityLibSQL = class _ObservabilityLibSQL extends ObservabilityStorage {
|
|
9347
|
+
/**
|
|
9348
|
+
* Spans are the only physical table; traces are derived from them. Spans
|
|
9349
|
+
* anchor on `startedAt` (not `createdAt`), stored as an ISO-8601 string.
|
|
9350
|
+
*/
|
|
9351
|
+
static retentionTables = {
|
|
9352
|
+
spans: { table: TABLE_SPANS, column: "startedAt", indexed: true }
|
|
9353
|
+
};
|
|
8938
9354
|
#db;
|
|
8939
9355
|
constructor(config) {
|
|
8940
9356
|
super();
|
|
@@ -8952,6 +9368,15 @@ var ObservabilityLibSQL = class extends ObservabilityStorage {
|
|
|
8952
9368
|
async dangerouslyClearAll() {
|
|
8953
9369
|
await this.#db.deleteData({ tableName: TABLE_SPANS });
|
|
8954
9370
|
}
|
|
9371
|
+
/** Delete spans older than the `spans` policy's `maxAge`, batched. */
|
|
9372
|
+
async prune(policies, options) {
|
|
9373
|
+
const targets = resolveTargets({
|
|
9374
|
+
policies,
|
|
9375
|
+
descriptor: _ObservabilityLibSQL.retentionTables,
|
|
9376
|
+
order: ["spans"]
|
|
9377
|
+
});
|
|
9378
|
+
return runPrune({ db: this.#db, domain: "observability", targets, options, logger: this.logger });
|
|
9379
|
+
}
|
|
8955
9380
|
/**
|
|
8956
9381
|
* Manually run the spans migration to deduplicate and add the unique constraint.
|
|
8957
9382
|
* This is intended to be called from the CLI when duplicates are detected.
|
|
@@ -9960,7 +10385,15 @@ function rowToTrigger(row) {
|
|
|
9960
10385
|
if (metadata !== void 0) trigger.metadata = metadata;
|
|
9961
10386
|
return trigger;
|
|
9962
10387
|
}
|
|
9963
|
-
var SchedulesLibSQL = class extends SchedulesStorage {
|
|
10388
|
+
var SchedulesLibSQL = class _SchedulesLibSQL extends SchedulesStorage {
|
|
10389
|
+
/**
|
|
10390
|
+
* The fire/run history (`schedule_triggers`, one row per fire) is the growth
|
|
10391
|
+
* table; schedule definitions are config and excluded. Anchored on
|
|
10392
|
+
* `actual_fire_at`, a bigint epoch-ms column (numeric comparison, not ISO).
|
|
10393
|
+
*/
|
|
10394
|
+
static retentionTables = {
|
|
10395
|
+
triggers: { table: TABLE_SCHEDULE_TRIGGERS, column: "actual_fire_at", indexed: true, anchorType: "epoch-ms" }
|
|
10396
|
+
};
|
|
9964
10397
|
#db;
|
|
9965
10398
|
#client;
|
|
9966
10399
|
constructor(config) {
|
|
@@ -9996,6 +10429,15 @@ var SchedulesLibSQL = class extends SchedulesStorage {
|
|
|
9996
10429
|
await this.#db.deleteData({ tableName: TABLE_SCHEDULE_TRIGGERS });
|
|
9997
10430
|
await this.#db.deleteData({ tableName: TABLE_SCHEDULES });
|
|
9998
10431
|
}
|
|
10432
|
+
/** Delete schedule fire history older than the `triggers` policy's `maxAge`, batched. */
|
|
10433
|
+
async prune(policies, options) {
|
|
10434
|
+
const targets = resolveTargets({
|
|
10435
|
+
policies,
|
|
10436
|
+
descriptor: _SchedulesLibSQL.retentionTables,
|
|
10437
|
+
order: ["triggers"]
|
|
10438
|
+
});
|
|
10439
|
+
return runPrune({ db: this.#db, domain: "schedules", targets, options, logger: this.logger });
|
|
10440
|
+
}
|
|
9999
10441
|
async createSchedule(schedule) {
|
|
10000
10442
|
const existing = await this.getSchedule(schedule.id);
|
|
10001
10443
|
if (existing) {
|
|
@@ -10670,7 +11112,23 @@ var ScorerDefinitionsLibSQL = class extends ScorerDefinitionsStorage {
|
|
|
10670
11112
|
};
|
|
10671
11113
|
}
|
|
10672
11114
|
};
|
|
10673
|
-
|
|
11115
|
+
function appendTenancyConditions(conditions, args, filters) {
|
|
11116
|
+
if (filters?.organizationId !== void 0) {
|
|
11117
|
+
conditions.push(`organizationId = ?`);
|
|
11118
|
+
args.push(filters.organizationId);
|
|
11119
|
+
}
|
|
11120
|
+
if (filters?.projectId !== void 0) {
|
|
11121
|
+
conditions.push(`projectId = ?`);
|
|
11122
|
+
args.push(filters.projectId);
|
|
11123
|
+
}
|
|
11124
|
+
}
|
|
11125
|
+
var ScoresLibSQL = class _ScoresLibSQL extends ScoresStorage {
|
|
11126
|
+
/**
|
|
11127
|
+
* Scorer results accumulate as evals run. Single table, anchored on `createdAt`.
|
|
11128
|
+
*/
|
|
11129
|
+
static retentionTables = {
|
|
11130
|
+
scorers: { table: TABLE_SCORERS, column: "createdAt", indexed: true }
|
|
11131
|
+
};
|
|
10674
11132
|
#db;
|
|
10675
11133
|
#client;
|
|
10676
11134
|
constructor(config) {
|
|
@@ -10684,21 +11142,35 @@ var ScoresLibSQL = class extends ScoresStorage {
|
|
|
10684
11142
|
await this.#db.alterTable({
|
|
10685
11143
|
tableName: TABLE_SCORERS,
|
|
10686
11144
|
schema: SCORERS_SCHEMA,
|
|
10687
|
-
ifNotExists: ["spanId", "requestContext"]
|
|
11145
|
+
ifNotExists: ["spanId", "requestContext", "organizationId", "projectId", "batchId", "datasetId", "datasetItemId"]
|
|
10688
11146
|
});
|
|
10689
11147
|
}
|
|
10690
11148
|
async dangerouslyClearAll() {
|
|
10691
11149
|
await this.#db.deleteData({ tableName: TABLE_SCORERS });
|
|
10692
11150
|
}
|
|
11151
|
+
/** Delete scorer results older than the `scorers` policy's `maxAge`, batched. */
|
|
11152
|
+
async prune(policies, options) {
|
|
11153
|
+
const targets = resolveTargets({
|
|
11154
|
+
policies,
|
|
11155
|
+
descriptor: _ScoresLibSQL.retentionTables,
|
|
11156
|
+
order: ["scorers"]
|
|
11157
|
+
});
|
|
11158
|
+
return runPrune({ db: this.#db, domain: "scores", targets, options, logger: this.logger });
|
|
11159
|
+
}
|
|
10693
11160
|
async listScoresByRunId({
|
|
10694
11161
|
runId,
|
|
10695
|
-
pagination
|
|
11162
|
+
pagination,
|
|
11163
|
+
filters
|
|
10696
11164
|
}) {
|
|
10697
11165
|
try {
|
|
10698
11166
|
const { page, perPage: perPageInput } = pagination;
|
|
11167
|
+
const conditions = [`runId = ?`];
|
|
11168
|
+
const queryParams = [runId];
|
|
11169
|
+
appendTenancyConditions(conditions, queryParams, filters);
|
|
11170
|
+
const whereClause = `WHERE ${conditions.join(" AND ")}`;
|
|
10699
11171
|
const countResult = await this.#client.execute({
|
|
10700
|
-
sql: `SELECT COUNT(*) as count FROM ${TABLE_SCORERS}
|
|
10701
|
-
args:
|
|
11172
|
+
sql: `SELECT COUNT(*) as count FROM ${TABLE_SCORERS} ${whereClause}`,
|
|
11173
|
+
args: queryParams
|
|
10702
11174
|
});
|
|
10703
11175
|
const total = Number(countResult.rows?.[0]?.count ?? 0);
|
|
10704
11176
|
if (total === 0) {
|
|
@@ -10717,8 +11189,8 @@ var ScoresLibSQL = class extends ScoresStorage {
|
|
|
10717
11189
|
const limitValue = perPageInput === false ? total : perPage;
|
|
10718
11190
|
const end = perPageInput === false ? total : start + perPage;
|
|
10719
11191
|
const result = await this.#client.execute({
|
|
10720
|
-
sql: `SELECT ${buildSelectColumns(TABLE_SCORERS)} FROM ${TABLE_SCORERS}
|
|
10721
|
-
args: [
|
|
11192
|
+
sql: `SELECT ${buildSelectColumns(TABLE_SCORERS)} FROM ${TABLE_SCORERS} ${whereClause} ORDER BY createdAt DESC LIMIT ? OFFSET ?`,
|
|
11193
|
+
args: [...queryParams, limitValue, start]
|
|
10722
11194
|
});
|
|
10723
11195
|
const scores = result.rows?.map((row) => this.transformScoreRow(row)) ?? [];
|
|
10724
11196
|
return {
|
|
@@ -10746,7 +11218,8 @@ var ScoresLibSQL = class extends ScoresStorage {
|
|
|
10746
11218
|
entityId,
|
|
10747
11219
|
entityType,
|
|
10748
11220
|
source,
|
|
10749
|
-
pagination
|
|
11221
|
+
pagination,
|
|
11222
|
+
filters
|
|
10750
11223
|
}) {
|
|
10751
11224
|
try {
|
|
10752
11225
|
const { page, perPage: perPageInput } = pagination;
|
|
@@ -10768,6 +11241,7 @@ var ScoresLibSQL = class extends ScoresStorage {
|
|
|
10768
11241
|
conditions.push(`source = ?`);
|
|
10769
11242
|
queryParams.push(source);
|
|
10770
11243
|
}
|
|
11244
|
+
appendTenancyConditions(conditions, queryParams, filters);
|
|
10771
11245
|
const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
10772
11246
|
const countResult = await this.#client.execute({
|
|
10773
11247
|
sql: `SELECT COUNT(*) as count FROM ${TABLE_SCORERS} ${whereClause}`,
|
|
@@ -10875,13 +11349,18 @@ var ScoresLibSQL = class extends ScoresStorage {
|
|
|
10875
11349
|
async listScoresByEntityId({
|
|
10876
11350
|
entityId,
|
|
10877
11351
|
entityType,
|
|
10878
|
-
pagination
|
|
11352
|
+
pagination,
|
|
11353
|
+
filters
|
|
10879
11354
|
}) {
|
|
10880
11355
|
try {
|
|
10881
11356
|
const { page, perPage: perPageInput } = pagination;
|
|
11357
|
+
const conditions = [`entityId = ?`, `entityType = ?`];
|
|
11358
|
+
const queryParams = [entityId, entityType];
|
|
11359
|
+
appendTenancyConditions(conditions, queryParams, filters);
|
|
11360
|
+
const whereClause = `WHERE ${conditions.join(" AND ")}`;
|
|
10882
11361
|
const countResult = await this.#client.execute({
|
|
10883
|
-
sql: `SELECT COUNT(*) as count FROM ${TABLE_SCORERS}
|
|
10884
|
-
args:
|
|
11362
|
+
sql: `SELECT COUNT(*) as count FROM ${TABLE_SCORERS} ${whereClause}`,
|
|
11363
|
+
args: queryParams
|
|
10885
11364
|
});
|
|
10886
11365
|
const total = Number(countResult.rows?.[0]?.count ?? 0);
|
|
10887
11366
|
if (total === 0) {
|
|
@@ -10900,8 +11379,8 @@ var ScoresLibSQL = class extends ScoresStorage {
|
|
|
10900
11379
|
const limitValue = perPageInput === false ? total : perPage;
|
|
10901
11380
|
const end = perPageInput === false ? total : start + perPage;
|
|
10902
11381
|
const result = await this.#client.execute({
|
|
10903
|
-
sql: `SELECT ${buildSelectColumns(TABLE_SCORERS)} FROM ${TABLE_SCORERS}
|
|
10904
|
-
args: [
|
|
11382
|
+
sql: `SELECT ${buildSelectColumns(TABLE_SCORERS)} FROM ${TABLE_SCORERS} ${whereClause} ORDER BY createdAt DESC LIMIT ? OFFSET ?`,
|
|
11383
|
+
args: [...queryParams, limitValue, start]
|
|
10905
11384
|
});
|
|
10906
11385
|
const scores = result.rows?.map((row) => this.transformScoreRow(row)) ?? [];
|
|
10907
11386
|
return {
|
|
@@ -10927,22 +11406,27 @@ var ScoresLibSQL = class extends ScoresStorage {
|
|
|
10927
11406
|
async listScoresBySpan({
|
|
10928
11407
|
traceId,
|
|
10929
11408
|
spanId,
|
|
10930
|
-
pagination
|
|
11409
|
+
pagination,
|
|
11410
|
+
filters
|
|
10931
11411
|
}) {
|
|
10932
11412
|
try {
|
|
10933
11413
|
const { page, perPage: perPageInput } = pagination;
|
|
10934
11414
|
const perPage = normalizePerPage(perPageInput, 100);
|
|
10935
11415
|
const { offset: start, perPage: perPageForResponse } = calculatePagination(page, perPageInput, perPage);
|
|
11416
|
+
const conditions = [`traceId = ?`, `spanId = ?`];
|
|
11417
|
+
const queryParams = [traceId, spanId];
|
|
11418
|
+
appendTenancyConditions(conditions, queryParams, filters);
|
|
11419
|
+
const whereClause = `WHERE ${conditions.join(" AND ")}`;
|
|
10936
11420
|
const countSQLResult = await this.#client.execute({
|
|
10937
|
-
sql: `SELECT COUNT(*) as count FROM ${TABLE_SCORERS}
|
|
10938
|
-
args:
|
|
11421
|
+
sql: `SELECT COUNT(*) as count FROM ${TABLE_SCORERS} ${whereClause}`,
|
|
11422
|
+
args: queryParams
|
|
10939
11423
|
});
|
|
10940
11424
|
const total = Number(countSQLResult.rows?.[0]?.count ?? 0);
|
|
10941
11425
|
const limitValue = perPageInput === false ? total : perPage;
|
|
10942
11426
|
const end = perPageInput === false ? total : start + perPage;
|
|
10943
11427
|
const result = await this.#client.execute({
|
|
10944
|
-
sql: `SELECT ${buildSelectColumns(TABLE_SCORERS)} FROM ${TABLE_SCORERS}
|
|
10945
|
-
args: [
|
|
11428
|
+
sql: `SELECT ${buildSelectColumns(TABLE_SCORERS)} FROM ${TABLE_SCORERS} ${whereClause} ORDER BY createdAt DESC LIMIT ? OFFSET ?`,
|
|
11429
|
+
args: [...queryParams, limitValue, start]
|
|
10946
11430
|
});
|
|
10947
11431
|
const scores = result.rows?.map((row) => this.transformScoreRow(row)) ?? [];
|
|
10948
11432
|
return {
|
|
@@ -11573,7 +12057,15 @@ var SkillsLibSQL = class extends SkillsStorage {
|
|
|
11573
12057
|
};
|
|
11574
12058
|
}
|
|
11575
12059
|
};
|
|
11576
|
-
var ThreadStateLibSQL = class extends ThreadStateStorage {
|
|
12060
|
+
var ThreadStateLibSQL = class _ThreadStateLibSQL extends ThreadStateStorage {
|
|
12061
|
+
/**
|
|
12062
|
+
* `thread_state` grows as a side effect of thread activity (one row per
|
|
12063
|
+
* thread per state type). It anchors on `updatedAt` (last activity), so state
|
|
12064
|
+
* for a thread that is still being appended to is not pruned by creation age.
|
|
12065
|
+
*/
|
|
12066
|
+
static retentionTables = {
|
|
12067
|
+
threadState: { table: TABLE_THREAD_STATE, column: "updatedAt", indexed: true }
|
|
12068
|
+
};
|
|
11577
12069
|
#db;
|
|
11578
12070
|
#client;
|
|
11579
12071
|
constructor(config) {
|
|
@@ -11589,6 +12081,15 @@ var ThreadStateLibSQL = class extends ThreadStateStorage {
|
|
|
11589
12081
|
compositePrimaryKey: ["threadId", "type"]
|
|
11590
12082
|
});
|
|
11591
12083
|
}
|
|
12084
|
+
/** Delete thread state older than the `threadState` policy's `maxAge`, batched. */
|
|
12085
|
+
async prune(policies, options) {
|
|
12086
|
+
const targets = resolveTargets({
|
|
12087
|
+
policies,
|
|
12088
|
+
descriptor: _ThreadStateLibSQL.retentionTables,
|
|
12089
|
+
order: ["threadState"]
|
|
12090
|
+
});
|
|
12091
|
+
return runPrune({ db: this.#db, domain: "threadState", targets, options, logger: this.logger });
|
|
12092
|
+
}
|
|
11592
12093
|
async dangerouslyClearAll() {
|
|
11593
12094
|
try {
|
|
11594
12095
|
await this.#client.execute(`DELETE FROM "${TABLE_THREAD_STATE}"`);
|
|
@@ -11857,7 +12358,14 @@ var ToolProviderConnectionsLibSQL = class extends ToolProviderConnectionsStorage
|
|
|
11857
12358
|
}
|
|
11858
12359
|
}
|
|
11859
12360
|
};
|
|
11860
|
-
var WorkflowsLibSQL = class extends WorkflowsStorage {
|
|
12361
|
+
var WorkflowsLibSQL = class _WorkflowsLibSQL extends WorkflowsStorage {
|
|
12362
|
+
/**
|
|
12363
|
+
* Workflow run snapshots accumulate as runs execute. Anchored on `updatedAt`
|
|
12364
|
+
* (last activity) so suspended/long-running runs are not pruned by start age.
|
|
12365
|
+
*/
|
|
12366
|
+
static retentionTables = {
|
|
12367
|
+
workflowSnapshot: { table: TABLE_WORKFLOW_SNAPSHOT, column: "updatedAt", indexed: true }
|
|
12368
|
+
};
|
|
11861
12369
|
#db;
|
|
11862
12370
|
#client;
|
|
11863
12371
|
executeWithRetry;
|
|
@@ -11910,6 +12418,15 @@ var WorkflowsLibSQL = class extends WorkflowsStorage {
|
|
|
11910
12418
|
async dangerouslyClearAll() {
|
|
11911
12419
|
await this.#db.deleteData({ tableName: TABLE_WORKFLOW_SNAPSHOT });
|
|
11912
12420
|
}
|
|
12421
|
+
/** Delete workflow snapshots older than the `workflowSnapshot` policy's `maxAge`, batched. */
|
|
12422
|
+
async prune(policies, options) {
|
|
12423
|
+
const targets = resolveTargets({
|
|
12424
|
+
policies,
|
|
12425
|
+
descriptor: _WorkflowsLibSQL.retentionTables,
|
|
12426
|
+
order: ["workflowSnapshot"]
|
|
12427
|
+
});
|
|
12428
|
+
return runPrune({ db: this.#db, domain: "workflows", targets, options, logger: this.logger });
|
|
12429
|
+
}
|
|
11913
12430
|
async setupPragmaSettings() {
|
|
11914
12431
|
try {
|
|
11915
12432
|
await this.#client.execute("PRAGMA busy_timeout = 10000;");
|
|
@@ -12765,7 +13282,7 @@ var LibSQLStore = class extends MastraCompositeStore {
|
|
|
12765
13282
|
if (!config.id || typeof config.id !== "string" || config.id.trim() === "") {
|
|
12766
13283
|
throw new Error("LibSQLStore: id must be provided and cannot be empty.");
|
|
12767
13284
|
}
|
|
12768
|
-
super({ id: config.id, name: `LibSQLStore`, disableInit: config.disableInit });
|
|
13285
|
+
super({ id: config.id, name: `LibSQLStore`, disableInit: config.disableInit, retention: config.retention });
|
|
12769
13286
|
this.maxRetries = config.maxRetries ?? 5;
|
|
12770
13287
|
this.initialBackoffMs = config.initialBackoffMs ?? 100;
|
|
12771
13288
|
this.connectionTimeoutMs = config.connectionTimeoutMs ?? DEFAULT_CONNECTION_TIMEOUT_MS;
|