---
title: Conversation Management
tags: [conversation-management, session-management, token-management]
---

In the Strands Agents SDK, context refers to the information provided to the agent for understanding and reasoning. This includes:

- User messages
- Agent responses
- Tool usage and results
- System prompts

As conversations grow, managing this context becomes increasingly important for several reasons:

1. **Token Limits**: Language models have fixed context windows (maximum tokens they can process)
2. **Performance**: Larger contexts require more processing time and resources
3. **Relevance**: Older messages may become less relevant to the current conversation
4. **Coherence**: Maintaining logical flow and preserving important information

## Built-in Conversation Managers

The SDK provides a flexible system for context management through the ConversationManager interface. This allows you to implement different strategies for managing conversation history. You can either leverage one of Strands's provided managers:

- [**NullConversationManager**](#nullconversationmanager): A simple implementation that does not modify conversation history
- [**SlidingWindowConversationManager**](#slidingwindowconversationmanager): Maintains a fixed number of recent messages (default manager)
- [**SummarizingConversationManager**](#summarizingconversationmanager): Intelligently summarizes older messages to preserve context

or [build your own manager](#creating-a-conversationmanager) that matches your requirements.


### NullConversationManager

The [`NullConversationManager`](@api/python/strands.agent.conversation_manager.null_conversation_manager#NullConversationManager) is a simple implementation that does not modify the conversation history. It's useful for:

- Short conversations that won't exceed context limits
- Debugging purposes
- Cases where you want to manage context manually

<Tabs>
<Tab label="Python">

```python
from strands import Agent
from strands.agent.conversation_manager import NullConversationManager

agent = Agent(
    conversation_manager=NullConversationManager()
)
```
</Tab>
<Tab label="TypeScript">

```typescript
--8<-- "user-guide/concepts/agents/conversation-management_imports.ts:null_conversation_manager_imports"

--8<-- "user-guide/concepts/agents/conversation-management.ts:null_conversation_manager"
```
</Tab>
</Tabs>

### SlidingWindowConversationManager

The [`SlidingWindowConversationManager`](@api/python/strands.agent.conversation_manager.sliding_window_conversation_manager#SlidingWindowConversationManager) implements a sliding window strategy that maintains a fixed number of recent messages. This is the default conversation manager used by the Agent class.

<Tabs>
<Tab label="Python">

```python
from strands import Agent
from strands.agent.conversation_manager import SlidingWindowConversationManager

# Create a conversation manager with custom window size
conversation_manager = SlidingWindowConversationManager(
    window_size=20,  # Maximum number of messages to keep
    should_truncate_results=True, # Enable truncating the tool result when a message is too large for the model's context window
)

agent = Agent(
    conversation_manager=conversation_manager
)
```
</Tab>
<Tab label="TypeScript">

```typescript
--8<-- "user-guide/concepts/agents/conversation-management_imports.ts:sliding_window_conversation_manager_imports"

--8<-- "user-guide/concepts/agents/conversation-management.ts:sliding_window_conversation_manager"
```
</Tab>
</Tabs>

Key features of the `SlidingWindowConversationManager`:

- **Maintains Window Size**: Automatically removes messages from the window if the number of messages exceeds the limit.
- **Dangling Message Cleanup**: Removes incomplete message sequences to maintain valid conversation state.
- **Overflow Trimming**: In the case of a context window overflow, it will trim the oldest messages from history until the request fits in the models context window.
- **Configurable Tool Result Truncation**: Enable or disable truncation of tool results when the message exceeds context window limits. When enabled (the default; `should_truncate_results=True` in Python, `shouldTruncateResults: true` in TypeScript), the oldest message with tool results is truncated first so recent context is preserved as long as possible. Truncation depends on content type:
    - Text payloads keep their head and tail, separated by a `<truncated chars="N"/>` marker.
    - Images, videos, binary documents, and oversized JSON are replaced by a typed placeholder, for example `[image: png, source: bytes, 12345 bytes]`.
    - The tool result's original `status` and `error` fields are preserved.

    When disabled, full results are preserved but more historical messages may be removed. For a proactive alternative that preserves full content externally, see the [Context Offloader](../plugins/context-offloader) plugin.
- **Per-Turn Management**: Optionally apply context management proactively during the agent loop execution, not just at the end.
- **Proactive Compression**: Pass `proactiveCompression: true` or `proactiveCompression: { compressionThreshold: 0.7 }` to trigger context reduction before the model call when projected input tokens exceed a configurable threshold. See [Proactive Context Compression](#proactive-context-compression).

**Per-Turn Management**:

By default, the `SlidingWindowConversationManager` applies context management only after the agent loop completes. The `per_turn` parameter allows you to proactively manage context during execution, which is useful for long-running agent loops with many tool calls.

<Tabs>
<Tab label="Python">

```python
from strands import Agent
from strands.agent.conversation_manager import SlidingWindowConversationManager

# Apply management before every model call
conversation_manager = SlidingWindowConversationManager(
    per_turn=True,  # Apply management before each model call
)

# Or apply management every N model calls
conversation_manager = SlidingWindowConversationManager(
    per_turn=3,  # Apply management every 3 model calls
)

agent = Agent(
    conversation_manager=conversation_manager
)
```
</Tab>
<Tab label="TypeScript">

```ts
// Not supported in TypeScript
```
</Tab>
</Tabs>

The `per_turn` parameter accepts:

- `False` (default): Only apply management after the agent loop completes
- `True`: Apply management before every model call
- An integer `N` (must be > 0): Apply management every N model calls

### SummarizingConversationManager

The [`SummarizingConversationManager`](@api/python/strands.agent.conversation_manager.summarizing_conversation_manager#SummarizingConversationManager) (Python) / [`SummarizingConversationManager`](@api/typescript/SummarizingConversationManager) (TypeScript) implements intelligent conversation context management by summarizing older messages instead of simply discarding them. This approach preserves important information while staying within context limits.

<Tabs>
<Tab label="Python">

Configuration parameters:

- **`summary_ratio`** (float, default: 0.3): Percentage of messages to summarize when reducing context (clamped between 0.1 and 0.8)
- **`preserve_recent_messages`** (int, default: 10): Minimum number of recent messages to always keep
- **`summarization_agent`** (Agent, optional): Custom agent for generating summaries. If not provided, uses the main agent instance. Cannot be used together with `summarization_system_prompt`.
- **`summarization_system_prompt`** (str, optional): Custom system prompt for summarization. If not provided, uses a default prompt that creates structured bullet-point summaries focusing on key topics, tools used, and technical information in third-person format. Cannot be used together with `summarization_agent`.

</Tab>
<Tab label="TypeScript">

Configuration parameters:

- **`model`** (Model, optional): Override model to use for generating summaries. When not provided, uses the agent's own model.
- **`summaryRatio`** (number, default: 0.3): Ratio of messages to summarize when reducing context (clamped between 0.1 and 0.8)
- **`preserveRecentMessages`** (number, default: 10): Minimum number of recent messages to always keep
- **`summarizationSystemPrompt`** (string, optional): Custom system prompt for summarization. If not provided, uses a default prompt that creates structured bullet-point summaries focusing on key topics, tools used, and technical information in third-person format.
- **`proactiveCompression`** (`boolean | { compressionThreshold: number }`, optional): Enable proactive context compression before the model call. Pass `true` for the default 0.7 threshold, or an object with a custom threshold. See [Proactive Context Compression](#proactive-context-compression).

</Tab>
</Tabs>

**Basic Usage:**

<Tabs>
<Tab label="Python">

By default, the `SummarizingConversationManager` leverages the same model and configuration as your main agent to perform summarization.

```python
from strands import Agent
from strands.agent.conversation_manager import SummarizingConversationManager

agent = Agent(
    conversation_manager=SummarizingConversationManager()
)
```
</Tab>
<Tab label="TypeScript">

By default, the `SummarizingConversationManager` uses the agent's own model for summarization. You can optionally provide a different model to override this behavior.

```typescript
--8<-- "user-guide/concepts/agents/conversation-management_imports.ts:summarizing_conversation_manager_basic_imports"

--8<-- "user-guide/concepts/agents/conversation-management.ts:summarizing_conversation_manager_basic"
```
</Tab>
</Tabs>

You can also customize the behavior by adjusting parameters like summary ratio and number of preserved messages:

<Tabs>
<Tab label="Python">

```python
from strands import Agent
from strands.agent.conversation_manager import SummarizingConversationManager

# Create the summarizing conversation manager with default settings
conversation_manager = SummarizingConversationManager(
    summary_ratio=0.3,  # Summarize 30% of messages when context reduction is needed
    preserve_recent_messages=10,  # Always keep 10 most recent messages
)

agent = Agent(
    conversation_manager=conversation_manager
)
```
</Tab>
<Tab label="TypeScript">

```typescript
--8<-- "user-guide/concepts/agents/conversation-management_imports.ts:summarizing_conversation_manager_custom_imports"

--8<-- "user-guide/concepts/agents/conversation-management.ts:summarizing_conversation_manager_custom"
```
</Tab>
</Tabs>

**Custom System Prompt for Domain-Specific Summarization:**

You can customize the summarization behavior by providing a custom system prompt that tailors the summarization to your domain or use case.

<Tabs>
<Tab label="Python">

```python
from strands import Agent
from strands.agent.conversation_manager import SummarizingConversationManager

# Custom system prompt for technical conversations
custom_system_prompt = """
You are summarizing a technical conversation. Create a concise bullet-point summary that:
- Focuses on code changes, architectural decisions, and technical solutions
- Preserves specific function names, file paths, and configuration details
- Omits conversational elements and focuses on actionable information
- Uses technical terminology appropriate for software development

Format as bullet points without conversational language.
"""

conversation_manager = SummarizingConversationManager(
    summarization_system_prompt=custom_system_prompt
)

agent = Agent(
    conversation_manager=conversation_manager
)
```
</Tab>
<Tab label="TypeScript">

```typescript
--8<-- "user-guide/concepts/agents/conversation-management_imports.ts:summarizing_conversation_manager_system_prompt_imports"

--8<-- "user-guide/concepts/agents/conversation-management.ts:summarizing_conversation_manager_system_prompt"
```
</Tab>
</Tabs>

**Advanced Configuration with Custom Summarization Agent:**

<Tabs>
<Tab label="Python">

For advanced use cases, you can provide a custom `summarization_agent` to handle the summarization process. This enables using a different model (such as a faster or a more cost-effective one), incorporating tools during summarization, or implementing specialized summarization logic tailored to your domain. The custom agent can leverage its own system prompt, tools, and model configuration to generate summaries that best preserve the essential context for your specific use case.

```python
from strands import Agent
from strands.agent.conversation_manager import SummarizingConversationManager
from strands.models import AnthropicModel

# Create a cheaper, faster model for summarization tasks
summarization_model = AnthropicModel(
    model_id="claude-haiku-4-5-20251001",  # More cost-effective for summarization
    max_tokens=1000,
    params={"temperature": 0.1}  # Low temperature for consistent summaries
)
custom_summarization_agent = Agent(model=summarization_model)

conversation_manager = SummarizingConversationManager(
    summary_ratio=0.4,
    preserve_recent_messages=8,
    summarization_agent=custom_summarization_agent
)

agent = Agent(
    conversation_manager=conversation_manager
)
```
</Tab>
<Tab label="TypeScript">

Pass a custom `model` to `SummarizingConversationManager` to override the model used for summarization. This enables using a different model, such as a faster or more cost-effective one, without affecting the agent's primary model. Injecting a full `Agent` (with its own tools, hooks, or plugins) for summarization is not currently supported in TypeScript.

```typescript
--8<-- "user-guide/concepts/agents/conversation-management_imports.ts:summarizing_conversation_manager_advanced_imports"

--8<-- "user-guide/concepts/agents/conversation-management.ts:summarizing_conversation_manager_advanced"
```

</Tab>
</Tabs>

#### Key Features

- **Context Window Management**: Automatically reduces context when token limits are exceeded
- **Intelligent Summarization**: Uses structured bullet-point summaries to capture key information
- **Tool Pair Preservation**: Ensures tool use and result message pairs aren't broken during summarization
- **Flexible Configuration**: Customize summarization behavior through various parameters
- **Fallback Safety**: Handles summarization failures gracefully

## Proactive Context Compression

By default, conversation managers are reactive. They only reduce context after the model rejects a request with a context window overflow error. Proactive compression avoids wasting round-trips and output token starvation by triggering context reduction before the model call when the projected input token count exceeds a configurable threshold of the model's context window.

### Enabling Proactive Compression

<Tabs>
<Tab label="Python">

Pass `proactive_compression` to any built-in conversation manager. Use `True` for the default 0.7 threshold, or pass a dict with a custom `compression_threshold` ratio between 0 and 1. For example, `0.7` will trigger compression when 70% of the model's context window is used:

**With SlidingWindowConversationManager:**

```python
from strands import Agent
from strands.agent.conversation_manager import SlidingWindowConversationManager

agent = Agent(
    conversation_manager=SlidingWindowConversationManager(
        window_size=50,
        proactive_compression={"compression_threshold": 0.7},
    ),
)
```

**With SummarizingConversationManager:**

```python
from strands import Agent
from strands.agent.conversation_manager import SummarizingConversationManager

agent = Agent(
    conversation_manager=SummarizingConversationManager(
        proactive_compression=True,
    ),
)
```

Without `proactive_compression`, only reactive overflow recovery is used.

</Tab>
<Tab label="TypeScript">

Pass `proactiveCompression` to any built-in conversation manager. Use `true` for the default 0.7 threshold, or pass an object with a custom `compressionThreshold` ratio between 0 and 1. For example, `0.7` will trigger compression when 70% of the model's context window is used:

**With SlidingWindowConversationManager:**

```typescript
--8<-- "user-guide/concepts/agents/conversation-management_imports.ts:proactive_sliding_window_imports"

--8<-- "user-guide/concepts/agents/conversation-management.ts:proactive_sliding_window"
```

**With SummarizingConversationManager:**

```typescript
--8<-- "user-guide/concepts/agents/conversation-management_imports.ts:proactive_summarizing_imports"

--8<-- "user-guide/concepts/agents/conversation-management.ts:proactive_summarizing"
```

Without `proactiveCompression`, only reactive overflow recovery is used.

</Tab>
</Tabs>

### How It Works

Before each model call, the agent estimates the projected input token count and attaches it to the `BeforeModelCallEvent`. When proactive compression is configured, the conversation manager compares this estimate against the model's `contextWindowLimit`:

```
if projectedInputTokens / contextWindowLimit >= compressionThreshold:
    reduce()  // proactively compress context
```

Each conversation manager uses the same reduction logic for proactive compression as reactive overflow recovery. Proactive compression is best-effort only, so if `reduce()` throws or returns `false`, the error is swallowed and the model call proceeds normally.

Because `BeforeModelCallEvent` triggers before every model call including calls within a tool-use cycle, this provides automatic in-loop compression. If an agent makes five tool calls in a single invocation and context grows past the threshold between calls three and four, compression triggers before call four.

### Context Window Limit

The threshold check requires the model's context window size. The SDK auto-populates `contextWindowLimit` from built-in lookup tables ([Python](https://github.com/strands-agents/harness-sdk/blob/main/strands-py/src/strands/models/_defaults.py), [TypeScript](https://github.com/strands-agents/harness-sdk/blob/main/strands-ts/src/models/defaults.ts)) for known models. You can override it manually for models not in the lookup table:

<Tabs>
<Tab label="Python">

```python
model = BedrockModel(
    model_id="my-custom-model",
    context_window_limit=128_000,
)
```

</Tab>
<Tab label="TypeScript">

```typescript
const model = new BedrockModel({
  modelId: 'my-custom-model',
  contextWindowLimit: 128_000,
})
```

</Tab>
</Tabs>

:::caution[Inaccurate compression with default fallback]
If `contextWindowLimit` is not set and the model ID is not in the built-in lookup table, the SDK falls back to a default of 200,000 tokens. When the default token limit is used but the model's actual context window is significantly different, proactive compression will not behave correctly.
:::

### Token Estimation

The agent estimates input tokens using the following strategy:

1. **Known baseline**: Reads `inputTokens + outputTokens` from the last assistant message's `metadata.usage`
2. **Delta estimation**: Only estimates tokens for new messages added since that assistant message using the model's `countTokens()` method
3. **Cold start fallback**: When no prior usage metadata exists (first call or after session restore without metadata), estimates all messages via `countTokens()`

The `countTokens()` method uses a character-based heuristic to estimate token count by default (characters ÷ 4 for text, characters ÷ 2 for JSON). Some model providers support native token counting APIs for exact counts, which can be enabled on the model. See the Token Counting section on each provider's page for details and instructions:

- [Amazon Bedrock](../model-providers/amazon-bedrock/#token-counting)
- [Anthropic](../model-providers/anthropic/#token-counting)
- [Google Gemini](../model-providers/google/#token-counting)
- [OpenAI Responses](../model-providers/openai-responses/#token-counting)
- [llama.cpp](../model-providers/llamacpp/#token-counting)

## Creating a ConversationManager

<Tabs>
<Tab label="Python">

To create a custom conversation manager, implement the [`ConversationManager`](@api/python/strands.agent.conversation_manager.conversation_manager#ConversationManager) interface, which is composed of three key elements:

1. [`apply_management`](@api/python/strands.agent.conversation_manager.conversation_manager#ConversationManager.apply_management): This method is called after each event loop cycle completes to manage the conversation history. It's responsible for applying your management strategy to the messages array, which may have been modified with tool results and assistant responses. The agent runs this method automatically after processing each user input and generating a response.

2. [`reduce_context`](@api/python/strands.agent.conversation_manager.conversation_manager#ConversationManager.reduce_context): This method is called when the model's context window is exceeded (typically due to token limits). It implements the specific strategy for reducing the window size when necessary. The agent calls this method when it encounters a context window overflow exception, giving your implementation a chance to trim the conversation history before retrying.

3. `removed_message_count`: This attribute is tracked by conversation managers, and utilized by [Session Management](./session-management.md) to efficiently load messages from the session storage. The count represents messages provided by the user or LLM that have been removed from the agent's messages, but not messages included by the conversation manager through something like summarization.

4. `register_hooks` (optional): Override this method to integrate with [hooks](./hooks.md). This enables proactive context management patterns, such as trimming context before model calls. Always call `super().register_hooks` when overriding.

See the [SlidingWindowConversationManager](https://github.com/strands-agents/harness-sdk/blob/main/strands-py/src/strands/agent/conversation_manager/sliding_window_conversation_manager.py) implementation as a reference example.
</Tab>
<Tab label="TypeScript">

To create a custom conversation manager, extend the abstract [`ConversationManager`](@api/typescript/ConversationManager) base class and implement the `reduce` method:

1. **`reduce(options: ReduceOptions): boolean`**: Called in two scenarios: reactively when a `ContextWindowOverflowError` occurs (`options.error` is set), and proactively before model calls that exceed the compression threshold (`options.error` is `undefined`). Mutate `agent.messages` in place to reduce history, then return `true` if any reduction was made. When `error` is set, returning `false` lets the error propagate out of the agent loop uncaught. When `error` is `undefined`, returning `false` or throwing is safe — the model call proceeds regardless.

2. **`initAgent(agent)` (optional)**: Override to add proactive management (e.g. trimming after each invocation). Always call `super.initAgent(agent)` to preserve the built-in overflow recovery and proactive compression hooks.

```typescript
--8<-- "user-guide/concepts/agents/conversation-management_imports.ts:custom_conversation_manager_imports"

--8<-- "user-guide/concepts/agents/conversation-management.ts:custom_conversation_manager"
```

For proactive management alongside overflow recovery, override `initAgent`:

```typescript
--8<-- "user-guide/concepts/agents/conversation-management_imports.ts:custom_conversation_manager_proactive_imports"

--8<-- "user-guide/concepts/agents/conversation-management.ts:custom_conversation_manager_proactive"
```

See the [SlidingWindowConversationManager](https://github.com/strands-agents/harness-sdk/blob/main/strands-ts/src/conversation-manager/sliding-window-conversation-manager.ts) implementation as a reference example.
</Tab>
</Tabs>
