import type { AgentConfig, MastraDBMessage, MessageList } from '@mastra/core/agent';
import type { Processor, ProcessInputStepArgs, ProcessOutputResultArgs } from '@mastra/core/processors';
import type { RequestContext } from '@mastra/core/request-context';
import type { MemoryStorage, ObservationalMemoryRecord } from '@mastra/core/storage';
import { TokenCounter } from './token-counter.js';
import type { ObservationConfig, ReflectionConfig, ThresholdRange, ModelSettings, ProviderOptions } from './types.js';
/**
 * Debug event emitted when observation-related events occur.
 * Useful for understanding what the Observer is doing.
 */
export interface ObservationDebugEvent {
    type: 'observation_triggered' | 'observation_complete' | 'reflection_triggered' | 'reflection_complete' | 'tokens_accumulated' | 'step_progress';
    timestamp: Date;
    threadId: string;
    resourceId: string;
    /** Messages that were sent to the Observer */
    messages?: Array<{
        role: string;
        content: string;
    }>;
    /** Token counts */
    pendingTokens?: number;
    sessionTokens?: number;
    totalPendingTokens?: number;
    threshold?: number;
    /** Input token count (for reflection events) */
    inputTokens?: number;
    /** Number of active observations (for reflection events) */
    activeObservationsLength?: number;
    /** Output token count after reflection */
    outputTokens?: number;
    /** The observations that were generated */
    observations?: string;
    /** Previous observations (before this event) */
    previousObservations?: string;
    /** Observer's raw output */
    rawObserverOutput?: string;
    /** LLM usage from Observer/Reflector calls */
    usage?: {
        inputTokens?: number;
        outputTokens?: number;
        totalTokens?: number;
    };
    /** Step progress fields (for step_progress events) */
    stepNumber?: number;
    finishReason?: string;
    thresholdPercent?: number;
    willSave?: boolean;
    willObserve?: boolean;
}
/**
 * Configuration for ObservationalMemory
 */
export interface ObservationalMemoryConfig {
    /**
     * Storage adapter for persisting observations.
     * Must be a MemoryStorage instance (from MastraStorage.stores.memory).
     */
    storage: MemoryStorage;
    /**
     * Model for both Observer and Reflector agents.
     * Sets the model for both agents at once. Cannot be used together with
     * `observation.model` or `reflection.model` — an error will be thrown.
     *
     * @default 'google/gemini-2.5-flash'
     */
    model?: AgentConfig['model'];
    /**
     * Observation step configuration.
     */
    observation?: ObservationConfig;
    /**
     * Reflection step configuration.
     */
    reflection?: ReflectionConfig;
    /**
     * Memory scope for observations.
     * - 'resource': Observations span all threads for a resource (cross-thread memory)
     * - 'thread': Observations are per-thread (default)
     */
    scope?: 'resource' | 'thread';
    /**
     * Debug callback for observation events.
     * Called whenever observation-related events occur.
     * Useful for debugging and understanding the observation flow.
     */
    onDebugEvent?: (event: ObservationDebugEvent) => void;
    obscureThreadIds?: boolean;
    /**
     * Share the token budget between messages and observations.
     * When true, the total budget = observation.messageTokens + reflection.observationTokens.
     * - Messages can use more space when observations are small
     * - Observations can use more space when messages are small
     *
     * This helps maximize context usage by allowing flexible allocation.
     *
     * @default false
     */
    shareTokenBudget?: boolean;
}
/**
 * Internal resolved config with all defaults applied.
 * Thresholds are stored as ThresholdRange internally for dynamic calculation,
 * even when user provides a simple number (converted based on shareTokenBudget).
 */
