Use durable memory for a fact, preference or decision that should remain useful beyond one conversation. This guide saves a review rule for a small pilot study, retrieves it in another session, then updates and deletes it. It is for users who have completed Getting started .

Context and compaction explains why a durable memory record, its injected digest and the model’s current request are different. Use Manage a long session when a detail needs to be recovered after summarization.

The workflow targets Sonny 0.12.7 on Linux. Qualification used the installed binary, synthetic inputs and a controlled local provider. It exercised real tool calls and files; it does not establish live-provider reasoning quality or synchronization between machines.

Choose what to remember

A useful record is focused, named clearly and understandable without its original conversation. For example: “When comparing the pilot notes, cite both sources and show both denominators.” Keep the original evidence available so a later user can check the rule. Do not save passwords, API keys or private tokens as memories.

These similarly named things have different lifecycles:

MaterialWhat it preserves
Durable memoryA YAML fact, preference, decision or reference in a selected project or global scope.
Session historyThe conversation and its recorded results. Deleting a memory does not retract copies already in a conversation or export.
Context and compactionMaterial selected or summarized for a model request. A summary does not become a durable memory record merely because it survives a turn.
Memory search indexA derived SQLite FTS cache. The YAML files remain authoritative.
Lanning knowledgeData held by a separately configured service, under that service’s scope and access controls. Local records are not automatically synchronized there.
Recipe policy-evidence retrievalA separate retrieval path controlled by SONNY_MEMORY=on, a Recipe’s memory: policy and a configured retriever.

Prepare a disposable project

Use a new directory and an approved model. This example uses the cookbook’s public review Recipe. Download and inspect it before starting:

mkdir sonny-memory-review
cd sonny-memory-review
mkdir -m 700 .sonny
mkdir approved-recipes
curl --fail --location https://sonny.sh/examples/cookbook/review-work.yaml --output approved-recipes/review-work.yaml
sonny recipe validate ./approved-recipes/review-work.yaml
sonny isolation doctor
sonny run --new --file ./approved-recipes/review-work.yaml --no-trace

The run creates a session and can create Sonny’s standard configuration/data/cache state. Creating this project’s own .sonny directory makes it the default project write scope; the memory tools create its memory/ subdirectory. Review the reported isolation and honor any policy refusal. The Recipe itself does not restrict the tool surface; your admitted tool policy, normal approvals and execution boundary still apply. The provider receives prompts and retrieved memory contents.

The six memory operations are model tools, not shell subcommands:

ToolResult to inspect
memory_storeSaved identifier, selected scope and whether a record was created or updated.
memory_retrieveFull record and the scope it came from.
memory_listIdentifiers, titles, categories, tags and scopes, without bodies.
memory_searchRanked matches with scopes and content snippets.
memory_deleteThe identifier and the single scope from which it was removed.
memory_statsThe scope chain and its record counts. Counts across scopes can include repeated identifiers.

If an operation asks for approval, read the tool, identifier, scope and proposed content first. Use Allow once for that reviewed operation. The TUI deliberately ignores an immediate approval key while its dialog is arming; read it before responding. A tool being discoverable does not establish permission to run it.

Save and inspect a rule

Ask Sonny:

Use memory_store to save a project memory:
id: pilot-review-rule
title: Pilot comparison review rule
category: decision
tags: pilot, review
content: Cite Notes A and B; show both denominators; do not infer satisfaction.
Then show the saved identifier, scope and whether it was newly created.

Approve the specific write after checking it. The expected result includes pilot-review-rule, created: true and a project scope ending in this directory’s .sonny. If the returned scope names an ancestor instead, inspect the scope chain before continuing.

The tool writes one YAML file per record. In this fresh project, inspect .sonny/memory/pilot-review-rule.yaml; the qualified record had this shape:

id: pilot-review-rule
category: decision
title: Pilot comparison review rule
content: Cite Notes A and B; show both denominators; do not infer satisfaction.
tags:
  - pilot
  - review
source: agent
created_at: 2026-10-25T03:07:29Z
updated_at: 2026-10-25T03:07:29Z

Your timestamps will differ. title and content are required tool inputs. An explicit identifier must match ^[a-z0-9][a-z0-9-]{0,63}$; if omitted, Sonny can derive one from the title. category is free-form and defaults to fact. The entire encoded record is limited to 1 MiB, not just its content field. New memory directories use mode 0700 and record files use 0600 on the tested Linux path. These permissions do not encrypt the contents.

Retrieve it in a new session

Close the first TUI session and run the same command again with --new, from the same project directory. The new session has a different identifier. Ask:

Use memory_retrieve to fetch pilot-review-rule. Show its complete content and scope.
Do not infer the rule from an earlier conversation.

Inspect the actual tool result and approve that read if requested. It should return the saved text and this project’s scope. A remembered-sounding answer without a retrieval result does not prove persistence. You can ask for memory_list or memory_stats to check which records and directories are visible.

Update and delete deliberately

To update the same record, ask:

