Skip to content
This repository was archived by the owner on Jun 3, 2026. It is now read-only.

feat(context): add message pinning to conversation managers#1105

Draft
lizradway wants to merge 1 commit into
strands-agents:mainfrom
lizradway:pinning
Draft

feat(context): add message pinning to conversation managers#1105
lizradway wants to merge 1 commit into
strands-agents:mainfrom
lizradway:pinning

Conversation

@lizradway
Copy link
Copy Markdown
Member

@lizradway lizradway commented May 26, 2026

Description

Adds opt-in message pinning to conversation managers. Pinned messages are protected from eviction during context reduction — they survive both sliding-window trimming and summarization.

This lays the groundwork for the agentic context management strategy described in strands-agents/docs#831, where agents can protect important context from being lost.

API Surface

import { pinMessage, unpinMessage, isPinned, pinMessageTool } from '@strands-agents/sdk/context-manager/compression'

// Utilities
pinMessage(message: Message): Message
unpinMessage(message: Message): Message
isPinned(message: Message): boolean
isPinned(messages: Message[], index: number): boolean  // also checks tool-pair partner (matched by toolUseId)

// Tool (for agentic pinning/unpinning)
pinMessageTool  // tool({ name: 'pin_message', inputSchema: z.object({ index, action: 'pin' | 'unpin' }) })

// Config (on both SlidingWindowConversationManager and SummarizingConversationManager)
protectedMessageRange?: number  // positive = first N, negative = last N

What's included

  • pinMessage/unpinMessage utility functions (stores pin state in metadata.custom.pinned)
  • isPinned: overloaded function — isPinned(message) for raw check, isPinned(messages, index) also protects tool-pair partners matched by toolUseId (prevents orphaned toolUse/toolResult pairs)
  • pinMessageTool: agent-invokable tool built with the tool() factory + Zod, supports both pin and unpin actions (defaults to pin)
  • protectedMessageRange config on both conversation managers: positive = protect first N, negative = protect last N
  • SlidingWindowConversationManager skips protected messages during trimming and tool-result truncation
  • SummarizingConversationManager excludes protected messages from summarization, compacting them to the front before the summary message
  • Exported via package subpath: @strands-agents/sdk/context-manager/compression

Usage

import { pinMessage, pinMessageTool } from '@strands-agents/sdk/context-manager/compression'
import { Agent, SlidingWindowConversationManager } from '@strands-agents/sdk'

// Auto-protect first message (position-based)
const agent = new Agent({
  conversationManager: new SlidingWindowConversationManager({ protectedMessageRange: 1 }),
})

// Auto-protect last 2 messages
new SlidingWindowConversationManager({ protectedMessageRange: -2 })

// Pin programmatically
agent.messages[0] = pinMessage(agent.messages[0])

// Or let the agent decide what to pin/unpin via tool
const agent = new Agent({
  tools: [pinMessageTool, ...otherTools],
  conversationManager: new SlidingWindowConversationManager(),
})
// Agent can invoke: { index: 3, action: 'pin' } or { index: 3, action: 'unpin' }

File structure

context-manager/
  compression/
    index.ts              — barrel export
    pin-message.ts        — utilities + tool
    __tests__/
      pin.test.ts
      pin-message-tool.test.ts

Related Issues

strands-agents/docs#831

Documentation PR

TK in context facade docs PR

Type of Change

New feature

Testing

How have you tested the change?

  • I ran npm run check
  • Unit tests for pinMessage/unpinMessage/isPinned overloads (18 tests)
  • Unit tests for pinMessageTool pin/unpin/default/validation (6 tests)
  • Integration tests for protectedMessageRange config on both managers (8 tests)
  • All existing sliding-window tests pass (50 tests)
  • All existing summarizing tests pass (15 tests)
  • Recall benchmark against Bedrock (claude-haiku-4-5) — 0% recall without pinning vs 100% with pinning across 20-turn conversations

Checklist

  • I have read the CONTRIBUTING document
  • I have added any necessary tests that prove my fix is effective or my feature works
  • I have updated the documentation accordingly
  • I have added an appropriate example to the documentation to outline the feature, or no new docs are needed
  • My changes generate no new warnings
  • Any dependent changes have been merged and published

