@mastra/libsql 0.10.4-alpha.1 → 0.11.0-alpha.3

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.
@@ -1,23 +1,23 @@
1
1
 
2
- > @mastra/libsql@0.10.4-alpha.1 build /home/runner/work/mastra/mastra/stores/libsql
2
+ > @mastra/libsql@0.11.0-alpha.3 build /home/runner/work/mastra/mastra/stores/libsql
3
3
  > tsup src/index.ts --format esm,cjs --experimental-dts --clean --treeshake=smallest --splitting
4
4
 
5
5
  CLI Building entry: src/index.ts
6
6
  CLI Using tsconfig: tsconfig.json
7
7
  CLI tsup v8.5.0
8
8
  TSC Build start
9
- TSC ⚡️ Build success in 9194ms
9
+ TSC ⚡️ Build success in 12207ms
10
10
  DTS Build start
11
11
  CLI Target: es2022
12
12
  Analysis will use the bundled TypeScript version 5.8.3
13
13
  Writing package typings: /home/runner/work/mastra/mastra/stores/libsql/dist/_tsup-dts-rollup.d.ts
14
14
  Analysis will use the bundled TypeScript version 5.8.3
15
15
  Writing package typings: /home/runner/work/mastra/mastra/stores/libsql/dist/_tsup-dts-rollup.d.cts
16
- DTS ⚡️ Build success in 10861ms
16
+ DTS ⚡️ Build success in 13745ms
17
17
  CLI Cleaning output folder
18
18
  ESM Build start
19
19
  CJS Build start
20
- ESM dist/index.js 69.60 KB
21
- ESM ⚡️ Build success in 1544ms
22
- CJS dist/index.cjs 70.57 KB
23
- CJS ⚡️ Build success in 1547ms
20
+ ESM dist/index.js 72.42 KB
21
+ ESM ⚡️ Build success in 2002ms
22
+ CJS dist/index.cjs 73.39 KB
23
+ CJS ⚡️ Build success in 2005ms
package/CHANGELOG.md CHANGED
@@ -1,5 +1,32 @@
1
1
  # @mastra/libsql
2
2
 
3
+ ## 0.11.0-alpha.3
4
+
5
+ ### Minor Changes
6
+
7
+ - 8a3bfd2: Update peerdeps to latest core
8
+
9
+ ### Patch Changes
10
+
11
+ - Updated dependencies [792c4c0]
12
+ - Updated dependencies [502fe05]
13
+ - Updated dependencies [4efcfa0]
14
+ - @mastra/core@0.10.7-alpha.3
15
+
16
+ ## 0.10.4-alpha.2
17
+
18
+ ### Patch Changes
19
+
20
+ - 15e9d26: Added per-resource working memory for LibSQL, Upstash, and PG
21
+ - 0fb9d64: [MASTRA-4018] Update saveMessages in storage adapters to upsert messages
22
+ - 144eb0b: [MASTRA-3669] Metadata Filter Types
23
+ - f0150c4: Fix LibSQLStore and PgStore getMessagesPaginated implementation
24
+ - Updated dependencies [15e9d26]
25
+ - Updated dependencies [07d6d88]
26
+ - Updated dependencies [5d74aab]
27
+ - Updated dependencies [144eb0b]
28
+ - @mastra/core@0.10.7-alpha.2
29
+
3
30
  ## 0.10.4-alpha.1
4
31
 
5
32
  ### Patch Changes
@@ -12,22 +12,25 @@ import type { MastraMessageV2 } from '@mastra/core/agent';
12
12
  import { MastraStorage } from '@mastra/core/storage';
13
13
  import { MastraVector } from '@mastra/core/vector';
14
14
  import type { OperatorSupport } from '@mastra/core/vector/filter';
15
+ import type { OperatorValueMap } from '@mastra/core/vector/filter';
15
16
  import type { PaginationArgs } from '@mastra/core/storage';
16
17
  import type { PaginationInfo } from '@mastra/core/storage';
17
18
  import type { QueryResult } from '@mastra/core/vector';
18
19
  import type { QueryVectorParams } from '@mastra/core/vector';
19
20
  import type { StorageColumn } from '@mastra/core/storage';
20
21
  import type { StorageGetMessagesArg } from '@mastra/core/storage';
22
+ import type { StorageResourceType } from '@mastra/core/storage';
21
23
  import type { StorageThreadType } from '@mastra/core/memory';
22
24
  import type { TABLE_NAMES } from '@mastra/core/storage';
23
25
  import type { Trace } from '@mastra/core/telemetry';
24
26
  import type { UpdateVectorParams } from '@mastra/core/vector';
25
27
  import type { UpsertVectorParams } from '@mastra/core/vector';
28
+ import type { VectorFieldValue } from '@mastra/core/vector/filter';
26
29
  import type { VectorFilter } from '@mastra/core/vector/filter';
27
30
  import type { WorkflowRun } from '@mastra/core/storage';
28
31
  import type { WorkflowRuns } from '@mastra/core/storage';
29
32
 
30
- export declare function buildFilterQuery(filter: VectorFilter): FilterResult;
33
+ export declare function buildFilterQuery(filter: LibSQLVectorFilter): FilterResult;
31
34
 
32
35
  declare interface FilterResult {
33
36
  sql: string;
@@ -70,13 +73,23 @@ export { LibSQLConfig as LibSQLConfig_alias_1 }
70
73
  * - Can take either a single condition or an array of conditions
71
74
  *
72
75
  */
73
- export declare class LibSQLFilterTranslator extends BaseFilterTranslator {
76
+ export declare class LibSQLFilterTranslator extends BaseFilterTranslator<LibSQLVectorFilter> {
74
77
  protected getSupportedOperators(): OperatorSupport;
75
- translate(filter?: VectorFilter): VectorFilter;
78
+ translate(filter?: LibSQLVectorFilter): LibSQLVectorFilter;
76
79
  private translateNode;
77
80
  }
78
81
 
79
- declare interface LibSQLQueryVectorParams extends QueryVectorParams {
82
+ declare type LibSQLOperatorValueMap = Omit<OperatorValueMap, '$regex' | '$options' | '$in' | '$all' | '$nin' | '$eq' | '$ne'> & {
83
+ $size: number;
84
+ $contains: VectorFieldValue | Record<string, unknown>;
85
+ $all: VectorFieldValue;
86
+ $in: VectorFieldValue;
87
+ $nin: VectorFieldValue;
88
+ $eq: VectorFieldValue;
89
+ $ne: VectorFieldValue;
90
+ };
91
+
92
+ declare interface LibSQLQueryVectorParams extends QueryVectorParams<LibSQLVectorFilter> {
80
93
  minScore?: number;
81
94
  }
82
95
 
@@ -87,6 +100,7 @@ declare class LibSQLStore extends MastraStorage {
87
100
  constructor(config: LibSQLConfig);
88
101
  get supports(): {
89
102
  selectByIncludeResourceScope: boolean;
103
+ resourceWorkingMemory: boolean;
90
104
  };
91
105
  private getCreateTableSQL;
92
106
  createTable({ tableName, schema, }: {
@@ -224,6 +238,17 @@ declare class LibSQLStore extends MastraStorage {
224
238
  runId: string;
225
239
  workflowName?: string;
226
240
  }): Promise<WorkflowRun | null>;
241
+ getResourceById({ resourceId }: {
242
+ resourceId: string;
243
+ }): Promise<StorageResourceType | null>;
244
+ saveResource({ resource }: {
245
+ resource: StorageResourceType;
246
+ }): Promise<StorageResourceType>;
247
+ updateResource({ resourceId, workingMemory, metadata, }: {
248
+ resourceId: string;
249
+ workingMemory?: string;
250
+ metadata?: Record<string, unknown>;
251
+ }): Promise<StorageResourceType>;
227
252
  private hasColumn;
228
253
  private parseWorkflowRun;
229
254
  }
@@ -232,13 +257,13 @@ export { LibSQLStore as DefaultStorage_alias_1 }
232
257
  export { LibSQLStore }
233
258
  export { LibSQLStore as LibSQLStore_alias_1 }
234
259
 
235
- declare class LibSQLVector extends MastraVector {
260
+ declare class LibSQLVector extends MastraVector<LibSQLVectorFilter> {
236
261
  private turso;
237
262
  private readonly maxRetries;
238
263
  private readonly initialBackoffMs;
239
264
  constructor({ connectionUrl, authToken, syncUrl, syncInterval, maxRetries, initialBackoffMs, }: LibSQLVectorConfig);
240
265
  private executeWriteOperationWithRetry;
241
- transformFilter(filter?: VectorFilter): VectorFilter;
266
+ transformFilter(filter?: LibSQLVectorFilter): LibSQLVectorFilter;
242
267
  query({ indexName, queryVector, topK, filter, includeVector, minScore, }: LibSQLQueryVectorParams): Promise<QueryResult[]>;
243
268
  upsert(args: UpsertVectorParams): Promise<string[]>;
244
269
  private doUpsert;
@@ -302,4 +327,6 @@ declare interface LibSQLVectorConfig {
302
327
  export { LibSQLVectorConfig }
303
328
  export { LibSQLVectorConfig as LibSQLVectorConfig_alias_1 }
304
329
 
330
+ export declare type LibSQLVectorFilter = VectorFilter<keyof LibSQLOperatorValueMap, LibSQLOperatorValueMap>;
331
+
305
332
  export { }
@@ -12,22 +12,25 @@ import type { MastraMessageV2 } from '@mastra/core/agent';
12
12
  import { MastraStorage } from '@mastra/core/storage';
13
13
  import { MastraVector } from '@mastra/core/vector';
14
14
  import type { OperatorSupport } from '@mastra/core/vector/filter';
15
+ import type { OperatorValueMap } from '@mastra/core/vector/filter';
15
16
  import type { PaginationArgs } from '@mastra/core/storage';
16
17
  import type { PaginationInfo } from '@mastra/core/storage';
17
18
  import type { QueryResult } from '@mastra/core/vector';
18
19
  import type { QueryVectorParams } from '@mastra/core/vector';
19
20
  import type { StorageColumn } from '@mastra/core/storage';
20
21
  import type { StorageGetMessagesArg } from '@mastra/core/storage';
22
+ import type { StorageResourceType } from '@mastra/core/storage';
21
23
  import type { StorageThreadType } from '@mastra/core/memory';
22
24
  import type { TABLE_NAMES } from '@mastra/core/storage';
23
25
  import type { Trace } from '@mastra/core/telemetry';
24
26
  import type { UpdateVectorParams } from '@mastra/core/vector';
25
27
  import type { UpsertVectorParams } from '@mastra/core/vector';
28
+ import type { VectorFieldValue } from '@mastra/core/vector/filter';
26
29
  import type { VectorFilter } from '@mastra/core/vector/filter';
27
30
  import type { WorkflowRun } from '@mastra/core/storage';
28
31
  import type { WorkflowRuns } from '@mastra/core/storage';
29
32
 
30
- export declare function buildFilterQuery(filter: VectorFilter): FilterResult;
33
+ export declare function buildFilterQuery(filter: LibSQLVectorFilter): FilterResult;
31
34
 
32
35
  declare interface FilterResult {
33
36
  sql: string;
@@ -70,13 +73,23 @@ export { LibSQLConfig as LibSQLConfig_alias_1 }
70
73
  * - Can take either a single condition or an array of conditions
71
74
  *
72
75
  */
73
- export declare class LibSQLFilterTranslator extends BaseFilterTranslator {
76
+ export declare class LibSQLFilterTranslator extends BaseFilterTranslator<LibSQLVectorFilter> {
74
77
  protected getSupportedOperators(): OperatorSupport;
75
- translate(filter?: VectorFilter): VectorFilter;
78
+ translate(filter?: LibSQLVectorFilter): LibSQLVectorFilter;
76
79
  private translateNode;
77
80
  }
78
81
 
79
- declare interface LibSQLQueryVectorParams extends QueryVectorParams {
82
+ declare type LibSQLOperatorValueMap = Omit<OperatorValueMap, '$regex' | '$options' | '$in' | '$all' | '$nin' | '$eq' | '$ne'> & {
83
+ $size: number;
84
+ $contains: VectorFieldValue | Record<string, unknown>;
85
+ $all: VectorFieldValue;
86
+ $in: VectorFieldValue;
87
+ $nin: VectorFieldValue;
88
+ $eq: VectorFieldValue;
89
+ $ne: VectorFieldValue;
90
+ };
91
+
92
+ declare interface LibSQLQueryVectorParams extends QueryVectorParams<LibSQLVectorFilter> {
80
93
  minScore?: number;
81
94
  }
82
95
 
@@ -87,6 +100,7 @@ declare class LibSQLStore extends MastraStorage {
87
100
  constructor(config: LibSQLConfig);
88
101
  get supports(): {
89
102
  selectByIncludeResourceScope: boolean;
103
+ resourceWorkingMemory: boolean;
90
104
  };
91
105
  private getCreateTableSQL;
92
106
  createTable({ tableName, schema, }: {
@@ -224,6 +238,17 @@ declare class LibSQLStore extends MastraStorage {
224
238
  runId: string;
225
239
  workflowName?: string;
226
240
  }): Promise<WorkflowRun | null>;
241
+ getResourceById({ resourceId }: {
242
+ resourceId: string;
243
+ }): Promise<StorageResourceType | null>;
244
+ saveResource({ resource }: {
245
+ resource: StorageResourceType;
246
+ }): Promise<StorageResourceType>;
247
+ updateResource({ resourceId, workingMemory, metadata, }: {
248
+ resourceId: string;
249
+ workingMemory?: string;
250
+ metadata?: Record<string, unknown>;
251
+ }): Promise<StorageResourceType>;
227
252
  private hasColumn;
228
253
  private parseWorkflowRun;
229
254
  }
@@ -232,13 +257,13 @@ export { LibSQLStore as DefaultStorage_alias_1 }
232
257
  export { LibSQLStore }
233
258
  export { LibSQLStore as LibSQLStore_alias_1 }
234
259
 
235
- declare class LibSQLVector extends MastraVector {
260
+ declare class LibSQLVector extends MastraVector<LibSQLVectorFilter> {
236
261
  private turso;
237
262
  private readonly maxRetries;
238
263
  private readonly initialBackoffMs;
239
264
  constructor({ connectionUrl, authToken, syncUrl, syncInterval, maxRetries, initialBackoffMs, }: LibSQLVectorConfig);
240
265
  private executeWriteOperationWithRetry;
241
- transformFilter(filter?: VectorFilter): VectorFilter;
266
+ transformFilter(filter?: LibSQLVectorFilter): LibSQLVectorFilter;
242
267
  query({ indexName, queryVector, topK, filter, includeVector, minScore, }: LibSQLQueryVectorParams): Promise<QueryResult[]>;
243
268
  upsert(args: UpsertVectorParams): Promise<string[]>;
244
269
  private doUpsert;
@@ -302,4 +327,6 @@ declare interface LibSQLVectorConfig {
302
327
  export { LibSQLVectorConfig }
303
328
  export { LibSQLVectorConfig as LibSQLVectorConfig_alias_1 }
304
329
 
330
+ export declare type LibSQLVectorFilter = VectorFilter<keyof LibSQLOperatorValueMap, LibSQLOperatorValueMap>;
331
+
305
332
  export { }
package/dist/index.cjs CHANGED
@@ -409,7 +409,7 @@ function buildCondition(key, value, parentPath) {
409
409
  return handleOperator(key, value);
410
410
  }
411
411
  function handleLogicalOperator(key, value, parentPath) {
412
- if (!value || value.length === 0) {
412
+ if (!value || Array.isArray(value) && value.length === 0) {
413
413
  switch (key) {
414
414
  case "$and":
415
415
  case "$nor":
@@ -433,7 +433,7 @@ function handleLogicalOperator(key, value, parentPath) {
433
433
  const values = [];
434
434
  const joinOperator = key === "$or" || key === "$nor" ? "OR" : "AND";
435
435
  const conditions = Array.isArray(value) ? value.map((f) => {
436
- const entries = Object.entries(f);
436
+ const entries = !!f ? Object.entries(f) : [];
437
437
  return entries.map(([k, v]) => buildCondition(k, v));
438
438
  }) : [buildCondition(key, value)];
439
439
  const joined = conditions.flat().map((c) => {
@@ -932,7 +932,8 @@ var LibSQLStore = class extends storage.MastraStorage {
932
932
  }
933
933
  get supports() {
934
934
  return {
935
- selectByIncludeResourceScope: true
935
+ selectByIncludeResourceScope: true,
936
+ resourceWorkingMemory: true
936
937
  };
937
938
  }
938
939
  getCreateTableSQL(tableName, schema) {
@@ -1481,7 +1482,8 @@ var LibSQLStore = class extends storage.MastraStorage {
1481
1482
  }
1482
1483
  async getMessagesPaginated(args) {
1483
1484
  const { threadId, format, selectBy } = args;
1484
- const { page = 0, perPage = 40, dateRange } = selectBy?.pagination || {};
1485
+ const { page = 0, perPage: perPageInput, dateRange } = selectBy?.pagination || {};
1486
+ const perPage = perPageInput !== void 0 ? perPageInput : this.resolveMessageLimit({ last: selectBy?.last, defaultLimit: 40 });
1485
1487
  const fromDate = dateRange?.start;
1486
1488
  const toDate = dateRange?.end;
1487
1489
  const messages = [];
@@ -1521,7 +1523,7 @@ var LibSQLStore = class extends storage.MastraStorage {
1521
1523
  args: queryParams
1522
1524
  });
1523
1525
  const total = Number(countResult.rows?.[0]?.count ?? 0);
1524
- if (total === 0) {
1526
+ if (total === 0 && messages.length === 0) {
1525
1527
  return {
1526
1528
  messages: [],
1527
1529
  total: 0,
@@ -1530,9 +1532,11 @@ var LibSQLStore = class extends storage.MastraStorage {
1530
1532
  hasMore: false
1531
1533
  };
1532
1534
  }
1535
+ const excludeIds = messages.map((m) => m.id);
1536
+ const excludeIdsParam = excludeIds.map((_, idx) => `$${idx + queryParams.length + 1}`).join(", ");
1533
1537
  const dataResult = await this.client.execute({
1534
- sql: `SELECT id, content, role, type, "createdAt", thread_id FROM ${storage.TABLE_MESSAGES} ${whereClause} ORDER BY "createdAt" DESC LIMIT ? OFFSET ?`,
1535
- args: [...queryParams, perPage, currentOffset]
1538
+ sql: `SELECT id, content, role, type, "createdAt", "resourceId", "thread_id" FROM ${storage.TABLE_MESSAGES} ${whereClause} ${excludeIds.length ? `AND id NOT IN (${excludeIdsParam})` : ""} ORDER BY "createdAt" DESC LIMIT ? OFFSET ?`,
1539
+ args: [...queryParams, ...excludeIds, perPage, currentOffset]
1536
1540
  });
1537
1541
  messages.push(...(dataResult.rows || []).map((row) => this.parseRow(row)));
1538
1542
  const messagesToReturn = format === "v1" ? new agent.MessageList().add(messages, "memory").get.all.v1() : new agent.MessageList().add(messages, "memory").get.all.v2();
@@ -1582,7 +1586,14 @@ var LibSQLStore = class extends storage.MastraStorage {
1582
1586
  }
1583
1587
  return {
1584
1588
  sql: `INSERT INTO ${storage.TABLE_MESSAGES} (id, thread_id, content, role, type, createdAt, resourceId)
1585
- VALUES (?, ?, ?, ?, ?, ?, ?)`,
1589
+ VALUES (?, ?, ?, ?, ?, ?, ?)
1590
+ ON CONFLICT(id) DO UPDATE SET
1591
+ thread_id=excluded.thread_id,
1592
+ content=excluded.content,
1593
+ role=excluded.role,
1594
+ type=excluded.type,
1595
+ resourceId=excluded.resourceId
1596
+ `,
1586
1597
  args: [
1587
1598
  message.id,
1588
1599
  message.threadId,
@@ -2008,6 +2019,75 @@ var LibSQLStore = class extends storage.MastraStorage {
2008
2019
  );
2009
2020
  }
2010
2021
  }
2022
+ async getResourceById({ resourceId }) {
2023
+ const result = await this.load({
2024
+ tableName: storage.TABLE_RESOURCES,
2025
+ keys: { id: resourceId }
2026
+ });
2027
+ if (!result) {
2028
+ return null;
2029
+ }
2030
+ return {
2031
+ ...result,
2032
+ // Ensure workingMemory is always returned as a string, even if auto-parsed as JSON
2033
+ workingMemory: typeof result.workingMemory === "object" ? JSON.stringify(result.workingMemory) : result.workingMemory,
2034
+ metadata: typeof result.metadata === "string" ? JSON.parse(result.metadata) : result.metadata
2035
+ };
2036
+ }
2037
+ async saveResource({ resource }) {
2038
+ await this.insert({
2039
+ tableName: storage.TABLE_RESOURCES,
2040
+ record: {
2041
+ ...resource,
2042
+ metadata: JSON.stringify(resource.metadata)
2043
+ }
2044
+ });
2045
+ return resource;
2046
+ }
2047
+ async updateResource({
2048
+ resourceId,
2049
+ workingMemory,
2050
+ metadata
2051
+ }) {
2052
+ const existingResource = await this.getResourceById({ resourceId });
2053
+ if (!existingResource) {
2054
+ const newResource = {
2055
+ id: resourceId,
2056
+ workingMemory,
2057
+ metadata: metadata || {},
2058
+ createdAt: /* @__PURE__ */ new Date(),
2059
+ updatedAt: /* @__PURE__ */ new Date()
2060
+ };
2061
+ return this.saveResource({ resource: newResource });
2062
+ }
2063
+ const updatedResource = {
2064
+ ...existingResource,
2065
+ workingMemory: workingMemory !== void 0 ? workingMemory : existingResource.workingMemory,
2066
+ metadata: {
2067
+ ...existingResource.metadata,
2068
+ ...metadata
2069
+ },
2070
+ updatedAt: /* @__PURE__ */ new Date()
2071
+ };
2072
+ const updates = [];
2073
+ const values = [];
2074
+ if (workingMemory !== void 0) {
2075
+ updates.push("workingMemory = ?");
2076
+ values.push(workingMemory);
2077
+ }
2078
+ if (metadata) {
2079
+ updates.push("metadata = ?");
2080
+ values.push(JSON.stringify(updatedResource.metadata));
2081
+ }
2082
+ updates.push("updatedAt = ?");
2083
+ values.push(updatedResource.updatedAt.toISOString());
2084
+ values.push(resourceId);
2085
+ await this.client.execute({
2086
+ sql: `UPDATE ${storage.TABLE_RESOURCES} SET ${updates.join(", ")} WHERE id = ?`,
2087
+ args: values
2088
+ });
2089
+ return updatedResource;
2090
+ }
2011
2091
  async hasColumn(table, column) {
2012
2092
  const result = await this.client.execute({
2013
2093
  sql: `PRAGMA table_info(${table})`
package/dist/index.js CHANGED
@@ -4,7 +4,7 @@ import { parseSqlIdentifier, parseFieldKey } from '@mastra/core/utils';
4
4
  import { MastraVector } from '@mastra/core/vector';
5
5
  import { BaseFilterTranslator } from '@mastra/core/vector/filter';
6
6
  import { MessageList } from '@mastra/core/agent';
7
- import { MastraStorage, TABLE_WORKFLOW_SNAPSHOT, TABLE_THREADS, TABLE_MESSAGES, TABLE_EVALS, TABLE_TRACES } from '@mastra/core/storage';
7
+ import { MastraStorage, TABLE_WORKFLOW_SNAPSHOT, TABLE_THREADS, TABLE_MESSAGES, TABLE_EVALS, TABLE_TRACES, TABLE_RESOURCES } from '@mastra/core/storage';
8
8
 
9
9
  // src/vector/index.ts
10
10
  var LibSQLFilterTranslator = class extends BaseFilterTranslator {
@@ -407,7 +407,7 @@ function buildCondition(key, value, parentPath) {
407
407
  return handleOperator(key, value);
408
408
  }
409
409
  function handleLogicalOperator(key, value, parentPath) {
410
- if (!value || value.length === 0) {
410
+ if (!value || Array.isArray(value) && value.length === 0) {
411
411
  switch (key) {
412
412
  case "$and":
413
413
  case "$nor":
@@ -431,7 +431,7 @@ function handleLogicalOperator(key, value, parentPath) {
431
431
  const values = [];
432
432
  const joinOperator = key === "$or" || key === "$nor" ? "OR" : "AND";
433
433
  const conditions = Array.isArray(value) ? value.map((f) => {
434
- const entries = Object.entries(f);
434
+ const entries = !!f ? Object.entries(f) : [];
435
435
  return entries.map(([k, v]) => buildCondition(k, v));
436
436
  }) : [buildCondition(key, value)];
437
437
  const joined = conditions.flat().map((c) => {
@@ -930,7 +930,8 @@ var LibSQLStore = class extends MastraStorage {
930
930
  }
931
931
  get supports() {
932
932
  return {
933
- selectByIncludeResourceScope: true
933
+ selectByIncludeResourceScope: true,
934
+ resourceWorkingMemory: true
934
935
  };
935
936
  }
936
937
  getCreateTableSQL(tableName, schema) {
@@ -1479,7 +1480,8 @@ var LibSQLStore = class extends MastraStorage {
1479
1480
  }
1480
1481
  async getMessagesPaginated(args) {
1481
1482
  const { threadId, format, selectBy } = args;
1482
- const { page = 0, perPage = 40, dateRange } = selectBy?.pagination || {};
1483
+ const { page = 0, perPage: perPageInput, dateRange } = selectBy?.pagination || {};
1484
+ const perPage = perPageInput !== void 0 ? perPageInput : this.resolveMessageLimit({ last: selectBy?.last, defaultLimit: 40 });
1483
1485
  const fromDate = dateRange?.start;
1484
1486
  const toDate = dateRange?.end;
1485
1487
  const messages = [];
@@ -1519,7 +1521,7 @@ var LibSQLStore = class extends MastraStorage {
1519
1521
  args: queryParams
1520
1522
  });
1521
1523
  const total = Number(countResult.rows?.[0]?.count ?? 0);
1522
- if (total === 0) {
1524
+ if (total === 0 && messages.length === 0) {
1523
1525
  return {
1524
1526
  messages: [],
1525
1527
  total: 0,
@@ -1528,9 +1530,11 @@ var LibSQLStore = class extends MastraStorage {
1528
1530
  hasMore: false
1529
1531
  };
1530
1532
  }
1533
+ const excludeIds = messages.map((m) => m.id);
1534
+ const excludeIdsParam = excludeIds.map((_, idx) => `$${idx + queryParams.length + 1}`).join(", ");
1531
1535
  const dataResult = await this.client.execute({
1532
- sql: `SELECT id, content, role, type, "createdAt", thread_id FROM ${TABLE_MESSAGES} ${whereClause} ORDER BY "createdAt" DESC LIMIT ? OFFSET ?`,
1533
- args: [...queryParams, perPage, currentOffset]
1536
+ sql: `SELECT id, content, role, type, "createdAt", "resourceId", "thread_id" FROM ${TABLE_MESSAGES} ${whereClause} ${excludeIds.length ? `AND id NOT IN (${excludeIdsParam})` : ""} ORDER BY "createdAt" DESC LIMIT ? OFFSET ?`,
1537
+ args: [...queryParams, ...excludeIds, perPage, currentOffset]
1534
1538
  });
1535
1539
  messages.push(...(dataResult.rows || []).map((row) => this.parseRow(row)));
1536
1540
  const messagesToReturn = format === "v1" ? new MessageList().add(messages, "memory").get.all.v1() : new MessageList().add(messages, "memory").get.all.v2();
@@ -1580,7 +1584,14 @@ var LibSQLStore = class extends MastraStorage {
1580
1584
  }
1581
1585
  return {
1582
1586
  sql: `INSERT INTO ${TABLE_MESSAGES} (id, thread_id, content, role, type, createdAt, resourceId)
1583
- VALUES (?, ?, ?, ?, ?, ?, ?)`,
1587
+ VALUES (?, ?, ?, ?, ?, ?, ?)
1588
+ ON CONFLICT(id) DO UPDATE SET
1589
+ thread_id=excluded.thread_id,
1590
+ content=excluded.content,
1591
+ role=excluded.role,
1592
+ type=excluded.type,
1593
+ resourceId=excluded.resourceId
1594
+ `,
1584
1595
  args: [
1585
1596
  message.id,
1586
1597
  message.threadId,
@@ -2006,6 +2017,75 @@ var LibSQLStore = class extends MastraStorage {
2006
2017
  );
2007
2018
  }
2008
2019
  }
2020
+ async getResourceById({ resourceId }) {
2021
+ const result = await this.load({
2022
+ tableName: TABLE_RESOURCES,
2023
+ keys: { id: resourceId }
2024
+ });
2025
+ if (!result) {
2026
+ return null;
2027
+ }
2028
+ return {
2029
+ ...result,
2030
+ // Ensure workingMemory is always returned as a string, even if auto-parsed as JSON
2031
+ workingMemory: typeof result.workingMemory === "object" ? JSON.stringify(result.workingMemory) : result.workingMemory,
2032
+ metadata: typeof result.metadata === "string" ? JSON.parse(result.metadata) : result.metadata
2033
+ };
2034
+ }
2035
+ async saveResource({ resource }) {
2036
+ await this.insert({
2037
+ tableName: TABLE_RESOURCES,
2038
+ record: {
2039
+ ...resource,
2040
+ metadata: JSON.stringify(resource.metadata)
2041
+ }
2042
+ });
2043
+ return resource;
2044
+ }
2045
+ async updateResource({
2046
+ resourceId,
2047
+ workingMemory,
2048
+ metadata
2049
+ }) {
2050
+ const existingResource = await this.getResourceById({ resourceId });
2051
+ if (!existingResource) {
2052
+ const newResource = {
2053
+ id: resourceId,
2054
+ workingMemory,
2055
+ metadata: metadata || {},
2056
+ createdAt: /* @__PURE__ */ new Date(),
2057
+ updatedAt: /* @__PURE__ */ new Date()
2058
+ };
2059
+ return this.saveResource({ resource: newResource });
2060
+ }
2061
+ const updatedResource = {
2062
+ ...existingResource,
2063
+ workingMemory: workingMemory !== void 0 ? workingMemory : existingResource.workingMemory,
2064
+ metadata: {
2065
+ ...existingResource.metadata,
2066
+ ...metadata
2067
+ },
2068
+ updatedAt: /* @__PURE__ */ new Date()
2069
+ };
2070
+ const updates = [];
2071
+ const values = [];
2072
+ if (workingMemory !== void 0) {
2073
+ updates.push("workingMemory = ?");
2074
+ values.push(workingMemory);
2075
+ }
2076
+ if (metadata) {
2077
+ updates.push("metadata = ?");
2078
+ values.push(JSON.stringify(updatedResource.metadata));
2079
+ }
2080
+ updates.push("updatedAt = ?");
2081
+ values.push(updatedResource.updatedAt.toISOString());
2082
+ values.push(resourceId);
2083
+ await this.client.execute({
2084
+ sql: `UPDATE ${TABLE_RESOURCES} SET ${updates.join(", ")} WHERE id = ?`,
2085
+ args: values
2086
+ });
2087
+ return updatedResource;
2088
+ }
2009
2089
  async hasColumn(table, column) {
2010
2090
  const result = await this.client.execute({
2011
2091
  sql: `PRAGMA table_info(${table})`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mastra/libsql",
3
- "version": "0.10.4-alpha.1",
3
+ "version": "0.11.0-alpha.3",
4
4
  "description": "Libsql provider for Mastra - includes both vector and db storage capabilities",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -25,16 +25,16 @@
25
25
  "devDependencies": {
26
26
  "@microsoft/api-extractor": "^7.52.8",
27
27
  "@types/node": "^20.19.0",
28
- "eslint": "^9.28.0",
28
+ "eslint": "^9.29.0",
29
29
  "tsup": "^8.5.0",
30
30
  "typescript": "^5.8.3",
31
31
  "vitest": "^3.2.3",
32
+ "@internal/lint": "0.0.13",
32
33
  "@internal/storage-test-utils": "0.0.9",
33
- "@mastra/core": "0.10.7-alpha.1",
34
- "@internal/lint": "0.0.13"
34
+ "@mastra/core": "0.10.7-alpha.3"
35
35
  },
36
36
  "peerDependencies": {
37
- "@mastra/core": ">=0.10.4-0 <0.11.0"
37
+ "@mastra/core": ">=0.10.7-0 <0.11.0-0"
38
38
  },
39
39
  "scripts": {
40
40
  "build": "tsup src/index.ts --format esm,cjs --experimental-dts --clean --treeshake=smallest --splitting",
@@ -11,6 +11,7 @@ import {
11
11
  TABLE_MESSAGES,
12
12
  TABLE_THREADS,
13
13
  TABLE_TRACES,
14
+ TABLE_RESOURCES,
14
15
  TABLE_WORKFLOW_SNAPSHOT,
15
16
  } from '@mastra/core/storage';
16
17
  import type {
@@ -19,6 +20,7 @@ import type {
19
20
  PaginationInfo,
20
21
  StorageColumn,
21
22
  StorageGetMessagesArg,
23
+ StorageResourceType,
22
24
  TABLE_NAMES,
23
25
  WorkflowRun,
24
26
  WorkflowRuns,
@@ -84,9 +86,11 @@ export class LibSQLStore extends MastraStorage {
84
86
 
85
87
  public get supports(): {
86
88
  selectByIncludeResourceScope: boolean;
89
+ resourceWorkingMemory: boolean;
87
90
  } {
88
91
  return {
89
92
  selectByIncludeResourceScope: true,
93
+ resourceWorkingMemory: true,
90
94
  };
91
95
  }
92
96
 
@@ -748,7 +752,9 @@ export class LibSQLStore extends MastraStorage {
748
752
  },
749
753
  ): Promise<PaginationInfo & { messages: MastraMessageV1[] | MastraMessageV2[] }> {
750
754
  const { threadId, format, selectBy } = args;
751
- const { page = 0, perPage = 40, dateRange } = selectBy?.pagination || {};
755
+ const { page = 0, perPage: perPageInput, dateRange } = selectBy?.pagination || {};
756
+ const perPage =
757
+ perPageInput !== undefined ? perPageInput : this.resolveMessageLimit({ last: selectBy?.last, defaultLimit: 40 });
752
758
  const fromDate = dateRange?.start;
753
759
  const toDate = dateRange?.end;
754
760
 
@@ -795,7 +801,7 @@ export class LibSQLStore extends MastraStorage {
795
801
  });
796
802
  const total = Number(countResult.rows?.[0]?.count ?? 0);
797
803
 
798
- if (total === 0) {
804
+ if (total === 0 && messages.length === 0) {
799
805
  return {
800
806
  messages: [],
801
807
  total: 0,
@@ -805,9 +811,12 @@ export class LibSQLStore extends MastraStorage {
805
811
  };
806
812
  }
807
813
 
814
+ const excludeIds = messages.map(m => m.id);
815
+ const excludeIdsParam = excludeIds.map((_, idx) => `$${idx + queryParams.length + 1}`).join(', ');
816
+
808
817
  const dataResult = await this.client.execute({
809
- sql: `SELECT id, content, role, type, "createdAt", thread_id FROM ${TABLE_MESSAGES} ${whereClause} ORDER BY "createdAt" DESC LIMIT ? OFFSET ?`,
810
- args: [...queryParams, perPage, currentOffset],
818
+ sql: `SELECT id, content, role, type, "createdAt", "resourceId", "thread_id" FROM ${TABLE_MESSAGES} ${whereClause} ${excludeIds.length ? `AND id NOT IN (${excludeIdsParam})` : ''} ORDER BY "createdAt" DESC LIMIT ? OFFSET ?`,
819
+ args: [...queryParams, ...excludeIds, perPage, currentOffset],
811
820
  });
812
821
 
813
822
  messages.push(...(dataResult.rows || []).map((row: any) => this.parseRow(row)));
@@ -871,7 +880,14 @@ export class LibSQLStore extends MastraStorage {
871
880
  }
872
881
  return {
873
882
  sql: `INSERT INTO ${TABLE_MESSAGES} (id, thread_id, content, role, type, createdAt, resourceId)
874
- VALUES (?, ?, ?, ?, ?, ?, ?)`,
883
+ VALUES (?, ?, ?, ?, ?, ?, ?)
884
+ ON CONFLICT(id) DO UPDATE SET
885
+ thread_id=excluded.thread_id,
886
+ content=excluded.content,
887
+ role=excluded.role,
888
+ type=excluded.type,
889
+ resourceId=excluded.resourceId
890
+ `,
875
891
  args: [
876
892
  message.id,
877
893
  message.threadId!,
@@ -1416,6 +1432,96 @@ export class LibSQLStore extends MastraStorage {
1416
1432
  }
1417
1433
  }
1418
1434
 
1435
+ async getResourceById({ resourceId }: { resourceId: string }): Promise<StorageResourceType | null> {
1436
+ const result = await this.load<StorageResourceType>({
1437
+ tableName: TABLE_RESOURCES,
1438
+ keys: { id: resourceId },
1439
+ });
1440
+
1441
+ if (!result) {
1442
+ return null;
1443
+ }
1444
+
1445
+ return {
1446
+ ...result,
1447
+ // Ensure workingMemory is always returned as a string, even if auto-parsed as JSON
1448
+ workingMemory:
1449
+ typeof result.workingMemory === 'object' ? JSON.stringify(result.workingMemory) : result.workingMemory,
1450
+ metadata: typeof result.metadata === 'string' ? JSON.parse(result.metadata) : result.metadata,
1451
+ };
1452
+ }
1453
+
1454
+ async saveResource({ resource }: { resource: StorageResourceType }): Promise<StorageResourceType> {
1455
+ await this.insert({
1456
+ tableName: TABLE_RESOURCES,
1457
+ record: {
1458
+ ...resource,
1459
+ metadata: JSON.stringify(resource.metadata),
1460
+ },
1461
+ });
1462
+
1463
+ return resource;
1464
+ }
1465
+
1466
+ async updateResource({
1467
+ resourceId,
1468
+ workingMemory,
1469
+ metadata,
1470
+ }: {
1471
+ resourceId: string;
1472
+ workingMemory?: string;
1473
+ metadata?: Record<string, unknown>;
1474
+ }): Promise<StorageResourceType> {
1475
+ const existingResource = await this.getResourceById({ resourceId });
1476
+
1477
+ if (!existingResource) {
1478
+ // Create new resource if it doesn't exist
1479
+ const newResource: StorageResourceType = {
1480
+ id: resourceId,
1481
+ workingMemory,
1482
+ metadata: metadata || {},
1483
+ createdAt: new Date(),
1484
+ updatedAt: new Date(),
1485
+ };
1486
+ return this.saveResource({ resource: newResource });
1487
+ }
1488
+
1489
+ const updatedResource = {
1490
+ ...existingResource,
1491
+ workingMemory: workingMemory !== undefined ? workingMemory : existingResource.workingMemory,
1492
+ metadata: {
1493
+ ...existingResource.metadata,
1494
+ ...metadata,
1495
+ },
1496
+ updatedAt: new Date(),
1497
+ };
1498
+
1499
+ const updates: string[] = [];
1500
+ const values: InValue[] = [];
1501
+
1502
+ if (workingMemory !== undefined) {
1503
+ updates.push('workingMemory = ?');
1504
+ values.push(workingMemory);
1505
+ }
1506
+
1507
+ if (metadata) {
1508
+ updates.push('metadata = ?');
1509
+ values.push(JSON.stringify(updatedResource.metadata));
1510
+ }
1511
+
1512
+ updates.push('updatedAt = ?');
1513
+ values.push(updatedResource.updatedAt.toISOString());
1514
+
1515
+ values.push(resourceId);
1516
+
1517
+ await this.client.execute({
1518
+ sql: `UPDATE ${TABLE_RESOURCES} SET ${updates.join(', ')} WHERE id = ?`,
1519
+ args: values,
1520
+ });
1521
+
1522
+ return updatedResource;
1523
+ }
1524
+
1419
1525
  private async hasColumn(table: string, column: string): Promise<boolean> {
1420
1526
  const result = await this.client.execute({
1421
1527
  sql: `PRAGMA table_info(${table})`,
@@ -1,5 +1,6 @@
1
1
  import { describe, expect, it } from 'vitest';
2
2
 
3
+ import type { LibSQLVectorFilter } from './filter';
3
4
  import { LibSQLFilterTranslator } from './filter';
4
5
 
5
6
  describe('LibSQLFilterTranslator', () => {
@@ -17,14 +18,8 @@ describe('LibSQLFilterTranslator', () => {
17
18
  });
18
19
  });
19
20
 
20
- it('translates array to $in', () => {
21
- expect(translator.translate({ field: ['a', 'b'] })).toEqual({
22
- field: { $in: ['a', 'b'] },
23
- });
24
- });
25
-
26
21
  it('preserves comparison operators', () => {
27
- const filter = {
22
+ const filter: LibSQLVectorFilter = {
28
23
  field1: { $eq: 'value' },
29
24
  field2: { $ne: 'value' },
30
25
  field3: { $gt: 5 },
@@ -55,7 +50,7 @@ describe('LibSQLFilterTranslator', () => {
55
50
  nested: {
56
51
  field: 'value',
57
52
  },
58
- }),
53
+ } as any),
59
54
  ).toEqual({
60
55
  'nested.field': { $eq: 'value' },
61
56
  });
@@ -65,7 +60,8 @@ describe('LibSQLFilterTranslator', () => {
65
60
  // Array Operations
66
61
  describe('array operations', () => {
67
62
  it('translates array to $in', () => {
68
- expect(translator.translate({ field: ['a', 'b'] })).toEqual({
63
+ const filter: LibSQLVectorFilter = { field: ['a', 'b'] };
64
+ expect(translator.translate(filter)).toEqual({
69
65
  field: { $in: ['a', 'b'] },
70
66
  });
71
67
  });
@@ -82,7 +78,7 @@ describe('LibSQLFilterTranslator', () => {
82
78
  it('handles empty arrays', () => {
83
79
  expect(
84
80
  translator.translate({
85
- field: [],
81
+ field: [] as any,
86
82
  }),
87
83
  ).toEqual({
88
84
  field: { $in: [] },
@@ -128,7 +124,7 @@ describe('LibSQLFilterTranslator', () => {
128
124
  });
129
125
 
130
126
  // Array Operator Normalization
131
- describe('array operator normalization', () => {
127
+ describe('Array operator normalization', () => {
132
128
  it('normalizes single values for $all', () => {
133
129
  expect(
134
130
  translator.translate({
@@ -173,7 +169,7 @@ describe('LibSQLFilterTranslator', () => {
173
169
  // Logical Operators
174
170
  describe('Logical Operators', () => {
175
171
  it('handles logical operators', () => {
176
- const filter = {
172
+ const filter: LibSQLVectorFilter = {
177
173
  $and: [{ field1: { $eq: 'value1' } }, { field2: { $eq: 'value2' } }],
178
174
  $or: [{ field3: { $eq: 'value3' } }, { field4: { $eq: 'value4' } }],
179
175
  };
@@ -457,16 +453,6 @@ describe('LibSQLFilterTranslator', () => {
457
453
  field: { $eq: '' },
458
454
  });
459
455
  });
460
-
461
- it('handles empty arrays', () => {
462
- expect(
463
- translator.translate({
464
- field: [],
465
- }),
466
- ).toEqual({
467
- field: { $in: [] },
468
- });
469
- });
470
456
  });
471
457
 
472
458
  // Complex Filter Structures
@@ -540,53 +526,10 @@ describe('LibSQLFilterTranslator', () => {
540
526
  });
541
527
  });
542
528
 
543
- // Array Operator Normalization
544
- describe('Array Operator Normalization', () => {
545
- it('normalizes single values for $all', () => {
546
- expect(
547
- translator.translate({
548
- field: { $all: 'value' },
549
- }),
550
- ).toEqual({
551
- field: { $all: ['value'] },
552
- });
553
- });
554
-
555
- it('normalizes single values for $in', () => {
556
- expect(
557
- translator.translate({
558
- field: { $in: 'value' },
559
- }),
560
- ).toEqual({
561
- field: { $in: ['value'] },
562
- });
563
- });
564
-
565
- it('normalizes single values for $nin', () => {
566
- expect(
567
- translator.translate({
568
- field: { $nin: 'value' },
569
- }),
570
- ).toEqual({
571
- field: { $nin: ['value'] },
572
- });
573
- });
574
-
575
- it('preserves arrays for array operators', () => {
576
- expect(
577
- translator.translate({
578
- field: { $all: ['value1', 'value2'] },
579
- }),
580
- ).toEqual({
581
- field: { $all: ['value1', 'value2'] },
582
- });
583
- });
584
- });
585
-
586
529
  // Operator Support Validation
587
530
  describe('Operator Support Validation', () => {
588
531
  it('ensure all operator filters are supported', () => {
589
- const supportedFilters = [
532
+ const supportedFilters: LibSQLVectorFilter[] = [
590
533
  // Basic comparison operators
591
534
  { field: { $eq: 'value' } },
592
535
  { field: { $ne: 'value' } },
@@ -612,12 +555,7 @@ describe('LibSQLFilterTranslator', () => {
612
555
  { $or: [{ field1: 'value1' }, { field2: 'value2' }] },
613
556
  { $nor: [{ field1: 'value1' }, { field2: 'value2' }] },
614
557
 
615
- { $and: { field: 'value' } },
616
- { $or: { field: 'value' } },
617
- { $nor: { field: 'value' } },
618
- { $not: { field: 'value' } },
619
-
620
- { $or: [{ $and: { field1: 'value1' } }, { $not: { field2: 'value2' } }] },
558
+ { $or: [{ $and: [{ field1: 'value1' }] }, { $not: { field2: 'value2' } }] },
621
559
 
622
560
  { field: { $not: { $eq: 'value' } } },
623
561
  { field: { $not: { $in: ['value1', 'value2'] } } },
@@ -634,7 +572,7 @@ describe('LibSQLFilterTranslator', () => {
634
572
 
635
573
  it('throws error for $not if not an object', () => {
636
574
  expect(() => translator.translate({ $not: 'value' })).toThrow();
637
- expect(() => translator.translate({ $not: [{ field: 'value' }] })).toThrow();
575
+ expect(() => translator.translate({ $not: [{ field: 'value' }] } as any)).toThrow();
638
576
  });
639
577
  it('throws error for $not if empty', () => {
640
578
  expect(() => translator.translate({ $not: {} })).toThrow();
@@ -646,21 +584,21 @@ describe('LibSQLFilterTranslator', () => {
646
584
  expect(() =>
647
585
  translator.translate({
648
586
  field: { $and: [{ $eq: 'value1' }, { $eq: 'value2' }] },
649
- }),
587
+ } as any),
650
588
  ).toThrow();
651
589
 
652
590
  // $or cannot be used in field conditions
653
591
  expect(() =>
654
592
  translator.translate({
655
593
  field: { $or: [{ $eq: 'value1' }, { $eq: 'value2' }] },
656
- }),
594
+ } as any),
657
595
  ).toThrow();
658
596
 
659
597
  // $nor cannot be used in field conditions
660
598
  expect(() =>
661
599
  translator.translate({
662
600
  field: { $nor: [{ $eq: 'value1' }, { $eq: 'value2' }] },
663
- }),
601
+ } as any),
664
602
  ).toThrow();
665
603
  });
666
604
 
@@ -709,7 +647,7 @@ describe('LibSQLFilterTranslator', () => {
709
647
  field: {
710
648
  $gt: {
711
649
  $or: [{ subfield: 'value1' }, { subfield: 'value2' }],
712
- },
650
+ } as any,
713
651
  },
714
652
  }),
715
653
  ).toThrow();
@@ -723,17 +661,17 @@ describe('LibSQLFilterTranslator', () => {
723
661
  $and: [{ subfield: 'value1' }, { subfield: 'value2' }],
724
662
  },
725
663
  ],
726
- },
664
+ } as any,
727
665
  }),
728
666
  ).toThrow();
729
667
  });
730
668
 
731
669
  it('validates $not operator structure', () => {
732
670
  // $not must be an object
733
- expect(() => translator.translate({ field: { $not: 'value' } })).toThrow();
671
+ expect(() => translator.translate({ field: { $not: 'value' } } as any)).toThrow();
734
672
  expect(() => translator.translate({ field: { $not: ['value'] } })).toThrow();
735
673
  expect(() => translator.translate({ $not: 'value' })).toThrow();
736
- expect(() => translator.translate({ $not: ['value'] })).toThrow();
674
+ expect(() => translator.translate({ $not: ['value'] } as any)).toThrow();
737
675
  });
738
676
 
739
677
  it('validates $not operator nesting', () => {
@@ -793,7 +731,7 @@ describe('LibSQLFilterTranslator', () => {
793
731
  const invalidFilters = [{ $gt: 100 }, { $in: ['value1', 'value2'] }, { $eq: true }];
794
732
 
795
733
  invalidFilters.forEach(filter => {
796
- expect(() => translator.translate(filter)).toThrow(/Invalid top-level operator/);
734
+ expect(() => translator.translate(filter as any)).toThrow(/Invalid top-level operator/);
797
735
  });
798
736
  });
799
737
  it('allows logical operators at top level', () => {
@@ -815,13 +753,13 @@ describe('LibSQLFilterTranslator', () => {
815
753
  // Should throw for non-object values
816
754
  expect(() =>
817
755
  translator.translate({
818
- field: { $elemMatch: 'value' },
756
+ field: { $elemMatch: 'value' } as any,
819
757
  }),
820
758
  ).toThrow('$elemMatch requires an object with conditions');
821
759
 
822
760
  expect(() =>
823
761
  translator.translate({
824
- field: { $elemMatch: ['value'] },
762
+ field: { $elemMatch: ['value'] } as any,
825
763
  }),
826
764
  ).toThrow('$elemMatch requires an object with conditions');
827
765
  });
@@ -962,7 +900,7 @@ describe('LibSQLFilterTranslator', () => {
962
900
  // Unsupported Operations
963
901
  describe('unsupported operators', () => {
964
902
  it('throws on unsupported operators', () => {
965
- expect(() => translator.translate({ field: { $regex: 'value' } })).toThrow();
903
+ expect(() => translator.translate({ field: { $regex: 'value' } } as any)).toThrow();
966
904
  });
967
905
  });
968
906
  });
@@ -1,5 +1,19 @@
1
1
  import { BaseFilterTranslator } from '@mastra/core/vector/filter';
2
- import type { FieldCondition, VectorFilter, OperatorSupport } from '@mastra/core/vector/filter';
2
+ import type { VectorFilter, OperatorSupport, OperatorValueMap, VectorFieldValue } from '@mastra/core/vector/filter';
3
+
4
+ type LibSQLOperatorValueMap = Omit<
5
+ OperatorValueMap,
6
+ '$regex' | '$options' | '$in' | '$all' | '$nin' | '$eq' | '$ne'
7
+ > & {
8
+ $size: number;
9
+ $contains: VectorFieldValue | Record<string, unknown>;
10
+ $all: VectorFieldValue;
11
+ $in: VectorFieldValue;
12
+ $nin: VectorFieldValue;
13
+ $eq: VectorFieldValue;
14
+ $ne: VectorFieldValue;
15
+ };
16
+ export type LibSQLVectorFilter = VectorFilter<keyof LibSQLOperatorValueMap, LibSQLOperatorValueMap>;
3
17
 
4
18
  /**
5
19
  * Translates MongoDB-style filters to LibSQL compatible filters.
@@ -11,7 +25,7 @@ import type { FieldCondition, VectorFilter, OperatorSupport } from '@mastra/core
11
25
  * - Can take either a single condition or an array of conditions
12
26
  *
13
27
  */
14
- export class LibSQLFilterTranslator extends BaseFilterTranslator {
28
+ export class LibSQLFilterTranslator extends BaseFilterTranslator<LibSQLVectorFilter> {
15
29
  protected override getSupportedOperators(): OperatorSupport {
16
30
  return {
17
31
  ...BaseFilterTranslator.DEFAULT_OPERATORS,
@@ -20,7 +34,7 @@ export class LibSQLFilterTranslator extends BaseFilterTranslator {
20
34
  };
21
35
  }
22
36
 
23
- translate(filter?: VectorFilter): VectorFilter {
37
+ translate(filter?: LibSQLVectorFilter): LibSQLVectorFilter {
24
38
  if (this.isEmpty(filter)) {
25
39
  return filter;
26
40
  }
@@ -28,7 +42,7 @@ export class LibSQLFilterTranslator extends BaseFilterTranslator {
28
42
  return this.translateNode(filter);
29
43
  }
30
44
 
31
- private translateNode(node: VectorFilter | FieldCondition, currentPath: string = ''): any {
45
+ private translateNode(node: LibSQLVectorFilter, currentPath: string = ''): any {
32
46
  if (this.isRegex(node)) {
33
47
  throw new Error('Direct regex pattern format is not supported in LibSQL');
34
48
  }
@@ -74,7 +88,7 @@ export class LibSQLFilterTranslator extends BaseFilterTranslator {
74
88
 
75
89
  if (this.isLogicalOperator(key)) {
76
90
  result[key] = Array.isArray(value)
77
- ? value.map((filter: VectorFilter) => this.translateNode(filter))
91
+ ? value.map((filter: LibSQLVectorFilter) => this.translateNode(filter))
78
92
  : this.translateNode(value);
79
93
  } else if (this.isOperator(key)) {
80
94
  if (this.isArrayOperator(key) && !Array.isArray(value) && key !== '$elemMatch') {
@@ -1130,7 +1130,7 @@ describe('LibSQLVector', () => {
1130
1130
  vectorDB.query({
1131
1131
  indexName,
1132
1132
  queryVector: [1, 0, 0],
1133
- filter: { price: { $invalid: 100 } },
1133
+ filter: { price: { $invalid: 100 } } as any,
1134
1134
  }),
1135
1135
  ).rejects.toThrow('Unsupported operator: $invalid');
1136
1136
  });
@@ -1360,16 +1360,6 @@ describe('LibSQLVector', () => {
1360
1360
  });
1361
1361
  });
1362
1362
 
1363
- it('should throw error for invalid operator', async () => {
1364
- await expect(
1365
- vectorDB.query({
1366
- indexName,
1367
- queryVector: [1, 0, 0],
1368
- filter: { price: { $invalid: 100 } },
1369
- }),
1370
- ).rejects.toThrow('Unsupported operator: $invalid');
1371
- });
1372
-
1373
1363
  it('should handle multiple logical operators at root level', async () => {
1374
1364
  const results = await vectorDB.query({
1375
1365
  indexName,
@@ -15,11 +15,11 @@ import type {
15
15
  DeleteVectorParams,
16
16
  UpdateVectorParams,
17
17
  } from '@mastra/core/vector';
18
- import type { VectorFilter } from '@mastra/core/vector/filter';
18
+ import type { LibSQLVectorFilter } from './filter';
19
19
  import { LibSQLFilterTranslator } from './filter';
20
20
  import { buildFilterQuery } from './sql-builder';
21
21
 
22
- interface LibSQLQueryVectorParams extends QueryVectorParams {
22
+ interface LibSQLQueryVectorParams extends QueryVectorParams<LibSQLVectorFilter> {
23
23
  minScore?: number;
24
24
  }
25
25
 
@@ -41,7 +41,7 @@ export interface LibSQLVectorConfig {
41
41
  initialBackoffMs?: number;
42
42
  }
43
43
 
44
- export class LibSQLVector extends MastraVector {
44
+ export class LibSQLVector extends MastraVector<LibSQLVectorFilter> {
45
45
  private turso: TursoClient;
46
46
  private readonly maxRetries: number;
47
47
  private readonly initialBackoffMs: number;
@@ -109,7 +109,7 @@ export class LibSQLVector extends MastraVector {
109
109
  throw new Error('LibSQLVector: Max retries reached, but no error was re-thrown from the loop.');
110
110
  }
111
111
 
112
- transformFilter(filter?: VectorFilter) {
112
+ transformFilter(filter?: LibSQLVectorFilter) {
113
113
  const translator = new LibSQLFilterTranslator();
114
114
  return translator.translate(filter);
115
115
  }
@@ -6,8 +6,8 @@ import type {
6
6
  ArrayOperator,
7
7
  ElementOperator,
8
8
  LogicalOperator,
9
- VectorFilter,
10
9
  } from '@mastra/core/vector/filter';
10
+ import type { LibSQLVectorFilter } from './filter';
11
11
 
12
12
  type OperatorType =
13
13
  | BasicOperator
@@ -372,7 +372,7 @@ function escapeLikePattern(str: string): string {
372
372
  return str.replace(/([%_\\])/g, '\\$1');
373
373
  }
374
374
 
375
- export function buildFilterQuery(filter: VectorFilter): FilterResult {
375
+ export function buildFilterQuery(filter: LibSQLVectorFilter): FilterResult {
376
376
  if (!filter) {
377
377
  return { sql: '', values: [] };
378
378
  }
@@ -428,11 +428,11 @@ function buildCondition(key: string, value: any, parentPath: string): FilterResu
428
428
 
429
429
  function handleLogicalOperator(
430
430
  key: '$and' | '$or' | '$not' | '$nor',
431
- value: VectorFilter[] | VectorFilter,
431
+ value: LibSQLVectorFilter[] | LibSQLVectorFilter,
432
432
  parentPath: string,
433
433
  ): FilterResult {
434
434
  // Handle empty conditions
435
- if (!value || value.length === 0) {
435
+ if (!value || (Array.isArray(value) && value.length === 0)) {
436
436
  switch (key) {
437
437
  case '$and':
438
438
  case '$nor':
@@ -460,7 +460,7 @@ function handleLogicalOperator(
460
460
  const joinOperator = key === '$or' || key === '$nor' ? 'OR' : 'AND';
461
461
  const conditions = Array.isArray(value)
462
462
  ? value.map(f => {
463
- const entries = Object.entries(f);
463
+ const entries = !!f ? Object.entries(f) : [];
464
464
  return entries.map(([k, v]) => buildCondition(k, v, key));
465
465
  })
466
466
  : [buildCondition(key, value, parentPath)];