interface ResolvedObservationConfig {
    model: AgentConfig['model'];
    /** Internal threshold - always stored as ThresholdRange for dynamic calculation */
    messageTokens: number | ThresholdRange;
    /** Whether shared token budget is enabled */
    shareTokenBudget: boolean;
    /** Model settings - merged with user config and defaults */
    modelSettings: ModelSettings;
    providerOptions: ProviderOptions;
    maxTokensPerBatch: number;
    /** Token interval for async background observation buffering (resolved from config) */
    bufferTokens?: number;
    /** Ratio of buffered observations to activate (0-1 float) */
    bufferActivation?: number;
    /** Token threshold above which synchronous observation is forced */
    blockAfter?: number;
}
interface ResolvedReflectionConfig {
    model: AgentConfig['model'];
    /** Internal threshold - always stored as ThresholdRange for dynamic calculation */
    observationTokens: number | ThresholdRange;
    /** Whether shared token budget is enabled */
    shareTokenBudget: boolean;
    /** Model settings - merged with user config and defaults */
    modelSettings: ModelSettings;
    providerOptions: ProviderOptions;
    /** Ratio (0-1) controlling when async reflection buffering starts */
    bufferActivation?: number;
    /** Token threshold above which synchronous reflection is forced */
    blockAfter?: number;
}
/**
 * Default configuration values matching the spec
 */
export declare const OBSERVATIONAL_MEMORY_DEFAULTS: {
    readonly observation: {
        readonly model: "google/gemini-2.5-flash";
        readonly messageTokens: 30000;
        readonly modelSettings: {
            readonly temperature: 0.3;
            readonly maxOutputTokens: 100000;
        };
        readonly providerOptions: {
            readonly google: {
                readonly thinkingConfig: {
                    readonly thinkingBudget: 215;
                };
            };
        };
        readonly maxTokensPerBatch: 10000;
        readonly bufferTokens: number | undefined;
        readonly bufferActivation: number | undefined;
    };
    readonly reflection: {
        readonly model: "google/gemini-2.5-flash";
        readonly observationTokens: 40000;
        readonly modelSettings: {
            readonly temperature: 0;
            readonly maxOutputTokens: 100000;
        };
        readonly providerOptions: {
            readonly google: {
                readonly thinkingConfig: {
                    readonly thinkingBudget: 1024;
                };
            };
        };
        readonly bufferActivation: number | undefined;
    };
};
/**
 * Continuation hint injected after observations to guide the model's behavior.
 * Prevents the model from awkwardly acknowledging the memory system or treating
 * the conversation as new after observed messages are removed.
 */
export declare const OBSERVATION_CONTINUATION_HINT = "This message is not from the user, the conversation history grew too long and wouldn't fit in context! Thankfully the entire conversation is stored in your memory observations. Please continue from where the observations left off. Do not refer to your \"memory observations\" directly, the user doesn't know about them, they are your memories! Just respond naturally as if you're remembering the conversation (you are!). Do not say \"Hi there!\" or \"based on our previous conversation\" as if the conversation is just starting, this is not a new conversation. This is an ongoing conversation, keep continuity by responding based on your memory. For example do not say \"I understand. I've reviewed my memory observations\", or \"I remember [...]\". Answer naturally following the suggestion from your memory. Note that your memory may contain a suggested first response, which you should follow.\n\nIMPORTANT: this system reminder is NOT from the user. The system placed it here as part of your memory system. This message is part of you remembering your conversation with the user.\n\nNOTE: Any messages following this system reminder are newer than your memories.";
/**
 * Preamble that introduces the observations block.
 * Use before `<observations>`, with instructions after.
 * Full pattern: `${OBSERVATION_CONTEXT_PROMPT}\n\n<observations>\n${obs}\n</observations>\n\n${OBSERVATION_CONTEXT_INSTRUCTIONS}`
 */
