Your agent doesn't fail because the model got dumb halfway through the task. It fails because the context window filled with stale tool output, half-finished reasoning, and search results it already used — until the model can no longer see what matters. Context engineering is the discipline of preventing that: filling the window, at each step, with just the right tokens for the next step and nothing else.
The good news for a solo builder is that the whole discipline reduces to four moves, sorted by what you do with a token — and each one maps to a shipped Claude API primitive you can call today. Write it out of the window. Select only what the step needs. Compress what's already there. Isolate it into a separate agent. Here is the build sheet.
The four moves, and why the order matters#
| Move | What you do with the token | Claude primitive |
|---|---|---|
| Write | push it out of the window to durable storage | memory tool |
| Select | pull only the relevant slice back in, just in time | file reads / tool search |
| Compress | shrink what's already in the window | context editing + compaction |
| Isolate | move the work into a separate window | sub-agents |
The order is a priority list, not just a list. Write the specifics you can't afford to lose before you Compress, because compression is lossy by design — Anthropic's own cookbook test kept 3 of 3 high-level facts through a compaction pass but 0 of 3 obscure ones. If a number, an ID, or a decision has to survive, it belongs in a memory file, not in a summary you're hoping preserves it.
1. Write — give the agent a memory file#
The memory tool (memory_20250818) lets the model persist facts to files outside the window that survive a context reset. It's generally available on the Messages API — no beta header — for Claude 4 and later. The model emits file operations (view, create, str_replace, insert, delete, rename) under a /memories prefix; your application executes them against storage you control.
import anthropic
from anthropic.tools import BetaLocalFilesystemMemoryTool
client = anthropic.Anthropic()
memory = BetaLocalFilesystemMemoryTool(base_path="./memory") # maps /memories -> ./memory
runner = client.beta.messages.tool_runner(
model="claude-opus-5",
max_tokens=1024,
messages=[{"role": "user",
"content": "Remember that Acme Corp prefers email follow-ups."}],
tools=[memory],
)
final = runner.until_done()
The specifics belong in a file, not in a summary you're hoping preserves them.
One callout that isn't optional: path-traversal protection is your job. The SDK's local backend handles it, but if you back memory with your own store, validate that every path resolves inside /memories and reject ../, ..\, and encoded variants like %2e%2e%2f. The model writes wherever it's allowed to.
2. Select — pull in only what the step needs#
Front-loading every document, tool, and past result into the prompt is the most common way agents rot their own window. Select flips it: keep the bulk in storage and retrieve just-in-time. In practice that's memory-file reads, vector RAG, or tool search — embedding-based lookup so an agent with fifty tools only ever sees the handful the current step needs, instead of paying attention tax on all fifty definitions every turn. The Select bottleneck is retrieval quality; if the wrong slice comes back, the model works from the wrong context, so this is where your evals should concentrate.
3. Compress — evict, then summarize#
Compression has two levers, from lightest to heaviest touch.
Context editing (clear_tool_uses_20250919) evicts old tool results automatically as the window fills, replacing them with a placeholder so the model knows material was removed. The defaults fire near 100K input tokens and keep the 3 most recent tool uses; the parameters that matter for tuning:
context_management={
"edits": [{
"type": "clear_tool_uses_20250919",
"trigger": {"type": "input_tokens", "value": 30_000}, # start clearing at 30K
"keep": {"type": "tool_uses", "value": 3}, # keep 3 most recent results
"clear_at_least": {"type": "input_tokens", "value": 5_000}, # justify the cache re-write
"exclude_tools": ["memory"], # never evict memory reads
}]
}
# betas=["context-management-2025-06-27"]
Two traps live in that block. Clearing invalidates the prompt cache prefix, so clear_at_least exists to stop a clear from firing unless it frees enough tokens to be worth re-caching. And exclude_tools: ["memory"] is what keeps move 1 from being undone by move 3 — you don't want the agent's durable notes evicted as if they were disposable search output. You can read back exactly what happened from response.context_management.applied_edits (cleared_tool_uses, cleared_input_tokens), which is also your token-budget telemetry.
When the transcript itself — not just tool results — is the bulk, add server-side compaction (compact_20260112) in the same edits array. It summarizes near a threshold you set, with a free-text instructions field so you can tell it what to preserve:
{
"type": "compact_20260112",
"trigger": {"type": "input_tokens", "value": 150_000},
"instructions": "Preserve quantitative figures and their source; "
"note which documents have been read.",
}
That instruction is doing real work: compaction drops specifics by default, so you name the specifics that must survive — and you still write the truly load-bearing ones to memory first.
4. Isolate — sub-agents, and when not to#
The heaviest move is to stop managing one window and split into several. A supervisor delegates a bounded sub-task to a sub-agent with its own clean window; only the result returns to the main thread. This is powerful for genuinely parallel, read-only fan-out — survey five sources at once, return five summaries.
But Isolate is where the field openly disagrees, and a solo builder should know it before reaching for it. Cognition's argument in Don't Build Multi-Agents is that splitting a decision across agents that don't share full context produces incoherent output — so keep the loop single-threaded and share the whole trace, not clipped messages. The reconciled rule: isolate work, not decisions, and keep writes single-threaded. If two sub-agents might make conflicting changes, you don't want two sub-agents.
The one-loop version#
For most long-running agents you don't choose one move — you layer three and hold the fourth in reserve. Turn on context editing so results evict themselves. Add the memory tool and write specifics to it before they can be summarized away. Add compaction for the very long tail. Reach for sub-agents only when a task truly forks. That's the playbook: Write what you can't lose, Select what you need, Compress the rest, Isolate only when the work — not the decision — divides. For the head-to-head on the three compression levers specifically, we broke down context editing vs compaction vs the memory tool separately.



