If you have built an agent in 2024 or 2025, you almost certainly used one of two memory architectures.

The first is a flat key-value store. You shove facts in under human-meaningful keys (“user_name”, “preferred_language”), look them up when you need them, and pretend the agent has memory. This works for about three turns. It breaks the moment the user contradicts an earlier fact, because you have nowhere to put the new fact except by overwriting the old one. The agent now believes the present and has no record of the past.

The second is vector search. You embed everything the user has ever said into a vector store, and at query time you pull the top-k embeddings closest to the current question. This works for about ten turns and a small enough corpus. It breaks once the corpus gets large enough that “what the user said yesterday” and “what the user said in April” both retrieve at top-k, with no temporal dimension to break the tie. The agent retrieves a confidently-stated old preference and serves it as if it were current. The user is annoyed.

Both architectures lose the same three things:

  1. The relationships between facts. Knowing “the user uses Gmail” and “the user has a meeting tomorrow” separately is less useful than knowing the meeting invite arrived through Gmail. The relationship is the load-bearing piece.

  2. The temporal dimension. “User prefers black coffee” was true in January. It is now June and the user has switched to oat-milk lattes. A flat store overwrites; a vector store retrieves both without ranking. Neither one can answer “what did I think was true six months ago, and when did that change.”

  3. Invalidation. A fact is rarely deleted; it is superseded. The old fact still happened. An agent that cannot tell the difference between “I never knew this” and “I used to know this and was wrong” is an agent that cannot learn from being wrong.

The third one is the killer. It is also the thing nobody wants to pay the engineering cost to fix.

What we ended up with

Memagen™ stores memory as a compound graph. “Compound” is doing real work in that phrase: it is not a single graph with one schema. It is seven domain-specific graphs sharing a small set of primitives, with a seventh meta-graph that records the curators that maintain them.

The domains, today, are:

  • memory — episodic events, things the user has said, things the agent has observed
  • agent_memory — the agent’s own internal traces, plans, decisions, reflections
  • presence — who is online, what they are doing, what window they have focus in
  • capabilities — what tools the agent can use right now, audited as use-and-revoke chains
  • recipes — guided multi-step procedures the agent knows how to run, with resume-mid-flight support
  • goals — what the user is trying to accomplish, decomposed into tasks
  • A meta-graph recording the curators and embedders that maintain the other six

Every node in any of these graphs carries the same primitives:

class Node:
    id: NodeId
    kind: str                    # "fact", "task", "tool_use", etc.
    domain: Literal["memory", "agent_memory", "presence", ...]
    parent_id: NodeId | None     # immediate parent in the subtree
    subtree_root_id: NodeId      # root of the logical subtree
    created_at: datetime
    revoked_at: datetime | None  # soft-delete with a timestamp
    body: dict                   # domain-specific payload

Edges carry meaning, not just connection. The edge kinds in current use:

  • owned_by — node belongs to a user / agent / session
  • decomposes_to — parent task to child sub-task
  • pursues — a task pursues a goal
  • caused_by — event A is the reason event B happened
  • revokes — a capability use that ends an earlier grant
  • supersedes — a fact that replaces an earlier fact
  • derived_from — a node that was computed from another

parent_id + subtree_root_id is the part that makes hierarchical traversal cheap. Asking “show me everything that descended from this goal” is a single index lookup on subtree_root_id, not a recursive walk.

Fact invalidation, concretely

Here is the case the abstract framing is built around.

In January, the user tells the agent: “I prefer black coffee.” The agent writes:

Node {
  id: n1,
  kind: "preference",
  domain: "memory",
  body: { topic: "coffee", value: "black" },
  created_at: 2026-01-04T08:12:00Z,
  revoked_at: null
}

In June, the user says: “I have switched to oat-milk lattes — my stomach is happier this way.” The agent does not delete n1. Instead, it writes:

Node {
  id: n2,
  kind: "preference",
  domain: "memory",
  body: { topic: "coffee", value: "oat-milk latte" },
  created_at: 2026-06-15T19:30:00Z,
  revoked_at: null
}

Edge {
  from: n2,
  to: n1,
  kind: "supersedes",
  body: { reason: "user explicit, stomach health" }
}

# and on n1:
n1.revoked_at = 2026-06-15T19:30:00Z

n1 stays in the graph. It is no longer the answer to “what does the user prefer for coffee right now” — the retriever filters revoked_at IS NULL for present-tense queries — but it is still the answer to “what did the user prefer in February” or “when did the user’s coffee preference change, and why.”