export declare const OBSERVATION_CONTEXT_PROMPT = "The following observations block contains your memory of past conversations with this user.";
/**
 * Instructions that tell the model how to interpret and use observations.
 * Place AFTER the `<observations>` block so the model sees the data before the rules.
 */
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.";
/**
 * ObservationalMemory - A three-agent memory system for long conversations.
 *
 * This processor:
 * 1. On input: Injects observations into context, filters out observed messages
 * 2. On output: Tracks new messages, triggers Observer/Reflector when thresholds hit
 *
 * The Actor (main agent) sees:
 * - Observations (compressed history)
 * - Suggested continuation message
 * - Recent unobserved messages
 *
 * @example
 * ```ts
 * import { ObservationalMemory } from '@mastra/memory/processors';
 *
 * // Minimal configuration
 * const om = new ObservationalMemory({ storage });
 *
 * // Full configuration
 * const om = new ObservationalMemory({
 *   storage,
 *   model: 'google/gemini-2.5-flash', // shared model for both agents
 *   shareTokenBudget: true,
 *   observation: {
 *     messageTokens: 30_000,
 *     modelSettings: { temperature: 0.3 },
 *   },
 *   reflection: {
 *     observationTokens: 40_000,
 *   },
 * });
 *
 * const agent = new Agent({
 *   inputProcessors: [om],
 *   outputProcessors: [om],
 * });
 * ```
 */
