Custom Strategies
When the built-in modes don’t fit, build a custom strategy pipeline. Define which content to reduce, how to reduce it, and when each strategy fires.
Strategy pipeline
Section titled “Strategy pipeline”Strategies run as an ordered pipeline. Each strategy sees the output of the previous one, so order determines priority. If two strategies target the same content, the first one to shrink it below the next strategy’s threshold wins.
The SDK always appends an emergency truncation strategy as the final step. It only fires when the context window is still overflowing after all user strategies have run.
from strands import Agentfrom strands.experimental.context_manager import Offload
agent = Agent( context_manager={ "strategies": [ Offload.truncate("tool_results").when( threshold=2000, ), Offload.summarize("*").when( utilization=0.8, preserve_recent=4, ), ], },)import { Agent } from '@strands-agents/sdk'import { Offload } from '@strands-agents/sdk/experimental'
const agent = new Agent({ contextManager: { strategies: [ Offload.truncate('toolResults').when({ threshold: 2000, }), Offload.summarize('*').when({ utilization: 0.8, preserveRecent: 4, }), ], },})Offload builder
Section titled “Offload builder”The Offload namespace exposes three methods. Each returns
a strategy builder that can be chained with
.when().when()
Truncate
Section titled “Truncate”Replaces oversized content with a head/tail preview. The original content is stored in the stash for later retrieval.
from strands.experimental.context_manager import Offload
Offload.truncate( "tool_results", {"preview_tokens": 750},).when(threshold=1500)import { Offload } from '@strands-agents/sdk/experimental'
Offload.truncate('toolResults', { previewTokens: 750,}).when({ threshold: 1500 })Truncate configuration:
| Parameter | Python | TypeScript | Default |
|---|---|---|---|
| Preview size | preview_tokens | previewTokens | 1,000 |
| Preview mode | preview | preview | "head_tail" / "headTail" |
Preview modes: "head", "tail",
"head_tail""headTail"
Summarize
Section titled “Summarize”Replaces content with an LLM-generated summary. Uses the agent’s model by default.
from strands.experimental.context_manager import Offload
Offload.summarize("*").when( utilization=0.85, preserve_recent=4,)import { Offload } from '@strands-agents/sdk/experimental'
Offload.summarize('*').when({ utilization: 0.85, preserveRecent: 4,})Summarize configuration:
| Parameter | Python | TypeScript | Default |
|---|---|---|---|
| Model | model | model | Agent’s model |
| System prompt | system_prompt | systemPrompt | Built-in |
Removes matching content entirely. No preview, no stash entry.
from strands.experimental.context_manager import Offload
Offload.drop("tool_results").when(preserve_recent=5)import { Offload } from '@strands-agents/sdk/experimental'
Offload.drop('toolResults').when({ preserveRecent: 5,})Conditions
Section titled “Conditions”Chain .when().when()
| Condition | Granularity | Behavior |
|---|---|---|
thresholdthreshold | Per-block, eager | Acts on each block above this token size on message arrival |
utilizationutilization | Message-level, batch | Removes/summarizes oldest messages when utilization exceeded |
| Both | Message-level | Targets messages with blocks over the threshold, fires at utilization |
preserve_recentpreserveRecent | — | Number of most recent matching messages to skip |
preserve_recentpreserveRecentaccepts an integer (absolute count) or a float between 0
and 1 (ratio of matching messages, e.g., 0.7 keeps
70%).
Per-block example
Section titled “Per-block example”Fire eagerly on individual blocks over 2,000 tokens:
from strands.experimental.context_manager import Offload
Offload.truncate("tool_results").when(threshold=2000)import { Offload } from '@strands-agents/sdk/experimental'
Offload.truncate('toolResults').when({ threshold: 2000,})Message-level example
Section titled “Message-level example”Summarize oldest messages when the window reaches 85%:
from strands.experimental.context_manager import Offload
Offload.summarize("*").when( utilization=0.85, preserve_recent=4,)import { Offload } from '@strands-agents/sdk/experimental'
Offload.summarize('*').when({ utilization: 0.85, preserveRecent: 4,})Combined example
Section titled “Combined example”Target tool results over 1,500 tokens, but only fire when the window reaches 90%:
from strands.experimental.context_manager import Offload
Offload.truncate("tool_results").when( threshold=1500, utilization=0.9,)import { Offload } from '@strands-agents/sdk/experimental'
Offload.truncate('toolResults').when({ threshold: 1500, utilization: 0.9,})Targets
Section titled “Targets”Targets specify which content a strategy applies to.
| Target | Python | TypeScript |
|---|---|---|
| All content | "*" | "*" |
| Tool results | "tool_results" | "toolResults" |
| Failed tool results | "tool_result_errors" | "toolResultErrors" |
| Assistant text | "assistant_text" | "assistantText" |
| User text | "user_text" | "userText" |
| Specific tools | ["tool::bash", "tool::read_file"] | ["tool::bash", "tool::read_file"] |
| Exclude tools | ["!tool::bash"] | ["!tool::bash"] |
Tool name lists use the tool:: prefix. Prefix a name
with ! to exclude it (apply to all tools except the
listed ones).
The stash stores all message content on arrival as JSON,
before any strategy runs. Truncated or summarized content
can be retrieved on demand through the
retrieve_contextretrieve_context
Stash uses in-memory storage by default. Configure it
through the
stashstash
- Omit or pass
truefor defaults (in-memory storage) - Pass a config with a custom storage backend
- Pass
falseto disable stash entirely
from strands import Agent
# Disable stash and retrieval toolagent = Agent( context_manager={ "stash": False, },)import { Agent } from '@strands-agents/sdk'
const agent = new Agent({ contextManager: { stash: false, },})Emergency truncation
Section titled “Emergency truncation”Every strategy pipeline ends with an emergency truncation strategy, appended automatically. It fires only when the context window is still overflowing after all user strategies have run. When it fires, it drops the oldest 20% of non-head messages.
Emergency truncation intentionally ignores pinned messages so an all-pinned overflow is still recoverable. Regular strategies respect pins.