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

feat: add context manager class#1115

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

feat: add context manager class#1115
lizradway wants to merge 1 commit into
strands-agents:mainfrom
lizradway:context-manager

Conversation

@lizradway
Copy link
Copy Markdown
Member

Description

Implements the v2 contextManager class as designed in strands-agents/docs#831.

Adds a contextManager parameter to AgentConfig that pre-composes the SDK's context management primitives into a single configuration surface. The ContextManager class is a plugin that owns shared infrastructure (storage, token estimation, budget tracking) and composes sub-plugins (ContextCompression, ContextOffloader) that handle the actual behavior.

Architecture

ContextManager (Plugin)
├── Token estimation + budget tracking (BeforeModelCallEvent hook)
├── ContextCompression (sub-plugin)
│   ├── Proactive compression (BeforeModelCallEvent)
│   ├── Reactive overflow recovery (AfterModelCallEvent)
│   └── Sliding window enforcement (AfterInvocationEvent)
└── ContextOffloader (sub-plugin)
    ├── Tool result caching (AfterToolCallEvent)
    └── retrieve_offloaded_content tool

Sub-plugins work independently when used standalone (without ContextManager). User-provided plugins with matching names take precedence over managed sub-plugins. When contextManager is set, ContextCompression takes priority over conversationManager.

What ships

  • ContextManager class (Plugin) — owns token estimation, budget tracking, composes sub-plugins
  • ContextCompression plugin — proactive/reactive compression with own reduction logic (truncate or summarize strategies via discriminated union config)
  • ContextStrategy const{ Auto: 'auto' } for strategy discoverability
  • CompressionStrategy const{ Truncate: 'truncate', Summarize: 'summarize' }
  • estimateInputTokens() utility — shared token estimation extracted to src/context-manager/token-estimation.ts
  • protectedMessageRange — positive protects first N, negative protects last N messages from eviction
  • conversationManager marked as pending deprecation — still works, JSDoc-tagged

Public API Surface

New exports:

  • ContextManager (class, Plugin)
  • ContextStrategy (const)
  • ContextCompression (class, Plugin)
  • CompressionStrategy (const)
  • Types: ContextManagerParam, ContextManagerConfig, ContextStrategyValue, ToolResultCacheConfig, CompressionConfig, ContextCompressionConfig, CompressionStrategyValue, TruncateCompressionConfig, SummarizeCompressionConfig

New on AgentConfig:

  • contextManager?: ContextManagerParam

Defaults (benchmark-validated)

Parameter Default Source
toolResultCache.threshold 2500 tokens Benchmark winner
toolResultCache.previewTokens 500 tokens Benchmark winner
compression.proactive true (threshold 0.7) Benchmark sweet spot
compression.strategy "truncate" Benchmark: summarize is worse
compression.windowSize 40 Benchmark sweet spot
storage InMemoryStorage Serverless-friendly default

v2 Deprecation Plan

The following will be deprecated in v2 and removed in v3:

  • AgentConfig.conversationManagercontextManager.compression
  • Agent._estimateInputTokens()ContextManager hook + shared utility
  • BeforeModelCallEvent.projectedInputTokenscm.budget
  • ConversationManager, SlidingWindowConversationManager, SummarizingConversationManager, NullConversationManagerContextCompression plugin
  • vended-plugins/context-offloader/ module → context-manager/tool-result-cache/

Breaking Changes

None. All changes are additive. Existing behavior is unchanged when contextManager is not set.

Related Issues

Documentation PR

strands-agents/docs#831

Type of Change

New feature

Testing

How have you tested the change?

  • I ran npm run check
  • All 2532 existing unit tests pass (no regressions)
  • Type check passes (npm run type-check)
  • Lint passes (npm run lint)

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 29, 2026 15:12 — 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 29, 2026
Comment thread strands-ts/src/agent/agent.ts Outdated
Comment thread strands-ts/src/agent/agent.ts
Comment thread strands-ts/src/context-manager/context-manager.ts
Comment thread strands-ts/src/context-manager/context-manager.ts
Comment thread strands-ts/src/context-manager/compression/strategies/summarize.ts
Comment thread strands-ts/src/context-manager/compression/strategies/truncate.ts
...messagesToSummarize,
new Message({ role: 'user', content: [new TextBlock('Please summarize this conversation.')] }),
]

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.

Issue: The summarize function generates a summary with role: 'user' (line 97). This means the model will see an injected "user" message containing a conversation summary, which may confuse the model into thinking the user sent that summary. This is especially concerning since the summary format includes headings like "## Conversation Summary" and "## Tools Executed".

Suggestion: Consider using role: 'assistant' for the summary message, or wrapping it with clear delimiters indicating it's a system-generated summary. Many summarization implementations in conversation managers use a user message with an explicit framing like [Previous conversation summary: ...] to make the origin clear.

Comment thread strands-ts/src/context-manager/compression/strategies/truncate.ts
Comment thread strands-ts/src/context-manager/compression/context-compression.ts
@github-actions
Copy link
Copy Markdown
Contributor

Assessment: Request Changes

This PR introduces a well-architected ContextManager abstraction with a clean plugin composition model. However, there are several issues that need to be addressed before merging.

Review Categories
  • Testing: No tests are included for ~700 lines of new logic. The repository requires 80%+ coverage and this PR introduces complex behavioral code (compression strategies, token estimation, protected ranges, tool-pair preservation) that needs thorough unit testing.
  • Correctness: Several potential bugs in the trim/split logic (tool-pair preservation when protected ranges are used, adjustSplitForToolPairs stopping at tool-use messages), and a documentation/implementation mismatch where contextManager JSDoc promises coexistence with conversationManager but the code silently discards it.
  • Scope: The diff includes unrelated breaking changes (removal of InterventionHandler, takeSnapshot/loadSnapshot, InterruptEvent, and structured output logic refactoring) that should be in separate PRs or at minimum documented.
  • Performance: Duplicate token estimation calls when ContextManager composes ContextCompression — the parent and child plugins both independently estimate tokens in their BeforeModelCallEvent hooks.
  • API Design: The _resolveSubPlugins method is public despite being @internal, and estimateInputTokens is listed as shipped but not exported.

The architecture and composability model are solid — the plugin-composes-sub-plugins pattern is clean and extensible.

@github-actions github-actions Bot removed the strands-running <strands-managed> Whether or not an agent is currently running label May 29, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant