Skip to content

Latest commit

 

History

History
227 lines (179 loc) · 10.2 KB

File metadata and controls

227 lines (179 loc) · 10.2 KB
description Optional `Memory` trait backend that delegates to a locally-running agentmemory REST server, for users who self-host agentmemory across Claude Code, Cursor, Codex, OpenCode, and OpenHuman.
icon database

agentmemory backend

OpenHuman's default Memory trait backend is sqlite — the unified store documented in Memory Trees. For users who already self-host agentmemory — typically because they want a single durable memory shared across Claude Code, Cursor, Codex, OpenCode, and OpenHuman — OpenHuman exposes an opt-in backend that proxies every trait call through agentmemory's REST surface.

Selecting backend = "agentmemory" skips OpenHuman's SQLite + embedder path entirely. agentmemory owns the storage, embedding, and retrieval layers. OpenHuman becomes a thin REST client.

When to use this

Use the agentmemory backend if:

  • You already run npx -y @agentmemory/agentmemory for one or more coding agents and want OpenHuman to share the same durable store.
  • You want hybrid BM25 + vector + graph retrieval without provisioning a separate embedder on the OpenHuman side.
  • You prefer agentmemory's lifecycle (consolidation, retention scoring, auto-forget, graph extraction) over OpenHuman's unified store.

Keep the default sqlite backend if:

  • You want self-contained, single-process operation with no external daemon dependency.
  • You rely on OpenHuman-specific Memory Tree features (chunking, sealing, summary trees) that operate on top of the SQLite store. The Memory Tree pipeline is unaffected by the trait backend — it operates on the host's document store, orthogonally — but the agentmemory backend is most valuable when you've already standardised on agentmemory across other agents.

Quick start

  1. Install + start agentmemory (one terminal):

    npx -y @agentmemory/agentmemory

    Defaults to http://localhost:3111 (REST) + ws://localhost:49134 (engine). First boot generates an HMAC secret at ~/.agentmemory/.hmac and prints it once.

  2. Point OpenHuman at it in your config.toml:

    [memory]
    backend = "agentmemory"
    # Defaults below — set only when overriding.
    # agentmemory_url        = "http://localhost:3111"
    # agentmemory_secret     = ""           # HMAC bearer token, optional
    # agentmemory_timeout_ms = 5000
  3. Restart OpenHuman. The factory short-circuits the SQLite path and logs [memory::factory] using agentmemory backend at <url>.

That's it. Existing OpenHuman call sites (store, recall, get, list, forget, namespace_summaries, count, health_check) work unchanged.

Config keys

Field Default Purpose
agentmemory_url http://localhost:3111 Base URL for the agentmemory REST server
agentmemory_secret none Optional HMAC bearer token. Sent as Authorization: Bearer <secret>
agentmemory_timeout_ms 5000 Per-request reqwest timeout

When backend == "agentmemory", the following existing MemoryConfig fields are ignored — agentmemory owns its own embedding stack via ~/.agentmemory/.env:

  • embedding_provider
  • embedding_model
  • embedding_dimensions
  • sqlite_open_timeout_secs

Setting them on this path is a no-op. The local-AI Ollama health-gate also doesn't run on this path — agentmemory's daemon manages its own embedder lifecycle.

Field mapping

OpenHuman's MemoryEntry ↔ agentmemory wire row:

OpenHuman field agentmemory field Notes
namespace project Defaults to "default" when empty
key title
content content
id id agentmemory-generated (mem_<rand>)
category: Core type: "fact"
category: Daily type: "conversation"
category: Conversation type: "conversation"
category: Custom(s) type: "fact" + concepts: [s] Custom tag rolled into the concepts array so it remains queryable
session_id sessionIds: [...] OpenHuman exposes a single id; agentmemory persists an array
timestamp updatedAt (RFC3339) Falls back to createdAt if updatedAt is absent
score (recall hits only) smart-search score Populated on recall responses, None on get / list

agentmemory carries additional fields — concepts (auto-extracted), files (path tags), strength (retention score), version, supersedes (the lifecycle chain) — that this backend leaves at defaults. They're internal to agentmemory's lifecycle layer and don't need to round-trip through OpenHuman's trait.

Trait method → endpoint

