/** * Observer/Reflector — LLM-driven session compression for CLEO BRAIN. * * Implements the Mastra-style two-agent compression pattern that achieves * 3-6x context compression on text and 5-40x on tool-call-heavy workloads. * * ## Architecture * * **Observer** (runs after each task or when observation count ≥ threshold): * - Takes raw brain observations from the current session * - Produces 3-5 dense, dated, prioritized observation notes (≤ 300 chars each) * - Compresses verbose tool outputs into 1-line summaries * - Stored as brain_observations with source='observer-compressed' * - Linked to original observation IDs via brain_memory_links table * * **Reflector** (runs at session end when observation count ≥ threshold): * - Takes accumulated observation notes from the current session * - Restructures: combines related items, identifies cross-cutting patterns, * drops superseded observations * - Outputs stored as brain_patterns and brain_learnings via existing store fns * - Source tagged as 'reflector-synthesized' * * ## LLM Backend * * Routes through {@link executeForRole} (T9320), which resolves * provider/model/credential via the standard role chain (T9255) and dispatches * to the matching transport — Anthropic Messages, OpenAI chat-completions, or * any future api-mode. `CLEO_OBSERVER_MODEL` continues to override the * resolved model when set. When no credential is configured for the resolved * provider, both functions silently no-op — the rest of the memory pipeline * still runs normally. * * ## Configuration * * Add to `config.json` under `brain.observer`: * ```json * { * "brain": { * "observer": { * "enabled": true, * "threshold": 10 * }, * "reflector": { * "enabled": true * } * } * } * ``` * * @task T554 * @epic T549 * @see .cleo/agent-outputs/R-llm-memory-systems-research.md §7 (Observer/Reflector Pattern) */ /** A single compressed observation produced by the Observer. */ export interface ObserverNote { /** ISO date string when the original events occurred. */ date: string; /** * Priority 1 (highest, architectural) through 5 (lowest, routine). * Maps to the research's RED/YELLOW/GREEN scale. */ priority: 1 | 2 | 3 | 4 | 5; /** Dense observation text, ≤ 300 characters. */ observation: string; /** IDs of the original observations this note compresses. */ source_ids: string[]; } /** Output of `runObserver`. */ export interface ObserverResult { /** Whether the LLM was invoked (false when no API key or disabled). */ ran: boolean; /** Number of new compressed observation entries stored. */ stored: number; /** IDs of original observations that were compressed. */ compressedIds: string[]; /** The raw notes produced by the LLM (for testing/debugging). */ notes: ObserverNote[]; } /** A single pattern extracted by the Reflector. */ export interface ReflectorPattern { pattern: string; context: string; } /** A single learning extracted by the Reflector. */ export interface ReflectorLearning { insight: string; confidence: number; } /** Output of `runReflector`. */ export interface ReflectorResult { /** Whether the LLM was invoked (false when no API key or disabled). */ ran: boolean; /** Number of new patterns stored. */ patternsStored: number; /** Number of new learnings stored. */ learningsStored: number; /** IDs of observations marked as superseded. */ supersededIds: string[]; } /** * Observer configuration resolved from `brain.observer` in config.json, * with safe defaults. */ export interface ObserverConfig { enabled: boolean; threshold: number; } /** * Reflector configuration resolved from `brain.reflector` in config.json, * with safe defaults. */ export interface ReflectorConfig { enabled: boolean; } /** * Run the Observer on current session observations. * * Triggered when: * 1. `brain.observer.enabled` is true (default) * 2. Uncompressed observation count ≥ `brain.observer.threshold` (default: 10) * 3. `ANTHROPIC_API_KEY` is set in the environment * * Steps: * 1. Fetch up to OBSERVER_BATCH_LIMIT recent uncompressed observations * 2. Call Anthropic LLM with Observer prompt * 3. Parse response as ObserverNote[] * 4. Store each note as brain_observation with source='observer-compressed' * 5. Add graph edges linking compressed notes → source observation IDs * * Always best-effort — errors are caught and logged, never thrown. * * @param projectRoot - Absolute path to project root. * @param sessionId - Current CLEO session ID (used to scope observation query). * @returns ObserverResult with counts and note details. */ /** * Options for overriding Observer behaviour at call time. * * T740: Callers (e.g. session-end hook) may bypass the default threshold * check by passing `thresholdOverride: 1` so Observer fires unconditionally * even in short sessions with fewer than 10 observations. */ export interface RunObserverOptions { /** * When provided, replaces `cfg.threshold` for this call only. * Pass `1` to fire unconditionally regardless of observation count. */ thresholdOverride?: number; } export declare function runObserver(projectRoot: string, sessionId?: string, options?: RunObserverOptions): Promise; /** * Run the Reflector on accumulated session observation notes. * * Triggered at session end when: * 1. `brain.reflector.enabled` is true (default) * 2. Sufficient observations exist to reflect on (≥ 3) * 3. `ANTHROPIC_API_KEY` is set in the environment * * Steps: * 1. Fetch recent observations (raw + observer-compressed) for the session * 2. Call Anthropic LLM with Reflector prompt * 3. Parse response as { patterns, learnings, superseded } * 4. Store patterns via storePattern() * 5. Store learnings via storeLearning() * 6. Mark superseded observations as invalid (soft-evict) * * Always best-effort — errors are caught and logged, never thrown. * * @param projectRoot - Absolute path to project root. * @param sessionId - Current CLEO session ID. * @returns ReflectorResult with counts. */ export declare function runReflector(projectRoot: string, sessionId?: string): Promise; //# sourceMappingURL=observer-reflector.d.ts.map