@mastra/lance 0.0.0-netlify-no-bundle-20251127120354 → 0.0.0-new-button-export-20251219130424

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,7 +1,8 @@
1
1
  import { connect, Index } from '@lancedb/lancedb';
2
2
  import { MastraError, ErrorCategory, ErrorDomain } from '@mastra/core/error';
3
- import { MastraStorage, StoreOperations, MemoryStorage, TABLE_THREADS, TABLE_MESSAGES, normalizePerPage, calculatePagination, TABLE_RESOURCES, ScoresStorage, TABLE_SCORERS, WorkflowsStorage, TABLE_WORKFLOW_SNAPSHOT, ensureDate } from '@mastra/core/storage';
3
+ import { MastraStorage, createStorageErrorId, createVectorErrorId, MemoryStorage, TABLE_SCHEMAS, TABLE_THREADS, TABLE_MESSAGES, TABLE_RESOURCES, normalizePerPage, calculatePagination, ScoresStorage, SCORERS_SCHEMA, TABLE_SCORERS, WorkflowsStorage, TABLE_WORKFLOW_SNAPSHOT, getDefaultValue, ensureDate } from '@mastra/core/storage';
4
4
  import { MessageList } from '@mastra/core/agent';
5
+ import { MastraBase } from '@mastra/core/base';
5
6
  import { Utf8, Float64, Binary, Float32, Int32, Field, Schema } from 'apache-arrow';
6
7
  import { saveScorePayloadSchema } from '@mastra/core/evals';
7
8
  import { MastraVector } from '@mastra/core/vector';
