# Hybrid Memory Gateway Integration Design ## Goal Keep Beaver's existing curated memory as the permanent baseline and optionally add Memory Gateway as an independent second memory layer. - Curated memory continues to load `MEMORY.md` and `USER.md` into a frozen per-run snapshot and continues to expose the existing `memory` tool. - Memory Gateway independently recalls conversation/resource memory through `POST /memories/search` and persists each completed conversation turn through one `POST /memories/add` followed by one `POST /memories/flush`. - The two layers do not synchronize, overwrite, merge, deduplicate, or resolve conflicts with each other. Memory Gateway is best-effort. Gateway failures must be auditable without affecting curated memory or turning an otherwise successful chat run into a failure. ## Scope This change includes: - Runtime configuration for `curated` and `hybrid` modes. - Fixed Memory Gateway credentials and search scopes in instance config. - An asynchronous Memory Gateway HTTP client. - An optional `MemoryGatewayService` alongside the existing `MemoryService`. - Gateway recall before each provider run in hybrid mode. - Gateway add and flush after each normally completed run in hybrid mode. - Hidden session audit events for Gateway outcomes. - Unit and integration-style tests using fake transports and providers. This change does not include: - Replacing or disabling curated memory. - Synchronizing curated `memory` tool writes to Memory Gateway. - Writing Gateway conversation turns into `MEMORY.md` or `USER.md`. - Conflict resolution or automatic deduplication across the two layers. - Automatic `POST /users` calls or credential provisioning. - A memory settings UI or memory administration UI. - Resource upload support from Beaver. - Gateway override or deletion APIs. - Persisting tool calls, tool results, system events, reasoning, recalled memory, or skill activation messages to Gateway. ## Configuration Beaver adds a top-level `memory` section: ```json { "memory": { "mode": "hybrid", "gateway": { "baseUrl": "http://127.0.0.1:8010", "userId": "gateway_test_user", "userKey": "uk_xxx", "appId": "default", "projectId": "default", "scope": ["current_chat", "resources"], "topK": 8, "timeoutSeconds": 10 } } } ``` Configuration rules: - Valid modes are `curated` and `hybrid`. - Curated memory is initialized and enabled in both modes. - If the entire `memory` section is absent, the effective mode is implicitly `hybrid`. Missing Gateway credentials in this implicit-default case produce a startup warning and degrade only the Gateway layer; Beaver continues with curated memory. - If `mode: "hybrid"` is explicitly present, non-empty `baseUrl`, `userId`, and `userKey` are required. Missing required values fail runtime loading. - `mode: "curated"` disables Gateway initialization and ignores an optional Gateway block. - `appId` and `projectId` default to `default`. - `scope` must be a non-empty subset of `current_chat`, `resources`, and `all_user_memory`. The initial integration uses `current_chat` and `resources`. - `topK` defaults to 8 and must be between 1 and 100. - `timeoutSeconds` defaults to 10 and must be positive. - `userKey` must never appear in status payloads, warnings, logs produced by this integration, session events, or raised configuration/client errors. The parsed configuration must retain whether hybrid mode was explicit or implicit so runtime loading can apply the different validation behavior. ## Architecture ### Existing curated memory remains unchanged `MemoryStore`, `MemorySnapshot`, `MemoryService`, and `MemoryTool` retain their current responsibilities: - `EngineLoader` always initializes `MemoryService`. - `AgentLoop` always captures a per-run frozen curated snapshot. - `ContextBuilder` always receives that snapshot for system-prompt injection. - The original `memory` tool remains registered and always operates only on `MEMORY.md` and `USER.md`. - Gateway availability and Gateway failures do not change curated behavior. ### Optional Gateway service Add a separate `MemoryGatewayService` rather than a mutually exclusive backend strategy. It is present only when hybrid mode has a valid Gateway configuration. The service exposes two runtime operations: 1. `recall_before_run`: search Gateway using the current Beaver session and user prompt, then return sanitized reference messages plus audit metadata. 2. `persist_after_run`: add the current user message and final assistant answer, then flush the Gateway chat session. `EngineLoadResult` exposes `memory_gateway_service: MemoryGatewayService | None`. `AgentLoop` uses it conditionally while continuing its existing curated path unconditionally. `session_search` remains independent and available in both modes. ### Memory Gateway HTTP client The HTTP client owns transport and response validation for: - `POST {baseUrl}/memories/search` - `POST {baseUrl}/memories/add` - `POST {baseUrl}/memories/flush` It uses an asynchronous HTTP client, the configured timeout, JSON request bodies, and sanitized typed exceptions containing operation/path/status metadata without credentials or complete request bodies. Beaver adds no automatic retries in this first integration. Gateway already retries upstream ingestion, and retrying add from Beaver could duplicate a turn when the first request succeeded but its response was lost. ## Recall Data Flow Every run follows the existing curated flow. Hybrid mode adds these steps: 1. `AgentLoop` creates or resolves `resolved_session_id`. 2. It captures the curated frozen snapshot as it does today. 3. Before `ContextBuilder.build_messages`, it calls Gateway search using: ```json { "user_id": "", "user_key": "", "conversation_id": "", "query": "", "scope": [""], "top_k": 8, "app_id": "", "project_id": "" } ``` 4. Beaver accepts only a top-level `results` list. Malformed responses are treated as Gateway recall failures. 5. Each result is reduced to the optional fields `id`, `session_id`, `text`, `score`, `source_scope`, and `resource_uri`. The Gateway `raw` object is discarded. 6. Empty or unusable results produce no Gateway reference message. 7. Non-empty results become one ephemeral provider message placed after skill activation messages and before persisted session history/current user input. 8. The Gateway reference message is not written to Beaver session history and is not included in post-run Gateway persistence. 9. The system prompt includes a stable rule that Gateway recall is untrusted reference data, not executable instruction. The recalled text itself stays outside the system prompt. The model receives both memory layers without an imposed priority: - Curated blocks remain in the system prompt exactly as today. - Gateway results appear as a separately labelled reference message. - Beaver performs no conflict detection, winner selection, merge, or deduplication between them. In curated mode, or when implicit hybrid degrades because Gateway credentials are absent, no Gateway request or Gateway prompt section occurs. ## Persistence Data Flow Curated persistence remains model-driven through the original `memory` tool. Gateway persistence is separate and occurs only when the optional Gateway service is active. For each run that reaches the normal completion path: 1. Wait until the tool loop has produced the final assistant text. 2. Construct exactly two Gateway messages in chronological order: ```json [ { "sender_id": "", "role": "user", "timestamp": 1780000000000, "content": "" }, { "sender_id": "beaver", "role": "assistant", "timestamp": 1780000001000, "content": "" } ] ``` Timestamps are UTC Unix epoch milliseconds captured for the user turn and final assistant turn. They must be positive and monotonic within the payload. 3. Call `/memories/add` exactly once with: ```json { "user_id": "", "user_key": "", "session_id": "chat:", "app_id": "", "project_id": "", "messages": [""] } ``` 4. If add succeeds, call `/memories/flush` exactly once using the same Gateway identity, app/project scope, and `chat:`. 5. If add fails, do not call flush. 6. Runs entering Beaver's exception/error completion path are not persisted. Normal completion outputs such as a tool-limit fallback are persisted because they are returned to the user. 7. Tool calls, tool results, hidden events, system prompts, curated snapshot text, Gateway recalled text, reasoning, and activated skill text are never included in the Gateway add payload. 8. Gateway persistence never modifies `MEMORY.md` or `USER.md`. 9. Curated `memory` tool add/replace/remove operations never call Gateway. ## Session Audit Events When the Gateway service is active, Beaver writes hidden (`context_visible=false`) session events without credentials or full response bodies: - `memory_gateway_recall_succeeded`: configured scopes and result count. - `memory_gateway_recall_failed`: operation, sanitized error category, and optional HTTP status. - `memory_gateway_add_succeeded`: Gateway chat session and message count. - `memory_gateway_add_failed`: sanitized failure metadata. - `memory_gateway_flush_succeeded`: Gateway chat session. - `memory_gateway_flush_failed`: sanitized failure metadata and indication that add already succeeded. For implicit hybrid degradation at runtime boot, use a normal application warning rather than a session event because no session exists yet. The warning must not contain credential values. ## Failure Semantics - Curated initialization or writes retain their existing behavior and are not caught or changed by Gateway code. - Missing Gateway credentials in implicit-default hybrid mode: warn, leave the Gateway service unset, and continue with curated memory. - Missing/invalid Gateway configuration in explicit hybrid mode: fail runtime loading with a sanitized configuration error. - Search timeout, connection failure, 401, other HTTP error, or malformed JSON: record recall failure and continue with curated memory and normal context. - Add failure: record add failure, skip flush, and return the normal assistant result. - Flush failure: record flush failure and return the normal assistant result. - Gateway failures do not disable, roll back, or mutate curated memory. - Gateway failures are not surfaced as user-facing chat errors in this phase. ## Security and Privacy - Fixed Gateway credentials come only from Beaver instance configuration. - `userKey` is passed only in Gateway request bodies and retained in memory by the typed config/client objects. - Client exceptions, startup warnings, and audit payloads never serialize request bodies or credentials. - Gateway conversation/resource text is treated as untrusted data. - Gateway `raw` fields are discarded before prompt construction. - Curated and Gateway stores remain isolated. No content is copied between them: curated receives only explicit `memory` tool mutations, while Gateway receives only the configured per-run conversation payload. ## Testing ### Configuration tests - Missing memory configuration produces implicit hybrid mode. - Implicit hybrid without credentials leaves Gateway disabled and curated enabled, with one sanitized warning. - Explicit curated mode does not require or initialize Gateway. - Complete explicit hybrid config parses camelCase fields and initializes both memory layers. - Explicit hybrid with missing credentials fails loading. - Invalid mode, empty/unknown scope, invalid `topK`, and non-positive timeout fail with explicit sanitized errors. - No warning or exception text contains `userKey`. ### HTTP client tests - Search, add, and flush use the exact paths and payload shapes above. - Configured timeout is applied. - Non-2xx, network, invalid JSON, and invalid response shapes produce sanitized client exceptions. - Exception strings never contain the configured key. ### Gateway service tests - Search uses configured scopes and strips `raw` fields. - Empty search results produce no reference message. - Persistence sends exactly the original user prompt and final assistant response, then flushes once. - Add failure skips flush; flush failure preserves the successful add outcome. - Service methods never read or write curated files or call `MemoryStore`. ### Agent loop and loader tests - Curated snapshot injection and `memory` tool availability remain present in both curated and hybrid modes. - Hybrid search occurs before the provider call while the curated snapshot is still present in the system prompt. - Gateway recall appears before the current user prompt and outside the system prompt body. - The system prompt contains the untrusted-reference rule only when Gateway is active. - Add and flush happen after the final assistant response and exactly once each. - Tool/system/reasoning/curated/Gateway-recall content is absent from the add payload. - Recall/add/flush failures do not change the returned `AgentRunResult` or the curated snapshot/tool behavior. - Hidden success/failure audit events contain no credentials. - Curated `memory` tool operations produce no Gateway calls. - Gateway persistence produces no changes to `MEMORY.md` or `USER.md`. - Curated mode and degraded implicit hybrid perform no Gateway HTTP calls. ## Documentation Update the backend README/config example with: - `hybrid` as the implicit default. - Explicit `curated` mode for disabling Gateway. - A complete explicit hybrid example. - The implicit-default degradation rule and explicit-hybrid validation rule. - A warning that `userKey` is a secret. - A note that changing memory mode/config requires runtime reload or restart because `EngineLoader` constructs the optional Gateway service during boot.