Skip to content

feat(session): deprecate repository-based managers and add migration guide - #4797

Open
nicolas-m-romero wants to merge 42 commits into
mainfrom
agent-tasks/3663-session-migration-guide
Open

nicolas-m-romero wants to merge 42 commits into
mainfrom
agent-tasks/3663-session-migration-guide

Conversation

@nicolas-m-romero

@nicolas-m-romero nicolas-m-romero commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor

Human Overview

Proposed migration towards SnapshotSessionManager. Entirely docs based with some example code of what the users custom solution would look like. PR outlines migration towards SnapshotSessionManager away from the previously implemented session managers in the SDK. Will outline the basic information customers can use to move their sessions to the SnapshotSessionManager. PR will also handle the deprecation of the other managers as well as the doc changes stating the deprecation.

Description

Motivation

Python currently exposes snapshot-based session persistence alongside the older repository-based managers, leaving two storage formats and no clear default for new applications. This change begins convergence on SnapshotSessionManager without removing existing behavior: RepositorySessionManager, FileSessionManager, and S3SessionManager now emit deprecation warnings and remain functional until their removal in v2.0.0.

Existing repository-format data cannot be read directly by SnapshotSessionManager. The migration guide provides a non-destructive reference procedure that restores each session through its existing manager and saves the reconstructed state as a snapshot before application cutover.

Public API Changes

The three repository-based session manager classes are now decorated with typing_extensions.deprecated. IDEs, type checkers, and warning-enabled runtimes direct users to the corresponding snapshot configuration and standalone migration guide.

# Deprecated; remains functional until v2.0.0
session_manager = FileSessionManager(
    session_id="user-123",
    storage_dir="./sessions",
)

# Replacement for new and migrated sessions
session_manager = SnapshotSessionManager(
    session_id="user-123",
    storage=LocalFileStorage("./sessions"),
)

Compatibility

This deprecation is backward-compatible: the repository-based managers continue to work in v1. The reference migration script covers standard Agent sessions. Graph and Swarm sessions must start a new snapshot-backed session, while BidiAgent migrations require the adaptation described in the guide.

Related Issues

Resolves #3663
Resolves #3664

Documentation PR

The session-management overview and standalone repository-session migration guide are included under site/.

Type of Change

New feature

Testing

Not run locally. Repository CI will validate the Python and documentation changes.

  • I ran hatch run prepare

Additional Details

Extended context

The deprecation warning links directly to the standalone migration guide. Source records remain unchanged during migration so users can verify the target snapshot before cutting over.

Checklist

  • I have read the CONTRIBUTING document
  • I have reviewed and understand every line of code in this PR, including any generated by AI tools, and I can explain why it works
  • My change is focused and reasonably small; I have split unrelated work into separate PRs
  • 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.

…hotSessionManager

Adds a 'Migrating from the message-log session managers' section to the
session-management guide: why there is no automatic migration, the state a
faithful migration must carry (history incl. redactions, agent state,
conversation-manager state with removed_message_count, model state/system
prompt, broken-tool-use repair), a non-destructive idempotent migrate_session()
procedure using the public API, verify/cut-over steps, and bulk migration.

Replaces the prior one-line 'migrate explicitly' stub with a cross-link to the
new section. Docs-only; no deprecation of the legacy managers.

Toward #3663.
@github-actions github-actions Bot added python Pull requests that update python code documentation Documentation changes, improvements, additions, content updates, site improvements, examples, guides area-sessions Related to session or session managment size/m labels Oct 1, 2026
…g sessions

Reframes the migration guide around the team's docs-first philosophy: lead with
why swapping the constructor silently starts the session empty and why the
message log cannot be converted from files alone (restore is configuration-
dependent), and present the procedure as a reference script the user adapts to
their own agent/session configuration rather than a one-size-fits-all command.

Expands the per-session guidance (reconstruct through production config, match
identifiers, conversation-manager offset, redactions, model state/system prompt,
broken tool-use pairs, never mutate the source, stop on an already-migrated
target), adds where to run it (offline, not in the request path / AgentCore
runtime instance) and how to discover sessions (SDK does not enumerate them).
Updates the example to stop on a non-empty target. Notes a first-class helper as
a possible future direction.

Toward #3663.
Remove the forward-looking note about a possible future migration helper and the
intro's link to it. Future direction belongs in the tracking issue, not the
user-facing docs.
…mistake

Swapping FileSessionManager/S3SessionManager for SnapshotSessionManager is a
legitimate change — fine for new sessions or when leaving old history behind.
Reword so the guide informs the user that existing session data is not carried
over unless migrated, rather than telling them they cannot swap.
Expand the 'before' Agent construction to the same multi-line form as the
'after', so the only visible difference between the two is the session manager.
Cut the migration section from blog-length prose to direct documentation: lead
sentences over paragraphs, lists over narration, and no persuasion or repeated
rationale. Fold 'what to pay attention to' into a compact 'What a migration must
carry' list, and merge the run/discover/bulk subsections into one 'Running the
migration'. All technical facts and the reference snippet are unchanged.
The verification steps are part of running a migration, not a standalone topic;
move them in as the final step of 'Running the migration'.
… guard

State up front why a migration is needed — production data persisted in the
deprecated message-log format must be moved, not just reconfigured. Correct the
already-migrated guard comment: restore_snapshot restores into the throwaway
agent (then discarded), it does not merely probe.
…nager

Lead with SnapshotSessionManager as the recommended manager rather than
framing the message-log managers as deprecated.
@nicolas-m-romero

Copy link
Copy Markdown
Contributor Author

@strandly-the-agent Can you give this docs PR a review

@github-actions github-actions Bot added strands-running complexity/low Touched functions have low cognitive complexity (<=10) labels Oct 27, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Assessment: Approve

c94325f ("docs(session): link migration guide from api docs") updates the deprecation docstrings and, more substantively, reworks the deprecation-warning mechanics. I verified the runtime behavior by running the suite locally.

Review notes
  • Warning mechanics (well done): Switching RepositorySessionManager/SessionRepository to @deprecated(..., category=None) (static __deprecated__ metadata, no decorator runtime warning) plus an explicit warnings.warn in __init__ guarded by self.__class__ is RepositorySessionManager correctly eliminates duplicate warnings. Since FileSessionManager/S3SessionManager inherit from both RepositorySessionManager and SessionRepository, the previous setup would have fired multiple warnings per instantiation. Confirmed via pytest (54 passed): direct RepositorySessionManager() → one warning; FileSessionManager() → exactly one (its own), no duplicate parent warning.
  • Tests: The two added tests (test_subclassing_does_not_emit_deprecation_warning, test_session_repository_has_static_deprecation_metadata) cover the new no-warn-on-subclassing contract and the static-metadata guarantee.
  • Docstring format: One non-blocking note left inline — the Markdown link syntax won't render in an RST docstring context; suggested RST hyperlink syntax to actually achieve the "clickable link" goal.

Solid fix for the double-warning problem with good test coverage.

@github-actions

Copy link
Copy Markdown
Contributor

Reviewed merge commit b38544d (merge of main into the feature branch).

  • Clean merge — the combined diff (git diff --cc) is empty, so there were no conflict-resolution / "evil-merge" edits.
  • The only deltas in PR-owned files versus the prior reviewed tip (c94325f) came from main, not this PR:
    • repository_session_manager.py (+5): a docstring note that the manager doesn't support an agent context_manager, appended cleanly to the same docstring the PR edited.
    • test_snapshot_session_manager.py: an unrelated invocation_context=ANY assertion update.
  • The PR's deprecation logic (@deprecated(category=None) + the __init__ warning guard) is untouched. Ran test_repository_session_manager.py on the merged tree → 54 passed.

No new PR-authored changes to review; prior Approve stands. The only outstanding item remains the non-blocking RST-vs-Markdown docstring-link suggestion from the previous round.

@github-actions

Copy link
Copy Markdown
Contributor

Reviewed merge commit ccf0cea (merge of newer main into the feature branch).

  • Clean merge — the combined diff (git diff --cc) is empty, so no conflict-resolution / "evil-merge" edits were introduced.
  • No PR-owned files changed versus the previously reviewed tip (b38544d): session/, session tests, and the migration guide (migrate-repository-sessions.mdx) are all identical.

Routine main-sync merge with no new PR-authored changes. Prior Approve stands.

…ion-migration-guide

# Conflicts:
#	site/src/content/docs/user-guide/sdk/agents/session-management.mdx
@github-actions

Copy link
Copy Markdown
Contributor

Reviewed merge commit d4d5ea3 (merge of newer main into the feature branch).

  • Clean merge — the combined diff (git diff --cc) is empty, so no conflict-resolution / "evil-merge" edits were introduced.
  • No PR-owned files changed versus the previously reviewed tip (ccf0cea): session/, session tests, and the migration guide are all identical.

Routine main-sync merge with no new PR-authored changes. Prior Approve stands.

@opieter-aws

Copy link
Copy Markdown
Contributor

Can you look at the CI tests that are currently red?

Also it seems there's a few pages that still steer users towards the deprecated SMs:

  • interrupts.mdx:369/409
  • migrate/langgraph.mdx:275/279 and langgraph.py:54
  • persistent-memory-with-session-managers.mdx:35–64, and the note at L80
  • harness-py/README.md:358

session_manager=source,
)

if not agent.messages and not agent.state.get():

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.

Stateful-model sessions restore only _model_state and no messages, so this rejects them. do they need their own migration path?

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.

I checked 27afc23 against the branch source (PYTHONPATH=strands-py/src). I seeded a FileSessionManager session that has only model_state={"response_id": "resp_123"} and no messages:

  • the updated guard accepts it and the migration writes the snapshot;
  • a fresh agent with SnapshotSessionManager on the same session_id restores model_state with 0 messages, as intended;
  • an unknown session still raises ValueError.

So stateful sessions don't need their own migration path; the existing script handles them now. The only remaining point is the private-attribute access, which I've commented on inline at L119.

Comment on lines +98 to +102
# Stop rather than overwrite an existing target snapshot.
if await snapshot.restore_snapshot(Agent(agent_id=agent_id)):
raise RuntimeError(
f"session={session_id!r} agent={agent_id!r} already migrated"
)

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.

Wouldn't this fail when loading into a default agent? Wondering if an interrupted batch can actually resume here, did you verify?

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.

I checked this against the branch source, since the latest commits don't change it:

  • Loading into a default agent works: restore_snapshot(Agent(agent_id=agent_id)) restores an existing snapshot into a bare agent without error. It returns False for a new target and True for one that was already migrated.
  • Resume doesn't work as the guide describes: on a second run the function raises RuntimeError("... already migrated"). A plain for sid in ids: await migrate_session(sid) therefore stops at the first session it already did, which contradicts L142 ("Keep each call independent and idempotent so an interrupted run can resume").

Suggestion (pick one):

  1. Make re-runs a no-op: return False (or log "skipped") instead of raising, and return True after a successful save. That makes the L142 claim true.
  2. Keep the raise, but change L142 to say the caller must catch the "already migrated" error to skip completed sessions when resuming.

Option 1 matches the "idempotent" wording better.

@github-actions

Copy link
Copy Markdown
Contributor

Reviewed merge commit 901bd78 (merge of newer main into the feature branch).

  • Clean merge: the combined diff (git diff --cc) is empty, so the merge added no conflict-resolution edits.
  • No PR files changed since d4d5ea3: session source, session tests and migrate-repository-sessions.mdx are all identical.
  • Main's edits to shared files don't affect this PR: navigation.yml changed upstream (new Vended Tools subsection), but the "Migrate repository-based sessions" entry is still there. Main made no changes under strands-py/src/strands/session/, so the APIs the guide refers to are the same.

This is a routine sync with main with no new changes from the PR author. Prior Approve stands.

@github-actions

github-actions Bot commented Nov 2, 2026

