Repository navigation
feat: add snapshot session manager to python #3283
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
opieter-aws
merged 15 commits into
strands-agents:main
from
opieter-aws:opieter-aws/session-checkpointing
Aug 5, 2026
Merged
Changes from 6 commits
Commits
Show all changes
15 commits
Select commit
Hold shift + click to select a range
e03f282
feat: add snapshot session manager to python
opieter-aws 0a479a2
Merge branch 'main' into opieter-aws/session-checkpointing
opieter-aws 2cb3714
Address comments
opieter-aws 50bd89c
Merge remote-tracking branch 'origin/opieter-aws/session-checkpointin…
opieter-aws 5b3120c
Extent to multi-agent
opieter-aws 2ffcf6e
Merge branch 'main' into opieter-aws/session-checkpointing
opieter-aws 37e7f6b
Merge branch 'main' into opieter-aws/session-checkpointing
opieter-aws 75c1770
Scope to single agent
opieter-aws aa11cff
Merge branch 'main' into opieter-aws/session-checkpointing
opieter-aws e3dd179
address comments
opieter-aws 0906fd7
Merge branch 'main' into opieter-aws/session-checkpointing
opieter-aws 5fa91a1
test(session): reuse the allowlisted S3 bucket for snapshot integ tests
opieter-aws b5a0bf1
Return snapshot id from save_snapshot and make restore_snapshot use l…
opieter-aws ed27173
Move uuidv7 to identifier file and address nits
opieter-aws 393f25d
Remove migration path
opieter-aws File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
272 changes: 174 additions & 98 deletions
272
site/src/content/docs/user-guide/concepts/agents/session-management.mdx
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,18 +1,28 @@ | ||
| """Session module. | ||
|
|
||
| This module provides session management functionality. | ||
|
|
||
| ``SnapshotSessionManager`` is the recommended path for new agents: it persists the whole | ||
| agent as a versioned snapshot to a unified :class:`~strands.storage.storage.Storage` backend | ||
| and supports checkpointing (time-travel restore). The repository-based managers | ||
| (``FileSessionManager``, ``S3SessionManager``, ``RepositorySessionManager``) persist each | ||
| message individually and are deprecated. | ||
| """ | ||
|
|
||
| from .file_session_manager import FileSessionManager | ||
| from .repository_session_manager import RepositorySessionManager | ||
| from .s3_session_manager import S3SessionManager | ||
| from .session_manager import SessionManager | ||
| from .session_repository import SessionRepository | ||
| from .snapshot_session_manager import SaveLatestStrategy, SnapshotSessionManager, SnapshotTrigger | ||
|
|
||
| __all__ = [ | ||
| "FileSessionManager", | ||
| "RepositorySessionManager", | ||
| "S3SessionManager", | ||
| "SaveLatestStrategy", | ||
| "SessionManager", | ||
| "SessionRepository", | ||
| "SnapshotSessionManager", | ||
| "SnapshotTrigger", | ||
| ] |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,73 @@ | ||
| """Monotonic UUIDv7 generation and validation for immutable snapshot ids. | ||
|
|
||
| Immutable snapshot ids are UUID version 7 (RFC 9562), matching the TypeScript SDK. The 48-bit | ||
| millisecond timestamp in the high bits makes lexicographic order equal creation order, so | ||
| listing returns oldest-first without a separate index. Ids are opaque handles that callers get | ||
| from ``list_snapshot_ids`` and never construct. | ||
| """ | ||
|
|
||
| import os | ||
| import re | ||
| import threading | ||
| import time | ||
| import uuid | ||
|
|
||
| # ``\A...\Z`` (not ``^...$``) so a trailing newline cannot slip past validation into a storage key. | ||
| _SNAPSHOT_ID_PATTERN = re.compile(r"\A[0-9a-f]{8}-[0-9a-f]{4}-7[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}\Z") | ||
|
|
||
| # UUIDv7 monotonic counter state (RFC 9562 method 2): within a single millisecond, ids that | ||
| # would otherwise collide are ordered by a counter seeded into the random bits, so sorting by | ||
| # the id preserves creation order even for sub-millisecond bursts. Guarded by a lock because | ||
| # snapshots can be written from the sync bridge and async paths on different threads. | ||
| _lock = threading.Lock() | ||
| _last_ms = -1 | ||
| _counter = 0 | ||
|
|
||
|
|
||
| def new_snapshot_id() -> str: | ||
| """Return a fresh, monotonic UUIDv7 immutable snapshot id. | ||
|
|
||
| Monotonic across calls so lexicographic order equals creation order, even for bursts that | ||
| mint more than 4096 ids inside one millisecond. | ||
| """ | ||
| global _last_ms, _counter | ||
|
|
||
| with _lock: | ||
| now_ms = int(time.time() * 1000) | ||
| if now_ms > _last_ms: | ||
| _last_ms = now_ms | ||
| # Seed in the low half of the 12-bit field (fresh randomness for uniqueness) so at | ||
| # least 2048 increments fit before overflow. | ||
| _counter = int.from_bytes(os.urandom(2), "big") & 0x07FF | ||
| else: | ||
| _counter += 1 | ||
| if _counter > 0x0FFF: | ||
| # Counter exhausted within this millisecond: borrow from the clock by advancing | ||
| # the timestamp 1ms (RFC 9562 method 1), keeping ids strictly increasing rather | ||
| # than wrapping the counter field backwards. | ||
| _last_ms += 1 | ||
| _counter = int.from_bytes(os.urandom(2), "big") & 0x07FF | ||
| counter = _counter | ||
| timestamp_ms = _last_ms | ||
|
|
||
| # Layout: 48-bit ms timestamp | version(7) | 12-bit counter | variant(0b10) | 62 random bits. | ||
| tail = int.from_bytes(os.urandom(8), "big") & ((1 << 62) - 1) | ||
| value = (timestamp_ms & ((1 << 48) - 1)) << 80 | ||
| value |= 0x7 << 76 | ||
| value |= (counter & 0x0FFF) << 64 | ||
| value |= 0b10 << 62 | ||
| value |= tail | ||
| return str(uuid.UUID(int=value)) | ||
|
|
||
|
|
||
| def validate_snapshot_id(snapshot_id: str) -> None: | ||
| """Validate that a string is an SDK-vended snapshot id (a UUIDv7). | ||
|
|
||
| Args: | ||
| snapshot_id: The string to validate. | ||
|
|
||
| Raises: | ||
| ValueError: If the string is not a valid snapshot id. | ||
| """ | ||
| if not _SNAPSHOT_ID_PATTERN.match(snapshot_id): | ||
| raise ValueError(f"'{snapshot_id}' is not a valid snapshot id") | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.