The agent can now ask things like:

# "What did I think was true six months ago?"
graph.query(domain="memory", kind="preference",
            valid_at=datetime(2026, 1, 15))
# returns n1, with revoked_at present but valid_at < revoked_at

# "When did the user's coffee preference change?"
graph.changes(topic="coffee", since=datetime(2026, 1, 1))
# returns [(n1, revoked_at=..., superseded_by=n2)]

# "Why did it change?"
graph.edge(from=n2, kind="supersedes").body["reason"]
# "user explicit, stomach health"

This is not a database trick. It is the precondition for an agent that can update its model of you over time without losing the previous model. Continuous learning requires the previous model to still be queryable. Otherwise “learning” is just “overwriting.”

Why this matters in practice

Three things break when you do not have invalidation as a first-class graph operation.

Continuous personalization stops being possible. You can re-tune to current preferences, but you cannot answer “what changed about this user in the last six months” because you only have the current state. The agent feels static even when it is updating, because the delta is invisible.

Capability audit chains stop being possible. When a tool was granted in March and used in April and revoked in May, you want a single chain you can follow: grant → use → use → use → revoke. With flat storage, the revoke deletes the grant and you have lost the audit trail. With versioned graph edges, all four events are present and ordered.

Recipe execution that resumes mid-flight stops being possible. Memagen recipes are sequences of steps. When step 4 of 12 fails because a network call timed out, you want to come back tomorrow and resume from step 5. That requires storing not just “the recipe is running” but the full subtree of what has happened in this run, with each step’s inputs and outputs preserved. The graph holds this naturally — recipe_run → step_1 → step_2 → ... with each step a real node — and the resume operation is a single subtree query.

Dream consolidation requires it. Memagen runs a periodic consolidation pass — call it dreaming if you want, the term has been used elsewhere — that reads recent activity and writes summary nodes that abstract over it. The abstraction is the value, but the abstraction is only useful if the original turns are still there to ground it. With versioned edges the summary can derive_from the raw nodes; we keep both, and the retriever can choose granularity at query time.

What you can build with it

If you are building on top of this graph (which you can, if you have Pro Memagen Local — bring your own LLM, run it on your machine, own it forever), the load-bearing patterns:

Continuous-learning agents. Every user message updates the relevant subgraph. Every preference change is a supersedes edge, not an overwrite. The agent’s model of the user gets sharper over time without losing what it used to think.

Capability audit chains. Every grant and revoke is a graph edge with a timestamp. Replaying “what could this agent do, on this date, and what did it actually do with that capability” is a single query. This is the basis for the eight hard invariants in the safety toggle framework — the audit chain is what makes the invariant enforceable retroactively, not just at the moment of action.

Recipes that resume. The recipe-run subtree carries all the state. Crash, sleep, restart, come back tomorrow — the graph still has step 1 through 4 and you start at step 5.

Dream consolidation. The periodic consolidation reads the recent subgraph, writes summary nodes, links them with derived_from, and the retriever uses the summary at low granularity and falls through to the raw nodes at high granularity.

The honest part

This is hard. We spent the year building it. The graph engine is the moat — it is where the actual difficulty lives. Everything else Memagen ships — the harness, the agent loop, the system prompt, the retrieval shim, the tools, the cognition stubs — is around the graph engine, not the engine itself.

That is why Memagen is paid software. The harness around the engine is free — it is V1-lite, MIT-licensed, on GitHub. You can read every line of it. You can fork it. You can ship it as your own product if you want. But the harness without the graph engine is a static-state agent: it works, it does useful things, it does not learn over time because the substrate that lets it learn over time is not there.

If that is fine for what you are building — great. Use V1-lite. We hope you ship something good with it.

If you want the substrate — the graph, the curators, the dual-weights retriever, the cognition modules that read from it — that is what Pro Memagen Local sells. Two hundred dollars, lifetime, your own LLM, your own machine, your own data. Or two hundred dollars a year if you want the Connected version with the relay and the priority bug fixes. Or if you are early enough, the Founder Member program gets you the lifetime version with a hand-shake direct line to me.

The harness is free because the harness is portable. The graph engine is paid because the graph engine is the year of work.

If you want to read the harness, fork it on GitHub. If you want the graph engine, you can buy Pro Memagen Local for $200 lifetime — bring your own LLM, run it on your machine, own it forever.

— Tab Levitas, founder of Memagen™