Copy link
Copy Markdown
Contributor

Reviewed merge commit e3ae8b6 (merge of newer main into the feature branch).

  • Clean merge: the combined diff (git diff --cc) is empty.
  • No PR files changed since 901bd78: session source and tests, migrate-repository-sessions.mdx, session-management.mdx and navigation.yml are all identical.
  • The 24 files brought in from main (mostly strands-ts) don't touch anything this PR depends on.

Routine sync with main. Prior Approve stands.

@github-actions

github-actions Bot commented Nov 2, 2026

Copy link
Copy Markdown
Contributor

Reviewed merge commit ee7c40b (merge of newer main into the feature branch).

  • Clean merge: the combined diff (git diff --cc) is empty.
  • No PR files changed since e3ae8b6: session source and tests, migrate-repository-sessions.mdx, session-management.mdx and navigation.yml are all identical.
  • Main changed S3Storage, which the guide names: the changes only add things. The constructor is the same; there are new read-only region_name / boto_session / prefix properties, search errors are now wrapped in StorageError, and delete() also removes the vector entry, but only when S3VectorSearchStrategy is configured. The guide's "use S3SessionManager and S3Storage" advice is still correct.

Routine sync with main. Prior Approve stands.

Nico Romero added 2 commits November 2, 2026 18:30
Stateful-model sessions restore only model state (the provider response ID),
so the no-source-data guard rejected them when the app stored no agent state.
Also note that the migrated session depends on the provider retaining that
response.
Replace FileSessionManager/S3SessionManager in the interrupts, LangGraph
migration, harness README, and persistent-memory course pages with
SnapshotSessionManager and a Storage backend. The course page now marks the
record-based managers shown in the video as deprecated and links to the
migration guide.
from strands import Agent, tool
from strands.multiagent import GraphBuilder
from strands.session import FileSessionManager
from strands.session import SnapshotSessionManager

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.

This is a false positive, so please don't apply the suggested fix. langgraph_imports.py only provides the import lines that langgraph.mdx:196 pulls in through --8<-- "user-guide/migrate/langgraph_imports.py:graph_migrated_imports", so every import in it is unused on purpose (the earlier FileSessionManager import was too). Removing these lines would drop SnapshotSessionManager / LocalFileStorage from the imports block shown on the LangGraph page. Dismiss the alert as "used in tests/docs" instead. This applies to the LocalFileStorage alert on L7 as well.

from strands.multiagent import GraphBuilder
from strands.session import FileSessionManager
from strands.session import SnapshotSessionManager
from strands.storage import LocalFileStorage

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.

Same as L6: this is a docs-snippet import file, so it's a false positive. Keep the import and dismiss the alert.

)

# Stateful models restore only model state, so check it as well.
if not agent.messages and not agent.state.get() and not agent._model_state:

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.

Important

Issue: The guard now reads agent._model_state, a private attribute, in user-facing sample code. This is the only place in the docs that uses it, and users who copy the script will depend on an internal field that can change without a deprecation cycle.

Suggestion: Use the public snapshot API instead. I checked it against the branch source and it returns the same value ({} for message-based sessions, {'response_id': ...} for stateful ones):

    # Stateful models restore only model state, so check it as well.
    model_state = agent.take_snapshot(include=["model_state"]).data["model_state"]
    if not agent.messages and not agent.state.get() and not model_state:

@github-actions

github-actions Bot commented Nov 2, 2026

Copy link
Copy Markdown
Contributor

Assessment: Comment

Reviewed 27afc23 and 08a641e, the first author changes since my earlier approval (the commits in between were merges of main). The stateful-model fix works, and the example updates cover every page from opieter-aws's list. One maintainer thread is still unresolved. My earlier Approve is withdrawn until that thread is resolved; the inline comment at L119 is not a blocker.

