@mastra/memory 1.10.1-alpha.0 → 1.10.1-alpha.1

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.
Files changed (73) hide show
  1. package/CHANGELOG.md +92 -0
  2. package/dist/{chunk-K2NLYL2O.js → chunk-2QSOQQPM.js} +6817 -6615
  3. package/dist/chunk-2QSOQQPM.js.map +1 -0
  4. package/dist/chunk-D4J4XPGM.cjs +111 -0
  5. package/dist/chunk-D4J4XPGM.cjs.map +1 -0
  6. package/dist/chunk-LSJJAJAF.js +105 -0
  7. package/dist/chunk-LSJJAJAF.js.map +1 -0
  8. package/dist/{chunk-HSOEY7M2.cjs → chunk-NS47X3OB.cjs} +6807 -6608
  9. package/dist/chunk-NS47X3OB.cjs.map +1 -0
  10. package/dist/constants-BDOITAO3.js +3 -0
  11. package/dist/constants-BDOITAO3.js.map +1 -0
  12. package/dist/constants-HXOCZPB7.cjs +28 -0
  13. package/dist/constants-HXOCZPB7.cjs.map +1 -0
  14. package/dist/docs/SKILL.md +1 -1
  15. package/dist/docs/assets/SOURCE_MAP.json +67 -61
  16. package/dist/docs/references/docs-memory-observational-memory.md +7 -5
  17. package/dist/docs/references/reference-memory-observational-memory.md +2 -2
  18. package/dist/index.cjs +207 -78
  19. package/dist/index.cjs.map +1 -1
  20. package/dist/index.d.ts +52 -5
  21. package/dist/index.d.ts.map +1 -1
  22. package/dist/index.js +200 -71
  23. package/dist/index.js.map +1 -1
  24. package/dist/{observational-memory-DJMF2UNC.cjs → observational-memory-I5UTOG63.cjs} +46 -41
  25. package/dist/{observational-memory-DJMF2UNC.cjs.map → observational-memory-I5UTOG63.cjs.map} +1 -1
  26. package/dist/observational-memory-WMCWT577.js +4 -0
  27. package/dist/{observational-memory-NMQ7URIP.js.map → observational-memory-WMCWT577.js.map} +1 -1
  28. package/dist/processors/index.cjs +44 -39
  29. package/dist/processors/index.js +2 -1
  30. package/dist/processors/observational-memory/buffering-coordinator.d.ts +61 -0
  31. package/dist/processors/observational-memory/buffering-coordinator.d.ts.map +1 -0
  32. package/dist/processors/observational-memory/constants.d.ts +62 -0
  33. package/dist/processors/observational-memory/constants.d.ts.map +1 -0
  34. package/dist/processors/observational-memory/debug.d.ts +3 -0
  35. package/dist/processors/observational-memory/debug.d.ts.map +1 -0
  36. package/dist/processors/observational-memory/index.d.ts +5 -2
  37. package/dist/processors/observational-memory/index.d.ts.map +1 -1
  38. package/dist/processors/observational-memory/message-utils.d.ts +69 -0
  39. package/dist/processors/observational-memory/message-utils.d.ts.map +1 -0
  40. package/dist/processors/observational-memory/observation-strategies/async-buffer.d.ts +33 -0
  41. package/dist/processors/observational-memory/observation-strategies/async-buffer.d.ts.map +1 -0
  42. package/dist/processors/observational-memory/observation-strategies/base.d.ts +102 -0
  43. package/dist/processors/observational-memory/observation-strategies/base.d.ts.map +1 -0
  44. package/dist/processors/observational-memory/observation-strategies/index.d.ts +7 -0
  45. package/dist/processors/observational-memory/observation-strategies/index.d.ts.map +1 -0
  46. package/dist/processors/observational-memory/observation-strategies/resource-scoped.d.ts +39 -0
  47. package/dist/processors/observational-memory/observation-strategies/resource-scoped.d.ts.map +1 -0
  48. package/dist/processors/observational-memory/observation-strategies/sync.d.ts +35 -0
  49. package/dist/processors/observational-memory/observation-strategies/sync.d.ts.map +1 -0
  50. package/dist/processors/observational-memory/observation-strategies/types.d.ts +55 -0
  51. package/dist/processors/observational-memory/observation-strategies/types.d.ts.map +1 -0
  52. package/dist/processors/observational-memory/observation-turn/index.d.ts +4 -0
  53. package/dist/processors/observational-memory/observation-turn/index.d.ts.map +1 -0
  54. package/dist/processors/observational-memory/observation-turn/step.d.ts +34 -0
  55. package/dist/processors/observational-memory/observation-turn/step.d.ts.map +1 -0
  56. package/dist/processors/observational-memory/observation-turn/turn.d.ts +84 -0
  57. package/dist/processors/observational-memory/observation-turn/turn.d.ts.map +1 -0
  58. package/dist/processors/observational-memory/observation-turn/types.d.ts +38 -0
  59. package/dist/processors/observational-memory/observation-turn/types.d.ts.map +1 -0
  60. package/dist/processors/observational-memory/observational-memory.d.ts +388 -553
  61. package/dist/processors/observational-memory/observational-memory.d.ts.map +1 -1
  62. package/dist/processors/observational-memory/observer-runner.d.ts +65 -0
  63. package/dist/processors/observational-memory/observer-runner.d.ts.map +1 -0
  64. package/dist/processors/observational-memory/processor.d.ts +70 -0
  65. package/dist/processors/observational-memory/processor.d.ts.map +1 -0
  66. package/dist/processors/observational-memory/reflector-runner.d.ts +95 -0
  67. package/dist/processors/observational-memory/reflector-runner.d.ts.map +1 -0
  68. package/dist/processors/observational-memory/types.d.ts +157 -0
  69. package/dist/processors/observational-memory/types.d.ts.map +1 -1
  70. package/package.json +3 -3
  71. package/dist/chunk-HSOEY7M2.cjs.map +0 -1
  72. package/dist/chunk-K2NLYL2O.js.map +0 -1
  73. package/dist/observational-memory-NMQ7URIP.js +0 -3
@@ -1,217 +1,25 @@
1
1
  import type { MastraDBMessage, MessageList } from '@mastra/core/agent';
2
- import type { Processor, ProcessInputStepArgs, ProcessOutputResultArgs } from '@mastra/core/processors';
2
+ import type { ProcessorStreamWriter } from '@mastra/core/processors';
3
+ import { MessageHistory } from '@mastra/core/processors';
3
4
  import type { RequestContext } from '@mastra/core/request-context';
4
5
  import type { MemoryStorage, ObservationalMemoryRecord } from '@mastra/core/storage';
5
- import { TokenCounter } from './token-counter.js';
6
- import type { ObservationConfig, ReflectionConfig, ThresholdRange, ModelSettings, ProviderOptions, ObservationalMemoryModel } from './types.js';
7
- /**
8
- * Debug event emitted when observation-related events occur.
9
- * Useful for understanding what the Observer is doing.
10
- */
11
- export interface ObservationDebugEvent {
12
- type: 'observation_triggered' | 'observation_complete' | 'reflection_triggered' | 'reflection_complete' | 'tokens_accumulated' | 'step_progress';
13
- timestamp: Date;
14
- threadId: string;
15
- resourceId: string;
16
- /** Messages that were sent to the Observer */
17
- messages?: Array<{
18
- role: string;
19
- content: string;
20
- }>;
21
- /** Token counts */
22
- pendingTokens?: number;
23
- sessionTokens?: number;
24
- totalPendingTokens?: number;
25
- threshold?: number;
26
- /** Input token count (for reflection events) */
27
- inputTokens?: number;
28
- /** Number of active observations (for reflection events) */
29
- activeObservationsLength?: number;
30
- /** Output token count after reflection */
31
- outputTokens?: number;
32
- /** The observations that were generated */
33
- observations?: string;
34
- /** Previous observations (before this event) */
35
- previousObservations?: string;
36
- /** Observer's raw output */
37
- rawObserverOutput?: string;
38
- /** LLM usage from Observer/Reflector calls */
39
- usage?: {
40
- inputTokens?: number;
41
- outputTokens?: number;
42
- totalTokens?: number;
43
- };
44
- /** Step progress fields (for step_progress events) */
45
- stepNumber?: number;
46
- finishReason?: string;
47
- thresholdPercent?: number;
48
- willSave?: boolean;
49
- willObserve?: boolean;
50
- }
51
- /**
52
- * Configuration for ObservationalMemory
53
- */
54
- export interface ObservationalMemoryConfig {
55
- /**
56
- * Storage adapter for persisting observations.
57
- * Must be a MemoryStorage instance (from MastraStorage.stores.memory).
58
- */
59
- storage: MemoryStorage;
60
- /**
61
- * **Experimental.** Enable retrieval-mode observation group metadata.
62
- * When true, observation groups are treated as durable pointers to raw
63
- * message history and a `recall` tool is registered so the actor can
64
- * inspect raw messages behind a stored observation summary.
65
- *
66
- * @experimental
67
- * @default false
68
- */
69
- retrieval?: boolean;
70
- /**
71
- * Model for both Observer and Reflector agents.
72
- * Sets the model for both agents at once. Cannot be used together with
73
- * `observation.model` or `reflection.model` — an error will be thrown.
74
- *
75
- * @default 'google/gemini-2.5-flash'
76
- */
77
- model?: ObservationalMemoryModel;
78
- /**
79
- * Observation step configuration.
80
- */
81
- observation?: ObservationConfig;
82
- /**
83
- * Reflection step configuration.
84
- */
85
- reflection?: ReflectionConfig;
86
- /**
87
- * Memory scope for observations.
88
- * - 'resource': Observations span all threads for a resource (cross-thread memory)
89
- * - 'thread': Observations are per-thread (default)
90
- */
91
- scope?: 'resource' | 'thread';
92
- /**
93
- * Debug callback for observation events.
94
- * Called whenever observation-related events occur.
95
- * Useful for debugging and understanding the observation flow.
96
- */
97
- onDebugEvent?: (event: ObservationDebugEvent) => void;
98
- obscureThreadIds?: boolean;
99
- /**
100
- * Share the token budget between messages and observations.
101
- * When true, the total budget = observation.messageTokens + reflection.observationTokens.
102
- * - Messages can use more space when observations are small
103
- * - Observations can use more space when messages are small
104
- *
105
- * This helps maximize context usage by allowing flexible allocation.
106
- *
107
- * @default false
108
- */
109
- shareTokenBudget?: boolean;
110
- }
6
+ import { BufferingCoordinator } from './buffering-coordinator.js';
111
7
  /**
112
- * Internal resolved config with all defaults applied.
113
- * Thresholds are stored as ThresholdRange internally for dynamic calculation,
114
- * even when user provides a simple number (converted based on shareTokenBudget).
8
+ * Returns the parts from the latest step of a message (after the last step-start marker).
9
+ * If no step-start marker exists, returns all parts.
115
10
  */
116
- interface ResolvedObservationConfig {
117
- model: ObservationalMemoryModel;
118
- /** Internal threshold - always stored as ThresholdRange for dynamic calculation */
119
- messageTokens: number | ThresholdRange;
120
- /** Whether shared token budget is enabled */
121
- shareTokenBudget: boolean;
122
- /** Model settings - merged with user config and defaults */
123
- modelSettings: ModelSettings;
124
- providerOptions: ProviderOptions;
125
- maxTokensPerBatch: number;
126
- /** Token interval for async background observation buffering (resolved from config) */
127
- bufferTokens?: number;
128
- /** Ratio of buffered observations to activate (0-1 float) */
129
- bufferActivation?: number;
130
- /** Token threshold above which synchronous observation is forced */
131
- blockAfter?: number;
132
- /** Optional token budget for observer context optimization (0 = full truncation, false = disabled) */
133
- previousObserverTokens?: number | false;
134
- /** Custom instructions to append to the Observer's system prompt */
135
- instruction?: string;
136
- /** Whether the Observer should suggest thread titles */
137
- threadTitle?: boolean;
138
- }
139
- interface ResolvedReflectionConfig {
140
- model: ObservationalMemoryModel;
141
- /** Internal threshold - always stored as ThresholdRange for dynamic calculation */
142
- observationTokens: number | ThresholdRange;
143
- /** Whether shared token budget is enabled */
144
- shareTokenBudget: boolean;
145
- /** Model settings - merged with user config and defaults */
146
- modelSettings: ModelSettings;
147
- providerOptions: ProviderOptions;
148
- /** Ratio (0-1) controlling when async reflection buffering starts */
149
- bufferActivation?: number;
150
- /** Token threshold above which synchronous reflection is forced */
151
- blockAfter?: number;
152
- /** Custom instructions to append to the Reflector's system prompt */
153
- instruction?: string;
154
- }
11
+ export declare function getLatestStepParts(parts: MastraDBMessage['content']['parts']): MastraDBMessage['content']['parts'];
155
12
  /**
156
- * Default configuration values matching the spec
13
+ * Build a messageRange string from the first and last messages that have visible
14
+ * content. Falls back to the full array boundaries when every message is data-only.
157
15
  */