Memory method agentmemory REST Notes
store POST /agentmemory/remember {project, title, content, type, concepts, sessionIds}
recall POST /agentmemory/smart-search Hybrid BM25 + vector + graph
get POST /agentmemory/smart-search + client-side exact-title filter
list GET /agentmemory/memories?latest=true&project=<ns>
forget get(ns, key)POST /agentmemory/forget Two-step: resolve id then forget
namespace_summaries GET /agentmemory/projects Returns [{name, count, lastUpdated}]
count GET /agentmemory/health Reads memories field
health_check GET /agentmemory/livez

RecallOpts.category, RecallOpts.session_id, and RecallOpts.min_score are applied as client-side filters on the smart-search response. agentmemory's REST surface doesn't expose them as server-side filters today. For very large recall windows (limit > 100) prefer issuing a tighter query string to reduce server-side work over relying on client-side post-filtering.

Security

When agentmemory_secret is set, the client honours agentmemory's v0.9.12 plaintext-bearer guard contract:

  • Loopback hosts (localhost, 127.0.0.1, ::1) over http:// — allowed. Local dev path.
  • https:// to any host — allowed.
  • Plaintext HTTP to a non-loopback host — emits a one-time stderr warning at construction time. The bearer is observable on the wire.
  • AGENTMEMORY_REQUIRE_HTTPS=1 (process env, ASCII-case-insensitive matches 1 or true) — escalates the warning into a hard refusal at client construction. The backend fails to start rather than leak the bearer once.

Production deploys should set AGENTMEMORY_REQUIRE_HTTPS=1 so a misconfigured TLS terminator fails loud rather than silently leaking.

The plaintext-bearer guard mirrors the integration plugin guards in agentmemory's PR #315 so an operator who's seen the warning on Hermes / OpenClaw / pi will recognise the same message on OpenHuman.

Failure modes

Failure Backend behaviour
Daemon unreachable at startup from_config succeeds (URL parses), but health_check() returns false on first call. Trait methods bubble up reqwest transport errors
Network timeout anyhow::Error per trait contract; surfaces to caller
4xx / 5xx response anyhow::Error with status + body snippet
Bearer over plaintext non-loopback (no env) One-time stderr warning, request proceeds
Bearer over plaintext non-loopback + AGENTMEMORY_REQUIRE_HTTPS=1 Hard refusal at construction time
Empty agentmemory_url Hard refusal at construction time with hint to leave it unset for the default
Invalid URL syntax Hard refusal at construction time with the parser error

No automatic fallback to SQLite. If the daemon is down at boot, the backend surfaces the transport error loudly. Operators flip back to backend = "sqlite" in config.toml to recover. Rationale: a silent SQLite fallback would hide a misconfigured daemon — "private, simple, predictable" wins over "magically tolerant".

Performance notes

The backend is a thin REST proxy — it adds one HTTP round-trip per trait call. Practical implications:

  • store and forget are single-RTT.
  • recall, get, list are single-RTT.
  • forget against an unknown key is two-RTT (the implicit get lookup
    • a no-op confirmation). Caller can short-circuit this by checking the return value of a prior list.
  • agentmemory's REST is 127.0.0.1 by default — same-host latency is sub-millisecond. Over a managed deploy with HTTPS termination, expect ~10–30ms per RTT.
  • The default per-request timeout is 5 seconds. Bump agentmemory_timeout_ms if you're seeing intermittent timeouts on cold-start of the iii engine; agentmemory's first-request latency after a long idle can stretch toward 3–5s depending on persistence state.

Migration: from SQLite to agentmemory

There's no in-place migration today. The recommended path:

  1. Export your existing memories from the SQLite store via OpenHuman's existing export RPC (or by direct SQL).
  2. Walk the export and POST each row to /agentmemory/remember with the same project + title + content. agentmemory will assign new ids; the OpenHuman side picks them up on first list.
  3. Set backend = "agentmemory" and restart.

A dedicated bulk import path is filed as a follow-up.

Implementation reference

In-tree files:

Related upstream:

  • agentmemory repo — https://github.com/rohitg00/agentmemory
  • agentmemory REST contract — ~/.agentmemory/.env keys + endpoint list in the agentmemory README
  • v0.9.12 plaintext-bearer guard — agentmemory PR #315