@mastra/lance 1.0.3 → 1.0.4-alpha.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/CHANGELOG.md CHANGED
@@ -1,5 +1,18 @@
1
1
  # @mastra/lance
2
2
 
3
+ ## 1.0.4-alpha.0
4
+
5
+ ### Patch Changes
6
+
7
+ - Added resilient column handling to insert and update operations. Unknown columns in records are now silently dropped instead of causing SQL errors, ensuring forward compatibility when newer domain packages add fields that haven't been migrated yet. ([#14021](https://github.com/mastra-ai/mastra/pull/14021))
8
+
9
+ For example, calling `db.insert({ tableName, record: { id: '1', title: 'Hello', futureField: 'value' } })` will silently ignore `futureField` if it doesn't exist in the database table, rather than throwing. The same applies to `update` — unknown fields in the data payload are dropped before building the SQL statement.
10
+
11
+ - Improved semantic recall performance for large message histories. Semantic recall no longer loads entire threads when only the recalled messages are needed, eliminating delays that previously scaled with total message count. (Fixes #11702) ([#14022](https://github.com/mastra-ai/mastra/pull/14022))
12
+
13
+ - Updated dependencies [[`4f71b43`](https://github.com/mastra-ai/mastra/commit/4f71b436a4a6b8839842d8da47b57b84509af56c), [`a070277`](https://github.com/mastra-ai/mastra/commit/a07027766ce195ba74d0783116d894cbab25d44c), [`b628b91`](https://github.com/mastra-ai/mastra/commit/b628b9128b372c0f54214d902b07279f03443900), [`332c014`](https://github.com/mastra-ai/mastra/commit/332c014e076b81edf7fe45b58205882726415e90), [`6b63153`](https://github.com/mastra-ai/mastra/commit/6b63153878ea841c0f4ce632ba66bb33e57e9c1b), [`4246e34`](https://github.com/mastra-ai/mastra/commit/4246e34cec9c26636d0965942268e6d07c346671), [`b8837ee`](https://github.com/mastra-ai/mastra/commit/b8837ee77e2e84197609762bfabd8b3da326d30c), [`5d950f7`](https://github.com/mastra-ai/mastra/commit/5d950f7bf426a215a1808f0abef7de5c8336ba1c), [`28c85b1`](https://github.com/mastra-ai/mastra/commit/28c85b184fc32b40f7f160483c982da6d388ecbd), [`e9a08fb`](https://github.com/mastra-ai/mastra/commit/e9a08fbef1ada7e50e961e2f54f55e8c10b4a45c), [`631ffd8`](https://github.com/mastra-ai/mastra/commit/631ffd82fed108648b448b28e6a90e38c5f53bf5), [`aae2295`](https://github.com/mastra-ai/mastra/commit/aae2295838a2d329ad6640829e87934790ffe5b8), [`aa61f29`](https://github.com/mastra-ai/mastra/commit/aa61f29ff8095ce46a4ae16e46c4d8c79b2b685b), [`7ff3714`](https://github.com/mastra-ai/mastra/commit/7ff37148515439bb3be009a60e02c3e363299760), [`41d79a1`](https://github.com/mastra-ai/mastra/commit/41d79a14bd8cb6de1e2565fd0a04786bae2f211b), [`e673376`](https://github.com/mastra-ai/mastra/commit/e6733763ad1321aa7e5ae15096b9c2104f93b1f3), [`b2204c9`](https://github.com/mastra-ai/mastra/commit/b2204c98a42848bbfb6f0440f005dc2b6354f1cd), [`a1bf1e3`](https://github.com/mastra-ai/mastra/commit/a1bf1e385ed4c0ef6f11b56c5887442970d127f2), [`b6f647a`](https://github.com/mastra-ai/mastra/commit/b6f647ae2388e091f366581595feb957e37d5b40), [`0c57b8b`](https://github.com/mastra-ai/mastra/commit/0c57b8b0a69a97b5a4ae3f79be6c610f29f3cf7b), [`b081f27`](https://github.com/mastra-ai/mastra/commit/b081f272cf411716e1d6bd72ceac4bcee2657b19), [`0c09eac`](https://github.com/mastra-ai/mastra/commit/0c09eacb1926f64cfdc9ae5c6d63385cf8c9f72c), [`6b9b93d`](https://github.com/mastra-ai/mastra/commit/6b9b93d6f459d1ba6e36f163abf62a085ddb3d64), [`31b6067`](https://github.com/mastra-ai/mastra/commit/31b6067d0cc3ab10e1b29c36147f3b5266bc714a), [`797ac42`](https://github.com/mastra-ai/mastra/commit/797ac4276de231ad2d694d9aeca75980f6cd0419), [`0bc289e`](https://github.com/mastra-ai/mastra/commit/0bc289e2d476bf46c5b91c21969e8d0c6864691c), [`9b75a06`](https://github.com/mastra-ai/mastra/commit/9b75a06e53ebb0b950ba7c1e83a0142047185f46), [`4c3a1b1`](https://github.com/mastra-ai/mastra/commit/4c3a1b122ea083e003d71092f30f3b31680b01c0), [`85cc3b3`](https://github.com/mastra-ai/mastra/commit/85cc3b3b6f32ae4b083c26498f50d5b250ba944b), [`97ea28c`](https://github.com/mastra-ai/mastra/commit/97ea28c746e9e4147d56047bbb1c4a92417a3fec), [`d567299`](https://github.com/mastra-ai/mastra/commit/d567299cf81e02bd9d5221d4bc05967d6c224161), [`716ffe6`](https://github.com/mastra-ai/mastra/commit/716ffe68bed81f7c2690bc8581b9e140f7bf1c3d), [`8296332`](https://github.com/mastra-ai/mastra/commit/8296332de21c16e3dfc3d0b2d615720a6dc88f2f), [`4df2116`](https://github.com/mastra-ai/mastra/commit/4df211619dd922c047d396ca41cd7027c8c4c8e7), [`2219c1a`](https://github.com/mastra-ai/mastra/commit/2219c1acbd21da116da877f0036ffb985a9dd5a3), [`17c4145`](https://github.com/mastra-ai/mastra/commit/17c4145166099354545582335b5252bdfdfd908b)]:
14
+ - @mastra/core@1.11.0-alpha.0
15
+
3
16
  ## 1.0.3
4
17
 
5
18
  ### Patch Changes
@@ -3,7 +3,7 @@ name: mastra-lance
3
3
  description: Documentation for @mastra/lance. Use when working with @mastra/lance APIs, configuration, or implementation.
4
4
  metadata:
5
5
  package: "@mastra/lance"
6
- version: "1.0.3"
6
+ version: "1.0.4-alpha.0"
7
7
  ---
8
8
 
9
9
  ## When to use
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "1.0.3",
2
+ "version": "1.0.4-alpha.0",
3
3
  "package": "@mastra/lance",
4
4
  "exports": {},
5
5
  "modules": {}
@@ -359,7 +359,7 @@ The dimension size must match the output dimension of your chosen embedding mode
359
359
  - Cohere embed-multilingual-v3: 1024 dimensions
360
360
  - Google gemini-embedding-001: 768 dimensions (or custom)
361
361
 
362
- > **Warning:** Index dimensions cannot be changed after creation. To use a different model, delete and recreate the index with the new dimension size.
362
+ > **Warning:** Index dimensions can't be changed after creation. To use a different model, delete and recreate the index with the new dimension size.
363
363
 
364
364
  ### Naming Rules for Databases
365
365
 
@@ -583,7 +583,7 @@ Key metadata considerations:
583
583
 
584
584
  ## Deleting Vectors
585
585
 
586
- When building RAG applications, you often need to clean up stale vectors when documents are deleted or updated. Mastra provides the `deleteVectors` method that supports deleting vectors by metadata filters, making it easy to remove all embeddings associated with a specific document.
586
+ When building RAG applications, you often need to clean up stale vectors when documents are deleted or updated. Mastra provides the `deleteVectors` method that supports deleting vectors by metadata filters, making it straightforward to remove all embeddings associated with a specific document.
587
587
 
588
588
  ### Delete by Metadata Filter
589
589
 
@@ -2,7 +2,7 @@
2
2
 
3
3
  The LanceDB storage implementation provides a high-performance storage solution using the LanceDB database system, which excels at handling both traditional data storage and vector operations.
4
4
 
5
- > **Observability Not Supported:** LanceDB storage **does not support the observability domain**. Traces from the `DefaultExporter` cannot be persisted to LanceDB, and Mastra Studio's observability features won't work with LanceDB as your only storage provider. To enable observability, use [composite storage](https://mastra.ai/reference/storage/composite) to route observability data to a supported provider like ClickHouse or PostgreSQL.
5
+ > **Observability Not Supported:** LanceDB storage **doesn't support the observability domain**. Traces from the `DefaultExporter` can't be persisted to LanceDB, and Mastra Studio's observability features won't work with LanceDB as your only storage provider. To enable observability, use [composite storage](https://mastra.ai/reference/storage/composite) to route observability data to a supported provider like ClickHouse or PostgreSQL.
6
6
 
7
7
  ## Installation
8
8
 
@@ -53,11 +53,11 @@ const storage = await LanceStorage.create('my-storage', 's3://bucket/db', {
53
53
 
54
54
  ### LanceStorage.create()
55
55
 
56
- **name:** (`string`): Name identifier for the storage instance
56
+ **name** (`string`): Name identifier for the storage instance
57
57
 
58
- **uri:** (`string`): URI to connect to the LanceDB database. Can be a local path, cloud DB URL, or S3 bucket URL
58
+ **uri** (`string`): URI to connect to the LanceDB database. Can be a local path, cloud DB URL, or S3 bucket URL
59
59
 
60
- **options?:** (`ConnectionOptions`): Connection options for LanceDB, such as timeout settings, authentication, etc.
60
+ **options** (`ConnectionOptions`): Connection options for LanceDB, such as timeout settings, authentication, etc.
61
61
 
62
62
  ## Additional Notes
63
63
 
@@ -101,7 +101,7 @@ const memoryStore = await storage.getStore('memory')
101
101
  const thread = await memoryStore?.getThreadById({ threadId: '...' })
102
102
  ```
103
103
 
104
- > **Warning:** If `init()` is not called, tables won't be created and storage operations will fail silently or throw errors.
104
+ > **Warning:** If `init()` isn't called, tables won't be created and storage operations will fail silently or throw errors.
105
105
 
106
106
  ### Deployment Options
107
107
 
@@ -6,9 +6,9 @@ The LanceVectorStore class provides vector search using [LanceDB](https://lanced
6
6
 
7
7
  The LanceVectorStore uses a factory pattern for creation. You should use the static `create()` method rather than the constructor directly.
8
8
 
9
- **uri:** (`string`): Path to LanceDB database or URI for cloud deployments
9
+ **uri** (`string`): Path to LanceDB database or URI for cloud deployments
10
10
 
11
- **options?:** (`ConnectionOptions`): Additional connection options for LanceDB
11
+ **options** (`ConnectionOptions`): Additional connection options for LanceDB
12
12
 
13
13
  ## Constructor Examples
14
14
 
@@ -33,59 +33,67 @@ const s3Store = await LanceVectorStore.create('s3://bucket/db', {
33
33
 
34
34
  ### createIndex()
35
35
 
36
- **tableName:** (`string`): Name of the table to create index in
36
+ **tableName** (`string`): Name of the table to create index in
37
37
 
38
- **indexName:** (`string`): Name of the index (column name) to create
38
+ **indexName** (`string`): Name of the index (column name) to create
39
39
 
40
- **dimension:** (`number`): Vector dimension (must match your embedding model)
40
+ **dimension** (`number`): Vector dimension (must match your embedding model)
41
41
 
42
- **metric?:** (`'cosine' | 'euclidean' | 'dotproduct'`): Distance metric for similarity search (Default: `cosine`)
42
+ **metric** (`'cosine' | 'euclidean' | 'dotproduct'`): Distance metric for similarity search (Default: `cosine`)
43
43
 
44
- **indexConfig?:** (`LanceIndexConfig`): Index configuration (Default: `{ type: 'hnsw' }`)
44
+ **indexConfig** (`LanceIndexConfig`): Index configuration (Default: `{ type: 'hnsw' }`)
45
45
 
46
46
  #### LanceIndexConfig
47
47
 
48
- **type:** (`'ivfflat' | 'hnsw'`): stringivfflat:ivfflatClusters vectors into lists for approximate search.hnsw:hnswGraph-based index offering fast search times and high recall. (Default: `hnsw`)
48
+ **type** (`'ivfflat' | 'hnsw'`): Index type (Default: `hnsw`)
49
49
 
50
- **numPartitions?:** (`number`): Number of partitions for IVF indexes (Default: `128`)
50
+ **type.ivfflat** (`ivfflat`): Clusters vectors into lists for approximate search.
51
51
 
52
- **numSubVectors?:** (`number`): Number of sub-vectors for product quantization (Default: `16`)
52
+ **type.hnsw** (`hnsw`): Graph-based index offering fast search times and high recall.
53
53
 
54
- **hnsw?:** (`HNSWConfig`): objectm?:numberMaximum number of connections per node (default: 16)efConstruction?:numberBuild-time complexity (default: 100)
54
+ **numPartitions** (`number`): Number of partitions for IVF indexes (Default: `128`)
55
+
56
+ **numSubVectors** (`number`): Number of sub-vectors for product quantization (Default: `16`)
57
+
58
+ **hnsw** (`HNSWConfig`): HNSW configuration
59
+
60
+ **hnsw\.m** (`number`): Maximum number of connections per node (default: 16)
61
+
62
+ **hnsw\.efConstruction** (`number`): Build-time complexity (default: 100)
55
63
 
56
64
  ### createTable()
57
65
 
58
- **tableName:** (`string`): Name of the table to create
66
+ **tableName** (`string`): Name of the table to create
59
67
 
60
- **data:** (`Record<string, unknown>[] | TableLike`): Initial data for the table
68
+ **data** (`Record<string, unknown>[] | TableLike`): Initial data for the table
61
69
 
62
- **options?:** (`Partial<CreateTableOptions>`): Additional table creation options
70
+ **options** (`Partial<CreateTableOptions>`): Additional table creation options
63
71
 
64
72
  ### upsert()
65
73
 
66
- **tableName:** (`string`): Name of the table to upsert vectors into
74
+ **tableName** (`string`): Name of the table to upsert vectors into
67
75
 
68
- **vectors:** (`number[][]`): Array of embedding vectors
76
+ **vectors** (`number[][]`): Array of embedding vectors
69
77
 
70
- **metadata?:** (`Record<string, any>[]`): Metadata for each vector
78
+ **metadata** (`Record<string, any>[]`): Metadata for each vector
71
79
 
72
- **ids?:** (`string[]`): Optional vector IDs (auto-generated if not provided)
80
+ **ids** (`string[]`): Optional vector IDs (auto-generated if not provided)
73
81
 
74
82
  ### query()
75
83
 
76
- **tableName:** (`string`): Name of the table to query
84
+ **tableName** (`string`): Name of the table to query
77
85
 
78
- **queryVector:** (`number[]`): Query vector
86
+ **queryVector** (`number[]`): Query vector
79
87
 
80
- **topK?:** (`number`): Number of results to return (Default: `10`)
88
+ **topK** (`number`): Number of results to return (Default: `10`)
81
89
 
82
- **filter?:** (`Record<string, any>`): Metadata filters
90
+ **filter** (`Record<string, any>`): Metadata filters
83
91
 
84
- **includeVector?:** (`boolean`): Whether to include the vector in the result (Default: `false`)
92
+ **includeVector** (`boolean`): Whether to include the vector in the result (Default: `false`)
85
93
 
86
- **columns?:** (`string[]`): Specific columns to include in the result (Default: `[]`)
94
+ **columns** (`string[]`): Specific columns to include in the result (Default: `[]`)
87
95
 
88
- **includeAllColumns?:** (`boolean`): Whether to include all columns in the result (Default: `false`)
96
+ **includeAllColumns** (`boolean`): Whether to include all columns in the result (Default: `false`)
89
97
 
90
98
  ### listTables()
91
99
 
@@ -98,13 +106,13 @@ const tables = await vectorStore.listTables()
98
106
 
99
107
  ### getTableSchema()
100
108
 
101
- **tableName:** (`string`): Name of the table to describe
109
+ **tableName** (`string`): Name of the table to describe
102
110
 
103
111
  Returns the schema of the specified table.
104
112
 
105
113
  ### deleteTable()
106
114
 
107
- **tableName:** (`string`): Name of the table to delete
115
+ **tableName** (`string`): Name of the table to delete
108
116
 
109
117
  ### deleteAllTables()
110
118
 
@@ -116,7 +124,7 @@ Returns an array of index names as strings.
116
124
 
117
125
  ### describeIndex()
118
126
 
119
- **indexName:** (`string`): Name of the index to describe
127
+ **indexName** (`string`): Name of the index to describe
120
128
 
121
129
  Returns information about the index:
122
130
 
@@ -137,35 +145,35 @@ interface IndexStats {
137
145
 
138
146
  ### deleteIndex()
139
147
 
140
- **indexName:** (`string`): Name of the index to delete
148
+ **indexName** (`string`): Name of the index to delete
141
149
 
142
150
  ### updateVector()
143
151
 
144
152
  Update a single vector by ID or by metadata filter. Either `id` or `filter` must be provided, but not both.
145
153
 
146
- **indexName:** (`string`): Name of the index containing the vector
154
+ **indexName** (`string`): Name of the index containing the vector
147
155
 
148
- **id?:** (`string`): ID of the vector to update (mutually exclusive with filter)
156
+ **id** (`string`): ID of the vector to update (mutually exclusive with filter)
149
157
 
150
- **filter?:** (`Record<string, any>`): Metadata filter to identify vector(s) to update (mutually exclusive with id)
158
+ **filter** (`Record<string, any>`): Metadata filter to identify vector(s) to update (mutually exclusive with id)
151
159
 
152
- **update:** (`{ vector?: number[]; metadata?: Record<string, any>; }`): Object containing the vector and/or metadata to update
160
+ **update** (`{ vector?: number[]; metadata?: Record<string, any>; }`): Object containing the vector and/or metadata to update
153
161
 
154
162
  ### deleteVector()
155
163
 
156
- **indexName:** (`string`): Name of the index containing the vector
164
+ **indexName** (`string`): Name of the index containing the vector
157
165
 
158
- **id:** (`string`): ID of the vector to delete
166
+ **id** (`string`): ID of the vector to delete
159
167
 
160
168
  ### deleteVectors()
161
169
 
162
170
  Delete multiple vectors by IDs or by metadata filter. Either `ids` or `filter` must be provided, but not both.
163
171
 
164
- **indexName:** (`string`): Name of the index containing the vectors to delete
172
+ **indexName** (`string`): Name of the index containing the vectors to delete
165
173
 
166
- **ids?:** (`string[]`): Array of vector IDs to delete (mutually exclusive with filter)
174
+ **ids** (`string[]`): Array of vector IDs to delete (mutually exclusive with filter)
167
175
 
168
- **filter?:** (`Record<string, any>`): Metadata filter to identify vectors to delete (mutually exclusive with ids)
176
+ **filter** (`Record<string, any>`): Metadata filter to identify vectors to delete (mutually exclusive with ids)
169
177
 
170
178
  ### close()
171
179
 
package/dist/index.cjs CHANGED
@@ -131,10 +131,52 @@ function resolveLanceConfig(config) {
131
131
  }
132
132
  var LanceDB = class extends base.MastraBase {
133
133
  client;
134
+ /** Cache of actual table columns: tableName -> Set<columnName> */
135
+ /** Cache of actual table columns: tableName -> Promise<Set<columnName>> (stores in-flight promise to coalesce concurrent calls) */
136
+ tableColumnsCache = /* @__PURE__ */ new Map();
134
137
  constructor({ client }) {
135
138
  super({ name: "lance-db" });
136
139
  this.client = client;
137
140
  }
141
+ /**
142
+ * Gets the set of column names that actually exist in the database table.
143
+ * Results are cached; the cache is invalidated when alterTable() adds new columns.
144
+ */
145
+ async getTableColumns(tableName) {
146
+ const cached = this.tableColumnsCache.get(tableName);
147
+ if (cached) return cached;
148
+ const promise = (async () => {
149
+ try {
150
+ const table = await this.client.openTable(tableName);
151
+ const schema = await table.schema();
152
+ const columns = new Set(schema.fields.map((f) => f.name));
153
+ if (columns.size === 0) {
154
+ this.tableColumnsCache.delete(tableName);
155
+ }
156
+ return columns;
157
+ } catch {
158
+ this.tableColumnsCache.delete(tableName);
159
+ return /* @__PURE__ */ new Set();
160
+ }
161
+ })();
162
+ this.tableColumnsCache.set(tableName, promise);
163
+ return promise;
164
+ }
165
+ /**
166
+ * Filters a record to only include columns that exist in the actual database table.
167
+ * Unknown columns are silently dropped to ensure forward compatibility.
168
+ */
169
+ async filterRecordToKnownColumns(tableName, record) {
170
+ const knownColumns = await this.getTableColumns(tableName);
171
+ if (knownColumns.size === 0) return record;
172
+ const filtered = {};
173
+ for (const [key, value] of Object.entries(record)) {
174
+ if (knownColumns.has(key)) {
175
+ filtered[key] = value;
176
+ }
177
+ }
178
+ return filtered;
179
+ }
138
180
  getDefaultValue(type) {
139
181
  switch (type) {
140
182
  case "text":
@@ -234,6 +276,8 @@ var LanceDB = class extends base.MastraBase {
234
276
  },
235
277
  error$1
236
278
  );
279
+ } finally {
280
+ this.tableColumnsCache.delete(tableName);
237
281
  }
238
282
  }
239
283
  async dropTable({ tableName }) {
@@ -272,6 +316,8 @@ var LanceDB = class extends base.MastraBase {
272
316
  },
273
317
  error$1
274
318
  );
319
+ } finally {
320
+ this.tableColumnsCache.delete(tableName);
275
321
  }
276
322
  }
277
323
  async alterTable({
@@ -338,6 +384,8 @@ var LanceDB = class extends base.MastraBase {
338
384
  },
339
385
  error$1
340
386
  );
387
+ } finally {
388
+ this.tableColumnsCache.delete(tableName);
341
389
  }
342
390
  }
343
391
  async clearTable({ tableName }) {
@@ -408,7 +456,9 @@ var LanceDB = class extends base.MastraBase {
408
456
  processedRecord[key] = JSON.stringify(processedRecord[key]);
409
457
  }
410
458
  }
411
- await table.mergeInsert(primaryId).whenMatchedUpdateAll().whenNotMatchedInsertAll().execute([processedRecord]);
459
+ const filteredRecord = await this.filterRecordToKnownColumns(tableName, processedRecord);
460
+ if (Object.keys(filteredRecord).length === 0) return;
461
+ await table.mergeInsert(primaryId).whenMatchedUpdateAll().whenNotMatchedInsertAll().execute([filteredRecord]);
412
462
  } catch (error$1) {
413
463
  throw new error.MastraError(
414
464
  {
@@ -457,7 +507,12 @@ var LanceDB = class extends base.MastraBase {
457
507
  }
458
508
  return processedRecord;
459
509
  });
460
- await table.mergeInsert(primaryId).whenMatchedUpdateAll().whenNotMatchedInsertAll().execute(processedRecords);
510
+ const filteredRecords = await Promise.all(
511
+ processedRecords.map((r) => this.filterRecordToKnownColumns(tableName, r))
512
+ );
513
+ const nonEmptyRecords = filteredRecords.filter((r) => Object.keys(r).length > 0);
514
+ if (nonEmptyRecords.length === 0) return;
515
+ await table.mergeInsert(primaryId).whenMatchedUpdateAll().whenNotMatchedInsertAll().execute(nonEmptyRecords);
461
516
  } catch (error$1) {
462
517
  throw new error.MastraError(
463
518
  {
@@ -808,6 +863,20 @@ var StoreMemoryLance = class extends storage.MemoryStorage {
808
863
  conditions.push(`\`createdAt\` ${endOp} ${endTime}`);
809
864
  }
810
865
  const whereClause = conditions.join(" AND ");
866
+ if (perPage === 0 && (!include || include.length === 0)) {
867
+ return { messages: [], total: 0, page, perPage: perPageForResponse, hasMore: false };
868
+ }
869
+ if (perPage === 0 && include && include.length > 0) {
870
+ const includedMessages = await this._getIncludedMessages(table, include);
871
+ const list2 = new agent.MessageList().add(includedMessages, "memory");
872
+ return {
873
+ messages: this._sortMessages(list2.get.all.db(), field, direction),
874
+ total: 0,
875
+ page,
876
+ perPage: perPageForResponse,
877
+ hasMore: false
878
+ };
879
+ }
811
880
  const total = await table.countRows(whereClause);
812
881
  const query = table.query().where(whereClause);
813
882
  let allRecords = await query.toArray();
@@ -835,16 +904,7 @@ var StoreMemoryLance = class extends storage.MemoryStorage {
835
904
  }
836
905
  const messageIds = new Set(messages.map((m) => m.id));
837
906
  if (include && include.length > 0) {
838
- const threadIds2 = [...new Set(include.map((item) => item.threadId || threadId))];
839
- const allThreadMessages = [];
840
- for (const tid of threadIds2) {
841
- const threadQuery = table.query().where(`thread_id = '${tid}'`);
842
- let threadRecords = await threadQuery.toArray();
843
- allThreadMessages.push(...threadRecords);
844
- }
845
- allThreadMessages.sort((a, b) => a.createdAt - b.createdAt);
846
- const contextMessages = this.processMessagesWithContext(allThreadMessages, include);
847
- const includedMessages = contextMessages.map((row) => this.normalizeMessage(row));
907
+ const includedMessages = await this._getIncludedMessages(table, include);
848
908
  for (const includeMsg of includedMessages) {
849
909
  if (!messageIds.has(includeMsg.id)) {
850
910
  messages.push(includeMsg);
@@ -854,17 +914,7 @@ var StoreMemoryLance = class extends storage.MemoryStorage {
854
914
  }
855
915
  const list = new agent.MessageList().add(messages, "memory");
856
916
  let finalMessages = list.get.all.db();
857
- finalMessages = finalMessages.sort((a, b) => {
858
- const aValue = field === "createdAt" ? new Date(a.createdAt).getTime() : a[field];
859
- const bValue = field === "createdAt" ? new Date(b.createdAt).getTime() : b[field];
860
- if (aValue == null && bValue == null) return 0;
861
- if (aValue == null) return direction === "ASC" ? -1 : 1;
862
- if (bValue == null) return direction === "ASC" ? 1 : -1;
863
- if (typeof aValue === "string" && typeof bValue === "string") {
864
- return direction === "ASC" ? aValue.localeCompare(bValue) : bValue.localeCompare(aValue);
865
- }
866
- return direction === "ASC" ? aValue - bValue : bValue - aValue;
867
- });
917
+ finalMessages = this._sortMessages(finalMessages, field, direction);
868
918
  const returnedThreadMessageIds = new Set(finalMessages.filter((m) => m.threadId === threadId).map((m) => m.id));
869
919
  const allThreadMessagesReturned = returnedThreadMessageIds.size >= total;
870
920
  const fetchedAll = perPageInput === false || allThreadMessagesReturned;
@@ -1043,6 +1093,62 @@ var StoreMemoryLance = class extends storage.MemoryStorage {
1043
1093
  );
1044
1094
  }
1045
1095
  }
1096
+ _sortMessages(messages, field, direction) {
1097
+ return messages.sort((a, b) => {
1098
+ const aValue = field === "createdAt" ? new Date(a.createdAt).getTime() : a[field];
1099
+ const bValue = field === "createdAt" ? new Date(b.createdAt).getTime() : b[field];
1100
+ if (aValue == null && bValue == null) return 0;
1101
+ if (aValue == null) return direction === "ASC" ? -1 : 1;
1102
+ if (bValue == null) return direction === "ASC" ? 1 : -1;
1103
+ if (typeof aValue === "string" && typeof bValue === "string") {
1104
+ return direction === "ASC" ? aValue.localeCompare(bValue) : bValue.localeCompare(aValue);
1105
+ }
1106
+ return direction === "ASC" ? aValue - bValue : bValue - aValue;
1107
+ });
1108
+ }
1109
+ async _getIncludedMessages(table, include) {
1110
+ if (include.length === 0) return [];
1111
+ const targetIds = include.map((item) => item.id);
1112
+ const idCondition = targetIds.length === 1 ? `id = '${this.escapeSql(targetIds[0])}'` : `id IN (${targetIds.map((id) => `'${this.escapeSql(id)}'`).join(", ")})`;
1113
+ const targetRecords = await table.query().where(idCondition).toArray();
1114
+ const needsContext = include.some((item) => item.withPreviousMessages || item.withNextMessages);
1115
+ if (!needsContext) {
1116
+ return targetRecords.map((row) => this.normalizeMessage(row));
1117
+ }
1118
+ const threadIdsToFetch = [...new Set(targetRecords.map((r) => r.thread_id))];
1119
+ const threadCache = /* @__PURE__ */ new Map();
1120
+ for (const tid of threadIdsToFetch) {
1121
+ const threadRecords = await table.query().where(`thread_id = '${this.escapeSql(tid)}'`).toArray();
1122
+ threadRecords.sort((a, b) => a.createdAt - b.createdAt);
1123
+ threadCache.set(tid, threadRecords);
1124
+ }
1125
+ const targetThreadMap = /* @__PURE__ */ new Map();
1126
+ for (const r of targetRecords) {
1127
+ targetThreadMap.set(r.id, r.thread_id);
1128
+ }
1129
+ const includeByThread = /* @__PURE__ */ new Map();
1130
+ for (const item of include) {
1131
+ const tid = targetThreadMap.get(item.id);
1132
+ if (!tid) continue;
1133
+ const items = includeByThread.get(tid) ?? [];
1134
+ items.push(item);
1135
+ includeByThread.set(tid, items);
1136
+ }
1137
+ const seen = /* @__PURE__ */ new Set();
1138
+ const allContextMessages = [];
1139
+ for (const [tid, threadInclude] of includeByThread) {
1140
+ const threadMessages = threadCache.get(tid) ?? [];
1141
+ const contextMessages = this.processMessagesWithContext(threadMessages, threadInclude);
1142
+ for (const msg of contextMessages) {
1143
+ if (!seen.has(msg.id)) {
1144
+ seen.add(msg.id);
1145
+ allContextMessages.push(msg);
1146
+ }
1147
+ }
1148
+ }
1149
+ allContextMessages.sort((a, b) => a.createdAt - b.createdAt);
1150
+ return allContextMessages.map((row) => this.normalizeMessage(row));
1151
+ }
1046
1152
  /**
1047
1153
  * Processes messages to include context messages based on withPreviousMessages and withNextMessages
1048
1154
  * @param records - The sorted array of records to process
@@ -1052,7 +1158,8 @@ var StoreMemoryLance = class extends storage.MemoryStorage {
1052
1158
  processMessagesWithContext(records, include) {
1053
1159
  const messagesWithContext = include.filter((item) => item.withPreviousMessages || item.withNextMessages);
1054
1160
  if (messagesWithContext.length === 0) {
1055
- return records;
1161
+ const targetIds = new Set(include.map((item) => item.id));
1162
+ return records.filter((record) => targetIds.has(record.id));
1056
1163
  }
1057
1164
  const messageIndexMap = /* @__PURE__ */ new Map();
1058
1165
  records.forEach((message, index) => {
@@ -1077,7 +1184,8 @@ var StoreMemoryLance = class extends storage.MemoryStorage {
1077
1184
  }
1078
1185
  }
1079
1186
  if (additionalIndices.size === 0) {
1080
- return records;
1187
+ const targetIds = new Set(include.map((item) => item.id));
1188
+ return records.filter((record) => targetIds.has(record.id));
1081
1189
  }
1082
1190
  const originalMatchIds = new Set(include.map((item) => item.id));
1083
1191
  const allIndices = /* @__PURE__ */ new Set();