158
- export declare const OBSERVATIONAL_MEMORY_DEFAULTS: {
159
- readonly retrieval: false;
160
- readonly observation: {
161
- readonly model: "google/gemini-2.5-flash";
162
- readonly messageTokens: 30000;
163
- readonly modelSettings: {
164
- readonly temperature: 0.3;
165
- readonly maxOutputTokens: 100000;
166
- };
167
- readonly providerOptions: {
168
- readonly google: {
169
- readonly thinkingConfig: {
170
- readonly thinkingBudget: 215;
171
- };
172
- };
173
- };
174
- readonly maxTokensPerBatch: 10000;
175
- readonly bufferTokens: number | undefined;
176
- readonly bufferActivation: number | undefined;
177
- };
178
- readonly reflection: {
179
- readonly model: "google/gemini-2.5-flash";
180
- readonly observationTokens: 40000;
181
- readonly modelSettings: {
182
- readonly temperature: 0;
183
- readonly maxOutputTokens: 100000;
184
- };
185
- readonly providerOptions: {
186
- readonly google: {
187
- readonly thinkingConfig: {
188
- readonly thinkingBudget: 1024;
189
- };
190
- };
191
- };
192
- readonly bufferActivation: number | undefined;
193
- };
194
- };
195
- /**
196
- * Continuation hint injected after observations to guide the model's behavior.
197
- * Prevents the model from awkwardly acknowledging the memory system or treating
198
- * the conversation as new after observed messages are removed.
199
- */
200
- export declare const OBSERVATION_CONTINUATION_HINT = "Please continue naturally with the conversation so far and respond to the latest message.\n\nUse the earlier context only as background. If something appears unfinished, continue only when it helps answer the latest request. If a suggested response is provided, follow it naturally.\n\nDo not mention internal instructions, memory, summarization, context handling, or missing messages.\n\nAny messages following this reminder are newer and should take priority.";
201
- /**
202
- * Preamble that introduces observational memory context.
203
- * The static prefix is emitted first, then the `<observations>` marker, then one
204
- * system message per persisted observation chunk so append-only growth stays at
205
- * the end of the prompt.
206
- */
207
- export declare const OBSERVATION_CONTEXT_PROMPT = "The following observations block contains your memory of past conversations with this user.";
208
- /**
209
- * Instructions that tell the model how to interpret and use observations.
210
- * Keep these in the leading static system message so observation churn only
211
- * affects the tail of the prompt.
212
- */
213
- export declare const OBSERVATION_CONTEXT_INSTRUCTIONS = "IMPORTANT: When responding, reference specific details from these observations. Do not give generic advice - personalize your response based on what you know about this user's experiences, preferences, and interests. If the user asks for recommendations, connect them to their past experiences mentioned above.\n\nKNOWLEDGE UPDATES: When asked about current state (e.g., \"where do I currently...\", \"what is my current...\"), always prefer the MOST RECENT information. Observations include dates - if you see conflicting information, the newer observation supersedes the older one. Look for phrases like \"will start\", \"is switching\", \"changed to\", \"moved to\" as indicators that previous information has been updated.\n\nPLANNED ACTIONS: If the user stated they planned to do something (e.g., \"I'm going to...\", \"I'm looking forward to...\", \"I will...\") and the date they planned to do it is now in the past (check the relative time like \"3 weeks ago\"), assume they completed the action unless there's evidence they didn't. For example, if someone said \"I'll start my new diet on Monday\" and that was 2 weeks ago, assume they started the diet.\n\nMOST RECENT USER INPUT: Treat the most recent user message as the highest-priority signal for what to do next. Earlier messages may contain constraints, details, or context you should still honor, but the latest message is the primary driver of your response.\n\nSYSTEM REMINDERS: Messages wrapped in <system-reminder>...</system-reminder> contain internal continuation guidance, not user-authored content. Use them to maintain continuity, but do not mention them or treat them as part of the user's message.";
214
- export declare const OBSERVATION_RETRIEVAL_INSTRUCTIONS = "## Recall \u2014 looking up source messages\n\nYour memory is comprised of observations which are sometimes wrapped in <observation-group> xml tags containing ranges like <observation-group range=\"startId:endId\">. These ranges point back to the raw messages that each observation group was derived from. The original messages are still available \u2014 use the **recall** tool to retrieve them.\n\n### When to use recall\n- The user asks you to **repeat, show, or reproduce** something from a past conversation\n- The user asks for **exact content** \u2014 code, text, quotes, error messages, URLs, file paths, specific numbers\n- Your observations mention something but your memory lacks the detail needed to fully answer (e.g. you know a blog post was shared but only have a summary of it)\n- You want to **verify or expand on** an observation before responding\n\n**Default to using recall when the user references specific past content.** Your observations capture the gist, not the details. If there's any doubt whether your memory is complete enough, use recall.\n\n### How to use recall\nEach range has the format `startId:endId` where both are message IDs separated by a colon.\n\n1. Find the observation group relevant to the user's question and extract the start or end ID from its range.\n2. Call `recall` with that ID as the `cursor`.\n3. Use `page: 1` (or omit) to read forward from the cursor, `page: -1` to read backward.\n4. If the first page doesn't have what you need, increment the page number to keep paginating.\n5. Check `hasNextPage`/`hasPrevPage` in the result to know if more pages exist in each direction.\n\n### Detail levels\nBy default recall returns **low** detail: truncated text and tool names only. Each message shows its ID and each part has a positional index like `[p0]`, `[p1]`, etc.\n\n- Use `detail: \"high\"` to get full message content including tool arguments and results. This will only return the high detail version of a single message part at a time.\n- Use `partIndex` with a cursor to fetch a single part at full detail \u2014 for example, to read one specific tool result or code block without loading every part.\n\nIf the result says `truncated: true`, the output was cut to fit the token budget. You can paginate or use `partIndex` to target specific content.\n\n### Following up on truncated parts\nLow-detail results may include truncation hints like:\n`[truncated \u2014 call recall cursor=\"...\" partIndex=N detail=\"high\" for full content]`\n\n**When you see these hints and need the full content, make the exact call described in the hint.** This is the normal workflow: first recall at low detail to scan, then drill into specific parts at high detail. Do not stop at the low-detail result if the user asked for exact content.\n\n### When recall is NOT needed\n- The user is asking for a high-level summary and your observations already cover it\n- The question is about general preferences or facts that don't require source text\n- There is no relevant range in your observations for the topic\n\nObservation groups with range IDs and your recall tool allows you to think back and remember details you're fuzzy on.";
16
+ export declare function buildMessageRange(messages: MastraDBMessage[]): string;
17
+ import { ObservationTurn } from './observation-turn/index.js';
18
+ import { ObserverRunner } from './observer-runner.js';
19
+ import type { CompressionLevel } from './reflector-agent.js';
20
+ import { ReflectorRunner } from './reflector-runner.js';
21
+ import { TokenCounter } from './token-counter.js';
22
+ import type { ObservationDebugEvent, ObservationalMemoryConfig, ObserveHooks, ResolvedObservationConfig, ResolvedReflectionConfig, ThresholdRange } from './types.js';
215
23
  /**
216
24
  * ObservationalMemory - A three-agent memory system for long conversations.
217
25
  *
@@ -251,37 +59,30 @@ export declare const OBSERVATION_RETRIEVAL_INSTRUCTIONS = "## Recall \u2014 look
251
59
  * });
252
60
  * ```
253
61
  */
