agent_memoryis hot identity memory plus prompt injection. It is keyed by stableAgentIdentity, stored by realm, and injected during create/resume/materialize and ordinary identity-first turns.mobkit/memory/*is an operational assertion and conflict ledger used by routing/gating flows. It is persisted as a local JSON file underpersistent_state, optionally gated by an HTTP health check. Query semantics are exact filters over stored assertions plus an optional substringqueryfilter.
Architecture
The bundledagent_memory provider is intentionally lightweight. It writes an
inspectable markdown hot store under <persistent_state>/agent-memory so an
explicit remember write is available to the next turn without requiring an
external service or LLM-powered extraction pipeline. This store is the default
gateway implementation, not the long-term knowledge authority.
Elephant is not integrated today. A real Elephant integration — a deep-memory
hub with a knowledge graph, document/vector retrieval, truth maintenance,
provenance, and ABAC/redaction — is the wire-boundary provider described in the
memory hub roadmap (docs/design/memory-hub-roadmap.md in the repository).
Nothing in the current runtime sends data to Elephant.
Meerkat’s session semantic memory is a separate layer again: it indexes
compacted session content and exposes semantic recall through Meerkat’s
memory_search tool. MobKit’s agent_memory injector can be backed by a
provider that consults session memory or other stores, but the MobKit runtime
does not assume semantic/vector search from mobkit/memory/*.
The operational ledger backend persists assertions and conflict signals as a
local JSON file, optionally gated by an HTTP health check before every ledger
read/write. Configuration is provided through LocalJsonMemoryBackendConfig:
The legacy
ElephantMemoryBackendConfig shape (kind = "elephant" with an
endpoint) is still accepted for config compatibility and logs a deprecation
warning: despite its name it never sent data to Elephant — the endpoint was
only ever health-checked and the ledger persisted locally. It maps to the
local-JSON backend with endpoint as the health-check endpoint.
When using the Python or TypeScript SDK through the stock rpc_gateway, configure the ledger with memory.local_json() (Python) / memory.localJson() (TypeScript), optionally passing a health-check endpoint, and enable .persistent_state(path). The gateway derives the state file as <persistent_state>/memory-ledger-state.json (an existing legacy <persistent_state>/elephant-memory-state.json is adopted automatically so restarts keep their data). The deprecated memory.elephant(endpoint) helpers still work and emit the legacy backend: "elephant" wire shape; fields such as stores, space_id, and collection are rejected at init so unsupported configuration does not become a silent no-op.
Indexing
Index an operational assertion with aMemoryIndexRequest:
conflict: Some(true) with conflict_reason to record a conflict signal instead of a normal assertion.
Querying
Query memory with aMemoryQueryRequest:
entity, topic, and store (exact matches). The optional query parameter is applied afterwards as a case-insensitive substring filter across entity, topic, and fact (reason for conflict signals). Semantic/vector search should be supplied by a provider or upstream Meerkat memory surface rather than assumed from this RPC.
Agent memory injection
Gateway users can enable identity-first injection with:<persistent_state>/agent-memory/<realm>/<identity>.md. Records are inspectable and are injected as a quoted observation block for the matching durable identity. The bundled markdown store enforces an explicit retention policy per identity file: at most 512 records and 8 MiB of rendered markdown, retaining the newest records when compacting. Recall reads within that bounded file, contextual recall receives the raw turn or RPC query_text, ignores common stopwords, scores matching records, and size-caps injected records; malformed records are skipped rather than promoted into runtime context. Automatic build/turn injection is time-bounded by recall_timeout_ms (default 500) and defaults to recall_failure_policy: "skip", so provider slowness or outage does not block normal delivery. Set recall_failure_policy: "fail" for fail-closed deployments. Explicit mobkit/agent_memory/recall calls still return provider errors to the caller.
Apps write identity-scoped agent memory explicitly through mobkit/agent_memory/remember, TypeScript handle.rememberAgentMemory(...), or Python handle.remember_agent_memory(...); read it through mobkit/agent_memory/recall, handle.recallAgentMemory(...), or handle.recall_agent_memory(...); and delete individual records through mobkit/agent_memory/forget, handle.forgetAgentMemory(...), or handle.forget_agent_memory(...). A successful remember means the configured hot identity-memory provider durably accepted the record and it is available to the next identity-first send for that identity, plus build context on materialization/resume/respawn/reset. It does not imply Elephant entity extraction, truth maintenance, or document/vector indexing has completed. Custom providers receive both raw query_text and normalized query_terms so they can implement stronger retrieval without changing MobKit runtime authority.
Agent memory is identity-scoped rather than session-scoped. A session respawn or identity reset does not delete stored memory; apps that need privacy reset semantics can call mobkit/agent_memory/forget for specific records or use a provider that clears memory as part of the app’s identity lifecycle. forget removes records from future recall and injection, but it cannot remove text already delivered into an active model context; reset or respawn the identity after deletion when immediate context revocation matters.
The memory recorder tool
With the SQLite store (the default), identity-first members get a capability-gated memory tool: remember, update, forget, recall, and propose_to_mob. Writes carry real agent authorship and always enter at the agent_observed trust tier or below — an agent that verified a fact records the verification as a claim in provenance (epistemic: "verified_claim" plus verification_evidence); the trust-tier upgrade is a steward-only reviewed operation. update supersedes a record within its own lineage in the agent’s own identity scope; cross-identity updates are rejected by the store validator. propose_to_mob queues a proposal for mob scope — a steward or operator must commit it. Tool results are an indexing-excluded message class, so confirmations and recall payloads never echo into session semantic memory. Disable the tool with agent_memory.recorder_tool: false (default true when agent memory is enabled).
Content trust and session taint
A memory tool that can be poisoned by prompt injection is worse than no memory tool, so the recorder ships with a deterministic firewall:- Content-trust classification (
agent_memory.content_trust): web/fetch tools and provider-native search are always untrusted; MCP servers are untrusted by default with atrusted_mcp_serversallowlist;untrusted_tools/trusted_toolsgive explicit per-tool overrides. In this release classification is by tool name (tool events do not carry provenance yet), so MCP tools are attributable to a server only when their names are qualified asmcp__<server>__<tool>; list unqualified MCP tool names inuntrusted_tools. - Session-sticky taint: once a session ingests untrusted tool content, every LLM-authored memory write from that session lands
quarantined— stored, auditable, but never injected or recalled until review. Taint clears only at a fresh-context boundary (reset, respawn, fresh spawn); compaction does not clear it. - Write posture (
agent_memory.llm_writes):"observed"(default) quarantines only tainted-session writes;"quarantined"quarantines every LLM-authored write pending review. Choose"quarantined"if you cannot accept the known race: a write issued in the same turn as the session’s first untrusted ingestion can beat the observe-stream taint signal (closed in a later phase by dispatch-time taint visibility).
mobkit/agent_memory/* RPCs) are non-LLM principals and are never quarantined by this machinery. llm_writes and content_trust require store: "sqlite"; configuring them with the markdown store fails loudly at startup.
Conflict detection
Conflict signals are recorded throughmobkit/memory/index with
conflict: true and an optional conflict_reason. When competing claims exist
about the same entity/topic, MobKit’s gating subsystem checks for an active
conflict before approving an action.
A MemoryConflictSignal carries:
- The
entity,topic, andstorethe conflict is scoped to - An optional free-form
reason - The
updated_at_mstimestamp of the most recent signal
Health checks
Whenhealth_check_endpoint is configured, the ledger adapter performs a TCP-level health check against it before every ledger read/write. The health check:
- Resolves the endpoint hostname
- Opens a TCP connection with
MEMORY_LEDGER_HEALTHCHECK_TIMEOUT(2s) - Sends an HTTP GET to
/v1/health - Verifies a 2xx status response
LocalJsonMemoryStoreError::ExternalCallFailed with diagnostic details. Without a configured endpoint, no health check is performed.
Memory store info
mobkit/memory/stores returns a MemoryStoreInfo entry per supported store:
record_count counts stored assertions plus active conflict signals for the store. This is available through the RPC API for monitoring and diagnostics.
Console Memory panel
When the bundled SQLite agent-memory store is wired, the console gains a Memory panel — trust through inspectability. It is strictly read-only and served by fourmobkit/memory/panel/* RPC methods:
Memory lifecycle events (
memory.dream.*, memory.record.promoted, memory.quarantine.verdict, memory.write.quarantined, memory.taint.transition, memory.hygiene.*, …) flow onto the console timeline as standard envelopes and render in the timeline and Signals surfaces.
Visibility is governed by the access-control memory actions: identity-scoped records need agent.memory.read + agent.view on the identity, mob-scoped records need mob.memory.read, operator-scoped records need operator.memory.read, realm-scoped records and dream history need an unscoped agent.memory.read grant, and quarantined content needs memory.quarantine.review. The panel’s nav entry appears when the experience projection reports memory.can_read.
Embedders wire the panel by handing the store to the runtime: runtime.set_memory_panel_store(store) before building the console router.
See also
- Gating — conflict-aware approval gates
- Access control — the memory read actions governing panel visibility
docs/design/memory-hub-roadmap.md(in the repository) — the roadmap for a real Elephant-backed memory hub- RPC API —
memory.*methods
