@mastra/cloudflare 0.0.0-tsconfig-compile-20250703214351 → 0.0.0-unified-sidebar-20251010130811

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,400 +1,576 @@
1
- import { MessageList } from '@mastra/core/agent';
2
1
  import { MastraError, ErrorCategory, ErrorDomain } from '@mastra/core/error';
3
- import { MastraStorage, TABLE_THREADS, TABLE_MESSAGES, TABLE_WORKFLOW_SNAPSHOT, TABLE_EVALS, TABLE_TRACES } from '@mastra/core/storage';
2
+ import { MastraStorage, TABLE_THREADS, TABLE_MESSAGES, TABLE_WORKFLOW_SNAPSHOT, TABLE_EVALS, TABLE_SCORERS, TABLE_TRACES, StoreOperations, serializeDate, ensureDate, LegacyEvalsStorage, WorkflowsStorage, TracesStorage, MemoryStorage, resolveMessageLimit, TABLE_RESOURCES, ScoresStorage, safelyParseJSON } from '@mastra/core/storage';
4
3
  import Cloudflare from 'cloudflare';
4
+ import { MessageList } from '@mastra/core/agent';
5
+ import { saveScorePayloadSchema } from '@mastra/core/scores';
5
6
 
6
7
  // src/storage/index.ts
