Decisions

Why Link Memory is named, writes, indexes, is sandboxed and searches as it does.

What Link Memory is called, what it writes, and whose conversations it remembers.

Link Memory, the plain word, no brand. You type lnk agent memory start link; its program is lnk-link-memory and its plugin link-memory, mapped the way Link Harness's is (MEMORIES in the harness plugin).

How does your agent reach its memory?

As a tool outside its sandbox, through the bridge every tool uses (lnk sandbox tool add), at $LNK_MEMORY: one way in for every harness, the calls logged and gated by the proxy. The tool is the agent's own, memory-<agent id>, defined again at a start when its settings, the engine or Link changed since, or the tool is gone, so it follows the agent to another machine and a new release of the engine. The harness gets one variable, whatever the agent's name.

One file only: the agent's notes, memory/notes.md, through remember. Its sandbox writes the folder Agent/memory/, not the file alone, since a file is replaced whole only by a rename, which takes its folder. Everything else it searches, reads and lists, and never changes.

Why traverse a parent without reading it?

The agent's private parent folder is opened for traversal without reading its entries. Notes and chat copies are opened through their own granted child folders, so saving notes needs no read access to the rest of Agent/.

Its notes

The notes are what an agent keeps in mind in every conversation, so they're in its prompt, and a tool writes them rather than the harness, so any harness with Link Memory takes notes the same way. They're one of its instructions, so the sandbox file decides as it does for the rest: where [files] keeps instructions/ read only, Link names no notes file to the engine, which then offers no remember.

Never past its size

remember doesn't ask you first, as the agent's own edits to its instructions don't, and it never writes past the spec's [tool.memory] notes, 4 KB by Link's, so the notes stay a small part of every prompt. There are two keys for the notes: [tool.memory] notes, what remember writes up to, and [instructions.max] notes, what the prompt holds, Link's 4 KB each. That is about 1,000 tokens in every prompt, and a file edited by hand past the first is still cut by the second.

Whose conversations can have the agent remember?

The owner's alone, where the harness is sent others' messages: a teammate's, a team's, a stranger's and a subagent's conversations get every tool but remember, so nobody else has the agent note something for every conversation after. A chat the owner shared (lnk agent share) counts as the owner's: its members can make the agent do all it can, its memory included, and an owner who wants less shared makes a second agent. A call that names no conversation is refused, as one can't tell whose it is.

How are an agent's notes kept?

As a markdown file, a line a note, which you read and edit as any other. remember adds a line, or changes or forgets the one line holding a piece of text it's given, so the model never rewrites the whole file and loses a note by accident. It writes the file whole in one step, through its folder's handle and never through a link, one writer at a time.

Only what's true of any folder of text: search by words and meaning, filters, what changed since, links, a size limit. It assumes no kind of note (people, projects, facts), runs no model over the notes, and imports nothing. An engine built around one way of working plugs in by the engine contract instead.

What does a memory engine's contract fix?

Only how the engine starts: install, and command <folder> <state> printing the command that serves MCP over stdio and which calls don't ask you first (link_memory_contract), with the agent's notes file in its environment where it may write it. The bridge passes each server's tools through, so no engine's names are translated, and a harness reads each tool's description. Engines compete on what they serve, not on a shape Link picked.

What memory indexes

Which folder memory reads, and how its index is kept.

Which folder does memory read?

The agent's own: its Home, and its conversations in its own folder. One folder shared by several agents would be a new decision about the layout, and agents are kept apart everywhere else.

Why does memory index only memory/ and the conversations?

So what's searchable is what you put there, the same on every machine, and a move never changes it. Indexing the whole files folder found drafts, scratch files and a repository's code among your documents, and paid to embed them. memory/ is a folder of the layout for it; everything else is still read by file, and a skill by its path through memory's read.

Why does memory read PDFs and Word documents?

Because the documents people keep are often PDFs: a tax report, a lease. Memory extracts their text for its index, on every machine; the file stays as it is, and file still reads text only. A document over 32 MiB, or text past 2 MiB, is left out rather than half read. Their parsers run in a child process, memory's own program again, given no environment, no key and limits: the process serving memory holds every bound agent's key, and a parser is where a crafted file would get in.

Where are conversations in memory's folder?

In chats/, where Link Harness keeps them, a folder per conversation, its conversation.md the one indexed, never the event files. A conversation kept compact or by a store driver has none on disk: memory makes the same copy from its events, at the same path. Memory follows the harness, and an end-to-end test holds the two together. A search says which results are conversations, so a document isn't lost among the chats about it.

Where is memory's index kept?

The index lives in the agent's memory collection at Agent/memory/<engine>, outside the harness's reach. It follows the agent's folder and is included when lnk uninstall removes agents.

How is an index kept in a bucket?

The index is split into 8 MiB segments named by their SHA-256, with a manifest written last (link_store_contract::files::index). A write succeeds only against the version it read. Changed segments are uploaded, and a concurrent writer receives a refusal and reads again, preserving both writers' changes.

Memory's sandbox

Why one process serves every agent's memory, and what its sandbox reaches.

Why does one process serve every agent's memory?

A session of memory on its own is a sandbox, the engine and its model server: about 37 MB and six processes, started for each agent, and again after it's been idle. Many agents on one machine would pay that many times over, for one program reading folders.

One engine for an owner

So an engine that can serve many folders (Link Memory) runs once for an owner's agents, started by the first session that needs it and ending a few seconds after its last, and each session is a small process that passes its messages on. Each session is answered from its own agent's folder, named by Link outside every sandbox, never by the agent.

