Project memory
How OpenOrc turns finished runs into short project facts, stores and ranks them, and gives them to later runs.
Checked against the source on September 27, 2026.
What a memory is
A memory is a short fact about a project that is worth knowing in later work: a decision and its reason, an approach that failed and why, a command that works, an environment quirk, a convention, or a preference. Each has a type, a title, a body, a confidence between 0.1 and 1, the files it concerns, and where it came from: you, an agent, or extraction from a finished run.
Memories belong to a project and are stored in the memories table. All Workspace conversations share one memory pool, whatever folder they use. After each extraction, OpenOrc also stores a summary of the run in session_summaries: what was asked, what was done, the outcome, and open items.
When extraction runs
Extraction runs when an agent process exits, not after every message. A conversation's process stays open between turns and closes after it has been idle (10 minutes by default), so a conversation is usually distilled once per working session. Team assignment runs close after every turn, so each of their turns is distilled.
- A conversation run must have finished at least one turn; runs that belong to a task, such as team assignments, are queued either way. Task discussion runs are never distilled.
- Memory must be on. It is off in a new profile until you turn it on. Extraction must not be turned off in settings.
- Runs that end because you quit OpenOrc are skipped.
Jobs wait in an in-memory queue, and at most 2 run at once, so extraction does not compete with your agents. Quitting OpenOrc discards the jobs still waiting. There is no retry: a failed job is recorded in extraction_jobs and skipped. If memory or extraction is turned off while a job waits, the job is skipped.
Source: packages/core/src/services/memory.ts, packages/core/src/services/runs.ts (onExit)
What extraction reads
Extraction reads the newest 5,000 of the run's message, tool, and file-change events from the ledger, which holds them redacted. If that window no longer includes your first message, the first message is added back so the original request is not lost. The events become a digest:
- User asked: every message you sent.
- Files changed: the first 60 changed paths.
- Tool activity: the last 60 tool calls, one line each with the tool name, whether it failed, and the first 160 characters of its output. Tool inputs, such as the exact commands, are not included.
- Agent said: the last 6 messages that are not from you, which can include notices OpenOrc added to the conversation.
The digest is cut at 12,000 characters. It is sent with the task or conversation title, the task's spec when there is one, and this prompt:
You distill a finished coding-agent run into durable memory for future runs on the same project.
Return ONLY minified JSON, no prose, no code fences, matching exactly:
{"summary":{"request":string,"workDone":string,"outcome":string,"openItems":string[]},
"memories":[{"type":"decision|spec|lesson|preference|convention|command|env_quirk|ownership","title":string,"body":string,"topicKey":string|null,"files":string[],"confidence":number}]}
Rules:
- Record only what helps a future run: failed approaches with the cause, decisions with rationale, commands that worked, environment quirks, conventions, durable preferences. Skip anything obvious from reading the code.
- title <= 80 chars, imperative. body <= 400 chars, concrete.
- topicKey groups memories that supersede each other, like "test/pool" or "architecture/auth"; null if none.
- confidence 0.3-0.9: higher when the run proved it, lower when inferred.
- 0 to 6 memories. Prefer none over noise.
Project task: <task or conversation title>
Spec: <task spec, when the run belongs to a task>
Run to distill:
## User asked
## Files changed
## Tool activity
## Agent saidSource: packages/memory/src/transcript.ts, extractor.ts
How extraction runs
OpenOrc has no language model of its own, so it runs one of your agent CLIs once, in the system's temporary folder:
- Claude Code:
claude -p <prompt> --model <model> --output-format json --max-turns 1 --strict-mcp-config --disallowed-tools Bash Edit Write WebFetch WebSearch, with a 180-second timeout: a single turn, without shell, write, or web tools, and without your MCP servers. The default model is Claude Haiku 4.5. When the Anthropic API key is the chosen provider, the same command runs with that key inANTHROPIC_API_KEY. - Codex:
codex exec --model <model> --skip-git-repo-check --sandbox read-only -c mcp_servers={} --output-schema <schema> -o <file> -, with the prompt on stdin, in a read-only sandbox without MCP servers. The JSON schema makes Codex return valid JSON. The default model is the first one whose name contains spark, mini, or nano, or your account's default model if none does.
Which agent summarizes a run depends on the extraction setting:
- Automatic, the default, keeps each run with the agent that did the work: Claude Code runs are summarized by Claude Code, and Codex runs by Codex, each with its default model above. OpenCode cannot run extraction yet, so OpenCode runs are not summarized. Extraction does not send a run's content to another provider this way, but the memories it saves reach every agent that later works in the project, through the brief and memory search.
- A chosen provider, or the Anthropic API key, summarizes every run, whichever agent did the work. You can also pick its model.
- Off stops extraction while keeping memory itself on, so agents can still search and save memories.
The memory settings show, in a sentence, which agent and model summarize runs under the current choice. The digest goes to that provider under your account's terms.
Source: packages/memory/src/text-generator.ts, packages/core/src/services/memory.ts
Saving and merging
Titles, bodies, and summaries are redacted before they are saved, whoever wrote them, because they return in later prompts. OpenOrc parses the reply leniently: it strips code fences and reads from the first { to the last }. It keeps up to 6 memories, turns unknown types into lessons, cuts titles at 120 characters and bodies at 800, and clamps confidence to 0.1 to 1 (0.6 when missing). The summary and the memories are saved in one transaction.
Memories with the same topicKey in a project merge: the newer title and body replace the older ones, confidence rises by 0.1 (up to 1), the evidence count goes up, and the age resets to now. A unique index allows one active memory per topic key. Memories without a topic key are never merged, and only you can replace a memory you wrote.
Feedback changes a memory's standing: helpful raises confidence by 0.1 and resets its age; wrong marks it retracted; stale marks it stale. Only active memories are searched and briefed.
Source: packages/memory/src/extractor.ts, packages/db/src/memories.ts
Embeddings
For search by meaning, every memory gets a 384-dimension vector from the all-MiniLM-L6-v2 model, computed on your CPU with ONNX Runtime through the fastembed package. The model runs in a worker thread, so loading it never blocks the core. OpenOrc embeds the title and body together, in batches of 32, and stores vectors in a memory_vec table from the sqlite-vec extension.
The model is not part of the app. The first time memory needs a vector, OpenOrc downloads fastembed's 83 MB archive of it from storage.googleapis.com. The download must succeed, must not be larger than the archive, and must match a SHA-256 written into OpenOrc's source; otherwise nothing is kept. fastembed then unpacks it into models/ in the profile folder. The model file matches the checksum Hugging Face publishes for Qdrant/all-MiniLM-L6-v2-onnx.
Every time the model loads, OpenOrc checks each of its files against a pinned SHA-256 again. A folder that does not match, such as one a failed download left behind, is deleted and downloaded again. OpenOrc replaced fastembed's native tokenizer with a JavaScript one through a patch, documented in docs/tokenizer-replacement.md.
While memory is off, nothing is embedded, and the Memory page searches by words only, so browsing your memories never downloads the model. If the model or the extension is unavailable, memory keeps working with full-text search only.
Source: packages/memory/src/model-files.ts, embedder.ts, worker-embedder.ts, packages/db/src/database.ts
Retrieval
A search combines two candidate lists of up to 40 memories each:
- Full text: an FTS5 query over titles and bodies, with the title weighted twice. The query uses up to 12 words of more than one letter, matching any of them.
- Meaning: the query is embedded (with a 500 ms limit) and compared with memory vectors.
The lists are merged with reciprocal rank fusion (a memory at position n in a list gets 1 / (60 + n) from that list), then scored:
score = fused rank
× type weight
× (0.5 + confidence / 2)
× (0.4 + 0.6 × 0.5 ^ (days since last confirmed / 75))Type weights favor what most often prevents repeated mistakes: lesson 1.3, environment quirk 1.25, decision 1.2, command 1.15, convention 1.0, ownership and preference 0.95, spec 0.85. At most 3 results come from any single run, so one long session cannot fill the list. There is no minimum similarity.
Source: packages/memory/src/retriever.ts
The project brief
When a run starts in a project that has active memories, OpenOrc adds a brief to its instructions. The brief takes the 400 most recently updated active memories, groups them by type, and ranks each group by confidence, halving the weight every 90 days since the memory was last confirmed. It shows at most 6 decisions, 8 lessons, 6 commands, 4 environment quirks, 5 conventions, and 5 preferences. Specs and ownership notes are only available through search.
Each line shows the memory's age: today, yesterday, a number of days, or a number of months. The brief also lists up to 8 tasks in progress or review and up to 6 open items from the last 5 run summaries. It is cut at 5,000 characters.
The brief is built when the agent process starts and delivered with the rest of OpenOrc's instructions. The template is on What OpenOrc tells agents.
Source: packages/memory/src/brief.ts
Memory tools
Agents can use memory through OpenOrc's MCP server while they work:
| Tool | What it does |
|---|---|
memory_search | Runs the retrieval above for a query and returns up to 20 memories (8 by default), each with its type, title, the first 200 characters of its body, its age, and its confidence. |
task_context | For a run that belongs to a task, such as a team assignment: the task and its spec, the last 4 run summaries, and the 8 most relevant memories. In an ordinary conversation it returns "No task context." |
memory_record | Saves a memory from the agent, with a type, title, body, and optional topic key. Agents can use six types: decision, lesson, command, environment quirk, convention, and spec. |
memory_feedback | Marks a memory as helpful, wrong, or stale. |
All four work only in the run's own project. While memory is off, agents are not offered memory_search, memory_record, or memory_feedback, and a call from an agent that saw them earlier is refused. task_context stays and returns only the task itself.
Saving and feedback change what every later run reads, so they follow the conversation's mode: Plan refuses them, Review everything asks you first, and Accept edits and Autonomous allow them. See Permissions and app tools. Whatever the mode:
- An agent cannot use the topic key of a memory you wrote, so it cannot overwrite it, and it cannot mark one of yours wrong or stale.
memory_recordtakes a title of up to 120 characters, a body of up to 800, and a topic key of up to 80.- The brief labels every memory with who saved it: you, an agent, or a run summary.
Controls
- Memory on or off is one switch for all projects, and it is off in a new profile. When it is off, nothing is extracted or embedded, no brief is added, and agents are not offered the memory tools, except
task_context. Saved memories stay, and you can still browse, edit, and delete them. A profile that saved a choice before memory became opt-in keeps it. - Extraction has its own setting for provider and model, and can be turned off separately. When memory is on, the setting says which agent and model will summarize runs.
- The Memory page lets you edit a memory's title and details, mark it helpful, retract it, delete it, or add it to the project's
CLAUDE.mdorAGENTS.mdunder a "Project memory (from OpenOrc)" heading.
Deleting a memory removes its row, search entry, and vector. Deleting the conversation or task a memory came from does not delete the memory.