/** * Memory backend abstraction. * * Backends are mutually exclusive — `await resolveMemoryBackend(settings)` returns * exactly one. Implementations MUST be self-contained: they own the per-session * state they create in `start()` and tear it down on `clear()`. */ import type { AgentMessage } from "@oh-my-pi/pi-agent-core"; import type { ModelRegistry } from "../config/model-registry.js"; import type { Settings } from "../config/settings.js"; import type { HindsightSessionStateApi } from "../hindsight/state.js"; import type { MnemopiSessionStateApi } from "../mnemopi/state.js"; import type { AgentSession } from "../session/agent-session.js"; export type MemoryBackendId = "off" | "local" | "hindsight" | "mnemopi" | "sharpshooter"; export interface MemoryBackendStatus { backend: MemoryBackendId; active: boolean; writable: boolean; searchable: boolean; scope?: string; retainBank?: string; recallBanks?: string[]; workingCount?: number; episodicCount?: number; tripleCount?: number; lastMemory?: string; lastRecall?: boolean; database?: string; message?: string; error?: string; } export interface MemoryBackendSearchOptions { limit?: number; /** Best-effort abort signal. Backends may only observe it before/after an underlying recall call. */ signal?: AbortSignal; } export interface MemoryBackendSearchItem { id?: string; content: string; source?: string; timestamp?: string; score?: number; } export interface MemoryBackendSearchResult { backend: MemoryBackendId; query: string; count: number; items: MemoryBackendSearchItem[]; message?: string; } export interface MemoryBackendSaveInput { content: string; context?: string; source?: string; importance?: number; } export interface MemoryBackendSaveResult { backend: MemoryBackendId; stored: number; ids?: string[]; queued?: boolean; message?: string; } export interface MemoryBackendOperationContext { agentDir: string; cwd: string; session?: AgentSession; } export interface MemoryRuntimeContext { status(): Promise; search(query: string, options?: MemoryBackendSearchOptions): Promise; save(input: string | MemoryBackendSaveInput): Promise; } export interface MemoryBackendStartOptions { session: AgentSession; settings: Settings; modelRegistry: ModelRegistry; agentDir: string; taskDepth: number; parentHindsightSessionState?: HindsightSessionStateApi; parentMnemopiSessionState?: MnemopiSessionStateApi; } /** A successful recall, including an empty result, staged until user-turn delivery. */ export interface MemoryPromptPreparation { context?: string; /** * Model-facing note delivered with this turn's user message, e.g. what changed in the * memories a reused recall holds. Persisted in the transcript as a hidden custom message * whose `details` carry the backend's bookkeeping, so both land only once delivered. */ notice?: { content: string; details: unknown; }; /** Commit synchronously after delivery validation; false rejects lost ownership without state writes. */ commit(): boolean; } export interface MemoryBackend { readonly id: MemoryBackendId; /** * Wire any background work or session subscriptions for this backend. * * Called once per agent session at startup. Implementations MUST be * non-throwing: failures should be logged and swallowed so a misconfigured * memory backend cannot break the agent loop. */ start(options: MemoryBackendStartOptions): void | Promise; /** * Markdown injected as the system-prompt append section. * Returned on every prompt rebuild via `refreshBaseSystemPrompt()`. * `cwd` is the session's project directory and selects project-scoped memory. */ buildDeveloperInstructions(agentDir: string, cwd: string, settings: Settings, session?: AgentSession): Promise; /** Wipe all persisted state for this backend (slash `/memory clear`). */ clear(agentDir: string, cwd: string, session?: AgentSession): Promise; /** Force consolidation/retain to happen now (slash `/memory enqueue`). */ enqueue(agentDir: string, cwd: string, session?: AgentSession): Promise; /** Structured state for UI, slash commands, and extensions. */ status?(context: MemoryBackendOperationContext): Promise; /** Explicit user-facing semantic/lexical search. */ search?(context: MemoryBackendOperationContext, query: string, options?: MemoryBackendSearchOptions): Promise; /** Explicit user-facing save operation. */ save?(context: MemoryBackendOperationContext, input: MemoryBackendSaveInput): Promise; /** Render backend-specific memory statistics as markdown (`/memory stats`). */ stats?(agentDir: string, cwd: string, session?: AgentSession): Promise; /** Render backend-specific memory diagnostics as markdown (`/memory diagnose`). */ diagnose?(agentDir: string, cwd: string, session?: AgentSession): Promise; /** Render pending deltas awaiting consolidation (`/memory queue`). */ queuePreview?(context: MemoryBackendOperationContext): Promise; /** * Optional hook to inject a backend-specific block into the current turn's * system prompt before the agent starts generating. * * This is the only place a backend can affect the very first answer of a * fresh session. Context is appended to the winning base prompt at delivery; * commit publishes the cached snippet and first-turn consumption together. * Return undefined for an ineligible or failed recall, not an empty success. */ beforeAgentStartPrompt?(session: AgentSession, promptText: string, signal?: AbortSignal): Promise; /** * Optional hook to splice extra context into a compaction summarization. * * Called from the compaction call site before the LLM summary is requested. * Returning a string appends one entry to the compaction's `extraContext` * list (which becomes part of the summarization prompt). Return `undefined` * to inject nothing — the local backend takes this branch because its * summary is already part of the system prompt. `signal` aborts when the * compaction pass is cancelled or superseded. */ preCompactionContext?(messages: AgentMessage[], settings: Settings, session?: AgentSession, signal?: AbortSignal): Promise; /** * Optional hook to apply live edits to this backend's own `.*` settings * (`changed` lists them) in a running top-level session. When omitted, the * session re-applies the whole backend (`applyMemoryBackend`), which rebuilds * its runtime state, memory tools, and prompt from the current settings. */ applySettings?(session: AgentSession, changed: readonly string[]): Promise; }