@mastra/oracledb 0.4.1 → 0.4.2-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/dist/docs/SKILL.md +1 -1
- package/dist/docs/assets/SOURCE_MAP.json +1 -1
- package/dist/docs/references/docs-memory-observational-memory.md +61 -14
- package/dist/docs/references/reference-storage-retention.md +1 -1
- package/dist/index.cjs +11 -11
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +12 -12
- package/dist/index.js.map +1 -1
- package/dist/storage/domains/memory/index.d.ts.map +1 -1
- package/dist/storage/domains/memory/observational-buffering.d.ts.map +1 -1
- package/dist/storage/domains/memory/observational.d.ts.map +1 -1
- package/dist/storage/domains/memory/utils.d.ts.map +1 -1
- package/dist/storage/domains/observability/logs.d.ts.map +1 -1
- package/dist/storage/domains/observability/scores-bridge.d.ts.map +1 -1
- package/dist/storage/domains/scores/index.d.ts.map +1 -1
- package/dist/vector/upsert.d.ts.map +1 -1
- package/package.json +4 -4
package/dist/docs/SKILL.md
CHANGED
|
@@ -128,7 +128,7 @@ export const agent = new Agent({
|
|
|
128
128
|
|
|
129
129
|
Mastra inserts a temporal gap marker when the gap is at least 10 minutes. The marker is stored in memory and also emitted as a transient reminder event, so clients can render it as a lightweight timeline hint.
|
|
130
130
|
|
|
131
|
-
The observer also sees these markers when it processes the thread
|
|
131
|
+
The observer also sees these markers when it processes the thread. The observations it writes can anchor memories to when they happened (for example, "User asked about deployment after a 2-day gap").
|
|
132
132
|
|
|
133
133
|
See [the API reference](https://mastra.ai/reference/memory/observational-memory) for the full configuration shape.
|
|
134
134
|
|
|
@@ -191,13 +191,47 @@ const memory = new Memory({
|
|
|
191
191
|
|
|
192
192
|
`bufferOnIdle` is off by default. It's separate from `bufferTokens`: `bufferTokens` controls step-time async buffering, while `bufferOnIdle` controls end-of-turn buffering for idle turns.
|
|
193
193
|
|
|
194
|
+
### Retries and failure policy
|
|
195
|
+
|
|
196
|
+
Observer and Reflector model calls are retried on transient provider errors, and a turn aborts if they still fail. Each stage is controlled independently by:
|
|
197
|
+
|
|
198
|
+
- `maxRetries`: retries after the initial model call (default `8`).
|
|
199
|
+
- `failurePolicy`: what happens once retries are exhausted, either `'abort'` (default) or `'continue'`.
|
|
200
|
+
|
|
201
|
+
```typescript
|
|
202
|
+
const memory = new Memory({
|
|
203
|
+
options: {
|
|
204
|
+
observationalMemory: {
|
|
205
|
+
observation: {
|
|
206
|
+
maxRetries: 8,
|
|
207
|
+
failurePolicy: 'continue',
|
|
208
|
+
},
|
|
209
|
+
reflection: {
|
|
210
|
+
maxRetries: 8,
|
|
211
|
+
failurePolicy: 'continue',
|
|
212
|
+
},
|
|
213
|
+
},
|
|
214
|
+
},
|
|
215
|
+
})
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
`maxRetries` governs OM's own retry ladder; the underlying model call is configured with no provider-level retries, so the option is the single retry knob for the stage.
|
|
219
|
+
|
|
220
|
+
With `failurePolicy: 'continue'`, Mastra retries as configured, reports the failure through the existing OM diagnostics, keeps the failed input pending for a later cycle, and lets the main agent turn continue. It doesn't advance observation boundaries or discard unobserved messages. A Reflector failure under `'continue'` leaves any already-persisted observations committed and defers reflection to the next threshold crossing.
|
|
221
|
+
|
|
222
|
+
A provider outage usually takes out both stages, so set the policy on both if you want the turn to survive one.
|
|
223
|
+
|
|
224
|
+
This policy applies only to Observer and Reflector model and provider failures in synchronous, resource-scoped, and buffered observation. Persistence, indexing, transform, locking, invariant, and explicit abort failures remain fatal. It doesn't prevent the underlying provider or network error, change `blockAfter`, or change attachment handling.
|
|
225
|
+
|
|
226
|
+
> **Warning:** `'continue'` has no backstop for a sustained outage. Unobserved messages stay pending and keep accruing in the main agent's context. A long outage moves the failure from the memory layer to the model's context limit.
|
|
227
|
+
|
|
194
228
|
See [the API reference](https://mastra.ai/reference/memory/observational-memory) for the full configuration shape.
|
|
195
229
|
|
|
196
230
|
## Benefits
|
|
197
231
|
|
|
198
232
|
- **Prompt caching**: OM's context is stable and observations append over time rather than being retrieved at runtime each turn. This keeps the prompt prefix cacheable, which reduces costs.
|
|
199
233
|
- **Compression**: Raw message history and tool results get compressed into a dense observation log. Smaller context means faster responses and longer coherent conversations.
|
|
200
|
-
- **Zero context rot**: The agent sees relevant information instead of noisy tool calls and irrelevant tokens,
|
|
234
|
+
- **Zero context rot**: The agent sees relevant information instead of noisy tool calls and irrelevant tokens, which keeps the agent on task over long sessions.
|
|
201
235
|
|
|
202
236
|
## How it works
|
|
203
237
|
|
|
@@ -615,29 +649,42 @@ const memory = new Memory({
|
|
|
615
649
|
|
|
616
650
|
Thread scope requires a valid `threadId` to be provided when calling the agent. If `threadId` is missing, Observational Memory throws an error. This prevents multiple threads from silently sharing a single observation record, which can cause database deadlocks.
|
|
617
651
|
|
|
618
|
-
### Resource scope (
|
|
652
|
+
### Resource scope (deprecated)
|
|
653
|
+
|
|
654
|
+
> **Deprecated:** `scope: 'resource'` is deprecated and will be removed in a future release. Mastra logs a warning the first time Observational Memory is created with resource scope. Remove the `scope` option to use the default thread scope, and use the [alternatives below](#cross-thread-continuity-without-resource-scope) for cross-conversation continuity.
|
|
655
|
+
|
|
656
|
+
In resource scope, observations are shared across all threads for a resource (typically a user). Resource scope works much worse than thread scope for prompt caching and for the agent's understanding of the conversation:
|
|
657
|
+
|
|
658
|
+
- **Prompt caching:** Observations from every thread share one record. When any thread is observed, the observation context changes for all other threads, which invalidates their cached prompt prefix.
|
|
659
|
+
- **Agent understanding:** Each thread sees a mix of observations from every thread for the resource. Every new thread starts with context from earlier threads, and the agent may continue work that another thread started but didn't finish.
|
|
660
|
+
- **Performance:** Unobserved messages across _all_ threads are processed together, which is slow for users with many existing threads.
|
|
661
|
+
|
|
662
|
+
A new knowledge and subconscious memory primitive for cross-thread memory is coming soon and will replace resource scope. Until then, use the alternatives below.
|
|
663
|
+
|
|
664
|
+
#### Cross-thread continuity without resource scope
|
|
665
|
+
|
|
666
|
+
Switching from resource scope to thread scope doesn't migrate resource-scoped observations. Each thread uses its existing thread-scoped record if it has one, or starts a new, empty one, and messages already observed under resource scope aren't observed again. Keep facts that must carry over in resource-scoped working memory.
|
|
619
667
|
|
|
620
|
-
|
|
668
|
+
Use thread scope with one or both of these features instead:
|
|
669
|
+
|
|
670
|
+
- [Retrieval mode](#retrieval-mode): The agent gets a `recall` tool that can list and browse other threads for the same resource, and optionally search them semantically. The agent looks up earlier conversations only when it needs them.
|
|
671
|
+
- [Resource-scoped working memory](https://mastra.ai/docs/memory/working-memory): Stores small, durable facts about the user that every thread can read.
|
|
621
672
|
|
|
622
673
|
```typescript
|
|
623
674
|
const memory = new Memory({
|
|
624
675
|
options: {
|
|
625
676
|
observationalMemory: {
|
|
626
677
|
model: 'google/gemini-2.5-flash',
|
|
678
|
+
retrieval: true,
|
|
679
|
+
},
|
|
680
|
+
workingMemory: {
|
|
681
|
+
enabled: true,
|
|
627
682
|
scope: 'resource',
|
|
628
683
|
},
|
|
629
684
|
},
|
|
630
685
|
})
|
|
631
686
|
```
|
|
632
687
|
|
|
633
|
-
Resource scope works, however it's marked as experimental for now until we prove task adherence/continuity across multiple ongoing simultaneous threads. As of today, you may need to tweak your system prompt to prevent one thread from continuing the work that another had already started (but hadn't finished).
|
|
634
|
-
|
|
635
|
-
This is because in resource scope, each thread is a perspective on _all_ threads for the resource.
|
|
636
|
-
|
|
637
|
-
For your use-case this may not be a problem, so your mileage may vary.
|
|
638
|
-
|
|
639
|
-
> **Warning:** In resource scope, unobserved messages across _all_ threads are processed together. For users with many existing threads, this can be slow. Use thread scope for existing apps.
|
|
640
|
-
|
|
641
688
|
## Token budgets
|
|
642
689
|
|
|
643
690
|
OM uses token thresholds to decide when to observe and reflect. See [token budget configuration](https://mastra.ai/reference/memory/observational-memory) for details.
|
|
@@ -786,7 +833,7 @@ const memory = new Memory({
|
|
|
786
833
|
|
|
787
834
|
Setting `bufferTokens: false` disables both observation and reflection async buffering. See [async buffering configuration](https://mastra.ai/reference/memory/observational-memory) for the full API.
|
|
788
835
|
|
|
789
|
-
> **Note:** Resource scope automatically disables async buffering.
|
|
836
|
+
> **Note:** Resource scope (deprecated) automatically disables async buffering.
|
|
790
837
|
|
|
791
838
|
## Observer Context Optimization
|
|
792
839
|
|
|
@@ -889,7 +936,7 @@ hooks: {
|
|
|
889
936
|
No manual migration needed. OM reads existing messages and observes them lazily when thresholds are exceeded.
|
|
890
937
|
|
|
891
938
|
- **Thread scope**: The first time a thread exceeds `observation.messageTokens`, the Observer processes the backlog.
|
|
892
|
-
- **Resource scope**: All unobserved messages across all threads for a resource are processed together. For users with many existing threads, this could take substantial time.
|
|
939
|
+
- **Resource scope (deprecated)**: All unobserved messages across all threads for a resource are processed together. For users with many existing threads, this could take substantial time.
|
|
893
940
|
|
|
894
941
|
## Comparing OM with other memory features
|
|
895
942
|
|
|
@@ -202,7 +202,7 @@ async function retentionTick() {
|
|
|
202
202
|
}
|
|
203
203
|
```
|
|
204
204
|
|
|
205
|
-
You can also cancel a long-running prune with an `AbortSignal`: the loop stops between batches and returns partial results with `done: false
|
|
205
|
+
You can also cancel a long-running prune with an `AbortSignal`: the loop stops between batches and returns partial results with `done: false`. The next run resumes cleanly.
|
|
206
206
|
|
|
207
207
|
## ClickHouse native TTL
|
|
208
208
|
|
package/dist/index.cjs
CHANGED
|
@@ -562,7 +562,7 @@ async function upsert(registry, withConnection, upsertBatchSize, { indexName, ve
|
|
|
562
562
|
if (error instanceof _mastra_core_error.MastraError) throw error;
|
|
563
563
|
throw asMastraError("UPSERT", "INVALID_INPUT", { indexName }, error, _mastra_core_error.ErrorCategory.USER);
|
|
564
564
|
}
|
|
565
|
-
const vectorIds = ids ?? vectors.map(() =>
|
|
565
|
+
const vectorIds = ids ?? vectors.map(() => globalThis.crypto.randomUUID());
|
|
566
566
|
return withConnection(async (connection) => {
|
|
567
567
|
const indexInfo = await registry.getIndexMetadata(connection, indexName);
|
|
568
568
|
try {
|
|
@@ -3464,7 +3464,7 @@ function parseBufferedChunks(value) {
|
|
|
3464
3464
|
return parsed.map((chunk) => {
|
|
3465
3465
|
const item = chunk;
|
|
3466
3466
|
return {
|
|
3467
|
-
id: String(item.id ?? `ombuf-${
|
|
3467
|
+
id: String(item.id ?? `ombuf-${globalThis.crypto.randomUUID()}`),
|
|
3468
3468
|
cycleId: String(item.cycleId ?? ""),
|
|
3469
3469
|
observations: String(item.observations ?? ""),
|
|
3470
3470
|
tokenCount: Math.round(numberOrZero(item.tokenCount)),
|
|
@@ -4348,7 +4348,7 @@ async function getObservationalMemoryHistory(ctx, threadId, resourceId, limit =
|
|
|
4348
4348
|
async function initializeObservationalMemory(ctx, input) {
|
|
4349
4349
|
const now = /* @__PURE__ */ new Date();
|
|
4350
4350
|
const record = {
|
|
4351
|
-
id:
|
|
4351
|
+
id: globalThis.crypto.randomUUID(),
|
|
4352
4352
|
scope: input.scope,
|
|
4353
4353
|
threadId: input.threadId,
|
|
4354
4354
|
resourceId: input.resourceId,
|
|
@@ -4425,7 +4425,7 @@ async function updateActiveObservations(ctx, input) {
|
|
|
4425
4425
|
async function createReflectionGeneration(ctx, input) {
|
|
4426
4426
|
const now = /* @__PURE__ */ new Date();
|
|
4427
4427
|
const record = {
|
|
4428
|
-
id:
|
|
4428
|
+
id: globalThis.crypto.randomUUID(),
|
|
4429
4429
|
scope: input.currentRecord.scope,
|
|
4430
4430
|
threadId: input.currentRecord.threadId,
|
|
4431
4431
|
resourceId: input.currentRecord.resourceId,
|
|
@@ -4749,7 +4749,7 @@ async function updateBufferedObservations(ctx, input) {
|
|
|
4749
4749
|
await ctx.db.tx(async (_client, connection) => {
|
|
4750
4750
|
const existingChunks = parseBufferedChunks((await lockOMRow(ctx, connection, input.id, "UPDATE_BUFFERED_OBSERVATIONS")).bufferedObservationChunks);
|
|
4751
4751
|
const newChunk = {
|
|
4752
|
-
id: `ombuf-${
|
|
4752
|
+
id: `ombuf-${globalThis.crypto.randomUUID()}`,
|
|
4753
4753
|
cycleId: input.chunk.cycleId,
|
|
4754
4754
|
observations: input.chunk.observations,
|
|
4755
4755
|
tokenCount: Math.round(input.chunk.tokenCount),
|
|
@@ -4855,7 +4855,7 @@ async function swapBufferedReflectionToActive(ctx, input) {
|
|
|
4855
4855
|
const now = /* @__PURE__ */ new Date();
|
|
4856
4856
|
const lockedRecord = parseOMRow(row);
|
|
4857
4857
|
const newRecord = {
|
|
4858
|
-
id:
|
|
4858
|
+
id: globalThis.crypto.randomUUID(),
|
|
4859
4859
|
scope: lockedRecord.scope,
|
|
4860
4860
|
threadId: lockedRecord.threadId,
|
|
4861
4861
|
resourceId: lockedRecord.resourceId,
|
|
@@ -5489,7 +5489,7 @@ var MemoryOracle = class MemoryOracle extends _mastra_core_storage.MemoryStorage
|
|
|
5489
5489
|
const sourceThread = await this.getThreadById({ threadId: args.sourceThreadId });
|
|
5490
5490
|
if (!sourceThread) throw storageError$1("CLONE_THREAD", "FAILED", { threadId: args.sourceThreadId }, /* @__PURE__ */ new Error(`Thread ${args.sourceThreadId} not found`), _mastra_core_error.ErrorCategory.USER);
|
|
5491
5491
|
const sourceMessages = await this.messagesForClone(args);
|
|
5492
|
-
const newThreadId = args.newThreadId ??
|
|
5492
|
+
const newThreadId = args.newThreadId ?? globalThis.crypto.randomUUID();
|
|
5493
5493
|
if (await this.getThreadById({ threadId: newThreadId })) throw storageError$1("CLONE_THREAD", "DESTINATION_EXISTS", { threadId: newThreadId }, /* @__PURE__ */ new Error(`Thread ${newThreadId} already exists`), _mastra_core_error.ErrorCategory.USER);
|
|
5494
5494
|
const now = /* @__PURE__ */ new Date();
|
|
5495
5495
|
const cloneMetadata = {
|
|
@@ -5511,7 +5511,7 @@ var MemoryOracle = class MemoryOracle extends _mastra_core_storage.MemoryStorage
|
|
|
5511
5511
|
};
|
|
5512
5512
|
const messageIdMap = {};
|
|
5513
5513
|
const clonedMessages = sourceMessages.map((message) => {
|
|
5514
|
-
const newMessageId =
|
|
5514
|
+
const newMessageId = globalThis.crypto.randomUUID();
|
|
5515
5515
|
messageIdMap[message.id] = newMessageId;
|
|
5516
5516
|
return {
|
|
5517
5517
|
...message,
|
|
@@ -5988,7 +5988,7 @@ async function batchCreateLogs(db, schemaName, args) {
|
|
|
5988
5988
|
try {
|
|
5989
5989
|
const binds = args.logs.map((log) => logRecordBinds({
|
|
5990
5990
|
...log,
|
|
5991
|
-
logId: log.logId ??
|
|
5991
|
+
logId: log.logId ?? globalThis.crypto.randomUUID(),
|
|
5992
5992
|
executionSource: log.executionSource ?? log.source ?? null
|
|
5993
5993
|
}));
|
|
5994
5994
|
await db.tx(async (client) => {
|
|
@@ -6245,7 +6245,7 @@ function transformObservabilityScoreRow(row) {
|
|
|
6245
6245
|
};
|
|
6246
6246
|
}
|
|
6247
6247
|
function scoreRecordToTableRecord(score) {
|
|
6248
|
-
const id = score.scoreId ??
|
|
6248
|
+
const id = score.scoreId ?? globalThis.crypto.randomUUID();
|
|
6249
6249
|
const timestamp = score.timestamp instanceof Date ? score.timestamp : new Date(score.timestamp ?? Date.now());
|
|
6250
6250
|
const source = score.scoreSource ?? score.source ?? "observability";
|
|
6251
6251
|
const contextualMetadata = removeUndefined({
|
|
@@ -7594,7 +7594,7 @@ var ScoresOracle = class ScoresOracle extends _mastra_core_storage.ScoresStorage
|
|
|
7594
7594
|
}, error, _mastra_core_error.ErrorCategory.USER);
|
|
7595
7595
|
}
|
|
7596
7596
|
try {
|
|
7597
|
-
const id =
|
|
7597
|
+
const id = globalThis.crypto.randomUUID();
|
|
7598
7598
|
const now = /* @__PURE__ */ new Date();
|
|
7599
7599
|
await this.db.insert({
|
|
7600
7600
|
tableName: _mastra_core_storage.TABLE_SCORERS,
|