export interface ObserveHooks {
    onObservationStart?: () => void;
    onObservationEnd?: () => void;
    onReflectionStart?: () => void;
    onReflectionEnd?: () => void;
}
export declare class ObservationalMemory implements Processor<'observational-memory'> {
    readonly id: "observational-memory";
    readonly name = "Observational Memory";
    private storage;
    private tokenCounter;
    private scope;
    private observationConfig;
    private reflectionConfig;
    private onDebugEvent?;
    /** Internal Observer agent - created lazily */
    private observerAgent?;
    /** Internal Reflector agent - created lazily */
    private reflectorAgent?;
    private shouldObscureThreadIds;
    private hasher;
    private threadIdCache;
    /**
     * Track message IDs observed during this instance's lifetime.
     * Prevents re-observing messages when per-thread lastObservedAt cursors
     * haven't fully advanced past messages observed in a prior cycle.
     */
    private observedMessageIds;
    /** Internal MessageHistory for message persistence */
    private messageHistory;
    /**
     * In-memory mutex for serializing observation/reflection cycles per resource/thread.
     * Prevents race conditions where two concurrent cycles could both read isObserving=false
     * before either sets it to true, leading to lost work.
     *
     * Key format: "resource:{resourceId}" or "thread:{threadId}"
     * Value: Promise that resolves when the lock is released
     *
     * NOTE: This mutex only works within a single Node.js process. For distributed
     * deployments, external locking (Redis, database locks) would be needed, or
     * accept eventual consistency (acceptable for v1).
     */
    private locks;
    /**
     * Track in-flight async buffering operations per resource/thread.
     * STATIC: Shared across all ObservationalMemory instances in this process.
     * This is critical because multiple OM instances are created per agent loop step,
     * and we need them to share knowledge of in-flight operations.
     * Key format: "obs:{lockKey}" or "refl:{lockKey}"
     * Value: Promise that resolves when buffering completes
     */
    private static asyncBufferingOps;
    /**
     * Track the last token boundary at which we started buffering.
     * STATIC: Shared across all instances so boundary tracking persists across OM recreations.
     * Key format: "obs:{lockKey}" or "refl:{lockKey}"
     */
    private static lastBufferedBoundary;
    /**
     * Track the timestamp cursor for buffered messages.
     * STATIC: Shared across all instances so each buffer only observes messages
     * newer than the previous buffer's boundary.
     * Key format: "obs:{lockKey}"
     */
    private static lastBufferedAtTime;
    /**
     * Tracks cycleId for in-flight buffered reflections.
     * STATIC: Shared across instances so we can match cycleId at activation time.
     * Key format: "refl:{lockKey}"
     */
    private static reflectionBufferCycleIds;
    /**
     * Track message IDs that have been sealed during async buffering.
     * STATIC: Shared across all instances so saveMessagesWithSealedIdTracking
     * generates new IDs when re-saving messages that were sealed in a previous step.
     * Key format: threadId
     * Value: Set of sealed message IDs
     */
    private static sealedMessageIds;
    /**
     * Check if async buffering is enabled for observations.
     */
    private isAsyncObservationEnabled;
    /**
     * Check if async buffering is enabled for reflections.
     * Reflection buffering is enabled when bufferActivation is set (triggers at threshold * bufferActivation).
     */
    private isAsyncReflectionEnabled;
    /**
     * Get the buffer interval boundary key for observations.
     */
    private getObservationBufferKey;
    /**
     * Get the buffer interval boundary key for reflections.
     */
    private getReflectionBufferKey;
    /**
     * Clean up static maps for a thread/resource to prevent memory leaks.
     * Called after activation (to remove activated message IDs from sealedMessageIds)
     * and from clear() (to fully remove all static state for a thread).
     */
    private cleanupStaticMaps;
    /**
     * Await any in-flight async buffering operations for a given thread/resource.
     * Returns once all buffering promises have settled (or after timeout).
     */
    static awaitBuffering(threadId: string | null | undefined, resourceId: string | null | undefined, scope: 'thread' | 'resource', timeoutMs?: number): Promise<void>;
    /**
     * Safely get bufferedObservationChunks as an array.
     * Handles cases where it might be a JSON string or undefined.
     */
    private getBufferedChunks;
    /**
     * Calculate the projected message tokens that would be removed if activation happened now.
     * This replicates the chunk boundary logic in swapBufferedToActive without actually activating.
     */
    private calculateProjectedMessageRemoval;
    /**
     * Check if we've crossed a new bufferTokens interval boundary.
     * Returns true if async buffering should be triggered.
     */
    private shouldTriggerAsyncObservation;
    /**
     * Check if async reflection buffering should be triggered.
     * Triggers once when observation tokens reach `threshold * bufferActivation`.
     * Only allows one buffered reflection at a time.
     */
    private shouldTriggerAsyncReflection;
    /**
     * Check if an async buffering operation is already in progress.
     */
    private isAsyncBufferingInProgress;
    /**
     * Acquire a lock for the given key, execute the callback, then release.
     * If a lock is already held, waits for it to be released before acquiring.
     */
    private withLock;
    /**
     * Get the lock key for the current scope
     */
    private getLockKey;
    constructor(config: ObservationalMemoryConfig);
    /**
     * Get the current configuration for this OM instance.
     * Used by the server to expose config to the UI when OM is added via processors.
     */
    get config(): {
        scope: 'resource' | 'thread';
        observation: {
            messageTokens: number | ThresholdRange;
        };
        reflection: {
            observationTokens: number | ThresholdRange;
        };
    };
    /**
     * Wait for any in-flight async buffering operations for the given thread/resource.
     * Used by server endpoints to block until buffering completes so the UI can get final state.
     */
    waitForBuffering(threadId: string | null | undefined, resourceId: string | null | undefined, timeoutMs?: number): Promise<void>;
    /**
     * Get the full config including resolved model names.
     * This is async because it needs to resolve the model configs.
     */
    getResolvedConfig(requestContext?: RequestContext): Promise<{
        scope: 'resource' | 'thread';
        observation: {
            messageTokens: number | ThresholdRange;
            model: string;
        };
        reflection: {
            observationTokens: number | ThresholdRange;
            model: string;
        };
    }>;
    /**
     * Emit a debug event if the callback is configured
     */
    private emitDebugEvent;
    /**
     * Validate buffer configuration on first use.
     * Ensures bufferTokens is less than the threshold and bufferActivation is valid.
     */
    private validateBufferConfig;
    /**
     * Resolve bufferTokens: if it's a fraction (0 < value < 1), multiply by messageTokens threshold.
     * Otherwise return the absolute token count.
     */
    private resolveBufferTokens;
    /**
     * Resolve blockAfter config value.
     * Values between 1 and 2 (exclusive) are treated as multipliers of the threshold.
     * e.g. blockAfter: 1.5 with messageTokens: 20_000 → 30_000
     * Values >= 2 are treated as absolute token counts.
     * Defaults to 1.2 (120% of threshold) when async buffering is enabled but blockAfter is omitted.
     */
    private resolveBlockAfter;
    /**
     * Get the maximum value from a threshold (simple number or range)
     */
    private getMaxThreshold;
    /**
     * Calculate dynamic threshold based on observation space.
     * When shareTokenBudget is enabled, the message threshold can expand
     * into unused observation space, up to the total context budget.
     *
     * Total budget = messageTokens + observationTokens
     * Effective threshold = totalBudget - currentObservationTokens
     *
     * Example with 30k:40k thresholds (70k total):
     * - 0 observations → messages can use ~70k
     * - 10k observations → messages can use ~60k
     * - 40k observations → messages back to ~30k
     */
    private calculateDynamicThreshold;
    /**
     * Check whether the unobserved message tokens meet the observation threshold.
     */
    private meetsObservationThreshold;
    /**
     * Get or create the Observer agent
     */
    private getObserverAgent;
    /**
     * Get or create the Reflector agent
     */
    private getReflectorAgent;
    /**
     * Get thread/resource IDs for storage lookup
     */
    private getStorageIds;
    /**
     * Get or create the observational memory record.
     * Returns the existing record if one exists, otherwise initializes a new one.
     */
    getOrCreateRecord(threadId: string, resourceId?: string): Promise<ObservationalMemoryRecord>;
    /**
     * Check if we need to trigger reflection.
     */
    private shouldReflect;
    /**
     * Get current config snapshot for observation markers.
     */
    private getObservationMarkerConfig;
    /**
     * Create a start marker for when observation begins.
     */
    private createObservationStartMarker;
    /**
     * Create an end marker for when observation completes successfully.
     */
    private createObservationEndMarker;
    /**
     * Create a failed marker for when observation fails.
     */
    private createObservationFailedMarker;
    /**
     * Create a start marker for when async buffering begins.
     */
    private createBufferingStartMarker;
    /**
     * Create an end marker for when async buffering completes successfully.
     */
    private createBufferingEndMarker;
    /**
     * Create a failed marker for when async buffering fails.
     */
    private createBufferingFailedMarker;
    /**
     * Create an activation marker for when buffered observations are activated.
     */
    private createActivationMarker;
    /**
     * Persist a data-om-* marker part on the last assistant message in messageList
     * AND save the updated message to the DB so it survives page reload.
     * (data-* parts are filtered out before sending to the LLM, so they don't affect model calls.)
     */
    private persistMarkerToMessage;
    /**
     * Persist a marker to the last assistant message in storage.
     * Unlike persistMarkerToMessage, this fetches messages directly from the DB
     * so it works even when no MessageList is available (e.g. async buffering ops).
     */
    private persistMarkerToStorage;
    /**
     * Find the last completed observation boundary in a message's parts.
     * A completed observation is a start marker followed by an end marker.
     *
     * Returns the index of the END marker (which is the observation boundary),
     * or -1 if no completed observation is found.
     */
    private findLastCompletedObservationBoundary;
    /**
     * Check if a message has an in-progress observation (start without end).
     */
    private hasInProgressObservation;
    /**
     * Seal messages to prevent new parts from being merged into them.
     * This is used when starting buffering to capture the current content state.
     *
     * Sealing works by:
     * 1. Setting `message.content.metadata.mastra.sealed = true` (message-level flag)
     * 2. Adding `metadata.mastra.sealedAt` to the last part (boundary marker)
     *
     * When MessageList.add() receives a message with the same ID as a sealed message,
     * it creates a new message with only the parts beyond the seal boundary.
     *
     * The messages are mutated in place - since they're references to the same objects
     * in the MessageList, the seal will be recognized immediately.
     *
     * @param messages - Messages to seal (mutated in place)
     */
    private sealMessagesForBuffering;
    /**
     * Insert an observation marker into a message.
     * The marker is appended directly to the message's parts array (mutating in place).
     * Also persists the change to storage so markers survive page refresh.
     *
     * For end/failed markers, the message is also "sealed" to prevent future content
     * from being merged into it. This ensures observation markers are preserved.
     */
    /**
     * Insert an observation marker into a message.
     * For start markers, this pushes the part directly.
     * For end/failed markers, this should be called AFTER writer.custom() has added the part,
     * so we just find the part and add sealing metadata.
     */
    /**
     * Get unobserved parts from a message.
     * If the message has a completed observation (start + end), only return parts after the end.
     * If observation is in progress (start without end), include parts before the start.
     * Otherwise, return all parts.
     */
    private getUnobservedParts;
    /**
     * Check if a message has any unobserved parts.
     */
    private hasUnobservedParts;
    /**
     * Create a virtual message containing only the unobserved parts.
     * This is used for token counting and observation.
     */
    private createUnobservedMessage;
    /**
     * Get unobserved messages with part-level filtering.
     *
     * This method uses data-om-observation-end markers to filter at the part level:
     * 1. For messages WITH a completed observation: only return parts AFTER the end marker
     * 2. For messages WITHOUT completed observation: check timestamp against lastObservedAt
     *
     * This handles the case where a single message accumulates many parts
     * (like tool calls) during an agentic loop - we only observe the new parts.
     */
    private getUnobservedMessages;
    /**
     * Wrapper for observer/reflector agent.generate() calls that checks for abort.
     * agent.generate() returns an empty result on abort instead of throwing,
     * so we must check the signal before and after the call.
     * Retries are handled by Mastra's built-in p-retry at the model execution layer.
     */
    private withAbortCheck;
    /**
     * Call the Observer agent to extract observations.
     */
    private callObserver;
    /**
     * Call the Observer agent for multiple threads in a single batched request.
     * This is more efficient than calling the Observer for each thread individually.
     * Returns per-thread results with observations, currentTask, and suggestedContinuation,
     * plus the total usage for the batch.
     */
    private callMultiThreadObserver;
    /**
     * Call the Reflector agent to condense observations.
     * Includes compression validation and retry logic.
     */
    private callReflector;
    /**
     * Format observations for injection into context.
     * Applies token optimization before presenting to the Actor.
     *
     * In resource scope mode, filters continuity messages to only show
     * the message for the current thread.
     */
    /**
     * Format observations for injection into the Actor's context.
     * @param observations - The observations to inject
     * @param suggestedResponse - Thread-specific suggested response (from thread metadata)
     * @param unobservedContextBlocks - Formatted <unobserved-context> blocks from other threads
     */
    private formatObservationsForContext;
    /**
     * Get threadId and resourceId from either RequestContext or MessageList
     */
    private getThreadContext;
    /**
     * Load historical unobserved messages into the message list (step 0 only).
     * In resource scope, loads only current thread's messages.
     * In thread scope, loads all unobserved messages for the thread.
     */
    private loadHistoricalMessagesIfNeeded;
    /**
     * Calculate all threshold-related values for observation decision making.
     */
    private calculateObservationThresholds;
    /**
     * Emit debug event and stream progress part for UI feedback.
     */
    private emitStepProgress;
    /**
     * Handle observation when threshold is reached.
     * Tries async activation first if enabled, then falls back to sync observation.
     * Returns whether observation succeeded.
     */
    private handleThresholdReached;
    /**
     * Remove observed messages from message list after successful observation.
     * Accepts optional observedMessageIds for activation-based cleanup (when no markers are present).
     */
    private cleanupAfterObservation;
    /**
     * Handle per-step save when threshold is not reached.
     * Persists messages incrementally to prevent data loss on interruption.
     */
    private handlePerStepSave;
    /**
     * Inject observations as system message and add continuation reminder.
     */
    private injectObservationsIntoContext;
    /**
     * Filter out already-observed messages from message list (step 0 only).
     * Historical messages loaded from DB may contain observation markers from previous sessions.
     */
    private filterAlreadyObservedMessages;
    /**
     * Process input at each step - check threshold, observe if needed, save, inject observations.
     * This is the ONLY processor method - all OM logic happens here.
     *
     * Flow:
     * 1. Load historical messages (step 0 only)
     * 2. Check if observation threshold is reached
     * 3. If threshold reached: observe, save messages with markers
     * 4. Inject observations into context
     * 5. Filter out already-observed messages
     */
    processInputStep(args: ProcessInputStepArgs): Promise<MessageList | MastraDBMessage[]>;
    /**
     * Save any unsaved messages at the end of the agent turn.
     *
     * This is the "final save" that catches messages that processInputStep didn't save
     * (e.g., when the observation threshold was never reached, or on single-step execution).
     * Without this, messages would be lost because MessageHistory is disabled when OM is active.
     */
    processOutputResult(args: ProcessOutputResultArgs): Promise<MessageList | MastraDBMessage[]>;
    /**
     * Save messages to storage, regenerating IDs for any messages that were
     * previously saved with observation markers (sealed).
     *
     * After saving, tracks which messages now have observation markers
     * so their IDs won't be reused in future save cycles.
     */
    private saveMessagesWithSealedIdTracking;
    /**
     * Load messages from storage that haven't been observed yet.
     * Uses cursor-based query with lastObservedAt timestamp for efficiency.
     *
     * In resource scope mode, loads messages for the entire resource (all threads).
     * In thread scope mode, loads messages for just the current thread.
     */
    private loadUnobservedMessages;
    /**
     * Load unobserved messages from other threads (not the current thread) for a resource.
     * Called fresh each step so it reflects the latest lastObservedAt cursors
     * after observations complete.
     */
    private loadOtherThreadsContext;
    /**
     * Format unobserved messages from other threads as <unobserved-context> blocks.
     * These are injected into the Actor's context so it has awareness of activity
     * in other threads for the same resource.
     */
    private formatUnobservedContextBlocks;
    private representThreadIDInContext;
    /**
     * Strip any thread tags that the Observer might have added.
     * Thread attribution is handled externally by the system, not by the Observer.
     * This is a defense-in-depth measure.
     */
    private stripThreadTags;
    /**
     * Get the maximum createdAt timestamp from a list of messages.
     * Used to set lastObservedAt to the most recent message timestamp instead of current time.
     * This ensures historical data (like LongMemEval fixtures) works correctly.
     */
    private getMaxMessageTimestamp;
    /**
     * Wrap observations in a thread attribution tag.
     * Used in resource scope to track which thread observations came from.
     */
    private wrapWithThreadTag;
    /**
     * Append or merge new thread sections.
     * If the new section has the same thread ID and date as an existing section,
     * merge the observations into that section to reduce token usage.
     * Otherwise, append as a new section.
     */
    private replaceOrAppendThreadSection;
    /**
     * Sort threads by their oldest unobserved message.
     * Returns thread IDs in order from oldest to most recent.
     * This ensures no thread's messages get "stuck" unobserved.
     */
    private sortThreadsByOldestMessage;
    /**
     * Do synchronous observation (fallback when no buffering)
     */
    private doSynchronousObservation;
    /**
     * Start an async background observation that stores results to bufferedObservations.
     * This is a fire-and-forget operation that runs in the background.
     * The results will be swapped to active when the main threshold is reached.
     *
     * If another buffering operation is already in progress for this scope, this will
     * wait for it to complete before starting a new one (mutex behavior).
     *
     * @param record - Current OM record
     * @param threadId - Thread ID
     * @param unobservedMessages - All unobserved messages (will be filtered for already-buffered)
     * @param lockKey - Lock key for this scope
     * @param writer - Optional stream writer for emitting buffering markers
     */
    private startAsyncBufferedObservation;
    /**
     * Internal method that waits for existing buffering operation and then runs new buffering.
     * This implements the mutex-wait behavior.
     */
    private runAsyncBufferedObservation;
    /**
     * Perform async buffered observation - observes messages and stores to bufferedObservations.
     * Does NOT update activeObservations or trigger reflection.
     *
     * The observer sees: active observations + existing buffered observations + message history
     * (excluding already-buffered messages).
     */
    private doAsyncBufferedObservation;
    /**
     * Combine active and buffered observations for the buffering observer context.
     * The buffering observer needs to see both so it doesn't duplicate content.
     */
    private combineObservationsForBuffering;
    /**
     * Try to activate buffered observations when threshold is reached.
     * Returns true if activation succeeded, false if no buffered content or activation failed.
     *
     * @param record - Current OM record
     * @param lockKey - Lock key for this scope
     * @param writer - Optional writer for emitting UI markers
     */
    private tryActivateBufferedObservations;
    /**
     * Start an async background reflection that stores results to bufferedReflection.
     * This is a fire-and-forget operation that runs in the background.
     * The results will be swapped to active when the main reflection threshold is reached.
     *
     * @param record - Current OM record
     * @param observationTokens - Current observation token count
     * @param lockKey - Lock key for this scope
     */
    private startAsyncBufferedReflection;
    /**
     * Perform async buffered reflection - reflects observations and stores to bufferedReflection.
     * Does NOT create a new generation or update activeObservations.
     */
    private doAsyncBufferedReflection;
    /**
     * Try to activate buffered reflection when threshold is reached.
     * Returns true if activation succeeded, false if no buffered content or activation failed.
     *
     * @param record - Current OM record
     * @param lockKey - Lock key for this scope
     */
    private tryActivateBufferedReflection;
    /**
     * Resource-scoped observation: observe ALL threads with unobserved messages.
     * Threads are observed in oldest-first order to ensure no thread's messages
     * get "stuck" unobserved forever.
     *
     * Key differences from thread-scoped observation:
     * 1. Loads messages from ALL threads for the resource
     * 2. Observes threads one-by-one in oldest-first order
     * 3. Only updates lastObservedAt AFTER all threads are observed
     * 4. Only triggers reflection AFTER all threads are observed
     */
    private doResourceScopedObservation;
    /**
     * Check if async reflection should be triggered or activated.
     * Only handles the async path — will never do synchronous (blocking) reflection.
     * Safe to call after buffered observation activation.
     */
    private maybeAsyncReflect;
    /**
     * Check if reflection needed and trigger if so.
     * Supports both synchronous reflection and async buffered reflection.
     * When async buffering is enabled via `bufferTokens`, reflection is triggered
     * in the background at intervals, and activated when the threshold is reached.
     */
    private maybeReflect;
    /**
     * Manually trigger observation.
     *
     * When `messages` is provided, those are used directly (filtered for unobserved)
     * instead of reading from storage. This allows external systems (e.g., opencode)
     * to pass conversation messages without duplicating them into Mastra's DB.
     */
    observe(opts: {
        threadId: string;
        resourceId?: string;
        messages?: MastraDBMessage[];
        hooks?: ObserveHooks;
    }): Promise<void>;
    /**
     * Manually trigger reflection with optional guidance prompt.
     *
     * @example
     * ```ts
     * // Trigger reflection with specific focus
     * await om.reflect(threadId, resourceId,
     *   "focus on the authentication implementation, only keep minimal details about UI styling"
     * );
     * ```
     */
    reflect(threadId: string, resourceId?: string, prompt?: string): Promise<void>;
    /**
     * Get current observations for a thread/resource
     */
    getObservations(threadId: string, resourceId?: string): Promise<string | undefined>;
    /**
     * Get current record for a thread/resource
     */
    getRecord(threadId: string, resourceId?: string): Promise<ObservationalMemoryRecord | null>;
    /**
     * Get observation history (previous generations)
     */
    getHistory(threadId: string, resourceId?: string, limit?: number): Promise<ObservationalMemoryRecord[]>;
    /**
     * Clear all memory for a specific thread/resource
     */
    clear(threadId: string, resourceId?: string): Promise<void>;
    /**
     * Get the underlying storage adapter
     */
    getStorage(): MemoryStorage;
    /**
     * Get the token counter
     */
    getTokenCounter(): TokenCounter;
    /**
     * Get current observation configuration
     */
    getObservationConfig(): ResolvedObservationConfig;
    /**
     * Get current reflection configuration
     */
    getReflectionConfig(): ResolvedReflectionConfig;
}
export {};
//# sourceMappingURL=observational-memory.d.ts.map