24 min read #ai #Agent
On this page

Memory and State

APIs are stateless; an agent’s "memory" is entirely engineered. Three layers of memory—re-sending history within a session, compaction/context editing to manage the window, and memory tools/stores for cross-session persistence—each use different mechanisms to solve different problems.

Overview

APIs are stateless (see Context Engineering): what the model "knows" in a single turn is entirely contained in that request; it does not automatically remember previous interactions. Therefore, an agent’s "memory" is entirely engineered—a set of mechanisms maintained by the caller, layered according to their lifecycle. Categorizing them by "how long they last" provides the clearest framework for understanding this:

flowchart TD
    A["① Within-session<br/>Re-sending full history (protocol-native)"] --> B["② Near window limit<br/>Compaction summary / context editing trimming"]
    B --> C["③ Cross-session persistence<br/>Memory tool / memory stores (outside the session)"]

The first layer is "remembering this turn," the second is "preventing overflow when the conversation gets too long," and the third is "remembering after closing and reopening." These three layers use different mechanisms to solve different problems; long-running agents often employ all three. Understanding their boundaries prevents misusing tools (e.g., relying solely on re-sending history when trying to remember preferences across sessions).

Layer 1: Within-Session — The Cost of Re-sending History

The model remembers the previous turn simply because the entire history is sent again. This layer is "free" (native to the protocol), but it has two costs:

  • Linear cost growth: Re-sending all history each turn causes input tokens to accumulate with each round.
  • Inevitable overflow: If history grows infinitely, it will eventually approach the window limit (stop_reason: "model_context_window_exceeded").

prompt caching allows stable prefixes in re-sent history to be billed at ~0.1x (see Context Engineering), alleviating cost; but the overflow problem must be addressed by Layer 2.

Layer 2: Managing the Approaching Window — Compaction vs. Editing

Two opposing API primitives:

CompactionContext Editing
What it doesSummarizes early history into a compaction blockClears old tool_result / thinking blocks
Information handlingSummarizes and retainsDeletes directly
Betacompact-2026-01-12context-management-2025-06-27
Key pitfallYou must return response.content (including the compaction block) as a whole block; returning only text will lose stateOnly deletes blocks, does not alter conversation structure

Mnemonic: compaction is "summarizing," context editing is "deleting." Both operate within a single session to free up window space and do not solve cross-session persistence—if you close the process, they are gone.

Layer 3: Cross-Session Persistent Memory

To "remember after closing and reopening," key points must be written outside the session. There are two paths, chosen based on self-managed vs. managed approaches.

Path A: Memory Tool (Backend Self-Managed)

Declare the tool {"type": "memory_20250818", "name": "memory"}, and the model gains read/write access to a /memories directory, with six commands:

CommandSemantics
viewRead file / list directory
createCreate/overwrite file
str_replaceReplace a string in a file
insertInsert after a specified line
deleteDelete file
renameRename/move

The storage backend is implemented by the user (local FS, object storage, DB)—the model only sends commands, and the implementation executes them and returns tool_result. Python/TypeScript SDKs provide scaffolding (BetaAbstractMemoryTool / betaMemoryTool + handler); you only need to fill in the storage logic for view/create/str_replace/.... The model writes "user preferences," "project conventions," and "lessons learned" into files, and reads them back via view in the next session.

This approach is currently being used in this very session: persistent memory resides under ~/.claude/projects/.../memory/, with one file per fact + a MEMORY.md indexMEMORY.md remains persistent, and each fact is read on demand. This is the practical implementation of the memory tool pattern (this turn updated the entry regarding "this site's build method").

Path B: Managed Agents Memory Stores (Managed)

A workspace-level persistent memory repository with a three-layer object model:

Memory Store Three-Layer Object Model memory_store (memstore_…) Workspace-level collection, attached to session memory (mem_…) A text file, addressed by path, ≤ 100KB memory_version (memver_…) An immutable snapshot for each change (created / modified / deleted)

