@mastra/libsql 1.4.0 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/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, DatasetsStorage, DATASETS_SCHEMA, TABLE_DATASETS, DATASET_ITEMS_SCHEMA, TABLE_DATASET_ITEMS, DATASET_VERSIONS_SCHEMA, TABLE_DATASET_VERSIONS, ensureDate, safelyParseJSON, TABLE_EXPERIMENT_RESULTS, TABLE_EXPERIMENTS, ExperimentsStorage, EXPERIMENTS_SCHEMA, EXPERIMENT_RESULTS_SCHEMA, MCPClientsStorage, MCP_CLIENTS_SCHEMA, TABLE_MCP_CLIENTS, MCP_CLIENT_VERSIONS_SCHEMA, TABLE_MCP_CLIENT_VERSIONS, MemoryStorage, TABLE_SCHEMAS, TABLE_THREADS, TABLE_MESSAGES, TABLE_RESOURCES, ObservabilityStorage, SPAN_SCHEMA, TABLE_SPANS, listTracesArgsSchema, toTraceSpans, PromptBlocksStorage, PROMPT_BLOCKS_SCHEMA, TABLE_PROMPT_BLOCKS, PROMPT_BLOCK_VERSIONS_SCHEMA, TABLE_PROMPT_BLOCK_VERSIONS, ScorerDefinitionsStorage, SCORER_DEFINITIONS_SCHEMA, TABLE_SCORER_DEFINITIONS, SCORER_DEFINITION_VERSIONS_SCHEMA, TABLE_SCORER_DEFINITION_VERSIONS, ScoresStorage, SCORERS_SCHEMA, TABLE_SCORERS, transformScoreRow, WorkflowsStorage, TABLE_WORKFLOW_SNAPSHOT, MastraCompositeStore, TraceStatus, getSqlType } from '@mastra/core/storage';
3
+ import { createVectorErrorId, AgentsStorage, AGENTS_SCHEMA, TABLE_AGENTS, AGENT_VERSIONS_SCHEMA, TABLE_AGENT_VERSIONS, createStorageErrorId, normalizePerPage, calculatePagination, BlobStore, TABLE_SKILL_BLOBS, SKILL_BLOBS_SCHEMA, DatasetsStorage, DATASETS_SCHEMA, TABLE_DATASETS, DATASET_ITEMS_SCHEMA, TABLE_DATASET_ITEMS, DATASET_VERSIONS_SCHEMA, TABLE_DATASET_VERSIONS, ensureDate, safelyParseJSON, TABLE_EXPERIMENT_RESULTS, TABLE_EXPERIMENTS, ExperimentsStorage, EXPERIMENTS_SCHEMA, EXPERIMENT_RESULTS_SCHEMA, MCPClientsStorage, MCP_CLIENTS_SCHEMA, TABLE_MCP_CLIENTS, MCP_CLIENT_VERSIONS_SCHEMA, TABLE_MCP_CLIENT_VERSIONS, MemoryStorage, TABLE_SCHEMAS, TABLE_THREADS, TABLE_MESSAGES, TABLE_RESOURCES, ObservabilityStorage, SPAN_SCHEMA, TABLE_SPANS, listTracesArgsSchema, toTraceSpans, PromptBlocksStorage, PROMPT_BLOCKS_SCHEMA, TABLE_PROMPT_BLOCKS, PROMPT_BLOCK_VERSIONS_SCHEMA, TABLE_PROMPT_BLOCK_VERSIONS, ScorerDefinitionsStorage, SCORER_DEFINITIONS_SCHEMA, TABLE_SCORER_DEFINITIONS, SCORER_DEFINITION_VERSIONS_SCHEMA, TABLE_SCORER_DEFINITION_VERSIONS, ScoresStorage, SCORERS_SCHEMA, TABLE_SCORERS, transformScoreRow, SkillsStorage, SKILLS_SCHEMA, TABLE_SKILLS, SKILL_VERSIONS_SCHEMA, TABLE_SKILL_VERSIONS, WorkflowsStorage, TABLE_WORKFLOW_SNAPSHOT, WorkspacesStorage, WORKSPACES_SCHEMA, TABLE_WORKSPACES, WORKSPACE_VERSIONS_SCHEMA, TABLE_WORKSPACE_VERSIONS, MastraCompositeStore, TraceStatus, getSqlType } 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';
@@ -1092,7 +1092,7 @@ function buildSelectColumns(tableName) {
1092
1092
  return Object.keys(schema).map((col) => {
1093
1093
  const colDef = schema[col];
1094
1094
  const parsedCol = parseSqlIdentifier(col, "column name");
1095
- return colDef?.type === "jsonb" ? `json(${parsedCol}) as ${parsedCol}` : parsedCol;
1095
+ return colDef?.type === "jsonb" ? `json("${parsedCol}") as "${parsedCol}"` : `"${parsedCol}"`;
1096
1096
  }).join(", ");
1097
1097
  }
1098
1098
  function isLockError(error) {
@@ -2053,23 +2053,6 @@ Note: This migration may take some time for large tables.
2053
2053
  };
2054
2054
 
2055
2055
  // src/storage/domains/agents/index.ts
2056
- var SNAPSHOT_FIELDS = [
2057
- "name",
2058
- "description",
2059
- "instructions",
2060
- "model",
2061
- "tools",
2062
- "defaultOptions",
2063
- "workflows",
2064
- "agents",
2065
- "integrationTools",
2066
- "inputProcessors",
2067
- "outputProcessors",
2068
- "memory",
2069
- "scorers",
2070
- "mcpClients",
2071
- "requestContextSchema"
2072
- ];
2073
2056
  var AgentsLibSQL = class extends AgentsStorage {
2074
2057
  #db;
2075
2058
  #client;
@@ -2089,6 +2072,11 @@ var AgentsLibSQL = class extends AgentsStorage {
2089
2072
  schema: AGENTS_SCHEMA,
2090
2073
  ifNotExists: ["status", "authorId"]
2091
2074
  });
2075
+ await this.#db.alterTable({
2076
+ tableName: TABLE_AGENT_VERSIONS,
2077
+ schema: AGENT_VERSIONS_SCHEMA,
2078
+ ifNotExists: ["mcpClients", "requestContextSchema", "workspace", "skills", "skillsFormat"]
2079
+ });
2092
2080
  await this.#migrateToolsToJsonbFormat();
2093
2081
  await this.#cleanupStaleDrafts();
2094
2082
  }
@@ -2360,8 +2348,8 @@ var AgentsLibSQL = class extends AgentsStorage {
2360
2348
  async update(input) {
2361
2349
  const { id, ...updates } = input;
2362
2350
  try {
2363
- const existingAgent = await this.getById(id);
2364
- if (!existingAgent) {
2351
+ const existing = await this.getById(id);
2352
+ if (!existing) {
2365
2353
  throw new MastraError({
2366
2354
  id: createStorageErrorId("LIBSQL", "UPDATE_AGENT", "NOT_FOUND"),
2367
2355
  domain: ErrorDomain.STORAGE,
@@ -2370,68 +2358,21 @@ var AgentsLibSQL = class extends AgentsStorage {
2370
2358
  details: { agentId: id }
2371
2359
  });
2372
2360
  }
2373
- const metadataFields = {
2374
- authorId: updates.authorId,
2375
- activeVersionId: updates.activeVersionId,
2376
- metadata: updates.metadata
2377
- };
2378
- const configFields = {};
2379
- for (const field of SNAPSHOT_FIELDS) {
2380
- if (updates[field] !== void 0) {
2381
- configFields[field] = updates[field];
2382
- }
2383
- }
2384
- if (Object.keys(configFields).length > 0) {
2385
- const latestVersion = await this.getLatestVersion(id);
2386
- const nextVersionNumber = latestVersion ? latestVersion.versionNumber + 1 : 1;
2387
- if (!latestVersion) {
2388
- throw new MastraError({
2389
- id: createStorageErrorId("LIBSQL", "UPDATE_AGENT", "NO_VERSION"),
2390
- domain: ErrorDomain.STORAGE,
2391
- category: ErrorCategory.USER,
2392
- text: `Cannot update config fields for agent ${id} - no versions exist`,
2393
- details: { id }
2394
- });
2395
- }
2396
- const latestSnapshot = {};
2397
- for (const field of SNAPSHOT_FIELDS) {
2398
- if (latestVersion[field] !== void 0) {
2399
- latestSnapshot[field] = latestVersion[field];
2400
- }
2401
- }
2402
- const sanitizedConfigFields = Object.fromEntries(
2403
- Object.entries(configFields).map(([key, value]) => [key, value === null ? void 0 : value])
2404
- );
2405
- const versionInput = {
2406
- id: crypto.randomUUID(),
2407
- agentId: id,
2408
- versionNumber: nextVersionNumber,
2409
- ...latestSnapshot,
2410
- // Start from latest version
2411
- ...sanitizedConfigFields,
2412
- // Apply updates (null values converted to undefined)
2413
- changedFields: Object.keys(configFields),
2414
- changeMessage: `Updated: ${Object.keys(configFields).join(", ")}`
2415
- };
2416
- await this.createVersion(versionInput);
2417
- }
2418
- const data = {
2419
- updatedAt: /* @__PURE__ */ new Date()
2361
+ const { authorId, activeVersionId, metadata, status } = updates;
2362
+ const updateData = {
2363
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
2420
2364
  };
2421
- if (metadataFields.authorId !== void 0) data.authorId = metadataFields.authorId;
2422
- if (metadataFields.activeVersionId !== void 0) {
2423
- data.activeVersionId = metadataFields.activeVersionId;
2424
- }
2425
- if (metadataFields.metadata !== void 0) {
2426
- data.metadata = metadataFields.metadata;
2427
- }
2428
- if (Object.keys(data).length > 1) {
2429
- await this.#db.update({
2430
- tableName: TABLE_AGENTS,
2431
- keys: { id },
2432
- data
2433
- });
2365
+ if (authorId !== void 0) updateData.authorId = authorId;
2366
+ if (activeVersionId !== void 0) updateData.activeVersionId = activeVersionId;
2367
+ if (status !== void 0) updateData.status = status;
2368
+ if (metadata !== void 0) {
2369
+ updateData.metadata = { ...existing.metadata, ...metadata };
2434
2370
  }
2371
+ await this.#db.update({
2372
+ tableName: TABLE_AGENTS,
2373
+ keys: { id },
2374
+ data: updateData
2375
+ });
2435
2376
  const updatedAgent = await this.getById(id);
2436
2377
  if (!updatedAgent) {
2437
2378
  throw new MastraError({
@@ -2479,7 +2420,7 @@ var AgentsLibSQL = class extends AgentsStorage {
2479
2420
  }
2480
2421
  }
2481
2422
  async list(args) {
2482
- const { page = 0, perPage: perPageInput, orderBy } = args || {};
2423
+ const { page = 0, perPage: perPageInput, orderBy, authorId, metadata, status } = args || {};
2483
2424
  const { field, direction } = this.parseOrderBy(orderBy);
2484
2425
  if (page < 0) {
2485
2426
  throw new MastraError(
@@ -2495,7 +2436,37 @@ var AgentsLibSQL = class extends AgentsStorage {
2495
2436
  const perPage = normalizePerPage(perPageInput, 100);
2496
2437
  const { offset, perPage: perPageForResponse } = calculatePagination(page, perPageInput, perPage);
2497
2438
  try {
2498
- const total = await this.#db.selectTotalCount({ tableName: TABLE_AGENTS });
2439
+ const conditions = [];
2440
+ const queryParams = [];
2441
+ if (status) {
2442
+ conditions.push("status = ?");
2443
+ queryParams.push(status);
2444
+ }
2445
+ if (authorId !== void 0) {
2446
+ conditions.push("authorId = ?");
2447
+ queryParams.push(authorId);
2448
+ }
2449
+ if (metadata && Object.keys(metadata).length > 0) {
2450
+ for (const [key, value] of Object.entries(metadata)) {
2451
+ if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(key)) {
2452
+ throw new MastraError({
2453
+ id: createStorageErrorId("LIBSQL", "LIST_AGENTS", "INVALID_METADATA_KEY"),
2454
+ domain: ErrorDomain.STORAGE,
2455
+ category: ErrorCategory.USER,
2456
+ text: `Invalid metadata key: ${key}. Keys must be alphanumeric with underscores.`,
2457
+ details: { key }
2458
+ });
2459
+ }
2460
+ conditions.push(`json_extract(metadata, '$.${key}') = ?`);
2461
+ queryParams.push(typeof value === "string" ? value : JSON.stringify(value));
2462
+ }
2463
+ }
2464
+ const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
2465
+ const countResult = await this.#client.execute({
2466
+ sql: `SELECT COUNT(*) as count FROM "${TABLE_AGENTS}" ${whereClause}`,
2467
+ args: queryParams
2468
+ });
2469
+ const total = Number(countResult.rows?.[0]?.count ?? 0);
2499
2470
  if (total === 0) {
2500
2471
  return {
2501
2472
  agents: [],
@@ -2506,12 +2477,11 @@ var AgentsLibSQL = class extends AgentsStorage {
2506
2477
  };
2507
2478
  }
2508
2479
  const limitValue = perPageInput === false ? total : perPage;
2509
- const rows = await this.#db.selectMany({
2510
- tableName: TABLE_AGENTS,
2511
- orderBy: `"${field}" ${direction}`,
2512
- limit: limitValue,
2513
- offset
2480
+ const result = await this.#client.execute({
2481
+ sql: `SELECT ${buildSelectColumns(TABLE_AGENTS)} FROM "${TABLE_AGENTS}" ${whereClause} ORDER BY "${field}" ${direction} LIMIT ? OFFSET ?`,
2482
+ args: [...queryParams, limitValue, offset]
2514
2483
  });
2484
+ const rows = result.rows ?? [];
2515
2485
  const agents = rows.map((row) => this.parseRow(row));
2516
2486
  return {
2517
2487
  agents,
@@ -2559,6 +2529,9 @@ var AgentsLibSQL = class extends AgentsStorage {
2559
2529
  scorers: input.scorers ?? null,
2560
2530
  mcpClients: input.mcpClients ?? null,
2561
2531
  requestContextSchema: input.requestContextSchema ?? null,
2532
+ workspace: input.workspace ?? null,
2533
+ skills: input.skills ?? null,
2534
+ skillsFormat: input.skillsFormat ?? null,
2562
2535
  changedFields: input.changedFields ?? null,
2563
2536
  changeMessage: input.changeMessage ?? null,
2564
2537
  createdAt: now
@@ -2829,12 +2802,101 @@ var AgentsLibSQL = class extends AgentsStorage {
2829
2802
  scorers: this.parseJson(row.scorers, "scorers"),
2830
2803
  mcpClients: this.parseJson(row.mcpClients, "mcpClients"),
2831
2804
  requestContextSchema: this.parseJson(row.requestContextSchema, "requestContextSchema"),
2805
+ workspace: this.parseJson(row.workspace, "workspace"),
2806
+ skills: this.parseJson(row.skills, "skills"),
2807
+ skillsFormat: row.skillsFormat,
2832
2808
  changedFields: this.parseJson(row.changedFields, "changedFields"),
2833
2809
  changeMessage: row.changeMessage,
2834
2810
  createdAt: new Date(row.createdAt)
2835
2811
  };
2836
2812
  }
2837
2813
  };
2814
+ var BlobsLibSQL = class extends BlobStore {
2815
+ #db;
2816
+ #client;
2817
+ static MANAGED_TABLES = [TABLE_SKILL_BLOBS];
2818
+ constructor(config) {
2819
+ super();
2820
+ const client = resolveClient(config);
2821
+ this.#client = client;
2822
+ this.#db = new LibSQLDB({ client, maxRetries: config.maxRetries, initialBackoffMs: config.initialBackoffMs });
2823
+ }
2824
+ async init() {
2825
+ await this.#db.createTable({ tableName: TABLE_SKILL_BLOBS, schema: SKILL_BLOBS_SCHEMA });
2826
+ }
2827
+ async put(entry) {
2828
+ const now = entry.createdAt ?? /* @__PURE__ */ new Date();
2829
+ await this.#client.execute({
2830
+ sql: `INSERT OR IGNORE INTO "${TABLE_SKILL_BLOBS}" ("hash", "content", "size", "mimeType", "createdAt") VALUES (?, ?, ?, ?, ?)`,
2831
+ args: [entry.hash, entry.content, entry.size, entry.mimeType ?? null, now.toISOString()]
2832
+ });
2833
+ }
2834
+ async get(hash) {
2835
+ const result = await this.#client.execute({
2836
+ sql: `SELECT ${buildSelectColumns(TABLE_SKILL_BLOBS)} FROM "${TABLE_SKILL_BLOBS}" WHERE "hash" = ?`,
2837
+ args: [hash]
2838
+ });
2839
+ if (!result.rows.length) return null;
2840
+ return this.#parseRow(result.rows[0]);
2841
+ }
2842
+ async has(hash) {
2843
+ const result = await this.#client.execute({
2844
+ sql: `SELECT 1 FROM "${TABLE_SKILL_BLOBS}" WHERE "hash" = ? LIMIT 1`,
2845
+ args: [hash]
2846
+ });
2847
+ return result.rows.length > 0;
2848
+ }
2849
+ async delete(hash) {
2850
+ const result = await this.#client.execute({
2851
+ sql: `DELETE FROM "${TABLE_SKILL_BLOBS}" WHERE "hash" = ?`,
2852
+ args: [hash]
2853
+ });
2854
+ return result.rowsAffected > 0;
2855
+ }
2856
+ async putMany(entries) {
2857
+ if (entries.length === 0) return;
2858
+ await this.#db.batchInsert({
2859
+ tableName: TABLE_SKILL_BLOBS,
2860
+ records: entries.map((entry) => ({
2861
+ hash: entry.hash,
2862
+ content: entry.content,
2863
+ size: entry.size,
2864
+ mimeType: entry.mimeType ?? null,
2865
+ createdAt: (entry.createdAt ?? /* @__PURE__ */ new Date()).toISOString()
2866
+ }))
2867
+ });
2868
+ }
2869
+ async getMany(hashes) {
2870
+ const result = /* @__PURE__ */ new Map();
2871
+ if (hashes.length === 0) return result;
2872
+ const batchSize = 500;
2873
+ for (let i = 0; i < hashes.length; i += batchSize) {
2874
+ const batch = hashes.slice(i, i + batchSize);
2875
+ const placeholders = batch.map(() => "?").join(", ");
2876
+ const queryResult = await this.#client.execute({
2877
+ sql: `SELECT ${buildSelectColumns(TABLE_SKILL_BLOBS)} FROM "${TABLE_SKILL_BLOBS}" WHERE "hash" IN (${placeholders})`,
2878
+ args: batch
2879
+ });
2880
+ for (const row of queryResult.rows) {
2881
+ const entry = this.#parseRow(row);
2882
+ result.set(entry.hash, entry);
2883
+ }
2884
+ }
2885
+ return result;
2886
+ }
2887
+ async dangerouslyClearAll() {
2888
+ await this.#db.deleteData({ tableName: TABLE_SKILL_BLOBS });
2889
+ }
2890
+ #parseRow(row) {
2891
+ return {
2892
+ hash: row.hash,
2893
+ content: row.content,
2894
+ size: Number(row.size),
2895
+ mimeType: row.mimeType || void 0,
2896
+ createdAt: new Date(row.createdAt)
2897
+ };
2898
+ }
2899
+ };
2838
2900
  function jsonbArg(value) {
2839
2901
  return value === void 0 || value === null ? null : JSON.stringify(value);
2840
2902
  }