Review Categories
  • Correctness (verified by running the guide's script against the branch source): stateful sessions now migrate and restore model_state at cutover. Re-running the script still raises, though, so the "idempotent / can resume" wording at L142 isn't accurate yet (see my reply in the resume thread).
  • Public API usage: the guard should use the public take_snapshot(include=["model_state"]) rather than agent._model_state (see inline at L119).
  • Example updates: interrupts, LangGraph (.mdx and both .py files), harness README and the course page all now use SnapshotSessionManager, with the correct constructor signatures. These examples rely on interrupt state and Graph persistence, and SnapshotSessionManager supports both: the default session preset includes interrupt_state, and Graph and Swarm are attached through MultiAgentInitializedEvent. The harness README now matches the harness default (SnapshotSessionManager). The only remaining mentions of the old managers are in the 1.0 blog post, changelogs, and the intentional deprecated section of session-management.mdx, all of which are fine.
  • CI: Python, Docs and integration jobs are still running at this commit. The one completed failure, check-api-review-label, is the API-review gate: api/needs-review is set but api/review-complete isn't, so it needs reviewer sign-off rather than a code change.

Addressing the earlier feedback this thoroughly made the docs consistent across the site.

items:
- label: "Persist state across sessions"
slug: docs/user-guide/sdk/agents/session-management
- label: "Migrate repository-based sessions"

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.

Critical (CI blocker)

Issue: Adding this sidebar entry breaks Docs / CI. site/test/sidebar.test.ts:192 asserts that the Sessions group contains exactly one item, so it now fails with expected [ {…}, {…} ] to deeply equal [ {…} ]. This failed on 08a641e and I reproduced it locally on a8a2785 (npx vitest run test/sidebar.test.ts: 1 failed, 9 passed).

Suggestion: Update the expectation in site/test/sidebar.test.ts to include the new page. With this change the file passes locally (10/10):

    expect(groups[1]?.items).toEqual([
      { label: 'Persist state across sessions', slug: 'docs/user-guide/sdk/agents/session-management' },
      { label: 'Migrate repository-based sessions', slug: 'docs/user-guide/sdk/agents/migrate-repository-sessions' },
    ])

@github-actions

github-actions Bot commented Nov 3, 2026

Copy link
Copy Markdown
Contributor

Assessment: Request Changes

a8a2785 cleanly merges main into the branch: git diff --cc is empty and no PR-owned files changed since 08a641e. Checking the CI that has finished since my last review turned up a failure caused by this PR, which I missed last round because those jobs were still running.

Review Categories
  • CI (blocking): Docs / CI fails because the new sidebar entry doesn't match site/test/sidebar.test.ts. I reproduced it and confirmed a one-line fix locally (see inline on navigation.yml:264).
  • Still open from last round: the resume/idempotency behaviour at L142 and the agent._model_state access at L119. The author hasn't replied yet.
  • CodeQL unused-import alerts: these are false positives on the docs snippet file (replied in-thread). Don't apply the auto-fix.
  • Other red checks: I couldn't read their logs (403 without auth), so they're unconfirmed:
    • Python 3.14 unit tests failed on linux (08a641e) and macOS (this head). The other OS/version jobs were still running. The 271 session tests pass locally on 3.13; if 3.14 stays red, it's worth checking whether it's related to the deprecation warnings.
    • The integration failure shows Event loop is closed, which looks flaky.
    • codecov/patch is at 80% against a 90% target.
    • check-api-review-label still needs api/review-complete.
  • Merge impact: nothing from main affects the guide. The changed Python code is in decisions/, bedrock and sagemaker, and the site changes (sample guides, footer, last-updated dates) are additive. No new references to the deprecated managers were added.

This branch was successfully deployed

1 inactive deployment
auto-approve — a8a27859 Deployed Nov 3, 2026 by nicolas-m-romero via Build and Deploy Preview #3386
manual-approval — 54a4911e Deployed Oct 5, 2026 by nicolas-m-romero via Build and Deploy Preview #2378
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

api/needs-review Makes changes to the public API surface area-sessions Related to session or session managment complexity/low Touched functions have low cognitive complexity (<=10) documentation Documentation changes, improvements, additions, content updates, site improvements, examples, guides python Pull requests that update python code size/l

Projects

None yet

3 participants