Key mechanisms:

  • Mounted as a filesystem: The store is mounted via FUSE to the container at /mnt/memory/<store>/; the agent reads/writes using standard file tools; the system prompt automatically injects a description informing it of the mount.
  • Access control: access: "read_only" | "read_write", enforced at the filesystem layer.
  • Optimistic concurrency: memories.update supports precondition: {type:"content_sha256", ...}; if it doesn't match, it returns 409—read-modify-write operations won't overwrite each other.
  • Audit and rollback: Every change produces a memory_version, which can be listed, fetched, and redacted (stripping content but leaving actor + timestamp, useful for leaks/PII removal).
  • Up to 8 stores can be mounted per session (can be layered by "shared read-only reference + per-user read/write").

Memory vs. Context: Which to Use?

Beginners often mistake "long context" for "memory." Distinguish them:

  • Context: What is stuffed into this single request, disappearing at the end of the turn (unless re-sent). The "window."
  • Memory: What survives across requests/sessions, written outside the session. The "hard drive."

Use context for "seeing this turn" (RAG puts snippets into the window); use memory for "remembering later" (writing files). Compaction/context editing are management tools for the context layer, not memory.

Design and Security

Good memory design (also how this session's memory is written):

  • One experience per file, with a one-line summary at the top to facilitate relevance judgment during retrieval.
  • Store only non-obvious things: Don't store what's already in the code/history (that's noise); store "why," "where the pitfalls are."
  • Update on write, delete on error: Update existing entries rather than stacking duplicates; delete outdated ones.

Security red lines:

  • Path validation is mandatory: The path provided by the model is untrusted output. Resolve to a canonical path (realpath/Path.resolve()), confirm it is still within the memory root directory, and reject .., symbolic links, absolute out-of-bounds paths, and URL-encoded traversal (%2e%2e%2f). Do not directly open() the raw path.
  • Never store secrets: API keys, passwords, and tokens must not enter memory. Be cautious with PII (GDPR/CCPA); redact in memory stores is a post-hoc remediation measure.
  • Multi-tenant isolation: Independent memory directories + authentication per user; reference implementations lack built-in access control.

Best Practices

  • Use the right tool for each layer based on "how long it lasts." Re-send history within sessions, use compaction/context editing near the window limit, and use memory tools/stores for cross-session persistence—don't use re-sending history to "remember preferences."
  • One experience per file, with a one-line summary at the top. Facilitates relevance judgment during retrieval; a single MEMORY.md index remains persistent, reading each entry on demand (this session does exactly this).
  • Store only non-obvious things. Don't store what's already in the code/history (it's noise); store "why," "where the pitfalls are," and "established conventions."
  • Update on write, delete on error. Update existing entries rather than stacking duplicates; delete outdated ones.
  • Normalize paths before validating boundaries. The path provided by the model is untrusted; resolve() + confirm it is within the memory root; reject ../symlinks/URL-encoded traversal (see Security and Protection).
  • Secrets never enter memory; multi-tenants are isolated by user. Reference implementations lack built-in access control; be cautious with PII, and remember redact is only a post-hoc remedy.

Trade-offs and Failure Modes

  • Stuffing everything into memory: Becomes noise, interferes with retrieval → store only high-value, non-obvious items.
  • Treating memory as a transactional database: It is notes for the model → use content_sha256 optimistic locking (memory stores) or self-managed versions for concurrent writes.
  • Cross-session tenant leakage: Multi-tenant isolation not enforced → separate directories by user + authentication.
  • Unvalidated paths: Directory traversal reading/writing arbitrary files → normalize + boundary checks.

References

  • Anthropic Official Documentation: Memory Tool, Managed Agents Memory Stores (platform.claude.com)

Keywords: stateless, session history, prompt caching, compaction, context editing, model_context_window_exceeded, persistent memory, memory tool, memory_20250818, /memories, view/create/str_replace/insert/delete/rename, BetaAbstractMemoryTool, memory stores, memstore, FUSE mount, access, content_sha256, precondition, redact, memory version, path traversal, secret, PII, multi-tenant isolation