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