By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.

@lizradway lizradway temporarily deployed to manual-approval May 26, 2026 18:56 — with GitHub Actions Inactive
@github-actions github-actions Bot added the strands-running <strands-managed> Whether or not an agent is currently running label May 26, 2026
Comment thread strands-ts/src/context-manager/compression/pin-message.ts
Comment thread strands-ts/src/context-manager/pin-message.ts Outdated
@github-actions
Copy link
Copy Markdown
Contributor

Assessment: Comment

Clean, well-designed feature that adds opt-in message pinning. The API surface is intuitive and the PR description is thorough. A few things need attention before merging.

Review Categories
  • Robustness: The pinMessageTool callback needs bounds checking to prevent crashes on invalid agent-provided indices.
  • Code duplication: _isProtected and _protectedMessages are duplicated across both conversation managers — consider moving to the base class.
  • Documentation: Exported pinMessage/unpinMessage are missing required TSDoc, and AGENTS.md needs the new context-manager/ directory.
  • Test coverage: No tests for the protectedMessages config integration in either conversation manager's test suite.
  • Behavioral clarity: Protected messages in the summarizing manager get compacted to the front of the array — worth documenting or confirming intentional.

Solid foundation for agentic context management — the utility functions are clean and well-tested.

@github-actions github-actions Bot removed the strands-running <strands-managed> Whether or not an agent is currently running label May 26, 2026
@lizradway lizradway temporarily deployed to manual-approval May 26, 2026 19:09 — with GitHub Actions Inactive
@github-actions github-actions Bot added the strands-running <strands-managed> Whether or not an agent is currently running label May 26, 2026
Comment thread strands-ts/src/context-manager/compression/pin-message.ts
Comment thread strands-ts/src/context-manager/compression/pin-message.ts
Comment thread strands-ts/src/context-manager/pin-message.ts Outdated
@github-actions
Copy link
Copy Markdown
Contributor

Assessment: Comment

Well-designed feature with a clean, composable API. The implementation is solid, tests cover the key scenarios (positive/negative protection, all-protected edge case, pinned-in-middle), and the PR description is thorough with good examples.

Review Themes
  • Documentation gaps: isPinned overloads and pinMessageTool need full TSDoc (@param, @returns) per AGENTS.md requirements. AGENTS.md directory structure needs updating.
  • Code duplication: _isProtected + _protectedMessages field are identical in both conversation managers — should live on the base class.
  • Correctness (minor): Tool-pair partner protection uses adjacency without verifying matching toolUseId, which could protect unrelated messages in edge cases.
  • Design clarity: Protected message compaction in the summarizing manager changes conversation ordering — worth a brief inline comment documenting the rationale.

Nice work on the bounds checking, warning logging for the all-protected case, and the immutable pinMessage/unpinMessage design.

@github-actions github-actions Bot removed the strands-running <strands-managed> Whether or not an agent is currently running label May 26, 2026
@lizradway lizradway temporarily deployed to manual-approval May 26, 2026 19:46 — with GitHub Actions Inactive
@github-actions github-actions Bot added the strands-running <strands-managed> Whether or not an agent is currently running label May 26, 2026
@github-actions
Copy link
Copy Markdown
Contributor

Assessment: Comment

Previous review items have been well-addressed (TSDoc, toolUseId matching, bounds checking). Three items remain from the prior round.

Remaining Items
  • Code duplication: _isProtected + _protectedMessages still identically duplicated in both managers — should live on the base class.
  • Repository guidelines: AGENTS.md directory structure needs the new context-manager/ directory.
  • Consistency: Summarizing manager doesn't log a warning when all messages are protected (sliding-window does).

@github-actions github-actions Bot removed the strands-running <strands-managed> Whether or not an agent is currently running label May 26, 2026
@lizradway lizradway marked this pull request as ready for review May 26, 2026 19:55
@github-actions
Copy link
Copy Markdown
Contributor

Assessment: Comment

Clean, well-structured feature. The previous review issues have been addressed (TSDoc, toolUseId matching, bounds checking, tests for both managers). Four items remain.