@@ -89,7 +90,7 @@ async function getTableSchema({
89
90
  } catch (validationError) {
90
91
  throw new MastraError(
91
92
  {
92
- id: "STORAGE_LANCE_STORAGE_GET_TABLE_SCHEMA_INVALID_ARGS",
93
+ id: createStorageErrorId("LANCE", "GET_TABLE_SCHEMA", "INVALID_ARGS"),
93
94
  domain: ErrorDomain.STORAGE,
94
95
  category: ErrorCategory.USER,
95
96
  text: validationError.message,
@@ -112,7 +113,7 @@ async function getTableSchema({
112
113
  } catch (error) {
113
114
  throw new MastraError(
114
115
  {
115
- id: "STORAGE_LANCE_STORAGE_GET_TABLE_SCHEMA_FAILED",
116
+ id: createStorageErrorId("LANCE", "GET_TABLE_SCHEMA", "FAILED"),
116
117
  domain: ErrorDomain.STORAGE,
117
118
  category: ErrorCategory.THIRD_PARTY,
118
119
  details: { tableName }
@@ -122,1118 +123,1198 @@ async function getTableSchema({
122
123
  }
123
124
  }
124
125
 
125
- // src/storage/domains/memory/index.ts
126
- var StoreMemoryLance = class extends MemoryStorage {
126
+ // src/storage/db/index.ts
127
+ function resolveLanceConfig(config) {
128
+ return config.client;
129
+ }
130
+ var LanceDB = class extends MastraBase {
127
131
  client;
128
- operations;
129
- constructor({ client, operations }) {
130
- super();
132
+ constructor({ client }) {
133
+ super({ name: "lance-db" });
131
134
  this.client = client;
132
- this.operations = operations;
133
135
  }
134
- // Utility to escape single quotes in SQL strings
135
- escapeSql(str) {
136
- return str.replace(/'/g, "''");
136
+ getDefaultValue(type) {
137
+ switch (type) {
138
+ case "text":
139
+ return "''";
140
+ case "timestamp":
141
+ return "CURRENT_TIMESTAMP";
142
+ case "integer":
143
+ case "bigint":
144
+ return "0";
145
+ case "jsonb":
146
+ return "'{}'";
147
+ case "uuid":
148
+ return "''";
149
+ default:
150
+ return getDefaultValue(type);
151
+ }
137
152
  }
138
- async getThreadById({ threadId }) {
153
+ async hasColumn(tableName, columnName) {
154
+ const table = await this.client.openTable(tableName);
155
+ const schema = await table.schema();
156
+ return schema.fields.some((field) => field.name === columnName);
157
+ }
158
+ translateSchema(schema) {
159
+ const fields = Object.entries(schema).map(([name, column]) => {
160
+ let arrowType;
161
+ switch (column.type.toLowerCase()) {
162
+ case "text":
163
+ case "uuid":
164
+ arrowType = new Utf8();
165
+ break;
166
+ case "int":
167
+ case "integer":
168
+ arrowType = new Int32();
169
+ break;
170
+ case "bigint":
171
+ arrowType = new Float64();
172
+ break;
173
+ case "float":
174
+ arrowType = new Float32();
175
+ break;
176
+ case "jsonb":
177
+ case "json":
178
+ arrowType = new Utf8();
179
+ break;
180
+ case "binary":
181
+ arrowType = new Binary();
182
+ break;
183
+ case "timestamp":
184
+ arrowType = new Float64();
185
+ break;
186
+ default:
187
+ arrowType = new Utf8();
188
+ }
189
+ return new Field(name, arrowType, column.nullable ?? true);
190
+ });
191
+ return new Schema(fields);
192
+ }
193
+ async createTable({
194
+ tableName,
195
+ schema
196
+ }) {
139
197
  try {
140
- const thread = await this.operations.load({ tableName: TABLE_THREADS, keys: { id: threadId } });
141
- if (!thread) {
142
- return null;
198
+ if (!this.client) {
199
+ throw new Error("LanceDB client not initialized. Call LanceStorage.create() first.");
200
+ }
201
+ if (!tableName) {
202
+ throw new Error("tableName is required for createTable.");
203
+ }
204
+ if (!schema) {
205
+ throw new Error("schema is required for createTable.");
143
206
  }
144
- return {
145
- ...thread,
146
- createdAt: new Date(thread.createdAt),
147
- updatedAt: new Date(thread.updatedAt)
148
- };
149
207
  } catch (error) {
150
208
  throw new MastraError(
151
209
  {
152
- id: "LANCE_STORE_GET_THREAD_BY_ID_FAILED",
210
+ id: createStorageErrorId("LANCE", "CREATE_TABLE", "INVALID_ARGS"),
153
211
  domain: ErrorDomain.STORAGE,
154
- category: ErrorCategory.THIRD_PARTY
212
+ category: ErrorCategory.USER,
213
+ details: { tableName }
155
214
  },
156
215
  error
157
216
  );
158
217
  }
159
- }
160
- /**
161
- * Saves a thread to the database. This function doesn't overwrite existing threads.
162
- * @param thread - The thread to save
163
- * @returns The saved thread
164
- */
165
- async saveThread({ thread }) {
166
218
  try {
167
- const record = { ...thread, metadata: JSON.stringify(thread.metadata) };
168
- const table = await this.client.openTable(TABLE_THREADS);
169
- await table.add([record], { mode: "append" });
170
- return thread;
219
+ const arrowSchema = this.translateSchema(schema);
220
+ await this.client.createEmptyTable(tableName, arrowSchema);
171
221
  } catch (error) {
222
+ if (error.message?.includes("already exists")) {
223
+ this.logger.debug(`Table '${tableName}' already exists, skipping create`);
224
+ return;
225
+ }
172
226
  throw new MastraError(
173
227
  {
174
- id: "LANCE_STORE_SAVE_THREAD_FAILED",
228
+ id: createStorageErrorId("LANCE", "CREATE_TABLE", "FAILED"),
175
229
  domain: ErrorDomain.STORAGE,
176
- category: ErrorCategory.THIRD_PARTY
230
+ category: ErrorCategory.THIRD_PARTY,
231
+ details: { tableName }
177
232
  },
178
233
  error
179
234
  );
180
235
  }
181
236
  }
182
- async updateThread({
183
- id,
184
- title,
185
- metadata
186
- }) {
187
- const maxRetries = 5;
188
- for (let attempt = 0; attempt < maxRetries; attempt++) {
189
- try {
190
- const current = await this.getThreadById({ threadId: id });
191
- if (!current) {
192
- throw new Error(`Thread with id ${id} not found`);
193
- }
194
- const mergedMetadata = { ...current.metadata, ...metadata };
195
- const record = {
196
- id,
197
- title,
198
- metadata: JSON.stringify(mergedMetadata),
199
- updatedAt: (/* @__PURE__ */ new Date()).getTime()
200
- };
201
- const table = await this.client.openTable(TABLE_THREADS);
202
- await table.mergeInsert("id").whenMatchedUpdateAll().whenNotMatchedInsertAll().execute([record]);
203
- const updatedThread = await this.getThreadById({ threadId: id });
204
- if (!updatedThread) {
205
- throw new Error(`Failed to retrieve updated thread ${id}`);
206
- }
207
- return updatedThread;
208
- } catch (error) {
209
- if (error.message?.includes("Commit conflict") && attempt < maxRetries - 1) {
210
- const delay = Math.pow(2, attempt) * 10;
211
- await new Promise((resolve) => setTimeout(resolve, delay));
212
- continue;
213
- }
214
- throw new MastraError(
215
- {
216
- id: "LANCE_STORE_UPDATE_THREAD_FAILED",
217
- domain: ErrorDomain.STORAGE,
218
- category: ErrorCategory.THIRD_PARTY
219
- },
220
- error
221
- );
222
- }
223
- }
224
- throw new MastraError(
225
- {
226
- id: "LANCE_STORE_UPDATE_THREAD_FAILED",
227
- domain: ErrorDomain.STORAGE,
228
- category: ErrorCategory.THIRD_PARTY
229
- },
230
- new Error("All retries exhausted")
231
- );
232
- }
233
- async deleteThread({ threadId }) {
237
+ async dropTable({ tableName }) {
234
238
  try {
235
- const table = await this.client.openTable(TABLE_THREADS);
236
- await table.delete(`id = '${threadId}'`);
237
- const messagesTable = await this.client.openTable(TABLE_MESSAGES);
238
- await messagesTable.delete(`thread_id = '${threadId}'`);
239
- } catch (error) {
239
+ if (!this.client) {
240
+ throw new Error("LanceDB client not initialized. Call LanceStorage.create() first.");
241
+ }
242
+ if (!tableName) {
243
+ throw new Error("tableName is required for dropTable.");
244
+ }
245
+ } catch (validationError) {
240
246
  throw new MastraError(
241
247
  {
242
- id: "LANCE_STORE_DELETE_THREAD_FAILED",
248
+ id: createStorageErrorId("LANCE", "DROP_TABLE", "INVALID_ARGS"),
243
249
  domain: ErrorDomain.STORAGE,
244
- category: ErrorCategory.THIRD_PARTY
250
+ category: ErrorCategory.USER,
251
+ text: validationError.message,
252
+ details: { tableName }
245
253
  },
246
- error
254
+ validationError
247
255
  );
248
256
  }
249
- }
250
- normalizeMessage(message) {
251
- const { thread_id, ...rest } = message;
252
- return {
253
- ...rest,
254
- threadId: thread_id,
255
- content: typeof message.content === "string" ? (() => {
256
- try {
257
- return JSON.parse(message.content);
258
- } catch {
259
- return message.content;
260
- }
261
- })() : message.content
262
- };
263
- }
264
- async listMessagesById({ messageIds }) {
265
- if (messageIds.length === 0) return { messages: [] };
266
257
  try {
267
- const table = await this.client.openTable(TABLE_MESSAGES);
268
- const quotedIds = messageIds.map((id) => `'${id}'`).join(", ");
269
- const allRecords = await table.query().where(`id IN (${quotedIds})`).toArray();
270
- const messages = processResultWithTypeConversion(
271
- allRecords,
272
- await getTableSchema({ tableName: TABLE_MESSAGES, client: this.client })
273
- );
274
- const list = new MessageList().add(
275
- messages.map(this.normalizeMessage),
276
- "memory"
277
- );
278
- return { messages: list.get.all.db() };
258
+ await this.client.dropTable(tableName);
279
259
  } catch (error) {
260
+ if (error.toString().includes("was not found") || error.message?.includes("Table not found")) {
261
+ this.logger.debug(`Table '${tableName}' does not exist, skipping drop`);
262
+ return;
263
+ }
280
264
  throw new MastraError(
281
265
  {
282
- id: "LANCE_STORE_LIST_MESSAGES_BY_ID_FAILED",
266
+ id: createStorageErrorId("LANCE", "DROP_TABLE", "FAILED"),
283
267
  domain: ErrorDomain.STORAGE,
284
268
  category: ErrorCategory.THIRD_PARTY,
285
- details: {
286
- messageIds: JSON.stringify(messageIds)
287
- }
269
+ details: { tableName }
288
270
  },
289
271
  error
290
272
  );
291
273
  }
292
274
  }
293
- async listMessages(args) {
294
- const { threadId, resourceId, include, filter, perPage: perPageInput, page = 0, orderBy } = args;
295
- if (!threadId.trim()) {
275
+ async alterTable({
276
+ tableName,
277
+ schema,
278
+ ifNotExists
279
+ }) {
280
+ try {
281
+ if (!this.client) {
282
+ throw new Error("LanceDB client not initialized. Call LanceStorage.create() first.");
283
+ }
284
+ if (!tableName) {
285
+ throw new Error("tableName is required for alterTable.");
286
+ }
287
+ if (!schema) {
288
+ throw new Error("schema is required for alterTable.");
289
+ }
290
+ if (!ifNotExists || ifNotExists.length === 0) {
291
+ this.logger.debug("No columns specified to add in alterTable, skipping.");
292
+ return;
293
+ }
294
+ } catch (validationError) {
296
295
  throw new MastraError(
297
296
  {
298
- id: "STORAGE_LANCE_LIST_MESSAGES_INVALID_THREAD_ID",
297
+ id: createStorageErrorId("LANCE", "ALTER_TABLE", "INVALID_ARGS"),
299
298
  domain: ErrorDomain.STORAGE,
300
- category: ErrorCategory.THIRD_PARTY,
301
- details: { threadId }
299
+ category: ErrorCategory.USER,
300
+ text: validationError.message,
301
+ details: { tableName }
302
302
  },
303
- new Error("threadId must be a non-empty string")
303
+ validationError
304
304
  );
305
305
  }
306
- const perPage = normalizePerPage(perPageInput, 40);
307
- const { offset, perPage: perPageForResponse } = calculatePagination(page, perPageInput, perPage);
308
306
  try {
309
- if (page < 0) {
310
- throw new MastraError(
311
- {
312
- id: "STORAGE_LANCE_LIST_MESSAGES_INVALID_PAGE",
313
- domain: ErrorDomain.STORAGE,
314
- category: ErrorCategory.USER,
315
- details: { page }
316
- },
317
- new Error("page must be >= 0")
318
- );
319
- }
320
- const { field, direction } = this.parseOrderBy(orderBy, "ASC");
321
- const table = await this.client.openTable(TABLE_MESSAGES);
322
- const conditions = [`thread_id = '${this.escapeSql(threadId)}'`];
323
- if (resourceId) {
324
- conditions.push(`\`resourceId\` = '${this.escapeSql(resourceId)}'`);
325
- }
326
- if (filter?.dateRange?.start) {
327
- const startTime = filter.dateRange.start instanceof Date ? filter.dateRange.start.getTime() : new Date(filter.dateRange.start).getTime();
328
- conditions.push(`\`createdAt\` >= ${startTime}`);
329
- }
330
- if (filter?.dateRange?.end) {
331
- const endTime = filter.dateRange.end instanceof Date ? filter.dateRange.end.getTime() : new Date(filter.dateRange.end).getTime();
332
- conditions.push(`\`createdAt\` <= ${endTime}`);
333
- }
334
- const whereClause = conditions.join(" AND ");
335
- const total = await table.countRows(whereClause);
336
- const query = table.query().where(whereClause);
337
- let allRecords = await query.toArray();
338
- allRecords.sort((a, b) => {
339
- const aValue = field === "createdAt" ? a.createdAt : a[field];
340
- const bValue = field === "createdAt" ? b.createdAt : b[field];
341
- if (aValue == null && bValue == null) return 0;
342
- if (aValue == null) return direction === "ASC" ? -1 : 1;
343
- if (bValue == null) return direction === "ASC" ? 1 : -1;
344
- if (typeof aValue === "string" && typeof bValue === "string") {
345
- return direction === "ASC" ? aValue.localeCompare(bValue) : bValue.localeCompare(aValue);
346
- }
347
- return direction === "ASC" ? aValue - bValue : bValue - aValue;
348
- });
349
- const paginatedRecords = allRecords.slice(offset, offset + perPage);
350
- const messages = paginatedRecords.map((row) => this.normalizeMessage(row));
351
- if (total === 0 && messages.length === 0 && (!include || include.length === 0)) {
307
+ const table = await this.client.openTable(tableName);
308
+ const currentSchema = await table.schema();
309
+ const existingFields = new Set(currentSchema.fields.map((f) => f.name));
310
+ const typeMap = {
311
+ text: "string",
312
+ integer: "int",
313
+ bigint: "bigint",
314
+ timestamp: "timestamp",
315
+ jsonb: "string",
316
+ uuid: "string"
317
+ };
318
+ const columnsToAdd = ifNotExists.filter((col) => schema[col] && !existingFields.has(col)).map((col) => {
319
+ const colDef = schema[col];
352
320
  return {
353
- messages: [],
354
- total: 0,
355
- page,
356
- perPage: perPageForResponse,
357
- hasMore: false
321
+ name: col,
322
+ valueSql: colDef?.nullable ? `cast(NULL as ${typeMap[colDef.type ?? "text"]})` : `cast(${this.getDefaultValue(colDef?.type ?? "text")} as ${typeMap[colDef?.type ?? "text"]})`
358
323
  };
359
- }
360
- const messageIds = new Set(messages.map((m) => m.id));
361
- if (include && include.length > 0) {
362
- const threadIds = [...new Set(include.map((item) => item.threadId || threadId))];
363
- const allThreadMessages = [];
364
- for (const tid of threadIds) {
365
- const threadQuery = table.query().where(`thread_id = '${tid}'`);
366
- let threadRecords = await threadQuery.toArray();
367
- allThreadMessages.push(...threadRecords);
368
- }
369
- allThreadMessages.sort((a, b) => a.createdAt - b.createdAt);
370
- const contextMessages = this.processMessagesWithContext(allThreadMessages, include);
371
- const includedMessages = contextMessages.map((row) => this.normalizeMessage(row));
372
- for (const includeMsg of includedMessages) {
373
- if (!messageIds.has(includeMsg.id)) {
374
- messages.push(includeMsg);
375
- messageIds.add(includeMsg.id);
376
- }
377
- }
378
- }
379
- const list = new MessageList().add(messages, "memory");
380
- let finalMessages = list.get.all.db();
381
- finalMessages = finalMessages.sort((a, b) => {
382
- const aValue = field === "createdAt" ? new Date(a.createdAt).getTime() : a[field];
383
- const bValue = field === "createdAt" ? new Date(b.createdAt).getTime() : b[field];
384
- if (aValue == null && bValue == null) return 0;
385
- if (aValue == null) return direction === "ASC" ? -1 : 1;
386
- if (bValue == null) return direction === "ASC" ? 1 : -1;
387
- if (typeof aValue === "string" && typeof bValue === "string") {
388
- return direction === "ASC" ? aValue.localeCompare(bValue) : bValue.localeCompare(aValue);
389
- }
390
- return direction === "ASC" ? aValue - bValue : bValue - aValue;
391
324
  });
392
- const returnedThreadMessageIds = new Set(finalMessages.filter((m) => m.threadId === threadId).map((m) => m.id));
393
- const allThreadMessagesReturned = returnedThreadMessageIds.size >= total;
394
- const fetchedAll = perPageInput === false || allThreadMessagesReturned;
395
- const hasMore = !fetchedAll && offset + perPage < total;
396
- return {
397
- messages: finalMessages,
398
- total,
399
- page,
400
- perPage: perPageForResponse,
401
- hasMore
402
- };
325
+ if (columnsToAdd.length > 0) {
326
+ await table.addColumns(columnsToAdd);
327
+ this.logger?.info?.(`Added columns [${columnsToAdd.map((c) => c.name).join(", ")}] to table ${tableName}`);
328
+ }
403
329
  } catch (error) {
404
- const mastraError = new MastraError(
330
+ throw new MastraError(
405
331
  {
406
- id: "LANCE_STORE_LIST_MESSAGES_FAILED",
332
+ id: createStorageErrorId("LANCE", "ALTER_TABLE", "FAILED"),
407
333
  domain: ErrorDomain.STORAGE,
408
334
  category: ErrorCategory.THIRD_PARTY,
409
- details: {
410
- threadId,
411
- resourceId: resourceId ?? ""
412
- }
335
+ details: { tableName }
413
336
  },
414
337
  error
415
338
  );
416
- this.logger?.error?.(mastraError.toString());
417
- this.logger?.trackException?.(mastraError);
418
- return {
419
- messages: [],
420
- total: 0,
421
- page,
422
- perPage: perPageForResponse,
423
- hasMore: false
424
- };
425
339
  }
426
340
  }
427
- async saveMessages(args) {
341
+ async clearTable({ tableName }) {
428
342
  try {
429
- const { messages } = args;
430
- if (messages.length === 0) {
431
- return { messages: [] };
432
- }
433
- const threadId = messages[0]?.threadId;
434
- if (!threadId) {
435
- throw new Error("Thread ID is required");
343
+ if (!this.client) {
344
+ throw new Error("LanceDB client not initialized. Call LanceStorage.create() first.");
436
345
  }
437
- for (const message of messages) {
438
- if (!message.id) {
439
- throw new Error("Message ID is required");
440
- }
441
- if (!message.threadId) {
442
- throw new Error("Thread ID is required for all messages");
443
- }
444
- if (message.resourceId === null || message.resourceId === void 0) {
445
- throw new Error("Resource ID cannot be null or undefined");
446
- }
447
- if (!message.content) {
448
- throw new Error("Message content is required");
449
- }
346
+ if (!tableName) {
347
+ throw new Error("tableName is required for clearTable.");
450
348
  }
451
- const transformedMessages = messages.map((message) => {
452
- const { threadId: threadId2, type, ...rest } = message;
453
- return {
454
- ...rest,
455
- thread_id: threadId2,
456
- type: type ?? "v2",
457
- content: JSON.stringify(message.content)
458
- };
459
- });
460
- const table = await this.client.openTable(TABLE_MESSAGES);
461
- await table.mergeInsert("id").whenMatchedUpdateAll().whenNotMatchedInsertAll().execute(transformedMessages);
462
- const threadsTable = await this.client.openTable(TABLE_THREADS);
463
- const currentTime = (/* @__PURE__ */ new Date()).getTime();
464
- const updateRecord = { id: threadId, updatedAt: currentTime };
465
- await threadsTable.mergeInsert("id").whenMatchedUpdateAll().whenNotMatchedInsertAll().execute([updateRecord]);
466
- const list = new MessageList().add(messages, "memory");
467
- return { messages: list.get.all.db() };
349
+ } catch (validationError) {
350
+ throw new MastraError(
351
+ {
352
+ id: createStorageErrorId("LANCE", "CLEAR_TABLE", "INVALID_ARGS"),
353
+ domain: ErrorDomain.STORAGE,
354
+ category: ErrorCategory.USER,
355
+ text: validationError.message,
356
+ details: { tableName }
357
+ },
358
+ validationError
359
+ );
360
+ }
361
+ try {
362
+ const table = await this.client.openTable(tableName);
363
+ await table.delete("1=1");
468
364
  } catch (error) {
469
365
  throw new MastraError(
470
366
  {
471
- id: "LANCE_STORE_SAVE_MESSAGES_FAILED",
367
+ id: createStorageErrorId("LANCE", "CLEAR_TABLE", "FAILED"),
472
368
  domain: ErrorDomain.STORAGE,
473
- category: ErrorCategory.THIRD_PARTY
369
+ category: ErrorCategory.THIRD_PARTY,
370
+ details: { tableName }
474
371
  },
475
372
  error
476
373
  );
477
374
  }
478
375
  }
479
- async listThreadsByResourceId(args) {
376
+ async insert({ tableName, record }) {
480
377
  try {
481
- const { resourceId, page = 0, perPage: perPageInput, orderBy } = args;
482
- const perPage = normalizePerPage(perPageInput, 100);
483
- if (page < 0) {
484
- throw new MastraError(
485
- {
486
- id: "STORAGE_LANCE_LIST_THREADS_BY_RESOURCE_ID_INVALID_PAGE",
487
- domain: ErrorDomain.STORAGE,
488
- category: ErrorCategory.USER,
489
- details: { page }
490
- },
491
- new Error("page must be >= 0")
492
- );
378
+ if (!this.client) {
379
+ throw new Error("LanceDB client not initialized. Call LanceStorage.create() first.");
493
380
  }
494
- const { offset, perPage: perPageForResponse } = calculatePagination(page, perPageInput, perPage);
495
- const { field, direction } = this.parseOrderBy(orderBy);
496
- const table = await this.client.openTable(TABLE_THREADS);
497
- const total = await table.countRows(`\`resourceId\` = '${this.escapeSql(resourceId)}'`);
498
- const query = table.query().where(`\`resourceId\` = '${this.escapeSql(resourceId)}'`);
499
- const records = await query.toArray();
500
- records.sort((a, b) => {
501
- const aValue = ["createdAt", "updatedAt"].includes(field) ? new Date(a[field]).getTime() : a[field];
502
- const bValue = ["createdAt", "updatedAt"].includes(field) ? new Date(b[field]).getTime() : b[field];
503
- if (aValue == null && bValue == null) return 0;
504
- if (aValue == null) return direction === "ASC" ? -1 : 1;
505
- if (bValue == null) return direction === "ASC" ? 1 : -1;
506
- if (typeof aValue === "string" && typeof bValue === "string") {
507
- return direction === "ASC" ? aValue.localeCompare(bValue) : bValue.localeCompare(aValue);
508
- }
509
- return direction === "ASC" ? aValue - bValue : bValue - aValue;
510
- });
511
- const paginatedRecords = records.slice(offset, offset + perPage);
512
- const schema = await getTableSchema({ tableName: TABLE_THREADS, client: this.client });
513
- const threads = paginatedRecords.map(
514
- (record) => processResultWithTypeConversion(record, schema)
381
+ if (!tableName) {
382
+ throw new Error("tableName is required for insert.");
383
+ }
384
+ if (!record || Object.keys(record).length === 0) {
385
+ throw new Error("record is required and cannot be empty for insert.");
386
+ }
387
+ } catch (validationError) {
388
+ throw new MastraError(
389
+ {
390
+ id: createStorageErrorId("LANCE", "INSERT", "INVALID_ARGS"),
391
+ domain: ErrorDomain.STORAGE,
392
+ category: ErrorCategory.USER,
393
+ text: validationError.message,
394
+ details: { tableName }
395
+ },
396
+ validationError
515
397
  );
516
- return {
517
- threads,
518
- total,
519
- page,
520
- perPage: perPageForResponse,
521
- hasMore: offset + perPage < total
522
- };
398
+ }
399
+ try {
400
+ const table = await this.client.openTable(tableName);
401
+ const primaryId = getPrimaryKeys(tableName);
402
+ const processedRecord = { ...record };
403
+ for (const key in processedRecord) {
404
+ if (processedRecord[key] !== null && typeof processedRecord[key] === "object" && !(processedRecord[key] instanceof Date)) {
405
+ this.logger.debug("Converting object to JSON string: ", processedRecord[key]);
406
+ processedRecord[key] = JSON.stringify(processedRecord[key]);
407
+ }
408
+ }
409
+ await table.mergeInsert(primaryId).whenMatchedUpdateAll().whenNotMatchedInsertAll().execute([processedRecord]);
523
410
  } catch (error) {
524
411
  throw new MastraError(
525
412
  {
526
- id: "LANCE_STORE_LIST_THREADS_BY_RESOURCE_ID_FAILED",
413
+ id: createStorageErrorId("LANCE", "INSERT", "FAILED"),
527
414
  domain: ErrorDomain.STORAGE,
528
- category: ErrorCategory.THIRD_PARTY
415
+ category: ErrorCategory.THIRD_PARTY,
416
+ details: { tableName }
529
417
  },
530
418
  error
531
419
  );
532
420
  }
533
421
  }
534
- /**
535
- * Processes messages to include context messages based on withPreviousMessages and withNextMessages
536
- * @param records - The sorted array of records to process
537
- * @param include - The array of include specifications with context parameters
538
- * @returns The processed array with context messages included
539
- */
540
- processMessagesWithContext(records, include) {
541
- const messagesWithContext = include.filter((item) => item.withPreviousMessages || item.withNextMessages);
542
- if (messagesWithContext.length === 0) {
543
- return records;
544
- }
545
- const messageIndexMap = /* @__PURE__ */ new Map();
546
- records.forEach((message, index) => {
547
- messageIndexMap.set(message.id, index);
548
- });
549
- const additionalIndices = /* @__PURE__ */ new Set();
550
- for (const item of messagesWithContext) {
551
- const messageIndex = messageIndexMap.get(item.id);
552
- if (messageIndex !== void 0) {
553
- if (item.withPreviousMessages) {
554
- const startIdx = Math.max(0, messageIndex - item.withPreviousMessages);
555
- for (let i = startIdx; i < messageIndex; i++) {
556
- additionalIndices.add(i);
557
- }
558
- }
559
- if (item.withNextMessages) {
560
- const endIdx = Math.min(records.length - 1, messageIndex + item.withNextMessages);
561
- for (let i = messageIndex + 1; i <= endIdx; i++) {
562
- additionalIndices.add(i);
563
- }
564
- }
422
+ async batchInsert({ tableName, records }) {
423
+ try {
424
+ if (!this.client) {
425
+ throw new Error("LanceDB client not initialized. Call LanceStorage.create() first.");
565
426
  }
566
- }
567
- if (additionalIndices.size === 0) {
568
- return records;
569
- }
570
- const originalMatchIds = new Set(include.map((item) => item.id));
571
- const allIndices = /* @__PURE__ */ new Set();
572
- records.forEach((record, index) => {
573
- if (originalMatchIds.has(record.id)) {
574
- allIndices.add(index);
427
+ if (!tableName) {
428
+ throw new Error("tableName is required for batchInsert.");
575
429
  }
576
- });
577
- additionalIndices.forEach((index) => {
578
- allIndices.add(index);
579
- });
580
- return Array.from(allIndices).sort((a, b) => a - b).map((index) => records[index]);
581
- }
582
- /**
583
- * Parse message data from LanceDB record format to MastraDBMessage format
584
- */
585
- parseMessageData(data) {
586
- const { thread_id, ...rest } = data;
587
- return {
588
- ...rest,
589
- threadId: thread_id,
590
- content: typeof data.content === "string" ? (() => {
591
- try {
592
- return JSON.parse(data.content);
593
- } catch {
594
- return data.content;
595
- }
596
- })() : data.content,
597
- createdAt: new Date(data.createdAt),
598
- updatedAt: new Date(data.updatedAt)
599
- };
600
- }
601
- async updateMessages(args) {
602
- const { messages } = args;
603
- this.logger.debug("Updating messages", { count: messages.length });
604
- if (!messages.length) {
605
- return [];
430
+ if (!records || records.length === 0) {
431
+ throw new Error("records array is required and cannot be empty for batchInsert.");
432
+ }
433
+ } catch (validationError) {
434
+ throw new MastraError(
435
+ {
436
+ id: createStorageErrorId("LANCE", "BATCH_INSERT", "INVALID_ARGS"),
437
+ domain: ErrorDomain.STORAGE,
438
+ category: ErrorCategory.USER,
439
+ text: validationError.message,
440
+ details: { tableName }
441
+ },
442
+ validationError
443
+ );
606
444
  }
607
- const updatedMessages = [];
608
- const affectedThreadIds = /* @__PURE__ */ new Set();
609
445
  try {
610
- for (const updateData of messages) {
611
- const { id, ...updates } = updateData;
612
- const existingMessage = await this.operations.load({ tableName: TABLE_MESSAGES, keys: { id } });
613
- if (!existingMessage) {
614
- this.logger.warn("Message not found for update", { id });
615
- continue;
616
- }
617
- const existingMsg = this.parseMessageData(existingMessage);
618
- const originalThreadId = existingMsg.threadId;
619
- affectedThreadIds.add(originalThreadId);
620
- const updatePayload = {};
621
- if ("role" in updates && updates.role !== void 0) updatePayload.role = updates.role;
622
- if ("type" in updates && updates.type !== void 0) updatePayload.type = updates.type;
623
- if ("resourceId" in updates && updates.resourceId !== void 0) updatePayload.resourceId = updates.resourceId;
624
- if ("threadId" in updates && updates.threadId !== void 0 && updates.threadId !== null) {
625
- updatePayload.thread_id = updates.threadId;
626
- affectedThreadIds.add(updates.threadId);
627
- }
628
- if (updates.content) {
629
- const existingContent = existingMsg.content;
630
- let newContent = { ...existingContent };
631
- if (updates.content.metadata !== void 0) {
632
- newContent.metadata = {
633
- ...existingContent.metadata || {},
634
- ...updates.content.metadata || {}
635
- };
636
- }
637
- if (updates.content.content !== void 0) {
638
- newContent.content = updates.content.content;
639
- }
640
- if ("parts" in updates.content && updates.content.parts !== void 0) {
641
- newContent.parts = updates.content.parts;
446
+ const table = await this.client.openTable(tableName);
447
+ const primaryId = getPrimaryKeys(tableName);
448
+ const processedRecords = records.map((record) => {
449
+ const processedRecord = { ...record };
450
+ for (const key in processedRecord) {
451
+ if (processedRecord[key] == null) continue;
452
+ if (processedRecord[key] !== null && typeof processedRecord[key] === "object" && !(processedRecord[key] instanceof Date)) {
453
+ processedRecord[key] = JSON.stringify(processedRecord[key]);
642
454
  }
643
- updatePayload.content = JSON.stringify(newContent);
644
- }
645
- await this.operations.insert({ tableName: TABLE_MESSAGES, record: { id, ...updatePayload } });
646
- const updatedMessage = await this.operations.load({ tableName: TABLE_MESSAGES, keys: { id } });
647
- if (updatedMessage) {
648
- updatedMessages.push(this.parseMessageData(updatedMessage));
649
455
  }
650
- }
651
- for (const threadId of affectedThreadIds) {
652
- await this.operations.insert({
653
- tableName: TABLE_THREADS,
654
- record: { id: threadId, updatedAt: Date.now() }
655
- });
656
- }
657
- return updatedMessages;
456
+ return processedRecord;
457
+ });
458
+ await table.mergeInsert(primaryId).whenMatchedUpdateAll().whenNotMatchedInsertAll().execute(processedRecords);
658
459
  } catch (error) {
659
460
  throw new MastraError(
660
461
  {
661
- id: "LANCE_STORE_UPDATE_MESSAGES_FAILED",
462
+ id: createStorageErrorId("LANCE", "BATCH_INSERT", "FAILED"),
662
463
  domain: ErrorDomain.STORAGE,
663
464
  category: ErrorCategory.THIRD_PARTY,
664
- details: { count: messages.length }
465
+ details: { tableName }
665
466
  },
666
467
  error
667
468
  );
668
469
  }
669
470
  }
670
- async getResourceById({ resourceId }) {
471
+ async load({ tableName, keys }) {
671
472
  try {
672
- const resource = await this.operations.load({ tableName: TABLE_RESOURCES, keys: { id: resourceId } });
673
- if (!resource) {
674
- return null;
675
- }
676
- let createdAt;
677
- let updatedAt;
678
- try {
679
- if (resource.createdAt instanceof Date) {
680
- createdAt = resource.createdAt;
681
- } else if (typeof resource.createdAt === "string") {
682
- createdAt = new Date(resource.createdAt);
683
- } else if (typeof resource.createdAt === "number") {
684
- createdAt = new Date(resource.createdAt);
685
- } else {
686
- createdAt = /* @__PURE__ */ new Date();
687
- }
688
- if (isNaN(createdAt.getTime())) {
689
- createdAt = /* @__PURE__ */ new Date();
690
- }
691
- } catch {
692
- createdAt = /* @__PURE__ */ new Date();
693
- }
694
- try {
695
- if (resource.updatedAt instanceof Date) {
696
- updatedAt = resource.updatedAt;
697
- } else if (typeof resource.updatedAt === "string") {
698
- updatedAt = new Date(resource.updatedAt);
699
- } else if (typeof resource.updatedAt === "number") {
700
- updatedAt = new Date(resource.updatedAt);
701
- } else {
702
- updatedAt = /* @__PURE__ */ new Date();
703
- }
704
- if (isNaN(updatedAt.getTime())) {
705
- updatedAt = /* @__PURE__ */ new Date();
706
- }
707
- } catch {
708
- updatedAt = /* @__PURE__ */ new Date();
473
+ if (!this.client) {
474
+ throw new Error("LanceDB client not initialized. Call LanceStorage.create() first.");
709
475
  }
710
- let workingMemory = resource.workingMemory;
711
- if (workingMemory === null || workingMemory === void 0) {
712
- workingMemory = void 0;
713
- } else if (workingMemory === "") {
714
- workingMemory = "";
715
- } else if (typeof workingMemory === "object") {
716
- workingMemory = JSON.stringify(workingMemory);
476
+ if (!tableName) {
477
+ throw new Error("tableName is required for load.");
717
478
  }
718
- let metadata = resource.metadata;
719
- if (metadata === "" || metadata === null || metadata === void 0) {
720
- metadata = void 0;
721
- } else if (typeof metadata === "string") {
722
- try {
723
- metadata = JSON.parse(metadata);
724
- } catch {
725
- metadata = metadata;
726
- }
479
+ if (!keys || Object.keys(keys).length === 0) {
480
+ throw new Error("keys are required and cannot be empty for load.");
727
481
  }
728
- return {
729
- ...resource,
730
- createdAt,
731
- updatedAt,
732
- workingMemory,
733
- metadata
734
- };
735
- } catch (error) {
482
+ } catch (validationError) {
736
483
  throw new MastraError(
737
484
  {
738
- id: "LANCE_STORE_GET_RESOURCE_BY_ID_FAILED",
485
+ id: createStorageErrorId("LANCE", "LOAD", "INVALID_ARGS"),
739
486
  domain: ErrorDomain.STORAGE,
740
- category: ErrorCategory.THIRD_PARTY
487
+ category: ErrorCategory.USER,
488
+ text: validationError.message,
489
+ details: { tableName }
741
490
  },
742
- error
491
+ validationError
743
492
  );
744
493
  }
745
- }
746
- async saveResource({ resource }) {
747
494
  try {
748
- const record = {
749
- ...resource,
750
- metadata: resource.metadata ? JSON.stringify(resource.metadata) : "",
751
- createdAt: resource.createdAt.getTime(),
752
- // Store as timestamp (milliseconds)
753
- updatedAt: resource.updatedAt.getTime()
754
- // Store as timestamp (milliseconds)
755
- };
756
- const table = await this.client.openTable(TABLE_RESOURCES);
757
- await table.add([record], { mode: "append" });
758
- return resource;
495
+ const table = await this.client.openTable(tableName);
496
+ const tableSchema = await getTableSchema({ tableName, client: this.client });
497
+ const query = table.query();
498
+ if (Object.keys(keys).length > 0) {
499
+ validateKeyTypes(keys, tableSchema);
500
+ const filterConditions = Object.entries(keys).map(([key, value]) => {
501
+ const isCamelCase = /^[a-z][a-zA-Z]*$/.test(key) && /[A-Z]/.test(key);
502
+ const quotedKey = isCamelCase ? `\`${key}\`` : key;
503
+ if (typeof value === "string") {
504
+ return `${quotedKey} = '${value}'`;
505
+ } else if (value === null) {
506
+ return `${quotedKey} IS NULL`;
507
+ } else {
508
+ return `${quotedKey} = ${value}`;
509
+ }
510
+ }).join(" AND ");
511
+ this.logger.debug("where clause generated: " + filterConditions);
512
+ query.where(filterConditions);
513
+ }
514
+ const result = await query.limit(1).toArray();
515
+ if (result.length === 0) {
516
+ this.logger.debug("No record found");
517
+ return null;
518
+ }
519
+ return processResultWithTypeConversion(result[0], tableSchema);
759
520
  } catch (error) {
521
+ if (error instanceof MastraError) throw error;
760
522
  throw new MastraError(
761
523
  {
762
- id: "LANCE_STORE_SAVE_RESOURCE_FAILED",
524
+ id: createStorageErrorId("LANCE", "LOAD", "FAILED"),
763
525
  domain: ErrorDomain.STORAGE,
764
- category: ErrorCategory.THIRD_PARTY
526
+ category: ErrorCategory.THIRD_PARTY,
527
+ details: { tableName, keyCount: Object.keys(keys).length, firstKey: Object.keys(keys)[0] ?? "" }
765
528
  },
766
529
  error
767
530
  );
768
531
  }
769
532
  }
770
- async updateResource({
771
- resourceId,
772
- workingMemory,
773
- metadata
774
- }) {
775
- const maxRetries = 3;
776
- for (let attempt = 0; attempt < maxRetries; attempt++) {
777
- try {
778
- const existingResource = await this.getResourceById({ resourceId });
779
- if (!existingResource) {
780
- const newResource = {
781
- id: resourceId,
782
- workingMemory,
783
- metadata: metadata || {},
784
- createdAt: /* @__PURE__ */ new Date(),
785
- updatedAt: /* @__PURE__ */ new Date()
786
- };
787
- return this.saveResource({ resource: newResource });
788
- }
789
- const updatedResource = {
790
- ...existingResource,
791
- workingMemory: workingMemory !== void 0 ? workingMemory : existingResource.workingMemory,
792
- metadata: {
793
- ...existingResource.metadata,
794
- ...metadata
795
- },
796
- updatedAt: /* @__PURE__ */ new Date()
797
- };
798
- const record = {
799
- id: resourceId,
800
- workingMemory: updatedResource.workingMemory || "",
801
- metadata: updatedResource.metadata ? JSON.stringify(updatedResource.metadata) : "",
802
- updatedAt: updatedResource.updatedAt.getTime()
803
- // Store as timestamp (milliseconds)
804
- };
805
- const table = await this.client.openTable(TABLE_RESOURCES);
806
- await table.mergeInsert("id").whenMatchedUpdateAll().whenNotMatchedInsertAll().execute([record]);
807
- return updatedResource;
808
- } catch (error) {
809
- if (error.message?.includes("Commit conflict") && attempt < maxRetries - 1) {
810
- const delay = Math.pow(2, attempt) * 10;
811
- await new Promise((resolve) => setTimeout(resolve, delay));
812
- continue;
813
- }
814
- throw new MastraError(
815
- {
816
- id: "LANCE_STORE_UPDATE_RESOURCE_FAILED",
817
- domain: ErrorDomain.STORAGE,
818
- category: ErrorCategory.THIRD_PARTY
819
- },
820
- error
821
- );
822
- }
823
- }
824
- throw new Error("Unexpected end of retry loop");
825
- }
826
533
  };
827
- var StoreOperationsLance = class extends StoreOperations {
534
+
535
+ // src/storage/domains/memory/index.ts
536
+ var StoreMemoryLance = class extends MemoryStorage {
828
537
  client;
829
- constructor({ client }) {
538
+ #db;
539
+ constructor(config) {
830
540
  super();
541
+ const client = resolveLanceConfig(config);
831
542
  this.client = client;
832
- }
833
- getDefaultValue(type) {
834
- switch (type) {
835
- case "text":
836
- return "''";
837
- case "timestamp":
838
- return "CURRENT_TIMESTAMP";
839
- case "integer":
840
- case "bigint":
841
- return "0";
842
- case "jsonb":
843
- return "'{}'";
844
- case "uuid":
845
- return "''";
846
- default:
847
- return super.getDefaultValue(type);
848
- }
849
- }
850
- async hasColumn(tableName, columnName) {
851
- const table = await this.client.openTable(tableName);
852
- const schema = await table.schema();
853
- return schema.fields.some((field) => field.name === columnName);
854
- }
855
- translateSchema(schema) {
856
- const fields = Object.entries(schema).map(([name, column]) => {
857
- let arrowType;
858
- switch (column.type.toLowerCase()) {
859
- case "text":
860
- case "uuid":
861
- arrowType = new Utf8();
862
- break;
863
- case "int":
864
- case "integer":
865
- arrowType = new Int32();
866
- break;
867
- case "bigint":
868
- arrowType = new Float64();
869
- break;
870
- case "float":
871
- arrowType = new Float32();
872
- break;
873
- case "jsonb":
874
- case "json":
875
- arrowType = new Utf8();
876
- break;
877
- case "binary":
878
- arrowType = new Binary();
879
- break;
880
- case "timestamp":
881
- arrowType = new Float64();
882
- break;
883
- default:
884
- arrowType = new Utf8();
885
- }
886
- return new Field(name, arrowType, column.nullable ?? true);
543
+ this.#db = new LanceDB({ client });
544
+ }
545
+ async init() {
546
+ await this.#db.createTable({ tableName: TABLE_THREADS, schema: TABLE_SCHEMAS[TABLE_THREADS] });
547
+ await this.#db.createTable({ tableName: TABLE_MESSAGES, schema: TABLE_SCHEMAS[TABLE_MESSAGES] });
548
+ await this.#db.createTable({ tableName: TABLE_RESOURCES, schema: TABLE_SCHEMAS[TABLE_RESOURCES] });
549
+ await this.#db.alterTable({
550
+ tableName: TABLE_MESSAGES,
551
+ schema: TABLE_SCHEMAS[TABLE_MESSAGES],
552
+ ifNotExists: ["resourceId"]
887
553
  });
888
- return new Schema(fields);
889
554
  }
890
- async createTable({
891
- tableName,
892
- schema
893
- }) {
555
+ async dangerouslyClearAll() {
556
+ await this.#db.clearTable({ tableName: TABLE_THREADS });
557
+ await this.#db.clearTable({ tableName: TABLE_MESSAGES });
558
+ await this.#db.clearTable({ tableName: TABLE_RESOURCES });
559
+ }
560
+ async deleteMessages(messageIds) {
561
+ if (!messageIds || messageIds.length === 0) {
562
+ return;
563
+ }
564
+ this.logger.debug("Deleting messages", { count: messageIds.length });
894
565
  try {
895
- if (!this.client) {
896
- throw new Error("LanceDB client not initialized. Call LanceStorage.create() first.");
897
- }
898
- if (!tableName) {
899
- throw new Error("tableName is required for createTable.");
566
+ const threadIds = /* @__PURE__ */ new Set();
567
+ for (const messageId of messageIds) {
568
+ const message = await this.#db.load({ tableName: TABLE_MESSAGES, keys: { id: messageId } });
569
+ if (message?.thread_id) {
570
+ threadIds.add(message.thread_id);
571
+ }
900
572
  }
901
- if (!schema) {
902
- throw new Error("schema is required for createTable.");
573
+ const messagesTable = await this.client.openTable(TABLE_MESSAGES);
574
+ const idConditions = messageIds.map((id) => `id = '${this.escapeSql(id)}'`).join(" OR ");
575
+ await messagesTable.delete(idConditions);
576
+ const now = (/* @__PURE__ */ new Date()).getTime();
577
+ const threadsTable = await this.client.openTable(TABLE_THREADS);
578
+ for (const threadId of threadIds) {
579
+ const thread = await this.getThreadById({ threadId });
580
+ if (thread) {
581
+ const record = {
582
+ id: threadId,
583
+ resourceId: thread.resourceId,
584
+ title: thread.title,
585
+ metadata: JSON.stringify(thread.metadata),
586
+ createdAt: new Date(thread.createdAt).getTime(),
587
+ updatedAt: now
588
+ };
589
+ await threadsTable.mergeInsert("id").whenMatchedUpdateAll().whenNotMatchedInsertAll().execute([record]);
590
+ }
903
591
  }
904
592
  } catch (error) {
905
593
  throw new MastraError(
906
594
  {
907
- id: "STORAGE_LANCE_STORAGE_CREATE_TABLE_INVALID_ARGS",
595
+ id: createStorageErrorId("LANCE", "DELETE_MESSAGES", "FAILED"),
908
596
  domain: ErrorDomain.STORAGE,
909
- category: ErrorCategory.USER,
910
- details: { tableName }
597
+ category: ErrorCategory.THIRD_PARTY,
598
+ details: { count: messageIds.length }
911
599
  },
912
600
  error
913
601
  );
914
602
  }
603
+ }
604
+ // Utility to escape single quotes in SQL strings
605
+ escapeSql(str) {
606
+ return str.replace(/'/g, "''");
607
+ }
608
+ async getThreadById({ threadId }) {
915
609
  try {
916
- const arrowSchema = this.translateSchema(schema);
917
- await this.client.createEmptyTable(tableName, arrowSchema);
918
- } catch (error) {
919
- if (error.message?.includes("already exists")) {
920
- this.logger.debug(`Table '${tableName}' already exists, skipping create`);
921
- return;
610
+ const thread = await this.#db.load({ tableName: TABLE_THREADS, keys: { id: threadId } });
611
+ if (!thread) {
612
+ return null;
922
613
  }
614
+ return {
615
+ ...thread,
616
+ createdAt: new Date(thread.createdAt),
617
+ updatedAt: new Date(thread.updatedAt)
618
+ };
619
+ } catch (error) {
923
620
  throw new MastraError(
924
621
  {
925
- id: "STORAGE_LANCE_STORAGE_CREATE_TABLE_FAILED",
622
+ id: createStorageErrorId("LANCE", "GET_THREAD_BY_ID", "FAILED"),
926
623
  domain: ErrorDomain.STORAGE,
927
- category: ErrorCategory.THIRD_PARTY,
928
- details: { tableName }
624
+ category: ErrorCategory.THIRD_PARTY
929
625
  },
930
626
  error
931
627
  );
932
628
  }
933
629
  }
934
- async dropTable({ tableName }) {
630
+ /**
631
+ * Saves a thread to the database. This function doesn't overwrite existing threads.
632
+ * @param thread - The thread to save
633
+ * @returns The saved thread
634
+ */
635
+ async saveThread({ thread }) {
935
636
  try {
936
- if (!this.client) {
937
- throw new Error("LanceDB client not initialized. Call LanceStorage.create() first.");
938
- }
939
- if (!tableName) {
940
- throw new Error("tableName is required for dropTable.");
941
- }
942
- } catch (validationError) {
637
+ const record = { ...thread, metadata: JSON.stringify(thread.metadata) };
638
+ const table = await this.client.openTable(TABLE_THREADS);
639
+ await table.add([record], { mode: "append" });
640
+ return thread;
641
+ } catch (error) {
943
642
  throw new MastraError(
944
643
  {
945
- id: "STORAGE_LANCE_STORAGE_DROP_TABLE_INVALID_ARGS",
644
+ id: createStorageErrorId("LANCE", "SAVE_THREAD", "FAILED"),
946
645
  domain: ErrorDomain.STORAGE,
947
- category: ErrorCategory.USER,
948
- text: validationError.message,
949
- details: { tableName }
646
+ category: ErrorCategory.THIRD_PARTY
950
647
  },
951
- validationError
648
+ error
952
649
  );
953
650
  }
651
+ }
652
+ async updateThread({
653
+ id,
654
+ title,
655
+ metadata
656
+ }) {
657
+ const maxRetries = 5;
658
+ for (let attempt = 0; attempt < maxRetries; attempt++) {
659
+ try {
660
+ const current = await this.getThreadById({ threadId: id });
661
+ if (!current) {
662
+ throw new Error(`Thread with id ${id} not found`);
663
+ }
664
+ const mergedMetadata = { ...current.metadata, ...metadata };
665
+ const record = {
666
+ id,
667
+ title,
668
+ metadata: JSON.stringify(mergedMetadata),
669
+ updatedAt: (/* @__PURE__ */ new Date()).getTime()
670
+ };
671
+ const table = await this.client.openTable(TABLE_THREADS);
672
+ await table.mergeInsert("id").whenMatchedUpdateAll().whenNotMatchedInsertAll().execute([record]);
673
+ const updatedThread = await this.getThreadById({ threadId: id });
674
+ if (!updatedThread) {
675
+ throw new Error(`Failed to retrieve updated thread ${id}`);
676
+ }
677
+ return updatedThread;
678
+ } catch (error) {
679
+ if (error.message?.includes("Commit conflict") && attempt < maxRetries - 1) {
680
+ const delay = Math.pow(2, attempt) * 10;
681
+ await new Promise((resolve) => setTimeout(resolve, delay));
682
+ continue;
683
+ }
684
+ throw new MastraError(
685
+ {
686
+ id: createStorageErrorId("LANCE", "UPDATE_THREAD", "FAILED"),
687
+ domain: ErrorDomain.STORAGE,
688
+ category: ErrorCategory.THIRD_PARTY
689
+ },
690
+ error
691
+ );
692
+ }
693
+ }
694
+ throw new MastraError(
695
+ {
696
+ id: createStorageErrorId("LANCE", "UPDATE_THREAD", "FAILED"),
697
+ domain: ErrorDomain.STORAGE,
698
+ category: ErrorCategory.THIRD_PARTY
699
+ },
700
+ new Error("All retries exhausted")
701
+ );
702
+ }
703
+ async deleteThread({ threadId }) {
954
704
  try {
955
- await this.client.dropTable(tableName);
705
+ const table = await this.client.openTable(TABLE_THREADS);
706
+ await table.delete(`id = '${threadId}'`);
707
+ const messagesTable = await this.client.openTable(TABLE_MESSAGES);
708
+ await messagesTable.delete(`thread_id = '${threadId}'`);
956
709
  } catch (error) {
957
- if (error.toString().includes("was not found") || error.message?.includes("Table not found")) {
958
- this.logger.debug(`Table '${tableName}' does not exist, skipping drop`);
959
- return;
960
- }
961
710
  throw new MastraError(
962
711
  {
963
- id: "STORAGE_LANCE_STORAGE_DROP_TABLE_FAILED",
712
+ id: createStorageErrorId("LANCE", "DELETE_THREAD", "FAILED"),
964
713
  domain: ErrorDomain.STORAGE,
965
- category: ErrorCategory.THIRD_PARTY,
966
- details: { tableName }
714
+ category: ErrorCategory.THIRD_PARTY
967
715
  },
968
716
  error
969
717
  );
970
718
  }
971
719
  }
972
- async alterTable({
973
- tableName,
974
- schema,
975
- ifNotExists
976
- }) {
977
- try {
978
- if (!this.client) {
979
- throw new Error("LanceDB client not initialized. Call LanceStorage.create() first.");
980
- }
981
- if (!tableName) {
982
- throw new Error("tableName is required for alterTable.");
983
- }
984
- if (!schema) {
985
- throw new Error("schema is required for alterTable.");
986
- }
987
- if (!ifNotExists || ifNotExists.length === 0) {
988
- this.logger.debug("No columns specified to add in alterTable, skipping.");
989
- return;
990
- }
991
- } catch (validationError) {
992
- throw new MastraError(
993
- {
994
- id: "STORAGE_LANCE_STORAGE_ALTER_TABLE_INVALID_ARGS",
995
- domain: ErrorDomain.STORAGE,
996
- category: ErrorCategory.USER,
997
- text: validationError.message,
998
- details: { tableName }
999
- },
1000
- validationError
1001
- );
1002
- }
720
+ normalizeMessage(message) {
721
+ const { thread_id, ...rest } = message;
722
+ return {
723
+ ...rest,
724
+ threadId: thread_id,
725
+ content: typeof message.content === "string" ? (() => {
726
+ try {
727
+ return JSON.parse(message.content);
728
+ } catch {
729
+ return message.content;
730
+ }
731
+ })() : message.content
732
+ };
733
+ }
734
+ async listMessagesById({ messageIds }) {
735
+ if (messageIds.length === 0) return { messages: [] };
1003
736
  try {
1004
- const table = await this.client.openTable(tableName);
1005
- const currentSchema = await table.schema();
1006
- const existingFields = new Set(currentSchema.fields.map((f) => f.name));
1007
- const typeMap = {
1008
- text: "string",
1009
- integer: "int",
1010
- bigint: "bigint",
1011
- timestamp: "timestamp",
1012
- jsonb: "string",
1013
- uuid: "string"
1014
- };
1015
- const columnsToAdd = ifNotExists.filter((col) => schema[col] && !existingFields.has(col)).map((col) => {
1016
- const colDef = schema[col];
1017
- return {
1018
- name: col,
1019
- valueSql: colDef?.nullable ? `cast(NULL as ${typeMap[colDef.type ?? "text"]})` : `cast(${this.getDefaultValue(colDef?.type ?? "text")} as ${typeMap[colDef?.type ?? "text"]})`
1020
- };
1021
- });
1022
- if (columnsToAdd.length > 0) {
1023
- await table.addColumns(columnsToAdd);
1024
- this.logger?.info?.(`Added columns [${columnsToAdd.map((c) => c.name).join(", ")}] to table ${tableName}`);
1025
- }
737
+ const table = await this.client.openTable(TABLE_MESSAGES);
738
+ const quotedIds = messageIds.map((id) => `'${id}'`).join(", ");
739
+ const allRecords = await table.query().where(`id IN (${quotedIds})`).toArray();
740
+ const messages = processResultWithTypeConversion(
741
+ allRecords,
742
+ await getTableSchema({ tableName: TABLE_MESSAGES, client: this.client })
743
+ );
744
+ const list = new MessageList().add(
745
+ messages.map(this.normalizeMessage),
746
+ "memory"
747
+ );
748
+ return { messages: list.get.all.db() };
1026
749
  } catch (error) {
1027
750
  throw new MastraError(
1028
751
  {
1029
- id: "STORAGE_LANCE_STORAGE_ALTER_TABLE_FAILED",
752
+ id: createStorageErrorId("LANCE", "LIST_MESSAGES_BY_ID", "FAILED"),
1030
753
  domain: ErrorDomain.STORAGE,
1031
754
  category: ErrorCategory.THIRD_PARTY,
1032
- details: { tableName }
755
+ details: {
756
+ messageIds: JSON.stringify(messageIds)
757
+ }
1033
758
  },
1034
759
  error
1035
760
  );
1036
761
  }
1037
762
  }
1038
- async clearTable({ tableName }) {
1039
- try {
1040
- if (!this.client) {
1041
- throw new Error("LanceDB client not initialized. Call LanceStorage.create() first.");
1042
- }
1043
- if (!tableName) {
1044
- throw new Error("tableName is required for clearTable.");
1045
- }
1046
- } catch (validationError) {
763
+ async listMessages(args) {
764
+ const { threadId, resourceId, include, filter, perPage: perPageInput, page = 0, orderBy } = args;
765
+ const threadIds = Array.isArray(threadId) ? threadId : [threadId];
766
+ if (threadIds.length === 0 || threadIds.some((id) => !id.trim())) {
1047
767
  throw new MastraError(
1048
768
  {
1049
- id: "STORAGE_LANCE_STORAGE_CLEAR_TABLE_INVALID_ARGS",
769
+ id: createStorageErrorId("LANCE", "LIST_MESSAGES", "INVALID_THREAD_ID"),
1050
770
  domain: ErrorDomain.STORAGE,
1051
- category: ErrorCategory.USER,
1052
- text: validationError.message,
1053
- details: { tableName }
771
+ category: ErrorCategory.THIRD_PARTY,
772
+ details: { threadId: Array.isArray(threadId) ? threadId.join(",") : threadId }
1054
773
  },
1055
- validationError
774
+ new Error("threadId must be a non-empty string or array of non-empty strings")
1056
775
  );
1057
776
  }
777
+ const perPage = normalizePerPage(perPageInput, 40);
778
+ const { offset, perPage: perPageForResponse } = calculatePagination(page, perPageInput, perPage);
1058
779
  try {
1059
- const table = await this.client.openTable(tableName);
1060
- await table.delete("1=1");
780
+ if (page < 0) {
781
+ throw new MastraError(
782
+ {
783
+ id: createStorageErrorId("LANCE", "LIST_MESSAGES", "INVALID_PAGE"),
784
+ domain: ErrorDomain.STORAGE,
785
+ category: ErrorCategory.USER,
786
+ details: { page }
787
+ },
788
+ new Error("page must be >= 0")
789
+ );
790
+ }
791
+ const { field, direction } = this.parseOrderBy(orderBy, "ASC");
792
+ const table = await this.client.openTable(TABLE_MESSAGES);
793
+ const threadCondition = threadIds.length === 1 ? `thread_id = '${this.escapeSql(threadIds[0])}'` : `thread_id IN (${threadIds.map((t) => `'${this.escapeSql(t)}'`).join(", ")})`;
794
+ const conditions = [threadCondition];
795
+ if (resourceId) {
796
+ conditions.push(`\`resourceId\` = '${this.escapeSql(resourceId)}'`);
797
+ }
798
+ if (filter?.dateRange?.start) {
799
+ const startTime = filter.dateRange.start instanceof Date ? filter.dateRange.start.getTime() : new Date(filter.dateRange.start).getTime();
800
+ conditions.push(`\`createdAt\` >= ${startTime}`);
801
+ }
802
+ if (filter?.dateRange?.end) {
803
+ const endTime = filter.dateRange.end instanceof Date ? filter.dateRange.end.getTime() : new Date(filter.dateRange.end).getTime();
804
+ conditions.push(`\`createdAt\` <= ${endTime}`);
805
+ }
806
+ const whereClause = conditions.join(" AND ");
807
+ const total = await table.countRows(whereClause);
808
+ const query = table.query().where(whereClause);
809
+ let allRecords = await query.toArray();
810
+ allRecords.sort((a, b) => {
811
+ const aValue = field === "createdAt" ? a.createdAt : a[field];
812
+ const bValue = field === "createdAt" ? b.createdAt : b[field];
813
+ if (aValue == null && bValue == null) return 0;
814
+ if (aValue == null) return direction === "ASC" ? -1 : 1;
815
+ if (bValue == null) return direction === "ASC" ? 1 : -1;
816
+ if (typeof aValue === "string" && typeof bValue === "string") {
817
+ return direction === "ASC" ? aValue.localeCompare(bValue) : bValue.localeCompare(aValue);
818
+ }
819
+ return direction === "ASC" ? aValue - bValue : bValue - aValue;
820
+ });
821
+ const paginatedRecords = allRecords.slice(offset, offset + perPage);
822
+ const messages = paginatedRecords.map((row) => this.normalizeMessage(row));
823
+ if (total === 0 && messages.length === 0 && (!include || include.length === 0)) {
824
+ return {
825
+ messages: [],
826
+ total: 0,
827
+ page,
828
+ perPage: perPageForResponse,
829
+ hasMore: false
830
+ };
831
+ }
832
+ const messageIds = new Set(messages.map((m) => m.id));
833
+ if (include && include.length > 0) {
834
+ const threadIds2 = [...new Set(include.map((item) => item.threadId || threadId))];
835
+ const allThreadMessages = [];
836
+ for (const tid of threadIds2) {
837
+ const threadQuery = table.query().where(`thread_id = '${tid}'`);
838
+ let threadRecords = await threadQuery.toArray();
839
+ allThreadMessages.push(...threadRecords);
840
+ }
841
+ allThreadMessages.sort((a, b) => a.createdAt - b.createdAt);
842
+ const contextMessages = this.processMessagesWithContext(allThreadMessages, include);
843
+ const includedMessages = contextMessages.map((row) => this.normalizeMessage(row));
844
+ for (const includeMsg of includedMessages) {
845
+ if (!messageIds.has(includeMsg.id)) {
846
+ messages.push(includeMsg);
847
+ messageIds.add(includeMsg.id);
848
+ }
849
+ }
850
+ }
851
+ const list = new MessageList().add(messages, "memory");
852
+ let finalMessages = list.get.all.db();
853
+ finalMessages = finalMessages.sort((a, b) => {
854
+ const aValue = field === "createdAt" ? new Date(a.createdAt).getTime() : a[field];
855
+ const bValue = field === "createdAt" ? new Date(b.createdAt).getTime() : b[field];
856
+ if (aValue == null && bValue == null) return 0;
857
+ if (aValue == null) return direction === "ASC" ? -1 : 1;
858
+ if (bValue == null) return direction === "ASC" ? 1 : -1;
859
+ if (typeof aValue === "string" && typeof bValue === "string") {
860
+ return direction === "ASC" ? aValue.localeCompare(bValue) : bValue.localeCompare(aValue);
861
+ }
862
+ return direction === "ASC" ? aValue - bValue : bValue - aValue;
863
+ });
864
+ const returnedThreadMessageIds = new Set(finalMessages.filter((m) => m.threadId === threadId).map((m) => m.id));
865
+ const allThreadMessagesReturned = returnedThreadMessageIds.size >= total;
866
+ const fetchedAll = perPageInput === false || allThreadMessagesReturned;
867
+ const hasMore = !fetchedAll && offset + perPage < total;
868
+ return {
869
+ messages: finalMessages,
870
+ total,
871
+ page,
872
+ perPage: perPageForResponse,
873
+ hasMore
874
+ };
1061
875
  } catch (error) {
1062
- throw new MastraError(
876
+ const mastraError = new MastraError(
1063
877
  {
1064
- id: "STORAGE_LANCE_STORAGE_CLEAR_TABLE_FAILED",
878
+ id: createStorageErrorId("LANCE", "LIST_MESSAGES", "FAILED"),
1065
879
  domain: ErrorDomain.STORAGE,
1066
880
  category: ErrorCategory.THIRD_PARTY,
1067
- details: { tableName }
881
+ details: {
882
+ threadId: Array.isArray(threadId) ? threadId.join(",") : threadId,
883
+ resourceId: resourceId ?? ""
884
+ }
1068
885
  },
1069
886
  error
1070
887
  );
888
+ this.logger?.error?.(mastraError.toString());
889
+ this.logger?.trackException?.(mastraError);
890
+ return {
891
+ messages: [],
892
+ total: 0,
893
+ page,
894
+ perPage: perPageForResponse,
895
+ hasMore: false
896
+ };
1071
897
  }
1072
898
  }
1073
- async insert({ tableName, record }) {
899
+ async saveMessages(args) {
1074
900
  try {
1075
- if (!this.client) {
1076
- throw new Error("LanceDB client not initialized. Call LanceStorage.create() first.");
901
+ const { messages } = args;
902
+ if (messages.length === 0) {
903
+ return { messages: [] };
1077
904
  }
1078
- if (!tableName) {
1079
- throw new Error("tableName is required for insert.");
905
+ const threadId = messages[0]?.threadId;
906
+ if (!threadId) {
907
+ throw new Error("Thread ID is required");
1080
908
  }
1081
- if (!record || Object.keys(record).length === 0) {
1082
- throw new Error("record is required and cannot be empty for insert.");
909
+ for (const message of messages) {
910
+ if (!message.id) {
911
+ throw new Error("Message ID is required");
912
+ }
913
+ if (!message.threadId) {
914
+ throw new Error("Thread ID is required for all messages");
915
+ }
916
+ if (message.resourceId === null || message.resourceId === void 0) {
917
+ throw new Error("Resource ID cannot be null or undefined");
918
+ }
919
+ if (!message.content) {
920
+ throw new Error("Message content is required");
921
+ }
1083
922
  }
1084
- } catch (validationError) {
923
+ const transformedMessages = messages.map((message) => {
924
+ const { threadId: threadId2, type, ...rest } = message;
925
+ return {
926
+ ...rest,
927
+ thread_id: threadId2,
928
+ type: type ?? "v2",
929
+ content: JSON.stringify(message.content)
930
+ };
931
+ });
932
+ const table = await this.client.openTable(TABLE_MESSAGES);
933
+ await table.mergeInsert("id").whenMatchedUpdateAll().whenNotMatchedInsertAll().execute(transformedMessages);
934
+ const threadsTable = await this.client.openTable(TABLE_THREADS);
935
+ const currentTime = (/* @__PURE__ */ new Date()).getTime();
936
+ const updateRecord = { id: threadId, updatedAt: currentTime };
937
+ await threadsTable.mergeInsert("id").whenMatchedUpdateAll().whenNotMatchedInsertAll().execute([updateRecord]);
938
+ const list = new MessageList().add(messages, "memory");
939
+ return { messages: list.get.all.db() };
940
+ } catch (error) {
1085
941
  throw new MastraError(
1086
942
  {
1087
- id: "STORAGE_LANCE_STORAGE_INSERT_INVALID_ARGS",
943
+ id: createStorageErrorId("LANCE", "SAVE_MESSAGES", "FAILED"),
1088
944
  domain: ErrorDomain.STORAGE,
1089
- category: ErrorCategory.USER,
1090
- text: validationError.message,
1091
- details: { tableName }
945
+ category: ErrorCategory.THIRD_PARTY
1092
946
  },
1093
- validationError
947
+ error
1094
948
  );
1095
949
  }
950
+ }
951
+ async listThreadsByResourceId(args) {
1096
952
  try {
1097
- const table = await this.client.openTable(tableName);
1098
- const primaryId = getPrimaryKeys(tableName);
1099
- const processedRecord = { ...record };
1100
- for (const key in processedRecord) {
1101
- if (processedRecord[key] !== null && typeof processedRecord[key] === "object" && !(processedRecord[key] instanceof Date)) {
1102
- this.logger.debug("Converting object to JSON string: ", processedRecord[key]);
1103
- processedRecord[key] = JSON.stringify(processedRecord[key]);
1104
- }
953
+ const { resourceId, page = 0, perPage: perPageInput, orderBy } = args;
954
+ const perPage = normalizePerPage(perPageInput, 100);
955
+ if (page < 0) {
956
+ throw new MastraError(
957
+ {
958
+ id: createStorageErrorId("LANCE", "LIST_THREADS_BY_RESOURCE_ID", "INVALID_PAGE"),
959
+ domain: ErrorDomain.STORAGE,
960
+ category: ErrorCategory.USER,
961
+ details: { page }
962
+ },
963
+ new Error("page must be >= 0")
964
+ );
1105
965
  }
1106
- console.info(await table.schema());
1107
- await table.mergeInsert(primaryId).whenMatchedUpdateAll().whenNotMatchedInsertAll().execute([processedRecord]);
966
+ const { offset, perPage: perPageForResponse } = calculatePagination(page, perPageInput, perPage);
967
+ const { field, direction } = this.parseOrderBy(orderBy);
968
+ const table = await this.client.openTable(TABLE_THREADS);
969
+ const total = await table.countRows(`\`resourceId\` = '${this.escapeSql(resourceId)}'`);
970
+ const query = table.query().where(`\`resourceId\` = '${this.escapeSql(resourceId)}'`);
971
+ const records = await query.toArray();
972
+ records.sort((a, b) => {
973
+ const aValue = ["createdAt", "updatedAt"].includes(field) ? new Date(a[field]).getTime() : a[field];
974
+ const bValue = ["createdAt", "updatedAt"].includes(field) ? new Date(b[field]).getTime() : b[field];
975
+ if (aValue == null && bValue == null) return 0;
976
+ if (aValue == null) return direction === "ASC" ? -1 : 1;
977
+ if (bValue == null) return direction === "ASC" ? 1 : -1;
978
+ if (typeof aValue === "string" && typeof bValue === "string") {
979
+ return direction === "ASC" ? aValue.localeCompare(bValue) : bValue.localeCompare(aValue);
980
+ }
981
+ return direction === "ASC" ? aValue - bValue : bValue - aValue;
982
+ });
983
+ const paginatedRecords = records.slice(offset, offset + perPage);
984
+ const schema = await getTableSchema({ tableName: TABLE_THREADS, client: this.client });
985
+ const threads = paginatedRecords.map(
986
+ (record) => processResultWithTypeConversion(record, schema)
987
+ );
988
+ return {
989
+ threads,
990
+ total,
991
+ page,
992
+ perPage: perPageForResponse,
993
+ hasMore: offset + perPage < total
994
+ };
1108
995
  } catch (error) {
1109
996
  throw new MastraError(
1110
997
  {
1111
- id: "STORAGE_LANCE_STORAGE_INSERT_FAILED",
998
+ id: createStorageErrorId("LANCE", "LIST_THREADS_BY_RESOURCE_ID", "FAILED"),
1112
999
  domain: ErrorDomain.STORAGE,
1113
- category: ErrorCategory.THIRD_PARTY,
1114
- details: { tableName }
1000
+ category: ErrorCategory.THIRD_PARTY
1115
1001
  },
1116
1002
  error
1117
1003
  );
1118
1004
  }
1119
1005
  }
1120
- async batchInsert({ tableName, records }) {
1121
- try {
1122
- if (!this.client) {
1123
- throw new Error("LanceDB client not initialized. Call LanceStorage.create() first.");
1124
- }
1125
- if (!tableName) {
1126
- throw new Error("tableName is required for batchInsert.");
1006
+ /**
1007
+ * Processes messages to include context messages based on withPreviousMessages and withNextMessages
1008
+ * @param records - The sorted array of records to process
1009
+ * @param include - The array of include specifications with context parameters
1010
+ * @returns The processed array with context messages included
1011
+ */
1012
+ processMessagesWithContext(records, include) {
1013
+ const messagesWithContext = include.filter((item) => item.withPreviousMessages || item.withNextMessages);
1014
+ if (messagesWithContext.length === 0) {
1015
+ return records;
1016
+ }
1017
+ const messageIndexMap = /* @__PURE__ */ new Map();
1018
+ records.forEach((message, index) => {
1019
+ messageIndexMap.set(message.id, index);
1020
+ });
1021
+ const additionalIndices = /* @__PURE__ */ new Set();
1022
+ for (const item of messagesWithContext) {
1023
+ const messageIndex = messageIndexMap.get(item.id);
1024
+ if (messageIndex !== void 0) {
1025
+ if (item.withPreviousMessages) {
1026
+ const startIdx = Math.max(0, messageIndex - item.withPreviousMessages);
1027
+ for (let i = startIdx; i < messageIndex; i++) {
1028
+ additionalIndices.add(i);
1029
+ }
1030
+ }
1031
+ if (item.withNextMessages) {
1032
+ const endIdx = Math.min(records.length - 1, messageIndex + item.withNextMessages);
1033
+ for (let i = messageIndex + 1; i <= endIdx; i++) {
1034
+ additionalIndices.add(i);
1035
+ }
1036
+ }
1127
1037
  }
1128
- if (!records || records.length === 0) {
1129
- throw new Error("records array is required and cannot be empty for batchInsert.");
1038
+ }
1039
+ if (additionalIndices.size === 0) {
1040
+ return records;
1041
+ }
1042
+ const originalMatchIds = new Set(include.map((item) => item.id));
1043
+ const allIndices = /* @__PURE__ */ new Set();
1044
+ records.forEach((record, index) => {
1045
+ if (originalMatchIds.has(record.id)) {
1046
+ allIndices.add(index);
1130
1047
  }
1131
- } catch (validationError) {
1132
- throw new MastraError(
1133
- {
1134
- id: "STORAGE_LANCE_STORAGE_BATCH_INSERT_INVALID_ARGS",
1135
- domain: ErrorDomain.STORAGE,
1136
- category: ErrorCategory.USER,
1137
- text: validationError.message,
1138
- details: { tableName }
1139
- },
1140
- validationError
1141
- );
1048
+ });
1049
+ additionalIndices.forEach((index) => {
1050
+ allIndices.add(index);
1051
+ });
1052
+ return Array.from(allIndices).sort((a, b) => a - b).map((index) => records[index]);
1053
+ }
1054
+ /**
1055
+ * Parse message data from LanceDB record format to MastraDBMessage format
1056
+ */
1057
+ parseMessageData(data) {
1058
+ const { thread_id, ...rest } = data;
1059
+ return {
1060
+ ...rest,
1061
+ threadId: thread_id,
1062
+ content: typeof data.content === "string" ? (() => {
1063
+ try {
1064
+ return JSON.parse(data.content);
1065
+ } catch {
1066
+ return data.content;
1067
+ }
1068
+ })() : data.content,
1069
+ createdAt: new Date(data.createdAt),
1070
+ updatedAt: new Date(data.updatedAt)
1071
+ };
1072
+ }
1073
+ async updateMessages(args) {
1074
+ const { messages } = args;
1075
+ this.logger.debug("Updating messages", { count: messages.length });
1076
+ if (!messages.length) {
1077
+ return [];
1142
1078
  }
1079
+ const updatedMessages = [];
1080
+ const affectedThreadIds = /* @__PURE__ */ new Set();
1143
1081
  try {
1144
- const table = await this.client.openTable(tableName);
1145
- const primaryId = getPrimaryKeys(tableName);
1146
- const processedRecords = records.map((record) => {
1147
- const processedRecord = { ...record };
1148
- for (const key in processedRecord) {
1149
- if (processedRecord[key] == null) continue;
1150
- if (processedRecord[key] !== null && typeof processedRecord[key] === "object" && !(processedRecord[key] instanceof Date)) {
1151
- processedRecord[key] = JSON.stringify(processedRecord[key]);
1082
+ for (const updateData of messages) {
1083
+ const { id, ...updates } = updateData;
1084
+ const existingMessage = await this.#db.load({ tableName: TABLE_MESSAGES, keys: { id } });
1085
+ if (!existingMessage) {
1086
+ this.logger.warn("Message not found for update", { id });
1087
+ continue;
1088
+ }
1089
+ const existingMsg = this.parseMessageData(existingMessage);
1090
+ const originalThreadId = existingMsg.threadId;
1091
+ affectedThreadIds.add(originalThreadId);
1092
+ const updatePayload = {};
1093
+ if ("role" in updates && updates.role !== void 0) updatePayload.role = updates.role;
1094
+ if ("type" in updates && updates.type !== void 0) updatePayload.type = updates.type;
1095
+ if ("resourceId" in updates && updates.resourceId !== void 0) updatePayload.resourceId = updates.resourceId;
1096
+ if ("threadId" in updates && updates.threadId !== void 0 && updates.threadId !== null) {
1097
+ updatePayload.thread_id = updates.threadId;
1098
+ affectedThreadIds.add(updates.threadId);
1099
+ }
1100
+ if (updates.content) {
1101
+ const existingContent = existingMsg.content;
1102
+ let newContent = { ...existingContent };
1103
+ if (updates.content.metadata !== void 0) {
1104
+ newContent.metadata = {
1105
+ ...existingContent.metadata || {},
1106
+ ...updates.content.metadata || {}
1107
+ };
1108
+ }
1109
+ if (updates.content.content !== void 0) {
1110
+ newContent.content = updates.content.content;
1152
1111
  }
1112
+ if ("parts" in updates.content && updates.content.parts !== void 0) {
1113
+ newContent.parts = updates.content.parts;
1114
+ }
1115
+ updatePayload.content = JSON.stringify(newContent);
1153
1116
  }
1154
- return processedRecord;
1155
- });
1156
- await table.mergeInsert(primaryId).whenMatchedUpdateAll().whenNotMatchedInsertAll().execute(processedRecords);
1117
+ await this.#db.insert({ tableName: TABLE_MESSAGES, record: { id, ...updatePayload } });
1118
+ const updatedMessage = await this.#db.load({ tableName: TABLE_MESSAGES, keys: { id } });
1119
+ if (updatedMessage) {
1120
+ updatedMessages.push(this.parseMessageData(updatedMessage));
1121
+ }
1122
+ }
1123
+ for (const threadId of affectedThreadIds) {
1124
+ await this.#db.insert({
1125
+ tableName: TABLE_THREADS,
1126
+ record: { id: threadId, updatedAt: Date.now() }
1127
+ });
1128
+ }
1129
+ return updatedMessages;
1157
1130
  } catch (error) {
1158
1131
  throw new MastraError(
1159
1132
  {
1160
- id: "STORAGE_LANCE_STORAGE_BATCH_INSERT_FAILED",
1133
+ id: createStorageErrorId("LANCE", "UPDATE_MESSAGES", "FAILED"),
1161
1134
  domain: ErrorDomain.STORAGE,
1162
1135
  category: ErrorCategory.THIRD_PARTY,
1163
- details: { tableName }
1136
+ details: { count: messages.length }
1164
1137
  },
1165
1138
  error
1166
1139
  );
1167
1140
  }
1168
1141
  }
1169
- async load({ tableName, keys }) {
1142
+ async getResourceById({ resourceId }) {
1170
1143
  try {
1171
- if (!this.client) {
1172
- throw new Error("LanceDB client not initialized. Call LanceStorage.create() first.");
1144
+ const resource = await this.#db.load({ tableName: TABLE_RESOURCES, keys: { id: resourceId } });
1145
+ if (!resource) {
1146
+ return null;
1173
1147
  }
1174
- if (!tableName) {
1175
- throw new Error("tableName is required for load.");
1148
+ let createdAt;
1149
+ let updatedAt;
1150
+ try {
1151
+ if (resource.createdAt instanceof Date) {
1152
+ createdAt = resource.createdAt;
1153
+ } else if (typeof resource.createdAt === "string") {
1154
+ createdAt = new Date(resource.createdAt);
1155
+ } else if (typeof resource.createdAt === "number") {
1156
+ createdAt = new Date(resource.createdAt);
1157
+ } else {
1158
+ createdAt = /* @__PURE__ */ new Date();
1159
+ }
1160
+ if (isNaN(createdAt.getTime())) {
1161
+ createdAt = /* @__PURE__ */ new Date();
1162
+ }
1163
+ } catch {
1164
+ createdAt = /* @__PURE__ */ new Date();
1176
1165
  }
1177
- if (!keys || Object.keys(keys).length === 0) {
1178
- throw new Error("keys are required and cannot be empty for load.");
1166
+ try {
1167
+ if (resource.updatedAt instanceof Date) {
1168
+ updatedAt = resource.updatedAt;
1169
+ } else if (typeof resource.updatedAt === "string") {
1170
+ updatedAt = new Date(resource.updatedAt);
1171
+ } else if (typeof resource.updatedAt === "number") {
1172
+ updatedAt = new Date(resource.updatedAt);
1173
+ } else {
1174
+ updatedAt = /* @__PURE__ */ new Date();
1175
+ }
1176
+ if (isNaN(updatedAt.getTime())) {
1177
+ updatedAt = /* @__PURE__ */ new Date();
1178
+ }
1179
+ } catch {
1180
+ updatedAt = /* @__PURE__ */ new Date();
1179
1181
  }
1180
- } catch (validationError) {
1182
+ let workingMemory = resource.workingMemory;
1183
+ if (workingMemory === null || workingMemory === void 0) {
1184
+ workingMemory = void 0;
1185
+ } else if (workingMemory === "") {
1186
+ workingMemory = "";
1187
+ } else if (typeof workingMemory === "object") {
1188
+ workingMemory = JSON.stringify(workingMemory);
1189
+ }
1190
+ let metadata = resource.metadata;
1191
+ if (metadata === "" || metadata === null || metadata === void 0) {
1192
+ metadata = void 0;
1193
+ } else if (typeof metadata === "string") {
1194
+ try {
1195
+ metadata = JSON.parse(metadata);
1196
+ } catch {
1197
+ metadata = metadata;
1198
+ }
1199
+ }
1200
+ return {
1201
+ ...resource,
1202
+ createdAt,
1203
+ updatedAt,
1204
+ workingMemory,
1205
+ metadata
1206
+ };
1207
+ } catch (error) {
1181
1208
  throw new MastraError(
1182
1209
  {
1183
- id: "STORAGE_LANCE_STORAGE_LOAD_INVALID_ARGS",
1210
+ id: createStorageErrorId("LANCE", "GET_RESOURCE_BY_ID", "FAILED"),
1184
1211
  domain: ErrorDomain.STORAGE,
1185
- category: ErrorCategory.USER,
1186
- text: validationError.message,
1187
- details: { tableName }
1212
+ category: ErrorCategory.THIRD_PARTY
1188
1213
  },
1189
- validationError
1214
+ error
1190
1215
  );
1191
1216
  }
1217
+ }
1218
+ async saveResource({ resource }) {
1192
1219
  try {
1193
- const table = await this.client.openTable(tableName);
1194
- const tableSchema = await getTableSchema({ tableName, client: this.client });
1195
- const query = table.query();
1196
- if (Object.keys(keys).length > 0) {
1197
- validateKeyTypes(keys, tableSchema);
1198
- const filterConditions = Object.entries(keys).map(([key, value]) => {
1199
- const isCamelCase = /^[a-z][a-zA-Z]*$/.test(key) && /[A-Z]/.test(key);
1200
- const quotedKey = isCamelCase ? `\`${key}\`` : key;
1201
- if (typeof value === "string") {
1202
- return `${quotedKey} = '${value}'`;
1203
- } else if (value === null) {
1204
- return `${quotedKey} IS NULL`;
1205
- } else {
1206
- return `${quotedKey} = ${value}`;
1207
- }
1208
- }).join(" AND ");
1209
- this.logger.debug("where clause generated: " + filterConditions);
1210
- query.where(filterConditions);
1211
- }
1212
- const result = await query.limit(1).toArray();
1213
- if (result.length === 0) {
1214
- this.logger.debug("No record found");
1215
- return null;
1216
- }
1217
- return processResultWithTypeConversion(result[0], tableSchema);
1220
+ const record = {
1221
+ ...resource,
1222
+ metadata: resource.metadata ? JSON.stringify(resource.metadata) : "",
1223
+ createdAt: resource.createdAt.getTime(),
1224
+ // Store as timestamp (milliseconds)
1225
+ updatedAt: resource.updatedAt.getTime()
1226
+ // Store as timestamp (milliseconds)
1227
+ };
1228
+ const table = await this.client.openTable(TABLE_RESOURCES);
1229
+ await table.add([record], { mode: "append" });
1230
+ return resource;
1218
1231
  } catch (error) {
1219
- if (error instanceof MastraError) throw error;
1220
1232
  throw new MastraError(
1221
1233
  {
1222
- id: "STORAGE_LANCE_STORAGE_LOAD_FAILED",
1234
+ id: createStorageErrorId("LANCE", "SAVE_RESOURCE", "FAILED"),
1223
1235
  domain: ErrorDomain.STORAGE,
1224
- category: ErrorCategory.THIRD_PARTY,
1225
- details: { tableName, keyCount: Object.keys(keys).length, firstKey: Object.keys(keys)[0] ?? "" }
1236
+ category: ErrorCategory.THIRD_PARTY
1226
1237
  },
1227
1238
  error
1228
1239
  );
1229
1240
  }
1230
1241
  }
1242
+ async updateResource({
1243
+ resourceId,
1244
+ workingMemory,
1245
+ metadata
1246
+ }) {
1247
+ const maxRetries = 3;
1248
+ for (let attempt = 0; attempt < maxRetries; attempt++) {
1249
+ try {
1250
+ const existingResource = await this.getResourceById({ resourceId });
1251
+ if (!existingResource) {
1252
+ const newResource = {
1253
+ id: resourceId,
1254
+ workingMemory,
1255
+ metadata: metadata || {},
1256
+ createdAt: /* @__PURE__ */ new Date(),
1257
+ updatedAt: /* @__PURE__ */ new Date()
1258
+ };
1259
+ return this.saveResource({ resource: newResource });
1260
+ }
1261
+ const updatedResource = {
1262
+ ...existingResource,
1263
+ workingMemory: workingMemory !== void 0 ? workingMemory : existingResource.workingMemory,
1264
+ metadata: {
1265
+ ...existingResource.metadata,
1266
+ ...metadata
1267
+ },
1268
+ updatedAt: /* @__PURE__ */ new Date()
1269
+ };
1270
+ const record = {
1271
+ id: resourceId,
1272
+ workingMemory: updatedResource.workingMemory || "",
1273
+ metadata: updatedResource.metadata ? JSON.stringify(updatedResource.metadata) : "",
1274
+ updatedAt: updatedResource.updatedAt.getTime()
1275
+ // Store as timestamp (milliseconds)
1276
+ };
1277
+ const table = await this.client.openTable(TABLE_RESOURCES);
1278
+ await table.mergeInsert("id").whenMatchedUpdateAll().whenNotMatchedInsertAll().execute([record]);
1279
+ return updatedResource;
1280
+ } catch (error) {
1281
+ if (error.message?.includes("Commit conflict") && attempt < maxRetries - 1) {
1282
+ const delay = Math.pow(2, attempt) * 10;
1283
+ await new Promise((resolve) => setTimeout(resolve, delay));
1284
+ continue;
1285
+ }
1286
+ throw new MastraError(
1287
+ {
1288
+ id: createStorageErrorId("LANCE", "UPDATE_RESOURCE", "FAILED"),
1289
+ domain: ErrorDomain.STORAGE,
1290
+ category: ErrorCategory.THIRD_PARTY
1291
+ },
1292
+ error
1293
+ );
1294
+ }
1295
+ }
1296
+ throw new Error("Unexpected end of retry loop");
1297
+ }
1231
1298
  };
1232
1299
  var StoreScoresLance = class extends ScoresStorage {
1233
1300
  client;
1234
- constructor({ client }) {
1301
+ #db;
1302
+ constructor(config) {
1235
1303
  super();
1304
+ const client = resolveLanceConfig(config);
1236
1305
  this.client = client;
1306
+ this.#db = new LanceDB({ client });
1307
+ }
1308
+ async init() {
1309
+ await this.#db.createTable({ tableName: TABLE_SCORERS, schema: SCORERS_SCHEMA });
1310
+ await this.#db.alterTable({
1311
+ tableName: TABLE_SCORERS,
1312
+ schema: SCORERS_SCHEMA,
1313
+ ifNotExists: ["spanId", "requestContext"]
1314
+ });
1315
+ }
1316
+ async dangerouslyClearAll() {
1317
+ await this.#db.clearTable({ tableName: TABLE_SCORERS });
1237
1318
  }
1238
1319
  async saveScore(score) {
1239
1320
  let validatedScore;
@@ -1242,39 +1323,47 @@ var StoreScoresLance = class extends ScoresStorage {
1242
1323
  } catch (error) {
1243
1324
  throw new MastraError(
1244
1325
  {
1245
- id: "LANCE_STORAGE_SAVE_SCORE_FAILED",
1326
+ id: createStorageErrorId("LANCE", "SAVE_SCORE", "VALIDATION_FAILED"),
1246
1327
  text: "Failed to save score in LanceStorage",
1247
1328
  domain: ErrorDomain.STORAGE,
1248
- category: ErrorCategory.THIRD_PARTY
1329
+ category: ErrorCategory.USER,
1330
+ details: {
1331
+ scorer: score.scorer?.id ?? "unknown",
1332
+ entityId: score.entityId ?? "unknown",
1333
+ entityType: score.entityType ?? "unknown",
1334
+ traceId: score.traceId ?? "",
1335
+ spanId: score.spanId ?? ""
1336
+ }
1249
1337
  },
1250
1338
  error
1251
1339
  );
1252
1340
  }
1341
+ const id = crypto.randomUUID();
1342
+ const now = /* @__PURE__ */ new Date();
1253
1343
  try {
1254
- const id = crypto.randomUUID();
1255
1344
  const table = await this.client.openTable(TABLE_SCORERS);
1256
1345
  const schema = await getTableSchema({ tableName: TABLE_SCORERS, client: this.client });
1257
1346
  const allowedFields = new Set(schema.fields.map((f) => f.name));
1258
1347
  const filteredScore = {};
1259
- Object.keys(validatedScore).forEach((key) => {
1348
+ for (const key of Object.keys(validatedScore)) {
1260
1349
  if (allowedFields.has(key)) {
1261
- filteredScore[key] = score[key];
1350
+ filteredScore[key] = validatedScore[key];
1262
1351
  }
1263
- });
1352
+ }
1264
1353
  for (const key in filteredScore) {
1265
1354
  if (filteredScore[key] !== null && typeof filteredScore[key] === "object" && !(filteredScore[key] instanceof Date)) {
1266
1355
  filteredScore[key] = JSON.stringify(filteredScore[key]);
1267
1356
  }
1268
1357
  }
1269
- filteredScore.createdAt = /* @__PURE__ */ new Date();
1270
- filteredScore.updatedAt = /* @__PURE__ */ new Date();
1271
1358
  filteredScore.id = id;
1359
+ filteredScore.createdAt = now;
1360
+ filteredScore.updatedAt = now;
1272
1361
  await table.add([filteredScore], { mode: "append" });
1273
- return { score };
1362
+ return { score: { ...validatedScore, id, createdAt: now, updatedAt: now } };
1274
1363
  } catch (error) {
1275
1364
  throw new MastraError(
1276
1365
  {
1277
- id: "LANCE_STORAGE_SAVE_SCORE_FAILED",
1366
+ id: createStorageErrorId("LANCE", "SAVE_SCORE", "FAILED"),
1278
1367
  text: "Failed to save score in LanceStorage",
1279
1368
  domain: ErrorDomain.STORAGE,
1280
1369
  category: ErrorCategory.THIRD_PARTY,
@@ -1294,7 +1383,7 @@ var StoreScoresLance = class extends ScoresStorage {
1294
1383
  } catch (error) {
1295
1384
  throw new MastraError(
1296
1385
  {
1297
- id: "LANCE_STORAGE_GET_SCORE_BY_ID_FAILED",
1386
+ id: createStorageErrorId("LANCE", "GET_SCORE_BY_ID", "FAILED"),
1298
1387
  text: "Failed to get score by id in LanceStorage",
1299
1388
  domain: ErrorDomain.STORAGE,
1300
1389
  category: ErrorCategory.THIRD_PARTY,
@@ -1304,15 +1393,21 @@ var StoreScoresLance = class extends ScoresStorage {
1304
1393
  );
1305
1394
  }
1306
1395
  }
1396
+ /**
1397
+ * LanceDB-specific score row transformation.
1398
+ *
1399
+ * Note: This implementation does NOT use coreTransformScoreRow because:
1400
+ * 1. LanceDB stores schema information in the table itself (requires async fetch)
1401
+ * 2. Uses processResultWithTypeConversion utility for LanceDB-specific type handling
1402
+ */
1307
1403
  async transformScoreRow(row) {
1308
1404
  const schema = await getTableSchema({ tableName: TABLE_SCORERS, client: this.client });
1309
1405
  const transformed = processResultWithTypeConversion(row, schema);
1310
- const result = {
1406
+ return {
1311
1407
  ...transformed,
1312
1408
  createdAt: row.createdAt,
1313
1409
  updatedAt: row.updatedAt
1314
1410
  };
1315
- return result;
1316
1411
  }
1317
1412
  async listScoresByScorerId({
1318
1413
  scorerId,
@@ -1367,7 +1462,7 @@ var StoreScoresLance = class extends ScoresStorage {
1367
1462
  } catch (error) {
1368
1463
  throw new MastraError(
1369
1464
  {
1370
- id: "LANCE_STORAGE_GET_SCORES_BY_SCORER_ID_FAILED",
1465
+ id: createStorageErrorId("LANCE", "LIST_SCORES_BY_SCORER_ID", "FAILED"),
1371
1466
  text: "Failed to get scores by scorerId in LanceStorage",
1372
1467
  domain: ErrorDomain.STORAGE,
1373
1468
  category: ErrorCategory.THIRD_PARTY,
@@ -1408,7 +1503,7 @@ var StoreScoresLance = class extends ScoresStorage {
1408
1503
  } catch (error) {
1409
1504
  throw new MastraError(
1410
1505
  {
1411
- id: "LANCE_STORAGE_GET_SCORES_BY_RUN_ID_FAILED",
1506
+ id: createStorageErrorId("LANCE", "LIST_SCORES_BY_RUN_ID", "FAILED"),
1412
1507
  text: "Failed to get scores by runId in LanceStorage",
1413
1508
  domain: ErrorDomain.STORAGE,
1414
1509
  category: ErrorCategory.THIRD_PARTY,
@@ -1450,7 +1545,7 @@ var StoreScoresLance = class extends ScoresStorage {
1450
1545
  } catch (error) {
1451
1546
  throw new MastraError(
1452
1547
  {
1453
- id: "LANCE_STORAGE_GET_SCORES_BY_ENTITY_ID_FAILED",
1548
+ id: createStorageErrorId("LANCE", "LIST_SCORES_BY_ENTITY_ID", "FAILED"),
1454
1549
  text: "Failed to get scores by entityId and entityType in LanceStorage",
1455
1550
  domain: ErrorDomain.STORAGE,
1456
1551
  category: ErrorCategory.THIRD_PARTY,
@@ -1492,7 +1587,7 @@ var StoreScoresLance = class extends ScoresStorage {
1492
1587
  } catch (error) {
1493
1588
  throw new MastraError(
1494
1589
  {
1495
- id: "LANCE_STORAGE_GET_SCORES_BY_SPAN_FAILED",
1590
+ id: createStorageErrorId("LANCE", "LIST_SCORES_BY_SPAN", "FAILED"),
1496
1591
  text: "Failed to get scores by traceId and spanId in LanceStorage",
1497
1592
  domain: ErrorDomain.STORAGE,
1498
1593
  category: ErrorCategory.THIRD_PARTY,
@@ -1503,6 +1598,9 @@ var StoreScoresLance = class extends ScoresStorage {
1503
1598
  }
1504
1599
  }
1505
1600
  };
1601
+ function escapeSql(str) {
1602
+ return str.replace(/'/g, "''");
1603
+ }
1506
1604
  function parseWorkflowRun(row) {
1507
1605
  let parsedSnapshot = row.snapshot;
1508
1606
  if (typeof parsedSnapshot === "string") {
@@ -1523,42 +1621,88 @@ function parseWorkflowRun(row) {
1523
1621
  }
1524
1622
  var StoreWorkflowsLance = class extends WorkflowsStorage {
1525
1623
  client;
1526
- constructor({ client }) {
1624
+ #db;
1625
+ constructor(config) {
1527
1626
  super();
1627
+ const client = resolveLanceConfig(config);
1528
1628
  this.client = client;
1629
+ this.#db = new LanceDB({ client });
1630
+ }
1631
+ async init() {
1632
+ const schema = TABLE_SCHEMAS[TABLE_WORKFLOW_SNAPSHOT];
1633
+ await this.#db.createTable({ tableName: TABLE_WORKFLOW_SNAPSHOT, schema });
1634
+ await this.#db.alterTable({
1635
+ tableName: TABLE_WORKFLOW_SNAPSHOT,
1636
+ schema,
1637
+ ifNotExists: ["resourceId"]
1638
+ });
1639
+ }
1640
+ async dangerouslyClearAll() {
1641
+ await this.#db.clearTable({ tableName: TABLE_WORKFLOW_SNAPSHOT });
1529
1642
  }
1530
- updateWorkflowResults({
1531
- // workflowName,
1532
- // runId,
1533
- // stepId,
1534
- // result,
1535
- // requestContext,
1643
+ async updateWorkflowResults({
1644
+ workflowName,
1645
+ runId,
1646
+ stepId,
1647
+ result,
1648
+ requestContext
1536
1649
  }) {
1537
- throw new Error("Method not implemented.");
1650
+ let snapshot = await this.loadWorkflowSnapshot({ workflowName, runId });
1651
+ if (!snapshot) {
1652
+ snapshot = {
1653
+ context: {},
1654
+ activePaths: [],
1655
+ timestamp: Date.now(),
1656
+ suspendedPaths: {},
1657
+ activeStepsPath: {},
1658
+ resumeLabels: {},
1659
+ serializedStepGraph: [],
1660
+ status: "pending",
1661
+ value: {},
1662
+ waitingPaths: {},
1663
+ runId,
1664
+ requestContext: {}
1665
+ };
1666
+ }
1667
+ snapshot.context[stepId] = result;
1668
+ snapshot.requestContext = { ...snapshot.requestContext, ...requestContext };
1669
+ await this.persistWorkflowSnapshot({ workflowName, runId, snapshot });
1670
+ return snapshot.context;
1538
1671
  }
1539
- updateWorkflowState({
1540
- // workflowName,
1541
- // runId,
1542
- // opts,
1672
+ async updateWorkflowState({
1673
+ workflowName,
1674
+ runId,
1675
+ opts
1543
1676
  }) {
1544
- throw new Error("Method not implemented.");
1677
+ const snapshot = await this.loadWorkflowSnapshot({ workflowName, runId });
1678
+ if (!snapshot) {
1679
+ return void 0;
1680
+ }
1681
+ if (!snapshot.context) {
1682
+ throw new Error(`Snapshot not found for runId ${runId}`);
1683
+ }
1684
+ const updatedSnapshot = { ...snapshot, ...opts };
1685
+ await this.persistWorkflowSnapshot({ workflowName, runId, snapshot: updatedSnapshot });
1686
+ return updatedSnapshot;
1545
1687
  }
1546
1688
  async persistWorkflowSnapshot({
1547
1689
  workflowName,
1548
1690
  runId,
1549
1691
  resourceId,
1550
- snapshot
1692
+ snapshot,
1693
+ createdAt,
1694
+ updatedAt
1551
1695
  }) {
1552
1696
  try {
1553
1697
  const table = await this.client.openTable(TABLE_WORKFLOW_SNAPSHOT);
1554
- const query = table.query().where(`workflow_name = '${workflowName}' AND run_id = '${runId}'`);
1698
+ const query = table.query().where(`workflow_name = '${escapeSql(workflowName)}' AND run_id = '${escapeSql(runId)}'`);
1555
1699
  const records = await query.toArray();
1556
- let createdAt;
1557
- const now = Date.now();
1700
+ let createdAtValue;
1701
+ const now = createdAt?.getTime() ?? Date.now();
1558
1702
  if (records.length > 0) {
1559
- createdAt = records[0].createdAt ?? now;
1703
+ createdAtValue = records[0].createdAt ?? now;
1560
1704
  } else {
1561
- createdAt = now;
1705
+ createdAtValue = now;
1562
1706
  }
1563
1707
  const { status, value, ...rest } = snapshot;
1564
1708
  const record = {
@@ -1567,14 +1711,14 @@ var StoreWorkflowsLance = class extends WorkflowsStorage {
1567
1711
  resourceId,
1568
1712
  snapshot: JSON.stringify({ status, value, ...rest }),
1569
1713
  // this is to ensure status is always just before value, for when querying the db by status
1570
- createdAt,
1571
- updatedAt: now
1714
+ createdAt: createdAtValue,
1715
+ updatedAt: updatedAt ?? now
1572
1716
  };
1573
1717
  await table.mergeInsert(["workflow_name", "run_id"]).whenMatchedUpdateAll().whenNotMatchedInsertAll().execute([record]);
1574
1718
  } catch (error) {
1575
1719
  throw new MastraError(
1576
1720
  {
1577
- id: "LANCE_STORE_PERSIST_WORKFLOW_SNAPSHOT_FAILED",
1721
+ id: createStorageErrorId("LANCE", "PERSIST_WORKFLOW_SNAPSHOT", "FAILED"),
1578
1722
  domain: ErrorDomain.STORAGE,
1579
1723
  category: ErrorCategory.THIRD_PARTY,
1580
1724
  details: { workflowName, runId }
@@ -1589,13 +1733,13 @@ var StoreWorkflowsLance = class extends WorkflowsStorage {
1589
1733
  }) {
1590
1734
  try {
1591
1735
  const table = await this.client.openTable(TABLE_WORKFLOW_SNAPSHOT);
1592
- const query = table.query().where(`workflow_name = '${workflowName}' AND run_id = '${runId}'`);
1736
+ const query = table.query().where(`workflow_name = '${escapeSql(workflowName)}' AND run_id = '${escapeSql(runId)}'`);
1593
1737
  const records = await query.toArray();
1594
1738
  return records.length > 0 ? JSON.parse(records[0].snapshot) : null;
1595
1739
  } catch (error) {
1596
1740
  throw new MastraError(
1597
1741
  {
1598
- id: "LANCE_STORE_LOAD_WORKFLOW_SNAPSHOT_FAILED",
1742
+ id: createStorageErrorId("LANCE", "LOAD_WORKFLOW_SNAPSHOT", "FAILED"),
1599
1743
  domain: ErrorDomain.STORAGE,
1600
1744
  category: ErrorCategory.THIRD_PARTY,
1601
1745
  details: { workflowName, runId }
@@ -1607,9 +1751,9 @@ var StoreWorkflowsLance = class extends WorkflowsStorage {
1607
1751
  async getWorkflowRunById(args) {
1608
1752
  try {
1609
1753
  const table = await this.client.openTable(TABLE_WORKFLOW_SNAPSHOT);
1610
- let whereClause = `run_id = '${args.runId}'`;
1754
+ let whereClause = `run_id = '${escapeSql(args.runId)}'`;
1611
1755
  if (args.workflowName) {
1612
- whereClause += ` AND workflow_name = '${args.workflowName}'`;
1756
+ whereClause += ` AND workflow_name = '${escapeSql(args.workflowName)}'`;
1613
1757
  }
1614
1758
  const query = table.query().where(whereClause);
1615
1759
  const records = await query.toArray();
@@ -1619,7 +1763,7 @@ var StoreWorkflowsLance = class extends WorkflowsStorage {
1619
1763
  } catch (error) {
1620
1764
  throw new MastraError(
1621
1765
  {
1622
- id: "LANCE_STORE_GET_WORKFLOW_RUN_BY_ID_FAILED",
1766
+ id: createStorageErrorId("LANCE", "GET_WORKFLOW_RUN_BY_ID", "FAILED"),
1623
1767
  domain: ErrorDomain.STORAGE,
1624
1768
  category: ErrorCategory.THIRD_PARTY,
1625
1769
  details: { runId: args.runId, workflowName: args.workflowName ?? "" }
@@ -1628,20 +1772,37 @@ var StoreWorkflowsLance = class extends WorkflowsStorage {
1628
1772
  );
1629
1773
  }
1630
1774
  }
1775
+ async deleteWorkflowRunById({ runId, workflowName }) {
1776
+ try {
1777
+ const table = await this.client.openTable(TABLE_WORKFLOW_SNAPSHOT);
1778
+ const whereClause = `run_id = '${escapeSql(runId)}' AND workflow_name = '${escapeSql(workflowName)}'`;
1779
+ await table.delete(whereClause);
1780
+ } catch (error) {
1781
+ throw new MastraError(
1782
+ {
1783
+ id: createStorageErrorId("LANCE", "DELETE_WORKFLOW_RUN_BY_ID", "FAILED"),
1784
+ domain: ErrorDomain.STORAGE,
1785
+ category: ErrorCategory.THIRD_PARTY,
1786
+ details: { runId, workflowName }
1787
+ },
1788
+ error
1789
+ );
1790
+ }
1791
+ }
1631
1792
  async listWorkflowRuns(args) {
1632
1793
  try {
1633
1794
  const table = await this.client.openTable(TABLE_WORKFLOW_SNAPSHOT);
1634
1795
  let query = table.query();
1635
1796
  const conditions = [];
1636
1797
  if (args?.workflowName) {
1637
- conditions.push(`workflow_name = '${args.workflowName.replace(/'/g, "''")}'`);
1798
+ conditions.push(`workflow_name = '${escapeSql(args.workflowName)}'`);
1638
1799
  }
1639
1800
  if (args?.status) {
1640
1801
  const escapedStatus = args.status.replace(/\\/g, "\\\\").replace(/'/g, "''").replace(/%/g, "\\%").replace(/_/g, "\\_");
1641
1802
  conditions.push(`\`snapshot\` LIKE '%"status":"${escapedStatus}","value"%'`);
1642
1803
  }
1643
1804
  if (args?.resourceId) {
1644
- conditions.push(`\`resourceId\` = '${args.resourceId}'`);
1805
+ conditions.push(`\`resourceId\` = '${escapeSql(args.resourceId)}'`);
1645
1806
  }
1646
1807
  if (args?.fromDate instanceof Date) {
1647
1808
  conditions.push(`\`createdAt\` >= ${args.fromDate.getTime()}`);
@@ -1661,7 +1822,7 @@ var StoreWorkflowsLance = class extends WorkflowsStorage {
1661
1822
  if (args.page < 0 || !Number.isInteger(args.page)) {
1662
1823
  throw new MastraError(
1663
1824
  {
1664
- id: "LANCE_STORE_INVALID_PAGINATION_PARAMS",
1825
+ id: createStorageErrorId("LANCE", "LIST_WORKFLOW_RUNS", "INVALID_PAGINATION"),
1665
1826
  domain: ErrorDomain.STORAGE,
1666
1827
  category: ErrorCategory.USER,
1667
1828
  details: { page: args.page, perPage: args.perPage }
@@ -1681,7 +1842,7 @@ var StoreWorkflowsLance = class extends WorkflowsStorage {
1681
1842
  } catch (error) {
1682
1843
  throw new MastraError(
1683
1844
  {
1684
- id: "LANCE_STORE_LIST_WORKFLOW_RUNS_FAILED",
1845
+ id: createStorageErrorId("LANCE", "LIST_WORKFLOW_RUNS", "FAILED"),
1685
1846
  domain: ErrorDomain.STORAGE,
1686
1847
  category: ErrorCategory.THIRD_PARTY,
1687
1848
  details: { resourceId: args?.resourceId ?? "", workflowName: args?.workflowName ?? "" }
@@ -1701,7 +1862,8 @@ var LanceStorage = class _LanceStorage extends MastraStorage {
1701
1862
  * @param id The unique identifier for this storage instance
1702
1863
  * @param name The name for this storage instance
1703
1864
  * @param uri The URI to connect to LanceDB
1704
- * @param options connection options
1865
+ * @param connectionOptions connection options for LanceDB
1866
+ * @param storageOptions storage options including disableInit
1705
1867
  *
1706
1868
  * Usage:
1707
1869
  *
@@ -1719,27 +1881,30 @@ var LanceStorage = class _LanceStorage extends MastraStorage {
1719
1881
  * ```ts
1720
1882
  * const store = await LanceStorage.create('my-storage-id', 'MyStorage', 's3://bucket/db', { storageOptions: { timeout: '60s' } });
1721
1883
  * ```
1884
+ *
1885
+ * Disable auto-init for runtime (after CI/CD has run migrations)
1886
+ * ```ts
1887
+ * const store = await LanceStorage.create('my-storage-id', 'MyStorage', '/path/to/db', undefined, { disableInit: true });
1888
+ * ```
1722
1889
  */
1723
- static async create(id, name, uri, options) {
1724
- const instance = new _LanceStorage(id, name);
1890
+ static async create(id, name, uri, connectionOptions, storageOptions) {
1891
+ const instance = new _LanceStorage(id, name, storageOptions?.disableInit);
1725
1892
  try {
1726
- instance.lanceClient = await connect(uri, options);
1727
- const operations = new StoreOperationsLance({ client: instance.lanceClient });
1893
+ instance.lanceClient = await connect(uri, connectionOptions);
1728
1894
  instance.stores = {
1729
- operations: new StoreOperationsLance({ client: instance.lanceClient }),
1730
1895
  workflows: new StoreWorkflowsLance({ client: instance.lanceClient }),
1731
1896
  scores: new StoreScoresLance({ client: instance.lanceClient }),
1732
- memory: new StoreMemoryLance({ client: instance.lanceClient, operations })
1897
+ memory: new StoreMemoryLance({ client: instance.lanceClient })
1733
1898
  };
1734
1899
  return instance;
1735
1900
  } catch (e) {
1736
1901
  throw new MastraError(
1737
1902
  {
1738
- id: "STORAGE_LANCE_STORAGE_CONNECT_FAILED",
1903
+ id: createStorageErrorId("LANCE", "CONNECT", "FAILED"),
1739
1904
  domain: ErrorDomain.STORAGE,
1740
1905
  category: ErrorCategory.THIRD_PARTY,
1741
1906
  text: `Failed to connect to LanceDB: ${e.message || e}`,
1742
- details: { uri, optionsProvided: !!options }
1907
+ details: { uri, optionsProvided: !!connectionOptions }
1743
1908
  },
1744
1909
  e
1745
1910
  );
@@ -1747,45 +1912,12 @@ var LanceStorage = class _LanceStorage extends MastraStorage {
1747
1912
  }
1748
1913
  /**
1749
1914
  * @internal
1750
- * Private constructor to enforce using the create factory method
1915
+ * Private constructor to enforce using the create factory method.
1916
+ * Note: stores is initialized in create() after the lanceClient is connected.
1751
1917
  */
1752
- constructor(id, name) {
1753
- super({ id, name });
1754
- const operations = new StoreOperationsLance({ client: this.lanceClient });
1755
- this.stores = {
1756
- operations: new StoreOperationsLance({ client: this.lanceClient }),
1757
- workflows: new StoreWorkflowsLance({ client: this.lanceClient }),
1758
- scores: new StoreScoresLance({ client: this.lanceClient }),
1759
- memory: new StoreMemoryLance({ client: this.lanceClient, operations })
1760
- };
1761
- }
1762
- async createTable({
1763
- tableName,
1764
- schema
1765
- }) {
1766
- return this.stores.operations.createTable({ tableName, schema });
1767
- }
1768
- async dropTable({ tableName }) {
1769
- return this.stores.operations.dropTable({ tableName });
1770
- }
1771
- async alterTable({
1772
- tableName,
1773
- schema,
1774
- ifNotExists
1775
- }) {
1776
- return this.stores.operations.alterTable({ tableName, schema, ifNotExists });
1777
- }
1778
- async clearTable({ tableName }) {
1779
- return this.stores.operations.clearTable({ tableName });
1780
- }
1781
- async insert({ tableName, record }) {
1782
- return this.stores.operations.insert({ tableName, record });
1783
- }
1784
- async batchInsert({ tableName, records }) {
1785
- return this.stores.operations.batchInsert({ tableName, records });
1786
- }
1787
- async load({ tableName, keys }) {
1788
- return this.stores.operations.load({ tableName, keys });
1918
+ constructor(id, name, disableInit) {
1919
+ super({ id, name, disableInit });
1920
+ this.stores = {};
1789
1921
  }
1790
1922
  async getThreadById({ threadId }) {
1791
1923
  return this.stores.memory.getThreadById({ threadId });
@@ -1808,13 +1940,16 @@ var LanceStorage = class _LanceStorage extends MastraStorage {
1808
1940
  async deleteThread({ threadId }) {
1809
1941
  return this.stores.memory.deleteThread({ threadId });
1810
1942
  }
1943
+ async deleteMessages(messageIds) {
1944
+ return this.stores.memory.deleteMessages(messageIds);
1945
+ }
1811
1946
  get supports() {
1812
1947
  return {
1813
1948
  selectByIncludeResourceScope: true,
1814
1949
  resourceWorkingMemory: true,
1815
1950
  hasColumn: true,
1816
1951
  createTable: true,
1817
- deleteMessages: false,
1952
+ deleteMessages: true,
1818
1953
  listScoresBySpan: true
1819
1954
  };
1820
1955
  }
@@ -1894,6 +2029,9 @@ var LanceStorage = class _LanceStorage extends MastraStorage {
1894
2029
  async getWorkflowRunById(args) {
1895
2030
  return this.stores.workflows.getWorkflowRunById(args);
1896
2031
  }
2032
+ async deleteWorkflowRunById({ runId, workflowName }) {
2033
+ return this.stores.workflows.deleteWorkflowRunById({ runId, workflowName });
2034
+ }
1897
2035
  async updateWorkflowResults({
1898
2036
  workflowName,
1899
2037
  runId,
@@ -1936,8 +2074,8 @@ var LanceStorage = class _LanceStorage extends MastraStorage {
1936
2074
  }) {
1937
2075
  return this.stores.scores.listScoresByScorerId({ scorerId, source, pagination, entityId, entityType });
1938
2076
  }
1939
- async saveScore(_score) {
1940
- return this.stores.scores.saveScore(_score);
2077
+ async saveScore(score) {
2078
+ return this.stores.scores.saveScore(score);
1941
2079
  }
1942
2080
  async listScoresByRunId({
1943
2081
  runId,
@@ -2313,7 +2451,7 @@ var LanceVectorStore = class _LanceVectorStore extends MastraVector {
2313
2451
  } catch (e) {
2314
2452
  throw new MastraError(
2315
2453
  {
2316
- id: "STORAGE_LANCE_VECTOR_CONNECT_FAILED",
2454
+ id: createVectorErrorId("LANCE", "CONNECT", "FAILED"),
2317
2455
  domain: ErrorDomain.STORAGE,
2318
2456
  category: ErrorCategory.THIRD_PARTY,
2319
2457
  details: { uri }
@@ -2356,7 +2494,7 @@ var LanceVectorStore = class _LanceVectorStore extends MastraVector {
2356
2494
  } catch (error) {
2357
2495
  throw new MastraError(
2358
2496
  {
2359
- id: "STORAGE_LANCE_VECTOR_QUERY_FAILED_INVALID_ARGS",
2497
+ id: createVectorErrorId("LANCE", "QUERY", "INVALID_ARGS"),
2360
2498
  domain: ErrorDomain.STORAGE,
2361
2499
  category: ErrorCategory.USER,
2362
2500
  text: "LanceDB client not initialized. Use LanceVectorStore.create() to create an instance",
@@ -2404,7 +2542,7 @@ var LanceVectorStore = class _LanceVectorStore extends MastraVector {
2404
2542
  } catch (error) {
2405
2543
  throw new MastraError(
2406
2544
  {
2407
- id: "STORAGE_LANCE_VECTOR_QUERY_FAILED",
2545
+ id: createVectorErrorId("LANCE", "QUERY", "FAILED"),
2408
2546
  domain: ErrorDomain.STORAGE,
2409
2547
  category: ErrorCategory.THIRD_PARTY,
2410
2548
  details: { tableName, includeVector, columnsCount: columns?.length, includeAllColumns }
@@ -2456,7 +2594,7 @@ var LanceVectorStore = class _LanceVectorStore extends MastraVector {
2456
2594
  } catch (error) {
2457
2595
  throw new MastraError(
2458
2596
  {
2459
- id: "STORAGE_LANCE_VECTOR_UPSERT_FAILED_INVALID_ARGS",
2597
+ id: createVectorErrorId("LANCE", "UPSERT", "INVALID_ARGS"),
2460
2598
  domain: ErrorDomain.STORAGE,
2461
2599
  category: ErrorCategory.USER,
2462
2600
  text: "LanceDB client not initialized. Use LanceVectorStore.create() to create an instance",
@@ -2492,7 +2630,7 @@ var LanceVectorStore = class _LanceVectorStore extends MastraVector {
2492
2630
  } catch (error) {
2493
2631
  throw new MastraError(
2494
2632
  {
2495
- id: "STORAGE_LANCE_VECTOR_UPSERT_FAILED",
2633
+ id: createVectorErrorId("LANCE", "UPSERT", "FAILED"),
2496
2634
  domain: ErrorDomain.STORAGE,
2497
2635
  category: ErrorCategory.THIRD_PARTY,
2498
2636
  details: { tableName, vectorCount: vectors.length, metadataCount: metadata.length, idsCount: ids.length }
@@ -2519,7 +2657,7 @@ var LanceVectorStore = class _LanceVectorStore extends MastraVector {
2519
2657
  async createTable(tableName, data, options) {
2520
2658
  if (!this.lanceClient) {
2521
2659
  throw new MastraError({
2522
- id: "STORAGE_LANCE_VECTOR_CREATE_TABLE_FAILED_INVALID_ARGS",
2660
+ id: createVectorErrorId("LANCE", "CREATE_TABLE", "INVALID_ARGS"),
2523
2661
  domain: ErrorDomain.STORAGE,
2524
2662
  category: ErrorCategory.USER,
2525
2663
  text: "LanceDB client not initialized. Use LanceVectorStore.create() to create an instance",
@@ -2534,7 +2672,7 @@ var LanceVectorStore = class _LanceVectorStore extends MastraVector {
2534
2672
  } catch (error) {
2535
2673
  throw new MastraError(
2536
2674
  {
2537
- id: "STORAGE_LANCE_VECTOR_CREATE_TABLE_FAILED",
2675
+ id: createVectorErrorId("LANCE", "CREATE_TABLE", "FAILED"),
2538
2676
  domain: ErrorDomain.STORAGE,
2539
2677
  category: ErrorCategory.THIRD_PARTY,
2540
2678
  details: { tableName }
@@ -2546,7 +2684,7 @@ var LanceVectorStore = class _LanceVectorStore extends MastraVector {
2546
2684
  async listTables() {
2547
2685
  if (!this.lanceClient) {
2548
2686
  throw new MastraError({
2549
- id: "STORAGE_LANCE_VECTOR_LIST_TABLES_FAILED_INVALID_ARGS",
2687
+ id: createVectorErrorId("LANCE", "LIST_TABLES", "INVALID_ARGS"),
2550
2688
  domain: ErrorDomain.STORAGE,
2551
2689
  category: ErrorCategory.USER,
2552
2690
  text: "LanceDB client not initialized. Use LanceVectorStore.create() to create an instance",
@@ -2558,7 +2696,7 @@ var LanceVectorStore = class _LanceVectorStore extends MastraVector {
2558
2696
  } catch (error) {
2559
2697
  throw new MastraError(
2560
2698
  {
2561
- id: "STORAGE_LANCE_VECTOR_LIST_TABLES_FAILED",
2699
+ id: createVectorErrorId("LANCE", "LIST_TABLES", "FAILED"),
2562
2700
  domain: ErrorDomain.STORAGE,
2563
2701
  category: ErrorCategory.THIRD_PARTY
2564
2702
  },
@@ -2569,7 +2707,7 @@ var LanceVectorStore = class _LanceVectorStore extends MastraVector {
2569
2707
  async getTableSchema(tableName) {
2570
2708
  if (!this.lanceClient) {
2571
2709
  throw new MastraError({
2572
- id: "STORAGE_LANCE_VECTOR_GET_TABLE_SCHEMA_FAILED_INVALID_ARGS",
2710
+ id: createVectorErrorId("LANCE", "GET_TABLE_SCHEMA", "INVALID_ARGS"),
2573
2711
  domain: ErrorDomain.STORAGE,
2574
2712
  category: ErrorCategory.USER,
2575
2713
  text: "LanceDB client not initialized. Use LanceVectorStore.create() to create an instance",
@@ -2582,7 +2720,7 @@ var LanceVectorStore = class _LanceVectorStore extends MastraVector {
2582
2720
  } catch (error) {
2583
2721
  throw new MastraError(
2584
2722
  {
2585
- id: "STORAGE_LANCE_VECTOR_GET_TABLE_SCHEMA_FAILED",
2723
+ id: createVectorErrorId("LANCE", "GET_TABLE_SCHEMA", "FAILED"),
2586
2724
  domain: ErrorDomain.STORAGE,
2587
2725
  category: ErrorCategory.THIRD_PARTY,
2588
2726
  details: { tableName }
@@ -2617,7 +2755,7 @@ var LanceVectorStore = class _LanceVectorStore extends MastraVector {
2617
2755
  } catch (err) {
2618
2756
  throw new MastraError(
2619
2757
  {
2620
- id: "STORAGE_LANCE_VECTOR_CREATE_INDEX_FAILED_INVALID_ARGS",
2758
+ id: createVectorErrorId("LANCE", "CREATE_INDEX", "INVALID_ARGS"),
2621
2759
  domain: ErrorDomain.STORAGE,
2622
2760
  category: ErrorCategory.USER,
2623
2761
  details: { tableName: tableName || "", indexName, dimension, metric }
@@ -2662,7 +2800,7 @@ var LanceVectorStore = class _LanceVectorStore extends MastraVector {
2662
2800
  } catch (error) {
2663
2801
  throw new MastraError(
2664
2802
  {
2665
- id: "STORAGE_LANCE_VECTOR_CREATE_INDEX_FAILED",
2803
+ id: createVectorErrorId("LANCE", "CREATE_INDEX", "FAILED"),
2666
2804
  domain: ErrorDomain.STORAGE,
2667
2805
  category: ErrorCategory.THIRD_PARTY,
2668
2806
  details: { tableName: tableName || "", indexName, dimension }
@@ -2674,7 +2812,7 @@ var LanceVectorStore = class _LanceVectorStore extends MastraVector {
2674
2812
  async listIndexes() {
2675
2813
  if (!this.lanceClient) {
2676
2814
  throw new MastraError({
2677
- id: "STORAGE_LANCE_VECTOR_LIST_INDEXES_FAILED_INVALID_ARGS",
2815
+ id: createVectorErrorId("LANCE", "LIST_INDEXES", "INVALID_ARGS"),
2678
2816
  domain: ErrorDomain.STORAGE,
2679
2817
  category: ErrorCategory.USER,
2680
2818
  text: "LanceDB client not initialized. Use LanceVectorStore.create() to create an instance",
@@ -2693,7 +2831,7 @@ var LanceVectorStore = class _LanceVectorStore extends MastraVector {
2693
2831
  } catch (error) {
2694
2832
  throw new MastraError(
2695
2833
  {
2696
- id: "STORAGE_LANCE_VECTOR_LIST_INDEXES_FAILED",
2834
+ id: createVectorErrorId("LANCE", "LIST_INDEXES", "FAILED"),
2697
2835
  domain: ErrorDomain.STORAGE,
2698
2836
  category: ErrorCategory.THIRD_PARTY
2699
2837
  },
@@ -2712,7 +2850,7 @@ var LanceVectorStore = class _LanceVectorStore extends MastraVector {
2712
2850
  } catch (err) {
2713
2851
  throw new MastraError(
2714
2852
  {
2715
- id: "STORAGE_LANCE_VECTOR_DESCRIBE_INDEX_FAILED_INVALID_ARGS",
2853
+ id: createVectorErrorId("LANCE", "DESCRIBE_INDEX", "INVALID_ARGS"),
2716
2854
  domain: ErrorDomain.STORAGE,
2717
2855
  category: ErrorCategory.USER,
2718
2856
  details: { indexName }
@@ -2747,7 +2885,7 @@ var LanceVectorStore = class _LanceVectorStore extends MastraVector {
2747
2885
  } catch (error) {
2748
2886
  throw new MastraError(
2749
2887
  {
2750
- id: "STORAGE_LANCE_VECTOR_DESCRIBE_INDEX_FAILED",
2888
+ id: createVectorErrorId("LANCE", "DESCRIBE_INDEX", "FAILED"),
2751
2889
  domain: ErrorDomain.STORAGE,
2752
2890
  category: ErrorCategory.THIRD_PARTY,
2753
2891
  details: { indexName }
@@ -2767,7 +2905,7 @@ var LanceVectorStore = class _LanceVectorStore extends MastraVector {
2767
2905
  } catch (err) {
2768
2906
  throw new MastraError(
2769
2907
  {
2770
- id: "STORAGE_LANCE_VECTOR_DELETE_INDEX_FAILED_INVALID_ARGS",
2908
+ id: createVectorErrorId("LANCE", "DELETE_INDEX", "INVALID_ARGS"),
2771
2909
  domain: ErrorDomain.STORAGE,
2772
2910
  category: ErrorCategory.USER,
2773
2911
  details: { indexName }
@@ -2790,7 +2928,7 @@ var LanceVectorStore = class _LanceVectorStore extends MastraVector {
2790
2928
  } catch (error) {
2791
2929
  throw new MastraError(
2792
2930
  {
2793
- id: "STORAGE_LANCE_VECTOR_DELETE_INDEX_FAILED",
2931
+ id: createVectorErrorId("LANCE", "DELETE_INDEX", "FAILED"),
2794
2932
  domain: ErrorDomain.STORAGE,
2795
2933
  category: ErrorCategory.THIRD_PARTY,
2796
2934
  details: { indexName }
@@ -2805,7 +2943,7 @@ var LanceVectorStore = class _LanceVectorStore extends MastraVector {
2805
2943
  async deleteAllTables() {
2806
2944
  if (!this.lanceClient) {
2807
2945
  throw new MastraError({
2808
- id: "STORAGE_LANCE_VECTOR_DELETE_ALL_TABLES_FAILED_INVALID_ARGS",
2946
+ id: createVectorErrorId("LANCE", "DELETE_ALL_TABLES", "INVALID_ARGS"),
2809
2947
  domain: ErrorDomain.STORAGE,
2810
2948
  category: ErrorCategory.USER,
2811
2949
  details: { methodName: "deleteAllTables" },
@@ -2817,7 +2955,7 @@ var LanceVectorStore = class _LanceVectorStore extends MastraVector {
2817
2955
  } catch (error) {
2818
2956
  throw new MastraError(
2819
2957
  {
2820
- id: "STORAGE_LANCE_VECTOR_DELETE_ALL_TABLES_FAILED",
2958
+ id: createVectorErrorId("LANCE", "DELETE_ALL_TABLES", "FAILED"),
2821
2959
  domain: ErrorDomain.STORAGE,
2822
2960
  category: ErrorCategory.THIRD_PARTY,
2823
2961
  details: { methodName: "deleteAllTables" }
@@ -2829,7 +2967,7 @@ var LanceVectorStore = class _LanceVectorStore extends MastraVector {
2829
2967
  async deleteTable(tableName) {
2830
2968
  if (!this.lanceClient) {
2831
2969
  throw new MastraError({
2832
- id: "STORAGE_LANCE_VECTOR_DELETE_TABLE_FAILED_INVALID_ARGS",
2970
+ id: createVectorErrorId("LANCE", "DELETE_TABLE", "INVALID_ARGS"),
2833
2971
  domain: ErrorDomain.STORAGE,
2834
2972
  category: ErrorCategory.USER,
2835
2973
  details: { tableName },
@@ -2841,7 +2979,7 @@ var LanceVectorStore = class _LanceVectorStore extends MastraVector {
2841
2979
  } catch (error) {
2842
2980
  throw new MastraError(
2843
2981
  {
2844
- id: "STORAGE_LANCE_VECTOR_DELETE_TABLE_FAILED",
2982
+ id: createVectorErrorId("LANCE", "DELETE_TABLE", "FAILED"),
2845
2983
  domain: ErrorDomain.STORAGE,
2846
2984
  category: ErrorCategory.THIRD_PARTY,
2847
2985
  details: { tableName }
@@ -2854,7 +2992,7 @@ var LanceVectorStore = class _LanceVectorStore extends MastraVector {
2854
2992
  const { indexName, update } = params;
2855
2993
  if ("id" in params && "filter" in params && params.id && params.filter) {
2856
2994
  throw new MastraError({
2857
- id: "STORAGE_LANCE_VECTOR_UPDATE_VECTOR_INVALID_ARGS",
2995
+ id: createVectorErrorId("LANCE", "UPDATE_VECTOR", "MUTUALLY_EXCLUSIVE"),
2858
2996
  domain: ErrorDomain.STORAGE,
2859
2997
  category: ErrorCategory.USER,
2860
2998
  text: "id and filter are mutually exclusive",
@@ -2863,7 +3001,7 @@ var LanceVectorStore = class _LanceVectorStore extends MastraVector {
2863
3001
  }
2864
3002
  if (!("id" in params || "filter" in params) || !params.id && !params.filter) {
2865
3003
  throw new MastraError({
2866
- id: "STORAGE_LANCE_VECTOR_UPDATE_VECTOR_INVALID_ARGS",
3004
+ id: createVectorErrorId("LANCE", "UPDATE_VECTOR", "NO_TARGET"),
2867
3005
  domain: ErrorDomain.STORAGE,
2868
3006
  category: ErrorCategory.USER,
2869
3007
  text: "Either id or filter must be provided",
@@ -2872,7 +3010,7 @@ var LanceVectorStore = class _LanceVectorStore extends MastraVector {
2872
3010
  }
2873
3011
  if ("filter" in params && params.filter && Object.keys(params.filter).length === 0) {
2874
3012
  throw new MastraError({
2875
- id: "STORAGE_LANCE_VECTOR_UPDATE_VECTOR_INVALID_ARGS",
3013
+ id: createVectorErrorId("LANCE", "UPDATE_VECTOR", "EMPTY_FILTER"),
2876
3014
  domain: ErrorDomain.STORAGE,
2877
3015
  category: ErrorCategory.USER,
2878
3016
  text: "Cannot update with empty filter",
@@ -2881,7 +3019,7 @@ var LanceVectorStore = class _LanceVectorStore extends MastraVector {
2881
3019
  }
2882
3020
  if (!update.vector && !update.metadata) {
2883
3021
  throw new MastraError({
2884
- id: "STORAGE_LANCE_VECTOR_UPDATE_VECTOR_INVALID_ARGS",
3022
+ id: createVectorErrorId("LANCE", "UPDATE_VECTOR", "NO_PAYLOAD"),
2885
3023
  domain: ErrorDomain.STORAGE,
2886
3024
  category: ErrorCategory.USER,
2887
3025
  text: "No updates provided",
@@ -2976,7 +3114,7 @@ var LanceVectorStore = class _LanceVectorStore extends MastraVector {
2976
3114
  if (error instanceof MastraError) throw error;
2977
3115
  throw new MastraError(
2978
3116
  {
2979
- id: "STORAGE_LANCE_VECTOR_UPDATE_VECTOR_FAILED",
3117
+ id: createVectorErrorId("LANCE", "UPDATE_VECTOR", "FAILED"),
2980
3118
  domain: ErrorDomain.STORAGE,
2981
3119
  category: ErrorCategory.THIRD_PARTY,
2982
3120
  details: {
@@ -3005,7 +3143,7 @@ var LanceVectorStore = class _LanceVectorStore extends MastraVector {
3005
3143
  } catch (err) {
3006
3144
  throw new MastraError(
3007
3145
  {
3008
- id: "STORAGE_LANCE_VECTOR_DELETE_VECTOR_FAILED_INVALID_ARGS",
3146
+ id: createVectorErrorId("LANCE", "DELETE_VECTOR", "INVALID_ARGS"),
3009
3147
  domain: ErrorDomain.STORAGE,
3010
3148
  category: ErrorCategory.USER,
3011
3149
  details: {
@@ -3038,7 +3176,7 @@ var LanceVectorStore = class _LanceVectorStore extends MastraVector {
3038
3176
  } catch (error) {
3039
3177
  throw new MastraError(
3040
3178
  {
3041
- id: "STORAGE_LANCE_VECTOR_DELETE_VECTOR_FAILED",
3179
+ id: createVectorErrorId("LANCE", "DELETE_VECTOR", "FAILED"),
3042
3180
  domain: ErrorDomain.STORAGE,
3043
3181
  category: ErrorCategory.THIRD_PARTY,
3044
3182
  details: {
@@ -3078,7 +3216,7 @@ var LanceVectorStore = class _LanceVectorStore extends MastraVector {
3078
3216
  async deleteVectors({ indexName, filter, ids }) {
3079
3217
  if (ids && filter) {
3080
3218
  throw new MastraError({
3081
- id: "STORAGE_LANCE_VECTOR_DELETE_VECTORS_INVALID_ARGS",
3219
+ id: createVectorErrorId("LANCE", "DELETE_VECTORS", "MUTUALLY_EXCLUSIVE"),
3082
3220
  domain: ErrorDomain.STORAGE,
3083
3221
  category: ErrorCategory.USER,
3084
3222
  text: "ids and filter are mutually exclusive",
@@ -3087,7 +3225,7 @@ var LanceVectorStore = class _LanceVectorStore extends MastraVector {
3087
3225
  }
3088
3226
  if (!ids && !filter) {
3089
3227
  throw new MastraError({
3090
- id: "STORAGE_LANCE_VECTOR_DELETE_VECTORS_INVALID_ARGS",
3228
+ id: createVectorErrorId("LANCE", "DELETE_VECTORS", "NO_TARGET"),
3091
3229
  domain: ErrorDomain.STORAGE,
3092
3230
  category: ErrorCategory.USER,
3093
3231
  text: "Either filter or ids must be provided",
@@ -3096,7 +3234,7 @@ var LanceVectorStore = class _LanceVectorStore extends MastraVector {
3096
3234
  }
3097
3235
  if (ids && ids.length === 0) {
3098
3236
  throw new MastraError({
3099
- id: "STORAGE_LANCE_VECTOR_DELETE_VECTORS_INVALID_ARGS",
3237
+ id: createVectorErrorId("LANCE", "DELETE_VECTORS", "EMPTY_IDS"),
3100
3238
  domain: ErrorDomain.STORAGE,
3101
3239
  category: ErrorCategory.USER,
3102
3240
  text: "Cannot delete with empty ids array",
@@ -3105,7 +3243,7 @@ var LanceVectorStore = class _LanceVectorStore extends MastraVector {
3105
3243
  }
3106
3244
  if (filter && Object.keys(filter).length === 0) {
3107
3245
  throw new MastraError({
3108
- id: "STORAGE_LANCE_VECTOR_DELETE_VECTORS_INVALID_ARGS",
3246
+ id: createVectorErrorId("LANCE", "DELETE_VECTORS", "EMPTY_FILTER"),
3109
3247
  domain: ErrorDomain.STORAGE,
3110
3248
  category: ErrorCategory.USER,
3111
3249
  text: "Cannot delete with empty filter",
@@ -3165,7 +3303,7 @@ var LanceVectorStore = class _LanceVectorStore extends MastraVector {
3165
3303
  if (error instanceof MastraError) throw error;
3166
3304
  throw new MastraError(
3167
3305
  {
3168
- id: "STORAGE_LANCE_VECTOR_DELETE_VECTORS_FAILED",
3306
+ id: createVectorErrorId("LANCE", "DELETE_VECTORS", "FAILED"),
3169
3307
  domain: ErrorDomain.STORAGE,
3170
3308
  category: ErrorCategory.THIRD_PARTY,
3171
3309
  details: {