Files
beaver_project/docs/superpowers/specs/2026-06-15-memory-gateway-backend-design.md

14 KiB

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:

{
  "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:
{
  "user_id": "<configured userId>",
  "user_key": "<configured userKey>",
  "conversation_id": "<resolved_session_id>",
  "query": "<current user prompt>",
  "scope": ["<configured scopes>"],
  "top_k": 8,
  "app_id": "<configured appId>",
  "project_id": "<configured projectId>"
}
  1. Beaver accepts only a top-level results list. Malformed responses are treated as Gateway recall failures.
  2. Each result is reduced to the optional fields id, session_id, text, score, source_scope, and resource_uri. The Gateway raw object is discarded.
  3. Empty or unusable results produce no Gateway reference message.
  4. Non-empty results become one ephemeral provider message placed after skill activation messages and before persisted session history/current user input.
  5. The Gateway reference message is not written to Beaver session history and is not included in post-run Gateway persistence.
  6. 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:
[
  {
    "sender_id": "<configured userId>",
    "role": "user",
    "timestamp": 1780000000000,
    "content": "<original current user prompt>"
  },
  {
    "sender_id": "beaver",
    "role": "assistant",
    "timestamp": 1780000001000,
    "content": "<final assistant text>"
  }
]

Timestamps are UTC Unix epoch milliseconds captured for the user turn and final assistant turn. They must be positive and monotonic within the payload.

  1. Call /memories/add exactly once with:
{
  "user_id": "<configured userId>",
  "user_key": "<configured userKey>",
  "session_id": "chat:<resolved_session_id>",
  "app_id": "<configured appId>",
  "project_id": "<configured projectId>",
  "messages": ["<the two messages above>"]
}
  1. If add succeeds, call /memories/flush exactly once using the same Gateway identity, app/project scope, and chat:<resolved_session_id>.
  2. If add fails, do not call flush.
  3. 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.
  4. 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.
  5. Gateway persistence never modifies MEMORY.md or USER.md.
  6. 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.