Update project memory pilot-review-rule with memory_store.
Keep its title. Replace the content with:
Cite Notes A and B, show both denominators, and retain missing survey evidence.
Retrieve it again so I can inspect the stored result.

Check that the identifier and scope stayed the same and created is false. An update preserves the original creation time and writes a new update time. Omitting a category or supplying an empty tag list retains existing metadata in this release; inspect the resulting record rather than assuming those values were cleared.

When the rule is no longer wanted, ask:

Delete pilot-review-rule from project scope using memory_delete.
Show the scope actually deleted, then use memory_list to check what remains.

Review the deletion before approving it. Deletion removes one matching record. scope: project selects the deepest matching project record; that may be in an ancestor directory. scope: global selects the global store. With no scope, delete searches from the deepest project toward global. It does not erase every copy of that identifier. A shallower copy can become visible after deletion.

Understand scope and precedence

Sonny reads global records, then owner-trusted project directories from the outermost ancestor toward the working directory. A .sonny directory owned by you or root participates; a directory owned by another user is skipped. For retrieval and listing, the deepest readable, unexpired record wins an identifier collision.

LocationExample authority directoryPrecedence for the same identifier
Global<Sonny data directory>/memory/Lowest; visible across projects using this user store.
Parent projectstudy/.sonny/memory/Overrides global for work under that project.
Deeper projectstudy/review/.sonny/memory/Overrides the parent when working in study/review.

A default project write targets the deepest existing .sonny directory on the chain. If none exists, the first write creates one in the working directory. Starting in a child directory alone does not create a child scope: an existing parent .sonny remains the write target. Choose global scope explicitly when a rule should apply across projects. Use memory_stats and returned scope labels to inspect the actual chain. Scope labels in results are descriptions, not new values to paste into the tool’s scope parameter.

Search has a release limitation

Search ranks matching records across scopes and returns snippets. On 0.12.7, a matching global record can appear even when a deeper record with the same identifier exists but does not match the query. For example, a search for a word present only in the global version returned that global copy, while memory_retrieve returned the overriding child version.

Check the search hit’s scope, then retrieve the identifier to inspect the effective record before relying on it. Do not assume search results and direct retrieval always select the same version. This limitation is retained in qualification; the successful retrieval/list precedence tests do not establish stronger search semantics.

What reaches a new model request

The qualified TUI startup path injects a compact digest headed Project memory (sonny). Each digest block lists up to 100 records by identifier and title, with category and global/project scope labels. A trailer reports additional records. Bodies are retrieved separately. The installed cap check seeded 101 records and observed 100 unique identifiers per block, an additional-record notice and no body text.

Suppress this startup digest for a new TUI process with:

SONNY_NO_MEMORY_INJECT=1 sonny run --new --file ./approved-recipes/review-work.yaml --no-trace

The flag does not delete the YAML records or remove the tools. Qualification retrieved a real record in that disabled-digest TUI session after Allow once. It does not retract a digest already present in an existing conversation. The qualified headless --prompt journeys did not receive this startup digest; check the actual client path rather than relying on ambient recall.

Search cache, backup and privacy

The authoritative records live in project .sonny/memory/ directories and the global data directory’s memory/. Use sonny config path for your active standard directories. Search derives SQLite FTS databases under the cache directory’s memindex/, keyed by scope path. The index contains record text; it is not an encrypted vault. A search refreshes an index when its record file set changes.

If a cache needs rebuilding, close concurrent users of it, identify the resolved cache memindex/ directory, and remove only that derived cache. The next search recreates it from YAML. Removing the data directory’s memory/ instead would delete authoritative records. Qualification removed existing FTS databases, observed recreation and verified that every YAML hash was unchanged.

For a backup, stop memory writers and copy the intended YAML directories, preserving file ownership and restrictive permissions. Include the global store and each relevant project/ancestor scope, and record where each belongs. Restore into the intended scope while writers are stopped; review collisions before overwriting existing identifiers. A tested file restore brought back the original record and its timestamps, and a new tool call retrieved it. Restoring old records can revive outdated decisions. The search index can be rebuilt; a session database or credential bundle is not needed to restore these YAML records.

Deletion is not secure erasure. A derived index can retain a removed record’s body until refreshed or removed. Stored results, conversation exports, provider requests, traces and backups can contain copies. The qualified post-delete search removed the record from results and refreshed the index; that did not erase older output files. --no-trace does not prevent provider transmission or make those other copies disappear.

Project scope does not automatically commit files or synchronize them to another machine. If you place records in Git or a shared directory, consider who can read that copy. Global scope deliberately broadens reuse within this user store; it is not a shared Lanning knowledge store.

If a record is missing

Check the working directory and memory_stats, then list and retrieve at the expected scope. A directory can be outside the owner-trusted chain; a record can be expired, malformed, oversized or unreadable. Listing skips malformed records and filename/identifier mismatches. Preserve the YAML for inspection before changing it. Rebuilding a search cache cannot recreate a missing authority file.

Continue with sessions and continuation , plan review and approval or the exact memory settings reference .