@@ -2950,8 +3012,8 @@ var DatasetsLibSQL = class extends DatasetsStorage {
2950
3012
  name: input.name,
2951
3013
  description: input.description,
2952
3014
  metadata: input.metadata,
2953
- inputSchema: input.inputSchema,
2954
- groundTruthSchema: input.groundTruthSchema,
3015
+ inputSchema: input.inputSchema ?? void 0,
3016
+ groundTruthSchema: input.groundTruthSchema ?? void 0,
2955
3017
  version: 0,
2956
3018
  createdAt: now,
2957
3019
  updatedAt: now
@@ -3029,8 +3091,8 @@ var DatasetsLibSQL = class extends DatasetsStorage {
3029
3091
  name: args.name ?? existing.name,
3030
3092
  description: args.description ?? existing.description,
3031
3093
  metadata: args.metadata ?? existing.metadata,
3032
- inputSchema: args.inputSchema !== void 0 ? args.inputSchema : existing.inputSchema,
3033
- groundTruthSchema: args.groundTruthSchema !== void 0 ? args.groundTruthSchema : existing.groundTruthSchema,
3094
+ inputSchema: (args.inputSchema !== void 0 ? args.inputSchema : existing.inputSchema) ?? void 0,
3095
+ groundTruthSchema: (args.groundTruthSchema !== void 0 ? args.groundTruthSchema : existing.groundTruthSchema) ?? void 0,
3034
3096
  updatedAt: new Date(now)
3035
3097
  };
3036
3098
  } catch (error) {
@@ -4089,7 +4151,6 @@ var ExperimentsLibSQL = class extends ExperimentsStorage {
4089
4151
  }
4090
4152
  }
4091
4153
  };
