@mastra/convex 1.3.2 → 1.4.0-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +38 -0
- package/README.md +28 -17
- package/dist/{chunk-6P3LEDHY.cjs → chunk-PKCUPSKO.cjs} +315 -2
- package/dist/chunk-PKCUPSKO.cjs.map +1 -0
- package/dist/{chunk-MC75WADX.js → chunk-QWWFHN52.js} +41 -3
- package/dist/chunk-QWWFHN52.js.map +1 -0
- package/dist/{chunk-77UWNT5X.js → chunk-SQMTQ7ZD.js} +315 -2
- package/dist/chunk-SQMTQ7ZD.js.map +1 -0
- package/dist/{chunk-SFRHJGSM.cjs → chunk-ZCCKLZQH.cjs} +42 -2
- package/dist/chunk-ZCCKLZQH.cjs.map +1 -0
- package/dist/docs/SKILL.md +1 -1
- package/dist/docs/assets/SOURCE_MAP.json +41 -33
- package/dist/docs/references/reference-storage-convex.md +18 -11
- package/dist/index.cjs +586 -44
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +542 -4
- package/dist/index.js.map +1 -1
- package/dist/schema.cjs +38 -30
- package/dist/schema.d.ts +95 -1
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +1 -1
- package/dist/server/index-map.d.ts.map +1 -1
- package/dist/server/index.cjs +41 -33
- package/dist/server/index.d.ts +1 -1
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +2 -2
- package/dist/server/observational-memory.d.ts +52 -0
- package/dist/server/observational-memory.d.ts.map +1 -0
- package/dist/server/storage.d.ts.map +1 -1
- package/dist/storage/db/index.d.ts +65 -14
- package/dist/storage/db/index.d.ts.map +1 -1
- package/dist/storage/domains/memory/index.d.ts +23 -1
- package/dist/storage/domains/memory/index.d.ts.map +1 -1
- package/dist/storage/domains/schedules/index.d.ts.map +1 -1
- package/dist/storage/domains/scores/index.d.ts.map +1 -1
- package/dist/storage/types.d.ts +133 -0
- package/dist/storage/types.d.ts.map +1 -1
- package/dist/vector/index.d.ts.map +1 -1
- package/dist/vector/native.d.ts.map +1 -1
- package/package.json +9 -9
- package/dist/chunk-6P3LEDHY.cjs.map +0 -1
- package/dist/chunk-77UWNT5X.js.map +0 -1
- package/dist/chunk-MC75WADX.js.map +0 -1
- package/dist/chunk-SFRHJGSM.cjs.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,43 @@
|
|
|
1
1
|
# @mastra/convex
|
|
2
2
|
|
|
3
|
+
## 1.4.0-alpha.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- Added Observational Memory support to the Convex storage adapter. Agents using `ConvexStore` can now enable `observationalMemory` in `@mastra/memory`, including async observation buffering and reflections. ([#19474](https://github.com/mastra-ai/mastra/pull/19474))
|
|
8
|
+
|
|
9
|
+
To enable it, add the new `mastraObservationalMemoryTable` to your Convex schema and redeploy:
|
|
10
|
+
|
|
11
|
+
```ts title="convex/schema.ts"
|
|
12
|
+
import { defineSchema } from 'convex/server';
|
|
13
|
+
import {
|
|
14
|
+
mastraThreadsTable,
|
|
15
|
+
mastraMessagesTable,
|
|
16
|
+
mastraResourcesTable,
|
|
17
|
+
mastraObservationalMemoryTable,
|
|
18
|
+
// ...other Mastra tables
|
|
19
|
+
} from '@mastra/convex/schema';
|
|
20
|
+
|
|
21
|
+
export default defineSchema({
|
|
22
|
+
mastra_threads: mastraThreadsTable,
|
|
23
|
+
mastra_messages: mastraMessagesTable,
|
|
24
|
+
mastra_resources: mastraResourcesTable,
|
|
25
|
+
mastra_observational_memory: mastraObservationalMemoryTable,
|
|
26
|
+
// ...other Mastra tables
|
|
27
|
+
});
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Then run `npx convex deploy` (or `npx convex dev`) so the new table and storage operations are live. All observational memory writes run inside the deployed Convex storage mutation, so buffered-observation swaps and reflection generations are atomic.
|
|
31
|
+
|
|
32
|
+
### Patch Changes
|
|
33
|
+
|
|
34
|
+
- Improved type safety of the Convex storage layer. Table names accepted by the internal ConvexDB are now a closed union (core storage tables plus the observational memory table) instead of any string, so table-name typos are caught at compile time instead of silently routing records to the mastra_documents fallback table. The internal observational memory operations no longer take a table-name argument. ([#19485](https://github.com/mastra-ai/mastra/pull/19485))
|
|
35
|
+
|
|
36
|
+
- Removed the Convex adapters' only Node builtin dependency. ConvexStore, the vector adapters, and the storage domains now use the Web Crypto API (crypto.randomUUID) instead of importing node:crypto, so @mastra/convex itself no longer requires the Node.js runtime ("use node") to bundle inside a Convex project. ([#19498](https://github.com/mastra-ai/mastra/pull/19498))
|
|
37
|
+
|
|
38
|
+
- Updated dependencies [[`8a0d145`](https://github.com/mastra-ai/mastra/commit/8a0d145aadbdf7278665aceaaec364b35dd9bd94), [`bd2f1d2`](https://github.com/mastra-ai/mastra/commit/bd2f1d274d05e60e2366f005ea0d94d5cea0d5ff), [`21a0eb8`](https://github.com/mastra-ai/mastra/commit/21a0eb86746ba0b703acea360d4f84c6a5a493f2), [`de86fd7`](https://github.com/mastra-ai/mastra/commit/de86fd7119f0438381d1a642e3d258143c0b9c29), [`2745031`](https://github.com/mastra-ai/mastra/commit/2745031d1d4a4978f037092da371428c32e2842a), [`db650ce`](https://github.com/mastra-ai/mastra/commit/db650ce490348914e85b93651d83acdf8f2a4c31), [`6354eeb`](https://github.com/mastra-ai/mastra/commit/6354eeb32efa9f5f68f51dda394e90e2ee76f1fb)]:
|
|
39
|
+
- @mastra/core@1.51.1-alpha.0
|
|
40
|
+
|
|
3
41
|
## 1.3.2
|
|
4
42
|
|
|
5
43
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Convex adapters for Mastra:
|
|
4
4
|
|
|
5
|
-
- `ConvexStore` implements the Mastra storage contract (threads, messages, workflows, scores, resources, schedules, channels, background tasks).
|
|
5
|
+
- `ConvexStore` implements the Mastra storage contract (threads, messages, workflows, scores, resources, schedules, channels, background tasks, observational memory).
|
|
6
6
|
- `ConvexVector` stores embeddings inside Convex and performs development-scale cosine similarity search.
|
|
7
7
|
- `ConvexNativeVector` uses Convex native vector search for production workloads.
|
|
8
8
|
- `ConvexServerCache` stores Mastra server cache entries in Convex for durable stream replay and response caching.
|
|
@@ -33,6 +33,7 @@ import {
|
|
|
33
33
|
mastraChannelInstallationsTable,
|
|
34
34
|
mastraChannelConfigTable,
|
|
35
35
|
mastraBackgroundTasksTable,
|
|
36
|
+
mastraObservationalMemoryTable,
|
|
36
37
|
mastraVectorIndexesTable,
|
|
37
38
|
mastraVectorsTable,
|
|
38
39
|
mastraCacheTable,
|
|
@@ -51,6 +52,7 @@ export default defineSchema({
|
|
|
51
52
|
mastra_channel_installations: mastraChannelInstallationsTable,
|
|
52
53
|
mastra_channel_config: mastraChannelConfigTable,
|
|
53
54
|
mastra_background_tasks: mastraBackgroundTasksTable,
|
|
55
|
+
mastra_observational_memory: mastraObservationalMemoryTable,
|
|
54
56
|
mastra_vector_indexes: mastraVectorIndexesTable,
|
|
55
57
|
mastra_vectors: mastraVectorsTable,
|
|
56
58
|
mastra_cache: mastraCacheTable,
|
|
@@ -170,22 +172,23 @@ Native vector search uses Convex's schema-defined vector indexes and action-only
|
|
|
170
172
|
|
|
171
173
|
This adapter uses **typed Convex tables** for each Mastra domain:
|
|
172
174
|
|
|
173
|
-
| Domain
|
|
174
|
-
|
|
|
175
|
-
| Threads
|
|
176
|
-
| Messages
|
|
177
|
-
| Resources
|
|
178
|
-
| Workflows
|
|
179
|
-
| Scorers
|
|
180
|
-
| Schedules
|
|
181
|
-
| Triggers
|
|
182
|
-
| Channels
|
|
183
|
-
| Background Tasks
|
|
184
|
-
|
|
|
185
|
-
|
|
|
186
|
-
|
|
|
187
|
-
| Cache
|
|
188
|
-
|
|
|
175
|
+
| Domain | Convex Table | Purpose |
|
|
176
|
+
| -------------------- | ------------------------------------------------------- | -------------------------------- |
|
|
177
|
+
| Threads | `mastra_threads` | Conversation threads |
|
|
178
|
+
| Messages | `mastra_messages` | Chat messages |
|
|
179
|
+
| Resources | `mastra_resources` | User working memory |
|
|
180
|
+
| Workflows | `mastra_workflow_snapshots` | Workflow state |
|
|
181
|
+
| Scorers | `mastra_scorers` | Evaluation data |
|
|
182
|
+
| Schedules | `mastra_schedules` | Workflow schedules |
|
|
183
|
+
| Triggers | `mastra_schedule_triggers` | Schedule history |
|
|
184
|
+
| Channels | `mastra_channel_installations`, `mastra_channel_config` | Channel installations and config |
|
|
185
|
+
| Background Tasks | `mastra_background_tasks` | Background task state |
|
|
186
|
+
| Observational Memory | `mastra_observational_memory` | Observational memory generations |
|
|
187
|
+
| Vector Indexes | `mastra_vector_indexes` | Index metadata |
|
|
188
|
+
| Vectors | `mastra_vectors` | Embeddings |
|
|
189
|
+
| Cache | `mastra_cache` | Cache metadata |
|
|
190
|
+
| Cache Items | `mastra_cache_list_items` | Cache list entries |
|
|
191
|
+
| Fallback | `mastra_documents` | Unknown tables |
|
|
189
192
|
|
|
190
193
|
All typed tables include:
|
|
191
194
|
|
|
@@ -196,6 +199,14 @@ Schedule due reads and trigger-history reads use bounded Convex queries to avoid
|
|
|
196
199
|
|
|
197
200
|
Background task reads and updates also tolerate older rows that were written to the fallback `mastra_documents` table.
|
|
198
201
|
|
|
202
|
+
### Observational memory
|
|
203
|
+
|
|
204
|
+
`ConvexStore` supports Mastra's observational memory. Records are keyed by a `lookupKey` (`thread:{id}` or `resource:{id}`) and the latest generation is served through the `by_lookup_key` index. All observational memory read-modify-write operations run inside the deployed storage mutation, so swaps and counter updates are atomic.
|
|
205
|
+
|
|
206
|
+
Upgrading from a version without observational memory: add `mastraObservationalMemoryTable` to your `convex/schema.ts` (as shown in the quick start) and run `npx convex deploy` again.
|
|
207
|
+
|
|
208
|
+
Active observations and buffered chunks share one Convex document, which is subject to Convex's 1 MiB document size limit. Default observational memory thresholds stay well below this limit; extremely large custom thresholds can exceed it.
|
|
209
|
+
|
|
199
210
|
## Testing
|
|
200
211
|
|
|
201
212
|
Set the following environment variables before running tests:
|
|
@@ -267,6 +267,10 @@ var TABLE_INDEX_MAP = {
|
|
|
267
267
|
mastra_vector_indexes: [
|
|
268
268
|
{ name: "by_name", fields: ["indexName"] },
|
|
269
269
|
{ name: "by_record_id", fields: ["id"] }
|
|
270
|
+
],
|
|
271
|
+
mastra_observational_memory: [
|
|
272
|
+
{ name: "by_lookup_key", fields: ["lookupKey", "generationCount"] },
|
|
273
|
+
{ name: "by_record_id", fields: ["id"] }
|
|
270
274
|
]
|
|
271
275
|
};
|
|
272
276
|
function findBestIndex(convexTable, filters) {
|
|
@@ -296,6 +300,299 @@ function findBestIndex(convexTable, filters) {
|
|
|
296
300
|
return best ? { indexName: best.indexName, indexedFilters: best.indexedFilters } : null;
|
|
297
301
|
}
|
|
298
302
|
|
|
303
|
+
// src/server/observational-memory.ts
|
|
304
|
+
var OM_QUERY_MAX_DOCS = 1e4;
|
|
305
|
+
function parseStoredChunks(value) {
|
|
306
|
+
if (typeof value !== "string" || !value) return [];
|
|
307
|
+
try {
|
|
308
|
+
const parsed = JSON.parse(value);
|
|
309
|
+
return Array.isArray(parsed) ? parsed : [];
|
|
310
|
+
} catch {
|
|
311
|
+
return [];
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
function selectActivationBoundary(chunks, opts) {
|
|
315
|
+
const retentionFloor = opts.messageTokensThreshold * (1 - opts.activationRatio);
|
|
316
|
+
const targetMessageTokens = Math.max(0, opts.currentPendingTokens - retentionFloor);
|
|
317
|
+
let cumulativeMessageTokens = 0;
|
|
318
|
+
let bestOverBoundary = 0;
|
|
319
|
+
let bestOverTokens = 0;
|
|
320
|
+
let bestUnderBoundary = 0;
|
|
321
|
+
let bestUnderTokens = 0;
|
|
322
|
+
for (let i = 0; i < chunks.length; i++) {
|
|
323
|
+
cumulativeMessageTokens += chunks[i].messageTokens ?? 0;
|
|
324
|
+
const boundary = i + 1;
|
|
325
|
+
if (cumulativeMessageTokens >= targetMessageTokens) {
|
|
326
|
+
if (bestOverBoundary === 0 || cumulativeMessageTokens < bestOverTokens) {
|
|
327
|
+
bestOverBoundary = boundary;
|
|
328
|
+
bestOverTokens = cumulativeMessageTokens;
|
|
329
|
+
}
|
|
330
|
+
} else {
|
|
331
|
+
if (cumulativeMessageTokens > bestUnderTokens) {
|
|
332
|
+
bestUnderBoundary = boundary;
|
|
333
|
+
bestUnderTokens = cumulativeMessageTokens;
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
const maxOvershoot = retentionFloor * 0.95;
|
|
338
|
+
const overshoot = bestOverTokens - targetMessageTokens;
|
|
339
|
+
const remainingAfterOver = opts.currentPendingTokens - bestOverTokens;
|
|
340
|
+
const remainingAfterUnder = opts.currentPendingTokens - bestUnderTokens;
|
|
341
|
+
const minRemaining = Math.min(1e3, retentionFloor);
|
|
342
|
+
if (opts.forceMaxActivation && bestOverBoundary > 0 && remainingAfterOver >= minRemaining) {
|
|
343
|
+
return bestOverBoundary;
|
|
344
|
+
}
|
|
345
|
+
if (bestOverBoundary > 0 && overshoot <= maxOvershoot && remainingAfterOver >= minRemaining) {
|
|
346
|
+
return bestOverBoundary;
|
|
347
|
+
}
|
|
348
|
+
if (bestUnderBoundary > 0 && remainingAfterUnder >= minRemaining) {
|
|
349
|
+
return bestUnderBoundary;
|
|
350
|
+
}
|
|
351
|
+
if (bestOverBoundary > 0) {
|
|
352
|
+
return bestOverBoundary;
|
|
353
|
+
}
|
|
354
|
+
return 1;
|
|
355
|
+
}
|
|
356
|
+
function mergeReflectionWithUnreflected(activeObservations, bufferedReflection, reflectedLineCount) {
|
|
357
|
+
const allLines = (activeObservations || "").split("\n");
|
|
358
|
+
const unreflectedLines = allLines.slice(reflectedLineCount);
|
|
359
|
+
const unreflectedContent = unreflectedLines.join("\n").trim();
|
|
360
|
+
return unreflectedContent ? `${bufferedReflection}
|
|
361
|
+
|
|
362
|
+
${unreflectedContent}` : bufferedReflection;
|
|
363
|
+
}
|
|
364
|
+
function isPlainObj(value) {
|
|
365
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
366
|
+
}
|
|
367
|
+
function deepMergeOMConfig(target, source) {
|
|
368
|
+
const output = { ...target };
|
|
369
|
+
for (const key of Object.keys(source)) {
|
|
370
|
+
const tVal = target[key];
|
|
371
|
+
const sVal = source[key];
|
|
372
|
+
if (isPlainObj(tVal) && isPlainObj(sVal)) {
|
|
373
|
+
output[key] = deepMergeOMConfig(tVal, sVal);
|
|
374
|
+
} else if (sVal !== void 0) {
|
|
375
|
+
output[key] = sVal;
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
return output;
|
|
379
|
+
}
|
|
380
|
+
function parseJsonObject(value) {
|
|
381
|
+
if (typeof value !== "string" || !value) return {};
|
|
382
|
+
try {
|
|
383
|
+
const parsed = JSON.parse(value);
|
|
384
|
+
return isPlainObj(parsed) ? parsed : {};
|
|
385
|
+
} catch {
|
|
386
|
+
return {};
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
async function findRecordById(ctx, convexTable, id) {
|
|
390
|
+
return await ctx.db.query(convexTable).withIndex("by_record_id", (q) => q.eq("id", id)).unique();
|
|
391
|
+
}
|
|
392
|
+
function requireRecord(doc, id) {
|
|
393
|
+
if (!doc) {
|
|
394
|
+
throw new Error(`Observational memory record not found: ${id}`);
|
|
395
|
+
}
|
|
396
|
+
return doc;
|
|
397
|
+
}
|
|
398
|
+
var EMPTY_SWAP_RESULT = {
|
|
399
|
+
chunksActivated: 0,
|
|
400
|
+
messageTokensActivated: 0,
|
|
401
|
+
observationTokensActivated: 0,
|
|
402
|
+
messagesActivated: 0,
|
|
403
|
+
activatedCycleIds: [],
|
|
404
|
+
activatedMessageIds: []
|
|
405
|
+
};
|
|
406
|
+
async function handleObservationalMemoryOperation(ctx, convexTable, request) {
|
|
407
|
+
switch (request.op) {
|
|
408
|
+
case "omGetLatest": {
|
|
409
|
+
const doc = await ctx.db.query(convexTable).withIndex("by_lookup_key", (q) => q.eq("lookupKey", request.lookupKey)).order("desc").first();
|
|
410
|
+
return { ok: true, result: doc ?? null };
|
|
411
|
+
}
|
|
412
|
+
case "omGetHistory": {
|
|
413
|
+
let docs = await ctx.db.query(convexTable).withIndex("by_lookup_key", (q) => q.eq("lookupKey", request.lookupKey)).order("desc").take(OM_QUERY_MAX_DOCS);
|
|
414
|
+
if (request.from) {
|
|
415
|
+
docs = docs.filter((doc) => typeof doc.createdAt === "string" && doc.createdAt >= request.from);
|
|
416
|
+
}
|
|
417
|
+
if (request.to) {
|
|
418
|
+
docs = docs.filter((doc) => typeof doc.createdAt === "string" && doc.createdAt <= request.to);
|
|
419
|
+
}
|
|
420
|
+
if (request.offset != null) {
|
|
421
|
+
docs = docs.slice(request.offset);
|
|
422
|
+
}
|
|
423
|
+
return { ok: true, result: docs.slice(0, request.limit) };
|
|
424
|
+
}
|
|
425
|
+
case "omUpdateActive": {
|
|
426
|
+
const doc = requireRecord(await findRecordById(ctx, convexTable, request.id), request.id);
|
|
427
|
+
const safeTokenCount = Number.isFinite(request.tokenCount) && request.tokenCount >= 0 ? request.tokenCount : 0;
|
|
428
|
+
await ctx.db.patch(doc._id, {
|
|
429
|
+
activeObservations: request.observations,
|
|
430
|
+
lastObservedAt: request.lastObservedAt,
|
|
431
|
+
// Reset pending tokens since we've now observed them
|
|
432
|
+
pendingMessageTokens: 0,
|
|
433
|
+
observationTokenCount: safeTokenCount,
|
|
434
|
+
totalTokensObserved: Number(doc.totalTokensObserved || 0) + safeTokenCount,
|
|
435
|
+
observedMessageIds: request.observedMessageIds,
|
|
436
|
+
updatedAt: request.updatedAt
|
|
437
|
+
});
|
|
438
|
+
return { ok: true };
|
|
439
|
+
}
|
|
440
|
+
case "omAppendBufferedChunk": {
|
|
441
|
+
const doc = requireRecord(await findRecordById(ctx, convexTable, request.id), request.id);
|
|
442
|
+
const chunks = parseStoredChunks(doc.bufferedObservationChunks);
|
|
443
|
+
chunks.push(request.chunk);
|
|
444
|
+
const patch = {
|
|
445
|
+
bufferedObservationChunks: JSON.stringify(chunks),
|
|
446
|
+
updatedAt: request.updatedAt
|
|
447
|
+
};
|
|
448
|
+
if (request.lastBufferedAtTime) {
|
|
449
|
+
patch.lastBufferedAtTime = request.lastBufferedAtTime;
|
|
450
|
+
}
|
|
451
|
+
await ctx.db.patch(doc._id, patch);
|
|
452
|
+
return { ok: true };
|
|
453
|
+
}
|
|
454
|
+
case "omSwapBuffered": {
|
|
455
|
+
const doc = requireRecord(await findRecordById(ctx, convexTable, request.id), request.id);
|
|
456
|
+
const persistedChunks = parseStoredChunks(doc.bufferedObservationChunks);
|
|
457
|
+
if (persistedChunks.length === 0) {
|
|
458
|
+
return { ok: true, result: EMPTY_SWAP_RESULT };
|
|
459
|
+
}
|
|
460
|
+
const chunks = Array.isArray(request.bufferedChunks) ? request.bufferedChunks : persistedChunks;
|
|
461
|
+
if (chunks.length === 0) {
|
|
462
|
+
return { ok: true, result: EMPTY_SWAP_RESULT };
|
|
463
|
+
}
|
|
464
|
+
const chunksToActivate = selectActivationBoundary(chunks, {
|
|
465
|
+
activationRatio: request.activationRatio,
|
|
466
|
+
messageTokensThreshold: request.messageTokensThreshold,
|
|
467
|
+
currentPendingTokens: request.currentPendingTokens,
|
|
468
|
+
forceMaxActivation: request.forceMaxActivation
|
|
469
|
+
});
|
|
470
|
+
const activatedChunks = chunks.slice(0, chunksToActivate);
|
|
471
|
+
const remainingChunks = chunks.slice(chunksToActivate);
|
|
472
|
+
const activatedContent = activatedChunks.map((c) => c.observations).join("\n\n");
|
|
473
|
+
const activatedTokens = activatedChunks.reduce((sum, c) => sum + c.tokenCount, 0);
|
|
474
|
+
const activatedMessageTokens = activatedChunks.reduce((sum, c) => sum + (c.messageTokens ?? 0), 0);
|
|
475
|
+
const activatedMessageCount = activatedChunks.reduce((sum, c) => sum + (c.messageIds?.length ?? 0), 0);
|
|
476
|
+
const activatedCycleIds = activatedChunks.map((c) => c.cycleId).filter((id) => !!id);
|
|
477
|
+
const activatedMessageIds = activatedChunks.flatMap((c) => c.messageIds ?? []);
|
|
478
|
+
const latestChunk = activatedChunks[activatedChunks.length - 1];
|
|
479
|
+
const lastObservedAt = request.lastObservedAt ?? latestChunk?.lastObservedAt ?? request.now;
|
|
480
|
+
const existingActive = doc.activeObservations || "";
|
|
481
|
+
const boundary = `
|
|
482
|
+
|
|
483
|
+
--- message boundary (${lastObservedAt}) ---
|
|
484
|
+
|
|
485
|
+
`;
|
|
486
|
+
const newActive = existingActive ? `${existingActive}${boundary}${activatedContent}` : activatedContent;
|
|
487
|
+
await ctx.db.patch(doc._id, {
|
|
488
|
+
activeObservations: newActive,
|
|
489
|
+
observationTokenCount: Number(doc.observationTokenCount || 0) + activatedTokens,
|
|
490
|
+
// Decrement pending message tokens (clamped to zero)
|
|
491
|
+
pendingMessageTokens: Math.max(0, Number(doc.pendingMessageTokens || 0) - activatedMessageTokens),
|
|
492
|
+
bufferedObservationChunks: remainingChunks.length > 0 ? JSON.stringify(remainingChunks) : null,
|
|
493
|
+
lastObservedAt,
|
|
494
|
+
updatedAt: request.now
|
|
495
|
+
});
|
|
496
|
+
const latestChunkHints = activatedChunks[activatedChunks.length - 1];
|
|
497
|
+
return {
|
|
498
|
+
ok: true,
|
|
499
|
+
result: {
|
|
500
|
+
chunksActivated: activatedChunks.length,
|
|
501
|
+
messageTokensActivated: activatedMessageTokens,
|
|
502
|
+
observationTokensActivated: activatedTokens,
|
|
503
|
+
messagesActivated: activatedMessageCount,
|
|
504
|
+
activatedCycleIds,
|
|
505
|
+
activatedMessageIds,
|
|
506
|
+
observations: activatedContent,
|
|
507
|
+
perChunk: activatedChunks.map((c) => ({
|
|
508
|
+
cycleId: c.cycleId ?? "",
|
|
509
|
+
messageTokens: c.messageTokens ?? 0,
|
|
510
|
+
observationTokens: c.tokenCount,
|
|
511
|
+
messageCount: c.messageIds?.length ?? 0,
|
|
512
|
+
observations: c.observations
|
|
513
|
+
})),
|
|
514
|
+
suggestedContinuation: latestChunkHints?.suggestedContinuation ?? void 0,
|
|
515
|
+
currentTask: latestChunkHints?.currentTask ?? void 0
|
|
516
|
+
}
|
|
517
|
+
};
|
|
518
|
+
}
|
|
519
|
+
case "omUpdateBufferedReflection": {
|
|
520
|
+
const doc = requireRecord(await findRecordById(ctx, convexTable, request.id), request.id);
|
|
521
|
+
const existingContent = doc.bufferedReflection || "";
|
|
522
|
+
await ctx.db.patch(doc._id, {
|
|
523
|
+
bufferedReflection: existingContent ? `${existingContent}
|
|
524
|
+
|
|
525
|
+
${request.reflection}` : request.reflection,
|
|
526
|
+
bufferedReflectionTokens: Number(doc.bufferedReflectionTokens || 0) + request.tokenCount,
|
|
527
|
+
bufferedReflectionInputTokens: Number(doc.bufferedReflectionInputTokens || 0) + request.inputTokenCount,
|
|
528
|
+
reflectedObservationLineCount: request.reflectedObservationLineCount,
|
|
529
|
+
updatedAt: request.updatedAt
|
|
530
|
+
});
|
|
531
|
+
return { ok: true };
|
|
532
|
+
}
|
|
533
|
+
case "omSwapBufferedReflection": {
|
|
534
|
+
const { currentRecord, newId, tokenCount, now } = request;
|
|
535
|
+
const doc = requireRecord(await findRecordById(ctx, convexTable, currentRecord.id), currentRecord.id);
|
|
536
|
+
const bufferedReflection = doc.bufferedReflection || "";
|
|
537
|
+
if (!bufferedReflection) {
|
|
538
|
+
throw new Error("No buffered reflection to swap");
|
|
539
|
+
}
|
|
540
|
+
const newObservations = mergeReflectionWithUnreflected(
|
|
541
|
+
doc.activeObservations || "",
|
|
542
|
+
bufferedReflection,
|
|
543
|
+
Number(doc.reflectedObservationLineCount || 0)
|
|
544
|
+
);
|
|
545
|
+
const newRecord = {
|
|
546
|
+
id: newId,
|
|
547
|
+
lookupKey: currentRecord.lookupKey,
|
|
548
|
+
scope: currentRecord.scope,
|
|
549
|
+
resourceId: currentRecord.resourceId,
|
|
550
|
+
threadId: currentRecord.threadId,
|
|
551
|
+
activeObservations: newObservations,
|
|
552
|
+
activeObservationsPendingUpdate: null,
|
|
553
|
+
originType: "reflection",
|
|
554
|
+
config: currentRecord.config,
|
|
555
|
+
generationCount: currentRecord.generationCount + 1,
|
|
556
|
+
lastObservedAt: currentRecord.lastObservedAt,
|
|
557
|
+
lastReflectionAt: now,
|
|
558
|
+
pendingMessageTokens: 0,
|
|
559
|
+
totalTokensObserved: currentRecord.totalTokensObserved,
|
|
560
|
+
observationTokenCount: tokenCount,
|
|
561
|
+
isObserving: false,
|
|
562
|
+
isReflecting: false,
|
|
563
|
+
isBufferingObservation: false,
|
|
564
|
+
isBufferingReflection: false,
|
|
565
|
+
lastBufferedAtTokens: 0,
|
|
566
|
+
lastBufferedAtTime: null,
|
|
567
|
+
observedTimezone: currentRecord.observedTimezone,
|
|
568
|
+
metadata: currentRecord.metadata,
|
|
569
|
+
createdAt: now,
|
|
570
|
+
updatedAt: now
|
|
571
|
+
};
|
|
572
|
+
await ctx.db.insert(convexTable, newRecord);
|
|
573
|
+
await ctx.db.patch(doc._id, {
|
|
574
|
+
bufferedReflection: null,
|
|
575
|
+
bufferedReflectionTokens: null,
|
|
576
|
+
bufferedReflectionInputTokens: null,
|
|
577
|
+
reflectedObservationLineCount: null,
|
|
578
|
+
updatedAt: now
|
|
579
|
+
});
|
|
580
|
+
return { ok: true, result: newRecord };
|
|
581
|
+
}
|
|
582
|
+
case "omUpdateConfig": {
|
|
583
|
+
const doc = requireRecord(await findRecordById(ctx, convexTable, request.id), request.id);
|
|
584
|
+
const existing = parseJsonObject(doc.config);
|
|
585
|
+
const incoming = parseJsonObject(request.config);
|
|
586
|
+
const merged = deepMergeOMConfig(existing, incoming);
|
|
587
|
+
await ctx.db.patch(doc._id, {
|
|
588
|
+
config: JSON.stringify(merged),
|
|
589
|
+
updatedAt: request.updatedAt
|
|
590
|
+
});
|
|
591
|
+
return { ok: true };
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
|
|
299
596
|
// src/server/workflow-snapshot.ts
|
|
300
597
|
var PENDING_MARKER_KEY = "__mastra_pending__";
|
|
301
598
|
function isPendingMarker(val) {
|
|
@@ -375,6 +672,7 @@ var VECTOR_TABLE_PREFIX = "mastra_vector_";
|
|
|
375
672
|
var CONVEX_TABLE_WORKFLOW_SNAPSHOTS = "mastra_workflow_snapshots";
|
|
376
673
|
var CONVEX_TABLE_BACKGROUND_TASKS = "mastra_background_tasks";
|
|
377
674
|
var CONVEX_TABLE_DOCUMENTS = "mastra_documents";
|
|
675
|
+
var CONVEX_TABLE_OBSERVATIONAL_MEMORY = "mastra_observational_memory";
|
|
378
676
|
var STORAGE_MUTATION_BATCH_SIZE = 25;
|
|
379
677
|
var LOAD_MANY_MAX_IDS_PER_REQUEST = 10;
|
|
380
678
|
var DEFAULT_SCHEDULE_QUERY_LIMIT = 100;
|
|
@@ -527,6 +825,8 @@ function resolveTable(tableName) {
|
|
|
527
825
|
return { convexTable: CONVEX_TABLE_BACKGROUND_TASKS, isTyped: true };
|
|
528
826
|
case TABLE_VECTOR_INDEXES:
|
|
529
827
|
return { convexTable: "mastra_vector_indexes", isTyped: true };
|
|
828
|
+
case CONVEX_TABLE_OBSERVATIONAL_MEMORY:
|
|
829
|
+
return { convexTable: CONVEX_TABLE_OBSERVATIONAL_MEMORY, isTyped: true };
|
|
530
830
|
default:
|
|
531
831
|
if (tableName.startsWith(VECTOR_TABLE_PREFIX)) {
|
|
532
832
|
return { convexTable: "mastra_vectors", isTyped: true };
|
|
@@ -587,6 +887,19 @@ function mergeMetadata(existing, update) {
|
|
|
587
887
|
}
|
|
588
888
|
async function handleTypedOperation(ctx, convexTable, request) {
|
|
589
889
|
switch (request.op) {
|
|
890
|
+
case "omGetLatest":
|
|
891
|
+
case "omGetHistory":
|
|
892
|
+
case "omUpdateActive":
|
|
893
|
+
case "omAppendBufferedChunk":
|
|
894
|
+
case "omSwapBuffered":
|
|
895
|
+
case "omUpdateBufferedReflection":
|
|
896
|
+
case "omSwapBufferedReflection":
|
|
897
|
+
case "omUpdateConfig": {
|
|
898
|
+
if (convexTable !== CONVEX_TABLE_OBSERVATIONAL_MEMORY) {
|
|
899
|
+
throw new Error(`${request.op} is only supported for ${CONVEX_TABLE_OBSERVATIONAL_MEMORY}`);
|
|
900
|
+
}
|
|
901
|
+
return handleObservationalMemoryOperation(ctx, convexTable, request);
|
|
902
|
+
}
|
|
590
903
|
case "createSchedule": {
|
|
591
904
|
if (convexTable !== "mastra_schedules") {
|
|
592
905
|
throw new Error(`createSchedule is only supported for mastra_schedules`);
|
|
@@ -1459,5 +1772,5 @@ exports.mastraNativeVectorAction = mastraNativeVectorAction;
|
|
|
1459
1772
|
exports.mastraNativeVectorMutation = mastraNativeVectorMutation;
|
|
1460
1773
|
exports.mastraNativeVectorQuery = mastraNativeVectorQuery;
|
|
1461
1774
|
exports.mastraStorage = mastraStorage;
|
|
1462
|
-
//# sourceMappingURL=chunk-
|
|
1463
|
-
//# sourceMappingURL=chunk-
|
|
1775
|
+
//# sourceMappingURL=chunk-PKCUPSKO.cjs.map
|
|
1776
|
+
//# sourceMappingURL=chunk-PKCUPSKO.cjs.map
|