Memory and long conversations

A model's context window cannot hold an entire conversation or every note about a person. Polychrome provides conversation-history tools and adds persona notes according to their scope and the current audience.

The conversation's own history is its signed log. The agent searches that history with a tool when earlier messages leave the context window. A person's notes are facts reconstructed from their memory journal. Turn assembly selects these notes and adds them to a system message without search. The scope predicate determines which notes the audience may receive.

Memory scope and audience. Direct memory is eligible only when the memory's subject alone reads the agent's output. Shared or unknown audiences cannot receive direct memory. Portable memory is eligible for every audience. Unspecified scope follows the same private rule as direct memory. Eligibility does not depend on where the memory was learned.you alonea shared audienceaudience unknownDirect (private)eligiblehiddenhiddenPortable (non-private)eligibleeligibleeligible

Scope determines whether memory is eligible, regardless of its relevance. Direct and unspecified scopes require proof that you alone read the output. Portable memory is eligible in any conversation.

Conversation history and persona notes

What it holdsWhere it livesHow it reaches a turnWho can see it
This conversation's own historythe conversation's own append-only signed logthe agent asks for it mid-turn, with a read-only toolthis conversation and nothing else
Notes about a personthat person's own append-only memory journal, one per person, held by the state planeprepended once at turn assembly as one system block, never searchedonly where the scope rule permits

History tools let the agent retrieve earlier messages beyond its current context window. Persona-note selection reads the memory journal separately from conversation-history search.

Scope determines who may receive a note

Scope determines eligibility before ranking. Direct notes are available only when the subject is the sole human audience member. Unspecified notes follow the same restriction. Portable notes are available to every audience because the person chose to make them non-private. The conversation where a note was learned does not determine its read scope.

The read audience determines whether private notes are eligible. A shared conversation does not become private because only one person speaks during a turn. The scope predicate checks for ReadAudience::SubjectAlone.

CI restricts access to unscoped notes

Reading notes without scope filtering requires an explicit infrastructure accessor. Five files may call it: the fold, extraction, merge, memory-control RPCs, and forensics. A build check rejects callers outside the allowlist. Prompt-facing reads use scope-filtered selection. See the fold.

How a note gets written

Notes come from post-turn extraction or an approved tool call.

After the turn. A separate extraction pass runs after the turn commits, without delaying its reply. It replays only committed events and skips turns already processed, including turns that produced no notes. The pass asks a model for new notes and contradictions against at most 24 ranked active notes. Before writing, it rechecks whether the conversation was erased or changed to incognito. Extraction runs serially for each person to avoid concurrent writes based on stale notes. See the extraction pass.

During the turn. The agent can request a note write through a tool that requires human approval. The turn's attribution supplies the person and source conversation; tool arguments cannot select another person. Neither extraction nor explicit writes record notes from an incognito conversation.

Contradictions preserve note history

A contradiction closes the old note's validity interval and preserves both notes for replay. A restatement adds a source to the existing note. The fold starts with the extractor's initial confidence and adds 500 basis points per independent corroborator, capped at 10,000. Replay therefore reproduces the same confidence value. Merging preserves scope boundaries so private and portable notes do not combine. See the merge pass.

Periodic maintenance

A periodic pass rebuilds the profile document from active notes and removes exact duplicates, retaining the oldest. It retires transient notes after four hours. Notes marked as lasting do not expire based on age.

What enters model context

Turn assembly adds one system message without persisting it in the transcript. The block has a budget of about 1,200 characters. It prioritizes language, tone, sorted preferences, notes, and the recent-conversation digest, in that order. When space runs out, it drops digest content first, then notes, before profile content. The selection includes at most 12 notes and 5 digest lines.

The block reserves space for the notes' explanatory caveat. If the heading and caveat cannot fit, it omits the notes. An observational heading separates notes from standing preferences. When the audience cannot receive private memory, a separate notice always states that restriction, regardless of whether private notes exist. See how the block is assembled.

Looking back in this conversation

The agent can search earlier messages in the current conversation and read matching turns in full.

YAML
# conversation_find: find matching earlier messages
{ "query": "the deploy plan we agreed on", "limit": 5 }
# Returns matching messages from this conversation, newest first
{ "matches": [ { "turn_id": "abc…", "excerpt": "…let's ship it behind a flag…" } ],
  "truncated": false }

# conversation_find, pattern mode: match literal text or a regular expression
{ "query": "INC-\\d+", "mode": "pattern" }
# Returns matching messages, newest first, with excerpts around each match

# No matches returns an empty result
{ "matches": [], "truncated": false,
  "hint": "nothing in this conversation matched; try fewer or different words, or switch mode to see what is there another way" }

# conversation_read_turn: read a matching turn
{ "turn_id": "abc…" }
# Returns turn text; long turns have their middle section truncated
{ "turn_id": "abc…", "text": "…", "truncated": false }
ToolWhat it answersBounds
conversation_findreturns matching messages from this conversation, each with a turn id and excerptrelevance or pattern mode; 5 matches by default, 25 at most; truncated and a hint when more matched
conversation_read_turnreads one earlier turn in full, by its idlong turns have their middle section truncated and report truncation
conversation_read_tool_resultreturns a recorded tool result by call idexcised results return a removal notice without the original content
conversation_recent_turnsreturns recent turns and their ids
conversation_list_tool_callsreturns recorded tool calls and their ids

Search ranks messages by the number of matching words, with newer messages first when scores tie. It uses lexical matching, without a vector store. No matches returns an empty result with a suggestion. An unknown turn id returns an error. See the navigator.

Reads stay within the current conversation

The control plane binds history reads to the current conversation. The tools accept no conversation argument and ignore scope fields supplied by the model. Recall similarly uses the turn's attribution to select the person. Search excludes content an admin has excised. Reading an excised result returns a removal notice. See how removal works.

Recall uses the same selection rules

The recall tool uses the same fold, scope rule, and ranking as turn assembly. It returns 12 notes by default, at most 25, plus 5 digest lines. Recall takes no search query. See the recall module.

Empty notes and failed reads

If turn assembly cannot read notes, the turn proceeds without them. Recall distinguishes an empty store from a failed read:

StateRecall response
No notes selected in a conversation where private memory is readable"nothing has been kept about this person yet — memory_write is how a note gets here"
The audience cannot read private notesRecall reports the audience restriction without revealing whether private notes exist.
The notes could not be read this time"your notes aren't readable right now, so nothing was recalled; carry on from what this conversation already tells you — the rest of the turn works normally"

A failed read does not establish that no notes exist. The recall module defines both response strings.

Inspect, edit, and erase notes

Listing and deleting notes use the person resolved from the request and accept no alternative target. Deleting a note closes its validity interval while retaining the audit record. Erasing a person deletes their entire memory store.

Deleting a conversation removes notes whose text originated there. Other conversations that corroborated a note cannot preserve its text after deletion of the source conversation. Incognito conversations produce no notes. See Personas.