# Linking memories: a small graph that keeps decisions connected (/blog/a-memory-graph-with-links)

![Illustration: A small friendly robot with a round camera-eye head ties short lengths of string between blank index cards pinned to a cork board, like a detective's board, with one card at the center and a few cards around it.](/blog/a-memory-graph-with-links/hero.webp)

A memory link is a durable, directed edge from a memory you own to any memory you can see. Each link has a `kind`, `related` by default, and an optional `weight` from 0 to 1. Writing the same link twice changes nothing. A memory can hold up to 1,000 outgoing links, and the dashboard draws the graph.

**A directed edge with a kind and a weight**

What writes an edge:

* memory\_\_remember — related\_ids at save time
* memory\_\_link\_memories — any kind, optional weight 0 to 1
* memory\_\_archive\_memory — writes supersedes only

All through **A memory you own** (up to 1,000 outgoing links).

Where it can point:

* Your own memory — a decision and its constraint
* A teammate's shared memory — any memory you can see
* A teammate's private memory — invisible to you, so no edge

A link runs from a memory you own to any memory you can see.

## What is a memory link? \[#what-is-a-memory-link]

Memories are saved one at a time: a preference, a decision, a constraint, a named relationship. The useful ones depend on each other. A release-day decision rests on a customer constraint. A preference belongs to a person. A runbook applies to one service. A link records that dependency as data instead of leaving it in the prose of each memory.

A link is named by its two ids and a kind. It runs from a memory you own to any memory you can see, so you can point your own memory at a teammate's shared one, and you cannot attach edges to a memory that is not yours. The `related_ids` argument of `memory__remember`, `memory__ingest` and `memory__update_memory` writes `related` links at save time. `memory__link_memories` writes any kind, such as `depends-on`, with an optional weight.

| Field                         | Values                                         | Notes                                   |
| ----------------------------- | ---------------------------------------------- | --------------------------------------- |
| direction                     | from a memory you own, to a memory you can see | one edge, one direction                 |
| `kind`                        | `related` by default, or any name you choose   | `supersedes` is reserved                |
| `weight`                      | 0 to 1, optional                               | a new weight replaces the old one       |
| outgoing links per memory     | up to 1,000                                    | `link_capacity_exceeded` past the bound |
| kinds toward one other memory | up to 16                                       | the same bound applies                  |

One kind is reserved. `supersedes` is written by `memory__archive_memory` when a memory is retired in favor of another, and only by it. The [memory page](/docs/memory) documents every field.

Writing a link is idempotent, and that is deliberate. Agents retry. A client times out, a session restarts, the same instruction runs twice. If a link call created a new edge each time, the graph would fill with duplicates that mean nothing and cost capacity.

So the same call again changes nothing. A call with a new weight replaces the old weight on the same edge. `memory__unlink_memories` removes exactly the link its key names, and no other. An agent can link at the end of every session without checking first whether it already did.

## Who can see a link? \[#who-can-see-a-link]

A link is visible only when both of its memories are. `memory__list_memory_links` reads a memory's links back, newest first, outbound or inbound, 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.

This is the same boundary memory itself has, applied to the edges. Identity comes from the credential on the request, every memory carries a scope, and the database enforces it. A link cannot leak the existence of a private memory, because the link is filtered by the same rule as the row.

## What are the limits? \[#what-are-the-limits]

A memory can hold up to 1,000 outgoing links and up to 16 kinds toward any one other memory. Past either bound the call answers an `isError` result:

```json
{ "error": { "code": "link_capacity_exceeded" } }
```

Treat it like the other tool-level codes in [the errors reference](/docs/errors): branch on `structuredContent.error.code`, and do not retry, because the bound does not move. Unlink something, or link from a different memory.

The bound is generous for the intended use. A memory with a thousand outgoing edges is a hub, and a hub usually means something is being used as an index that should be a search instead.

## When should you link? \[#when-should-you-link]

Link when a memory is only correct in the light of another one. A decision and the constraint behind it. A preference and the person it belongs to. An operating rule and the service it covers. These are the categories the docs say belong in memory in the first place, and the link is what lets an agent that found one of them find the reason for it.

Do not link to build an archive. Memory is for context that improves a later action, and the graph serves that. Transcripts, application data and secrets do not belong in memory, and linking them does not change that.

Two patterns work well:

* At save time, pass `related_ids` so a new decision points at the constraint or preference it depends on.
* When a memory is retired, let archive write the `supersedes` link, so an agent holding the old id follows the pointer forward.

## How does the dashboard show it? \[#how-does-the-dashboard-show-it]

Dashboard → Memory draws every link you can see. The graph view is where a person reads what the agents have been connecting: which decisions cluster around one constraint, which memory has become a hub, which old memory still has a `supersedes` pointer to follow. The same page opens a memory's detail view, where you can inspect and delete content.

Memory operations, links included, cost 0 credits per call. The graph is part of the org-owned memory described in the [Introducing CoreSpeed](/blog/introducing-corespeed) post: it belongs to the organization, and every agent you sign in works from the same one.

## FAQ \[#faq]

**Can I link to a teammate's memory?**
Yes, if you can see it, which means a shared one. The link runs from a memory you own. You cannot attach a link to a memory you do not own.

**What happens if my agent links the same two memories twice?**
Nothing. The same call again is a no-op, and a call with a new weight only updates the weight.

**Can I write a `supersedes` link myself?**
No. `supersedes` is reserved for `memory__archive_memory`, which writes it when a memory is retired with a named successor.

**What does `link_capacity_exceeded` mean?**
The memory already has 1,000 outgoing links, or 16 kinds toward the same target. Remove a link or link from another memory; retrying does not help.

**Do links show in search results?**
Search returns memories. Read a memory's links with `memory__list_memory_links`, or look at the graph in Dashboard → Memory.