@mastra/libsql 1.17.1-alpha.0 → 1.17.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,25 @@
1
1
  # @mastra/libsql
2
2
 
3
+ ## 1.17.1
4
+
5
+ ### Patch Changes
6
+
7
+ - Fixed data loss and corruption in observational memory when it is written to concurrently on a local SQLite database. Buffered observations could previously be dropped or leave the memory record in an inconsistent state under load; memory updates are now serialized so concurrent writes are preserved. ([#20110](https://github.com/mastra-ai/mastra/pull/20110))
8
+
9
+ - Fixed local LibSQL stores changing shared database journal state when they close. ([#20227](https://github.com/mastra-ai/mastra/pull/20227))
10
+
11
+ - Updated dependencies [[`c8d8a01`](https://github.com/mastra-ai/mastra/commit/c8d8a010ee2efe2b7bf4d07707382c34c87b14e4), [`df6a9ce`](https://github.com/mastra-ai/mastra/commit/df6a9ce87214f7aadb2edfe62f67605fe998a0a4), [`73839cb`](https://github.com/mastra-ai/mastra/commit/73839cb58322679c170627d1015669ede5f619aa), [`371cf60`](https://github.com/mastra-ai/mastra/commit/371cf6075cef88ac6919a08d59a82e485397364a), [`8e4dc79`](https://github.com/mastra-ai/mastra/commit/8e4dc793dcf035ea506f9ce79f56d2d501a4be14), [`2db93cc`](https://github.com/mastra-ai/mastra/commit/2db93ccd0b872e4de7853a93383efe0647901df8), [`094ab61`](https://github.com/mastra-ai/mastra/commit/094ab6129a1a3ecf6eeb86decac17d5faea4e02a), [`fe80944`](https://github.com/mastra-ai/mastra/commit/fe80944f3ef6681fea6eae8200fce387b7bb3c2f), [`263d2ca`](https://github.com/mastra-ai/mastra/commit/263d2cac80ba3b03b9c0f008db6f1f1b9eb0278c), [`75f843d`](https://github.com/mastra-ai/mastra/commit/75f843d09f758223e6eeb321321bdcc5c7e779d0), [`e51e166`](https://github.com/mastra-ai/mastra/commit/e51e166c52e220abc9b64554ce37359dca8544b1)]:
12
+ - @mastra/core@1.53.0
13
+
14
+ ## 1.17.1-alpha.1
15
+
16
+ ### Patch Changes
17
+
18
+ - Fixed local LibSQL stores changing shared database journal state when they close. ([#20227](https://github.com/mastra-ai/mastra/pull/20227))
19
+
20
+ - Updated dependencies [[`73839cb`](https://github.com/mastra-ai/mastra/commit/73839cb58322679c170627d1015669ede5f619aa), [`8e4dc79`](https://github.com/mastra-ai/mastra/commit/8e4dc793dcf035ea506f9ce79f56d2d501a4be14), [`2db93cc`](https://github.com/mastra-ai/mastra/commit/2db93ccd0b872e4de7853a93383efe0647901df8), [`094ab61`](https://github.com/mastra-ai/mastra/commit/094ab6129a1a3ecf6eeb86decac17d5faea4e02a), [`fe80944`](https://github.com/mastra-ai/mastra/commit/fe80944f3ef6681fea6eae8200fce387b7bb3c2f), [`e51e166`](https://github.com/mastra-ai/mastra/commit/e51e166c52e220abc9b64554ce37359dca8544b1)]:
21
+ - @mastra/core@1.53.0-alpha.4
22
+
3
23
  ## 1.17.1-alpha.0
4
24
 
5
25
  ### Patch Changes
@@ -3,7 +3,7 @@ name: mastra-libsql
3
3
  description: Documentation for @mastra/libsql. Use when working with @mastra/libsql APIs, configuration, or implementation.
4
4
  metadata:
5
5
  package: "@mastra/libsql"
6
- version: "1.17.1-alpha.0"
6
+ version: "1.17.1"
7
7
  ---
8
8
 
9
9
  ## When to use
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "1.17.1-alpha.0",
2
+ "version": "1.17.1",
3
3
  "package": "@mastra/libsql",
4
4
  "exports": {},
5
5
  "modules": {}
@@ -124,7 +124,7 @@ You can use this history in two ways:
124
124
 
125
125
  ## Thread title generation
126
126
 
127
- Mastra can automatically generate descriptive thread titles based on the user's first message when `generateTitle` is enabled. Use this option when you build a chat interface that renders conversation titles in a thread list or sidebar.
127
+ Mastra can automatically generate descriptive thread titles from the conversation transcript when `generateTitle` is enabled. Use this option when you build a chat interface that renders conversation titles in a thread list or sidebar.
128
128
 
129
129
  ```typescript
130
130
  import { Agent } from '@mastra/core/agent'
@@ -263,6 +263,24 @@ const { messages } = await memory.recall({
263
263
  })
264
264
  ```
265
265
 
266
+ Filter by shallow message metadata:
267
+
268
+ ```typescript
269
+ const { messages } = await memory.recall({
270
+ threadId: 'thread-123',
271
+ filter: {
272
+ metadata: {
273
+ category: 'billing',
274
+ escalated: true,
275
+ priority: 2,
276
+ archivedAt: null,
277
+ },
278
+ },
279
+ })
280
+ ```
281
+
282
+ Metadata filters match shallow scalar values only: `string`, finite `number`, `boolean`, and `null`. All specified metadata keys must match with AND semantics, and `null` matches an explicit `null` value, not a missing metadata key. Metadata keys must start with a letter or underscore, may contain only alphanumeric characters and underscores, must be 128 characters or fewer, and can't use reserved prototype keys such as `__proto__`, `constructor`, or `prototype`. Performance depends on the storage backend. Some backends can push parts of the filter into the database, while others scan candidate messages after thread, resource, and date constraints are applied but before pagination.
283
+
266
284
  Fetch a single message by ID:
267
285
 
268
286
  ```typescript
@@ -47,7 +47,7 @@ export const agent = new Agent({
47
47
 
48
48
  **options.observationalMemory** (`boolean | ObservationalMemoryOptions`): Enable Observational Memory for long-context agentic memory. Set to true for defaults, or pass a config object to customize token budgets, models, and scope. See Observational Memory reference for configuration details.
49
49
 
50
- **options.generateTitle** (`boolean | { model: DynamicArgument<MastraLanguageModel>; instructions?: DynamicArgument<string> }`): Controls automatic thread title generation from the user's first message. Can be a boolean or an object with custom model and instructions.
50
+ **options.generateTitle** (`boolean | { model: DynamicArgument<MastraLanguageModel>; instructions?: DynamicArgument<string> }`): Controls automatic thread title generation from the conversation transcript. Can be a boolean or an object with custom model and instructions.
51
51
 
52
52
  ## Returns
53
53
 
package/dist/index.cjs CHANGED
@@ -546,29 +546,14 @@ var LibSQLVector = class extends vector.MastraVector {
546
546
  this.vectorIndexes = this.isMemoryDb ? Promise.resolve(/* @__PURE__ */ new Set()) : this.discoverVectorIndexes();
547
547
  }
548
548
  /**
549
- * Closes the underlying libsql client, releasing all OS file handles.
550
- *
551
- * For local file databases, first runs PRAGMA wal_checkpoint(TRUNCATE) and
552
- * switches back to journal_mode=DELETE so the -wal and -shm sidecar files
553
- * are released promptly (mirrors LibSQLStore.close()).
554
- *
555
- * Remote (Turso) databases skip the WAL pragmas and just close the client.
549
+ * Closes the underlying libsql client, releasing this vector store's OS file handles.
556
550
  *
557
551
  * Safe to call more than once; subsequent calls are no-ops.
558
552
  */
559
553
  async close() {
560
- if (this.turso.closed) {
561
- return;
562
- }
563
- if (this.turso.protocol === "file" && !this.isMemoryDb) {
564
- try {
565
- await this.turso.execute("PRAGMA wal_checkpoint(TRUNCATE);");
566
- await this.turso.execute("PRAGMA journal_mode=DELETE;");
567
- } catch (err) {
568
- this.logger.warn("LibSQLVector: Failed to checkpoint WAL before close.", err);
569
- }
554
+ if (!this.turso.closed) {
555
+ this.turso.close();
570
556
  }
571
- this.turso.close();
572
557
  }
573
558
  async discoverVectorIndexes() {
574
559
  try {
@@ -7023,6 +7008,40 @@ var MCPServersLibSQL = class extends storage.MCPServersStorage {
7023
7008
  }
7024
7009
  };
7025
7010
  var OM_TABLE = "mastra_observational_memory";
7011
+ function addSqliteMetadataFilter(conditions, params, metadataFilter) {
7012
+ if (!metadataFilter) return;
7013
+ for (const [key, value] of Object.entries(metadataFilter)) {
7014
+ const path = `$.metadata.${key}`;
7015
+ conditions.push(`CASE WHEN json_valid(content) THEN json_type(content, ?) IS NOT NULL ELSE 0 END`);
7016
+ params.push(path);
7017
+ addSqliteMetadataValuePredicate(conditions, params, path, value);
7018
+ }
7019
+ }
7020
+ function addSqliteMetadataValuePredicate(conditions, params, path, value) {
7021
+ if (value === null) {
7022
+ conditions.push(`CASE WHEN json_valid(content) THEN json_type(content, ?) = 'null' ELSE 0 END`);
7023
+ params.push(path);
7024
+ return;
7025
+ }
7026
+ if (typeof value === "string") {
7027
+ conditions.push(
7028
+ `CASE WHEN json_valid(content) THEN json_type(content, ?) = 'text' AND json_extract(content, ?) = ? ELSE 0 END`
7029
+ );
7030
+ params.push(path, path, value);
7031
+ return;
7032
+ }
7033
+ if (typeof value === "number") {
7034
+ conditions.push(
7035
+ `CASE WHEN json_valid(content) THEN json_type(content, ?) IN ('integer', 'real') AND json_extract(content, ?) = ? ELSE 0 END`
7036
+ );
7037
+ params.push(path, path, value);
7038
+ return;
7039
+ }
7040
+ conditions.push(
7041
+ `CASE WHEN json_valid(content) THEN json_type(content, ?) = ? AND json_extract(content, ?) = ? ELSE 0 END`
7042
+ );
7043
+ params.push(path, value ? "true" : "false", path, value ? 1 : 0);
7044
+ }
7026
7045
  var MemoryLibSQL = class _MemoryLibSQL extends storage.MemoryStorage {
7027
7046
  supportsObservationalMemory = true;
7028
7047
  /**
@@ -7268,6 +7287,7 @@ var MemoryLibSQL = class _MemoryLibSQL extends storage.MemoryStorage {
7268
7287
  }
7269
7288
  const perPage = storage.normalizePerPage(perPageInput, 40);
7270
7289
  const { offset, perPage: perPageForResponse } = storage.calculatePagination(page, perPageInput, perPage);
7290
+ const metadataFilter = storage.validateStorageMetadataFilter(filter?.metadata);
7271
7291
  try {
7272
7292
  const { field, direction } = this.parseOrderBy(orderBy, "ASC");
7273
7293
  const orderByStatement = `ORDER BY "${field}" ${direction}`;
@@ -7292,6 +7312,7 @@ var MemoryLibSQL = class _MemoryLibSQL extends storage.MemoryStorage {
7292
7312
  filter.dateRange.end instanceof Date ? filter.dateRange.end.toISOString() : filter.dateRange.end
7293
7313
  );
7294
7314
  }
7315
+ addSqliteMetadataFilter(conditions, queryParams, metadataFilter);
7295
7316
  const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
7296
7317
  if (perPage === 0 && (!include || include.length === 0)) {
7297
7318
  return { messages: [], total: 0, page, perPage: perPageForResponse, hasMore: false };
@@ -7321,6 +7342,7 @@ var MemoryLibSQL = class _MemoryLibSQL extends storage.MemoryStorage {
7321
7342
  args: [...queryParams, limitValue, offset]
7322
7343
  });
7323
7344
  const messages = (dataResult.rows || []).map((row) => this.parseRow(row));
7345
+ const paginatedCount = messages.length;
7324
7346
  if (total === 0 && messages.length === 0 && (!include || include.length === 0)) {
7325
7347
  return {
7326
7348
  messages: [],
@@ -7349,7 +7371,7 @@ var MemoryLibSQL = class _MemoryLibSQL extends storage.MemoryStorage {
7349
7371
  finalMessages.filter((m) => m.threadId && threadIdSet.has(m.threadId)).map((m) => m.id)
7350
7372
  );
7351
7373
  const allThreadMessagesReturned = returnedThreadMessageIds.size >= total;
7352
- const hasMore = perPageInput !== false && !allThreadMessagesReturned && offset + perPage < total;
7374
+ const hasMore = metadataFilter ? perPageInput !== false && offset + paginatedCount < total : perPageInput !== false && !allThreadMessagesReturned && offset + perPage < total;
7353
7375
  return {
7354
7376
  messages: finalMessages,
7355
7377
  total,
@@ -7407,6 +7429,7 @@ var MemoryLibSQL = class _MemoryLibSQL extends storage.MemoryStorage {
7407
7429
  }
7408
7430
  const perPage = storage.normalizePerPage(perPageInput, 40);
7409
7431
  const { offset, perPage: perPageForResponse } = storage.calculatePagination(page, perPageInput, perPage);
7432
+ const metadataFilter = storage.validateStorageMetadataFilter(filter?.metadata);
7410
7433
  try {
7411
7434
  const { field, direction } = this.parseOrderBy(orderBy, "ASC");
7412
7435
  const orderByStatement = `ORDER BY "${field}" ${direction}`;
@@ -7428,6 +7451,7 @@ var MemoryLibSQL = class _MemoryLibSQL extends storage.MemoryStorage {
7428
7451
  filter.dateRange.end instanceof Date ? filter.dateRange.end.toISOString() : filter.dateRange.end
7429
7452
  );
7430
7453
  }
7454
+ addSqliteMetadataFilter(conditions, queryParams, metadataFilter);
7431
7455
  const whereClause = `WHERE ${conditions.join(" AND ")}`;
7432
7456
  if (perPage === 0 && (!include || include.length === 0)) {
7433
7457
  return { messages: [], total: 0, page, perPage: perPageForResponse, hasMore: false };
@@ -14018,32 +14042,14 @@ var LibSQLStore = class extends storage.MastraCompositeStore {
14018
14042
  await this.initDomainsSequentially();
14019
14043
  }
14020
14044
  /**
14021
- * Closes the underlying libsql client, releasing all OS file handles.
14022
- *
14023
- * For local file databases, first runs PRAGMA wal_checkpoint(TRUNCATE) and
14024
- * switches back to journal_mode=DELETE so that Windows releases the -wal
14025
- * and -shm sidecar files promptly. Without this, the handles stay open
14026
- * until process exit, causing EBUSY errors when callers try to fs.rm the
14027
- * storage directory after Mastra.shutdown().
14028
- *
14029
- * Remote (Turso) databases skip the WAL pragmas and just close the client.
14045
+ * Closes the underlying libsql client, releasing this store's OS file handles.
14030
14046
  *
14031
14047
  * Safe to call more than once; subsequent calls are no-ops.
14032
14048
  */
14033
14049
  async close() {
14034
- if (this.client.closed) {
14035
- return;
14036
- }
14037
- const isLocalFileDb = this.isLocalDb || this.client.protocol === "file";
14038
- if (isLocalFileDb) {
14039
- try {
14040
- await this.client.execute("PRAGMA wal_checkpoint(TRUNCATE);");
14041
- await this.client.execute("PRAGMA journal_mode=DELETE;");
14042
- } catch (err) {
14043
- this.logger.warn("LibSQLStore: Failed to checkpoint WAL before close.", err);
14044
- }
14050
+ if (!this.client.closed) {
14051
+ this.client.close();
14045
14052
  }
14046
- this.client.close();
14047
14053
  }
14048
14054
  };
14049
14055