Skip to main content
This is a Naive → Naive guide. The “vendor” is a previous version of Naive. It is a separate migration from legacy orchestration → durable teams, and it is deliberately not folded into that page — the two have different blast radii, and the difference breaks a real migration on day one. See the warning immediately below.
The memory primitive is how Naive first gave an agent context that outlived one run: POST /v1/memory with some prose, naive memory add --target memory "…", and a MEMORY.md the agent was expected to consult. It is company-scoped, it is on by default, and it keeps working. The brain is a knowledge substrate with content nouns that have a lifecycle (a company may hold several brains, one flagged as the default): a belief is written, cited, reaffirmed, contested, and eventually expires under a stated retention. That is the difference this migration is about — not storage, but whether a stored thing has a stated life.

Read this before you plan the work

memory is on by default. brain is opt-in — and a gated call with no kit entry is a refusal, not a default.brain is declared optIn: true in the primitive registry; memory is not. So the very first thing a migration hits is not a code change: it is that every tenant whose Account Kit has no brain entry gets 403 primitive_disabled_by_kit on the new path while the old path keeps answering 201. Enable brain on the kits before you change a single call site, or the migration will look like a bug in the brain.
POST /v1/memory does not write a fact. Measured on this build: the handler composes the string Please add the following to your memory (target: MEMORY.md):\n\n<your content>, posts it to the legacy runtime’s /ceo/run as a prompt, and answers 201 with { "status": "memory_requested" }. There is a direct table insert, but it is a development-only fallback behind a NODE_ENV !== "development" re-throw.So a 201 from /v1/memory means an agent was asked to remember something, not that anything was stored. If you have ever written a fact through this route and later failed to read it back, that is the reason — and it is the single strongest argument for this migration.

Concept map

memory survives as a wire name. The primitive slug, the /v1/memory path and the table names keep the word. It is retired as a product noun only: one row cannot be both a curated item and a lesson without re-creating the two-vocabulary problem the brain exists to end.

Before / after: the core path

Writing something the agent should know

Reading it back

Forgetting

brain.forget is approval-gated, and the difference is visible only in the response body. A human caller executes immediately (200). An agent caller queues an approval (202) and the erase does not happen — you get an approval id, not a result.The Node SDK’s namespaced brain handle folds on the body discriminator (status: "pending_approval") so the two are distinguishable in TypeScript. The older BrainClient.forget returns Promise<unknown> and discards the status code, so a resolved promise from it reads as “it happened” whether or not it did. If you are migrating deletion logic, use the namespaced handle.

Minimal viable migration

1

Enable the brain primitive on the kits

Nothing else works until this is done, and the failure mode is a 403 that looks like a bug in the new code. Confirm with POST /v1/policy/explain before you change a call site.
2

Dual-write, do not backfill

There is no export from the memory primitive worth migrating: what /v1/memory returns is a mirror of prompts that were requested, not a set of facts that were stored. Start writing the facts you care about into the brain and let the old rows age out.
3

Move reads first

naive brain recall and naive brain think answer against real rows today. Point your read path at them while writes still go both ways.
4

Adopt the run loop when you adopt teams

attach before the work, consolidate after it. This is the half memory never had, and it is worth doing after the read/write swap rather than during it.
5

Leave `naive memory` in place

It is frozen, not removed. Every command and route keeps answering.

Consolidate further once you’re on the brain

Gain #1 — a write has a stated life

A memory row has no retention, no expiry and no reaffirmation. A brain belief has all three, and retention is declared per content noun in naive.config.ts — narrowable per level, and bounded by a 365-day ceiling that is not configurable. A declaration that tries to widen retention at a lower level throws at define time and names both levels.

Gain #2 — a model-authored claim waits for a human

Anything a run wants the brain to record arrives as a writeback envelope and becomes a proposal. POST …/proposals/{id}/accept canonises it; reject, merge and quarantine are the other three outcomes. From an agent, accept parks for a human and reject applies immediately with the caller recorded — that asymmetry is deliberate, not an oversight: promotion into the company’s source of truth needs a human who is not the author. Inspect the queue without leaving the terminal:

Gain #3 — the run loop

attach is a read capsule assembled before work starts; consolidate is the run-end debrief that produces the writeback. Neither has an equivalent in the memory primitive, and together they are the reason the brain is a substrate rather than a store.

What does not map yet

Measured against this build. Each of these answers 501 not_configured and names its missing dependency — it does not return an empty list, which would be a claim about your tenant:
  • POST /v1/brain/beliefs/{id}/reaffirm — reaffirming a belief.
  • Lessons. There is no lessons surface: both routes refuse, and there is no naive brain lessons command. Use naive brain recall or naive brain think for the read.
  • Retention as a readable surface (/v1/brain/retention).
  • Decisions (/v1/brain/decisions).
Also absent, for different reasons:
  • propose and learn as client verbs. They are designed but have no route on this build, so the SDK omits them rather than shipping guaranteed 404s.
  • Eight of the designed naive brain subcommandsbeliefs, belief, reaffirm, lessons, learn, retention, levels, decisions — are not shipped, for the same reason.
  • An agent level. The brain has a company level and partitions; there is no per-agent level on any brain_* table. Agent-level context is the agent’s own memory namespace in the in-agent SDK (self.memory), not a brain lane.
  • GET /v1/brain/metrics is process-global. It sits behind a company auth gate but returns a body with no company filter, so treat its counters as deployment-wide, not yours. naive brain metrics says so on the command.
If you are reading a cached copy of the deprecation notice. Until recently the REST deprecation register named naive brain remember / naive brain lessons / naive brain forget as the replacement for naive memory, and that text rode the X-Naive-Replacement header and the orchestration API reference. naive brain lessons never existed. The register now names only the two commands that work; a response you captured earlier may still carry the old string.

Where to go next