Skip to content

feat: add session manager implementation and related tests#569

Open
JackYPCOnline wants to merge 20 commits intostrands-agents:mainfrom
JackYPCOnline:session_manager
Open

feat: add session manager implementation and related tests#569
JackYPCOnline wants to merge 20 commits intostrands-agents:mainfrom
JackYPCOnline:session_manager

Conversation

@JackYPCOnline
Copy link
Contributor

@JackYPCOnline JackYPCOnline commented Feb 24, 2026

Description

Adds a SessionManager to the Strands Agents TypeScript SDK, enabling agents to persist and restore conversation state across invocations using pluggable storage backends (filesystem or S3).

Related issue

#80

Type of Change

New feature

New APIs Introduced

SessionManager

import { SessionManager } from '@strands-agents/sdk/session'

new SessionManager(config?: SessionManagerConfig)
Config Type Default Description
sessionId string 'default-session' Scopes snapshots to a session
agentId string 'default' Scopes snapshots within a session
storage.snapshot SnapshotStorage FileStorage in OS temp dir Pluggable storage backend
saveLatestOn 'message' | 'invocation' | 'never' 'message' When to auto-save latest snapshot
snapshotTrigger SnapshotTriggerCallback Callback to conditionally create immutable snapshots
loadSnapshotId string Restore a specific snapshot on agent initialization

Methods:

  • saveSnapshot({ target: Agent, isLatest: boolean }): Promise<void> — manually persist agent state
  • restoreSnapshot({ target: Agent, snapshotId?: string }): Promise<boolean> — restore agent state; returns false if no snapshot exists

Implements HookProvider — integrates with the agent lifecycle via InitializedEvent, MessageAddedEvent, and AfterInvocationEvent.


FileStorage

File-based SnapshotStorage backend. Default location: os.tmpdir()/strands-sessions.

import { FileStorage } from '@strands-agents/sdk/session'

new FileStorage(baseDir: string)

Storage layout:

<baseDir>/<sessionId>/scopes/agent/<agentId>/snapshots/
  snapshot_latest.json
  immutable_history/
    snapshot_00001.json
    snapshot_00002.json

S3Storage

S3-based SnapshotStorage backend. Auto-creates the bucket if it doesn't exist.

import { S3Storage } from '@strands-agents/sdk/session'

new S3Storage(config: S3StorageConfig)
Config Type Description
bucket string S3 bucket name
prefix string Optional key prefix
region string AWS region (default: us-east-1)
s3Client S3Client Pre-configured S3 client (mutually exclusive with region)

SnapshotStorage Interface

Implement to provide a custom backend:

interface SnapshotStorage {
  saveSnapshot(params): Promise<void>
  loadSnapshot(params): Promise<Snapshot | null>
  listSnapshotIds(params): Promise<string[]>
  loadManifest(params): Promise<SnapshotManifest>
  saveManifest(params): Promise<void>
}

SnapshotTriggerCallback

Controls when immutable snapshots are created, called after each agent invocation:

type SnapshotTriggerCallback = (params: SnapshotTriggerParams) => boolean

// Examples:
const everyFiveTurns: SnapshotTriggerCallback = ({ turnCount }) => turnCount % 5 === 0
const every60Seconds: SnapshotTriggerCallback = ({ lastSnapshotAt }) =>
  !!lastSnapshotAt && Date.now() - lastSnapshotAt > 60_000
const onLongConversation: SnapshotTriggerCallback = ({ agentData }) =>
  agentData.messages.length > 10

Usage Example

import { Agent } from '@strands-agents/sdk'
import { SessionManager, S3Storage } from '@strands-agents/sdk/session'

const session = new SessionManager({
  sessionId: 'my-session',
  storage: { snapshot: new S3Storage({ bucket: 'my-bucket' }) },
  saveLatestOn: 'invocation',
  snapshotTrigger: ({ turnCount }) => turnCount % 10 === 0,
})

const agent = new Agent({ sessionManager : session })
await agent.invoke('Hello!')

Files Changed

File Change
src/session/session-manager.ts New — SessionManager class
src/session/file-storage.ts New — FileStorage implementation
src/session/s3-storage.ts New — S3Storage implementation
src/session/storage.ts New — SnapshotStorage interface and types
src/session/types.ts New — Snapshot, SnapshotManifest, SnapshotTriggerCallback types
src/session/validation.ts New — validateIdentifier utility
src/session/__tests__/ New — unit tests
test/integ/session-manager.test.node.ts New — integration tests

Follow up

  • Manifest interface in unused for now since we don't write useful metadata. I will update the usage later.
  • Need to add new APIs : delete session and get latestImmutable snapshot id.

Testing

  • Unit tests for SessionManager, FileStorage, S3Storage, and validateIdentifier
  • Integration test: test/integ/session-manager.test.node.ts
  • Ran npm run check

Checklist

  • Tests added
  • No new warnings
  • Documentation updated
  • CONTRIBUTING read

@JackYPCOnline JackYPCOnline changed the title Session manager feat: add session manager implementation and related tests Feb 24, 2026
@JackYPCOnline JackYPCOnline marked this pull request as ready for review February 24, 2026 21:32
@github-actions github-actions bot added the strands-running <strands-managed> Whether or not an agent is currently running label Feb 25, 2026
@github-actions github-actions bot removed the strands-running <strands-managed> Whether or not an agent is currently running label Feb 25, 2026
* Manifest tracks snapshot metadata.
* Stored alongside snapshots to support versioning and future multi-agent patterns.
*/
export interface SnapshotManifest {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

SnapshotManifest is now unused, we don't create manifest while we create immutable_history, but it will be used in future, so keep it to follow initial design.

private _getHistorySnapshotKey(location: SnapshotLocation, snapshotId: string): string {
return this._getKey(location, `${IMMUTABLE_HISTORY}snapshot_${String(snapshotId).padStart(5, '0')}.json`)
validateIdentifier(snapshotId)
return this._getKey(location, `${IMMUTABLE_HISTORY}snapshot_${snapshotId}.json`)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This resolves an internal ticket

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants