@mastra/libsql 1.10.1 → 1.11.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.cjs CHANGED
@@ -1211,6 +1211,15 @@ function buildSelectColumns(tableName) {
1211
1211
  return colDef?.type === "jsonb" ? `json("${parsedCol}") as "${parsedCol}"` : `"${parsedCol}"`;
1212
1212
  }).join(", ");
1213
1213
  }
1214
+ function buildSelectColumnsWithAlias(tableName, alias) {
1215
+ const parsedAlias = utils.parseSqlIdentifier(alias, "table alias");
1216
+ const schema = storage.TABLE_SCHEMAS[tableName];
1217
+ return Object.keys(schema).map((col) => {
1218
+ const colDef = schema[col];
1219
+ const parsedCol = utils.parseSqlIdentifier(col, "column name");
1220
+ return colDef?.type === "jsonb" ? `json(${parsedAlias}."${parsedCol}") as "${parsedCol}"` : `${parsedAlias}."${parsedCol}"`;
1221
+ }).join(", ");
1222
+ }
1214
1223
  function isLockError(error) {
1215
1224
  return error.code === "SQLITE_BUSY" || error.code === "SQLITE_LOCKED" || error.message?.toLowerCase().includes("database is locked") || error.message?.toLowerCase().includes("database table is locked") || error.message?.toLowerCase().includes("table is locked") || error.constructor.name === "SqliteError" && error.message?.toLowerCase().includes("locked");
1216
1225
  }
@@ -2261,12 +2270,12 @@ var AgentsLibSQL = class extends storage.AgentsStorage {
2261
2270
  await this.#db.alterTable({
2262
2271
  tableName: storage.TABLE_AGENTS,
2263
2272
  schema: storage.AGENTS_SCHEMA,
2264
- ifNotExists: ["status", "authorId"]
2273
+ ifNotExists: ["status", "authorId", "visibility", "favoriteCount"]
2265
2274
  });
2266
2275
  await this.#db.alterTable({
2267
2276
  tableName: storage.TABLE_AGENT_VERSIONS,
2268
2277
  schema: storage.AGENT_VERSIONS_SCHEMA,
2269
- ifNotExists: ["mcpClients", "requestContextSchema", "workspace", "skills", "skillsFormat"]
2278
+ ifNotExists: ["mcpClients", "requestContextSchema", "workspace", "skills", "skillsFormat", "browser"]
2270
2279
  });
2271
2280
  await this.#migrateToolsToJsonbFormat();
2272
2281
  await this.#cleanupStaleDrafts();
@@ -2357,14 +2366,23 @@ var AgentsLibSQL = class extends storage.AgentsStorage {
2357
2366
  });
2358
2367
  }
2359
2368
  /**
2360
- * Removes stale draft agent records that have no activeVersionId.
2369
+ * Removes stale draft agent records that have no versions at all.
2361
2370
  * These are left behind when createAgent partially fails (inserts thin record
2362
2371
  * but fails to create the version due to schema mismatch).
2372
+ *
2373
+ * A legitimate draft (never published) will have rows in the versions table,
2374
+ * so we must only delete records with zero associated versions.
2363
2375
  */
2364
2376
  async #cleanupStaleDrafts() {
2365
2377
  try {
2366
2378
  await this.#client.execute({
2367
- sql: `DELETE FROM "${storage.TABLE_AGENTS}" WHERE status = 'draft' AND activeVersionId IS NULL`
2379
+ sql: `DELETE FROM "${storage.TABLE_AGENTS}"
2380
+ WHERE status = 'draft'
2381
+ AND activeVersionId IS NULL
2382
+ AND NOT EXISTS (
2383
+ SELECT 1 FROM "${storage.TABLE_AGENT_VERSIONS}"
2384
+ WHERE "${storage.TABLE_AGENT_VERSIONS}".agentId = "${storage.TABLE_AGENTS}".id
2385
+ )`
2368
2386
  });
2369
2387
  } catch {
2370
2388
  }
@@ -2459,7 +2477,9 @@ var AgentsLibSQL = class extends storage.AgentsStorage {
2459
2477
  status: row.status,
2460
2478
  activeVersionId: row.activeVersionId,
2461
2479
  authorId: row.authorId,
2480
+ visibility: row.visibility ?? void 0,
2462
2481
  metadata: this.parseJson(row.metadata, "metadata"),
2482
+ favoriteCount: row.favoriteCount === null || row.favoriteCount === void 0 ? 0 : Number(row.favoriteCount),
2463
2483
  createdAt: new Date(row.createdAt),
2464
2484
  updatedAt: new Date(row.updatedAt)
2465
2485
  };
@@ -2488,6 +2508,7 @@ var AgentsLibSQL = class extends storage.AgentsStorage {
2488
2508
  const { agent } = input;
2489
2509
  try {
2490
2510
  const now = /* @__PURE__ */ new Date();
2511
+ const visibility = agent.visibility ?? (agent.authorId ? "private" : null);
2491
2512
  await this.#db.insert({
2492
2513
  tableName: storage.TABLE_AGENTS,
2493
2514
  record: {
@@ -2495,12 +2516,14 @@ var AgentsLibSQL = class extends storage.AgentsStorage {
2495
2516
  status: "draft",
2496
2517
  activeVersionId: null,
2497
2518
  authorId: agent.authorId ?? null,
2519
+ visibility,
2498
2520
  metadata: agent.metadata ?? null,
2521
+ favoriteCount: 0,
2499
2522
  createdAt: now,
2500
2523
  updatedAt: now
2501
2524
  }
2502
2525
  });
2503
- const { id: _id, authorId: _authorId, metadata: _metadata, ...snapshotConfig } = agent;
2526
+ const { id: _id, authorId: _authorId, visibility: _visibility, metadata: _metadata, ...snapshotConfig } = agent;
2504
2527
  const versionId = crypto.randomUUID();
2505
2528
  await this.createVersion({
2506
2529
  id: versionId,
@@ -2549,13 +2572,14 @@ var AgentsLibSQL = class extends storage.AgentsStorage {
2549
2572
  details: { agentId: id }
2550
2573
  });
2551
2574
  }
2552
- const { authorId, activeVersionId, metadata, status } = updates;
2575
+ const { authorId, activeVersionId, metadata, status, visibility } = updates;
2553
2576
  const updateData = {
2554
2577
  updatedAt: (/* @__PURE__ */ new Date()).toISOString()
2555
2578
  };
2556
2579
  if (authorId !== void 0) updateData.authorId = authorId;
2557
2580
  if (activeVersionId !== void 0) updateData.activeVersionId = activeVersionId;
2558
2581
  if (status !== void 0) updateData.status = status;
2582
+ if (visibility !== void 0) updateData.visibility = visibility;
2559
2583
  if (metadata !== void 0) {
2560
2584
  updateData.metadata = { ...existing.metadata, ...metadata };
2561
2585
  }
@@ -2611,7 +2635,18 @@ var AgentsLibSQL = class extends storage.AgentsStorage {
2611
2635
  }
2612
2636
  }
2613
2637
  async list(args) {
2614
- const { page = 0, perPage: perPageInput, orderBy, authorId, metadata, status } = args || {};
2638
+ const {
2639
+ page = 0,
2640
+ perPage: perPageInput,
2641
+ orderBy,
2642
+ authorId,
2643
+ metadata,
2644
+ status,
2645
+ visibility,
2646
+ entityIds,
2647
+ pinFavoritedFor,
2648
+ favoritedOnly
2649
+ } = args || {};
2615
2650
  const { field, direction } = this.parseOrderBy(orderBy);
2616
2651
  if (page < 0) {
2617
2652
  throw new error.MastraError(
@@ -2627,16 +2662,29 @@ var AgentsLibSQL = class extends storage.AgentsStorage {
2627
2662
  const perPage = storage.normalizePerPage(perPageInput, 100);
2628
2663
  const { offset, perPage: perPageForResponse } = storage.calculatePagination(page, perPageInput, perPage);
2629
2664
  try {
2665
+ if (entityIds && entityIds.length === 0) {
2666
+ return {
2667
+ agents: [],
2668
+ total: 0,
2669
+ page,
2670
+ perPage: perPageForResponse,
2671
+ hasMore: false
2672
+ };
2673
+ }
2630
2674
  const conditions = [];
2631
2675
  const queryParams = [];
2632
2676
  if (status) {
2633
- conditions.push("status = ?");
2677
+ conditions.push("a.status = ?");
2634
2678
  queryParams.push(status);
2635
2679
  }
2636
2680
  if (authorId !== void 0) {
2637
- conditions.push("authorId = ?");
2681
+ conditions.push("a.authorId = ?");
2638
2682
  queryParams.push(authorId);
2639
2683
  }
2684
+ if (visibility !== void 0) {
2685
+ conditions.push("a.visibility = ?");
2686
+ queryParams.push(visibility);
2687
+ }
2640
2688
  if (metadata && Object.keys(metadata).length > 0) {
2641
2689
  for (const [key, value] of Object.entries(metadata)) {
2642
2690
  if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(key)) {
@@ -2648,14 +2696,32 @@ var AgentsLibSQL = class extends storage.AgentsStorage {
2648
2696
  details: { key }
2649
2697
  });
2650
2698
  }
2651
- conditions.push(`json_extract(metadata, '$.${key}') = ?`);
2699
+ conditions.push(`json_extract(a.metadata, '$.${key}') = ?`);
2652
2700
  queryParams.push(typeof value === "string" ? value : JSON.stringify(value));
2653
2701
  }
2654
2702
  }
2703
+ if (entityIds && entityIds.length > 0) {
2704
+ const placeholders = entityIds.map(() => "?").join(", ");
2705
+ conditions.push(`a.id IN (${placeholders})`);
2706
+ queryParams.push(...entityIds);
2707
+ }
2708
+ const joinUserId = pinFavoritedFor;
2709
+ const useJoin = Boolean(joinUserId);
2710
+ let joinClause = "";
2711
+ const joinParams = [];
2712
+ if (useJoin && joinUserId) {
2713
+ joinClause = `LEFT JOIN "${storage.TABLE_FAVORITES}" s ON s."entityType" = 'agent' AND s."entityId" = a.id AND s."userId" = ?`;
2714
+ joinParams.push(joinUserId);
2715
+ if (favoritedOnly) {
2716
+ conditions.push('s."userId" IS NOT NULL');
2717
+ }
2718
+ } else if (favoritedOnly) {
2719
+ conditions.push("1=0");
2720
+ }
2655
2721
  const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
2656
2722
  const countResult = await this.#client.execute({
2657
- sql: `SELECT COUNT(*) as count FROM "${storage.TABLE_AGENTS}" ${whereClause}`,
2658
- args: queryParams
2723
+ sql: `SELECT COUNT(*) as count FROM "${storage.TABLE_AGENTS}" a ${joinClause} ${whereClause}`,
2724
+ args: [...joinParams, ...queryParams]
2659
2725
  });
2660
2726
  const total = Number(countResult.rows?.[0]?.count ?? 0);
2661
2727
  if (total === 0) {
@@ -2667,10 +2733,18 @@ var AgentsLibSQL = class extends storage.AgentsStorage {
2667
2733
  hasMore: false
2668
2734
  };
2669
2735
  }
2736
+ const orderByParts = [];
2737
+ if (useJoin && joinUserId) {
2738
+ orderByParts.push(`(s."userId" IS NOT NULL) DESC`);
2739
+ }
2740
+ orderByParts.push(`a."${field}" ${direction}`);
2741
+ orderByParts.push(`a."id" ASC`);
2742
+ const orderByClause = `ORDER BY ${orderByParts.join(", ")}`;
2670
2743
  const limitValue = perPageInput === false ? total : perPage;
2744
+ const selectCols = buildSelectColumnsWithAlias(storage.TABLE_AGENTS, "a");
2671
2745
  const result = await this.#client.execute({
2672
- sql: `SELECT ${buildSelectColumns(storage.TABLE_AGENTS)} FROM "${storage.TABLE_AGENTS}" ${whereClause} ORDER BY "${field}" ${direction} LIMIT ? OFFSET ?`,
2673
- args: [...queryParams, limitValue, offset]
2746
+ sql: `SELECT ${selectCols} FROM "${storage.TABLE_AGENTS}" a ${joinClause} ${whereClause} ${orderByClause} LIMIT ? OFFSET ?`,
2747
+ args: [...joinParams, ...queryParams, limitValue, offset]
2674
2748
  });
2675
2749
  const rows = result.rows ?? [];
2676
2750
  const agents = rows.map((row) => this.parseRow(row));
@@ -2723,6 +2797,7 @@ var AgentsLibSQL = class extends storage.AgentsStorage {
2723
2797
  workspace: input.workspace ?? null,
2724
2798
  skills: input.skills ?? null,
2725
2799
  skillsFormat: input.skillsFormat ?? null,
2800
+ browser: input.browser ?? null,
2726
2801
  changedFields: input.changedFields ?? null,
2727
2802
  changeMessage: input.changeMessage ?? null,
2728
2803
  createdAt: now
@@ -2996,6 +3071,7 @@ var AgentsLibSQL = class extends storage.AgentsStorage {
2996
3071
  workspace: this.parseJson(row.workspace, "workspace"),
2997
3072
  skills: this.parseJson(row.skills, "skills"),
2998
3073
  skillsFormat: row.skillsFormat,
3074
+ browser: this.parseJson(row.browser, "browser"),
2999
3075
  changedFields: this.parseJson(row.changedFields, "changedFields"),
3000
3076
  changeMessage: row.changeMessage,
3001
3077
  createdAt: new Date(row.createdAt)
@@ -4977,6 +5053,255 @@ var ExperimentsLibSQL = class extends storage.ExperimentsStorage {
4977
5053
  }
4978
5054
  }
4979
5055
  };
5056
+ var ENTITY_TABLE = {
5057
+ agent: storage.TABLE_AGENTS,
5058
+ skill: storage.TABLE_SKILLS
5059
+ };
5060
+ var FavoritesLibSQL = class extends storage.FavoritesStorage {
5061
+ #db;
5062
+ #client;
5063
+ constructor(config) {
5064
+ super();
5065
+ const client = resolveClient(config);
5066
+ this.#client = client;
5067
+ this.#db = new LibSQLDB({ client, maxRetries: config.maxRetries, initialBackoffMs: config.initialBackoffMs });
5068
+ }
5069
+ async init() {
5070
+ await this.#db.createTable({
5071
+ tableName: storage.TABLE_FAVORITES,
5072
+ schema: storage.FAVORITES_SCHEMA,
5073
+ compositePrimaryKey: ["userId", "entityType", "entityId"]
5074
+ });
5075
+ await this.#client.execute(
5076
+ `CREATE INDEX IF NOT EXISTS idx_favorites_entity ON "${storage.TABLE_FAVORITES}" ("entityType", "entityId")`
5077
+ );
5078
+ }
5079
+ async dangerouslyClearAll() {
5080
+ const tx = await this.#client.transaction("write");
5081
+ try {
5082
+ await tx.execute(`DELETE FROM "${storage.TABLE_FAVORITES}"`);
5083
+ await tx.execute(`UPDATE "${storage.TABLE_AGENTS}" SET "favoriteCount" = 0 WHERE "favoriteCount" > 0`);
5084
+ await tx.execute(`UPDATE "${storage.TABLE_SKILLS}" SET "favoriteCount" = 0 WHERE "favoriteCount" > 0`);
5085
+ await tx.commit();
5086
+ } catch (error) {
5087
+ if (!tx.closed) {
5088
+ await tx.rollback();
5089
+ }
5090
+ throw error;
5091
+ }
5092
+ }
5093
+ async favorite(input) {
5094
+ const { userId, entityType, entityId } = input;
5095
+ const entityTable = ENTITY_TABLE[entityType];
5096
+ try {
5097
+ const tx = await this.#client.transaction("write");
5098
+ try {
5099
+ const entityRow = await tx.execute({
5100
+ sql: `SELECT "favoriteCount" FROM "${entityTable}" WHERE id = ?`,
5101
+ args: [entityId]
5102
+ });
5103
+ if (!entityRow.rows?.[0]) {
5104
+ throw new error.MastraError({
5105
+ id: storage.createStorageErrorId("LIBSQL", "FAVORITE", "ENTITY_NOT_FOUND"),
5106
+ domain: error.ErrorDomain.STORAGE,
5107
+ category: error.ErrorCategory.USER,
5108
+ text: `${entityType} ${entityId} not found`,
5109
+ details: { entityType, entityId }
5110
+ });
5111
+ }
5112
+ const inserted = await tx.execute({
5113
+ sql: `INSERT OR IGNORE INTO "${storage.TABLE_FAVORITES}" ("userId", "entityType", "entityId", "createdAt") VALUES (?, ?, ?, ?)`,
5114
+ args: [userId, entityType, entityId, (/* @__PURE__ */ new Date()).toISOString()]
5115
+ });
5116
+ if ((inserted.rowsAffected ?? 0) > 0) {
5117
+ await tx.execute({
5118
+ sql: `UPDATE "${entityTable}" SET "favoriteCount" = COALESCE("favoriteCount", 0) + 1 WHERE id = ?`,
5119
+ args: [entityId]
5120
+ });
5121
+ }
5122
+ const after = await tx.execute({
5123
+ sql: `SELECT "favoriteCount" FROM "${entityTable}" WHERE id = ?`,
5124
+ args: [entityId]
5125
+ });
5126
+ const favoriteCount = Number(after.rows?.[0]?.favoriteCount ?? 0);
5127
+ await tx.commit();
5128
+ return { favorited: true, favoriteCount };
5129
+ } catch (error) {
5130
+ if (!tx.closed) {
5131
+ await tx.rollback();
5132
+ }
5133
+ throw error;
5134
+ }
5135
+ } catch (error$1) {
5136
+ if (error$1 instanceof error.MastraError) throw error$1;
5137
+ throw new error.MastraError(
5138
+ {
5139
+ id: storage.createStorageErrorId("LIBSQL", "FAVORITE", "FAILED"),
5140
+ domain: error.ErrorDomain.STORAGE,
5141
+ category: error.ErrorCategory.THIRD_PARTY,
5142
+ details: { entityType, entityId }
5143
+ },
5144
+ error$1
5145
+ );
5146
+ }
5147
+ }
5148
+ async unfavorite(input) {
5149
+ const { userId, entityType, entityId } = input;
5150
+ const entityTable = ENTITY_TABLE[entityType];
5151
+ try {
5152
+ const tx = await this.#client.transaction("write");
5153
+ try {
5154
+ const entityRow = await tx.execute({
5155
+ sql: `SELECT "favoriteCount" FROM "${entityTable}" WHERE id = ?`,
5156
+ args: [entityId]
5157
+ });
5158
+ if (!entityRow.rows?.[0]) {
5159
+ throw new error.MastraError({
5160
+ id: storage.createStorageErrorId("LIBSQL", "UNFAVORITE", "ENTITY_NOT_FOUND"),
5161
+ domain: error.ErrorDomain.STORAGE,
5162
+ category: error.ErrorCategory.USER,
5163
+ text: `${entityType} ${entityId} not found`,
5164
+ details: { entityType, entityId }
5165
+ });
5166
+ }
5167
+ const deleted = await tx.execute({
5168
+ sql: `DELETE FROM "${storage.TABLE_FAVORITES}" WHERE "userId" = ? AND "entityType" = ? AND "entityId" = ?`,
5169
+ args: [userId, entityType, entityId]
5170
+ });
5171
+ if ((deleted.rowsAffected ?? 0) > 0) {
5172
+ await tx.execute({
5173
+ sql: `UPDATE "${entityTable}" SET "favoriteCount" = MAX(COALESCE("favoriteCount", 0) - 1, 0) WHERE id = ?`,
5174
+ args: [entityId]
5175
+ });
5176
+ }
5177
+ const after = await tx.execute({
5178
+ sql: `SELECT "favoriteCount" FROM "${entityTable}" WHERE id = ?`,
5179
+ args: [entityId]
5180
+ });
5181
+ const favoriteCount = Number(after.rows?.[0]?.favoriteCount ?? 0);
5182
+ await tx.commit();
5183
+ return { favorited: false, favoriteCount };
5184
+ } catch (error) {
5185
+ if (!tx.closed) {
5186
+ await tx.rollback();
5187
+ }
5188
+ throw error;
5189
+ }
5190
+ } catch (error$1) {
5191
+ if (error$1 instanceof error.MastraError) throw error$1;
5192
+ throw new error.MastraError(
5193
+ {
5194
+ id: storage.createStorageErrorId("LIBSQL", "UNFAVORITE", "FAILED"),
5195
+ domain: error.ErrorDomain.STORAGE,
5196
+ category: error.ErrorCategory.THIRD_PARTY,
5197
+ details: { entityType, entityId }
5198
+ },
5199
+ error$1
5200
+ );
5201
+ }
5202
+ }
5203
+ async isFavorited(input) {
5204
+ try {
5205
+ const result = await this.#client.execute({
5206
+ sql: `SELECT 1 FROM "${storage.TABLE_FAVORITES}" WHERE "userId" = ? AND "entityType" = ? AND "entityId" = ? LIMIT 1`,
5207
+ args: [input.userId, input.entityType, input.entityId]
5208
+ });
5209
+ return (result.rows?.length ?? 0) > 0;
5210
+ } catch (error$1) {
5211
+ if (error$1 instanceof error.MastraError) throw error$1;
5212
+ throw new error.MastraError(
5213
+ {
5214
+ id: storage.createStorageErrorId("LIBSQL", "IS_FAVORITED", "FAILED"),
5215
+ domain: error.ErrorDomain.STORAGE,
5216
+ category: error.ErrorCategory.THIRD_PARTY
5217
+ },
5218
+ error$1
5219
+ );
5220
+ }
5221
+ }
5222
+ async isFavoritedBatch(input) {
5223
+ const { userId, entityType, entityIds } = input;
5224
+ if (entityIds.length === 0) {
5225
+ return /* @__PURE__ */ new Set();
5226
+ }
5227
+ try {
5228
+ const placeholders = entityIds.map(() => "?").join(", ");
5229
+ const args = [userId, entityType, ...entityIds];
5230
+ const result = await this.#client.execute({
5231
+ sql: `SELECT "entityId" FROM "${storage.TABLE_FAVORITES}" WHERE "userId" = ? AND "entityType" = ? AND "entityId" IN (${placeholders})`,
5232
+ args
5233
+ });
5234
+ const set = /* @__PURE__ */ new Set();
5235
+ for (const row of result.rows ?? []) {
5236
+ set.add(row.entityId);
5237
+ }
5238
+ return set;
5239
+ } catch (error$1) {
5240
+ if (error$1 instanceof error.MastraError) throw error$1;
5241
+ throw new error.MastraError(
5242
+ {
5243
+ id: storage.createStorageErrorId("LIBSQL", "IS_FAVORITED_BATCH", "FAILED"),
5244
+ domain: error.ErrorDomain.STORAGE,
5245
+ category: error.ErrorCategory.THIRD_PARTY
5246
+ },
5247
+ error$1
5248
+ );
5249
+ }
5250
+ }
5251
+ async listFavoritedIds(input) {
5252
+ try {
5253
+ const result = await this.#client.execute({
5254
+ sql: `SELECT "entityId" FROM "${storage.TABLE_FAVORITES}" WHERE "userId" = ? AND "entityType" = ? ORDER BY "createdAt" DESC, "entityId" ASC`,
5255
+ args: [input.userId, input.entityType]
5256
+ });
5257
+ return (result.rows ?? []).map((row) => row.entityId);
5258
+ } catch (error$1) {
5259
+ if (error$1 instanceof error.MastraError) throw error$1;
5260
+ throw new error.MastraError(
5261
+ {
5262
+ id: storage.createStorageErrorId("LIBSQL", "LIST_FAVORITED_IDS", "FAILED"),
5263
+ domain: error.ErrorDomain.STORAGE,
5264
+ category: error.ErrorCategory.THIRD_PARTY
5265
+ },
5266
+ error$1
5267
+ );
5268
+ }
5269
+ }
5270
+ async deleteFavoritesForEntity(input) {
5271
+ const entityTable = ENTITY_TABLE[input.entityType];
5272
+ try {
5273
+ const tx = await this.#client.transaction("write");
5274
+ try {
5275
+ const result = await tx.execute({
5276
+ sql: `DELETE FROM "${storage.TABLE_FAVORITES}" WHERE "entityType" = ? AND "entityId" = ?`,
5277
+ args: [input.entityType, input.entityId]
5278
+ });
5279
+ await tx.execute({
5280
+ sql: `UPDATE "${entityTable}" SET "favoriteCount" = 0 WHERE id = ?`,
5281
+ args: [input.entityId]
5282
+ });
5283
+ await tx.commit();
5284
+ return Number(result.rowsAffected ?? 0);
5285
+ } catch (txError) {
5286
+ if (!tx.closed) {
5287
+ await tx.rollback();
5288
+ }
5289
+ throw txError;
5290
+ }
5291
+ } catch (error$1) {
5292
+ if (error$1 instanceof error.MastraError) throw error$1;
5293
+ throw new error.MastraError(
5294
+ {
5295
+ id: storage.createStorageErrorId("LIBSQL", "DELETE_FAVORITES_FOR_ENTITY", "FAILED"),
5296
+ domain: error.ErrorDomain.STORAGE,
5297
+ category: error.ErrorCategory.THIRD_PARTY,
5298
+ details: { entityType: input.entityType, entityId: input.entityId }
5299
+ },
5300
+ error$1
5301
+ );
5302
+ }
5303
+ }
5304
+ };
4980
5305
  var MCPClientsLibSQL = class extends storage.MCPClientsStorage {
4981
5306
  #db;
4982
5307
  #client;
@@ -10048,6 +10373,7 @@ var SNAPSHOT_FIELDS = [
10048
10373
  "references",
10049
10374
  "scripts",
10050
10375
  "assets",
10376
+ "files",
10051
10377
  "metadata",
10052
10378
  "tree"
10053
10379
  ];
@@ -10066,6 +10392,16 @@ var SkillsLibSQL = class extends storage.SkillsStorage {
10066
10392
  tableName: storage.TABLE_SKILL_VERSIONS,
10067
10393
  schema: storage.SKILL_VERSIONS_SCHEMA
10068
10394
  });
10395
+ await this.#db.alterTable({
10396
+ tableName: storage.TABLE_SKILLS,
10397
+ schema: storage.SKILLS_SCHEMA,
10398
+ ifNotExists: ["visibility", "favoriteCount"]
10399
+ });
10400
+ await this.#db.alterTable({
10401
+ tableName: storage.TABLE_SKILL_VERSIONS,
10402
+ schema: storage.SKILL_VERSIONS_SCHEMA,
10403
+ ifNotExists: ["files"]
10404
+ });
10069
10405
  await this.#client.execute(
10070
10406
  `CREATE UNIQUE INDEX IF NOT EXISTS idx_skill_versions_skill_version ON "${storage.TABLE_SKILL_VERSIONS}" ("skillId", "versionNumber")`
10071
10407
  );
@@ -10101,6 +10437,7 @@ var SkillsLibSQL = class extends storage.SkillsStorage {
10101
10437
  const { skill } = input;
10102
10438
  try {
10103
10439
  const now = /* @__PURE__ */ new Date();
10440
+ const visibility = skill.visibility ?? (skill.authorId ? "private" : void 0);
10104
10441
  await this.#db.insert({
10105
10442
  tableName: storage.TABLE_SKILLS,
10106
10443
  record: {
@@ -10108,11 +10445,13 @@ var SkillsLibSQL = class extends storage.SkillsStorage {
10108
10445
  status: "draft",
10109
10446
  activeVersionId: null,
10110
10447
  authorId: skill.authorId ?? null,
10448
+ visibility: visibility ?? null,
10449
+ favoriteCount: 0,
10111
10450
  createdAt: now.toISOString(),
10112
10451
  updatedAt: now.toISOString()
10113
10452
  }
10114
10453
  });
10115
- const { id: _id, authorId: _authorId, ...snapshotConfig } = skill;
10454
+ const { id: _id, authorId: _authorId, visibility: _visibility, ...snapshotConfig } = skill;
10116
10455
  const versionId = crypto.randomUUID();
10117
10456
  try {
10118
10457
  await this.createVersion({
@@ -10160,13 +10499,18 @@ var SkillsLibSQL = class extends storage.SkillsStorage {
10160
10499
  details: { skillId: id }
10161
10500
  });
10162
10501
  }
10163
- const { authorId, activeVersionId, status, ...configFields } = updates;
10502
+ const { authorId, visibility, activeVersionId, status, ...rawConfigFields } = updates;
10503
+ const configFields = {};
10504
+ for (const [key, value] of Object.entries(rawConfigFields)) {
10505
+ if (value !== void 0) configFields[key] = value;
10506
+ }
10164
10507
  const configFieldNames = SNAPSHOT_FIELDS;
10165
10508
  const hasConfigUpdate = configFieldNames.some((field) => field in configFields);
10166
10509
  const updateData = {
10167
10510
  updatedAt: (/* @__PURE__ */ new Date()).toISOString()
10168
10511
  };
10169
10512
  if (authorId !== void 0) updateData.authorId = authorId;
10513
+ if (visibility !== void 0) updateData.visibility = visibility;
10170
10514
  if (activeVersionId !== void 0) {
10171
10515
  updateData.activeVersionId = activeVersionId;
10172
10516
  if (status === void 0) {
@@ -10259,18 +10603,63 @@ var SkillsLibSQL = class extends storage.SkillsStorage {
10259
10603
  }
10260
10604
  async list(args) {
10261
10605
  try {
10262
- const { page = 0, perPage: perPageInput, orderBy, authorId } = args || {};
10606
+ const {
10607
+ page = 0,
10608
+ perPage: perPageInput,
10609
+ orderBy,
10610
+ authorId,
10611
+ visibility,
10612
+ status,
10613
+ entityIds,
10614
+ pinFavoritedFor,
10615
+ favoritedOnly
10616
+ } = args || {};
10263
10617
  const { field, direction } = this.parseOrderBy(orderBy);
10618
+ if (entityIds && entityIds.length === 0) {
10619
+ return {
10620
+ skills: [],
10621
+ total: 0,
10622
+ page,
10623
+ perPage: perPageInput ?? 100,
10624
+ hasMore: false
10625
+ };
10626
+ }
10264
10627
  const conditions = [];
10265
10628
  const queryParams = [];
10266
10629
  if (authorId !== void 0) {
10267
- conditions.push("authorId = ?");
10630
+ conditions.push("s_e.authorId = ?");
10268
10631
  queryParams.push(authorId);
10269
10632
  }
10633
+ if (visibility !== void 0) {
10634
+ conditions.push("s_e.visibility = ?");
10635
+ queryParams.push(visibility);
10636
+ }
10637
+ if (status !== void 0) {
10638
+ conditions.push("s_e.status = ?");
10639
+ queryParams.push(status);
10640
+ }
10641
+ if (entityIds && entityIds.length > 0) {
10642
+ const placeholders = entityIds.map(() => "?").join(", ");
10643
+ conditions.push(`s_e.id IN (${placeholders})`);
10644
+ queryParams.push(...entityIds);
10645
+ }
10646
+ const joinUserId = pinFavoritedFor;
10647
+ const useJoin = Boolean(joinUserId);
10648
+ let joinClause = "";
10649
+ const joinParams = [];
10650
+ if (useJoin && joinUserId) {
10651
+ joinClause = `LEFT JOIN "${storage.TABLE_FAVORITES}" st ON st."entityType" = 'skill' AND st."entityId" = s_e.id AND st."userId" = ?`;
10652
+ joinParams.push(joinUserId);
10653
+ if (favoritedOnly) {
10654
+ conditions.push('st."userId" IS NOT NULL');
10655
+ }
10656
+ } else if (favoritedOnly) {
10657
+ conditions.push("1=0");
10658
+ }
10270
10659
  const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
10271
10660
  const countResult = await this.#client.execute({
10272
- sql: `SELECT COUNT(*) as count FROM "${storage.TABLE_SKILLS}" ${whereClause}`,
10273
- args: queryParams
10661
+ sql: `SELECT COUNT(*) as count FROM "${storage.TABLE_SKILLS}" s_e ${joinClause} ${whereClause}`,
10662
+ args: [...joinParams, ...queryParams]
10274
10663
  });
10275
10664
  const total = Number(countResult.rows?.[0]?.count ?? 0);
10276
10665
  if (total === 0) {
@@ -10286,9 +10675,17 @@ var SkillsLibSQL = class extends storage.SkillsStorage {
10286
10675
  const { offset: start, perPage: perPageForResponse } = storage.calculatePagination(page, perPageInput, perPage);
10287
10676
  const limitValue = perPageInput === false ? total : perPage;
10288
10677
  const end = perPageInput === false ? total : start + perPage;
10678
+ const orderByParts = [];
10679
+ if (useJoin && joinUserId) {
10680
+ orderByParts.push(`(st."userId" IS NOT NULL) DESC`);
10681
+ }
10682
+ orderByParts.push(`s_e."${field}" ${direction}`);
10683
+ orderByParts.push(`s_e."id" ASC`);
10684
+ const orderByClause = `ORDER BY ${orderByParts.join(", ")}`;
10685
+ const selectCols = buildSelectColumnsWithAlias(storage.TABLE_SKILLS, "s_e");
10289
10686
  const result = await this.#client.execute({
10290
- sql: `SELECT ${buildSelectColumns(storage.TABLE_SKILLS)} FROM "${storage.TABLE_SKILLS}" ${whereClause} ORDER BY ${field} ${direction} LIMIT ? OFFSET ?`,
10291
- args: [...queryParams, limitValue, start]
10687
+ sql: `SELECT ${selectCols} FROM "${storage.TABLE_SKILLS}" s_e ${joinClause} ${whereClause} ${orderByClause} LIMIT ? OFFSET ?`,
10688
+ args: [...joinParams, ...queryParams, limitValue, start]
10292
10689
  });
10293
10690
  const skills = result.rows?.map((row) => this.#parseSkillRow(row)) ?? [];
10294
10691
  return {
@@ -10320,9 +10717,9 @@ var SkillsLibSQL = class extends storage.SkillsStorage {
10320
10717
  sql: `INSERT INTO "${storage.TABLE_SKILL_VERSIONS}" (
10321
10718
  id, "skillId", "versionNumber",
10322
10719
  name, description, instructions, license, compatibility,
10323
- source, "references", scripts, assets, metadata, tree,
10720
+ source, "references", scripts, assets, files, metadata, tree,
10324
10721
  "changedFields", "changeMessage", "createdAt"
10325
- ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
10722
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
10326
10723
  args: [
10327
10724
  input.id,
10328
10725
  input.skillId,
@@ -10336,6 +10733,7 @@ var SkillsLibSQL = class extends storage.SkillsStorage {
10336
10733
  input.references ? JSON.stringify(input.references) : null,
10337
10734
  input.scripts ? JSON.stringify(input.scripts) : null,
10338
10735
  input.assets ? JSON.stringify(input.assets) : null,
10736
+ input.files ? JSON.stringify(input.files) : null,
10339
10737
  input.metadata ? JSON.stringify(input.metadata) : null,
10340
10738
  input.tree ? JSON.stringify(input.tree) : null,
10341
10739
  input.changedFields ? JSON.stringify(input.changedFields) : null,
@@ -10529,6 +10927,8 @@ var SkillsLibSQL = class extends storage.SkillsStorage {
10529
10927
  status: row.status ?? "draft",
10530
10928
  activeVersionId: row.activeVersionId ?? void 0,
10531
10929
  authorId: row.authorId ?? void 0,
10930
+ visibility: row.visibility ?? void 0,
10931
+ favoriteCount: row.favoriteCount === null || row.favoriteCount === void 0 ? 0 : Number(row.favoriteCount),
10532
10932
  createdAt: new Date(row.createdAt),
10533
10933
  updatedAt: new Date(row.updatedAt)
10534
10934
  };
@@ -10558,6 +10958,7 @@ var SkillsLibSQL = class extends storage.SkillsStorage {
10558
10958
  references: safeParseJSON(row.references),
10559
10959
  scripts: safeParseJSON(row.scripts),
10560
10960
  assets: safeParseJSON(row.assets),
10961
+ files: safeParseJSON(row.files),
10561
10962
  metadata: safeParseJSON(row.metadata),
10562
10963
  tree: safeParseJSON(row.tree),
10563
10964
  changedFields: safeParseJSON(row.changedFields),
@@ -11009,7 +11410,11 @@ var WorkspacesLibSQL = class extends storage.WorkspacesStorage {
11009
11410
  details: { workspaceId: id }
11010
11411
  });
11011
11412
  }
11012
- const { authorId, activeVersionId, metadata, status, ...configFields } = updates;
11413
+ const { authorId, activeVersionId, metadata, status, ...rawConfigFields } = updates;
11414
+ const configFields = {};
11415
+ for (const [key, value] of Object.entries(rawConfigFields)) {
11416
+ if (value !== void 0) configFields[key] = value;
11417
+ }
11013
11418
  const configFieldNames = SNAPSHOT_FIELDS2;
11014
11419
  const hasConfigUpdate = configFieldNames.some((field) => field in configFields);
11015
11420
  const updateData = {
@@ -11494,6 +11899,7 @@ var LibSQLStore = class extends storage.MastraCompositeStore {
11494
11899
  const mcpServers = new MCPServersLibSQL(domainConfig);
11495
11900
  const workspaces = new WorkspacesLibSQL(domainConfig);
11496
11901
  const skills = new SkillsLibSQL(domainConfig);
11902
+ const favorites = new FavoritesLibSQL(domainConfig);
11497
11903
  const blobs = new BlobsLibSQL(domainConfig);
11498
11904
  const backgroundTasks = new BackgroundTasksLibSQL(domainConfig);
11499
11905
  const schedules = new SchedulesLibSQL(domainConfig);
@@ -11512,6 +11918,7 @@ var LibSQLStore = class extends storage.MastraCompositeStore {
11512
11918
  mcpServers,
11513
11919
  workspaces,
11514
11920
  skills,
11921
+ favorites,
11515
11922
  blobs,
11516
11923
  backgroundTasks,
11517
11924
  schedules
@@ -11682,6 +12089,7 @@ exports.ChannelsLibSQL = ChannelsLibSQL;
11682
12089
  exports.DatasetsLibSQL = DatasetsLibSQL;
11683
12090
  exports.DefaultStorage = LibSQLStore;
11684
12091
  exports.ExperimentsLibSQL = ExperimentsLibSQL;
12092
+ exports.FavoritesLibSQL = FavoritesLibSQL;
11685
12093
  exports.LIBSQL_PROMPT = LIBSQL_PROMPT;
11686
12094
  exports.LibSQLStore = LibSQLStore;
11687
12095
  exports.LibSQLVector = LibSQLVector;