7
-
8
- // src/storage/types.ts
9
- function isWorkersConfig(config) {
10
- return "bindings" in config;
11
- }
12
-
13
- // src/storage/index.ts
14
- var CloudflareStore = class extends MastraStorage {
15
- client;
16
- accountId;
17
- namespacePrefix;
18
- bindings;
19
- validateWorkersConfig(config) {
20
- if (!isWorkersConfig(config)) {
21
- throw new Error("Invalid Workers API configuration");
22
- }
23
- if (!config.bindings) {
24
- throw new Error("KV bindings are required when using Workers Binding API");
25
- }
26
- const requiredTables = [TABLE_THREADS, TABLE_MESSAGES, TABLE_WORKFLOW_SNAPSHOT, TABLE_EVALS, TABLE_TRACES];
27
- for (const table of requiredTables) {
28
- if (!(table in config.bindings)) {
29
- throw new Error(`Missing KV binding for table: ${table}`);
30
- }
31
- }
8
+ var LegacyEvalsStorageCloudflare = class extends LegacyEvalsStorage {
9
+ operations;
10
+ constructor({ operations }) {
11
+ super();
12
+ this.operations = operations;
32
13
  }
33
- validateRestConfig(config) {
34
- if (isWorkersConfig(config)) {
35
- throw new Error("Invalid REST API configuration");
36
- }
37
- if (!config.accountId?.trim()) {
38
- throw new Error("accountId is required for REST API");
39
- }
40
- if (!config.apiToken?.trim()) {
41
- throw new Error("apiToken is required for REST API");
14
+ async getEvalsByAgentName(agentName, type) {
15
+ try {
16
+ const prefix = this.operations.namespacePrefix ? `${this.operations.namespacePrefix}:` : "";
17
+ const keyObjs = await this.operations.listKV(TABLE_EVALS, { prefix: `${prefix}${TABLE_EVALS}` });
18
+ const evals = [];
19
+ for (const { name: key } of keyObjs) {
20
+ const data = await this.operations.getKV(TABLE_EVALS, key);
21
+ if (!data) continue;
22
+ if (data.agent_name !== agentName) continue;
23
+ if (type) {
24
+ const isTest = data.test_info !== null && data.test_info !== void 0;
25
+ const evalType = isTest ? "test" : "live";
26
+ if (evalType !== type) continue;
27
+ }
28
+ const mappedData = {
29
+ ...data,
30
+ runId: data.run_id,
31
+ testInfo: data.test_info
32
+ };
33
+ evals.push(mappedData);
34
+ }
35
+ evals.sort((a, b) => {
36
+ const aTime = new Date(a.createdAt || 0).getTime();
37
+ const bTime = new Date(b.createdAt || 0).getTime();
38
+ return bTime - aTime;
39
+ });
40
+ return evals;
41
+ } catch (error) {
42
+ throw new MastraError(
43
+ {
44
+ id: "CLOUDFLARE_STORAGE_GET_EVALS_BY_AGENT_NAME_FAILED",
45
+ domain: ErrorDomain.STORAGE,
46
+ category: ErrorCategory.THIRD_PARTY,
47
+ text: "Failed to get evals by agent name"
48
+ },
49
+ error
50
+ );
42
51
  }
43
52
  }
44
- constructor(config) {
45
- super({ name: "Cloudflare" });
53
+ async getEvals(options) {
46
54
  try {
47
- if (isWorkersConfig(config)) {
48
- this.validateWorkersConfig(config);
49
- this.bindings = config.bindings;
50
- this.namespacePrefix = config.keyPrefix?.trim() || "";
51
- this.logger.info("Using Cloudflare KV Workers Binding API");
52
- } else {
53
- this.validateRestConfig(config);
54
- this.accountId = config.accountId.trim();
55
- this.namespacePrefix = config.namespacePrefix?.trim() || "";
56
- this.client = new Cloudflare({
57
- apiToken: config.apiToken.trim()
58
- });
59
- this.logger.info("Using Cloudflare KV REST API");
55
+ const { agentName, type, page = 0, perPage = 100, dateRange } = options;
56
+ const prefix = this.operations.namespacePrefix ? `${this.operations.namespacePrefix}:` : "";
57
+ const keyObjs = await this.operations.listKV(TABLE_EVALS, { prefix: `${prefix}${TABLE_EVALS}` });
58
+ const evals = [];
59
+ for (const { name: key } of keyObjs) {
60
+ const data = await this.operations.getKV(TABLE_EVALS, key);
61
+ if (!data) continue;
62
+ if (agentName && data.agent_name !== agentName) continue;
63
+ if (type) {
64
+ const isTest = data.test_info !== null && data.test_info !== void 0;
65
+ const evalType = isTest ? "test" : "live";
66
+ if (evalType !== type) continue;
67
+ }
68
+ if (dateRange?.start || dateRange?.end) {
69
+ const evalDate = new Date(data.createdAt || data.created_at || 0);
70
+ if (dateRange.start && evalDate < dateRange.start) continue;
71
+ if (dateRange.end && evalDate > dateRange.end) continue;
72
+ }
73
+ const mappedData = {
74
+ ...data,
75
+ runId: data.run_id,
76
+ testInfo: data.test_info
77
+ };
78
+ evals.push(mappedData);
60
79
  }
80
+ evals.sort((a, b) => {
81
+ const aTime = new Date(a.createdAt || 0).getTime();
82
+ const bTime = new Date(b.createdAt || 0).getTime();
83
+ return bTime - aTime;
84
+ });
85
+ const start = page * perPage;
86
+ const end = start + perPage;
87
+ const paginatedEvals = evals.slice(start, end);
88
+ return {
89
+ page,
90
+ perPage,
91
+ total: evals.length,
92
+ hasMore: start + perPage < evals.length,
93
+ evals: paginatedEvals
94
+ };
61
95
  } catch (error) {
62
96
  throw new MastraError(
63
97
  {
64
- id: "CLOUDFLARE_STORAGE_INIT_FAILED",
98
+ id: "CLOUDFLARE_STORAGE_GET_EVALS_FAILED",
65
99
  domain: ErrorDomain.STORAGE,
66
- category: ErrorCategory.THIRD_PARTY
100
+ category: ErrorCategory.THIRD_PARTY,
101
+ text: "Failed to get evals"
67
102
  },
68
103
  error
69
104
  );
70
105
  }
71
106
  }
72
- getBinding(tableName) {
73
- if (!this.bindings) {
74
- throw new Error(`Cannot use Workers API binding for ${tableName}: Store initialized with REST API configuration`);
75
- }
76
- const binding = this.bindings[tableName];
77
- if (!binding) throw new Error(`No binding found for namespace ${tableName}`);
78
- return binding;
107
+ };
108
+ var MemoryStorageCloudflare = class extends MemoryStorage {
109
+ operations;
110
+ constructor({ operations }) {
111
+ super();
112
+ this.operations = operations;
79
113
  }
80
- async listNamespaces() {
81
- if (this.bindings) {
114
+ ensureMetadata(metadata) {
115
+ if (!metadata) return void 0;
116
+ return typeof metadata === "string" ? JSON.parse(metadata) : metadata;
117
+ }
118
+ async getThreadById({ threadId }) {
119
+ const thread = await this.operations.load({ tableName: TABLE_THREADS, keys: { id: threadId } });
120
+ if (!thread) return null;
121
+ try {
82
122
  return {
83
- result: Object.keys(this.bindings).map((name) => ({
84
- id: name,
85
- title: name,
86
- supports_url_encoding: true
87
- }))
123
+ ...thread,
124
+ createdAt: ensureDate(thread.createdAt),
125
+ updatedAt: ensureDate(thread.updatedAt),
126
+ metadata: this.ensureMetadata(thread.metadata)
88
127
  };
128
+ } catch (error) {
129
+ const mastraError = new MastraError(
130
+ {
131
+ id: "CLOUDFLARE_STORAGE_GET_THREAD_BY_ID_FAILED",
132
+ domain: ErrorDomain.STORAGE,
133
+ category: ErrorCategory.THIRD_PARTY,
134
+ details: {
135
+ threadId
136
+ }
137
+ },
138
+ error
139
+ );
140
+ this.logger?.trackException(mastraError);
141
+ this.logger?.error(mastraError.toString());
142
+ return null;
89
143
  }
90
- let allNamespaces = [];
91
- let currentPage = 1;
92
- const perPage = 50;
93
- let morePagesExist = true;
94
- while (morePagesExist) {
95
- const response = await this.client.kv.namespaces.list({
96
- account_id: this.accountId,
97
- page: currentPage,
98
- per_page: perPage
99
- });
100
- if (response.result) {
101
- allNamespaces = allNamespaces.concat(response.result);
102
- }
103
- morePagesExist = response.result ? response.result.length === perPage : false;
104
- if (morePagesExist) {
105
- currentPage++;
106
- }
107
- }
108
- return { result: allNamespaces };
109
144
  }
110
- async getNamespaceValue(tableName, key) {
145
+ async getThreadsByResourceId({ resourceId }) {
111
146
  try {
112
- if (this.bindings) {
113
- const binding = this.getBinding(tableName);
114
- const result = await binding.getWithMetadata(key, "text");
115
- if (!result) return null;
116
- return JSON.stringify(result);
117
- } else {
118
- const namespaceId = await this.getNamespaceId(tableName);
119
- const response = await this.client.kv.namespaces.values.get(namespaceId, key, {
120
- account_id: this.accountId
121
- });
122
- return await response.text();
123
- }
147
+ const keyList = await this.operations.listKV(TABLE_THREADS);
148
+ const threads = await Promise.all(
149
+ keyList.map(async (keyObj) => {
150
+ try {
151
+ const data = await this.operations.getKV(TABLE_THREADS, keyObj.name);
152
+ if (!data) return null;
153
+ const thread = typeof data === "string" ? JSON.parse(data) : data;
154
+ if (!thread || !thread.resourceId || thread.resourceId !== resourceId) return null;
155
+ return {
156
+ ...thread,
157
+ createdAt: ensureDate(thread.createdAt),
158
+ updatedAt: ensureDate(thread.updatedAt),
159
+ metadata: this.ensureMetadata(thread.metadata)
160
+ };
161
+ } catch (error) {
162
+ const mastraError = new MastraError(
163
+ {
164
+ id: "CLOUDFLARE_STORAGE_GET_THREADS_BY_RESOURCE_ID_FAILED",
165
+ domain: ErrorDomain.STORAGE,
166
+ category: ErrorCategory.THIRD_PARTY,
167
+ details: {
168
+ resourceId
169
+ }
170
+ },
171
+ error
172
+ );
173
+ this.logger?.trackException(mastraError);
174
+ this.logger?.error(mastraError.toString());
175
+ return null;
176
+ }
177
+ })
178
+ );
179
+ return threads.filter((thread) => thread !== null);
124
180
  } catch (error) {
125
- if (error.message && error.message.includes("key not found")) {
126
- return null;
127
- }
128
- const message = error instanceof Error ? error.message : String(error);
129
- this.logger.error(`Failed to get value for ${tableName} ${key}:`, { message });
130
- throw error;
181
+ const mastraError = new MastraError(
182
+ {
183
+ id: "CLOUDFLARE_STORAGE_GET_THREADS_BY_RESOURCE_ID_FAILED",
184
+ domain: ErrorDomain.STORAGE,
185
+ category: ErrorCategory.THIRD_PARTY,
186
+ details: {
187
+ resourceId
188
+ }
189
+ },
190
+ error
191
+ );
192
+ this.logger?.trackException(mastraError);
193
+ this.logger?.error(mastraError.toString());
194
+ return [];
131
195
  }
132
196
  }
133
- async putNamespaceValue({
134
- tableName,
135
- key,
136
- value,
137
- metadata
138
- }) {
197
+ async getThreadsByResourceIdPaginated(args) {
139
198
  try {
140
- const serializedValue = this.safeSerialize(value);
141
- const serializedMetadata = metadata ? this.safeSerialize(metadata) : "";
142
- if (this.bindings) {
143
- const binding = this.getBinding(tableName);
144
- await binding.put(key, serializedValue, { metadata: serializedMetadata });
145
- } else {
146
- const namespaceId = await this.getNamespaceId(tableName);
147
- await this.client.kv.namespaces.values.update(namespaceId, key, {
148
- account_id: this.accountId,
149
- value: serializedValue,
150
- metadata: serializedMetadata
151
- });
199
+ const { resourceId, page = 0, perPage = 100 } = args;
200
+ const prefix = this.operations.namespacePrefix ? `${this.operations.namespacePrefix}:` : "";
201
+ const keyObjs = await this.operations.listKV(TABLE_THREADS, { prefix: `${prefix}${TABLE_THREADS}` });
202
+ const threads = [];
203
+ for (const { name: key } of keyObjs) {
204
+ const data = await this.operations.getKV(TABLE_THREADS, key);
205
+ if (!data) continue;
206
+ if (data.resourceId !== resourceId) continue;
207
+ threads.push(data);
152
208
  }
209
+ threads.sort((a, b) => {
210
+ const aTime = new Date(a.createdAt || 0).getTime();
211
+ const bTime = new Date(b.createdAt || 0).getTime();
212
+ return bTime - aTime;
213
+ });
214
+ const start = page * perPage;
215
+ const end = start + perPage;
216
+ const paginatedThreads = threads.slice(start, end);
217
+ return {
218
+ page,
219
+ perPage,
220
+ total: threads.length,
221
+ hasMore: start + perPage < threads.length,
222
+ threads: paginatedThreads
223
+ };
153
224
  } catch (error) {
154
- const message = error instanceof Error ? error.message : String(error);
155
- this.logger.error(`Failed to put value for ${tableName} ${key}:`, { message });
156
- throw error;
225
+ throw new MastraError(
226
+ {
227
+ id: "CLOUDFLARE_STORAGE_GET_THREADS_BY_RESOURCE_ID_PAGINATED_FAILED",
228
+ domain: ErrorDomain.STORAGE,
229
+ category: ErrorCategory.THIRD_PARTY,
230
+ text: "Failed to get threads by resource ID with pagination"
231
+ },
232
+ error
233
+ );
157
234
  }
158
235
  }
159
- async deleteNamespaceValue(tableName, key) {
160
- if (this.bindings) {
161
- const binding = this.getBinding(tableName);
162
- await binding.delete(key);
163
- } else {
164
- const namespaceId = await this.getNamespaceId(tableName);
165
- await this.client.kv.namespaces.values.delete(namespaceId, key, {
166
- account_id: this.accountId
167
- });
236
+ async saveThread({ thread }) {
237
+ try {
238
+ await this.operations.insert({ tableName: TABLE_THREADS, record: thread });
239
+ return thread;
240
+ } catch (error) {
241
+ throw new MastraError(
242
+ {
243
+ id: "CLOUDFLARE_STORAGE_SAVE_THREAD_FAILED",
244
+ domain: ErrorDomain.STORAGE,
245
+ category: ErrorCategory.THIRD_PARTY,
246
+ details: {
247
+ threadId: thread.id
248
+ }
249
+ },
250
+ error
251
+ );
168
252
  }
169
253
  }
170
- async listNamespaceKeys(tableName, options) {
254
+ async updateThread({
255
+ id,
256
+ title,
257
+ metadata
258
+ }) {
171
259
  try {
172
- if (this.bindings) {
173
- const binding = this.getBinding(tableName);
174
- const response = await binding.list({
175
- limit: options?.limit || 1e3,
176
- prefix: options?.prefix
177
- });
178
- return response.keys;
179
- } else {
180
- const namespaceId = await this.getNamespaceId(tableName);
181
- const response = await this.client.kv.namespaces.keys.list(namespaceId, {
182
- account_id: this.accountId,
183
- limit: options?.limit || 1e3,
184
- prefix: options?.prefix
185
- });
186
- return response.result;
260
+ const thread = await this.getThreadById({ threadId: id });
261
+ if (!thread) {
262
+ throw new Error(`Thread ${id} not found`);
187
263
  }
264
+ const updatedThread = {
265
+ ...thread,
266
+ title,
267
+ metadata: this.ensureMetadata({
268
+ ...thread.metadata ?? {},
269
+ ...metadata
270
+ }),
271
+ updatedAt: /* @__PURE__ */ new Date()
272
+ };
273
+ await this.operations.insert({ tableName: TABLE_THREADS, record: updatedThread });
274
+ return updatedThread;
188
275
  } catch (error) {
189
276
  throw new MastraError(
190
277
  {
191
- id: "CLOUDFLARE_STORAGE_LIST_NAMESPACE_KEYS_FAILED",
278
+ id: "CLOUDFLARE_STORAGE_UPDATE_THREAD_FAILED",
192
279
  domain: ErrorDomain.STORAGE,
193
280
  category: ErrorCategory.THIRD_PARTY,
194
281
  details: {
195
- tableName
282
+ threadId: id,
283
+ title
196
284
  }
197
285
  },
198
286
  error
199
287
  );
200
288
  }
201
289
  }
202
- async createNamespaceById(title) {
203
- if (this.bindings) {
204
- return {
205
- id: title,
206
- // Use title as ID since that's what we need
207
- title,
208
- supports_url_encoding: true
209
- };
210
- }
211
- return await this.client.kv.namespaces.create({
212
- account_id: this.accountId,
213
- title
214
- });
215
- }
216
- async getNamespaceIdByName(namespaceName) {
290
+ getMessageKey(threadId, messageId) {
217
291
  try {
218
- const response = await this.listNamespaces();
219
- const namespace = response.result.find((ns) => ns.title === namespaceName);
220
- return namespace ? namespace.id : null;
292
+ return this.operations.getKey(TABLE_MESSAGES, { threadId, id: messageId });
221
293
  } catch (error) {
222
- this.logger.error(`Failed to get namespace ID for ${namespaceName}:`, error);
223
- return null;
294
+ const message = error instanceof Error ? error.message : String(error);
295
+ this.logger.error(`Error getting message key for thread ${threadId} and message ${messageId}:`, { message });
296
+ throw error;
224
297
  }
225
298
  }
226
- async createNamespace(namespaceName) {
299
+ getThreadMessagesKey(threadId) {
227
300
  try {
228
- const response = await this.createNamespaceById(namespaceName);
229
- return response.id;
301
+ return this.operations.getKey(TABLE_MESSAGES, { threadId, id: "messages" });
230
302
  } catch (error) {
231
- if (error.message && error.message.includes("already exists")) {
232
- const namespaces = await this.listNamespaces();
233
- const namespace = namespaces.result.find((ns) => ns.title === namespaceName);
234
- if (namespace) return namespace.id;
235
- }
236
- this.logger.error("Error creating namespace:", error);
237
- throw new Error(`Failed to create namespace ${namespaceName}: ${error.message}`);
238
- }
239
- }
240
- async getOrCreateNamespaceId(namespaceName) {
241
- let namespaceId = await this.getNamespaceIdByName(namespaceName);
242
- if (!namespaceId) {
243
- namespaceId = await this.createNamespace(namespaceName);
303
+ const message = error instanceof Error ? error.message : String(error);
304
+ this.logger.error(`Error getting thread messages key for thread ${threadId}:`, { message });
305
+ throw error;
244
306
  }
245
- return namespaceId;
246
307
  }
247
- async getNamespaceId(tableName) {
248
- const prefix = this.namespacePrefix ? `${this.namespacePrefix}_` : "";
308
+ async deleteThread({ threadId }) {
249
309
  try {
250
- const legacyNamespaceId = await this.checkLegacyNamespace(tableName, prefix);
251
- if (legacyNamespaceId) {
252
- return legacyNamespaceId;
310
+ const thread = await this.getThreadById({ threadId });
311
+ if (!thread) {
312
+ throw new Error(`Thread ${threadId} not found`);
253
313
  }
254
- return await this.getOrCreateNamespaceId(`${prefix}${tableName}`);
314
+ const messageKeys = await this.operations.listKV(TABLE_MESSAGES);
315
+ const threadMessageKeys = messageKeys.filter((key) => key.name.includes(`${TABLE_MESSAGES}:${threadId}:`));
316
+ await Promise.all([
317
+ // Delete message order
318
+ this.operations.deleteKV(TABLE_MESSAGES, this.getThreadMessagesKey(threadId)),
319
+ // Delete all messages
320
+ ...threadMessageKeys.map((key) => this.operations.deleteKV(TABLE_MESSAGES, key.name)),
321
+ // Delete thread
322
+ this.operations.deleteKV(TABLE_THREADS, this.operations.getKey(TABLE_THREADS, { id: threadId }))
323
+ ]);
255
324
  } catch (error) {
256
- this.logger.error("Error fetching namespace ID:", error);
257
- throw new Error(`Failed to fetch namespace ID for table ${tableName}: ${error.message}`);
325
+ throw new MastraError(
326
+ {
327
+ id: "CLOUDFLARE_STORAGE_DELETE_THREAD_FAILED",
328
+ domain: ErrorDomain.STORAGE,
329
+ category: ErrorCategory.THIRD_PARTY,
330
+ details: {
331
+ threadId
332
+ }
333
+ },
334
+ error
335
+ );
258
336
  }
259
337
  }
260
- LEGACY_NAMESPACE_MAP = {
261
- [TABLE_MESSAGES]: TABLE_THREADS,
262
- [TABLE_WORKFLOW_SNAPSHOT]: "mastra_workflows",
263
- [TABLE_TRACES]: TABLE_EVALS
264
- };
265
- /**
266
- * There were a few legacy mappings for tables such as
267
- * - messages -> threads
268
- * - workflow_snapshot -> mastra_workflows
269
- * - traces -> evals
270
- * This has been updated to use dedicated namespaces for each table.
271
- * In the case of data for a table existing in the legacy namespace, warn the user to migrate to the new namespace.
272
- *
273
- * @param tableName The table name to check for legacy data
274
- * @param prefix The namespace prefix
275
- * @returns The legacy namespace ID if data exists; otherwise, null
276
- */
277
- async checkLegacyNamespace(tableName, prefix) {
278
- const legacyNamespaceBase = this.LEGACY_NAMESPACE_MAP[tableName];
279
- if (legacyNamespaceBase) {
280
- const legacyNamespace = `${prefix}${legacyNamespaceBase}`;
281
- const keyPrefix = this.namespacePrefix ? `${this.namespacePrefix}:` : "";
282
- const prefixKey = `${keyPrefix}${tableName}:`;
283
- const legacyId = await this.getNamespaceIdByName(legacyNamespace);
284
- if (legacyId) {
285
- const response = await this.client.kv.namespaces.keys.list(legacyId, {
286
- account_id: this.accountId,
287
- prefix: prefixKey
288
- });
289
- const keys = response.result;
290
- const hasTableData = keys.length > 0;
291
- if (hasTableData) {
292
- this.logger.warn(
293
- `Using legacy namespace "${legacyNamespace}" for ${tableName}. Consider migrating to a dedicated namespace "${prefix}${tableName}".`
294
- );
295
- return legacyId;
338
+ async findMessageInAnyThread(messageId) {
339
+ try {
340
+ const prefix = this.operations.namespacePrefix ? `${this.operations.namespacePrefix}:` : "";
341
+ const threadKeys = await this.operations.listKV(TABLE_THREADS, { prefix: `${prefix}${TABLE_THREADS}` });
342
+ for (const { name: threadKey } of threadKeys) {
343
+ const threadId = threadKey.split(":").pop();
344
+ if (!threadId || threadId === "messages") continue;
345
+ const messageKey = this.getMessageKey(threadId, messageId);
346
+ const message = await this.operations.getKV(TABLE_MESSAGES, messageKey);
347
+ if (message) {
348
+ return { ...message, threadId };
296
349
  }
297
350
  }
351
+ return null;
352
+ } catch (error) {
353
+ this.logger?.error(`Error finding message ${messageId} in any thread:`, error);
354
+ return null;
298
355
  }
299
- return null;
300
356
  }
301
357
  /**
302
- * Helper to safely serialize data for KV storage
358
+ * Queue for serializing sorted order updates.
359
+ * Updates the sorted order for a given key. This operation is eventually consistent.
303
360
  */
304
- safeSerialize(data) {
305
- return typeof data === "string" ? data : JSON.stringify(data);
361
+ updateQueue = /* @__PURE__ */ new Map();
362
+ async updateSorting(threadMessages) {
363
+ return threadMessages.map((msg) => ({
364
+ message: msg,
365
+ // Use _index if available, otherwise timestamp, matching Upstash
366
+ score: msg._index !== void 0 ? msg._index : msg.createdAt.getTime()
367
+ })).sort((a, b) => a.score - b.score).map((item) => ({
368
+ id: item.message.id,
369
+ score: item.score
370
+ }));
306
371
  }
307
372
  /**
308
- * Helper to safely parse data from KV storage
373
+ * Updates the sorted order for a given key. This operation is eventually consistent.
374
+ * Note: Operations on the same orderKey are serialized using a queue to prevent
375
+ * concurrent updates from conflicting with each other.
309
376
  */
310
- safeParse(text) {
311
- if (!text) return null;
312
- try {
313
- const data = JSON.parse(text);
314
- if (data && typeof data === "object" && "value" in data) {
315
- if (typeof data.value === "string") {
316
- try {
317
- return JSON.parse(data.value);
318
- } catch {
319
- return data.value;
320
- }
377
+ async updateSortedMessages(orderKey, newEntries) {
378
+ const currentPromise = this.updateQueue.get(orderKey) || Promise.resolve();
379
+ const nextPromise = currentPromise.then(async () => {
380
+ try {
381
+ const currentOrder = await this.getSortedMessages(orderKey);
382
+ const orderMap = new Map(currentOrder.map((entry) => [entry.id, entry]));
383
+ for (const entry of newEntries) {
384
+ orderMap.set(entry.id, entry);
385
+ }
386
+ const updatedOrder = Array.from(orderMap.values()).sort((a, b) => a.score - b.score);
387
+ await this.operations.putKV({
388
+ tableName: TABLE_MESSAGES,
389
+ key: orderKey,
390
+ value: JSON.stringify(updatedOrder)
391
+ });
392
+ } catch (error) {
393
+ const message = error instanceof Error ? error.message : String(error);
394
+ this.logger.error(`Error updating sorted order for key ${orderKey}:`, { message });
395
+ throw error;
396
+ } finally {
397
+ if (this.updateQueue.get(orderKey) === nextPromise) {
398
+ this.updateQueue.delete(orderKey);
321
399
  }
322
- return null;
323
400
  }
324
- return data;
325
- } catch (error) {
326
- const message = error instanceof Error ? error.message : String(error);
327
- this.logger.error("Failed to parse text:", { message, text });
328
- return null;
329
- }
401
+ });
402
+ this.updateQueue.set(orderKey, nextPromise);
403
+ return nextPromise;
330
404
  }
331
- async putKV({
332
- tableName,
333
- key,
334
- value,
335
- metadata
336
- }) {
405
+ async getSortedMessages(orderKey) {
406
+ const raw = await this.operations.getKV(TABLE_MESSAGES, orderKey);
407
+ if (!raw) return [];
337
408
  try {
338
- await this.putNamespaceValue({ tableName, key, value, metadata });
339
- } catch (error) {
340
- this.logger.error(`Failed to put KV value for ${tableName}:${key}:`, error);
341
- throw new Error(`Failed to put KV value: ${error.message}`);
409
+ const arr = JSON.parse(typeof raw === "string" ? raw : JSON.stringify(raw));
410
+ return Array.isArray(arr) ? arr : [];
411
+ } catch (e) {
412
+ this.logger.error(`Error parsing order data for key ${orderKey}:`, { e });
413
+ return [];
342
414
  }
343
415
  }
344
- async getKV(tableName, key) {
416
+ async migrateMessage(messageId, fromThreadId, toThreadId) {
345
417
  try {
346
- const text = await this.getNamespaceValue(tableName, key);
347
- return this.safeParse(text);
418
+ const oldMessageKey = this.getMessageKey(fromThreadId, messageId);
419
+ const message = await this.operations.getKV(TABLE_MESSAGES, oldMessageKey);
420
+ if (!message) return;
421
+ const updatedMessage = {
422
+ ...message,
423
+ threadId: toThreadId
424
+ };
425
+ const newMessageKey = this.getMessageKey(toThreadId, messageId);
426
+ await this.operations.putKV({ tableName: TABLE_MESSAGES, key: newMessageKey, value: updatedMessage });
427
+ const oldOrderKey = this.getThreadMessagesKey(fromThreadId);
428
+ const oldEntries = await this.getSortedMessages(oldOrderKey);
429
+ const filteredEntries = oldEntries.filter((entry) => entry.id !== messageId);
430
+ await this.updateSortedMessages(oldOrderKey, filteredEntries);
431
+ const newOrderKey = this.getThreadMessagesKey(toThreadId);
432
+ const newEntries = await this.getSortedMessages(newOrderKey);
433
+ const newEntry = { id: messageId, score: Date.now() };
434
+ newEntries.push(newEntry);
435
+ await this.updateSortedMessages(newOrderKey, newEntries);
436
+ await this.operations.deleteKV(TABLE_MESSAGES, oldMessageKey);
348
437
  } catch (error) {
349
- this.logger.error(`Failed to get KV value for ${tableName}:${key}:`, error);
350
- throw new Error(`Failed to get KV value: ${error.message}`);
438
+ this.logger?.error(`Error migrating message ${messageId} from ${fromThreadId} to ${toThreadId}:`, error);
439
+ throw error;
351
440
  }
352
441
  }
353
- async deleteKV(tableName, key) {
442
+ async saveMessages(args) {
443
+ const { messages, format = "v1" } = args;
444
+ if (!Array.isArray(messages) || messages.length === 0) return [];
354
445
  try {
355
- await this.deleteNamespaceValue(tableName, key);
446
+ const validatedMessages = messages.map((message, index) => {
447
+ const errors = [];
448
+ if (!message.id) errors.push("id is required");
449
+ if (!message.threadId) errors.push("threadId is required");
450
+ if (!message.content) errors.push("content is required");
451
+ if (!message.role) errors.push("role is required");
452
+ if (!message.createdAt) errors.push("createdAt is required");
453
+ if (message.resourceId === null || message.resourceId === void 0) errors.push("resourceId is required");
454
+ if (errors.length > 0) {
455
+ throw new Error(`Invalid message at index ${index}: ${errors.join(", ")}`);
456
+ }
457
+ return {
458
+ ...message,
459
+ createdAt: ensureDate(message.createdAt),
460
+ type: message.type || "v2",
461
+ _index: index
462
+ };
463
+ }).filter((m) => !!m);
464
+ const messageMigrationTasks = [];
465
+ for (const message of validatedMessages) {
466
+ const existingMessage = await this.findMessageInAnyThread(message.id);
467
+ console.info(`Checking message ${message.id}: existing=${existingMessage?.threadId}, new=${message.threadId}`);
468
+ if (existingMessage && existingMessage.threadId && existingMessage.threadId !== message.threadId) {
469
+ console.info(`Migrating message ${message.id} from ${existingMessage.threadId} to ${message.threadId}`);
470
+ messageMigrationTasks.push(this.migrateMessage(message.id, existingMessage.threadId, message.threadId));
471
+ }
472
+ }
473
+ await Promise.all(messageMigrationTasks);
474
+ const messagesByThread = validatedMessages.reduce((acc, message) => {
475
+ if (message.threadId && !acc.has(message.threadId)) {
476
+ acc.set(message.threadId, []);
477
+ }
478
+ if (message.threadId) {
479
+ acc.get(message.threadId).push(message);
480
+ }
481
+ return acc;
482
+ }, /* @__PURE__ */ new Map());
483
+ await Promise.all(
484
+ Array.from(messagesByThread.entries()).map(async ([threadId, threadMessages]) => {
485
+ try {
486
+ const thread = await this.getThreadById({ threadId });
487
+ if (!thread) {
488
+ throw new Error(`Thread ${threadId} not found`);
489
+ }
490
+ await Promise.all(
491
+ threadMessages.map(async (message) => {
492
+ const key = this.getMessageKey(threadId, message.id);
493
+ const { _index, ...cleanMessage } = message;
494
+ const serializedMessage = {
495
+ ...cleanMessage,
496
+ createdAt: serializeDate(cleanMessage.createdAt)
497
+ };
498
+ console.info(`Saving message ${message.id} with content:`, {
499
+ content: serializedMessage.content,
500
+ contentType: typeof serializedMessage.content,
501
+ isArray: Array.isArray(serializedMessage.content)
502
+ });
503
+ await this.operations.putKV({ tableName: TABLE_MESSAGES, key, value: serializedMessage });
504
+ })
505
+ );
506
+ const orderKey = this.getThreadMessagesKey(threadId);
507
+ const entries = await this.updateSorting(threadMessages);
508
+ await this.updateSortedMessages(orderKey, entries);
509
+ const updatedThread = {
510
+ ...thread,
511
+ updatedAt: /* @__PURE__ */ new Date()
512
+ };
513
+ await this.operations.putKV({
514
+ tableName: TABLE_THREADS,
515
+ key: this.operations.getKey(TABLE_THREADS, { id: threadId }),
516
+ value: updatedThread
517
+ });
518
+ } catch (error) {
519
+ throw new MastraError(
520
+ {
521
+ id: "CLOUDFLARE_STORAGE_SAVE_MESSAGES_FAILED",
522
+ domain: ErrorDomain.STORAGE,
523
+ category: ErrorCategory.THIRD_PARTY,
524
+ details: {
525
+ threadId
526
+ }
527
+ },
528
+ error
529
+ );
530
+ }
531
+ })
532
+ );
533
+ const prepared = validatedMessages.map(
534
+ ({ _index, ...message }) => ({ ...message, type: message.type !== "v2" ? message.type : void 0 })
535
+ );
536
+ const list = new MessageList().add(prepared, "memory");
537
+ if (format === `v2`) return list.get.all.v2();
538
+ return list.get.all.v1();
356
539
  } catch (error) {
357
- this.logger.error(`Failed to delete KV value for ${tableName}:${key}:`, error);
358
- throw new Error(`Failed to delete KV value: ${error.message}`);
540
+ throw new MastraError(
541
+ {
542
+ id: "CLOUDFLARE_STORAGE_SAVE_MESSAGES_FAILED",
543
+ domain: ErrorDomain.STORAGE,
544
+ category: ErrorCategory.THIRD_PARTY
545
+ },
546
+ error
547
+ );
359
548
  }
360
549
  }
361
- async listKV(tableName, options) {
362
- try {
363
- return await this.listNamespaceKeys(tableName, options);
364
- } catch (error) {
365
- this.logger.error(`Failed to list KV for ${tableName}:`, error);
366
- throw new Error(`Failed to list KV: ${error.message}`);
367
- }
550
+ async getRank(orderKey, id) {
551
+ const order = await this.getSortedMessages(orderKey);
552
+ const index = order.findIndex((item) => item.id === id);
553
+ return index >= 0 ? index : null;
368
554
  }
369
- /*---------------------------------------------------------------------------
370
- Sorted set simulation helpers for message ordering.
371
- We store an array of objects { id, score } as JSON under a dedicated key.
372
- ---------------------------------------------------------------------------*/
373
- async getSortedMessages(orderKey) {
374
- const raw = await this.getKV(TABLE_MESSAGES, orderKey);
375
- if (!raw) return [];
376
- try {
377
- const arr = JSON.parse(typeof raw === "string" ? raw : JSON.stringify(raw));
378
- return Array.isArray(arr) ? arr : [];
379
- } catch (e) {
380
- this.logger.error(`Error parsing order data for key ${orderKey}:`, { e });
381
- return [];
382
- }
555
+ async getRange(orderKey, start, end) {
556
+ const order = await this.getSortedMessages(orderKey);
557
+ const actualStart = start < 0 ? Math.max(0, order.length + start) : start;
558
+ const actualEnd = end < 0 ? order.length + end : Math.min(end, order.length - 1);
559
+ const sliced = order.slice(actualStart, actualEnd + 1);
560
+ return sliced.map((item) => item.id);
383
561
  }
384
- async updateSorting(threadMessages) {
385
- return threadMessages.map((msg) => ({
386
- message: msg,
387
- // Use _index if available, otherwise timestamp, matching Upstash
388
- score: msg._index !== void 0 ? msg._index : msg.createdAt.getTime()
389
- })).sort((a, b) => a.score - b.score).map((item) => ({
390
- id: item.message.id,
391
- score: item.score
392
- }));
562
+ async getLastN(orderKey, n) {
563
+ return this.getRange(orderKey, -n, -1);
564
+ }
565
+ async getFullOrder(orderKey) {
566
+ return this.getRange(orderKey, 0, -1);
393
567
  }
394
568
  async getIncludedMessagesWithContext(threadId, include, messageIds) {
395
- const threadMessagesKey = this.getThreadMessagesKey(threadId);
396
569
  await Promise.all(
397
570
  include.map(async (item) => {
571
+ const targetThreadId = item.threadId || threadId;
572
+ if (!targetThreadId) return;
573
+ const threadMessagesKey = this.getThreadMessagesKey(targetThreadId);
398
574
  messageIds.add(item.id);
399
575
  if (!item.withPreviousMessages && !item.withNextMessages) return;
400
576
  const rank = await this.getRank(threadMessagesKey, item.id);
@@ -415,23 +591,50 @@ var CloudflareStore = class extends MastraStorage {
415
591
  );
416
592
  }
417
593
  async getRecentMessages(threadId, limit, messageIds) {
594
+ if (!threadId.trim()) throw new Error("threadId must be a non-empty string");
418
595
  if (limit <= 0) return;
419
596
  try {
420
597
  const threadMessagesKey = this.getThreadMessagesKey(threadId);
421
598
  const latestIds = await this.getLastN(threadMessagesKey, limit);
422
599
  latestIds.forEach((id) => messageIds.add(id));
423
600
  } catch {
424
- console.log(`No message order found for thread ${threadId}, skipping latest messages`);
601
+ console.info(`No message order found for thread ${threadId}, skipping latest messages`);
425
602
  }
426
603
  }
427
- async fetchAndParseMessages(threadId, messageIds) {
604
+ async fetchAndParseMessagesFromMultipleThreads(messageIds, include, targetThreadId) {
605
+ const messageIdToThreadId = /* @__PURE__ */ new Map();
606
+ if (include) {
607
+ for (const item of include) {
608
+ if (item.threadId) {
609
+ messageIdToThreadId.set(item.id, item.threadId);
610
+ }
611
+ }
612
+ }
428
613
  const messages = await Promise.all(
429
614
  messageIds.map(async (id) => {
430
615
  try {
616
+ let threadId = messageIdToThreadId.get(id);
617
+ if (!threadId) {
618
+ if (targetThreadId) {
619
+ threadId = targetThreadId;
620
+ } else {
621
+ const foundMessage = await this.findMessageInAnyThread(id);
622
+ if (foundMessage) {
623
+ threadId = foundMessage.threadId;
624
+ }
625
+ }
626
+ }
627
+ if (!threadId) return null;
431
628
  const key = this.getMessageKey(threadId, id);
432
- const data = await this.getKV(TABLE_MESSAGES, key);
629
+ const data = await this.operations.getKV(TABLE_MESSAGES, key);
433
630
  if (!data) return null;
434
- return typeof data === "string" ? JSON.parse(data) : data;
631
+ const parsed = typeof data === "string" ? JSON.parse(data) : data;
632
+ console.info(`Retrieved message ${id} from thread ${threadId} with content:`, {
633
+ content: parsed.content,
634
+ contentType: typeof parsed.content,
635
+ isArray: Array.isArray(parsed.content)
636
+ });
637
+ return parsed;
435
638
  } catch (error) {
436
639
  const message = error instanceof Error ? error.message : String(error);
437
640
  this.logger.error(`Error retrieving message ${id}:`, { message });
@@ -441,61 +644,432 @@ var CloudflareStore = class extends MastraStorage {
441
644
  );
442
645
  return messages.filter((msg) => msg !== null);
443
646
  }
444
- /**
445
- * Queue for serializing sorted order updates.
446
- * Updates the sorted order for a given key. This operation is eventually consistent.
447
- */
448
- updateQueue = /* @__PURE__ */ new Map();
449
- /**
450
- * Updates the sorted order for a given key. This operation is eventually consistent.
451
- * Note: Operations on the same orderKey are serialized using a queue to prevent
452
- * concurrent updates from conflicting with each other.
453
- */
454
- async updateSortedMessages(orderKey, newEntries) {
455
- const currentPromise = this.updateQueue.get(orderKey) || Promise.resolve();
456
- const nextPromise = currentPromise.then(async () => {
647
+ async getMessages({
648
+ threadId,
649
+ resourceId,
650
+ selectBy,
651
+ format
652
+ }) {
653
+ console.info(`getMessages called with format: ${format}, threadId: ${threadId}`);
654
+ const actualFormat = format || "v1";
655
+ console.info(`Using format: ${actualFormat}`);
656
+ const limit = resolveMessageLimit({ last: selectBy?.last, defaultLimit: 40 });
657
+ const messageIds = /* @__PURE__ */ new Set();
658
+ if (limit === 0 && !selectBy?.include?.length) return [];
659
+ try {
660
+ if (!threadId.trim()) throw new Error("threadId must be a non-empty string");
661
+ await Promise.all([
662
+ selectBy?.include?.length ? this.getIncludedMessagesWithContext(threadId, selectBy.include, messageIds) : Promise.resolve(),
663
+ limit > 0 ? this.getRecentMessages(threadId, limit, messageIds) : Promise.resolve()
664
+ ]);
665
+ const targetThreadId = selectBy?.include?.length ? void 0 : threadId;
666
+ const messages = await this.fetchAndParseMessagesFromMultipleThreads(
667
+ Array.from(messageIds),
668
+ selectBy?.include,
669
+ targetThreadId
670
+ );
671
+ if (!messages.length) return [];
457
672
  try {
458
- const currentOrder = await this.getSortedMessages(orderKey);
459
- const orderMap = new Map(currentOrder.map((entry) => [entry.id, entry]));
460
- for (const entry of newEntries) {
461
- orderMap.set(entry.id, entry);
462
- }
463
- const updatedOrder = Array.from(orderMap.values()).sort((a, b) => a.score - b.score);
464
- await this.putKV({
465
- tableName: TABLE_MESSAGES,
466
- key: orderKey,
467
- value: JSON.stringify(updatedOrder)
673
+ const threadMessagesKey = this.getThreadMessagesKey(threadId);
674
+ const messageOrder = await this.getFullOrder(threadMessagesKey);
675
+ const orderMap = new Map(messageOrder.map((id, index) => [id, index]));
676
+ messages.sort((a, b) => {
677
+ const indexA = orderMap.get(a.id);
678
+ const indexB = orderMap.get(b.id);
679
+ if (indexA !== void 0 && indexB !== void 0) return orderMap.get(a.id) - orderMap.get(b.id);
680
+ return new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime();
468
681
  });
469
682
  } catch (error) {
470
- const message = error instanceof Error ? error.message : String(error);
471
- this.logger.error(`Error updating sorted order for key ${orderKey}:`, { message });
472
- throw error;
473
- } finally {
474
- if (this.updateQueue.get(orderKey) === nextPromise) {
475
- this.updateQueue.delete(orderKey);
683
+ const mastraError = new MastraError(
684
+ {
685
+ id: "CLOUDFLARE_STORAGE_SORT_MESSAGES_FAILED",
686
+ domain: ErrorDomain.STORAGE,
687
+ category: ErrorCategory.THIRD_PARTY,
688
+ text: `Error sorting messages for thread ${threadId} falling back to creation time`,
689
+ details: {
690
+ threadId
691
+ }
692
+ },
693
+ error
694
+ );
695
+ this.logger?.trackException(mastraError);
696
+ this.logger?.error(mastraError.toString());
697
+ messages.sort((a, b) => new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime());
698
+ }
699
+ const prepared = messages.map(({ _index, ...message }) => ({
700
+ ...message,
701
+ type: message.type === `v2` ? void 0 : message.type,
702
+ createdAt: ensureDate(message.createdAt)
703
+ }));
704
+ if (actualFormat === `v1`) {
705
+ console.info(`Processing ${prepared.length} messages for v1 format - returning directly without MessageList`);
706
+ return prepared.map((msg) => ({
707
+ ...msg,
708
+ createdAt: new Date(msg.createdAt)
709
+ }));
710
+ }
711
+ const list = new MessageList({ threadId, resourceId }).add(prepared, "memory");
712
+ return list.get.all.v2();
713
+ } catch (error) {
714
+ const mastraError = new MastraError(
715
+ {
716
+ id: "CLOUDFLARE_STORAGE_GET_MESSAGES_FAILED",
717
+ domain: ErrorDomain.STORAGE,
718
+ category: ErrorCategory.THIRD_PARTY,
719
+ text: `Error retrieving messages for thread ${threadId}`,
720
+ details: {
721
+ threadId,
722
+ resourceId: resourceId ?? ""
723
+ }
724
+ },
725
+ error
726
+ );
727
+ this.logger?.trackException(mastraError);
728
+ this.logger?.error(mastraError.toString());
729
+ return [];
730
+ }
731
+ }
732
+ async getMessagesById({
733
+ messageIds,
734
+ format
735
+ }) {
736
+ if (messageIds.length === 0) return [];
737
+ try {
738
+ const messages = (await Promise.all(messageIds.map((id) => this.findMessageInAnyThread(id)))).filter(
739
+ (result) => !!result
740
+ );
741
+ const prepared = messages.map(({ _index, ...message }) => ({
742
+ ...message,
743
+ ...message.type !== `v2` && { type: message.type },
744
+ createdAt: ensureDate(message.createdAt)
745
+ }));
746
+ const list = new MessageList().add(prepared, "memory");
747
+ if (format === `v1`) return list.get.all.v1();
748
+ return list.get.all.v2();
749
+ } catch (error) {
750
+ const mastraError = new MastraError(
751
+ {
752
+ id: "CLOUDFLARE_STORAGE_GET_MESSAGES_BY_ID_FAILED",
753
+ domain: ErrorDomain.STORAGE,
754
+ category: ErrorCategory.THIRD_PARTY,
755
+ text: `Error retrieving messages by ID`,
756
+ details: {
757
+ messageIds: JSON.stringify(messageIds)
758
+ }
759
+ },
760
+ error
761
+ );
762
+ this.logger?.trackException(mastraError);
763
+ this.logger?.error(mastraError.toString());
764
+ return [];
765
+ }
766
+ }
767
+ async getMessagesPaginated(args) {
768
+ const { threadId, resourceId, selectBy, format = "v1" } = args;
769
+ const { page = 0, perPage = 100 } = selectBy?.pagination || {};
770
+ try {
771
+ if (!threadId.trim()) throw new Error("threadId must be a non-empty string");
772
+ const messages = format === "v2" ? await this.getMessages({ threadId, selectBy, format: "v2" }) : await this.getMessages({ threadId, selectBy, format: "v1" });
773
+ let filteredMessages = messages;
774
+ if (selectBy?.pagination?.dateRange) {
775
+ const { start: dateStart, end: dateEnd } = selectBy.pagination.dateRange;
776
+ filteredMessages = messages.filter((message) => {
777
+ const messageDate = new Date(message.createdAt);
778
+ if (dateStart && messageDate < dateStart) return false;
779
+ if (dateEnd && messageDate > dateEnd) return false;
780
+ return true;
781
+ });
782
+ }
783
+ const start = page * perPage;
784
+ const end = start + perPage;
785
+ const paginatedMessages = filteredMessages.slice(start, end);
786
+ return {
787
+ page,
788
+ perPage,
789
+ total: filteredMessages.length,
790
+ hasMore: start + perPage < filteredMessages.length,
791
+ messages: paginatedMessages
792
+ };
793
+ } catch (error) {
794
+ const mastraError = new MastraError(
795
+ {
796
+ id: "CLOUDFLARE_STORAGE_GET_MESSAGES_PAGINATED_FAILED",
797
+ domain: ErrorDomain.STORAGE,
798
+ category: ErrorCategory.THIRD_PARTY,
799
+ text: "Failed to get messages with pagination",
800
+ details: {
801
+ threadId,
802
+ resourceId: resourceId ?? ""
803
+ }
804
+ },
805
+ error
806
+ );
807
+ this.logger?.trackException?.(mastraError);
808
+ this.logger?.error?.(mastraError.toString());
809
+ return { messages: [], total: 0, page, perPage: perPage || 40, hasMore: false };
810
+ }
811
+ }
812
+ async updateMessages(args) {
813
+ try {
814
+ const { messages } = args;
815
+ const updatedMessages = [];
816
+ for (const messageUpdate of messages) {
817
+ const { id, content, ...otherFields } = messageUpdate;
818
+ const prefix = this.operations.namespacePrefix ? `${this.operations.namespacePrefix}:` : "";
819
+ const keyObjs = await this.operations.listKV(TABLE_MESSAGES, { prefix: `${prefix}${TABLE_MESSAGES}` });
820
+ let existingMessage = null;
821
+ let messageKey = "";
822
+ for (const { name: key } of keyObjs) {
823
+ const data = await this.operations.getKV(TABLE_MESSAGES, key);
824
+ if (data && data.id === id) {
825
+ existingMessage = data;
826
+ messageKey = key;
827
+ break;
828
+ }
829
+ }
830
+ if (!existingMessage) {
831
+ continue;
832
+ }
833
+ const updatedMessage = {
834
+ ...existingMessage,
835
+ ...otherFields,
836
+ id
837
+ };
838
+ if (content) {
839
+ if (content.metadata !== void 0) {
840
+ updatedMessage.content = {
841
+ ...updatedMessage.content,
842
+ metadata: {
843
+ ...updatedMessage.content?.metadata,
844
+ ...content.metadata
845
+ }
846
+ };
847
+ }
848
+ if (content.content !== void 0) {
849
+ updatedMessage.content = {
850
+ ...updatedMessage.content,
851
+ content: content.content
852
+ };
853
+ }
854
+ }
855
+ if ("threadId" in messageUpdate && messageUpdate.threadId && messageUpdate.threadId !== existingMessage.threadId) {
856
+ await this.operations.deleteKV(TABLE_MESSAGES, messageKey);
857
+ updatedMessage.threadId = messageUpdate.threadId;
858
+ const newMessageKey = this.getMessageKey(messageUpdate.threadId, id);
859
+ await this.operations.putKV({
860
+ tableName: TABLE_MESSAGES,
861
+ key: newMessageKey,
862
+ value: updatedMessage
863
+ });
864
+ if (existingMessage.threadId) {
865
+ const sourceOrderKey = this.getThreadMessagesKey(existingMessage.threadId);
866
+ const sourceEntries = await this.getSortedMessages(sourceOrderKey);
867
+ const filteredEntries = sourceEntries.filter((entry) => entry.id !== id);
868
+ await this.updateSortedMessages(sourceOrderKey, filteredEntries);
869
+ }
870
+ const destOrderKey = this.getThreadMessagesKey(messageUpdate.threadId);
871
+ const destEntries = await this.getSortedMessages(destOrderKey);
872
+ const newEntry = { id, score: Date.now() };
873
+ destEntries.push(newEntry);
874
+ await this.updateSortedMessages(destOrderKey, destEntries);
875
+ } else {
876
+ await this.operations.putKV({
877
+ tableName: TABLE_MESSAGES,
878
+ key: messageKey,
879
+ value: updatedMessage
880
+ });
881
+ }
882
+ const threadsToUpdate = /* @__PURE__ */ new Set();
883
+ if (updatedMessage.threadId) {
884
+ threadsToUpdate.add(updatedMessage.threadId);
885
+ }
886
+ if ("threadId" in messageUpdate && messageUpdate.threadId && messageUpdate.threadId !== existingMessage.threadId) {
887
+ if (existingMessage.threadId) {
888
+ threadsToUpdate.add(existingMessage.threadId);
889
+ }
890
+ threadsToUpdate.add(messageUpdate.threadId);
891
+ }
892
+ for (const threadId of threadsToUpdate) {
893
+ const thread = await this.getThreadById({ threadId });
894
+ if (thread) {
895
+ const updatedThread = {
896
+ ...thread,
897
+ updatedAt: /* @__PURE__ */ new Date()
898
+ };
899
+ await this.operations.putKV({
900
+ tableName: TABLE_THREADS,
901
+ key: this.operations.getKey(TABLE_THREADS, { id: threadId }),
902
+ value: updatedThread
903
+ });
904
+ }
476
905
  }
906
+ updatedMessages.push(updatedMessage);
477
907
  }
478
- });
479
- this.updateQueue.set(orderKey, nextPromise);
480
- return nextPromise;
908
+ return updatedMessages;
909
+ } catch (error) {
910
+ throw new MastraError(
911
+ {
912
+ id: "CLOUDFLARE_STORAGE_UPDATE_MESSAGES_FAILED",
913
+ domain: ErrorDomain.STORAGE,
914
+ category: ErrorCategory.THIRD_PARTY,
915
+ text: "Failed to update messages"
916
+ },
917
+ error
918
+ );
919
+ }
920
+ }
921
+ async getResourceById({ resourceId }) {
922
+ try {
923
+ const data = await this.operations.getKV(TABLE_RESOURCES, resourceId);
924
+ if (!data) return null;
925
+ const resource = typeof data === "string" ? JSON.parse(data) : data;
926
+ return {
927
+ ...resource,
928
+ createdAt: ensureDate(resource.createdAt),
929
+ updatedAt: ensureDate(resource.updatedAt),
930
+ metadata: this.ensureMetadata(resource.metadata)
931
+ };
932
+ } catch (error) {
933
+ const mastraError = new MastraError(
934
+ {
935
+ id: "CLOUDFLARE_STORAGE_GET_RESOURCE_BY_ID_FAILED",
936
+ domain: ErrorDomain.STORAGE,
937
+ category: ErrorCategory.THIRD_PARTY,
938
+ details: {
939
+ resourceId
940
+ }
941
+ },
942
+ error
943
+ );
944
+ this.logger?.trackException(mastraError);
945
+ this.logger?.error(mastraError.toString());
946
+ return null;
947
+ }
948
+ }
949
+ async saveResource({ resource }) {
950
+ try {
951
+ const resourceToSave = {
952
+ ...resource,
953
+ metadata: resource.metadata ? JSON.stringify(resource.metadata) : null
954
+ };
955
+ await this.operations.putKV({
956
+ tableName: TABLE_RESOURCES,
957
+ key: resource.id,
958
+ value: resourceToSave
959
+ });
960
+ return resource;
961
+ } catch (error) {
962
+ throw new MastraError(
963
+ {
964
+ id: "CLOUDFLARE_STORAGE_SAVE_RESOURCE_FAILED",
965
+ domain: ErrorDomain.STORAGE,
966
+ category: ErrorCategory.THIRD_PARTY,
967
+ details: {
968
+ resourceId: resource.id
969
+ }
970
+ },
971
+ error
972
+ );
973
+ }
974
+ }
975
+ async updateResource({
976
+ resourceId,
977
+ workingMemory,
978
+ metadata
979
+ }) {
980
+ const existingResource = await this.getResourceById({ resourceId });
981
+ if (!existingResource) {
982
+ const newResource = {
983
+ id: resourceId,
984
+ workingMemory,
985
+ metadata: metadata || {},
986
+ createdAt: /* @__PURE__ */ new Date(),
987
+ updatedAt: /* @__PURE__ */ new Date()
988
+ };
989
+ return this.saveResource({ resource: newResource });
990
+ }
991
+ const updatedAt = /* @__PURE__ */ new Date();
992
+ const updatedResource = {
993
+ ...existingResource,
994
+ workingMemory: workingMemory !== void 0 ? workingMemory : existingResource.workingMemory,
995
+ metadata: {
996
+ ...existingResource.metadata,
997
+ ...metadata
998
+ },
999
+ updatedAt
1000
+ };
1001
+ return this.saveResource({ resource: updatedResource });
1002
+ }
1003
+ };
1004
+ var StoreOperationsCloudflare = class extends StoreOperations {
1005
+ bindings;
1006
+ client;
1007
+ accountId;
1008
+ namespacePrefix;
1009
+ constructor({
1010
+ namespacePrefix,
1011
+ bindings,
1012
+ client,
1013
+ accountId
1014
+ }) {
1015
+ super();
1016
+ this.bindings = bindings;
1017
+ this.namespacePrefix = namespacePrefix;
1018
+ this.client = client;
1019
+ this.accountId = accountId;
481
1020
  }
482
- async getRank(orderKey, id) {
483
- const order = await this.getSortedMessages(orderKey);
484
- const index = order.findIndex((item) => item.id === id);
485
- return index >= 0 ? index : null;
1021
+ async hasColumn() {
1022
+ return true;
486
1023
  }
487
- async getRange(orderKey, start, end) {
488
- const order = await this.getSortedMessages(orderKey);
489
- const actualStart = start < 0 ? Math.max(0, order.length + start) : start;
490
- const actualEnd = end < 0 ? order.length + end : Math.min(end, order.length - 1);
491
- const sliced = order.slice(actualStart, actualEnd + 1);
492
- return sliced.map((item) => item.id);
1024
+ async alterTable(_args) {
493
1025
  }
494
- async getLastN(orderKey, n) {
495
- return this.getRange(orderKey, -n, -1);
1026
+ async clearTable({ tableName }) {
1027
+ try {
1028
+ const keys = await this.listKV(tableName);
1029
+ if (keys.length > 0) {
1030
+ await Promise.all(keys.map((keyObj) => this.deleteKV(tableName, keyObj.name)));
1031
+ }
1032
+ } catch (error) {
1033
+ throw new MastraError(
1034
+ {
1035
+ id: "CLOUDFLARE_STORAGE_CLEAR_TABLE_FAILED",
1036
+ domain: ErrorDomain.STORAGE,
1037
+ category: ErrorCategory.THIRD_PARTY,
1038
+ details: {
1039
+ tableName
1040
+ }
1041
+ },
1042
+ error
1043
+ );
1044
+ }
496
1045
  }
497
- async getFullOrder(orderKey) {
498
- return this.getRange(orderKey, 0, -1);
1046
+ async dropTable({ tableName }) {
1047
+ try {
1048
+ const keys = await this.listKV(tableName);
1049
+ if (keys.length > 0) {
1050
+ await Promise.all(keys.map((keyObj) => this.deleteKV(tableName, keyObj.name)));
1051
+ }
1052
+ } catch (error) {
1053
+ throw new MastraError(
1054
+ {
1055
+ id: "CLOUDFLARE_STORAGE_DROP_TABLE_FAILED",
1056
+ domain: ErrorDomain.STORAGE,
1057
+ category: ErrorCategory.THIRD_PARTY,
1058
+ details: {
1059
+ tableName
1060
+ }
1061
+ },
1062
+ error
1063
+ );
1064
+ }
1065
+ }
1066
+ getBinding(tableName) {
1067
+ if (!this.bindings) {
1068
+ throw new Error(`Cannot use Workers API binding for ${tableName}: Store initialized with REST API configuration`);
1069
+ }
1070
+ const binding = this.bindings[tableName];
1071
+ if (!binding) throw new Error(`No binding found for namespace ${tableName}`);
1072
+ return binding;
499
1073
  }
500
1074
  getKey(tableName, record) {
501
1075
  const prefix = this.namespacePrefix ? `${this.namespacePrefix}:` : "";
@@ -507,10 +1081,10 @@ var CloudflareStore = class extends MastraStorage {
507
1081
  if (!record.threadId || !record.id) throw new Error("Thread ID and Message ID are required");
508
1082
  return `${prefix}${tableName}:${record.threadId}:${record.id}`;
509
1083
  case TABLE_WORKFLOW_SNAPSHOT:
510
- if (!record.namespace || !record.workflow_name || !record.run_id) {
511
- throw new Error("Namespace, workflow name, and run ID are required");
1084
+ if (!record.workflow_name || !record.run_id) {
1085
+ throw new Error("Workflow name, and run ID are required");
512
1086
  }
513
- let key = `${prefix}${tableName}:${record.namespace}:${record.workflow_name}:${record.run_id}`;
1087
+ let key = `${prefix}${tableName}:${record.workflow_name}:${record.run_id}`;
514
1088
  if (record.resourceId) {
515
1089
  key = `${key}:${record.resourceId}`;
516
1090
  }
@@ -518,6 +1092,13 @@ var CloudflareStore = class extends MastraStorage {
518
1092
  case TABLE_TRACES:
519
1093
  if (!record.id) throw new Error("Trace ID is required");
520
1094
  return `${prefix}${tableName}:${record.id}`;
1095
+ case TABLE_EVALS:
1096
+ const evalId = record.id || record.run_id;
1097
+ if (!evalId) throw new Error("Eval ID or run_id is required");
1098
+ return `${prefix}${tableName}:${evalId}`;
1099
+ case TABLE_SCORERS:
1100
+ if (!record.id) throw new Error("Score ID is required");
1101
+ return `${prefix}${tableName}:${record.id}`;
521
1102
  default:
522
1103
  throw new Error(`Unsupported table: ${tableName}`);
523
1104
  }
@@ -526,6 +1107,146 @@ var CloudflareStore = class extends MastraStorage {
526
1107
  const prefix = this.namespacePrefix ? `${this.namespacePrefix}:` : "";
527
1108
  return `${prefix}schema:${tableName}`;
528
1109
  }
1110
+ /**
1111
+ * Helper to safely parse data from KV storage
1112
+ */
1113
+ safeParse(text) {
1114
+ if (!text) return null;
1115
+ try {
1116
+ const data = JSON.parse(text);
1117
+ if (data && typeof data === "object" && "value" in data) {
1118
+ if (typeof data.value === "string") {
1119
+ try {
1120
+ return JSON.parse(data.value);
1121
+ } catch {
1122
+ return data.value;
1123
+ }
1124
+ }
1125
+ return null;
1126
+ }
1127
+ return data;
1128
+ } catch (error) {
1129
+ const message = error instanceof Error ? error.message : String(error);
1130
+ this.logger.error("Failed to parse text:", { message, text });
1131
+ return null;
1132
+ }
1133
+ }
1134
+ async createNamespaceById(title) {
1135
+ if (this.bindings) {
1136
+ return {
1137
+ id: title,
1138
+ // Use title as ID since that's what we need
1139
+ title,
1140
+ supports_url_encoding: true
1141
+ };
1142
+ }
1143
+ return await this.client.kv.namespaces.create({
1144
+ account_id: this.accountId,
1145
+ title
1146
+ });
1147
+ }
1148
+ async createNamespace(namespaceName) {
1149
+ try {
1150
+ const response = await this.createNamespaceById(namespaceName);
1151
+ return response.id;
1152
+ } catch (error) {
1153
+ if (error.message && error.message.includes("already exists")) {
1154
+ const namespaces = await this.listNamespaces();
1155
+ const namespace = namespaces.result.find((ns) => ns.title === namespaceName);
1156
+ if (namespace) return namespace.id;
1157
+ }
1158
+ this.logger.error("Error creating namespace:", error);
1159
+ throw new Error(`Failed to create namespace ${namespaceName}: ${error.message}`);
1160
+ }
1161
+ }
1162
+ async listNamespaces() {
1163
+ if (this.bindings) {
1164
+ return {
1165
+ result: Object.keys(this.bindings).map((name) => ({
1166
+ id: name,
1167
+ title: name,
1168
+ supports_url_encoding: true
1169
+ }))
1170
+ };
1171
+ }
1172
+ let allNamespaces = [];
1173
+ let currentPage = 1;
1174
+ const perPage = 50;
1175
+ let morePagesExist = true;
1176
+ while (morePagesExist) {
1177
+ const response = await this.client.kv.namespaces.list({
1178
+ account_id: this.accountId,
1179
+ page: currentPage,
1180
+ per_page: perPage
1181
+ });
1182
+ if (response.result) {
1183
+ allNamespaces = allNamespaces.concat(response.result);
1184
+ }
1185
+ morePagesExist = response.result ? response.result.length === perPage : false;
1186
+ if (morePagesExist) {
1187
+ currentPage++;
1188
+ }
1189
+ }
1190
+ return { result: allNamespaces };
1191
+ }
1192
+ async getNamespaceIdByName(namespaceName) {
1193
+ try {
1194
+ const response = await this.listNamespaces();
1195
+ const namespace = response.result.find((ns) => ns.title === namespaceName);
1196
+ return namespace ? namespace.id : null;
1197
+ } catch (error) {
1198
+ this.logger.error(`Failed to get namespace ID for ${namespaceName}:`, error);
1199
+ return null;
1200
+ }
1201
+ }
1202
+ async getOrCreateNamespaceId(namespaceName) {
1203
+ let namespaceId = await this.getNamespaceIdByName(namespaceName);
1204
+ if (!namespaceId) {
1205
+ namespaceId = await this.createNamespace(namespaceName);
1206
+ }
1207
+ return namespaceId;
1208
+ }
1209
+ async getNamespaceId(tableName) {
1210
+ const prefix = this.namespacePrefix ? `${this.namespacePrefix}_` : "";
1211
+ try {
1212
+ return await this.getOrCreateNamespaceId(`${prefix}${tableName}`);
1213
+ } catch (error) {
1214
+ this.logger.error("Error fetching namespace ID:", error);
1215
+ throw new Error(`Failed to fetch namespace ID for table ${tableName}: ${error.message}`);
1216
+ }
1217
+ }
1218
+ async getNamespaceValue(tableName, key) {
1219
+ try {
1220
+ if (this.bindings) {
1221
+ const binding = this.getBinding(tableName);
1222
+ const result = await binding.getWithMetadata(key, "text");
1223
+ if (!result) return null;
1224
+ return JSON.stringify(result);
1225
+ } else {
1226
+ const namespaceId = await this.getNamespaceId(tableName);
1227
+ const response = await this.client.kv.namespaces.values.get(namespaceId, key, {
1228
+ account_id: this.accountId
1229
+ });
1230
+ return await response.text();
1231
+ }
1232
+ } catch (error) {
1233
+ if (error.message && error.message.includes("key not found")) {
1234
+ return null;
1235
+ }
1236
+ const message = error instanceof Error ? error.message : String(error);
1237
+ this.logger.error(`Failed to get value for ${tableName} ${key}:`, { message });
1238
+ throw error;
1239
+ }
1240
+ }
1241
+ async getKV(tableName, key) {
1242
+ try {
1243
+ const text = await this.getNamespaceValue(tableName, key);
1244
+ return this.safeParse(text);
1245
+ } catch (error) {
1246
+ this.logger.error(`Failed to get KV value for ${tableName}:${key}:`, error);
1247
+ throw new Error(`Failed to get KV value: ${error.message}`);
1248
+ }
1249
+ }
529
1250
  async getTableSchema(tableName) {
530
1251
  try {
531
1252
  const schemaKey = this.getSchemaKey(tableName);
@@ -605,7 +1326,7 @@ var CloudflareStore = class extends MastraStorage {
605
1326
  }
606
1327
  break;
607
1328
  case TABLE_WORKFLOW_SNAPSHOT:
608
- if (!("namespace" in recordTyped) || !("workflow_name" in recordTyped) || !("run_id" in recordTyped)) {
1329
+ if (!("workflow_name" in recordTyped) || !("run_id" in recordTyped)) {
609
1330
  throw new Error("Workflow record missing required fields");
610
1331
  }
611
1332
  break;
@@ -614,83 +1335,32 @@ var CloudflareStore = class extends MastraStorage {
614
1335
  throw new Error("Trace record missing required fields");
615
1336
  }
616
1337
  break;
617
- default:
618
- throw new Error(`Unknown table type: ${tableName}`);
619
- }
620
- } catch (error) {
621
- const message = error instanceof Error ? error.message : String(error);
622
- this.logger.error(`Failed to validate record for ${tableName}:`, { message, record });
623
- throw error;
624
- }
625
- }
626
- ensureMetadata(metadata) {
627
- if (!metadata) return {};
628
- return typeof metadata === "string" ? JSON.parse(metadata) : metadata;
629
- }
630
- async createTable({
631
- tableName,
632
- schema
633
- }) {
634
- try {
635
- const schemaKey = this.getSchemaKey(tableName);
636
- const metadata = {
637
- type: "table_schema",
638
- tableName,
639
- createdAt: (/* @__PURE__ */ new Date()).toISOString()
640
- };
641
- await this.putKV({ tableName, key: schemaKey, value: schema, metadata });
642
- } catch (error) {
643
- throw new MastraError(
644
- {
645
- id: "CLOUDFLARE_STORAGE_CREATE_TABLE_FAILED",
646
- domain: ErrorDomain.STORAGE,
647
- category: ErrorCategory.THIRD_PARTY,
648
- details: {
649
- tableName
1338
+ case TABLE_EVALS:
1339
+ if (!("agent_name" in recordTyped) || !("run_id" in recordTyped)) {
1340
+ throw new Error("Eval record missing required fields");
650
1341
  }
651
- },
652
- error
653
- );
654
- }
655
- }
656
- /**
657
- * No-op: This backend is schemaless and does not require schema changes.
658
- * @param tableName Name of the table
659
- * @param schema Schema of the table
660
- * @param ifNotExists Array of column names to add if they don't exist
661
- */
662
- async alterTable(_args) {
663
- }
664
- async clearTable({ tableName }) {
665
- try {
666
- const keys = await this.listKV(tableName);
667
- if (keys.length > 0) {
668
- await Promise.all(keys.map((keyObj) => this.deleteKV(tableName, keyObj.name)));
1342
+ break;
1343
+ case TABLE_SCORERS:
1344
+ if (!("id" in recordTyped) || !("scorerId" in recordTyped)) {
1345
+ throw new Error("Score record missing required fields");
1346
+ }
1347
+ break;
1348
+ default:
1349
+ throw new Error(`Unknown table type: ${tableName}`);
669
1350
  }
670
1351
  } catch (error) {
671
- throw new MastraError(
672
- {
673
- id: "CLOUDFLARE_STORAGE_CLEAR_TABLE_FAILED",
674
- domain: ErrorDomain.STORAGE,
675
- category: ErrorCategory.THIRD_PARTY,
676
- details: {
677
- tableName
678
- }
679
- },
680
- error
681
- );
1352
+ const message = error instanceof Error ? error.message : String(error);
1353
+ this.logger.error(`Failed to validate record for ${tableName}:`, { message, record });
1354
+ throw error;
682
1355
  }
683
1356
  }
684
- async insert({
685
- tableName,
686
- record
687
- }) {
1357
+ async insert({ tableName, record }) {
688
1358
  try {
689
1359
  const key = this.getKey(tableName, record);
690
1360
  const processedRecord = {
691
1361
  ...record,
692
- createdAt: record.createdAt ? this.serializeDate(record.createdAt) : void 0,
693
- updatedAt: record.updatedAt ? this.serializeDate(record.updatedAt) : void 0,
1362
+ createdAt: record.createdAt ? serializeDate(record.createdAt) : void 0,
1363
+ updatedAt: record.updatedAt ? serializeDate(record.updatedAt) : void 0,
694
1364
  metadata: record.metadata ? JSON.stringify(record.metadata) : ""
695
1365
  };
696
1366
  await this.validateRecord(processedRecord, tableName);
@@ -709,6 +1379,10 @@ var CloudflareStore = class extends MastraStorage {
709
1379
  );
710
1380
  }
711
1381
  }
1382
+ ensureMetadata(metadata) {
1383
+ if (!metadata) return {};
1384
+ return typeof metadata === "string" ? JSON.parse(metadata) : metadata;
1385
+ }
712
1386
  async load({ tableName, keys }) {
713
1387
  try {
714
1388
  const key = this.getKey(tableName, keys);
@@ -716,8 +1390,8 @@ var CloudflareStore = class extends MastraStorage {
716
1390
  if (!data) return null;
717
1391
  const processed = {
718
1392
  ...data,
719
- createdAt: this.ensureDate(data.createdAt),
720
- updatedAt: this.ensureDate(data.updatedAt),
1393
+ createdAt: ensureDate(data.createdAt),
1394
+ updatedAt: ensureDate(data.updatedAt),
721
1395
  metadata: this.ensureMetadata(data.metadata)
722
1396
  };
723
1397
  return processed;
@@ -738,578 +1412,664 @@ var CloudflareStore = class extends MastraStorage {
738
1412
  return null;
739
1413
  }
740
1414
  }
741
- async getThreadById({ threadId }) {
742
- const thread = await this.load({ tableName: TABLE_THREADS, keys: { id: threadId } });
743
- if (!thread) return null;
1415
+ async batchInsert(input) {
1416
+ if (!input.records || input.records.length === 0) return;
744
1417
  try {
745
- return {
746
- ...thread,
747
- createdAt: this.ensureDate(thread.createdAt),
748
- updatedAt: this.ensureDate(thread.updatedAt),
749
- metadata: this.ensureMetadata(thread.metadata)
750
- };
1418
+ await Promise.all(
1419
+ input.records.map(async (record) => {
1420
+ const key = this.getKey(input.tableName, record);
1421
+ const processedRecord = {
1422
+ ...record,
1423
+ createdAt: record.createdAt ? serializeDate(record.createdAt) : void 0,
1424
+ updatedAt: record.updatedAt ? serializeDate(record.updatedAt) : void 0,
1425
+ metadata: record.metadata ? JSON.stringify(record.metadata) : void 0
1426
+ };
1427
+ await this.putKV({ tableName: input.tableName, key, value: processedRecord });
1428
+ })
1429
+ );
751
1430
  } catch (error) {
752
- const mastraError = new MastraError(
1431
+ throw new MastraError(
753
1432
  {
754
- id: "CLOUDFLARE_STORAGE_GET_THREAD_BY_ID_FAILED",
1433
+ id: "CLOUDFLARE_STORAGE_BATCH_INSERT_FAILED",
755
1434
  domain: ErrorDomain.STORAGE,
756
1435
  category: ErrorCategory.THIRD_PARTY,
1436
+ text: `Error in batch insert for table ${input.tableName}`,
757
1437
  details: {
758
- threadId
1438
+ tableName: input.tableName
759
1439
  }
760
1440
  },
761
1441
  error
762
1442
  );
763
- this.logger?.trackException(mastraError);
764
- this.logger?.error(mastraError.toString());
765
- return null;
766
1443
  }
767
1444
  }
768
- async getThreadsByResourceId({ resourceId }) {
1445
+ /**
1446
+ * Helper to safely serialize data for KV storage
1447
+ */
1448
+ safeSerialize(data) {
1449
+ return typeof data === "string" ? data : JSON.stringify(data);
1450
+ }
1451
+ async putNamespaceValue({
1452
+ tableName,
1453
+ key,
1454
+ value,
1455
+ metadata
1456
+ }) {
769
1457
  try {
770
- const keyList = await this.listKV(TABLE_THREADS);
771
- const threads = await Promise.all(
772
- keyList.map(async (keyObj) => {
773
- try {
774
- const data = await this.getKV(TABLE_THREADS, keyObj.name);
775
- if (!data) return null;
776
- const thread = typeof data === "string" ? JSON.parse(data) : data;
777
- if (!thread || !thread.resourceId || thread.resourceId !== resourceId) return null;
778
- return {
779
- ...thread,
780
- createdAt: this.ensureDate(thread.createdAt),
781
- updatedAt: this.ensureDate(thread.updatedAt),
782
- metadata: this.ensureMetadata(thread.metadata)
783
- };
784
- } catch (error) {
785
- const mastraError = new MastraError(
786
- {
787
- id: "CLOUDFLARE_STORAGE_GET_THREADS_BY_RESOURCE_ID_FAILED",
788
- domain: ErrorDomain.STORAGE,
789
- category: ErrorCategory.THIRD_PARTY,
790
- details: {
791
- resourceId
792
- }
793
- },
794
- error
795
- );
796
- this.logger?.trackException(mastraError);
797
- this.logger?.error(mastraError.toString());
798
- return null;
799
- }
800
- })
801
- );
802
- return threads.filter((thread) => thread !== null);
1458
+ const serializedValue = this.safeSerialize(value);
1459
+ const serializedMetadata = metadata ? this.safeSerialize(metadata) : "";
1460
+ if (this.bindings) {
1461
+ const binding = this.getBinding(tableName);
1462
+ await binding.put(key, serializedValue, { metadata: serializedMetadata });
1463
+ } else {
1464
+ const namespaceId = await this.getNamespaceId(tableName);
1465
+ await this.client.kv.namespaces.values.update(namespaceId, key, {
1466
+ account_id: this.accountId,
1467
+ value: serializedValue,
1468
+ metadata: serializedMetadata
1469
+ });
1470
+ }
803
1471
  } catch (error) {
804
- const mastraError = new MastraError(
1472
+ const message = error instanceof Error ? error.message : String(error);
1473
+ this.logger.error(`Failed to put value for ${tableName} ${key}:`, { message });
1474
+ throw error;
1475
+ }
1476
+ }
1477
+ async putKV({
1478
+ tableName,
1479
+ key,
1480
+ value,
1481
+ metadata
1482
+ }) {
1483
+ try {
1484
+ await this.putNamespaceValue({ tableName, key, value, metadata });
1485
+ } catch (error) {
1486
+ this.logger.error(`Failed to put KV value for ${tableName}:${key}:`, error);
1487
+ throw new Error(`Failed to put KV value: ${error.message}`);
1488
+ }
1489
+ }
1490
+ async createTable({
1491
+ tableName,
1492
+ schema
1493
+ }) {
1494
+ try {
1495
+ const schemaKey = this.getSchemaKey(tableName);
1496
+ const metadata = {
1497
+ type: "table_schema",
1498
+ tableName,
1499
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
1500
+ };
1501
+ await this.putKV({ tableName, key: schemaKey, value: schema, metadata });
1502
+ } catch (error) {
1503
+ throw new MastraError(
805
1504
  {
806
- id: "CLOUDFLARE_STORAGE_GET_THREADS_BY_RESOURCE_ID_FAILED",
1505
+ id: "CLOUDFLARE_STORAGE_CREATE_TABLE_FAILED",
807
1506
  domain: ErrorDomain.STORAGE,
808
1507
  category: ErrorCategory.THIRD_PARTY,
809
1508
  details: {
810
- resourceId
1509
+ tableName
811
1510
  }
812
1511
  },
813
1512
  error
814
1513
  );
815
- this.logger?.trackException(mastraError);
816
- this.logger?.error(mastraError.toString());
817
- return [];
818
1514
  }
819
1515
  }
820
- async saveThread({ thread }) {
1516
+ async listNamespaceKeys(tableName, options) {
821
1517
  try {
822
- await this.insert({ tableName: TABLE_THREADS, record: thread });
823
- return thread;
1518
+ if (this.bindings) {
1519
+ const binding = this.getBinding(tableName);
1520
+ const response = await binding.list({
1521
+ limit: options?.limit || 1e3,
1522
+ prefix: options?.prefix
1523
+ });
1524
+ return response.keys;
1525
+ } else {
1526
+ const namespaceId = await this.getNamespaceId(tableName);
1527
+ const response = await this.client.kv.namespaces.keys.list(namespaceId, {
1528
+ account_id: this.accountId,
1529
+ limit: options?.limit || 1e3,
1530
+ prefix: options?.prefix
1531
+ });
1532
+ return response.result;
1533
+ }
824
1534
  } catch (error) {
825
1535
  throw new MastraError(
826
1536
  {
827
- id: "CLOUDFLARE_STORAGE_SAVE_THREAD_FAILED",
1537
+ id: "CLOUDFLARE_STORAGE_LIST_NAMESPACE_KEYS_FAILED",
828
1538
  domain: ErrorDomain.STORAGE,
829
1539
  category: ErrorCategory.THIRD_PARTY,
830
1540
  details: {
831
- threadId: thread.id
1541
+ tableName
832
1542
  }
833
1543
  },
834
1544
  error
835
1545
  );
836
1546
  }
837
1547
  }
838
- async updateThread({
839
- id,
840
- title,
841
- metadata
842
- }) {
1548
+ async deleteNamespaceValue(tableName, key) {
1549
+ if (this.bindings) {
1550
+ const binding = this.getBinding(tableName);
1551
+ await binding.delete(key);
1552
+ } else {
1553
+ const namespaceId = await this.getNamespaceId(tableName);
1554
+ await this.client.kv.namespaces.values.delete(namespaceId, key, {
1555
+ account_id: this.accountId
1556
+ });
1557
+ }
1558
+ }
1559
+ async deleteKV(tableName, key) {
843
1560
  try {
844
- const thread = await this.getThreadById({ threadId: id });
845
- if (!thread) {
846
- throw new Error(`Thread ${id} not found`);
1561
+ await this.deleteNamespaceValue(tableName, key);
1562
+ } catch (error) {
1563
+ this.logger.error(`Failed to delete KV value for ${tableName}:${key}:`, error);
1564
+ throw new Error(`Failed to delete KV value: ${error.message}`);
1565
+ }
1566
+ }
1567
+ async listKV(tableName, options) {
1568
+ try {
1569
+ return await this.listNamespaceKeys(tableName, options);
1570
+ } catch (error) {
1571
+ this.logger.error(`Failed to list KV for ${tableName}:`, error);
1572
+ throw new Error(`Failed to list KV: ${error.message}`);
1573
+ }
1574
+ }
1575
+ };
1576
+ function transformScoreRow(row) {
1577
+ const deserialized = { ...row };
1578
+ deserialized.input = safelyParseJSON(row.input);
1579
+ deserialized.output = safelyParseJSON(row.output);
1580
+ deserialized.scorer = safelyParseJSON(row.scorer);
1581
+ deserialized.preprocessStepResult = safelyParseJSON(row.preprocessStepResult);
1582
+ deserialized.analyzeStepResult = safelyParseJSON(row.analyzeStepResult);
1583
+ deserialized.metadata = safelyParseJSON(row.metadata);
1584
+ deserialized.additionalContext = safelyParseJSON(row.additionalContext);
1585
+ deserialized.runtimeContext = safelyParseJSON(row.runtimeContext);
1586
+ deserialized.entity = safelyParseJSON(row.entity);
1587
+ return deserialized;
1588
+ }
1589
+ var ScoresStorageCloudflare = class extends ScoresStorage {
1590
+ operations;
1591
+ constructor({ operations }) {
1592
+ super();
1593
+ this.operations = operations;
1594
+ }
1595
+ async getScoreById({ id }) {
1596
+ try {
1597
+ const score = await this.operations.getKV(TABLE_SCORERS, id);
1598
+ if (!score) {
1599
+ return null;
847
1600
  }
848
- const updatedThread = {
849
- ...thread,
850
- title,
851
- metadata: this.ensureMetadata({
852
- ...thread.metadata ?? {},
853
- ...metadata
854
- }),
855
- updatedAt: /* @__PURE__ */ new Date()
1601
+ return transformScoreRow(score);
1602
+ } catch (error) {
1603
+ const mastraError = new MastraError(
1604
+ {
1605
+ id: "CLOUDFLARE_STORAGE_SCORES_GET_SCORE_BY_ID_FAILED",
1606
+ domain: ErrorDomain.STORAGE,
1607
+ category: ErrorCategory.THIRD_PARTY,
1608
+ text: `Failed to get score by id: ${id}`
1609
+ },
1610
+ error
1611
+ );
1612
+ this.logger.trackException(mastraError);
1613
+ this.logger.error(mastraError.toString());
1614
+ return null;
1615
+ }
1616
+ }
1617
+ async saveScore(score) {
1618
+ let parsedScore;
1619
+ try {
1620
+ parsedScore = saveScorePayloadSchema.parse(score);
1621
+ } catch (error) {
1622
+ throw new MastraError(
1623
+ {
1624
+ id: "CLOUDFLARE_STORAGE_SAVE_SCORE_FAILED_INVALID_SCORE_PAYLOAD",
1625
+ domain: ErrorDomain.STORAGE,
1626
+ category: ErrorCategory.USER,
1627
+ details: { scoreId: score.id }
1628
+ },
1629
+ error
1630
+ );
1631
+ }
1632
+ try {
1633
+ const id = crypto.randomUUID();
1634
+ const serializedRecord = {};
1635
+ for (const [key, value] of Object.entries(parsedScore)) {
1636
+ if (value !== null && value !== void 0) {
1637
+ if (typeof value === "object") {
1638
+ serializedRecord[key] = JSON.stringify(value);
1639
+ } else {
1640
+ serializedRecord[key] = value;
1641
+ }
1642
+ } else {
1643
+ serializedRecord[key] = null;
1644
+ }
1645
+ }
1646
+ serializedRecord.id = id;
1647
+ serializedRecord.createdAt = (/* @__PURE__ */ new Date()).toISOString();
1648
+ serializedRecord.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
1649
+ await this.operations.putKV({
1650
+ tableName: TABLE_SCORERS,
1651
+ key: id,
1652
+ value: serializedRecord
1653
+ });
1654
+ const scoreFromDb = await this.getScoreById({ id: score.id });
1655
+ return { score: scoreFromDb };
1656
+ } catch (error) {
1657
+ const mastraError = new MastraError(
1658
+ {
1659
+ id: "CLOUDFLARE_STORAGE_SCORES_SAVE_SCORE_FAILED",
1660
+ domain: ErrorDomain.STORAGE,
1661
+ category: ErrorCategory.THIRD_PARTY,
1662
+ text: `Failed to save score: ${score.id}`
1663
+ },
1664
+ error
1665
+ );
1666
+ this.logger.trackException(mastraError);
1667
+ this.logger.error(mastraError.toString());
1668
+ throw mastraError;
1669
+ }
1670
+ }
1671
+ async getScoresByScorerId({
1672
+ scorerId,
1673
+ entityId,
1674
+ entityType,
1675
+ source,
1676
+ pagination
1677
+ }) {
1678
+ try {
1679
+ const keys = await this.operations.listKV(TABLE_SCORERS);
1680
+ const scores = [];
1681
+ for (const { name: key } of keys) {
1682
+ const score = await this.operations.getKV(TABLE_SCORERS, key);
1683
+ if (entityId && score.entityId !== entityId) {
1684
+ continue;
1685
+ }
1686
+ if (entityType && score.entityType !== entityType) {
1687
+ continue;
1688
+ }
1689
+ if (source && score.source !== source) {
1690
+ continue;
1691
+ }
1692
+ if (score && score.scorerId === scorerId) {
1693
+ scores.push(transformScoreRow(score));
1694
+ }
1695
+ }
1696
+ scores.sort((a, b) => {
1697
+ const dateA = new Date(a.createdAt || 0).getTime();
1698
+ const dateB = new Date(b.createdAt || 0).getTime();
1699
+ return dateB - dateA;
1700
+ });
1701
+ const total = scores.length;
1702
+ const start = pagination.page * pagination.perPage;
1703
+ const end = start + pagination.perPage;
1704
+ const pagedScores = scores.slice(start, end);
1705
+ return {
1706
+ pagination: {
1707
+ total,
1708
+ page: pagination.page,
1709
+ perPage: pagination.perPage,
1710
+ hasMore: end < total
1711
+ },
1712
+ scores: pagedScores
856
1713
  };
857
- await this.insert({ tableName: TABLE_THREADS, record: updatedThread });
858
- return updatedThread;
859
1714
  } catch (error) {
860
- throw new MastraError(
1715
+ const mastraError = new MastraError(
861
1716
  {
862
- id: "CLOUDFLARE_STORAGE_UPDATE_THREAD_FAILED",
1717
+ id: "CLOUDFLARE_STORAGE_SCORES_GET_SCORES_BY_SCORER_ID_FAILED",
863
1718
  domain: ErrorDomain.STORAGE,
864
1719
  category: ErrorCategory.THIRD_PARTY,
865
- details: {
866
- threadId: id,
867
- title
868
- }
1720
+ text: `Failed to get scores by scorer id: ${scorerId}`
869
1721
  },
870
1722
  error
871
1723
  );
1724
+ this.logger?.trackException(mastraError);
1725
+ this.logger?.error(mastraError.toString());
1726
+ return { pagination: { total: 0, page: 0, perPage: 100, hasMore: false }, scores: [] };
872
1727
  }
873
1728
  }
874
- async deleteThread({ threadId }) {
1729
+ async getScoresByRunId({
1730
+ runId,
1731
+ pagination
1732
+ }) {
875
1733
  try {
876
- const thread = await this.getThreadById({ threadId });
877
- if (!thread) {
878
- throw new Error(`Thread ${threadId} not found`);
1734
+ const keys = await this.operations.listKV(TABLE_SCORERS);
1735
+ const scores = [];
1736
+ for (const { name: key } of keys) {
1737
+ const score = await this.operations.getKV(TABLE_SCORERS, key);
1738
+ if (score && score.runId === runId) {
1739
+ scores.push(transformScoreRow(score));
1740
+ }
879
1741
  }
880
- const messageKeys = await this.listKV(TABLE_MESSAGES);
881
- const threadMessageKeys = messageKeys.filter((key) => key.name.includes(`${TABLE_MESSAGES}:${threadId}:`));
882
- await Promise.all([
883
- // Delete message order
884
- this.deleteKV(TABLE_MESSAGES, this.getThreadMessagesKey(threadId)),
885
- // Delete all messages
886
- ...threadMessageKeys.map((key) => this.deleteKV(TABLE_MESSAGES, key.name)),
887
- // Delete thread
888
- this.deleteKV(TABLE_THREADS, this.getKey(TABLE_THREADS, { id: threadId }))
889
- ]);
1742
+ scores.sort((a, b) => {
1743
+ const dateA = new Date(a.createdAt || 0).getTime();
1744
+ const dateB = new Date(b.createdAt || 0).getTime();
1745
+ return dateB - dateA;
1746
+ });
1747
+ const total = scores.length;
1748
+ const start = pagination.page * pagination.perPage;
1749
+ const end = start + pagination.perPage;
1750
+ const pagedScores = scores.slice(start, end);
1751
+ return {
1752
+ pagination: {
1753
+ total,
1754
+ page: pagination.page,
1755
+ perPage: pagination.perPage,
1756
+ hasMore: end < total
1757
+ },
1758
+ scores: pagedScores
1759
+ };
890
1760
  } catch (error) {
891
- throw new MastraError(
1761
+ const mastraError = new MastraError(
892
1762
  {
893
- id: "CLOUDFLARE_STORAGE_DELETE_THREAD_FAILED",
1763
+ id: "CLOUDFLARE_STORAGE_SCORES_GET_SCORES_BY_RUN_ID_FAILED",
894
1764
  domain: ErrorDomain.STORAGE,
895
1765
  category: ErrorCategory.THIRD_PARTY,
896
- details: {
897
- threadId
898
- }
1766
+ text: `Failed to get scores by run id: ${runId}`
899
1767
  },
900
1768
  error
901
1769
  );
1770
+ this.logger.trackException(mastraError);
1771
+ this.logger.error(mastraError.toString());
1772
+ return { pagination: { total: 0, page: 0, perPage: 100, hasMore: false }, scores: [] };
902
1773
  }
903
1774
  }
904
- getMessageKey(threadId, messageId) {
905
- try {
906
- return this.getKey(TABLE_MESSAGES, { threadId, id: messageId });
907
- } catch (error) {
908
- const message = error instanceof Error ? error.message : String(error);
909
- this.logger.error(`Error getting message key for thread ${threadId} and message ${messageId}:`, { message });
910
- throw error;
911
- }
912
- }
913
- getThreadMessagesKey(threadId) {
914
- try {
915
- return this.getKey(TABLE_MESSAGES, { threadId, id: "messages" });
916
- } catch (error) {
917
- const message = error instanceof Error ? error.message : String(error);
918
- this.logger.error(`Error getting thread messages key for thread ${threadId}:`, { message });
919
- throw error;
920
- }
921
- }
922
- async saveMessages(args) {
923
- const { messages, format = "v1" } = args;
924
- if (!Array.isArray(messages) || messages.length === 0) return [];
1775
+ async getScoresByEntityId({
1776
+ entityId,
1777
+ entityType,
1778
+ pagination
1779
+ }) {
925
1780
  try {
926
- const validatedMessages = messages.map((message, index) => {
927
- const errors = [];
928
- if (!message.id) errors.push("id is required");
929
- if (!message.threadId) errors.push("threadId is required");
930
- if (!message.content) errors.push("content is required");
931
- if (!message.role) errors.push("role is required");
932
- if (!message.createdAt) errors.push("createdAt is required");
933
- if (errors.length > 0) {
934
- throw new Error(`Invalid message at index ${index}: ${errors.join(", ")}`);
935
- }
936
- return {
937
- ...message,
938
- createdAt: this.ensureDate(message.createdAt),
939
- type: message.type || "v2",
940
- _index: index
941
- };
942
- }).filter((m) => !!m);
943
- const messagesByThread = validatedMessages.reduce((acc, message) => {
944
- if (message.threadId && !acc.has(message.threadId)) {
945
- acc.set(message.threadId, []);
1781
+ const keys = await this.operations.listKV(TABLE_SCORERS);
1782
+ const scores = [];
1783
+ for (const { name: key } of keys) {
1784
+ const score = await this.operations.getKV(TABLE_SCORERS, key);
1785
+ if (score && score.entityId === entityId && score.entityType === entityType) {
1786
+ scores.push(transformScoreRow(score));
946
1787
  }
947
- if (message.threadId) {
948
- acc.get(message.threadId).push(message);
949
- }
950
- return acc;
951
- }, /* @__PURE__ */ new Map());
952
- await Promise.all(
953
- Array.from(messagesByThread.entries()).map(async ([threadId, threadMessages]) => {
954
- try {
955
- const thread = await this.getThreadById({ threadId });
956
- if (!thread) {
957
- throw new Error(`Thread ${threadId} not found`);
958
- }
959
- await Promise.all(
960
- threadMessages.map(async (message) => {
961
- const key = this.getMessageKey(threadId, message.id);
962
- const { _index, ...cleanMessage } = message;
963
- const serializedMessage = {
964
- ...cleanMessage,
965
- createdAt: this.serializeDate(cleanMessage.createdAt)
966
- };
967
- await this.putKV({ tableName: TABLE_MESSAGES, key, value: serializedMessage });
968
- })
969
- );
970
- const orderKey = this.getThreadMessagesKey(threadId);
971
- const entries = await this.updateSorting(threadMessages);
972
- await this.updateSortedMessages(orderKey, entries);
973
- } catch (error) {
974
- throw new MastraError(
975
- {
976
- id: "CLOUDFLARE_STORAGE_SAVE_MESSAGES_FAILED",
977
- domain: ErrorDomain.STORAGE,
978
- category: ErrorCategory.THIRD_PARTY,
979
- details: {
980
- threadId
981
- }
982
- },
983
- error
984
- );
985
- }
986
- })
987
- );
988
- const prepared = validatedMessages.map(
989
- ({ _index, ...message }) => ({ ...message, type: message.type !== "v2" ? message.type : void 0 })
990
- );
991
- const list = new MessageList().add(prepared, "memory");
992
- if (format === `v2`) return list.get.all.v2();
993
- return list.get.all.v1();
1788
+ }
1789
+ scores.sort((a, b) => {
1790
+ const dateA = new Date(a.createdAt || 0).getTime();
1791
+ const dateB = new Date(b.createdAt || 0).getTime();
1792
+ return dateB - dateA;
1793
+ });
1794
+ const total = scores.length;
1795
+ const start = pagination.page * pagination.perPage;
1796
+ const end = start + pagination.perPage;
1797
+ const pagedScores = scores.slice(start, end);
1798
+ return {
1799
+ pagination: {
1800
+ total,
1801
+ page: pagination.page,
1802
+ perPage: pagination.perPage,
1803
+ hasMore: end < total
1804
+ },
1805
+ scores: pagedScores
1806
+ };
994
1807
  } catch (error) {
995
- throw new MastraError(
1808
+ const mastraError = new MastraError(
996
1809
  {
997
- id: "CLOUDFLARE_STORAGE_SAVE_MESSAGES_FAILED",
1810
+ id: "CLOUDFLARE_STORAGE_SCORES_GET_SCORES_BY_ENTITY_ID_FAILED",
998
1811
  domain: ErrorDomain.STORAGE,
999
- category: ErrorCategory.THIRD_PARTY
1812
+ category: ErrorCategory.THIRD_PARTY,
1813
+ text: `Failed to get scores by entity id: ${entityId}, type: ${entityType}`
1000
1814
  },
1001
1815
  error
1002
1816
  );
1817
+ this.logger.trackException(mastraError);
1818
+ this.logger.error(mastraError.toString());
1819
+ return { pagination: { total: 0, page: 0, perPage: 100, hasMore: false }, scores: [] };
1003
1820
  }
1004
1821
  }
1005
- async getMessages({
1006
- threadId,
1007
- resourceId,
1008
- selectBy,
1009
- format
1822
+ async getScoresBySpan({
1823
+ traceId,
1824
+ spanId,
1825
+ pagination
1010
1826
  }) {
1011
- if (!threadId) throw new Error("threadId is required");
1012
- const limit = this.resolveMessageLimit({ last: selectBy?.last, defaultLimit: 40 });
1013
- const messageIds = /* @__PURE__ */ new Set();
1014
- if (limit === 0 && !selectBy?.include?.length) return [];
1015
1827
  try {
1016
- await Promise.all([
1017
- selectBy?.include?.length ? this.getIncludedMessagesWithContext(threadId, selectBy.include, messageIds) : Promise.resolve(),
1018
- limit > 0 && !selectBy?.include?.length ? this.getRecentMessages(threadId, limit, messageIds) : Promise.resolve()
1019
- ]);
1020
- const messages = await this.fetchAndParseMessages(threadId, Array.from(messageIds));
1021
- if (!messages.length) return [];
1022
- try {
1023
- const threadMessagesKey = this.getThreadMessagesKey(threadId);
1024
- const messageOrder = await this.getFullOrder(threadMessagesKey);
1025
- const orderMap = new Map(messageOrder.map((id, index) => [id, index]));
1026
- messages.sort((a, b) => {
1027
- const indexA = orderMap.get(a.id);
1028
- const indexB = orderMap.get(b.id);
1029
- if (indexA !== void 0 && indexB !== void 0) return orderMap.get(a.id) - orderMap.get(b.id);
1030
- return new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime();
1031
- });
1032
- } catch (error) {
1033
- const mastraError = new MastraError(
1034
- {
1035
- id: "CLOUDFLARE_STORAGE_SORT_MESSAGES_FAILED",
1036
- domain: ErrorDomain.STORAGE,
1037
- category: ErrorCategory.THIRD_PARTY,
1038
- text: `Error sorting messages for thread ${threadId} falling back to creation time`,
1039
- details: {
1040
- threadId
1041
- }
1042
- },
1043
- error
1044
- );
1045
- this.logger?.trackException(mastraError);
1046
- this.logger?.error(mastraError.toString());
1047
- messages.sort((a, b) => new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime());
1828
+ const keys = await this.operations.listKV(TABLE_SCORERS);
1829
+ const scores = [];
1830
+ for (const { name: key } of keys) {
1831
+ const score = await this.operations.getKV(TABLE_SCORERS, key);
1832
+ if (score && score.traceId === traceId && score.spanId === spanId) {
1833
+ scores.push(transformScoreRow(score));
1834
+ }
1048
1835
  }
1049
- const prepared = messages.map(({ _index, ...message }) => ({
1050
- ...message,
1051
- type: message.type === `v2` ? void 0 : message.type,
1052
- createdAt: this.ensureDate(message.createdAt)
1053
- }));
1054
- const list = new MessageList({ threadId, resourceId }).add(prepared, "memory");
1055
- if (format === `v1`) return list.get.all.v1();
1056
- return list.get.all.v2();
1836
+ scores.sort((a, b) => {
1837
+ const dateA = new Date(a.createdAt || 0).getTime();
1838
+ const dateB = new Date(b.createdAt || 0).getTime();
1839
+ return dateB - dateA;
1840
+ });
1841
+ const total = scores.length;
1842
+ const start = pagination.page * pagination.perPage;
1843
+ const end = start + pagination.perPage;
1844
+ const pagedScores = scores.slice(start, end);
1845
+ return {
1846
+ pagination: {
1847
+ total,
1848
+ page: pagination.page,
1849
+ perPage: pagination.perPage,
1850
+ hasMore: end < total
1851
+ },
1852
+ scores: pagedScores
1853
+ };
1057
1854
  } catch (error) {
1058
- const mastraError = new MastraError(
1855
+ throw new MastraError(
1059
1856
  {
1060
- id: "CLOUDFLARE_STORAGE_GET_MESSAGES_FAILED",
1857
+ id: "CLOUDFLARE_STORAGE_SCORES_GET_SCORES_BY_SPAN_FAILED",
1061
1858
  domain: ErrorDomain.STORAGE,
1062
1859
  category: ErrorCategory.THIRD_PARTY,
1063
- text: `Error retrieving messages for thread ${threadId}`,
1064
- details: {
1065
- threadId
1066
- }
1860
+ text: `Failed to get scores by span: traceId=${traceId}, spanId=${spanId}`
1067
1861
  },
1068
1862
  error
1069
1863
  );
1070
- this.logger?.trackException(mastraError);
1071
- this.logger?.error(mastraError.toString());
1072
- return [];
1073
- }
1074
- }
1075
- validateWorkflowParams(params) {
1076
- const { namespace, workflowName, runId } = params;
1077
- if (!namespace || !workflowName || !runId) {
1078
- throw new Error("Invalid workflow snapshot parameters");
1079
1864
  }
1080
1865
  }
1081
- validateWorkflowState(state) {
1082
- if (!state?.runId || !state?.value || !state?.context?.input || !state?.activePaths) {
1083
- throw new Error("Invalid workflow state structure");
1084
- }
1085
- }
1086
- normalizeSteps(steps) {
1087
- const normalizedSteps = {};
1088
- for (const [stepId, step] of Object.entries(steps)) {
1089
- normalizedSteps[stepId] = {
1090
- status: step.status,
1091
- payload: step.payload || step.result,
1092
- error: step.error
1093
- };
1094
- }
1095
- return normalizedSteps;
1096
- }
1097
- normalizeWorkflowState(data) {
1098
- return {
1099
- runId: data.runId,
1100
- value: data.value,
1101
- context: data.context,
1102
- serializedStepGraph: data.serializedStepGraph,
1103
- suspendedPaths: data.suspendedPaths || {},
1104
- activePaths: data.activePaths || [],
1105
- timestamp: data.timestamp || Date.now(),
1106
- status: data.status,
1107
- result: data.result,
1108
- error: data.error
1866
+ };
1867
+ var TracesStorageCloudflare = class extends TracesStorage {
1868
+ operations;
1869
+ constructor({ operations }) {
1870
+ super();
1871
+ this.operations = operations;
1872
+ }
1873
+ async getTraces(args) {
1874
+ const paginatedArgs = {
1875
+ name: args.name,
1876
+ scope: args.scope,
1877
+ page: args.page,
1878
+ perPage: args.perPage,
1879
+ attributes: args.attributes,
1880
+ filters: args.filters,
1881
+ dateRange: args.fromDate || args.toDate ? {
1882
+ start: args.fromDate,
1883
+ end: args.toDate
1884
+ } : void 0
1109
1885
  };
1110
- }
1111
- async persistWorkflowSnapshot(params) {
1112
1886
  try {
1113
- this.validateWorkflowParams(params);
1114
- const { namespace, workflowName, runId, snapshot } = params;
1115
- const normalizedState = this.normalizeWorkflowState(snapshot);
1116
- this.validateWorkflowState(normalizedState);
1117
- await this.insert({
1118
- tableName: TABLE_WORKFLOW_SNAPSHOT,
1119
- record: {
1120
- namespace,
1121
- workflow_name: workflowName,
1122
- run_id: runId,
1123
- snapshot: normalizedState,
1124
- createdAt: /* @__PURE__ */ new Date(),
1125
- updatedAt: /* @__PURE__ */ new Date()
1126
- }
1127
- });
1887
+ const result = await this.getTracesPaginated(paginatedArgs);
1888
+ return result.traces;
1128
1889
  } catch (error) {
1129
1890
  throw new MastraError(
1130
1891
  {
1131
- id: "CLOUDFLARE_STORAGE_PERSIST_WORKFLOW_SNAPSHOT_FAILED",
1892
+ id: "CLOUDFLARE_STORAGE_GET_TRACES_ERROR",
1132
1893
  domain: ErrorDomain.STORAGE,
1133
1894
  category: ErrorCategory.THIRD_PARTY,
1134
- text: `Error persisting workflow snapshot for namespace ${params.namespace}, workflow ${params.workflowName}, run ${params.runId}`,
1895
+ text: `Failed to retrieve traces: ${error instanceof Error ? error.message : String(error)}`,
1135
1896
  details: {
1136
- namespace: params.namespace,
1137
- workflowName: params.workflowName,
1138
- runId: params.runId
1897
+ name: args.name ?? "",
1898
+ scope: args.scope ?? ""
1139
1899
  }
1140
1900
  },
1141
1901
  error
1142
1902
  );
1143
1903
  }
1144
1904
  }
1145
- async loadWorkflowSnapshot(params) {
1905
+ async getTracesPaginated(args) {
1146
1906
  try {
1147
- this.validateWorkflowParams(params);
1148
- const { namespace, workflowName, runId } = params;
1149
- const key = this.getKey(TABLE_WORKFLOW_SNAPSHOT, { namespace, workflow_name: workflowName, run_id: runId });
1150
- const data = await this.getKV(TABLE_WORKFLOW_SNAPSHOT, key);
1151
- if (!data) return null;
1152
- const state = this.normalizeWorkflowState(data.snapshot || data);
1153
- this.validateWorkflowState(state);
1154
- return state;
1907
+ const { name, scope, attributes, filters, page = 0, perPage = 100, dateRange } = args;
1908
+ const prefix = this.operations.namespacePrefix ? `${this.operations.namespacePrefix}:` : "";
1909
+ const keyObjs = await this.operations.listKV(TABLE_TRACES, { prefix: `${prefix}${TABLE_TRACES}` });
1910
+ const traces = [];
1911
+ for (const { name: key } of keyObjs) {
1912
+ try {
1913
+ const data = await this.operations.getKV(TABLE_TRACES, key);
1914
+ if (!data) continue;
1915
+ if (name && data.name !== name) continue;
1916
+ if (scope && data.scope !== scope) continue;
1917
+ if (attributes) {
1918
+ const dataAttributes = data.attributes || {};
1919
+ let shouldSkip = false;
1920
+ for (const [key2, value] of Object.entries(attributes)) {
1921
+ if (dataAttributes[key2] !== value) {
1922
+ shouldSkip = true;
1923
+ break;
1924
+ }
1925
+ }
1926
+ if (shouldSkip) continue;
1927
+ }
1928
+ if (dateRange?.start || dateRange?.end) {
1929
+ const traceDate = new Date(data.createdAt || 0);
1930
+ if (dateRange.start && traceDate < dateRange.start) continue;
1931
+ if (dateRange.end && traceDate > dateRange.end) continue;
1932
+ }
1933
+ if (filters) {
1934
+ let shouldSkip = false;
1935
+ for (const [key2, value] of Object.entries(filters)) {
1936
+ if (data[key2] !== value) {
1937
+ shouldSkip = true;
1938
+ break;
1939
+ }
1940
+ }
1941
+ if (shouldSkip) continue;
1942
+ }
1943
+ traces.push(data);
1944
+ } catch (err) {
1945
+ this.logger.error("Failed to parse trace:", { key, error: err });
1946
+ }
1947
+ }
1948
+ traces.sort((a, b) => {
1949
+ const aTime = new Date(a.createdAt || 0).getTime();
1950
+ const bTime = new Date(b.createdAt || 0).getTime();
1951
+ return bTime - aTime;
1952
+ });
1953
+ const total = traces.length;
1954
+ const start = page * perPage;
1955
+ const end = start + perPage;
1956
+ const pagedTraces = traces.slice(start, end);
1957
+ return {
1958
+ traces: pagedTraces,
1959
+ total,
1960
+ page,
1961
+ perPage,
1962
+ hasMore: end < total
1963
+ };
1155
1964
  } catch (error) {
1156
1965
  const mastraError = new MastraError(
1157
1966
  {
1158
- id: "CLOUDFLARE_STORAGE_LOAD_WORKFLOW_SNAPSHOT_FAILED",
1967
+ id: "CLOUDFLARE_STORAGE_GET_TRACES_PAGINATED_FAILED",
1159
1968
  domain: ErrorDomain.STORAGE,
1160
1969
  category: ErrorCategory.THIRD_PARTY,
1161
- text: `Error loading workflow snapshot for namespace ${params.namespace}, workflow ${params.workflowName}, run ${params.runId}`,
1162
- details: {
1163
- namespace: params.namespace,
1164
- workflowName: params.workflowName,
1165
- runId: params.runId
1166
- }
1970
+ text: "Error getting traces with pagination"
1167
1971
  },
1168
1972
  error
1169
1973
  );
1170
- this.logger?.trackException(mastraError);
1171
- this.logger?.error(mastraError.toString());
1172
- return null;
1974
+ this.logger.trackException?.(mastraError);
1975
+ this.logger.error(mastraError.toString());
1976
+ return { traces: [], total: 0, page: 0, perPage: 100, hasMore: false };
1977
+ }
1978
+ }
1979
+ async batchTraceInsert({ records }) {
1980
+ this.logger.debug("Batch inserting traces", { count: records.length });
1981
+ await this.operations.batchInsert({
1982
+ tableName: TABLE_TRACES,
1983
+ records
1984
+ });
1985
+ }
1986
+ };
1987
+ var WorkflowsStorageCloudflare = class extends WorkflowsStorage {
1988
+ operations;
1989
+ constructor({ operations }) {
1990
+ super();
1991
+ this.operations = operations;
1992
+ }
1993
+ validateWorkflowParams(params) {
1994
+ const { workflowName, runId } = params;
1995
+ if (!workflowName || !runId) {
1996
+ throw new Error("Invalid workflow snapshot parameters");
1173
1997
  }
1174
1998
  }
1175
- async batchInsert(input) {
1176
- if (!input.records || input.records.length === 0) return;
1999
+ updateWorkflowResults({
2000
+ // workflowName,
2001
+ // runId,
2002
+ // stepId,
2003
+ // result,
2004
+ // runtimeContext,
2005
+ }) {
2006
+ throw new Error("Method not implemented.");
2007
+ }
2008
+ updateWorkflowState({
2009
+ // workflowName,
2010
+ // runId,
2011
+ // opts,
2012
+ }) {
2013
+ throw new Error("Method not implemented.");
2014
+ }
2015
+ async persistWorkflowSnapshot(params) {
1177
2016
  try {
1178
- await Promise.all(
1179
- input.records.map(async (record) => {
1180
- const key = this.getKey(input.tableName, record);
1181
- const processedRecord = {
1182
- ...record,
1183
- createdAt: record.createdAt ? this.serializeDate(record.createdAt) : void 0,
1184
- updatedAt: record.updatedAt ? this.serializeDate(record.updatedAt) : void 0,
1185
- metadata: record.metadata ? JSON.stringify(record.metadata) : void 0
1186
- };
1187
- await this.putKV({ tableName: input.tableName, key, value: processedRecord });
1188
- })
1189
- );
2017
+ const { workflowName, runId, resourceId, snapshot } = params;
2018
+ await this.operations.putKV({
2019
+ tableName: TABLE_WORKFLOW_SNAPSHOT,
2020
+ key: this.operations.getKey(TABLE_WORKFLOW_SNAPSHOT, { workflow_name: workflowName, run_id: runId }),
2021
+ value: {
2022
+ workflow_name: workflowName,
2023
+ run_id: runId,
2024
+ resourceId,
2025
+ snapshot: typeof snapshot === "string" ? snapshot : JSON.stringify(snapshot),
2026
+ createdAt: /* @__PURE__ */ new Date(),
2027
+ updatedAt: /* @__PURE__ */ new Date()
2028
+ }
2029
+ });
1190
2030
  } catch (error) {
1191
2031
  throw new MastraError(
1192
2032
  {
1193
- id: "CLOUDFLARE_STORAGE_BATCH_INSERT_FAILED",
2033
+ id: "CLOUDFLARE_STORAGE_PERSIST_WORKFLOW_SNAPSHOT_FAILED",
1194
2034
  domain: ErrorDomain.STORAGE,
1195
2035
  category: ErrorCategory.THIRD_PARTY,
1196
- text: `Error in batch insert for table ${input.tableName}`,
2036
+ text: `Error persisting workflow snapshot for workflow ${params.workflowName}, run ${params.runId}`,
1197
2037
  details: {
1198
- tableName: input.tableName
2038
+ workflowName: params.workflowName,
2039
+ runId: params.runId
1199
2040
  }
1200
2041
  },
1201
2042
  error
1202
2043
  );
1203
2044
  }
1204
2045
  }
1205
- async getTraces({
1206
- name,
1207
- scope,
1208
- page = 0,
1209
- perPage = 100,
1210
- attributes,
1211
- fromDate,
1212
- toDate
1213
- }) {
2046
+ async loadWorkflowSnapshot(params) {
1214
2047
  try {
1215
- let keys;
1216
- if (this.bindings) {
1217
- keys = (await this.listKV(TABLE_TRACES))?.map((k) => k.name) || [];
1218
- } else {
1219
- const namespaceId = await this.getNamespaceId(TABLE_TRACES);
1220
- const result = await this.client.kv.namespaces.keys.list(namespaceId, {
1221
- prefix: "",
1222
- limit: 1e3,
1223
- account_id: this.accountId
1224
- });
1225
- keys = result.result?.map((k) => k.name) || [];
1226
- }
1227
- const traceRecords = await Promise.all(
1228
- keys.map(async (key) => {
1229
- const record = await this.getKV(TABLE_TRACES, key);
1230
- if (!record) return null;
1231
- return record;
1232
- })
1233
- );
1234
- let filteredTraces = traceRecords.filter(
1235
- (record) => record !== null && typeof record === "object"
1236
- );
1237
- if (name) {
1238
- filteredTraces = filteredTraces.filter((record) => record.name?.toLowerCase().startsWith(name.toLowerCase()));
1239
- }
1240
- if (scope) {
1241
- filteredTraces = filteredTraces.filter((record) => record.scope === scope);
1242
- }
1243
- if (attributes) {
1244
- filteredTraces = filteredTraces.filter((record) => {
1245
- if (!record.attributes) return false;
1246
- const recordAttrs = this.parseJSON(record.attributes);
1247
- if (!recordAttrs) return false;
1248
- return Object.entries(attributes).every(([key, value]) => recordAttrs[key] === value);
1249
- });
1250
- }
1251
- if (fromDate) {
1252
- filteredTraces = filteredTraces.filter((record) => new Date(record.createdAt).getTime() >= fromDate.getTime());
1253
- }
1254
- if (toDate) {
1255
- filteredTraces = filteredTraces.filter((record) => new Date(record.createdAt).getTime() <= toDate.getTime());
1256
- }
1257
- filteredTraces.sort((a, b) => {
1258
- const dateA = new Date(a.createdAt).getTime();
1259
- const dateB = new Date(b.createdAt).getTime();
1260
- return dateB - dateA;
1261
- });
1262
- const start = page * perPage;
1263
- const end = start + perPage;
1264
- const paginatedTraces = filteredTraces.slice(start, end);
1265
- return paginatedTraces.map((record) => ({
1266
- id: record.id,
1267
- parentSpanId: record.parentSpanId,
1268
- traceId: record.traceId,
1269
- name: record.name,
1270
- scope: record.scope,
1271
- kind: record.kind,
1272
- status: this.parseJSON(record.status),
1273
- events: this.parseJSON(record.events) || [],
1274
- links: this.parseJSON(record.links) || [],
1275
- attributes: this.parseJSON(record?.attributes) || {},
1276
- startTime: record.startTime,
1277
- endTime: record.endTime,
1278
- other: this.parseJSON(record.other) || {},
1279
- createdAt: record.createdAt
1280
- }));
2048
+ this.validateWorkflowParams(params);
2049
+ const { workflowName, runId } = params;
2050
+ const key = this.operations.getKey(TABLE_WORKFLOW_SNAPSHOT, { workflow_name: workflowName, run_id: runId });
2051
+ const data = await this.operations.getKV(TABLE_WORKFLOW_SNAPSHOT, key);
2052
+ if (!data) return null;
2053
+ const snapshotData = typeof data.snapshot === "string" ? JSON.parse(data.snapshot) : data.snapshot;
2054
+ return snapshotData;
1281
2055
  } catch (error) {
1282
2056
  const mastraError = new MastraError(
1283
2057
  {
1284
- id: "CLOUDFLARE_STORAGE_GET_TRACES_FAILED",
2058
+ id: "CLOUDFLARE_STORAGE_LOAD_WORKFLOW_SNAPSHOT_FAILED",
1285
2059
  domain: ErrorDomain.STORAGE,
1286
2060
  category: ErrorCategory.THIRD_PARTY,
1287
- text: `Failed to get traces`
2061
+ text: `Error loading workflow snapshot for workflow ${params.workflowName}, run ${params.runId}`,
2062
+ details: {
2063
+ workflowName: params.workflowName,
2064
+ runId: params.runId
2065
+ }
1288
2066
  },
1289
2067
  error
1290
2068
  );
1291
- this.logger?.trackException(mastraError);
1292
- this.logger?.error(mastraError.toString());
1293
- return [];
1294
- }
1295
- }
1296
- parseJSON(value) {
1297
- if (typeof value === "string") {
1298
- try {
1299
- return JSON.parse(value);
1300
- } catch {
1301
- return value;
1302
- }
2069
+ this.logger.trackException?.(mastraError);
2070
+ this.logger.error(mastraError.toString());
2071
+ return null;
1303
2072
  }
1304
- return value;
1305
- }
1306
- getEvalsByAgentName(_agentName, _type) {
1307
- throw new MastraError({
1308
- id: "CLOUDFLARE_STORAGE_GET_EVALS_BY_AGENT_NAME_FAILED",
1309
- domain: ErrorDomain.STORAGE,
1310
- category: ErrorCategory.THIRD_PARTY,
1311
- text: `Failed to get evals by agent name`
1312
- });
1313
2073
  }
1314
2074
  parseWorkflowRun(row) {
1315
2075
  let parsedSnapshot = row.snapshot;
@@ -1324,28 +2084,24 @@ var CloudflareStore = class extends MastraStorage {
1324
2084
  workflowName: row.workflow_name,
1325
2085
  runId: row.run_id,
1326
2086
  snapshot: parsedSnapshot,
1327
- createdAt: this.ensureDate(row.createdAt),
1328
- updatedAt: this.ensureDate(row.updatedAt),
2087
+ createdAt: ensureDate(row.createdAt),
2088
+ updatedAt: ensureDate(row.updatedAt),
1329
2089
  resourceId: row.resourceId
1330
2090
  };
1331
2091
  }
1332
2092
  buildWorkflowSnapshotPrefix({
1333
- namespace,
1334
2093
  workflowName,
1335
2094
  runId,
1336
2095
  resourceId
1337
2096
  }) {
1338
- const prefix = this.namespacePrefix ? `${this.namespacePrefix}:` : "";
2097
+ const prefix = this.operations.namespacePrefix ? `${this.operations.namespacePrefix}:` : "";
1339
2098
  let key = `${prefix}${TABLE_WORKFLOW_SNAPSHOT}`;
1340
- if (namespace) key += `:${namespace}`;
1341
2099
  if (workflowName) key += `:${workflowName}`;
1342
2100
  if (runId) key += `:${runId}`;
1343
2101
  if (resourceId) key += `:${resourceId}`;
1344
- if (!resourceId && (runId || workflowName || namespace)) key += ":";
1345
2102
  return key;
1346
2103
  }
1347
2104
  async getWorkflowRuns({
1348
- namespace,
1349
2105
  workflowName,
1350
2106
  limit = 20,
1351
2107
  offset = 0,
@@ -1354,28 +2110,32 @@ var CloudflareStore = class extends MastraStorage {
1354
2110
  toDate
1355
2111
  } = {}) {
1356
2112
  try {
1357
- const prefix = this.buildWorkflowSnapshotPrefix({ namespace, workflowName });
1358
- const keyObjs = await this.listKV(TABLE_WORKFLOW_SNAPSHOT, { prefix });
2113
+ const prefix = this.buildWorkflowSnapshotPrefix({ workflowName });
2114
+ const keyObjs = await this.operations.listKV(TABLE_WORKFLOW_SNAPSHOT, { prefix });
1359
2115
  const runs = [];
1360
2116
  for (const { name: key } of keyObjs) {
1361
2117
  const parts = key.split(":");
1362
2118
  const idx = parts.indexOf(TABLE_WORKFLOW_SNAPSHOT);
1363
- if (idx === -1 || parts.length < idx + 4) continue;
1364
- const ns = parts[idx + 1];
1365
- const wfName = parts[idx + 2];
1366
- const keyResourceId = parts.length > idx + 4 ? parts[idx + 4] : void 0;
1367
- if (namespace && ns !== namespace || workflowName && wfName !== workflowName) continue;
1368
- if (resourceId && keyResourceId && keyResourceId !== resourceId) continue;
1369
- const data = await this.getKV(TABLE_WORKFLOW_SNAPSHOT, key);
2119
+ if (idx === -1 || parts.length < idx + 3) continue;
2120
+ const wfName = parts[idx + 1];
2121
+ const keyResourceId = parts.length > idx + 3 ? parts[idx + 3] : void 0;
2122
+ if (workflowName && wfName !== workflowName) continue;
2123
+ if (resourceId && keyResourceId !== resourceId) continue;
2124
+ const data = await this.operations.getKV(TABLE_WORKFLOW_SNAPSHOT, key);
1370
2125
  if (!data) continue;
1371
2126
  try {
1372
- if (resourceId && data.resourceId && data.resourceId !== resourceId) continue;
1373
- const createdAt = this.ensureDate(data.createdAt);
2127
+ if (resourceId && !keyResourceId) continue;
2128
+ const createdAt = ensureDate(data.createdAt);
1374
2129
  if (fromDate && createdAt && createdAt < fromDate) continue;
1375
2130
  if (toDate && createdAt && createdAt > toDate) continue;
1376
- const state = this.normalizeWorkflowState(data.snapshot || data);
1377
- this.validateWorkflowState(state);
1378
- const run = this.parseWorkflowRun({ ...data, snapshot: state });
2131
+ const snapshotData = typeof data.snapshot === "string" ? JSON.parse(data.snapshot) : data.snapshot;
2132
+ const resourceIdToUse = keyResourceId || data.resourceId;
2133
+ const run = this.parseWorkflowRun({
2134
+ ...data,
2135
+ workflow_name: wfName,
2136
+ resourceId: resourceIdToUse,
2137
+ snapshot: snapshotData
2138
+ });
1379
2139
  runs.push(run);
1380
2140
  } catch (err) {
1381
2141
  this.logger.error("Failed to parse workflow snapshot:", { key, error: err });
@@ -1400,29 +2160,35 @@ var CloudflareStore = class extends MastraStorage {
1400
2160
  },
1401
2161
  error
1402
2162
  );
1403
- this.logger?.trackException(mastraError);
1404
- this.logger?.error(mastraError.toString());
2163
+ this.logger.trackException?.(mastraError);
2164
+ this.logger.error(mastraError.toString());
1405
2165
  return { runs: [], total: 0 };
1406
2166
  }
1407
2167
  }
1408
2168
  async getWorkflowRunById({
1409
- namespace,
1410
2169
  runId,
1411
2170
  workflowName
1412
2171
  }) {
1413
2172
  try {
1414
- if (!runId || !workflowName || !namespace) {
1415
- throw new Error("runId, workflowName, and namespace are required");
2173
+ if (!runId || !workflowName) {
2174
+ throw new Error("runId, workflowName, are required");
1416
2175
  }
1417
- const prefix = this.buildWorkflowSnapshotPrefix({ namespace, workflowName, runId });
1418
- const keyObjs = await this.listKV(TABLE_WORKFLOW_SNAPSHOT, { prefix });
2176
+ const prefix = this.buildWorkflowSnapshotPrefix({ workflowName, runId });
2177
+ const keyObjs = await this.operations.listKV(TABLE_WORKFLOW_SNAPSHOT, { prefix });
1419
2178
  if (!keyObjs.length) return null;
1420
- const key = keyObjs[0]?.name;
1421
- const data = await this.getKV(TABLE_WORKFLOW_SNAPSHOT, key);
2179
+ const exactKey = keyObjs.find((k) => {
2180
+ const parts = k.name.split(":");
2181
+ const idx = parts.indexOf(TABLE_WORKFLOW_SNAPSHOT);
2182
+ if (idx === -1 || parts.length < idx + 3) return false;
2183
+ const wfName = parts[idx + 1];
2184
+ const rId = parts[idx + 2];
2185
+ return wfName === workflowName && rId === runId;
2186
+ });
2187
+ if (!exactKey) return null;
2188
+ const data = await this.operations.getKV(TABLE_WORKFLOW_SNAPSHOT, exactKey.name);
1422
2189
  if (!data) return null;
1423
- const state = this.normalizeWorkflowState(data.snapshot || data);
1424
- this.validateWorkflowState(state);
1425
- return this.parseWorkflowRun({ ...data, snapshot: state });
2190
+ const snapshotData = typeof data.snapshot === "string" ? JSON.parse(data.snapshot) : data.snapshot;
2191
+ return this.parseWorkflowRun({ ...data, snapshot: snapshotData });
1426
2192
  } catch (error) {
1427
2193
  const mastraError = new MastraError(
1428
2194
  {
@@ -1430,48 +2196,322 @@ var CloudflareStore = class extends MastraStorage {
1430
2196
  domain: ErrorDomain.STORAGE,
1431
2197
  category: ErrorCategory.THIRD_PARTY,
1432
2198
  details: {
1433
- namespace,
1434
2199
  workflowName,
1435
2200
  runId
1436
2201
  }
1437
2202
  },
1438
2203
  error
1439
2204
  );
1440
- this.logger?.trackException(mastraError);
1441
- this.logger?.error(mastraError.toString());
2205
+ this.logger.trackException?.(mastraError);
2206
+ this.logger.error(mastraError.toString());
1442
2207
  return null;
1443
2208
  }
1444
2209
  }
1445
- async getTracesPaginated(_args) {
1446
- throw new MastraError({
1447
- id: "CLOUDFLARE_STORAGE_GET_TRACES_PAGINATED_FAILED",
1448
- domain: ErrorDomain.STORAGE,
1449
- category: ErrorCategory.THIRD_PARTY,
1450
- text: "Method not implemented."
1451
- });
2210
+ };
2211
+
2212
+ // src/storage/types.ts
2213
+ function isWorkersConfig(config) {
2214
+ return "bindings" in config;
2215
+ }
2216
+
2217
+ // src/storage/index.ts
2218
+ var CloudflareStore = class extends MastraStorage {
2219
+ stores;
2220
+ client;
2221
+ accountId;
2222
+ namespacePrefix;
2223
+ bindings;
2224
+ validateWorkersConfig(config) {
2225
+ if (!isWorkersConfig(config)) {
2226
+ throw new Error("Invalid Workers API configuration");
2227
+ }
2228
+ if (!config.bindings) {
2229
+ throw new Error("KV bindings are required when using Workers Binding API");
2230
+ }
2231
+ const requiredTables = [
2232
+ TABLE_THREADS,
2233
+ TABLE_MESSAGES,
2234
+ TABLE_WORKFLOW_SNAPSHOT,
2235
+ TABLE_EVALS,
2236
+ TABLE_SCORERS,
2237
+ TABLE_TRACES
2238
+ ];
2239
+ for (const table of requiredTables) {
2240
+ if (!(table in config.bindings)) {
2241
+ throw new Error(`Missing KV binding for table: ${table}`);
2242
+ }
2243
+ }
2244
+ }
2245
+ validateRestConfig(config) {
2246
+ if (isWorkersConfig(config)) {
2247
+ throw new Error("Invalid REST API configuration");
2248
+ }
2249
+ if (!config.accountId?.trim()) {
2250
+ throw new Error("accountId is required for REST API");
2251
+ }
2252
+ if (!config.apiToken?.trim()) {
2253
+ throw new Error("apiToken is required for REST API");
2254
+ }
2255
+ }
2256
+ get supports() {
2257
+ const supports = super.supports;
2258
+ supports.getScoresBySpan = true;
2259
+ return supports;
2260
+ }
2261
+ constructor(config) {
2262
+ super({ name: "Cloudflare" });
2263
+ try {
2264
+ if (isWorkersConfig(config)) {
2265
+ this.validateWorkersConfig(config);
2266
+ this.bindings = config.bindings;
2267
+ this.namespacePrefix = config.keyPrefix?.trim() || "";
2268
+ this.logger.info("Using Cloudflare KV Workers Binding API");
2269
+ } else {
2270
+ this.validateRestConfig(config);
2271
+ this.accountId = config.accountId.trim();
2272
+ this.namespacePrefix = config.namespacePrefix?.trim() || "";
2273
+ this.client = new Cloudflare({
2274
+ apiToken: config.apiToken.trim()
2275
+ });
2276
+ this.logger.info("Using Cloudflare KV REST API");
2277
+ }
2278
+ const operations = new StoreOperationsCloudflare({
2279
+ accountId: this.accountId,
2280
+ client: this.client,
2281
+ namespacePrefix: this.namespacePrefix,
2282
+ bindings: this.bindings
2283
+ });
2284
+ const legacyEvals = new LegacyEvalsStorageCloudflare({
2285
+ operations
2286
+ });
2287
+ const workflows = new WorkflowsStorageCloudflare({
2288
+ operations
2289
+ });
2290
+ const traces = new TracesStorageCloudflare({
2291
+ operations
2292
+ });
2293
+ const memory = new MemoryStorageCloudflare({
2294
+ operations
2295
+ });
2296
+ const scores = new ScoresStorageCloudflare({
2297
+ operations
2298
+ });
2299
+ this.stores = {
2300
+ operations,
2301
+ legacyEvals,
2302
+ workflows,
2303
+ traces,
2304
+ memory,
2305
+ scores
2306
+ };
2307
+ } catch (error) {
2308
+ throw new MastraError(
2309
+ {
2310
+ id: "CLOUDFLARE_STORAGE_INIT_FAILED",
2311
+ domain: ErrorDomain.STORAGE,
2312
+ category: ErrorCategory.THIRD_PARTY
2313
+ },
2314
+ error
2315
+ );
2316
+ }
2317
+ }
2318
+ async createTable({
2319
+ tableName,
2320
+ schema
2321
+ }) {
2322
+ return this.stores.operations.createTable({ tableName, schema });
2323
+ }
2324
+ async alterTable(_args) {
2325
+ return this.stores.operations.alterTable(_args);
2326
+ }
2327
+ async clearTable({ tableName }) {
2328
+ return this.stores.operations.clearTable({ tableName });
2329
+ }
2330
+ async dropTable({ tableName }) {
2331
+ return this.stores.operations.dropTable({ tableName });
2332
+ }
2333
+ async insert({
2334
+ tableName,
2335
+ record
2336
+ }) {
2337
+ return this.stores.operations.insert({ tableName, record });
2338
+ }
2339
+ async load({ tableName, keys }) {
2340
+ return this.stores.operations.load({ tableName, keys });
2341
+ }
2342
+ async getThreadById({ threadId }) {
2343
+ return this.stores.memory.getThreadById({ threadId });
2344
+ }
2345
+ async getThreadsByResourceId({ resourceId }) {
2346
+ return this.stores.memory.getThreadsByResourceId({ resourceId });
2347
+ }
2348
+ async saveThread({ thread }) {
2349
+ return this.stores.memory.saveThread({ thread });
2350
+ }
2351
+ async updateThread({
2352
+ id,
2353
+ title,
2354
+ metadata
2355
+ }) {
2356
+ return this.stores.memory.updateThread({ id, title, metadata });
2357
+ }
2358
+ async deleteThread({ threadId }) {
2359
+ return this.stores.memory.deleteThread({ threadId });
2360
+ }
2361
+ async saveMessages(args) {
2362
+ return this.stores.memory.saveMessages(args);
2363
+ }
2364
+ async getMessages({
2365
+ threadId,
2366
+ resourceId,
2367
+ selectBy,
2368
+ format
2369
+ }) {
2370
+ return this.stores.memory.getMessages({ threadId, resourceId, selectBy, format });
2371
+ }
2372
+ async updateWorkflowResults({
2373
+ workflowName,
2374
+ runId,
2375
+ stepId,
2376
+ result,
2377
+ runtimeContext
2378
+ }) {
2379
+ return this.stores.workflows.updateWorkflowResults({ workflowName, runId, stepId, result, runtimeContext });
2380
+ }
2381
+ async updateWorkflowState({
2382
+ workflowName,
2383
+ runId,
2384
+ opts
2385
+ }) {
2386
+ return this.stores.workflows.updateWorkflowState({ workflowName, runId, opts });
2387
+ }
2388
+ async getMessagesById({
2389
+ messageIds,
2390
+ format
2391
+ }) {
2392
+ return this.stores.memory.getMessagesById({ messageIds, format });
2393
+ }
2394
+ async persistWorkflowSnapshot(params) {
2395
+ return this.stores.workflows.persistWorkflowSnapshot(params);
2396
+ }
2397
+ async loadWorkflowSnapshot(params) {
2398
+ return this.stores.workflows.loadWorkflowSnapshot(params);
2399
+ }
2400
+ async batchInsert(input) {
2401
+ return this.stores.operations.batchInsert(input);
1452
2402
  }
1453
- async getThreadsByResourceIdPaginated(_args) {
1454
- throw new MastraError({
1455
- id: "CLOUDFLARE_STORAGE_GET_THREADS_BY_RESOURCE_ID_PAGINATED_FAILED",
1456
- domain: ErrorDomain.STORAGE,
1457
- category: ErrorCategory.THIRD_PARTY,
1458
- text: "Method not implemented."
2403
+ async getTraces({
2404
+ name,
2405
+ scope,
2406
+ page = 0,
2407
+ perPage = 100,
2408
+ attributes,
2409
+ fromDate,
2410
+ toDate
2411
+ }) {
2412
+ return this.stores.traces.getTraces({
2413
+ name,
2414
+ scope,
2415
+ page,
2416
+ perPage,
2417
+ attributes,
2418
+ fromDate,
2419
+ toDate
1459
2420
  });
1460
2421
  }
1461
- async getMessagesPaginated(_args) {
1462
- throw new MastraError({
1463
- id: "CLOUDFLARE_STORAGE_GET_MESSAGES_PAGINATED_FAILED",
1464
- domain: ErrorDomain.STORAGE,
1465
- category: ErrorCategory.THIRD_PARTY,
1466
- text: "Method not implemented."
2422
+ async getEvalsByAgentName(agentName, type) {
2423
+ return this.stores.legacyEvals.getEvalsByAgentName(agentName, type);
2424
+ }
2425
+ async getEvals(options) {
2426
+ return this.stores.legacyEvals.getEvals(options);
2427
+ }
2428
+ async getWorkflowRuns({
2429
+ workflowName,
2430
+ limit = 20,
2431
+ offset = 0,
2432
+ resourceId,
2433
+ fromDate,
2434
+ toDate
2435
+ } = {}) {
2436
+ return this.stores.workflows.getWorkflowRuns({
2437
+ workflowName,
2438
+ limit,
2439
+ offset,
2440
+ resourceId,
2441
+ fromDate,
2442
+ toDate
1467
2443
  });
1468
2444
  }
1469
- async close() {
2445
+ async getWorkflowRunById({
2446
+ runId,
2447
+ workflowName
2448
+ }) {
2449
+ return this.stores.workflows.getWorkflowRunById({ runId, workflowName });
2450
+ }
2451
+ async getTracesPaginated(args) {
2452
+ return this.stores.traces.getTracesPaginated(args);
2453
+ }
2454
+ async getThreadsByResourceIdPaginated(args) {
2455
+ return this.stores.memory.getThreadsByResourceIdPaginated(args);
2456
+ }
2457
+ async getMessagesPaginated(args) {
2458
+ return this.stores.memory.getMessagesPaginated(args);
2459
+ }
2460
+ async updateMessages(args) {
2461
+ return this.stores.memory.updateMessages(args);
2462
+ }
2463
+ async getScoreById({ id }) {
2464
+ return this.stores.scores.getScoreById({ id });
1470
2465
  }
1471
- async updateMessages(_args) {
1472
- this.logger.error("updateMessages is not yet implemented in CloudflareStore");
1473
- throw new Error("Method not implemented");
2466
+ async saveScore(score) {
2467
+ return this.stores.scores.saveScore(score);
2468
+ }
2469
+ async getScoresByRunId({
2470
+ runId,
2471
+ pagination
2472
+ }) {
2473
+ return this.stores.scores.getScoresByRunId({ runId, pagination });
2474
+ }
2475
+ async getScoresByEntityId({
2476
+ entityId,
2477
+ entityType,
2478
+ pagination
2479
+ }) {
2480
+ return this.stores.scores.getScoresByEntityId({ entityId, entityType, pagination });
2481
+ }
2482
+ async getScoresByScorerId({
2483
+ scorerId,
2484
+ entityId,
2485
+ entityType,
2486
+ source,
2487
+ pagination
2488
+ }) {
2489
+ return this.stores.scores.getScoresByScorerId({ scorerId, entityId, entityType, source, pagination });
2490
+ }
2491
+ async getScoresBySpan({
2492
+ traceId,
2493
+ spanId,
2494
+ pagination
2495
+ }) {
2496
+ return this.stores.scores.getScoresBySpan({ traceId, spanId, pagination });
2497
+ }
2498
+ async getResourceById({ resourceId }) {
2499
+ return this.stores.memory.getResourceById({ resourceId });
2500
+ }
2501
+ async saveResource({ resource }) {
2502
+ return this.stores.memory.saveResource({ resource });
2503
+ }
2504
+ async updateResource({
2505
+ resourceId,
2506
+ workingMemory,
2507
+ metadata
2508
+ }) {
2509
+ return this.stores.memory.updateResource({ resourceId, workingMemory, metadata });
2510
+ }
2511
+ async close() {
1474
2512
  }
1475
2513
  };
1476
2514
 
1477
2515
  export { CloudflareStore };
2516
+ //# sourceMappingURL=index.js.map
2517
+ //# sourceMappingURL=index.js.map