Told which agent

A team's shared tool runs one server for every member's session, as is, which an agent's memory can't: each session reads a different agent's folder, so the engine itself is told which agent each session is. One owner's agents share it, never two owners': the folders its sandbox holds together are all one person's, and no agent reaches the process but through the session Link opened for it.

Why does the memory engine run in a sandbox too?

It runs outside the agent's sandbox, as every tool does, but it needs nothing a tool usually runs outside for: no logins, no desktop. So it gets the least: it reads Home, the agent's conversations and skills in its own folder, and its program, and writes its state folder and, for the notes, Agent/memory/. Link Memory already reads and writes nothing else; the sandbox makes that true of any engine. It writes the folder its notes are in, not the file alone: a file is replaced whole by renaming a new one over it, which takes its folder.

Why is memory's model served outside its sandbox?

Link Memory searches by meaning with a model lnk model serve serves, and running that from inside would need lnk, the models plugin and what they run, so its sandbox read the whole folder its program is in.

Started outside

Instead Link starts the model server for it outside, once for the service its owner's agents share, and gives it the address, as a harness is given its tools'. The address is a port chosen before the server starts, so a runtime that comes up late is found there, its server started again until it does, without the engine started again. Its sandbox reads its own program and nothing beside it.

To reach its embedding model on this machine: on Linux, without the network a sandbox reaches none of this machine's ports. The other way, the proxy's filtered path to a model, would give the engine a proxy of its own for one port. It still reads only its folder, and writes only its state and its notes.

How memory searches by meaning, ranks, merges and keeps its vectors, and how much a call gives.

How does memory search by meaning?

With an embedding model on your machine, served by lnk model serve, so memory stays keyless and free. A hosted model through the proxy would be one more source, but memory is kept keyless. Each document is split at its headings, then at blank lines, into chunks of up to about 1,500 characters, each embedded with its document's title in front.

How does memory rank what it finds?

By its words (SQLite's bm25, the title counting four times the body) and by its meaning, the two lists merged by their ranks. Within each list, documents within a tenth of the best score around them match about as well, and the newest of them comes first: a newer note beats an older one saying nearly the same, never one that matches clearly better. A title from a file's name is embedded as its own chunk, so it counts by meaning as it does by words.

Copies

A copy (all the same words, or, from twenty words, nine in ten of them) comes once, the others named in its copies, the shorter path kept. A test of sixteen questions over a fixed folder (tests/quality.rs) holds how many find their document first, so a change can't make search worse unnoticed.

How do words and meaning merge?

Reciprocal rank fusion: each ranks its best 50 documents, and a document scores the sum of 1 / (60 + its rank) in each. There are no scores to calibrate between the two, and a new model needs no retuning. There is one search, no mode to pick: matched says why each hit came back.

Where are the vectors kept and compared?

In the same SQLite index, a blob of numbers per chunk with the model's name, compared in Rust: 100,000 chunks of 1,024 numbers is well under a second, with no new dependency. Comparing is one function, so another store can replace it.

Kept between searches

A server keeps the vectors in memory between searches. The index counts every change to its chunks, by triggers in the file, so a change by the embedding thread or another server over the same file counts too, and the vectors are read again whenever the count moved: a changed or deleted chunk's old vector is never used.

Why does embedding run in the background?

So the first index doesn't hold up a search: what's embedded is searched by meaning, the rest by words, and the reply says how far it got. It needs the index in a file, which the embedding thread opens too, and which keeps each vector from one session to the next, so only what changed is embedded again.

Which embedding model does memory ask for?

qwen3-embedding, Apache-2.0 and many languages, over the smaller nomic-embed-text. A model under terms that aren't permissive isn't asked for.

Another model

The agent's spec can name another, or none to search by words only. A spec that names something that isn't a model's name is refused, so a typo never runs another model than asked.

How does an agent catch up on what changed?

list {since}, with since on search too: what was changed or first seen after a time, each new or changed, and what was removed. The index notes when it first saw each document, so a file copied in with an old date is new all the same, and keeps the paths and titles of what was deleted, the latest 10,000.

What was there before

What was in the folder when memory first read it counts as there before, not new, so turning memory on doesn't make everything news. since takes a date, since a model writes one more reliably than a count of seconds, or the seconds list gives. It holds for any folder of text, as everything Link Memory adds does.

How much does a memory call give?

At most about 6,000 tokens, four characters each, unless the call asks for another size: under the 32 KiB Link Harness keeps of a tool's answer, so an answer arrives whole rather than cut mid-way. search gives whole results and read whole lines, at least one, and each says where it stopped (next), so the next call carries on from there rather than starting again.

What's cut

Only a single line longer than the size is cut, and the read says so (cut). list and links keep the same size, leaving out whole entries that don't fit, and a title is at most 200 characters, so no one document fills an answer.

links {path} gives what a document links to and what links to it, from markdown links and wiki links, the two ways notes link in a folder of text. The index keeps each link as written, resolved against the folder, and matches it to documents when asked, so a link to a note made later starts working without reindexing.

A wiki link names a document by its path or its file's name; several of that name, the shortest path, as notes apps do. Nothing is followed on disk: a link only names an indexed document, and one to the web or out of the folder is no link. It is a fourth read-only call, so it never asks you first. A document keeps its first 1,000 links, and none longer than 512 bytes.