254
- export interface ObserveHooks {
255
- onObservationStart?: () => void;
256
- onObservationEnd?: () => void;
257
- onReflectionStart?: () => void;
258
- onReflectionEnd?: () => void;
259
- }
260
- export declare class ObservationalMemory implements Processor<'observational-memory'> {
261
- readonly id: "observational-memory";
262
- readonly name = "Observational Memory";
62
+ export declare class ObservationalMemory {
263
63
  private storage;
264
64
  private tokenCounter;
265
- private scope;
266
- private retrieval;
65
+ readonly scope: 'resource' | 'thread';
66
+ /** Whether retrieval-mode observation groups are enabled (thread scope only). */
67
+ readonly retrieval: boolean;
267
68
  private observationConfig;
268
69
  private reflectionConfig;
269
70
  private onDebugEvent?;
270
- /** Internal Observer agent - created lazily */
271
- private observerAgent?;
272
- private observerAgentModel?;
273
- /** Internal Reflector agent - created lazily */
274
- private reflectorAgent?;
275
- private reflectorAgentModel?;
71
+ /** Observer agent runner handles LLM calls for extracting observations. */
72
+ readonly observer: ObserverRunner;
73
+ /** Reflector agent runner — handles LLM calls for compressing observations. */
74
+ readonly reflector: ReflectorRunner;
75
+ /** Buffering state coordinator — manages static maps and buffering lifecycle. */
76
+ readonly buffering: BufferingCoordinator;
276
77
  private shouldObscureThreadIds;
277
78
  private hasher;
278
- private threadIdCache;
279
79
  /**
280
80
  * Track message IDs observed during this instance's lifetime.
281
81
  * Prevents re-observing messages when per-thread lastObservedAt cursors
282
82
  * haven't fully advanced past messages observed in a prior cycle.
83
+ * @internal Used by observation strategies. Do not call directly.
283
84
  */
284
- private observedMessageIds;
85
+ observedMessageIds: Set<string>;
285
86
  /** Internal MessageHistory for message persistence */
286
87
  private messageHistory;
287
88
  /**
@@ -297,116 +98,11 @@ export declare class ObservationalMemory implements Processor<'observational-mem
297
98
  * accept eventual consistency (acceptable for v1).
298
99
  */
299
100
  private locks;
300
- /**
301
- * Track in-flight async buffering operations per resource/thread.
302
- * STATIC: Shared across all ObservationalMemory instances in this process.
303
- * This is critical because multiple OM instances are created per agent loop step,
304
- * and we need them to share knowledge of in-flight operations.
305
- * Key format: "obs:{lockKey}" or "refl:{lockKey}"
306
- * Value: Promise that resolves when buffering completes
307
- */
308
- private static asyncBufferingOps;
309
- /**
310
- * Track the last token boundary at which we started buffering.
311
- * STATIC: Shared across all instances so boundary tracking persists across OM recreations.
312
- * Key format: "obs:{lockKey}" or "refl:{lockKey}"
313
- */
314
- private static lastBufferedBoundary;
315
- /**
316
- * Track the timestamp cursor for buffered messages.
317
- * STATIC: Shared across all instances so each buffer only observes messages
318
- * newer than the previous buffer's boundary.
319
- * Key format: "obs:{lockKey}"
320
- */
321
- private static lastBufferedAtTime;
322
- /**
323
- * Tracks cycleId for in-flight buffered reflections.
324
- * STATIC: Shared across instances so we can match cycleId at activation time.
325
- * Key format: "refl:{lockKey}"
326
- */
327
- private static reflectionBufferCycleIds;
328
- /**
329
- * Track message IDs that have been sealed during async buffering.
330
- * STATIC: Shared across all instances so saveMessagesWithSealedIdTracking
331
- * generates new IDs when re-saving messages that were sealed in a previous step.
332
- * Key format: threadId
333
- * Value: Set of sealed message IDs
334
- */
335
- private static sealedMessageIds;
336
- /**
337
- * Check if async buffering is enabled for observations.
338
- */
339
- private isAsyncObservationEnabled;
340
- /**
341
- * Check if async buffering is enabled for reflections.
342
- * Reflection buffering is enabled when bufferActivation is set (triggers at threshold * bufferActivation).
343
- */
344
- private isAsyncReflectionEnabled;
345
- /**
346
- * Get the buffer interval boundary key for observations.
347
- */
348
- private getObservationBufferKey;
349
- /**
350
- * Get the buffer interval boundary key for reflections.
351
- */
352
- private getReflectionBufferKey;
353
- /**
354
- * Clean up static maps for a thread/resource to prevent memory leaks.
355
- * Called after activation (to remove activated message IDs from sealedMessageIds)
356
- * and from clear() (to fully remove all static state for a thread).
357
- */
358
- private cleanupStaticMaps;
359
- /**
360
- * Await any in-flight async buffering operations for a given thread/resource.
361
- * Returns once all buffering promises have settled (or after timeout).
362
- */
363
- static awaitBuffering(threadId: string | null | undefined, resourceId: string | null | undefined, scope: 'thread' | 'resource', timeoutMs?: number): Promise<void>;
364
- /**
365
- * Safely get bufferedObservationChunks as an array.
366
- * Handles cases where it might be a JSON string or undefined.
367
- */
368
- private getBufferedChunks;
369
- /**
370
- * Refresh per-chunk messageTokens from the current in-memory message list.
371
- *
372
- * Buffered chunks store a messageTokens snapshot from when they were created,
373
- * but messages can be edited/sealed between buffering and activation, changing
374
- * their token weight. Using stale weights causes projected-removal math to
375
- * over- or under-estimate, leading to skipped activations or over-activation.
376
- *
377
- * Token recount only runs when the full chunk is present in the message list.
378
- * Partial recount is skipped because it would undercount and could cause
379
- * over-activation of buffered chunks.
380
- */
381
- private refreshBufferedChunkMessageTokens;
382
- /**
383
- * Check if we've crossed a new bufferTokens interval boundary.
384
- * Returns true if async buffering should be triggered.
385
- *
386
- * When pending tokens are within ~1 bufferTokens of the observation threshold,
387
- * the buffer interval is halved to produce finer-grained chunks right before
388
- * activation. This improves chunk boundary selection, reducing overshoot.
389
- */
390
- private shouldTriggerAsyncObservation;
391
- /**
392
- * Check if async reflection buffering should be triggered.
393
- * Triggers once when observation tokens reach `threshold * bufferActivation`.
394
- * Only allows one buffered reflection at a time.
395
- */
396
- private shouldTriggerAsyncReflection;
397
- /**
398
- * Check if an async buffering operation is already in progress.
399
- */
400
- private isAsyncBufferingInProgress;
401
101
  /**
402
102
  * Acquire a lock for the given key, execute the callback, then release.
403
103
  * If a lock is already held, waits for it to be released before acquiring.
404
104
  */
405
105
  private withLock;
406
- /**
407
- * Get the lock key for the current scope
408
- */
409
- private getLockKey;
410
106
  constructor(config: ObservationalMemoryConfig);
411
107
  /**
412
108
  * Get the current configuration for this OM instance.
@@ -436,40 +132,28 @@ export declare class ObservationalMemory implements Processor<'observational-mem
436
132
  * Get the default compression start level based on model behavior.
437
133
  * gemini-2.5-flash is a faithful transcriber that needs explicit pressure to compress effectively.
438
134
  */
439
- private getCompressionStartLevel;
440
- private getRuntimeModelContext;
441
- private runWithTokenCounterModelContext;
442
- private formatRoutingModel;
443
- private withOmTracingSpan;
135
+ getCompressionStartLevel(requestContext?: RequestContext): Promise<CompressionLevel>;
444
136
  /**
445
137
  * Get the full config including resolved model names.
446
138
  * This is async because it needs to resolve the model configs.
447
139
  */
448
140
  getResolvedConfig(requestContext?: RequestContext): Promise<{
449
141
  scope: 'resource' | 'thread';
450
- shareTokenBudget: boolean;
451
142
  observation: {
452
143
  messageTokens: number | ThresholdRange;
453
144
  model: string;
454
145
  previousObserverTokens: number | false | undefined;
455
- routing?: Array<{
456
- upTo: number;
457
- model: string;
458
- }>;
459
146
  };
460
147
  reflection: {
461
148
  observationTokens: number | ThresholdRange;
462
149
  model: string;
463
- routing?: Array<{
464
- upTo: number;
465
- model: string;
466
- }>;
467
150
  };
468
151
  }>;
469
152
  /**
470
- * Emit a debug event if the callback is configured
153
+ * Emit a debug event if the callback is configured.
154
+ * @internal Used by observation strategies. Do not call directly.
471
155
  */
472
- private emitDebugEvent;
156
+ emitDebugEvent(event: ObservationDebugEvent): void;
473
157
  /**
474
158
  * Validate buffer configuration on first use.
475
159
  * Ensures bufferTokens is less than the threshold and bufferActivation is valid.
@@ -479,14 +163,6 @@ export declare class ObservationalMemory implements Processor<'observational-mem
479
163
  * Check whether the unobserved message tokens meet the observation threshold.
480
164
  */
481
165
  private meetsObservationThreshold;
482
- /**
483
- * Get or create the Observer agent
484
- */
485
- private getObserverAgent;
486
- /**
487
- * Get or create the Reflector agent
488
- */
489
- private getReflectorAgent;
490
166
  /**
491
167
  * Get thread/resource IDs for storage lookup
492
168
  */
@@ -496,10 +172,6 @@ export declare class ObservationalMemory implements Processor<'observational-mem
496
172
  * Returns the existing record if one exists, otherwise initializes a new one.
497
173
  */
498
174
  getOrCreateRecord(threadId: string, resourceId?: string): Promise<ObservationalMemoryRecord>;
499
- /**
500
- * Check if we need to trigger reflection.
501
- */
502
- private shouldReflect;
503
175
  /**
504
176
  * Get current config snapshot for observation markers.
505
177
  */
@@ -508,14 +180,22 @@ export declare class ObservationalMemory implements Processor<'observational-mem
508
180
  * Persist a data-om-* marker part on the last assistant message in messageList
509
181
  * AND save the updated message to the DB so it survives page reload.
510
182
  * (data-* parts are filtered out before sending to the LLM, so they don't affect model calls.)
183
+ * @internal Used by ReflectorRunner. Do not call directly.
511
184
  */
512
- private persistMarkerToMessage;
185
+ persistMarkerToMessage(marker: {
186
+ type: string;
187
+ data: unknown;
188
+ }, messageList: MessageList | undefined, threadId: string, resourceId?: string): Promise<void>;
513
189
  /**
514
190
  * Persist a marker to the last assistant message in storage.
515
191
  * Unlike persistMarkerToMessage, this fetches messages directly from the DB
516
192
  * so it works even when no MessageList is available (e.g. async buffering ops).
193
+ * @internal Used by observation strategies. Do not call directly.
517
194
  */
518
- private persistMarkerToStorage;
195
+ persistMarkerToStorage(marker: {
196
+ type: string;
197
+ data: unknown;
198
+ }, threadId: string, resourceId?: string): Promise<void>;
519
199
  /**
520
200
  * Find the last completed observation boundary in a message's parts.
521
201
  * A completed observation is a start marker followed by an end marker.
@@ -523,7 +203,6 @@ export declare class ObservationalMemory implements Processor<'observational-mem
523
203
  * Returns the index of the END marker (which is the observation boundary),
524
204
  * or -1 if no completed observation is found.
525
205
  */
526
- private findLastCompletedObservationBoundary;
527
206
  /**
528
207
  * Check if a message has an in-progress observation (start without end).
529
208
  */
@@ -544,7 +223,8 @@ export declare class ObservationalMemory implements Processor<'observational-mem
544
223
  *
545
224
  * @param messages - Messages to seal (mutated in place)
546
225
  */
547
- private sealMessagesForBuffering;
226
+ /** @internal Used by ObservationStep. */
227
+ sealMessagesForBuffering(messages: MastraDBMessage[]): void;
548
228
  /**
549
229
  * Insert an observation marker into a message.
550
230
  * The marker is appended directly to the message's parts array (mutating in place).
@@ -559,17 +239,6 @@ export declare class ObservationalMemory implements Processor<'observational-mem
559
239
  * For end/failed markers, this should be called AFTER writer.custom() has added the part,
560
240
  * so we just find the part and add sealing metadata.
561
241
  */
562
- /**
563
- * Get unobserved parts from a message.
564
- * If the message has a completed observation (start + end), only return parts after the end.
565
- * If observation is in progress (start without end), include parts before the start.
566
- * Otherwise, return all parts.
567
- */
568
- private getUnobservedParts;
569
- /**
570
- * Check if a message has any unobserved parts.
571
- */
572
- private hasUnobservedParts;
573
242
  /**
574
243
  * Create a virtual message containing only the unobserved parts.
575
244
  * This is used for token counting and observation.
@@ -585,21 +254,20 @@ export declare class ObservationalMemory implements Processor<'observational-mem
585
254
  * This handles the case where a single message accumulates many parts
586
255
  * (like tool calls) during an agentic loop - we only observe the new parts.
587
256
  */
588
- private getUnobservedMessages;
589
- /**
590
- * Wrapper for observer/reflector agent.generate() calls that checks for abort.
591
- * agent.generate() returns an empty result on abort instead of throwing,
592
- * so we must check the signal before and after the call.
593
- * Retries are handled by Mastra's built-in p-retry at the model execution layer.
594
- */
595
- private withAbortCheck;
257
+ /** @internal Used by ObservationStep. */
258
+ getUnobservedMessages(allMessages: MastraDBMessage[], record: ObservationalMemoryRecord, opts?: {
259
+ excludeBuffered?: boolean;
260
+ }): MastraDBMessage[];
596
261
  /**
597
262
  * Prepare optimized observer context by applying truncation and buffered-reflection inclusion.
598
263
  *
599
264
  * Returns the (possibly optimized) observations string to pass as "Previous Observations"
600
265
  * to the observer prompt. When no optimization options are set, returns the input unchanged.
601
266
  */
602
- private prepareObserverContext;
267
+ prepareObserverContext(existingObservations: string | undefined, record?: ObservationalMemoryRecord | null): {
268
+ context: string | undefined;
269
+ wasTruncated: boolean;
270
+ };
603
271
  /**
604
272
  * Truncate observations to fit within a token budget.
605
273
  *
@@ -610,22 +278,6 @@ export declare class ObservationalMemory implements Processor<'observational-mem
610
278
  * 4. Enforce that at least 50% of kept observations remain raw tail observations.
611
279
  */
612
280
  private truncateObservationsToTokenBudget;
613
- /**
614
- * Call the Observer agent to extract observations.
615
- */
616
- private callObserver;
617
- /**
618
- * Call the Observer agent for multiple threads in a single batched request.
619
- * This is more efficient than calling the Observer for each thread individually.
620
- * Returns per-thread results with observations, currentTask, and suggestedContinuation,
621
- * plus the total usage for the batch.
622
- */
623
- private callMultiThreadObserver;
624
- /**
625
- * Call the Reflector agent to condense observations.
626
- * Includes compression validation and retry logic.
627
- */
628
- private callReflector;
629
281
  /**
630
282
  * Format observations for injection into context.
631
283
  * Applies token optimization before presenting to the Actor.
@@ -633,12 +285,6 @@ export declare class ObservationalMemory implements Processor<'observational-mem
633
285
  * In resource scope mode, filters continuity messages to only show
634
286
  * the message for the current thread.
635
287
  */
636
- /**
637
- * Format observations for injection into the Actor's context.
638
- * @param observations - The observations to inject
639
- * @param suggestedResponse - Thread-specific suggested response (from thread metadata)
640
- * @param unobservedContextBlocks - Formatted <unobserved-context> blocks from other threads
641
- */
642
288
  private formatObservationsForContext;
643
289
  private splitObservationContextChunks;
644
290
  /**
@@ -650,77 +296,19 @@ export declare class ObservationalMemory implements Processor<'observational-mem
650
296
  /**
651
297
  * Get threadId and resourceId from either RequestContext or MessageList
652
298
  */
653
- private getThreadContext;
654
- /**
655
- * Load historical unobserved messages into the message list (step 0 only).
656
- * In resource scope, loads only current thread's messages.
657
- * In thread scope, loads all unobserved messages for the thread.
658
- */
659
- private loadHistoricalMessagesIfNeeded;
660
- /**
661
- * Calculate all threshold-related values for observation decision making.
662
- */
663
- private calculateObservationThresholds;
664
- /**
665
- * Emit debug event and stream progress part for UI feedback.
666
- */
667
- private emitStepProgress;
668
- /**
669
- * Handle observation when threshold is reached.
670
- * Tries async activation first if enabled, then falls back to sync observation.
671
- * Returns whether observation succeeded.
672
- */
673
- private handleThresholdReached;
674
- /**
675
- * Remove observed messages from message list after successful observation.
676
- * Accepts optional observedMessageIds for activation-based cleanup (when no markers are present).
677
- */
678
- private cleanupAfterObservation;
679
- /**
680
- * Handle per-step save when threshold is not reached.
681
- * Persists messages incrementally to prevent data loss on interruption.
682
- */
683
- private handlePerStepSave;
684
- /**
685
- * Inject observations as system message and add continuation reminder.
686
- */
687
- private injectObservationsIntoContext;
688
- /**
689
- * Filter out already-observed messages from the in-memory context.
690
- *
691
- * Marker-boundary pruning is safest at step 0 (historical resume/rebuild), where
692
- * list ordering mirrors persisted history.
693
- * For step > 0, the list may include mid-loop mutations (sealing/splitting/trim),
694
- * so we prefer record-based fallback pruning over position-based marker pruning.
695
- */
696
- private filterAlreadyObservedMessages;
697
- /**
698
- * Process input at each step - check threshold, observe if needed, save, inject observations.
699
- * This is the ONLY processor method - all OM logic happens here.
700
- *
701
- * Flow:
702
- * 1. Load historical messages (step 0 only)
703
- * 2. Check if observation threshold is reached
704
- * 3. If threshold reached: observe, save messages with markers
705
- * 4. Inject observations into context
706
- * 5. Filter out already-observed messages
707
- */
708
- processInputStep(args: ProcessInputStepArgs): Promise<MessageList | MastraDBMessage[]>;
709
- /**
710
- * Save any unsaved messages at the end of the agent turn.
711
- *
712
- * This is the "final save" that catches messages that processInputStep didn't save
713
- * (e.g., when the observation threshold was never reached, or on single-step execution).
714
- * Without this, messages would be lost because MessageHistory is disabled when OM is active.
715
- */
716
- processOutputResult(args: ProcessOutputResultArgs): Promise<MessageList | MastraDBMessage[]>;
299
+ getThreadContext(requestContext: RequestContext | undefined, messageList: MessageList): {
300
+ threadId: string;
301
+ resourceId?: string;
302
+ } | null;
717
303
  /**
718
- * Save messages to storage while preventing duplicate inserts for sealed messages.
304
+ * Save messages to storage, skipping messages that were already persisted by
305
+ * async buffering. Uses the message-level sealed flag (metadata.mastra.sealed)
306
+ * to detect already-persisted messages, avoiding redundant DB operations.
719
307
  *
720
- * Sealed messages that do not yet contain a completed observation boundary are
721
- * skipped because async buffering already persisted them.
308
+ * Messages with observation markers are always saved (upserted) even if sealed,
309
+ * because the markers need to be persisted to storage.
722
310
  */
723
- private saveMessagesWithSealedIdTracking;
311
+ persistMessages(messagesToSave: MastraDBMessage[], threadId: string, resourceId: string | undefined): Promise<void>;
724
312
  /**
725
313
  * Load messages from storage that haven't been observed yet.
726
314
  * Uses cursor-based query with lastObservedAt timestamp for efficiency.
@@ -729,12 +317,6 @@ export declare class ObservationalMemory implements Processor<'observational-mem
729
317
  * In thread scope mode, loads messages for just the current thread.
730
318
  */
731
319
  private loadUnobservedMessages;
732
- /**
733
- * Load unobserved messages from other threads (not the current thread) for a resource.
734
- * Called fresh each step so it reflects the latest lastObservedAt cursors
735
- * after observations complete.
736
- */
737
- private loadOtherThreadsContext;
738
320
  /**
739
321
  * Format unobserved messages from other threads as <unobserved-context> blocks.
740
322
  * These are injected into the Actor's context so it has awareness of activity
@@ -742,32 +324,18 @@ export declare class ObservationalMemory implements Processor<'observational-mem
742
324
  */
743
325
  private formatUnobservedContextBlocks;
744
326
  private representThreadIDInContext;
745
- /**
746
- * Strip any thread tags that the Observer might have added.
747
- * Thread attribution is handled externally by the system, not by the Observer.
748
- * This is a defense-in-depth measure.
749
- */
750
- private stripThreadTags;
751
327
  /**
752
328
  * Get the maximum createdAt timestamp from a list of messages.
753
329
  * Used to set lastObservedAt to the most recent message timestamp instead of current time.
754
330
  * This ensures historical data (like LongMemEval fixtures) works correctly.
755
331
  */
756
332
  private getMaxMessageTimestamp;
757
- /**
758
- * Compute a cursor pointing at the latest message by createdAt.
759
- * Used to derive a stable observation boundary for replay pruning.
760
- */
761
- private getLastObservedMessageCursor;
762
- /**
763
- * Check if a message is at or before a cursor (by createdAt then id).
764
- */
765
- private isMessageAtOrBeforeCursor;
766
333
  /**
767
334
  * Wrap observations in a thread attribution tag.
768
335
  * Used in resource scope to track which thread observations came from.
336
+ * @internal Used by observation strategies. Do not call directly.
769
337
  */
770
- private wrapWithThreadTag;
338
+ wrapWithThreadTag(threadId: string, observations: string, messageRange?: string): Promise<string>;
771
339
  /**
772
340
  * Append or merge new thread sections.
773
341
  * If the new section has the same thread ID and date as an existing section,
@@ -776,15 +344,9 @@ export declare class ObservationalMemory implements Processor<'observational-mem
776
344
  */
777
345
  private replaceOrAppendThreadSection;
778
346
  /**
779
- * Sort threads by their oldest unobserved message.
780
- * Returns thread IDs in order from oldest to most recent.
781
- * This ensures no thread's messages get "stuck" unobserved.
782
- */
783
- private sortThreadsByOldestMessage;
784
- /**
785
- * Do synchronous observation (fallback when no buffering)
347
+ * @internal Used by observation strategies. Do not call directly.
786
348
  */
787
- private doSynchronousObservation;
349
+ wrapObservations(rawObservations: string, existingObservations: string, threadId: string, lastObservedAt?: Date, messageRange?: string): Promise<string> | string;
788
350
  /**
789
351
  * Start an async background observation that stores results to bufferedObservations.
790
352
  * This is a fire-and-forget operation that runs in the background.
@@ -806,81 +368,318 @@ export declare class ObservationalMemory implements Processor<'observational-mem
806
368
  */
807
369
  private runAsyncBufferedObservation;
808
370
  /**
809
- * Perform async buffered observation - observes messages and stores to bufferedObservations.
810
- * Does NOT update activeObservations or trigger reflection.
371
+ * Trigger async buffered observation if the token count has crossed a new interval.
811
372
  *
812
- * The observer sees: active observations + existing buffered observations + message history
813
- * (excluding already-buffered messages).
373
+ * Encapsulates the shouldTrigger check + startAsyncBufferedObservation call.
374
+ * Returns whether buffering was actually triggered.
814
375
  */
815
- private doAsyncBufferedObservation;
376
+ triggerAsyncBuffering(opts: {
377
+ threadId: string;
378
+ resourceId?: string;
379
+ record: ObservationalMemoryRecord;
380
+ pendingTokens: number;
381
+ unbufferedPendingTokens: number;
382
+ unobservedMessages: MastraDBMessage[];
383
+ threshold: number;
384
+ writer?: ProcessorStreamWriter;
385
+ requestContext?: RequestContext;
386
+ }): Promise<boolean>;
387
+ private isMessageList;
388
+ private removeIdsFromArray;
816
389
  /**
817
- * Combine active and buffered observations for the buffering observer context.
818
- * The buffering observer needs to see both so it doesn't duplicate content.
390
+ * Mutate partially observed messages in place and return the fully observed
391
+ * message IDs that should be removed from the live context.
392
+ *
393
+ * This is the shared activation-cleanup primitive used by both the processor
394
+ * and AI SDK integrations: callers pass the current live messages, OM trims
395
+ * any partially observed messages down to their unobserved parts, and OM
396
+ * returns only the IDs that are safe to remove entirely.
819
397
  */
820
- private combineObservationsForBuffering;
398
+ getObservedMessageIdsForCleanup(opts: {
399
+ threadId: string;
400
+ resourceId?: string;
401
+ messages: MastraDBMessage[];
402
+ observedMessageIds?: string[];
403
+ retentionFloor?: number;
404
+ }): Promise<string[]>;
821
405
  /**
822
- * Try to activate buffered observations when threshold is reached.
823
- * Returns true if activation succeeded, false if no buffered content or activation failed.
406
+ * Clean up observed content from either a live MessageList or a plain message array.
824
407
  *
825
- * @param record - Current OM record
826
- * @param lockKey - Lock key for this scope
827
- * @param writer - Optional writer for emitting UI markers
408
+ * - MessageList input: mutates the live container in place and returns the remaining messages
409
+ * - Array input: mutates the array in place and returns it
410
+ *
411
+ * This is the shared cleanup primitive intended for both processor and non-processor
412
+ * integrations. The processor may still pass sealedIds/state so marker/fallback cleanup
413
+ * can persist messages safely, but callers that do not need that bookkeeping can omit it.
828
414
  */
829
- private tryActivateBufferedObservations;
415
+ /** @internal Used by ObservationStep. */
416
+ cleanupMessages(opts: {
417
+ threadId: string;
418
+ resourceId?: string;
419
+ messages: MessageList | MastraDBMessage[];
420
+ observedMessageIds?: string[];
421
+ retentionFloor?: number;
422
+ }): Promise<MastraDBMessage[]>;
830
423
  /**
831
- * Start an async background reflection that stores results to bufferedReflection.
832
- * This is a fire-and-forget operation that runs in the background.
833
- * The results will be swapped to active when the main reflection threshold is reached.
424
+ * Clean up the message context after a successful observation.
834
425
  *
835
- * @param record - Current OM record
836
- * @param observationTokens - Current observation token count
837
- * @param lockKey - Lock key for this scope
426
+ * Handles both activation-based cleanup (using observedMessageIds) and
427
+ * marker-based cleanup (using observation boundary markers). Respects
428
+ * retention floors to prevent removing too many messages.
429
+ */
430
+ cleanupObservedContext(opts: {
431
+ messageList: MessageList;
432
+ threadId: string;
433
+ resourceId?: string;
434
+ observedMessageIds?: string[];
435
+ retentionFloor?: number;
436
+ }): Promise<void>;
437
+ /**
438
+ * Reset buffering state after a successful observation activation.
439
+ *
440
+ * Clears the lastBufferedBoundary, buffering flag, and optionally cleans up
441
+ * static maps for activated message IDs.
838
442
  */
839
- private startAsyncBufferedReflection;
443
+ /** @internal Used by ObservationStep. */
444
+ resetBufferingState(opts: {
445
+ threadId: string;
446
+ resourceId?: string;
447
+ recordId: string;
448
+ activatedMessageIds?: string[];
449
+ }): Promise<void>;
840
450
  /**
841
- * Perform async buffered reflection - reflects observations and stores to bufferedReflection.
842
- * Does NOT create a new generation or update activeObservations.
451
+ * Build the observation system message string for injection into an LLM prompt.
452
+ *
453
+ * Loads thread metadata (currentTask, suggestedResponse), formats observations
454
+ * with context prompts and instructions, and returns the fully-formed string.
455
+ * Returns undefined if no observations exist.
456
+ *
457
+ * This is the public entry point for context formatting — used by both
458
+ * Memory.getContext() (standalone) and the processor (via injectObservationsIntoMessages).
459
+ *
460
+ * @example
461
+ * ```ts
462
+ * const systemMsg = await om.buildContextSystemMessage({ threadId: 'thread-1' });
463
+ * if (systemMsg) {
464
+ * const result = await generateText({ system: systemMsg, messages });
465
+ * }
466
+ * ```
843
467
  */
844
- private doAsyncBufferedReflection;
468
+ buildContextSystemMessage(opts: {
469
+ threadId: string;
470
+ resourceId?: string;
471
+ record?: ObservationalMemoryRecord;
472
+ unobservedContextBlocks?: string;
473
+ currentDate?: Date;
474
+ }): Promise<string | undefined>;
845
475
  /**
846
- * Try to activate buffered reflection when threshold is reached.
847
- * Returns true if activation succeeded, false if no buffered content or activation failed.
476
+ * Build observation context as an array of system message chunks.
477
+ * Each chunk is a separate system message for better LLM cache hit rates.
478
+ * Used by the processor to inject multiple system messages.
479
+ * @internal
480
+ */
481
+ buildContextSystemMessages(opts: {
482
+ threadId: string;
483
+ resourceId?: string;
484
+ record?: ObservationalMemoryRecord;
485
+ unobservedContextBlocks?: string;
486
+ currentDate?: Date;
487
+ }): Promise<string[] | undefined>;
488
+ /**
489
+ * Get unobserved messages from other threads for resource-scoped observation.
848
490
  *
849
- * @param record - Current OM record
850
- * @param lockKey - Lock key for this scope
491
+ * Lists all threads for the resource, filters to unobserved messages,
492
+ * and formats them as context blocks.
493
+ */
494
+ /** @internal Used by ObservationTurn. */
495
+ getOtherThreadsContext(resourceId: string, currentThreadId: string): Promise<string | undefined>;
496
+ /**
497
+ * Emit debug event and stream progress for UI feedback.
498
+ */
499
+ emitProgress(opts: {
500
+ record: ObservationalMemoryRecord;
501
+ pendingTokens: number;
502
+ threshold: number;
503
+ effectiveObservationTokensThreshold: number;
504
+ currentObservationTokens: number;
505
+ writer?: ProcessorStreamWriter;
506
+ stepNumber: number;
507
+ threadId: string;
508
+ resourceId?: string;
509
+ }): Promise<void>;
510
+ /**
511
+ * Get the current observation status for a thread/resource.
512
+ *
513
+ * Loads unobserved messages from storage, counts tokens, and checks against
514
+ * configured thresholds. Returns a comprehensive status object that tells the
515
+ * caller what actions are needed.
516
+ *
517
+ * This is a pure read operation with no side effects.
518
+ *
519
+ * @example
520
+ * ```ts
521
+ * const status = await om.getStatus({ threadId });
522
+ * if (status.shouldObserve) {
523
+ * await om.observe({ threadId });
524
+ * } else if (status.shouldBuffer) {
525
+ * await om.buffer({ threadId });
526
+ * }
527
+ * if (status.shouldReflect) {
528
+ * await om.reflect(threadId);
529
+ * }
530
+ * ```
851
531
  */
852
- private tryActivateBufferedReflection;
532
+ getStatus(opts: {
533
+ threadId: string;
534
+ resourceId?: string;
535
+ messages?: MastraDBMessage[];
536
+ }): Promise<{
537
+ record: ObservationalMemoryRecord;
538
+ pendingTokens: number;
539
+ threshold: number;
540
+ effectiveObservationTokensThreshold: number;
541
+ unbufferedPendingTokens: number;
542
+ shouldObserve: boolean;
543
+ shouldBuffer: boolean;
544
+ shouldReflect: boolean;
545
+ bufferedChunkCount: number;
546
+ bufferedChunkTokens: number;
547
+ canActivate: boolean;
548
+ asyncObservationEnabled: boolean;
549
+ asyncReflectionEnabled: boolean;
550
+ scope: 'resource' | 'thread';
551
+ }>;
853
552
  /**
854
- * Resource-scoped observation: observe ALL threads with unobserved messages.
855
- * Threads are observed in oldest-first order to ensure no thread's messages
856
- * get "stuck" unobserved forever.
553
+ * Finalize the observation lifecycle: activate any remaining buffered chunks,
554
+ * then observe if the threshold is crossed.
857
555
  *
858
- * Key differences from thread-scoped observation:
859
- * 1. Loads messages from ALL threads for the resource
860
- * 2. Observes threads one-by-one in oldest-first order
861
- * 3. Only updates lastObservedAt AFTER all threads are observed
862
- * 4. Only triggers reflection AFTER all threads are observed
556
+ * Call this at the end of a conversation, session, or turn sequence to ensure
557
+ * no buffered observations are left orphaned and the observation cursor is
558
+ * advanced. Produces a clean terminal state (no pending chunks, cursor up to date).
559
+ *
560
+ * @example
561
+ * ```ts
562
+ * // After all turns are complete
563
+ * const result = await om.finalize({ threadId });
564
+ * // result.activated: true if buffered chunks were promoted
565
+ * // result.observed: true if a full observation pass ran
566
+ * ```
863
567
  */
864
- private doResourceScopedObservation;
568
+ finalize(opts: {
569
+ threadId: string;
570
+ resourceId?: string;
571
+ messages?: MastraDBMessage[];
572
+ }): Promise<{
573
+ activated: boolean;
574
+ observed: boolean;
575
+ reflected: boolean;
576
+ record: ObservationalMemoryRecord;
577
+ }>;
865
578
  /**
866
- * Check if async reflection should be triggered or activated.
867
- * Only handles the async path — will never do synchronous (blocking) reflection.
868
- * Safe to call after buffered observation activation.
579
+ * Return only the messages that haven't been fully observed yet.
580
+ *
581
+ * Use this to prune observed messages from an in-memory message array,
582
+ * preventing unbounded context growth across steps in a multi-step loop.
583
+ * This is the array-based equivalent of the processor's `cleanupObservedContext()`.
584
+ *
585
+ * @example
586
+ * ```ts
587
+ * // In a prepareStep hook, prune before sending to the model
588
+ * messages = await om.pruneObserved({ threadId, messages });
589
+ * ```
869
590
  */
870
- private maybeAsyncReflect;
591
+ pruneObserved(opts: {
592
+ threadId: string;
593
+ resourceId?: string;
594
+ messages: MastraDBMessage[];
595
+ }): Promise<MastraDBMessage[]>;
596
+ /**
597
+ * Create a buffered observation chunk without merging into active observations.
598
+ *
599
+ * Loads unobserved messages from storage (filtered by the buffer cursor to avoid
600
+ * re-buffering), calls the observer LLM, and stores the result as a pending
601
+ * buffered chunk in the DB. The chunk can later be merged into active observations
602
+ * via `activate()`.
603
+ *
604
+ * This is a synchronous (awaited) operation — the caller decides whether to
605
+ * `await` it or fire-and-forget. All state lives in storage; no in-process
606
+ * coordination is needed.
607
+ *
608
+ * @example
609
+ * ```ts
610
+ * const status = await om.getStatus({ threadId });
611
+ * if (status.shouldBuffer) {
612
+ * await om.buffer({ threadId });
613
+ * }
614
+ * ```
615
+ */
616
+ /** @internal Used by ObservationStep. */
617
+ buffer(opts: {
618
+ threadId: string;
619
+ resourceId?: string;
620
+ messages?: MastraDBMessage[];
621
+ /** The freshly-counted pending token count from the caller. If not provided,
622
+ * falls back to record.pendingMessageTokens (which may be stale). */
623
+ pendingTokens?: number;
624
+ /** Pre-loaded record to skip the initial getOrCreateRecord() fetch.
625
+ * When called fire-and-forget, passing the record avoids an async gap
626
+ * before lastBufferedBoundary is set. */
627
+ record?: ObservationalMemoryRecord;
628
+ writer?: ProcessorStreamWriter;
629
+ requestContext?: RequestContext;
630
+ /** Called with the final candidate messages after cursor filtering, before the observer runs.
631
+ * Use this to seal messages in a live MessageList and persist them to storage. */
632
+ beforeBuffer?: (candidates: MastraDBMessage[]) => Promise<void>;
633
+ }): Promise<{
634
+ buffered: boolean;
635
+ record: ObservationalMemoryRecord;
636
+ }>;
871
637
  /**
872
- * Check if reflection needed and trigger if so.
873
- * Supports both synchronous reflection and async buffered reflection.
874
- * When async buffering is enabled via `bufferTokens`, reflection is triggered
875
- * in the background at intervals, and activated when the threshold is reached.
638
+ * Activate buffered observation chunks by merging them into active observations.
639
+ *
640
+ * This is a pure storage operation no LLM call. It reads buffered chunks from
641
+ * the DB and swaps them into active observations via `storage.swapBufferedToActive()`.
642
+ *
643
+ * Call this after `buffer()` has created chunks, typically at the start of a new
644
+ * turn or when `getStatus().canActivate` is true.
645
+ *
646
+ * @example
647
+ * ```ts
648
+ * const status = await om.getStatus({ threadId });
649
+ * if (status.canActivate) {
650
+ * const result = await om.activate({ threadId });
651
+ * if (result.activated) {
652
+ * console.log('Activated', result.activatedMessageIds?.length, 'message observations');
653
+ * }
654
+ * }
655
+ * ```
876
656
  */
877
- private maybeReflect;
657
+ /** @internal Used by ObservationStep. */
658
+ activate(opts: {
659
+ threadId: string;
660
+ resourceId?: string;
661
+ /** When true, skip activation if pending tokens are below the observation threshold. */
662
+ checkThreshold?: boolean;
663
+ /** Messages to use for threshold check (in-memory). If omitted, loads from storage. */
664
+ messages?: MastraDBMessage[];
665
+ /** Stream writer for emitting activation markers to the UI. */
666
+ writer?: ProcessorStreamWriter;
667
+ /** MessageList for persisting activation markers on the last assistant message. */
668
+ messageList?: MessageList;
669
+ }): Promise<{
670
+ activated: boolean;
671
+ record: ObservationalMemoryRecord;
672
+ activatedMessageIds?: string[];
673
+ }>;
878
674
  /**
879
675
  * Manually trigger observation.
880
676
  *
881
677
  * When `messages` is provided, those are used directly (filtered for unobserved)
882
678
  * instead of reading from storage. This allows external systems (e.g., opencode)
883
679
  * to pass conversation messages without duplicating them into Mastra's DB.
680
+ *
681
+ * Returns a result indicating whether observation and/or reflection occurred,
682
+ * along with the updated record.
884
683
  */
885
684
  observe(opts: {
886
685
  threadId: string;
@@ -888,7 +687,12 @@ export declare class ObservationalMemory implements Processor<'observational-mem
888
687
  messages?: MastraDBMessage[];
889
688
  hooks?: ObserveHooks;
890
689
  requestContext?: RequestContext;
891
- }): Promise<void>;
690
+ writer?: ProcessorStreamWriter;
691
+ }): Promise<{
692
+ observed: boolean;
693
+ reflected: boolean;
694
+ record: ObservationalMemoryRecord;
695
+ }>;
892
696
  /**
893
697
  * Manually trigger reflection with optional guidance prompt.
894
698
  *
@@ -900,7 +704,10 @@ export declare class ObservationalMemory implements Processor<'observational-mem
900
704
  * );
901
705
  * ```
902
706
  */
903
- reflect(threadId: string, resourceId?: string, prompt?: string, requestContext?: RequestContext): Promise<void>;
707
+ reflect(threadId: string, resourceId?: string, prompt?: string, requestContext?: RequestContext): Promise<{
708
+ reflected: boolean;
709
+ record: ObservationalMemoryRecord;
710
+ }>;
904
711
  /**
905
712
  * Get current observations for a thread/resource
906
713
  */
@@ -933,6 +740,34 @@ export declare class ObservationalMemory implements Processor<'observational-mem
933
740
  * Get current reflection configuration
934
741
  */
935
742
  getReflectionConfig(): ResolvedReflectionConfig;
743
+ /**
744
+ * Get the message history instance for marker persistence.
745
+ */
746
+ getMessageHistory(): MessageHistory;
747
+ /**
748
+ * Get whether thread IDs should be obscured in observations.
749
+ */
750
+ getObscureThreadIds(): boolean;
751
+ /**
752
+ * Begin a new observation turn — the high-level API for managing the
753
+ * observe/buffer/activate/reflect lifecycle across agentic loop steps.
754
+ *
755
+ * @example
756
+ * ```ts
757
+ * const turn = om.beginTurn({ threadId, resourceId, messageList });
758
+ * await turn.start(memory);
759
+ *
760
+ * const step0 = turn.step(0);
761
+ * const ctx = await step0.prepare();
762
+ * // ... agent generates ...
763
+ *
764
+ * await turn.end();
765
+ * ```
766
+ */
767
+ beginTurn(opts: {
768
+ threadId: string;
769
+ resourceId?: string;
770
+ messageList: MessageList;
771
+ }): ObservationTurn;
936
772
  }
937
- export {};
938
773
  //# sourceMappingURL=observational-memory.d.ts.map