4092
- var SNAPSHOT_FIELDS2 = ["name", "description", "servers"];
4093
4154
  var MCPClientsLibSQL = class extends MCPClientsStorage {
4094
4155
  #db;
4095
4156
  #client;
@@ -4195,19 +4256,12 @@ var MCPClientsLibSQL = class extends MCPClientsStorage {
4195
4256
  if (!existing) {
4196
4257
  throw new Error(`MCP client with id ${id} not found`);
4197
4258
  }
4198
- const { authorId, activeVersionId, metadata, status, ...configFields } = updates;
4199
- const configFieldNames = SNAPSHOT_FIELDS2;
4200
- const hasConfigUpdate = configFieldNames.some((field) => field in configFields);
4259
+ const { authorId, activeVersionId, metadata, status } = updates;
4201
4260
  const updateData = {
4202
4261
  updatedAt: (/* @__PURE__ */ new Date()).toISOString()
4203
4262
  };
4204
4263
  if (authorId !== void 0) updateData.authorId = authorId;
4205
- if (activeVersionId !== void 0) {
4206
- updateData.activeVersionId = activeVersionId;
4207
- if (status === void 0) {
4208
- updateData.status = "published";
4209
- }
4210
- }
4264
+ if (activeVersionId !== void 0) updateData.activeVersionId = activeVersionId;
4211
4265
  if (status !== void 0) updateData.status = status;
4212
4266
  if (metadata !== void 0) {
4213
4267
  updateData.metadata = { ...existing.metadata, ...metadata };
@@ -4217,36 +4271,6 @@ var MCPClientsLibSQL = class extends MCPClientsStorage {
4217
4271
  keys: { id },
4218
4272
  data: updateData
4219
4273
  });
4220
- if (hasConfigUpdate) {
4221
- const latestVersion = await this.getLatestVersion(id);
4222
- if (!latestVersion) {
4223
- throw new Error(`No versions found for MCP client ${id}`);
4224
- }
4225
- const {
4226
- id: _versionId,
4227
- mcpClientId: _mcpClientId,
4228
- versionNumber: _versionNumber,
4229
- changedFields: _changedFields,
4230
- changeMessage: _changeMessage,
4231
- createdAt: _createdAt,
4232
- ...latestConfig
4233
- } = latestVersion;
4234
- const newConfig = { ...latestConfig, ...configFields };
4235
- const changedFields = configFieldNames.filter(
4236
- (field) => field in configFields && JSON.stringify(configFields[field]) !== JSON.stringify(latestConfig[field])
4237
- );
4238
- if (changedFields.length > 0) {
4239
- const newVersionId = crypto.randomUUID();
4240
- await this.createVersion({
4241
- id: newVersionId,
4242
- mcpClientId: id,
4243
- versionNumber: latestVersion.versionNumber + 1,
4244
- ...newConfig,
4245
- changedFields,
4246
- changeMessage: `Updated ${changedFields.join(", ")}`
4247
- });
4248
- }
4249
- }
4250
4274
  const updated = await this.getById(id);
4251
4275
  if (!updated) {
4252
4276
  throw new MastraError({
@@ -4291,10 +4315,12 @@ var MCPClientsLibSQL = class extends MCPClientsStorage {
4291
4315
  }
4292
4316
  async list(args) {
4293
4317
  try {
4294
- const { page = 0, perPage: perPageInput, orderBy, authorId, metadata } = args || {};
4318
+ const { page = 0, perPage: perPageInput, orderBy, authorId, metadata, status = "published" } = args || {};
4295
4319
  const { field, direction } = this.parseOrderBy(orderBy);
4296
4320
  const conditions = [];
4297
4321
  const queryParams = [];
4322
+ conditions.push("status = ?");
4323
+ queryParams.push(status);
4298
4324
  if (authorId !== void 0) {
4299
4325
  conditions.push("authorId = ?");
4300
4326
  queryParams.push(authorId);
@@ -4314,7 +4340,7 @@ var MCPClientsLibSQL = class extends MCPClientsStorage {
4314
4340
  queryParams.push(typeof value === "string" ? value : JSON.stringify(value));
4315
4341
  }
4316
4342
  }
4317
- const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
4343
+ const whereClause = `WHERE ${conditions.join(" AND ")}`;
4318
4344
  const countResult = await this.#client.execute({
4319
4345
  sql: `SELECT COUNT(*) as count FROM "${TABLE_MCP_CLIENTS}" ${whereClause}`,
4320
4346
  args: queryParams
@@ -5594,7 +5620,7 @@ var MemoryLibSQL = class extends MemoryStorage {
5594
5620
  const newThread = {
5595
5621
  id: newThreadId,
5596
5622
  resourceId: resourceId || sourceThread.resourceId,
5597
- title: title || (sourceThread.title ? `Clone of ${sourceThread.title}` : void 0),
5623
+ title: title || (sourceThread.title ? `Clone of ${sourceThread.title}` : ""),
5598
5624
  metadata: {
5599
5625
  ...metadata,
5600
5626
  clone: cloneMetadata
@@ -5610,7 +5636,7 @@ var MemoryLibSQL = class extends MemoryStorage {
5610
5636
  args: [
5611
5637
  newThread.id,
5612
5638
  newThread.resourceId,
5613
- newThread.title || null,
5639
+ newThread.title ?? "",
5614
5640
  JSON.stringify(newThread.metadata),
5615
5641
  nowStr,
5616
5642
  nowStr
@@ -6930,7 +6956,6 @@ var ObservabilityLibSQL = class extends ObservabilityStorage {
6930
6956
  }
6931
6957
  }
6932
6958
  };
6933
- var SNAPSHOT_FIELDS3 = ["name", "description", "content", "rules"];
6934
6959
  var PromptBlocksLibSQL = class extends PromptBlocksStorage {
6935
6960
  #db;
6936
6961
  #client;
@@ -7028,19 +7053,12 @@ var PromptBlocksLibSQL = class extends PromptBlocksStorage {
7028
7053
  if (!existing) {
7029
7054
  throw new Error(`Prompt block with id ${id} not found`);
7030
7055
  }
7031
- const { authorId, activeVersionId, metadata, status, ...configFields } = updates;
7032
- const configFieldNames = SNAPSHOT_FIELDS3;
7033
- const hasConfigUpdate = configFieldNames.some((field) => field in configFields);
7056
+ const { authorId, activeVersionId, metadata, status } = updates;
7034
7057
  const updateData = {
7035
7058
  updatedAt: (/* @__PURE__ */ new Date()).toISOString()
7036
7059
  };
7037
7060
  if (authorId !== void 0) updateData.authorId = authorId;
7038
- if (activeVersionId !== void 0) {
7039
- updateData.activeVersionId = activeVersionId;
7040
- if (status === void 0) {
7041
- updateData.status = "published";
7042
- }
7043
- }
7061
+ if (activeVersionId !== void 0) updateData.activeVersionId = activeVersionId;
7044
7062
  if (status !== void 0) updateData.status = status;
7045
7063
  if (metadata !== void 0) {
7046
7064
  updateData.metadata = { ...existing.metadata, ...metadata };
@@ -7050,34 +7068,6 @@ var PromptBlocksLibSQL = class extends PromptBlocksStorage {
7050
7068
  keys: { id },
7051
7069
  data: updateData
7052
7070
  });
7053
- if (hasConfigUpdate) {
7054
- const latestVersion = await this.getLatestVersion(id);
7055
- if (!latestVersion) {
7056
- throw new Error(`No versions found for prompt block ${id}`);
7057
- }
7058
- const {
7059
- id: _versionId,
7060
- blockId: _blockId,
7061
- versionNumber: _versionNumber,
7062
- changedFields: _changedFields,
7063
- changeMessage: _changeMessage,
7064
- createdAt: _createdAt,
7065
- ...latestConfig
7066
- } = latestVersion;
7067
- const newConfig = { ...latestConfig, ...configFields };
7068
- const changedFields = configFieldNames.filter(
7069
- (field) => field in configFields && JSON.stringify(configFields[field]) !== JSON.stringify(latestConfig[field])
7070
- );
7071
- const newVersionId = crypto.randomUUID();
7072
- await this.createVersion({
7073
- id: newVersionId,
7074
- blockId: id,
7075
- versionNumber: latestVersion.versionNumber + 1,
7076
- ...newConfig,
7077
- changedFields,
7078
- changeMessage: `Updated ${changedFields.join(", ")}`
7079
- });
7080
- }
7081
7071
  const updated = await this.getById(id);
7082
7072
  if (!updated) {
7083
7073
  throw new MastraError({
@@ -7122,10 +7112,12 @@ var PromptBlocksLibSQL = class extends PromptBlocksStorage {
7122
7112
  }
7123
7113
  async list(args) {
7124
7114
  try {
7125
- const { page = 0, perPage: perPageInput, orderBy, authorId, metadata } = args || {};
7115
+ const { page = 0, perPage: perPageInput, orderBy, authorId, metadata, status = "published" } = args || {};
7126
7116
  const { field, direction } = this.parseOrderBy(orderBy);
7127
7117
  const conditions = [];
7128
7118
  const queryParams = [];
7119
+ conditions.push("status = ?");
7120
+ queryParams.push(status);
7129
7121
  if (authorId !== void 0) {
7130
7122
  conditions.push("authorId = ?");
7131
7123
  queryParams.push(authorId);
@@ -7145,7 +7137,7 @@ var PromptBlocksLibSQL = class extends PromptBlocksStorage {
7145
7137
  queryParams.push(typeof value === "string" ? value : JSON.stringify(value));
7146
7138
  }
7147
7139
  }
7148
- const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
7140
+ const whereClause = `WHERE ${conditions.join(" AND ")}`;
7149
7141
  const countResult = await this.#client.execute({
7150
7142
  sql: `SELECT COUNT(*) as count FROM "${TABLE_PROMPT_BLOCKS}" ${whereClause}`,
7151
7143
  args: queryParams
@@ -7437,16 +7429,6 @@ var PromptBlocksLibSQL = class extends PromptBlocksStorage {
7437
7429
  };
7438
7430
  }
7439
7431
  };
7440
- var SNAPSHOT_FIELDS4 = [
7441
- "name",
7442
- "description",
7443
- "type",
7444
- "model",
7445
- "instructions",
7446
- "scoreRange",
7447
- "presetConfig",
7448
- "defaultSampling"
7449
- ];
7450
7432
  var ScorerDefinitionsLibSQL = class extends ScorerDefinitionsStorage {
7451
7433
  #db;
7452
7434
  #client;
@@ -7547,19 +7529,12 @@ var ScorerDefinitionsLibSQL = class extends ScorerDefinitionsStorage {
7547
7529
  if (!existing) {
7548
7530
  throw new Error(`Scorer definition with id ${id} not found`);
7549
7531
  }
7550
- const { authorId, activeVersionId, metadata, status, ...configFields } = updates;
7551
- const configFieldNames = SNAPSHOT_FIELDS4;
7552
- const hasConfigUpdate = configFieldNames.some((field) => field in configFields);
7532
+ const { authorId, activeVersionId, metadata, status } = updates;
7553
7533
  const updateData = {
7554
7534
  updatedAt: (/* @__PURE__ */ new Date()).toISOString()
7555
7535
  };
7556
7536
  if (authorId !== void 0) updateData.authorId = authorId;
7557
- if (activeVersionId !== void 0) {
7558
- updateData.activeVersionId = activeVersionId;
7559
- if (status === void 0) {
7560
- updateData.status = "published";
7561
- }
7562
- }
7537
+ if (activeVersionId !== void 0) updateData.activeVersionId = activeVersionId;
7563
7538
  if (status !== void 0) updateData.status = status;
7564
7539
  if (metadata !== void 0) {
7565
7540
  updateData.metadata = { ...existing.metadata, ...metadata };
@@ -7569,34 +7544,6 @@ var ScorerDefinitionsLibSQL = class extends ScorerDefinitionsStorage {
7569
7544
  keys: { id },
7570
7545
  data: updateData
7571
7546
  });
7572
- if (hasConfigUpdate) {
7573
- const latestVersion = await this.getLatestVersion(id);
7574
- if (!latestVersion) {
7575
- throw new Error(`No versions found for scorer definition ${id}`);
7576
- }
7577
- const {
7578
- id: _versionId,
7579
- scorerDefinitionId: _scorerDefinitionId,
7580
- versionNumber: _versionNumber,
7581
- changedFields: _changedFields,
7582
- changeMessage: _changeMessage,
7583
- createdAt: _createdAt,
7584
- ...latestConfig
7585
- } = latestVersion;
7586
- const newConfig = { ...latestConfig, ...configFields };
7587
- const changedFields = configFieldNames.filter(
7588
- (field) => field in configFields && JSON.stringify(configFields[field]) !== JSON.stringify(latestConfig[field])
7589
- );
7590
- const newVersionId = crypto.randomUUID();
7591
- await this.createVersion({
7592
- id: newVersionId,
7593
- scorerDefinitionId: id,
7594
- versionNumber: latestVersion.versionNumber + 1,
7595
- ...newConfig,
7596
- changedFields,
7597
- changeMessage: `Updated ${changedFields.join(", ")}`
7598
- });
7599
- }
7600
7547
  const updated = await this.getById(id);
7601
7548
  if (!updated) {
7602
7549
  throw new MastraError({
@@ -7641,10 +7588,14 @@ var ScorerDefinitionsLibSQL = class extends ScorerDefinitionsStorage {
7641
7588
  }
7642
7589
  async list(args) {
7643
7590
  try {
7644
- const { page = 0, perPage: perPageInput, orderBy, authorId, metadata } = args || {};
7591
+ const { page = 0, perPage: perPageInput, orderBy, authorId, metadata, status } = args || {};
7645
7592
  const { field, direction } = this.parseOrderBy(orderBy);
7646
7593
  const conditions = [];
7647
7594
  const queryParams = [];
7595
+ if (status) {
7596
+ conditions.push("status = ?");
7597
+ queryParams.push(status);
7598
+ }
7648
7599
  if (authorId !== void 0) {
7649
7600
  conditions.push("authorId = ?");
7650
7601
  queryParams.push(authorId);
@@ -8260,229 +8211,1237 @@ var ScoresLibSQL = class extends ScoresStorage {
8260
8211
  }
8261
8212
  }
8262
8213
  };
8263
- var WorkflowsLibSQL = class extends WorkflowsStorage {
8214
+ var SNAPSHOT_FIELDS = [
8215
+ "name",
8216
+ "description",
8217
+ "instructions",
8218
+ "license",
8219
+ "compatibility",
8220
+ "source",
8221
+ "references",
8222
+ "scripts",
8223
+ "assets",
8224
+ "metadata",
8225
+ "tree"
8226
+ ];
8227
+ var SkillsLibSQL = class extends SkillsStorage {
8264
8228
  #db;
8265
8229
  #client;
8266
- executeWithRetry;
8267
8230
  constructor(config) {
8268
8231
  super();
8269
8232
  const client = resolveClient(config);
8270
- const maxRetries = config.maxRetries ?? 5;
8271
- const initialBackoffMs = config.initialBackoffMs ?? 500;
8272
8233
  this.#client = client;
8273
- this.#db = new LibSQLDB({ client, maxRetries, initialBackoffMs });
8274
- this.executeWithRetry = createExecuteWriteOperationWithRetry({
8275
- logger: this.logger,
8276
- maxRetries,
8277
- initialBackoffMs
8234
+ this.#db = new LibSQLDB({ client, maxRetries: config.maxRetries, initialBackoffMs: config.initialBackoffMs });
8235
+ }
8236
+ async init() {
8237
+ await this.#db.createTable({ tableName: TABLE_SKILLS, schema: SKILLS_SCHEMA });
8238
+ await this.#db.createTable({
8239
+ tableName: TABLE_SKILL_VERSIONS,
8240
+ schema: SKILL_VERSIONS_SCHEMA
8278
8241
  });
8279
- this.setupPragmaSettings().catch(
8280
- (err) => this.logger.warn("LibSQL Workflows: Failed to setup PRAGMA settings.", err)
8242
+ await this.#client.execute(
8243
+ `CREATE UNIQUE INDEX IF NOT EXISTS idx_skill_versions_skill_version ON "${TABLE_SKILL_VERSIONS}" ("skillId", "versionNumber")`
8281
8244
  );
8282
8245
  }
8283
- parseWorkflowRun(row) {
8284
- let parsedSnapshot = row.snapshot;
8285
- if (typeof parsedSnapshot === "string") {
8286
- try {
8287
- parsedSnapshot = JSON.parse(row.snapshot);
8288
- } catch (e) {
8289
- this.logger.warn(`Failed to parse snapshot for workflow ${row.workflow_name}: ${e}`);
8290
- }
8291
- }
8292
- return {
8293
- workflowName: row.workflow_name,
8294
- runId: row.run_id,
8295
- snapshot: parsedSnapshot,
8296
- resourceId: row.resourceId,
8297
- createdAt: new Date(row.createdAt),
8298
- updatedAt: new Date(row.updatedAt)
8299
- };
8246
+ async dangerouslyClearAll() {
8247
+ await this.#db.deleteData({ tableName: TABLE_SKILLS });
8248
+ await this.#db.deleteData({ tableName: TABLE_SKILL_VERSIONS });
8300
8249
  }
8301
- async init() {
8302
- const schema = TABLE_SCHEMAS[TABLE_WORKFLOW_SNAPSHOT];
8303
- await this.#db.createTable({ tableName: TABLE_WORKFLOW_SNAPSHOT, schema });
8304
- await this.#db.alterTable({
8305
- tableName: TABLE_WORKFLOW_SNAPSHOT,
8306
- schema,
8307
- ifNotExists: ["resourceId"]
8308
- });
8309
- }
8310
- async dangerouslyClearAll() {
8311
- await this.#db.deleteData({ tableName: TABLE_WORKFLOW_SNAPSHOT });
8250
+ // ==========================================================================
8251
+ // Skill CRUD
8252
+ // ==========================================================================
8253
+ async getById(id) {
8254
+ try {
8255
+ const result = await this.#client.execute({
8256
+ sql: `SELECT ${buildSelectColumns(TABLE_SKILLS)} FROM "${TABLE_SKILLS}" WHERE id = ?`,
8257
+ args: [id]
8258
+ });
8259
+ const row = result.rows?.[0];
8260
+ return row ? this.#parseSkillRow(row) : null;
8261
+ } catch (error) {
8262
+ if (error instanceof MastraError) throw error;
8263
+ throw new MastraError(
8264
+ {
8265
+ id: createStorageErrorId("LIBSQL", "GET_SKILL", "FAILED"),
8266
+ domain: ErrorDomain.STORAGE,
8267
+ category: ErrorCategory.THIRD_PARTY
8268
+ },
8269
+ error
8270
+ );
8271
+ }
8312
8272
  }
8313
- async setupPragmaSettings() {
8273
+ async create(input) {
8274
+ const { skill } = input;
8314
8275
  try {
8315
- await this.#client.execute("PRAGMA busy_timeout = 10000;");
8316
- this.logger.debug("LibSQL Workflows: PRAGMA busy_timeout=10000 set.");
8317
- try {
8318
- await this.#client.execute("PRAGMA journal_mode = WAL;");
8319
- this.logger.debug("LibSQL Workflows: PRAGMA journal_mode=WAL set.");
8320
- } catch {
8321
- this.logger.debug("LibSQL Workflows: WAL mode not supported, using default journal mode.");
8322
- }
8276
+ const now = /* @__PURE__ */ new Date();
8277
+ await this.#db.insert({
8278
+ tableName: TABLE_SKILLS,
8279
+ record: {
8280
+ id: skill.id,
8281
+ status: "draft",
8282
+ activeVersionId: null,
8283
+ authorId: skill.authorId ?? null,
8284
+ createdAt: now.toISOString(),
8285
+ updatedAt: now.toISOString()
8286
+ }
8287
+ });
8288
+ const { id: _id, authorId: _authorId, ...snapshotConfig } = skill;
8289
+ const versionId = crypto.randomUUID();
8323
8290
  try {
8324
- await this.#client.execute("PRAGMA synchronous = NORMAL;");
8325
- this.logger.debug("LibSQL Workflows: PRAGMA synchronous=NORMAL set.");
8326
- } catch {
8327
- this.logger.debug("LibSQL Workflows: Failed to set synchronous mode.");
8291
+ await this.createVersion({
8292
+ id: versionId,
8293
+ skillId: skill.id,
8294
+ versionNumber: 1,
8295
+ ...snapshotConfig,
8296
+ changedFields: Object.keys(snapshotConfig),
8297
+ changeMessage: "Initial version"
8298
+ });
8299
+ } catch (versionError) {
8300
+ await this.#db.delete({ tableName: TABLE_SKILLS, keys: { id: skill.id } });
8301
+ throw versionError;
8328
8302
  }
8329
- } catch (err) {
8330
- this.logger.warn("LibSQL Workflows: Failed to set PRAGMA settings.", err);
8303
+ return {
8304
+ id: skill.id,
8305
+ status: "draft",
8306
+ activeVersionId: void 0,
8307
+ authorId: skill.authorId,
8308
+ createdAt: now,
8309
+ updatedAt: now
8310
+ };
8311
+ } catch (error) {
8312
+ if (error instanceof MastraError) throw error;
8313
+ throw new MastraError(
8314
+ {
8315
+ id: createStorageErrorId("LIBSQL", "CREATE_SKILL", "FAILED"),
8316
+ domain: ErrorDomain.STORAGE,
8317
+ category: ErrorCategory.THIRD_PARTY
8318
+ },
8319
+ error
8320
+ );
8331
8321
  }
8332
8322
  }
8333
- async updateWorkflowResults({
8334
- workflowName,
8335
- runId,
8336
- stepId,
8337
- result,
8338
- requestContext
8339
- }) {
8340
- return this.executeWithRetry(async () => {
8341
- const tx = await this.#client.transaction("write");
8342
- try {
8343
- const existingSnapshotResult = await tx.execute({
8344
- sql: `SELECT json(snapshot) as snapshot FROM ${TABLE_WORKFLOW_SNAPSHOT} WHERE workflow_name = ? AND run_id = ?`,
8345
- args: [workflowName, runId]
8346
- });
8347
- let snapshot;
8348
- if (!existingSnapshotResult.rows?.[0]) {
8349
- snapshot = {
8350
- context: {},
8351
- activePaths: [],
8352
- timestamp: Date.now(),
8353
- suspendedPaths: {},
8354
- activeStepsPath: {},
8355
- resumeLabels: {},
8356
- serializedStepGraph: [],
8357
- status: "pending",
8358
- value: {},
8359
- waitingPaths: {},
8360
- runId,
8361
- requestContext: {}
8362
- };
8363
- } else {
8364
- const existingSnapshot = existingSnapshotResult.rows[0].snapshot;
8365
- snapshot = typeof existingSnapshot === "string" ? JSON.parse(existingSnapshot) : existingSnapshot;
8366
- }
8367
- snapshot.context[stepId] = result;
8368
- snapshot.requestContext = { ...snapshot.requestContext, ...requestContext };
8369
- const now = (/* @__PURE__ */ new Date()).toISOString();
8370
- await tx.execute({
8371
- sql: `INSERT INTO ${TABLE_WORKFLOW_SNAPSHOT} (workflow_name, run_id, snapshot, createdAt, updatedAt)
8372
- VALUES (?, ?, jsonb(?), ?, ?)
8373
- ON CONFLICT(workflow_name, run_id)
8374
- DO UPDATE SET snapshot = excluded.snapshot, updatedAt = excluded.updatedAt`,
8375
- args: [workflowName, runId, JSON.stringify(snapshot), now, now]
8323
+ async update(input) {
8324
+ const { id, ...updates } = input;
8325
+ try {
8326
+ const existing = await this.getById(id);
8327
+ if (!existing) {
8328
+ throw new MastraError({
8329
+ id: createStorageErrorId("LIBSQL", "UPDATE_SKILL", "NOT_FOUND"),
8330
+ domain: ErrorDomain.STORAGE,
8331
+ category: ErrorCategory.USER,
8332
+ text: `Skill ${id} not found`,
8333
+ details: { skillId: id }
8376
8334
  });
8377
- await tx.commit();
8378
- return snapshot.context;
8379
- } catch (error) {
8380
- if (!tx.closed) {
8381
- await tx.rollback();
8335
+ }
8336
+ const { authorId, activeVersionId, status, ...configFields } = updates;
8337
+ const configFieldNames = SNAPSHOT_FIELDS;
8338
+ const hasConfigUpdate = configFieldNames.some((field) => field in configFields);
8339
+ const updateData = {
8340
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
8341
+ };
8342
+ if (authorId !== void 0) updateData.authorId = authorId;
8343
+ if (activeVersionId !== void 0) {
8344
+ updateData.activeVersionId = activeVersionId;
8345
+ if (status === void 0) {
8346
+ updateData.status = "published";
8382
8347
  }
8383
- throw error;
8384
8348
  }
8385
- }, "updateWorkflowResults");
8386
- }
8387
- async updateWorkflowState({
8388
- workflowName,
8389
- runId,
8390
- opts
8391
- }) {
8392
- return this.executeWithRetry(async () => {
8393
- const tx = await this.#client.transaction("write");
8394
- try {
8395
- const existingSnapshotResult = await tx.execute({
8396
- sql: `SELECT json(snapshot) as snapshot FROM ${TABLE_WORKFLOW_SNAPSHOT} WHERE workflow_name = ? AND run_id = ?`,
8397
- args: [workflowName, runId]
8398
- });
8399
- if (!existingSnapshotResult.rows?.[0]) {
8400
- await tx.rollback();
8401
- return void 0;
8349
+ if (status !== void 0) updateData.status = status;
8350
+ await this.#db.update({
8351
+ tableName: TABLE_SKILLS,
8352
+ keys: { id },
8353
+ data: updateData
8354
+ });
8355
+ if (hasConfigUpdate) {
8356
+ const latestVersion = await this.getLatestVersion(id);
8357
+ if (!latestVersion) {
8358
+ throw new MastraError({
8359
+ id: createStorageErrorId("LIBSQL", "UPDATE_SKILL", "NO_VERSIONS"),
8360
+ domain: ErrorDomain.STORAGE,
8361
+ category: ErrorCategory.USER,
8362
+ text: `No versions found for skill ${id}`,
8363
+ details: { skillId: id }
8364
+ });
8402
8365
  }
8403
- const existingSnapshot = existingSnapshotResult.rows[0].snapshot;
8404
- const snapshot = typeof existingSnapshot === "string" ? JSON.parse(existingSnapshot) : existingSnapshot;
8405
- if (!snapshot || !snapshot?.context) {
8406
- await tx.rollback();
8407
- throw new Error(`Snapshot not found for runId ${runId}`);
8366
+ const {
8367
+ id: _versionId,
8368
+ skillId: _skillId,
8369
+ versionNumber: _versionNumber,
8370
+ changedFields: _changedFields,
8371
+ changeMessage: _changeMessage,
8372
+ createdAt: _createdAt,
8373
+ ...latestConfig
8374
+ } = latestVersion;
8375
+ const newConfig = { ...latestConfig, ...configFields };
8376
+ const changedFields = configFieldNames.filter(
8377
+ (field) => field in configFields && JSON.stringify(configFields[field]) !== JSON.stringify(latestConfig[field])
8378
+ );
8379
+ if (changedFields.length > 0) {
8380
+ const newVersionId = crypto.randomUUID();
8381
+ await this.createVersion({
8382
+ id: newVersionId,
8383
+ skillId: id,
8384
+ versionNumber: latestVersion.versionNumber + 1,
8385
+ ...newConfig,
8386
+ changedFields,
8387
+ changeMessage: `Updated ${changedFields.join(", ")}`
8388
+ });
8408
8389
  }
8409
- const updatedSnapshot = { ...snapshot, ...opts };
8410
- await tx.execute({
8411
- sql: `UPDATE ${TABLE_WORKFLOW_SNAPSHOT} SET snapshot = jsonb(?) WHERE workflow_name = ? AND run_id = ?`,
8412
- args: [JSON.stringify(updatedSnapshot), workflowName, runId]
8390
+ }
8391
+ const updated = await this.getById(id);
8392
+ if (!updated) {
8393
+ throw new MastraError({
8394
+ id: createStorageErrorId("LIBSQL", "UPDATE_SKILL", "NOT_FOUND_AFTER_UPDATE"),
8395
+ domain: ErrorDomain.STORAGE,
8396
+ category: ErrorCategory.SYSTEM,
8397
+ text: `Skill ${id} not found after update`,
8398
+ details: { id }
8413
8399
  });
8414
- await tx.commit();
8415
- return updatedSnapshot;
8416
- } catch (error) {
8417
- if (!tx.closed) {
8418
- await tx.rollback();
8419
- }
8420
- throw error;
8421
8400
  }
8422
- }, "updateWorkflowState");
8423
- }
8424
- async persistWorkflowSnapshot({
8425
- workflowName,
8426
- runId,
8427
- resourceId,
8428
- snapshot,
8429
- createdAt,
8430
- updatedAt
8431
- }) {
8432
- const now = /* @__PURE__ */ new Date();
8433
- const data = {
8434
- workflow_name: workflowName,
8435
- run_id: runId,
8436
- resourceId,
8437
- snapshot,
8438
- createdAt: createdAt ?? now,
8439
- updatedAt: updatedAt ?? now
8440
- };
8441
- this.logger.debug("Persisting workflow snapshot", { workflowName, runId, data });
8442
- await this.#db.insert({
8443
- tableName: TABLE_WORKFLOW_SNAPSHOT,
8444
- record: data
8445
- });
8401
+ return updated;
8402
+ } catch (error) {
8403
+ if (error instanceof MastraError) throw error;
8404
+ throw new MastraError(
8405
+ {
8406
+ id: createStorageErrorId("LIBSQL", "UPDATE_SKILL", "FAILED"),
8407
+ domain: ErrorDomain.STORAGE,
8408
+ category: ErrorCategory.THIRD_PARTY
8409
+ },
8410
+ error
8411
+ );
8412
+ }
8446
8413
  }
8447
- async loadWorkflowSnapshot({
8448
- workflowName,
8449
- runId
8450
- }) {
8451
- this.logger.debug("Loading workflow snapshot", { workflowName, runId });
8452
- const d = await this.#db.select({
8453
- tableName: TABLE_WORKFLOW_SNAPSHOT,
8454
- keys: { workflow_name: workflowName, run_id: runId }
8414
+ async delete(id) {
8415
+ try {
8416
+ await this.deleteVersionsByParentId(id);
8417
+ await this.#client.execute({
8418
+ sql: `DELETE FROM "${TABLE_SKILLS}" WHERE "id" = ?`,
8419
+ args: [id]
8420
+ });
8421
+ } catch (error) {
8422
+ if (error instanceof MastraError) throw error;
8423
+ throw new MastraError(
8424
+ {
8425
+ id: createStorageErrorId("LIBSQL", "DELETE_SKILL", "FAILED"),
8426
+ domain: ErrorDomain.STORAGE,
8427
+ category: ErrorCategory.THIRD_PARTY
8428
+ },
8429
+ error
8430
+ );
8431
+ }
8432
+ }
8433
+ async list(args) {
8434
+ try {
8435
+ const { page = 0, perPage: perPageInput, orderBy, authorId } = args || {};
8436
+ const { field, direction } = this.parseOrderBy(orderBy);
8437
+ const conditions = [];
8438
+ const queryParams = [];
8439
+ if (authorId !== void 0) {
8440
+ conditions.push("authorId = ?");
8441
+ queryParams.push(authorId);
8442
+ }
8443
+ const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
8444
+ const countResult = await this.#client.execute({
8445
+ sql: `SELECT COUNT(*) as count FROM "${TABLE_SKILLS}" ${whereClause}`,
8446
+ args: queryParams
8447
+ });
8448
+ const total = Number(countResult.rows?.[0]?.count ?? 0);
8449
+ if (total === 0) {
8450
+ return {
8451
+ skills: [],
8452
+ total: 0,
8453
+ page,
8454
+ perPage: perPageInput ?? 100,
8455
+ hasMore: false
8456
+ };
8457
+ }
8458
+ const perPage = normalizePerPage(perPageInput, 100);
8459
+ const { offset: start, perPage: perPageForResponse } = calculatePagination(page, perPageInput, perPage);
8460
+ const limitValue = perPageInput === false ? total : perPage;
8461
+ const end = perPageInput === false ? total : start + perPage;
8462
+ const result = await this.#client.execute({
8463
+ sql: `SELECT ${buildSelectColumns(TABLE_SKILLS)} FROM "${TABLE_SKILLS}" ${whereClause} ORDER BY ${field} ${direction} LIMIT ? OFFSET ?`,
8464
+ args: [...queryParams, limitValue, start]
8465
+ });
8466
+ const skills = result.rows?.map((row) => this.#parseSkillRow(row)) ?? [];
8467
+ return {
8468
+ skills,
8469
+ total,
8470
+ page,
8471
+ perPage: perPageForResponse,
8472
+ hasMore: end < total
8473
+ };
8474
+ } catch (error) {
8475
+ if (error instanceof MastraError) throw error;
8476
+ throw new MastraError(
8477
+ {
8478
+ id: createStorageErrorId("LIBSQL", "LIST_SKILLS", "FAILED"),
8479
+ domain: ErrorDomain.STORAGE,
8480
+ category: ErrorCategory.THIRD_PARTY
8481
+ },
8482
+ error
8483
+ );
8484
+ }
8485
+ }
8486
+ // ==========================================================================
8487
+ // Skill Version Methods
8488
+ // ==========================================================================
8489
+ async createVersion(input) {
8490
+ try {
8491
+ const now = /* @__PURE__ */ new Date();
8492
+ await this.#client.execute({
8493
+ sql: `INSERT INTO "${TABLE_SKILL_VERSIONS}" (
8494
+ id, "skillId", "versionNumber",
8495
+ name, description, instructions, license, compatibility,
8496
+ source, "references", scripts, assets, metadata, tree,
8497
+ "changedFields", "changeMessage", "createdAt"
8498
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
8499
+ args: [
8500
+ input.id,
8501
+ input.skillId,
8502
+ input.versionNumber,
8503
+ input.name,
8504
+ input.description ?? null,
8505
+ input.instructions ?? null,
8506
+ input.license ?? null,
8507
+ input.compatibility ? JSON.stringify(input.compatibility) : null,
8508
+ input.source ? JSON.stringify(input.source) : null,
8509
+ input.references ? JSON.stringify(input.references) : null,
8510
+ input.scripts ? JSON.stringify(input.scripts) : null,
8511
+ input.assets ? JSON.stringify(input.assets) : null,
8512
+ input.metadata ? JSON.stringify(input.metadata) : null,
8513
+ input.tree ? JSON.stringify(input.tree) : null,
8514
+ input.changedFields ? JSON.stringify(input.changedFields) : null,
8515
+ input.changeMessage ?? null,
8516
+ now.toISOString()
8517
+ ]
8518
+ });
8519
+ return {
8520
+ ...input,
8521
+ createdAt: now
8522
+ };
8523
+ } catch (error) {
8524
+ if (error instanceof MastraError) throw error;
8525
+ throw new MastraError(
8526
+ {
8527
+ id: createStorageErrorId("LIBSQL", "CREATE_SKILL_VERSION", "FAILED"),
8528
+ domain: ErrorDomain.STORAGE,
8529
+ category: ErrorCategory.THIRD_PARTY
8530
+ },
8531
+ error
8532
+ );
8533
+ }
8534
+ }
8535
+ async getVersion(id) {
8536
+ try {
8537
+ const result = await this.#client.execute({
8538
+ sql: `SELECT ${buildSelectColumns(TABLE_SKILL_VERSIONS)} FROM "${TABLE_SKILL_VERSIONS}" WHERE id = ?`,
8539
+ args: [id]
8540
+ });
8541
+ const row = result.rows?.[0];
8542
+ return row ? this.#parseVersionRow(row) : null;
8543
+ } catch (error) {
8544
+ if (error instanceof MastraError) throw error;
8545
+ throw new MastraError(
8546
+ {
8547
+ id: createStorageErrorId("LIBSQL", "GET_SKILL_VERSION", "FAILED"),
8548
+ domain: ErrorDomain.STORAGE,
8549
+ category: ErrorCategory.THIRD_PARTY
8550
+ },
8551
+ error
8552
+ );
8553
+ }
8554
+ }
8555
+ async getVersionByNumber(skillId, versionNumber) {
8556
+ try {
8557
+ const result = await this.#client.execute({
8558
+ sql: `SELECT ${buildSelectColumns(TABLE_SKILL_VERSIONS)} FROM "${TABLE_SKILL_VERSIONS}" WHERE skillId = ? AND versionNumber = ?`,
8559
+ args: [skillId, versionNumber]
8560
+ });
8561
+ const row = result.rows?.[0];
8562
+ return row ? this.#parseVersionRow(row) : null;
8563
+ } catch (error) {
8564
+ if (error instanceof MastraError) throw error;
8565
+ throw new MastraError(
8566
+ {
8567
+ id: createStorageErrorId("LIBSQL", "GET_SKILL_VERSION_BY_NUMBER", "FAILED"),
8568
+ domain: ErrorDomain.STORAGE,
8569
+ category: ErrorCategory.THIRD_PARTY
8570
+ },
8571
+ error
8572
+ );
8573
+ }
8574
+ }
8575
+ async getLatestVersion(skillId) {
8576
+ try {
8577
+ const result = await this.#client.execute({
8578
+ sql: `SELECT ${buildSelectColumns(TABLE_SKILL_VERSIONS)} FROM "${TABLE_SKILL_VERSIONS}" WHERE skillId = ? ORDER BY versionNumber DESC LIMIT 1`,
8579
+ args: [skillId]
8580
+ });
8581
+ const row = result.rows?.[0];
8582
+ return row ? this.#parseVersionRow(row) : null;
8583
+ } catch (error) {
8584
+ if (error instanceof MastraError) throw error;
8585
+ throw new MastraError(
8586
+ {
8587
+ id: createStorageErrorId("LIBSQL", "GET_LATEST_SKILL_VERSION", "FAILED"),
8588
+ domain: ErrorDomain.STORAGE,
8589
+ category: ErrorCategory.THIRD_PARTY
8590
+ },
8591
+ error
8592
+ );
8593
+ }
8594
+ }
8595
+ async listVersions(input) {
8596
+ try {
8597
+ const { skillId, page = 0, perPage: perPageInput, orderBy } = input;
8598
+ const { field, direction } = this.parseVersionOrderBy(orderBy);
8599
+ const countResult = await this.#client.execute({
8600
+ sql: `SELECT COUNT(*) as count FROM "${TABLE_SKILL_VERSIONS}" WHERE skillId = ?`,
8601
+ args: [skillId]
8602
+ });
8603
+ const total = Number(countResult.rows?.[0]?.count ?? 0);
8604
+ if (total === 0) {
8605
+ return {
8606
+ versions: [],
8607
+ total: 0,
8608
+ page,
8609
+ perPage: perPageInput ?? 20,
8610
+ hasMore: false
8611
+ };
8612
+ }
8613
+ const perPage = normalizePerPage(perPageInput, 20);
8614
+ const { offset: start, perPage: perPageForResponse } = calculatePagination(page, perPageInput, perPage);
8615
+ const limitValue = perPageInput === false ? total : perPage;
8616
+ const end = perPageInput === false ? total : start + perPage;
8617
+ const result = await this.#client.execute({
8618
+ sql: `SELECT ${buildSelectColumns(TABLE_SKILL_VERSIONS)} FROM "${TABLE_SKILL_VERSIONS}" WHERE skillId = ? ORDER BY ${field} ${direction} LIMIT ? OFFSET ?`,
8619
+ args: [skillId, limitValue, start]
8620
+ });
8621
+ const versions = result.rows?.map((row) => this.#parseVersionRow(row)) ?? [];
8622
+ return {
8623
+ versions,
8624
+ total,
8625
+ page,
8626
+ perPage: perPageForResponse,
8627
+ hasMore: end < total
8628
+ };
8629
+ } catch (error) {
8630
+ if (error instanceof MastraError) throw error;
8631
+ throw new MastraError(
8632
+ {
8633
+ id: createStorageErrorId("LIBSQL", "LIST_SKILL_VERSIONS", "FAILED"),
8634
+ domain: ErrorDomain.STORAGE,
8635
+ category: ErrorCategory.THIRD_PARTY
8636
+ },
8637
+ error
8638
+ );
8639
+ }
8640
+ }
8641
+ async deleteVersion(id) {
8642
+ try {
8643
+ await this.#client.execute({
8644
+ sql: `DELETE FROM "${TABLE_SKILL_VERSIONS}" WHERE "id" = ?`,
8645
+ args: [id]
8646
+ });
8647
+ } catch (error) {
8648
+ if (error instanceof MastraError) throw error;
8649
+ throw new MastraError(
8650
+ {
8651
+ id: createStorageErrorId("LIBSQL", "DELETE_SKILL_VERSION", "FAILED"),
8652
+ domain: ErrorDomain.STORAGE,
8653
+ category: ErrorCategory.THIRD_PARTY
8654
+ },
8655
+ error
8656
+ );
8657
+ }
8658
+ }
8659
+ async deleteVersionsByParentId(entityId) {
8660
+ try {
8661
+ await this.#client.execute({
8662
+ sql: `DELETE FROM "${TABLE_SKILL_VERSIONS}" WHERE "skillId" = ?`,
8663
+ args: [entityId]
8664
+ });
8665
+ } catch (error) {
8666
+ if (error instanceof MastraError) throw error;
8667
+ throw new MastraError(
8668
+ {
8669
+ id: createStorageErrorId("LIBSQL", "DELETE_SKILL_VERSIONS_BY_SKILL", "FAILED"),
8670
+ domain: ErrorDomain.STORAGE,
8671
+ category: ErrorCategory.THIRD_PARTY
8672
+ },
8673
+ error
8674
+ );
8675
+ }
8676
+ }
8677
+ async countVersions(skillId) {
8678
+ try {
8679
+ const result = await this.#client.execute({
8680
+ sql: `SELECT COUNT(*) as count FROM "${TABLE_SKILL_VERSIONS}" WHERE skillId = ?`,
8681
+ args: [skillId]
8682
+ });
8683
+ return Number(result.rows?.[0]?.count ?? 0);
8684
+ } catch (error) {
8685
+ if (error instanceof MastraError) throw error;
8686
+ throw new MastraError(
8687
+ {
8688
+ id: createStorageErrorId("LIBSQL", "COUNT_SKILL_VERSIONS", "FAILED"),
8689
+ domain: ErrorDomain.STORAGE,
8690
+ category: ErrorCategory.THIRD_PARTY
8691
+ },
8692
+ error
8693
+ );
8694
+ }
8695
+ }
8696
+ // ==========================================================================
8697
+ // Private Helpers
8698
+ // ==========================================================================
8699
+ #parseSkillRow(row) {
8700
+ return {
8701
+ id: row.id,
8702
+ status: row.status ?? "draft",
8703
+ activeVersionId: row.activeVersionId ?? void 0,
8704
+ authorId: row.authorId ?? void 0,
8705
+ createdAt: new Date(row.createdAt),
8706
+ updatedAt: new Date(row.updatedAt)
8707
+ };
8708
+ }
8709
+ #parseVersionRow(row) {
8710
+ const safeParseJSON = (val) => {
8711
+ if (val === null || val === void 0) return void 0;
8712
+ if (typeof val === "string") {
8713
+ try {
8714
+ return JSON.parse(val);
8715
+ } catch {
8716
+ return val;
8717
+ }
8718
+ }
8719
+ return val;
8720
+ };
8721
+ return {
8722
+ id: row.id,
8723
+ skillId: row.skillId,
8724
+ versionNumber: Number(row.versionNumber),
8725
+ name: row.name,
8726
+ description: row.description ?? void 0,
8727
+ instructions: row.instructions ?? void 0,
8728
+ license: row.license ?? void 0,
8729
+ compatibility: safeParseJSON(row.compatibility),
8730
+ source: safeParseJSON(row.source),
8731
+ references: safeParseJSON(row.references),
8732
+ scripts: safeParseJSON(row.scripts),
8733
+ assets: safeParseJSON(row.assets),
8734
+ metadata: safeParseJSON(row.metadata),
8735
+ tree: safeParseJSON(row.tree),
8736
+ changedFields: safeParseJSON(row.changedFields),
8737
+ changeMessage: row.changeMessage ?? void 0,
8738
+ createdAt: new Date(row.createdAt)
8739
+ };
8740
+ }
8741
+ };
8742
+ var WorkflowsLibSQL = class extends WorkflowsStorage {
8743
+ #db;
8744
+ #client;
8745
+ executeWithRetry;
8746
+ constructor(config) {
8747
+ super();
8748
+ const client = resolveClient(config);
8749
+ const maxRetries = config.maxRetries ?? 5;
8750
+ const initialBackoffMs = config.initialBackoffMs ?? 500;
8751
+ this.#client = client;
8752
+ this.#db = new LibSQLDB({ client, maxRetries, initialBackoffMs });
8753
+ this.executeWithRetry = createExecuteWriteOperationWithRetry({
8754
+ logger: this.logger,
8755
+ maxRetries,
8756
+ initialBackoffMs
8757
+ });
8758
+ this.setupPragmaSettings().catch(
8759
+ (err) => this.logger.warn("LibSQL Workflows: Failed to setup PRAGMA settings.", err)
8760
+ );
8761
+ }
8762
+ parseWorkflowRun(row) {
8763
+ let parsedSnapshot = row.snapshot;
8764
+ if (typeof parsedSnapshot === "string") {
8765
+ try {
8766
+ parsedSnapshot = JSON.parse(row.snapshot);
8767
+ } catch (e) {
8768
+ this.logger.warn(`Failed to parse snapshot for workflow ${row.workflow_name}: ${e}`);
8769
+ }
8770
+ }
8771
+ return {
8772
+ workflowName: row.workflow_name,
8773
+ runId: row.run_id,
8774
+ snapshot: parsedSnapshot,
8775
+ resourceId: row.resourceId,
8776
+ createdAt: new Date(row.createdAt),
8777
+ updatedAt: new Date(row.updatedAt)
8778
+ };
8779
+ }
8780
+ async init() {
8781
+ const schema = TABLE_SCHEMAS[TABLE_WORKFLOW_SNAPSHOT];
8782
+ await this.#db.createTable({ tableName: TABLE_WORKFLOW_SNAPSHOT, schema });
8783
+ await this.#db.alterTable({
8784
+ tableName: TABLE_WORKFLOW_SNAPSHOT,
8785
+ schema,
8786
+ ifNotExists: ["resourceId"]
8787
+ });
8788
+ }
8789
+ async dangerouslyClearAll() {
8790
+ await this.#db.deleteData({ tableName: TABLE_WORKFLOW_SNAPSHOT });
8791
+ }
8792
+ async setupPragmaSettings() {
8793
+ try {
8794
+ await this.#client.execute("PRAGMA busy_timeout = 10000;");
8795
+ this.logger.debug("LibSQL Workflows: PRAGMA busy_timeout=10000 set.");
8796
+ try {
8797
+ await this.#client.execute("PRAGMA journal_mode = WAL;");
8798
+ this.logger.debug("LibSQL Workflows: PRAGMA journal_mode=WAL set.");
8799
+ } catch {
8800
+ this.logger.debug("LibSQL Workflows: WAL mode not supported, using default journal mode.");
8801
+ }
8802
+ try {
8803
+ await this.#client.execute("PRAGMA synchronous = NORMAL;");
8804
+ this.logger.debug("LibSQL Workflows: PRAGMA synchronous=NORMAL set.");
8805
+ } catch {
8806
+ this.logger.debug("LibSQL Workflows: Failed to set synchronous mode.");
8807
+ }
8808
+ } catch (err) {
8809
+ this.logger.warn("LibSQL Workflows: Failed to set PRAGMA settings.", err);
8810
+ }
8811
+ }
8812
+ async updateWorkflowResults({
8813
+ workflowName,
8814
+ runId,
8815
+ stepId,
8816
+ result,
8817
+ requestContext
8818
+ }) {
8819
+ return this.executeWithRetry(async () => {
8820
+ const tx = await this.#client.transaction("write");
8821
+ try {
8822
+ const existingSnapshotResult = await tx.execute({
8823
+ sql: `SELECT json(snapshot) as snapshot FROM ${TABLE_WORKFLOW_SNAPSHOT} WHERE workflow_name = ? AND run_id = ?`,
8824
+ args: [workflowName, runId]
8825
+ });
8826
+ let snapshot;
8827
+ if (!existingSnapshotResult.rows?.[0]) {
8828
+ snapshot = {
8829
+ context: {},
8830
+ activePaths: [],
8831
+ timestamp: Date.now(),
8832
+ suspendedPaths: {},
8833
+ activeStepsPath: {},
8834
+ resumeLabels: {},
8835
+ serializedStepGraph: [],
8836
+ status: "pending",
8837
+ value: {},
8838
+ waitingPaths: {},
8839
+ runId,
8840
+ requestContext: {}
8841
+ };
8842
+ } else {
8843
+ const existingSnapshot = existingSnapshotResult.rows[0].snapshot;
8844
+ snapshot = typeof existingSnapshot === "string" ? JSON.parse(existingSnapshot) : existingSnapshot;
8845
+ }
8846
+ snapshot.context[stepId] = result;
8847
+ snapshot.requestContext = { ...snapshot.requestContext, ...requestContext };
8848
+ const now = (/* @__PURE__ */ new Date()).toISOString();
8849
+ await tx.execute({
8850
+ sql: `INSERT INTO ${TABLE_WORKFLOW_SNAPSHOT} (workflow_name, run_id, snapshot, createdAt, updatedAt)
8851
+ VALUES (?, ?, jsonb(?), ?, ?)
8852
+ ON CONFLICT(workflow_name, run_id)
8853
+ DO UPDATE SET snapshot = excluded.snapshot, updatedAt = excluded.updatedAt`,
8854
+ args: [workflowName, runId, JSON.stringify(snapshot), now, now]
8855
+ });
8856
+ await tx.commit();
8857
+ return snapshot.context;
8858
+ } catch (error) {
8859
+ if (!tx.closed) {
8860
+ await tx.rollback();
8861
+ }
8862
+ throw error;
8863
+ }
8864
+ }, "updateWorkflowResults");
8865
+ }
8866
+ async updateWorkflowState({
8867
+ workflowName,
8868
+ runId,
8869
+ opts
8870
+ }) {
8871
+ return this.executeWithRetry(async () => {
8872
+ const tx = await this.#client.transaction("write");
8873
+ try {
8874
+ const existingSnapshotResult = await tx.execute({
8875
+ sql: `SELECT json(snapshot) as snapshot FROM ${TABLE_WORKFLOW_SNAPSHOT} WHERE workflow_name = ? AND run_id = ?`,
8876
+ args: [workflowName, runId]
8877
+ });
8878
+ if (!existingSnapshotResult.rows?.[0]) {
8879
+ await tx.rollback();
8880
+ return void 0;
8881
+ }
8882
+ const existingSnapshot = existingSnapshotResult.rows[0].snapshot;
8883
+ const snapshot = typeof existingSnapshot === "string" ? JSON.parse(existingSnapshot) : existingSnapshot;
8884
+ if (!snapshot || !snapshot?.context) {
8885
+ await tx.rollback();
8886
+ throw new Error(`Snapshot not found for runId ${runId}`);
8887
+ }
8888
+ const updatedSnapshot = { ...snapshot, ...opts };
8889
+ await tx.execute({
8890
+ sql: `UPDATE ${TABLE_WORKFLOW_SNAPSHOT} SET snapshot = jsonb(?) WHERE workflow_name = ? AND run_id = ?`,
8891
+ args: [JSON.stringify(updatedSnapshot), workflowName, runId]
8892
+ });
8893
+ await tx.commit();
8894
+ return updatedSnapshot;
8895
+ } catch (error) {
8896
+ if (!tx.closed) {
8897
+ await tx.rollback();
8898
+ }
8899
+ throw error;
8900
+ }
8901
+ }, "updateWorkflowState");
8902
+ }
8903
+ async persistWorkflowSnapshot({
8904
+ workflowName,
8905
+ runId,
8906
+ resourceId,
8907
+ snapshot,
8908
+ createdAt,
8909
+ updatedAt
8910
+ }) {
8911
+ const now = /* @__PURE__ */ new Date();
8912
+ const data = {
8913
+ workflow_name: workflowName,
8914
+ run_id: runId,
8915
+ resourceId,
8916
+ snapshot,
8917
+ createdAt: createdAt ?? now,
8918
+ updatedAt: updatedAt ?? now
8919
+ };
8920
+ this.logger.debug("Persisting workflow snapshot", { workflowName, runId, data });
8921
+ await this.#db.insert({
8922
+ tableName: TABLE_WORKFLOW_SNAPSHOT,
8923
+ record: data
8924
+ });
8925
+ }
8926
+ async loadWorkflowSnapshot({
8927
+ workflowName,
8928
+ runId
8929
+ }) {
8930
+ this.logger.debug("Loading workflow snapshot", { workflowName, runId });
8931
+ const d = await this.#db.select({
8932
+ tableName: TABLE_WORKFLOW_SNAPSHOT,
8933
+ keys: { workflow_name: workflowName, run_id: runId }
8934
+ });
8935
+ return d ? d.snapshot : null;
8936
+ }
8937
+ async getWorkflowRunById({
8938
+ runId,
8939
+ workflowName
8940
+ }) {
8941
+ const conditions = [];
8942
+ const args = [];
8943
+ if (runId) {
8944
+ conditions.push("run_id = ?");
8945
+ args.push(runId);
8946
+ }
8947
+ if (workflowName) {
8948
+ conditions.push("workflow_name = ?");
8949
+ args.push(workflowName);
8950
+ }
8951
+ const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
8952
+ try {
8953
+ const result = await this.#client.execute({
8954
+ sql: `SELECT workflow_name, run_id, resourceId, json(snapshot) as snapshot, createdAt, updatedAt FROM ${TABLE_WORKFLOW_SNAPSHOT} ${whereClause} ORDER BY createdAt DESC LIMIT 1`,
8955
+ args
8956
+ });
8957
+ if (!result.rows?.[0]) {
8958
+ return null;
8959
+ }
8960
+ return this.parseWorkflowRun(result.rows[0]);
8961
+ } catch (error) {
8962
+ throw new MastraError(
8963
+ {
8964
+ id: createStorageErrorId("LIBSQL", "GET_WORKFLOW_RUN_BY_ID", "FAILED"),
8965
+ domain: ErrorDomain.STORAGE,
8966
+ category: ErrorCategory.THIRD_PARTY
8967
+ },
8968
+ error
8969
+ );
8970
+ }
8971
+ }
8972
+ async deleteWorkflowRunById({ runId, workflowName }) {
8973
+ return this.executeWithRetry(async () => {
8974
+ try {
8975
+ await this.#client.execute({
8976
+ sql: `DELETE FROM ${TABLE_WORKFLOW_SNAPSHOT} WHERE workflow_name = ? AND run_id = ?`,
8977
+ args: [workflowName, runId]
8978
+ });
8979
+ } catch (error) {
8980
+ throw new MastraError(
8981
+ {
8982
+ id: createStorageErrorId("LIBSQL", "DELETE_WORKFLOW_RUN_BY_ID", "FAILED"),
8983
+ domain: ErrorDomain.STORAGE,
8984
+ category: ErrorCategory.THIRD_PARTY,
8985
+ details: { runId, workflowName }
8986
+ },
8987
+ error
8988
+ );
8989
+ }
8990
+ }, "deleteWorkflowRunById");
8991
+ }
8992
+ async listWorkflowRuns({
8993
+ workflowName,
8994
+ fromDate,
8995
+ toDate,
8996
+ page,
8997
+ perPage,
8998
+ resourceId,
8999
+ status
9000
+ } = {}) {
9001
+ try {
9002
+ const conditions = [];
9003
+ const args = [];
9004
+ if (workflowName) {
9005
+ conditions.push("workflow_name = ?");
9006
+ args.push(workflowName);
9007
+ }
9008
+ if (status) {
9009
+ conditions.push("json_extract(snapshot, '$.status') = ?");
9010
+ args.push(status);
9011
+ }
9012
+ if (fromDate) {
9013
+ conditions.push("createdAt >= ?");
9014
+ args.push(fromDate.toISOString());
9015
+ }
9016
+ if (toDate) {
9017
+ conditions.push("createdAt <= ?");
9018
+ args.push(toDate.toISOString());
9019
+ }
9020
+ if (resourceId) {
9021
+ const hasResourceId = await this.#db.hasColumn(TABLE_WORKFLOW_SNAPSHOT, "resourceId");
9022
+ if (hasResourceId) {
9023
+ conditions.push("resourceId = ?");
9024
+ args.push(resourceId);
9025
+ } else {
9026
+ this.logger.warn(`[${TABLE_WORKFLOW_SNAPSHOT}] resourceId column not found. Skipping resourceId filter.`);
9027
+ }
9028
+ }
9029
+ const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
9030
+ let total = 0;
9031
+ const usePagination = typeof perPage === "number" && typeof page === "number";
9032
+ if (usePagination) {
9033
+ const countResult = await this.#client.execute({
9034
+ sql: `SELECT COUNT(*) as count FROM ${TABLE_WORKFLOW_SNAPSHOT} ${whereClause}`,
9035
+ args
9036
+ });
9037
+ total = Number(countResult.rows?.[0]?.count ?? 0);
9038
+ }
9039
+ const normalizedPerPage = usePagination ? normalizePerPage(perPage, Number.MAX_SAFE_INTEGER) : 0;
9040
+ const offset = usePagination ? page * normalizedPerPage : 0;
9041
+ const result = await this.#client.execute({
9042
+ sql: `SELECT workflow_name, run_id, resourceId, json(snapshot) as snapshot, createdAt, updatedAt FROM ${TABLE_WORKFLOW_SNAPSHOT} ${whereClause} ORDER BY createdAt DESC${usePagination ? ` LIMIT ? OFFSET ?` : ""}`,
9043
+ args: usePagination ? [...args, normalizedPerPage, offset] : args
9044
+ });
9045
+ const runs = (result.rows || []).map((row) => this.parseWorkflowRun(row));
9046
+ return { runs, total: total || runs.length };
9047
+ } catch (error) {
9048
+ throw new MastraError(
9049
+ {
9050
+ id: createStorageErrorId("LIBSQL", "LIST_WORKFLOW_RUNS", "FAILED"),
9051
+ domain: ErrorDomain.STORAGE,
9052
+ category: ErrorCategory.THIRD_PARTY
9053
+ },
9054
+ error
9055
+ );
9056
+ }
9057
+ }
9058
+ };
9059
+ var SNAPSHOT_FIELDS2 = [
9060
+ "name",
9061
+ "description",
9062
+ "filesystem",
9063
+ "sandbox",
9064
+ "mounts",
9065
+ "search",
9066
+ "skills",
9067
+ "tools",
9068
+ "autoSync",
9069
+ "operationTimeout"
9070
+ ];
9071
+ var WorkspacesLibSQL = class extends WorkspacesStorage {
9072
+ #db;
9073
+ #client;
9074
+ constructor(config) {
9075
+ super();
9076
+ const client = resolveClient(config);
9077
+ this.#client = client;
9078
+ this.#db = new LibSQLDB({ client, maxRetries: config.maxRetries, initialBackoffMs: config.initialBackoffMs });
9079
+ }
9080
+ async init() {
9081
+ await this.#db.createTable({ tableName: TABLE_WORKSPACES, schema: WORKSPACES_SCHEMA });
9082
+ await this.#db.createTable({
9083
+ tableName: TABLE_WORKSPACE_VERSIONS,
9084
+ schema: WORKSPACE_VERSIONS_SCHEMA
8455
9085
  });
8456
- return d ? d.snapshot : null;
9086
+ await this.#client.execute(
9087
+ `CREATE UNIQUE INDEX IF NOT EXISTS idx_workspace_versions_workspace_version ON "${TABLE_WORKSPACE_VERSIONS}" ("workspaceId", "versionNumber")`
9088
+ );
9089
+ }
9090
+ async dangerouslyClearAll() {
9091
+ await this.#db.deleteData({ tableName: TABLE_WORKSPACES });
9092
+ await this.#db.deleteData({ tableName: TABLE_WORKSPACE_VERSIONS });
9093
+ }
9094
+ // ==========================================================================
9095
+ // Workspace CRUD
9096
+ // ==========================================================================
9097
+ async getById(id) {
9098
+ try {
9099
+ const result = await this.#client.execute({
9100
+ sql: `SELECT ${buildSelectColumns(TABLE_WORKSPACES)} FROM "${TABLE_WORKSPACES}" WHERE id = ?`,
9101
+ args: [id]
9102
+ });
9103
+ const row = result.rows?.[0];
9104
+ return row ? this.#parseWorkspaceRow(row) : null;
9105
+ } catch (error) {
9106
+ if (error instanceof MastraError) throw error;
9107
+ throw new MastraError(
9108
+ {
9109
+ id: createStorageErrorId("LIBSQL", "GET_WORKSPACE", "FAILED"),
9110
+ domain: ErrorDomain.STORAGE,
9111
+ category: ErrorCategory.THIRD_PARTY
9112
+ },
9113
+ error
9114
+ );
9115
+ }
9116
+ }
9117
+ async create(input) {
9118
+ const { workspace } = input;
9119
+ try {
9120
+ const now = /* @__PURE__ */ new Date();
9121
+ await this.#db.insert({
9122
+ tableName: TABLE_WORKSPACES,
9123
+ record: {
9124
+ id: workspace.id,
9125
+ status: "draft",
9126
+ activeVersionId: null,
9127
+ authorId: workspace.authorId ?? null,
9128
+ metadata: workspace.metadata ?? null,
9129
+ createdAt: now.toISOString(),
9130
+ updatedAt: now.toISOString()
9131
+ }
9132
+ });
9133
+ const { id: _id, authorId: _authorId, metadata: _metadata, ...snapshotConfig } = workspace;
9134
+ const versionId = crypto.randomUUID();
9135
+ try {
9136
+ await this.createVersion({
9137
+ id: versionId,
9138
+ workspaceId: workspace.id,
9139
+ versionNumber: 1,
9140
+ ...snapshotConfig,
9141
+ changedFields: Object.keys(snapshotConfig),
9142
+ changeMessage: "Initial version"
9143
+ });
9144
+ } catch (versionError) {
9145
+ await this.#db.delete({ tableName: TABLE_WORKSPACES, keys: { id: workspace.id } });
9146
+ throw versionError;
9147
+ }
9148
+ return {
9149
+ id: workspace.id,
9150
+ status: "draft",
9151
+ activeVersionId: void 0,
9152
+ authorId: workspace.authorId,
9153
+ metadata: workspace.metadata,
9154
+ createdAt: now,
9155
+ updatedAt: now
9156
+ };
9157
+ } catch (error) {
9158
+ if (error instanceof MastraError) throw error;
9159
+ throw new MastraError(
9160
+ {
9161
+ id: createStorageErrorId("LIBSQL", "CREATE_WORKSPACE", "FAILED"),
9162
+ domain: ErrorDomain.STORAGE,
9163
+ category: ErrorCategory.THIRD_PARTY
9164
+ },
9165
+ error
9166
+ );
9167
+ }
9168
+ }
9169
+ async update(input) {
9170
+ const { id, ...updates } = input;
9171
+ try {
9172
+ const existing = await this.getById(id);
9173
+ if (!existing) {
9174
+ throw new MastraError({
9175
+ id: createStorageErrorId("LIBSQL", "UPDATE_WORKSPACE", "NOT_FOUND"),
9176
+ domain: ErrorDomain.STORAGE,
9177
+ category: ErrorCategory.USER,
9178
+ text: `Workspace ${id} not found`,
9179
+ details: { workspaceId: id }
9180
+ });
9181
+ }
9182
+ const { authorId, activeVersionId, metadata, status, ...configFields } = updates;
9183
+ const configFieldNames = SNAPSHOT_FIELDS2;
9184
+ const hasConfigUpdate = configFieldNames.some((field) => field in configFields);
9185
+ const updateData = {
9186
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
9187
+ };
9188
+ if (authorId !== void 0) updateData.authorId = authorId;
9189
+ if (activeVersionId !== void 0) {
9190
+ updateData.activeVersionId = activeVersionId;
9191
+ if (status === void 0) {
9192
+ updateData.status = "published";
9193
+ }
9194
+ }
9195
+ if (status !== void 0) updateData.status = status;
9196
+ if (metadata !== void 0) {
9197
+ updateData.metadata = { ...existing.metadata || {}, ...metadata };
9198
+ }
9199
+ await this.#db.update({
9200
+ tableName: TABLE_WORKSPACES,
9201
+ keys: { id },
9202
+ data: updateData
9203
+ });
9204
+ if (hasConfigUpdate) {
9205
+ const latestVersion = await this.getLatestVersion(id);
9206
+ if (!latestVersion) {
9207
+ throw new Error(`No versions found for workspace ${id}`);
9208
+ }
9209
+ const {
9210
+ id: _versionId,
9211
+ workspaceId: _workspaceId,
9212
+ versionNumber: _versionNumber,
9213
+ changedFields: _changedFields,
9214
+ changeMessage: _changeMessage,
9215
+ createdAt: _createdAt,
9216
+ ...latestConfig
9217
+ } = latestVersion;
9218
+ const newConfig = { ...latestConfig, ...configFields };
9219
+ const changedFields = configFieldNames.filter(
9220
+ (field) => field in configFields && JSON.stringify(configFields[field]) !== JSON.stringify(latestConfig[field])
9221
+ );
9222
+ if (changedFields.length > 0) {
9223
+ const newVersionId = crypto.randomUUID();
9224
+ await this.createVersion({
9225
+ id: newVersionId,
9226
+ workspaceId: id,
9227
+ versionNumber: latestVersion.versionNumber + 1,
9228
+ ...newConfig,
9229
+ changedFields,
9230
+ changeMessage: `Updated ${changedFields.join(", ")}`
9231
+ });
9232
+ }
9233
+ }
9234
+ const updated = await this.getById(id);
9235
+ if (!updated) {
9236
+ throw new MastraError({
9237
+ id: createStorageErrorId("LIBSQL", "UPDATE_WORKSPACE", "NOT_FOUND_AFTER_UPDATE"),
9238
+ domain: ErrorDomain.STORAGE,
9239
+ category: ErrorCategory.SYSTEM,
9240
+ text: `Workspace ${id} not found after update`,
9241
+ details: { id }
9242
+ });
9243
+ }
9244
+ return updated;
9245
+ } catch (error) {
9246
+ if (error instanceof MastraError) throw error;
9247
+ throw new MastraError(
9248
+ {
9249
+ id: createStorageErrorId("LIBSQL", "UPDATE_WORKSPACE", "FAILED"),
9250
+ domain: ErrorDomain.STORAGE,
9251
+ category: ErrorCategory.THIRD_PARTY
9252
+ },
9253
+ error
9254
+ );
9255
+ }
9256
+ }
9257
+ async delete(id) {
9258
+ try {
9259
+ await this.deleteVersionsByParentId(id);
9260
+ await this.#client.execute({
9261
+ sql: `DELETE FROM "${TABLE_WORKSPACES}" WHERE "id" = ?`,
9262
+ args: [id]
9263
+ });
9264
+ } catch (error) {
9265
+ if (error instanceof MastraError) throw error;
9266
+ throw new MastraError(
9267
+ {
9268
+ id: createStorageErrorId("LIBSQL", "DELETE_WORKSPACE", "FAILED"),
9269
+ domain: ErrorDomain.STORAGE,
9270
+ category: ErrorCategory.THIRD_PARTY
9271
+ },
9272
+ error
9273
+ );
9274
+ }
9275
+ }
9276
+ async list(args) {
9277
+ try {
9278
+ const { page = 0, perPage: perPageInput, orderBy, authorId, metadata } = args || {};
9279
+ const { field, direction } = this.parseOrderBy(orderBy);
9280
+ const conditions = [];
9281
+ const queryParams = [];
9282
+ if (authorId !== void 0) {
9283
+ conditions.push("authorId = ?");
9284
+ queryParams.push(authorId);
9285
+ }
9286
+ if (metadata && Object.keys(metadata).length > 0) {
9287
+ for (const [key, value] of Object.entries(metadata)) {
9288
+ if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(key)) {
9289
+ throw new MastraError({
9290
+ id: createStorageErrorId("LIBSQL", "LIST_WORKSPACES", "INVALID_METADATA_KEY"),
9291
+ domain: ErrorDomain.STORAGE,
9292
+ category: ErrorCategory.USER,
9293
+ text: `Invalid metadata key: ${key}. Keys must be alphanumeric with underscores.`,
9294
+ details: { key }
9295
+ });
9296
+ }
9297
+ conditions.push(`json_extract(metadata, '$.${key}') = ?`);
9298
+ queryParams.push(typeof value === "string" ? value : JSON.stringify(value));
9299
+ }
9300
+ }
9301
+ const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
9302
+ const countResult = await this.#client.execute({
9303
+ sql: `SELECT COUNT(*) as count FROM "${TABLE_WORKSPACES}" ${whereClause}`,
9304
+ args: queryParams
9305
+ });
9306
+ const total = Number(countResult.rows?.[0]?.count ?? 0);
9307
+ if (total === 0) {
9308
+ return {
9309
+ workspaces: [],
9310
+ total: 0,
9311
+ page,
9312
+ perPage: perPageInput ?? 100,
9313
+ hasMore: false
9314
+ };
9315
+ }
9316
+ const perPage = normalizePerPage(perPageInput, 100);
9317
+ const { offset: start, perPage: perPageForResponse } = calculatePagination(page, perPageInput, perPage);
9318
+ const limitValue = perPageInput === false ? total : perPage;
9319
+ const end = perPageInput === false ? total : start + perPage;
9320
+ const result = await this.#client.execute({
9321
+ sql: `SELECT ${buildSelectColumns(TABLE_WORKSPACES)} FROM "${TABLE_WORKSPACES}" ${whereClause} ORDER BY ${field} ${direction} LIMIT ? OFFSET ?`,
9322
+ args: [...queryParams, limitValue, start]
9323
+ });
9324
+ const workspaces = result.rows?.map((row) => this.#parseWorkspaceRow(row)) ?? [];
9325
+ return {
9326
+ workspaces,
9327
+ total,
9328
+ page,
9329
+ perPage: perPageForResponse,
9330
+ hasMore: end < total
9331
+ };
9332
+ } catch (error) {
9333
+ if (error instanceof MastraError) throw error;
9334
+ throw new MastraError(
9335
+ {
9336
+ id: createStorageErrorId("LIBSQL", "LIST_WORKSPACES", "FAILED"),
9337
+ domain: ErrorDomain.STORAGE,
9338
+ category: ErrorCategory.THIRD_PARTY
9339
+ },
9340
+ error
9341
+ );
9342
+ }
9343
+ }
9344
+ // ==========================================================================
9345
+ // Workspace Version Methods
9346
+ // ==========================================================================
9347
+ async createVersion(input) {
9348
+ try {
9349
+ const now = /* @__PURE__ */ new Date();
9350
+ await this.#client.execute({
9351
+ sql: `INSERT INTO "${TABLE_WORKSPACE_VERSIONS}" (
9352
+ id, "workspaceId", "versionNumber",
9353
+ name, description, filesystem, sandbox, mounts, search, skills, tools,
9354
+ "autoSync", "operationTimeout",
9355
+ "changedFields", "changeMessage", "createdAt"
9356
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
9357
+ args: [
9358
+ input.id,
9359
+ input.workspaceId,
9360
+ input.versionNumber,
9361
+ input.name,
9362
+ input.description ?? null,
9363
+ input.filesystem ? JSON.stringify(input.filesystem) : null,
9364
+ input.sandbox ? JSON.stringify(input.sandbox) : null,
9365
+ input.mounts ? JSON.stringify(input.mounts) : null,
9366
+ input.search ? JSON.stringify(input.search) : null,
9367
+ input.skills ? JSON.stringify(input.skills) : null,
9368
+ input.tools ? JSON.stringify(input.tools) : null,
9369
+ input.autoSync ? 1 : 0,
9370
+ input.operationTimeout ?? null,
9371
+ input.changedFields ? JSON.stringify(input.changedFields) : null,
9372
+ input.changeMessage ?? null,
9373
+ now.toISOString()
9374
+ ]
9375
+ });
9376
+ return {
9377
+ ...input,
9378
+ createdAt: now
9379
+ };
9380
+ } catch (error) {
9381
+ if (error instanceof MastraError) throw error;
9382
+ throw new MastraError(
9383
+ {
9384
+ id: createStorageErrorId("LIBSQL", "CREATE_WORKSPACE_VERSION", "FAILED"),
9385
+ domain: ErrorDomain.STORAGE,
9386
+ category: ErrorCategory.THIRD_PARTY
9387
+ },
9388
+ error
9389
+ );
9390
+ }
8457
9391
  }
8458
- async getWorkflowRunById({
8459
- runId,
8460
- workflowName
8461
- }) {
8462
- const conditions = [];
8463
- const args = [];
8464
- if (runId) {
8465
- conditions.push("run_id = ?");
8466
- args.push(runId);
9392
+ async getVersion(id) {
9393
+ try {
9394
+ const result = await this.#client.execute({
9395
+ sql: `SELECT ${buildSelectColumns(TABLE_WORKSPACE_VERSIONS)} FROM "${TABLE_WORKSPACE_VERSIONS}" WHERE id = ?`,
9396
+ args: [id]
9397
+ });
9398
+ const row = result.rows?.[0];
9399
+ return row ? this.#parseVersionRow(row) : null;
9400
+ } catch (error) {
9401
+ if (error instanceof MastraError) throw error;
9402
+ throw new MastraError(
9403
+ {
9404
+ id: createStorageErrorId("LIBSQL", "GET_WORKSPACE_VERSION", "FAILED"),
9405
+ domain: ErrorDomain.STORAGE,
9406
+ category: ErrorCategory.THIRD_PARTY
9407
+ },
9408
+ error
9409
+ );
8467
9410
  }
8468
- if (workflowName) {
8469
- conditions.push("workflow_name = ?");
8470
- args.push(workflowName);
9411
+ }
9412
+ async getVersionByNumber(workspaceId, versionNumber) {
9413
+ try {
9414
+ const result = await this.#client.execute({
9415
+ sql: `SELECT ${buildSelectColumns(TABLE_WORKSPACE_VERSIONS)} FROM "${TABLE_WORKSPACE_VERSIONS}" WHERE "workspaceId" = ? AND "versionNumber" = ?`,
9416
+ args: [workspaceId, versionNumber]
9417
+ });
9418
+ const row = result.rows?.[0];
9419
+ return row ? this.#parseVersionRow(row) : null;
9420
+ } catch (error) {
9421
+ if (error instanceof MastraError) throw error;
9422
+ throw new MastraError(
9423
+ {
9424
+ id: createStorageErrorId("LIBSQL", "GET_WORKSPACE_VERSION_BY_NUMBER", "FAILED"),
9425
+ domain: ErrorDomain.STORAGE,
9426
+ category: ErrorCategory.THIRD_PARTY
9427
+ },
9428
+ error
9429
+ );
8471
9430
  }
8472
- const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
9431
+ }
9432
+ async getLatestVersion(workspaceId) {
8473
9433
  try {
8474
9434
  const result = await this.#client.execute({
8475
- sql: `SELECT workflow_name, run_id, resourceId, json(snapshot) as snapshot, createdAt, updatedAt FROM ${TABLE_WORKFLOW_SNAPSHOT} ${whereClause} ORDER BY createdAt DESC LIMIT 1`,
8476
- args
9435
+ sql: `SELECT ${buildSelectColumns(TABLE_WORKSPACE_VERSIONS)} FROM "${TABLE_WORKSPACE_VERSIONS}" WHERE "workspaceId" = ? ORDER BY "versionNumber" DESC LIMIT 1`,
9436
+ args: [workspaceId]
8477
9437
  });
8478
- if (!result.rows?.[0]) {
8479
- return null;
8480
- }
8481
- return this.parseWorkflowRun(result.rows[0]);
9438
+ const row = result.rows?.[0];
9439
+ return row ? this.#parseVersionRow(row) : null;
8482
9440
  } catch (error) {
9441
+ if (error instanceof MastraError) throw error;
8483
9442
  throw new MastraError(
8484
9443
  {
8485
- id: createStorageErrorId("LIBSQL", "GET_WORKFLOW_RUN_BY_ID", "FAILED"),
9444
+ id: createStorageErrorId("LIBSQL", "GET_LATEST_WORKSPACE_VERSION", "FAILED"),
8486
9445
  domain: ErrorDomain.STORAGE,
8487
9446
  category: ErrorCategory.THIRD_PARTY
8488
9447
  },
@@ -8490,85 +9449,100 @@ var WorkflowsLibSQL = class extends WorkflowsStorage {
8490
9449
  );
8491
9450
  }
8492
9451
  }
8493
- async deleteWorkflowRunById({ runId, workflowName }) {
8494
- return this.executeWithRetry(async () => {
8495
- try {
8496
- await this.#client.execute({
8497
- sql: `DELETE FROM ${TABLE_WORKFLOW_SNAPSHOT} WHERE workflow_name = ? AND run_id = ?`,
8498
- args: [workflowName, runId]
8499
- });
8500
- } catch (error) {
8501
- throw new MastraError(
8502
- {
8503
- id: createStorageErrorId("LIBSQL", "DELETE_WORKFLOW_RUN_BY_ID", "FAILED"),
8504
- domain: ErrorDomain.STORAGE,
8505
- category: ErrorCategory.THIRD_PARTY,
8506
- details: { runId, workflowName }
8507
- },
8508
- error
8509
- );
9452
+ async listVersions(input) {
9453
+ try {
9454
+ const { workspaceId, page = 0, perPage: perPageInput, orderBy } = input;
9455
+ const { field, direction } = this.parseVersionOrderBy(orderBy);
9456
+ const countResult = await this.#client.execute({
9457
+ sql: `SELECT COUNT(*) as count FROM "${TABLE_WORKSPACE_VERSIONS}" WHERE "workspaceId" = ?`,
9458
+ args: [workspaceId]
9459
+ });
9460
+ const total = Number(countResult.rows?.[0]?.count ?? 0);
9461
+ if (total === 0) {
9462
+ return {
9463
+ versions: [],
9464
+ total: 0,
9465
+ page,
9466
+ perPage: perPageInput ?? 20,
9467
+ hasMore: false
9468
+ };
8510
9469
  }
8511
- }, "deleteWorkflowRunById");
9470
+ const perPage = normalizePerPage(perPageInput, 20);
9471
+ const { offset: start, perPage: perPageForResponse } = calculatePagination(page, perPageInput, perPage);
9472
+ const limitValue = perPageInput === false ? total : perPage;
9473
+ const end = perPageInput === false ? total : start + perPage;
9474
+ const result = await this.#client.execute({
9475
+ sql: `SELECT ${buildSelectColumns(TABLE_WORKSPACE_VERSIONS)} FROM "${TABLE_WORKSPACE_VERSIONS}" WHERE "workspaceId" = ? ORDER BY ${field} ${direction} LIMIT ? OFFSET ?`,
9476
+ args: [workspaceId, limitValue, start]
9477
+ });
9478
+ const versions = result.rows?.map((row) => this.#parseVersionRow(row)) ?? [];
9479
+ return {
9480
+ versions,
9481
+ total,
9482
+ page,
9483
+ perPage: perPageForResponse,
9484
+ hasMore: end < total
9485
+ };
9486
+ } catch (error) {
9487
+ if (error instanceof MastraError) throw error;
9488
+ throw new MastraError(
9489
+ {
9490
+ id: createStorageErrorId("LIBSQL", "LIST_WORKSPACE_VERSIONS", "FAILED"),
9491
+ domain: ErrorDomain.STORAGE,
9492
+ category: ErrorCategory.THIRD_PARTY
9493
+ },
9494
+ error
9495
+ );
9496
+ }
8512
9497
  }
8513
- async listWorkflowRuns({
8514
- workflowName,
8515
- fromDate,
8516
- toDate,
8517
- page,
8518
- perPage,
8519
- resourceId,
8520
- status
8521
- } = {}) {
9498
+ async deleteVersion(id) {
9499
+ try {
9500
+ await this.#client.execute({
9501
+ sql: `DELETE FROM "${TABLE_WORKSPACE_VERSIONS}" WHERE "id" = ?`,
9502
+ args: [id]
9503
+ });
9504
+ } catch (error) {
9505
+ if (error instanceof MastraError) throw error;
9506
+ throw new MastraError(
9507
+ {
9508
+ id: createStorageErrorId("LIBSQL", "DELETE_WORKSPACE_VERSION", "FAILED"),
9509
+ domain: ErrorDomain.STORAGE,
9510
+ category: ErrorCategory.THIRD_PARTY
9511
+ },
9512
+ error
9513
+ );
9514
+ }
9515
+ }
9516
+ async deleteVersionsByParentId(entityId) {
9517
+ try {
9518
+ await this.#client.execute({
9519
+ sql: `DELETE FROM "${TABLE_WORKSPACE_VERSIONS}" WHERE "workspaceId" = ?`,
9520
+ args: [entityId]
9521
+ });
9522
+ } catch (error) {
9523
+ if (error instanceof MastraError) throw error;
9524
+ throw new MastraError(
9525
+ {
9526
+ id: createStorageErrorId("LIBSQL", "DELETE_WORKSPACE_VERSIONS_BY_WORKSPACE", "FAILED"),
9527
+ domain: ErrorDomain.STORAGE,
9528
+ category: ErrorCategory.THIRD_PARTY
9529
+ },
9530
+ error
9531
+ );
9532
+ }
9533
+ }
9534
+ async countVersions(workspaceId) {
8522
9535
  try {
8523
- const conditions = [];
8524
- const args = [];
8525
- if (workflowName) {
8526
- conditions.push("workflow_name = ?");
8527
- args.push(workflowName);
8528
- }
8529
- if (status) {
8530
- conditions.push("json_extract(snapshot, '$.status') = ?");
8531
- args.push(status);
8532
- }
8533
- if (fromDate) {
8534
- conditions.push("createdAt >= ?");
8535
- args.push(fromDate.toISOString());
8536
- }
8537
- if (toDate) {
8538
- conditions.push("createdAt <= ?");
8539
- args.push(toDate.toISOString());
8540
- }
8541
- if (resourceId) {
8542
- const hasResourceId = await this.#db.hasColumn(TABLE_WORKFLOW_SNAPSHOT, "resourceId");
8543
- if (hasResourceId) {
8544
- conditions.push("resourceId = ?");
8545
- args.push(resourceId);
8546
- } else {
8547
- this.logger.warn(`[${TABLE_WORKFLOW_SNAPSHOT}] resourceId column not found. Skipping resourceId filter.`);
8548
- }
8549
- }
8550
- const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
8551
- let total = 0;
8552
- const usePagination = typeof perPage === "number" && typeof page === "number";
8553
- if (usePagination) {
8554
- const countResult = await this.#client.execute({
8555
- sql: `SELECT COUNT(*) as count FROM ${TABLE_WORKFLOW_SNAPSHOT} ${whereClause}`,
8556
- args
8557
- });
8558
- total = Number(countResult.rows?.[0]?.count ?? 0);
8559
- }
8560
- const normalizedPerPage = usePagination ? normalizePerPage(perPage, Number.MAX_SAFE_INTEGER) : 0;
8561
- const offset = usePagination ? page * normalizedPerPage : 0;
8562
9536
  const result = await this.#client.execute({
8563
- sql: `SELECT workflow_name, run_id, resourceId, json(snapshot) as snapshot, createdAt, updatedAt FROM ${TABLE_WORKFLOW_SNAPSHOT} ${whereClause} ORDER BY createdAt DESC${usePagination ? ` LIMIT ? OFFSET ?` : ""}`,
8564
- args: usePagination ? [...args, normalizedPerPage, offset] : args
9537
+ sql: `SELECT COUNT(*) as count FROM "${TABLE_WORKSPACE_VERSIONS}" WHERE "workspaceId" = ?`,
9538
+ args: [workspaceId]
8565
9539
  });
8566
- const runs = (result.rows || []).map((row) => this.parseWorkflowRun(row));
8567
- return { runs, total: total || runs.length };
9540
+ return Number(result.rows?.[0]?.count ?? 0);
8568
9541
  } catch (error) {
9542
+ if (error instanceof MastraError) throw error;
8569
9543
  throw new MastraError(
8570
9544
  {
8571
- id: createStorageErrorId("LIBSQL", "LIST_WORKFLOW_RUNS", "FAILED"),
9545
+ id: createStorageErrorId("LIBSQL", "COUNT_WORKSPACE_VERSIONS", "FAILED"),
8572
9546
  domain: ErrorDomain.STORAGE,
8573
9547
  category: ErrorCategory.THIRD_PARTY
8574
9548
  },
@@ -8576,6 +9550,62 @@ var WorkflowsLibSQL = class extends WorkflowsStorage {
8576
9550
  );
8577
9551
  }
8578
9552
  }
9553
+ // ==========================================================================
9554
+ // Private Helpers
9555
+ // ==========================================================================
9556
+ #parseWorkspaceRow(row) {
9557
+ const safeParseJSON = (val) => {
9558
+ if (val === null || val === void 0) return void 0;
9559
+ if (typeof val === "string") {
9560
+ try {
9561
+ return JSON.parse(val);
9562
+ } catch {
9563
+ return val;
9564
+ }
9565
+ }
9566
+ return val;
9567
+ };
9568
+ return {
9569
+ id: row.id,
9570
+ status: row.status ?? "draft",
9571
+ activeVersionId: row.activeVersionId ?? void 0,
9572
+ authorId: row.authorId ?? void 0,
9573
+ metadata: safeParseJSON(row.metadata),
9574
+ createdAt: new Date(row.createdAt),
9575
+ updatedAt: new Date(row.updatedAt)
9576
+ };
9577
+ }
9578
+ #parseVersionRow(row) {
9579
+ const safeParseJSON = (val) => {
9580
+ if (val === null || val === void 0) return void 0;
9581
+ if (typeof val === "string") {
9582
+ try {
9583
+ return JSON.parse(val);
9584
+ } catch {
9585
+ return val;
9586
+ }
9587
+ }
9588
+ return val;
9589
+ };
9590
+ return {
9591
+ id: row.id,
9592
+ workspaceId: row.workspaceId,
9593
+ versionNumber: Number(row.versionNumber),
9594
+ name: row.name,
9595
+ description: row.description ?? void 0,
9596
+ filesystem: safeParseJSON(row.filesystem),
9597
+ sandbox: safeParseJSON(row.sandbox),
9598
+ mounts: safeParseJSON(row.mounts),
9599
+ search: safeParseJSON(row.search),
9600
+ skills: safeParseJSON(row.skills),
9601
+ tools: safeParseJSON(row.tools),
9602
+ autoSync: Boolean(row.autoSync),
9603
+ operationTimeout: row.operationTimeout != null ? Number(row.operationTimeout) : void 0,
9604
+ changedFields: safeParseJSON(row.changedFields),
9605
+ changeMessage: row.changeMessage ?? void 0,
9606
+ createdAt: new Date(row.createdAt)
9607
+ };
9608
+ }
8579
9609
  };
8580
9610
 
8581
9611
  // src/storage/index.ts
@@ -8621,6 +9651,9 @@ var LibSQLStore = class extends MastraCompositeStore {
8621
9651
  const promptBlocks = new PromptBlocksLibSQL(domainConfig);
8622
9652
  const scorerDefinitions = new ScorerDefinitionsLibSQL(domainConfig);
8623
9653
  const mcpClients = new MCPClientsLibSQL(domainConfig);
9654
+ const workspaces = new WorkspacesLibSQL(domainConfig);
9655
+ const skills = new SkillsLibSQL(domainConfig);
9656
+ const blobs = new BlobsLibSQL(domainConfig);
8624
9657
  this.stores = {
8625
9658
  scores,
8626
9659
  workflows,
@@ -8631,7 +9664,10 @@ var LibSQLStore = class extends MastraCompositeStore {
8631
9664
  experiments,
8632
9665
  promptBlocks,
8633
9666
  scorerDefinitions,
8634
- mcpClients
9667
+ mcpClients,
9668
+ workspaces,
9669
+ skills,
9670
+ blobs
8635
9671
  };
8636
9672
  }
8637
9673
  };
@@ -8735,6 +9771,6 @@ Example Complex Query:
8735
9771
  ]
8736
9772
  }`;
8737
9773
 
8738
- export { AgentsLibSQL, DatasetsLibSQL, LibSQLStore as DefaultStorage, ExperimentsLibSQL, LIBSQL_PROMPT, LibSQLStore, LibSQLVector, MCPClientsLibSQL, MemoryLibSQL, ObservabilityLibSQL, PromptBlocksLibSQL, ScorerDefinitionsLibSQL, ScoresLibSQL, WorkflowsLibSQL };
9774
+ export { AgentsLibSQL, BlobsLibSQL, DatasetsLibSQL, LibSQLStore as DefaultStorage, ExperimentsLibSQL, LIBSQL_PROMPT, LibSQLStore, LibSQLVector, MCPClientsLibSQL, MemoryLibSQL, ObservabilityLibSQL, PromptBlocksLibSQL, ScorerDefinitionsLibSQL, ScoresLibSQL, SkillsLibSQL, WorkflowsLibSQL, WorkspacesLibSQL };
8739
9775
  //# sourceMappingURL=index.js.map
8740
9776
  //# sourceMappingURL=index.js.map