Memory

Keep durable, searchable context attached to the right member and organization across agent clients.

Memory preserves the part of an agent setup that should outlive a client or conversation. A user can move from Claude Code to Codex—or replace the agent entirely—without rebuilding stable preferences, facts, and project decisions.

The organization owns its memory. Each write is scoped as shared or private inside that organization, and CoreSpeed derives the caller identity from auth; the agent cannot supply a different user ID to cross the boundary. The boundary is enforced in Postgres by row-level security, not only in application code.

Tools

Names below are the operations exposed on the unified MCP surface:

ToolUse it for
memory__rememberSave one concise fact, preference, or decision.
memory__searchFind memories relevant to the current task.
memory__getRead one memory in full by ID.
memory__ingestSave recent messages as memories, one memory per message (at most 4,000 characters each).
memory__updateCorrect an existing memory by ID — or, for a teammate's, propose the correction.
memory__deleteRemove one of your own memories.
memory__listList memories newest first, a page at a time.
memory__clear_allDelete all memory after explicit confirmation.
memory__linkLink one of your memories to another one you can see.
memory__unlinkRemove one of those links.
memory__list_linksList a memory's links, outbound or inbound.
memory__archiveRetire a stale memory, naming the one that supersedes it.
memory__restoreBring an archived memory back.
memory__list_proposalsList proposed changes to memories you can see.
memory__withdraw_proposalTake back a proposal you made.

These fifteen are the whole public surface — eleven for an organization without memory maintenance, which is served neither the last four nor the proposal path of memory__update (see Keeping shared memory accurate). Retrieval underneath them is hybrid — exact and relaxed lexical matching, entity aliases, and vector similarity, fused into one ranking — but it is reached through memory__search rather than exposed as separate knobs.

Long memories come back as excerpts

memory__search and memory__list return many memories at once, so they bound how much text each one contributes. A memory of 1,200 characters or fewer — the overwhelming majority — is returned whole. A longer one is returned as an excerpt, marked in the result:

{
  "id": "0b5f…",
  "memory": "Escalation runbook: page the on-call rotation first…",
  "truncated": true,
  "full_length": 5310
}

For a search hit the excerpt is the passage that actually matched the query, not the head of the memory — the same chunk the ranking scored. list_memory has no query to be relevant to, so it excerpts from the start.

Pass the id to memory__get to read the whole thing. That read follows normal visibility: your own private memories plus the organization's shared ones, whoever wrote them.

A whole result is bounded too, not just each row in it. If the memories that matched would together exceed what a client can receive, the lowest-ranked ones are dropped and the result says how many:

{ "memories": [ /* … */ ], "omitted": 29 }

The top hit is always returned, however large. In practice the default limit of 10 never reaches the budget — only an explicitly high limit does. Narrow the query or lower limit to see what was dropped.

Shared and private context

memory__remember accepts scope: "shared" | "private":

  • private is the default and can be read only by the member who created it.
  • shared must be asked for explicitly, and can then be read by agents operating inside the same organization boundary.

Member state is always organization-scoped. Turning memory off in one organization cannot affect the same user in another organization.

A link is a durable, directed edge from a memory you own to any memory you can see, named by its two ids and a kind — "related" unless you choose another, such as "depends-on". "supersedes" is reserved: archiving writes it. The related_ids argument of memory__remember, memory__ingest, and memory__update writes related links; memory__link writes any kind, with an optional weight from 0 to 1.

Linking is safe to repeat: the same call again changes nothing, and a call with a new weight replaces the old one. memory__unlink removes exactly the link its key names. memory__list_links reads a memory's links back, newest first, and lists only links whose two memories you can see — a link to a teammate's private memory stays invisible to you, as that memory does.

The graph in Dashboard → Memory draws every link. A memory can hold up to 1,000 outgoing links and 16 kinds toward any one other memory; past a bound the call answers link_capacity_exceeded.

Keeping shared memory accurate

Memory maintenance — archive and restore, proposals, and version history — is rolling out organization by organization. Until yours has it, the archive, restore, and proposal tools are not in tools/list, a teammate's memory is changed only by its author, and the dashboard shows none of it.

A shared memory is read by every agent in the organization, so a stale one misleads all of them — and a correction saved next to it does not stop the old one being retrieved. Retire it instead:

  • Archive. memory__archive takes the memory out of search, lists, and the memory index every session starts with. superseded_by names the memory that replaces it; reading the archived one by id says so. Nothing is deleted, and memory__restore brings it back.
  • Your own memories are archived, restored, or corrected at once. So are an agent's memories for the member accountable for that agent.
  • A teammate's memory changes through a proposal. memory__update, memory__archive, and memory__restore on it take a reason and answer status: "proposed". The memory's author — or an org admin — merges or rejects it in Dashboard → Memory → Proposals, like a pull request. An agent can propose but never merge.
  • Needs my review holds only what others proposed. Your own pending proposals stay under Mine. On a wide screen, j and k move between proposals, and m and r open the focused one's Merge or Reject.
  • A proposal is written against the memory's current version. If the memory changes before review, merging records the proposal as outdated and applies nothing; nothing is ever rebased.

Every change keeps the memory's three most recent prior versions, with who replaced each; the memory's page lists them and can restore one. Pending proposals expire after 30 days, and resolved ones leave after another 30.

Pricing and holds

Saving costs 1 credit per memory, and one memory holds at most 4,000 characters: save longer text as several memories. That covers each memory memory__remember saves, each memory memory__ingest saves, and a memory__update that changes a memory's text. Everything else costs nothing: search, reads and lists, an update that changes only the pin, scope, category, tags or links, proposals, and delete, archive, restore and link. Every charge appears in the activity log. An update that repeats text already saved is not charged again.

When the organization runs out of credit, or an API key reaches its monthly spend cap, every memory call is refused with payment_required or key_spend_limit_exceeded, reads included, until credit is added or the month rolls over. A suspended organization is refused everything. Storage quotas and rate limits protect the shared service.

Inspect and remove memory

Use Dashboard → Memory to browse memories, search, inspect the graph, open a detail view, and delete content — and, with memory maintenance, archive memories and review proposals. Disabling the memory capability hides its MCP tools but preserves existing data; deletion is a separate explicit action.

What belongs in memory

Save context that will improve a later action: a preference, a product decision, a named relationship, or an operating constraint. Do not use memory as a transcript archive, a secret store, or a substitute for application data.