Remaining Items
  • Code duplication (Important): _isProtected + _protectedMessages field are identical across both conversation managers — should live on the base ConversationManager class per AGENTS.md's duplication rule.
  • Repository guidelines (Important): AGENTS.md directory structure and "Directory Purposes" need the new context-manager/ directory.
  • Consistency (Suggestion): Summarizing manager silently returns false when all messages are protected; sliding-window logs a warning in the same scenario.
  • Test coverage (Suggestion): Missing test for the pinMessageTool out-of-bounds error path.

The API design is composable and well-documented — the overloaded isPinned with tool-pair partner awareness is a nice touch.

@github-actions github-actions Bot removed the strands-running <strands-managed> Whether or not an agent is currently running label May 26, 2026
@github-actions github-actions Bot added the strands-running <strands-managed> Whether or not an agent is currently running label May 26, 2026
@github-actions
Copy link
Copy Markdown
Contributor

Assessment: Approve

All prior review feedback has been addressed — warning log added in the summarizing manager, out-of-bounds test added for pinMessageTool, TSDoc complete, toolUseId matching in place. One minor item remains.

Remaining Item
  • Repository guidelines: AGENTS.md directory structure and "Directory Purposes" still need the new context-manager/ directory entry (required per AGENTS.md).

Clean, well-tested feature with a composable API surface. The isPinned overload with tool-pair awareness is a thoughtful design choice that prevents orphaned pairs automatically.

@github-actions github-actions Bot removed the strands-running <strands-managed> Whether or not an agent is currently running label May 26, 2026
@lizradway lizradway added needs-api-review area-context Items associated with context management labels May 26, 2026
Comment thread strands-ts/src/context-manager/compression/pin-message.ts
}

/**
* Returns a new Message marked as pinned (protected from eviction during context reduction).
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the PR, can you give reasoning towards why immutable vs modifying the message directly?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Message (+ metadata) are readonly :/

I think metadata being readonly is extremely unnecessary since this isn't getting sent over the wire. thoughts on changing this?

* When added to an agent's tools array, allows the agent to protect important
* messages from eviction during context reduction.
*/
export const pinMessageTool = tool({
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is the first time we're vending a tool that is not on a class of some sort; did we have alternative suggestions as to where this should live?

Copy link
Copy Markdown
Member Author

@lizradway lizradway May 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i thought about both vended tool and just community tool, but i think the path forward is for this to live/be added to the agent in the ContextManager class once it exists (via addTools on plugin)

* Protected messages are compacted to the front of the array before the summary message.
* For agent-controlled pinning, add `pinMessageTool` to the agent's tools array.
*/
protectedMessages?: number
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a case where we'd want to support both? (can be a follow-up)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

naming; thoughts on proectedMessageCount?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

both as in both agent controlled and manually?
or both as in first/last N?

because yes to both lol. agentic management context should always be able to exist along side framework-managed.

hm i agree im not sold on the name protectedMessages since it doesn't specify much, but i think proectedMessageCount suggests an actual count without the ordering part. i can brainstorm some alternatives

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

protectedMessageRange?
protectedMessageWindow?

@lizradway lizradway marked this pull request as draft May 28, 2026 14:13
@github-actions github-actions Bot added the strands-running <strands-managed> Whether or not an agent is currently running label May 28, 2026
@github-actions
Copy link
Copy Markdown
Contributor

Assessment: Approve

All previously raised issues have been addressed (TSDoc, bounds checking, toolUseId matching, warning log consistency, test coverage). The implementation is clean, well-tested (96 tests pass), and the API surface is composable and intuitive.

Remaining Item
  • Repository guidelines: AGENTS.md needs the new context-manager/compression/ directory in both the tree and "Directory Purposes" section.

Note: @zastrowm has open design questions (immutable vs mutable, tool vending pattern, naming) that should be resolved before merge.

@github-actions github-actions Bot removed the strands-running <strands-managed> Whether or not an agent is currently running label May 28, 2026
*
* For agent-controlled pinning, add `pinMessageTool` to the agent's tools array.
*/
protectedMessageRange?: number
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To me, range is misleading given that we're not giving a range available. What about making it an object and giving more freedom to the user?

protectedMessages?: { first?: number; last?: number }

Makes it more extensible (I can see use cases for last), and solves the naming problem

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

area-context Items associated with context management needs-api-review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants