diff --git a/site/src/content/docs/user-guide/concepts/agents/conversation-management.mdx b/site/src/content/docs/user-guide/concepts/agents/conversation-management.mdx index cd54b5e012..9eb9ebc257 100644 --- a/site/src/content/docs/user-guide/concepts/agents/conversation-management.mdx +++ b/site/src/content/docs/user-guide/concepts/agents/conversation-management.mdx @@ -107,6 +107,7 @@ Key features of the `SlidingWindowConversationManager`: 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. +- **Message Pinning**: Protect specific messages from trimming during context reduction. See [Message Pinning](#message-pinning). - **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**: @@ -325,9 +326,95 @@ Pass a custom `model` to `SummarizingConversationManager` to override the model - **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 +- **Message Pinning**: Protect specific messages from summarization during context reduction. See [Message Pinning](#message-pinning). - **Flexible Configuration**: Customize summarization behavior through various parameters - **Fallback Safety**: Handles summarization failures gracefully +## Message Pinning + +Message pinning protects specific messages from eviction during context reduction. Pinned messages survive both sliding-window trimming and summarization, which makes pinning useful for preserving system prompts, critical instructions, or key decisions that must remain in the conversation regardless of length. + +Messages are pinned by setting `metadata.custom.pinned = true` on the message object. The SDK provides both a declarative configuration (`pin_first` / `pinFirst`) and runtime utility functions for programmatic control. + +### Protecting Initial Messages with `pin_first` + +Both `SlidingWindowConversationManager` and `SummarizingConversationManager` accept a `pin_first` (Python) / `pinFirst` (TypeScript) parameter that permanently protects the first N messages from eviction. This is the simplest way to preserve system prompts or initial instructions across all context reductions. + + + + +```python +from strands import Agent +from strands.agent.conversation_manager import SlidingWindowConversationManager + +agent = Agent( + conversation_manager=SlidingWindowConversationManager( + window_size=40, + pin_first=1, + ) +) +``` + +The same parameter works with `SummarizingConversationManager`: + +```python +from strands.agent.conversation_manager import SummarizingConversationManager + +agent = Agent( + conversation_manager=SummarizingConversationManager( + pin_first=2, + ) +) +``` + + + + +```typescript +--8<-- "user-guide/concepts/agents/conversation-management_imports.ts:pin_first_imports" + +--8<-- "user-guide/concepts/agents/conversation-management.ts:pin_first" +``` + +The same parameter works with `SummarizingConversationManager`. + + + + +The pin metadata is written during the first context reduction and remains set permanently, protecting those messages through all subsequent reductions. + +### Pinning Messages at Runtime + +For dynamic control, use the utility functions to pin or unpin messages by index. This enables agents or application logic to mark messages as important based on their content rather than position. + + + + +```python +from strands.agent.conversation_manager import pin_message, unpin_message, is_pinned + +pin_message(agent.messages, 0) + +if is_pinned(agent.messages, 0): + print("Message is protected from eviction") + +unpin_message(agent.messages, 0) +``` + + + + +```typescript +--8<-- "user-guide/concepts/agents/conversation-management_imports.ts:pin_message_imports" + +--8<-- "user-guide/concepts/agents/conversation-management.ts:pin_message_usage" +``` + + + + +**Tool-pair partner protection**: Pinning a `toolUse` message automatically protects its adjacent `toolResult` partner (matched by `toolUseId`), and vice versa. This prevents orphaned tool interactions in the conversation history. + ## 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. diff --git a/site/src/content/docs/user-guide/concepts/agents/conversation-management.ts b/site/src/content/docs/user-guide/concepts/agents/conversation-management.ts index 141781d716..467b2e3b6f 100644 --- a/site/src/content/docs/user-guide/concepts/agents/conversation-management.ts +++ b/site/src/content/docs/user-guide/concepts/agents/conversation-management.ts @@ -6,6 +6,9 @@ import { SlidingWindowConversationManager, SummarizingConversationManager, BedrockModel, + pinMessage, + unpinMessage, + isPinned, } from '@strands-agents/sdk' import { AnthropicModel } from '@strands-agents/sdk/models/anthropic' import type { LocalAgent, ConversationManagerReduceOptions } from '@strands-agents/sdk' @@ -165,3 +168,26 @@ async function proactiveSummarizing() { // --8<-- [end:proactive_summarizing] } +async function pinFirstExample() { + // --8<-- [start:pin_first] + const agent = new Agent({ + conversationManager: new SlidingWindowConversationManager({ + windowSize: 40, + pinFirst: 1, + }), + }) + // --8<-- [end:pin_first] +} + +async function pinMessageUsage() { + const agent = new Agent({}) + // --8<-- [start:pin_message_usage] + pinMessage(agent.messages, 0) + + if (isPinned(agent.messages, 0)) { + console.log('Message is protected from eviction') + } + + unpinMessage(agent.messages, 0) + // --8<-- [end:pin_message_usage] +} diff --git a/site/src/content/docs/user-guide/concepts/agents/conversation-management_imports.ts b/site/src/content/docs/user-guide/concepts/agents/conversation-management_imports.ts index 0b74f78a76..e2974288d4 100644 --- a/site/src/content/docs/user-guide/concepts/agents/conversation-management_imports.ts +++ b/site/src/content/docs/user-guide/concepts/agents/conversation-management_imports.ts @@ -50,3 +50,11 @@ import { Agent, SlidingWindowConversationManager } from '@strands-agents/sdk' // --8<-- [start:proactive_summarizing_imports] import { Agent, SummarizingConversationManager } from '@strands-agents/sdk' // --8<-- [end:proactive_summarizing_imports] + +// --8<-- [start:pin_first_imports] +import { Agent, SlidingWindowConversationManager } from '@strands-agents/sdk' +// --8<-- [end:pin_first_imports] + +// --8<-- [start:pin_message_imports] +import { pinMessage, unpinMessage, isPinned } from '@strands-agents/sdk' +// --8<-- [end:pin_message_imports] diff --git a/strands-py/src/strands/agent/conversation_manager/__init__.py b/strands-py/src/strands/agent/conversation_manager/__init__.py index 9f6d54ff98..62982027bf 100644 --- a/strands-py/src/strands/agent/conversation_manager/__init__.py +++ b/strands-py/src/strands/agent/conversation_manager/__init__.py @@ -16,6 +16,7 @@ from .conversation_manager import ConversationManager, ProactiveCompressionConfig from .null_conversation_manager import NullConversationManager +from .pin_message import is_pinned, pin_message, unpin_message from .sliding_window_conversation_manager import SlidingWindowConversationManager from .summarizing_conversation_manager import SummarizingConversationManager @@ -25,4 +26,7 @@ "ProactiveCompressionConfig", "SlidingWindowConversationManager", "SummarizingConversationManager", + "is_pinned", + "pin_message", + "unpin_message", ] diff --git a/strands-ts/src/conversation-manager/index.ts b/strands-ts/src/conversation-manager/index.ts index 9ebbffd703..b19fffffe4 100644 --- a/strands-ts/src/conversation-manager/index.ts +++ b/strands-ts/src/conversation-manager/index.ts @@ -19,3 +19,4 @@ export { SummarizingConversationManager, type SummarizingConversationManagerConfig, } from './summarizing-conversation-manager.js' +export { isPinned, pinMessage, unpinMessage } from './pin-message.js' diff --git a/strands-ts/src/index.ts b/strands-ts/src/index.ts index 24d3aea976..7c56235cab 100644 --- a/strands-ts/src/index.ts +++ b/strands-ts/src/index.ts @@ -268,6 +268,7 @@ export { SummarizingConversationManager, type SummarizingConversationManagerConfig, } from './conversation-manager/summarizing-conversation-manager.js' +export { isPinned, pinMessage, unpinMessage } from './conversation-manager/pin-message.js' // Logging export { configureLogging } from './logging/logger.js'