Repository navigation
Description
SDK Language
Python
Problem Statement
Support snapshot capture, restore, and snapshot-based session persistence for BidiAgent.
On upstream/main, Agent exposes take_snapshot() and load_snapshot(), but BidiAgent does not. SnapshotSessionManager.initialize() explicitly rejects Bidi. The shared repository session managers already support Bidi message history and application state through LocalAgent; snapshot support should extend that same design.
Proposed Solution
Share the snapshot contract through LocalAgent
Add take_snapshot() and load_snapshot() to the shared LocalAgent protocol and implement them on BidiAgent, with compatible signatures and the existing Snapshot envelope. This lets the session manager delegate capture and restore to the agent.
Follow the generic pattern established by SessionManager: a manager supporting both agent types implements the SessionManager[LocalAgent] contract. Widen the snapshot manager's common persistence methods accordingly and remove its Bidi rejection once the required surface exists.
Apply LocalAgent where an operation actually works for both agents:
| Area | Intended boundary |
|---|---|
| Snapshot capture/restore, save/list/restore APIs, and shared message/state handling | LocalAgent and its shared snapshot methods |
| Shared initialization/message hooks | AgentInitializedEvent[LocalAgent] and MessageAddedEvent[LocalAgent] where callbacks need explicit annotations |
| Regular invocation callback | Keep AfterInvocationEvent specific to Agent |
| Bidi stop callback | Keep BidiAgentStopEvent specific to BidiAgent; delegate the common save decision to shared logic |
| Conversation manager, model/interrupt internals, context-manager stash, and agent-level storage fallback | Keep concrete Agent handling, narrowing at the boundary where needed |
| Graph/Swarm persistence | Retain the existing MultiAgentBase path |
LocalAgent is the shared protocol, not itself a generic. Parameterize the interfaces that carry an agent type. In particular, review SnapshotTrigger, which currently accepts Agent: shared triggers must support LocalAgent, while existing Agent-only callback typing should remain valid for Agent-only use. Follow the existing generic/default conventions and cover both forms with typing tests.
Do not mechanically replace every Agent annotation, add unsupported state to LocalAgent, or introduce parallel save_bidi_snapshot/restore_bidi_snapshot APIs. If the change requires extensive casts, type ignores, or a new dispatch framework, reconsider where the shared boundary was applied.
Capture the fields Bidi actually owns
Add the Bidi snapshot field/preset handling alongside the existing snapshot definitions:
- The Bidi
"session"preset capturesmessagesand applicationstate, matching repository session persistence. - Support
system_promptthroughinclude, preserving its structured content-block representation.SnapshotSessionManageralready opts into this field. - Preserve
app_data, independent copies, and the rule that fields absent from a snapshot leave the target unchanged. - Preserve the existing
Agentpreset and its conversation-manager, interrupt, and model state. Bidi should not serialize placeholder versions of these fields merely to reuse that preset. - Validate explicitly requested unsupported fields consistently with the snapshot API. Keep the shared method signatures compatible rather than narrowing an override's accepted keyword types.
This restores durable conversation/application state into a newly configured agent. Live connections, provider resumption handles, audio buffers, pending tasks, and invocation_state are outside this change.
Wire persistence to Bidi lifecycle events
SnapshotSessionManager overrides register_hooks(), so widening its type annotations alone will not provide Bidi persistence.
Restore synchronously through the existing shared initialization event, before Bidi opens its model connection. Reuse the shared message-added event for the "message" strategy.
Proposed initial completion boundary: use BidiAgentStopEvent, matching the repository session manager and the former Bidi invocation hook. Reuse the existing save/trigger decision:
"message"saves on each message and flushes final state at stop."invocation"saves at stop."trigger"saves when its trigger fires at stop, or when explicitly requested.- Preserve immutable-checkpoint behavior and the existing fallback save when a trigger raises.
Respect stop-hook ordering and cleanup-failure behavior. BidiResponseCompleteEvent describes an individual model response, not the lifetime of the streaming session; per-response checkpoint cadence can be considered separately.
Bidi currently has no agent-level storage or context_manager. Use the manager's supplied storage, or its local-file fallback, and guard Agent-only storage/stash handling. Reuse the existing serialization and storage layout.
Acceptance Criteria
- Bidi supports direct snapshot capture/restore, applicable presets and include/exclude options, structured prompts, independent copies, and missing-field behavior.
- A fresh
BidiAgentand manager over the same storage/session/agent IDs restore messages, application state, and the persisted prompt before model startup. - Bidi supports latest snapshots, immutable checkpoint creation/listing/restore, and session deletion through the existing APIs.
- Tests cover each save policy, trigger success/failure, final state changes at stop, and stop cleanup errors. Define and test restoration behavior while a Bidi connection is active; do not silently replace local state while the provider continues with different history.
- Typing tests exercise the shared snapshot contract and manager with both agents, including shared and Agent-only triggers. Replace the existing typing assertion that rejects a Bidi snapshot manager.
- Existing Agent snapshots, stateful-model restoration, redaction persistence, stash integration, and Graph/Swarm persistence continue to pass.
- Update the Bidi session-management and snapshot documentation with capture/restore and persistence examples, replacing the current Bidi unsupported statements.
Use Case
A voice assistant can resume persisted conversation history and application state after a process restart, or restore an earlier checkpoint through the same snapshot APIs used by regular agents.
Alternatives Solutions
Continue using FileSessionManager or S3SessionManager for message-log persistence. They already support Bidi, but do not provide the snapshot checkpoint/time-travel API.
Additional Context
Part of #1722. Related shared-typing work: #3764.
Reference PRs:
- feat(agent): add LocalAgent protocol #4091 introduced
LocalAgentand opt-in shared typing while preserving existing defaults. - refactor(python): share repository session methods through LocalAgent #4257 consolidated repository session methods through
LocalAgent, keeping Agent-only internal state handling local. - feat(bidi): share lifecycle hooks and add response completion hooks #4280 shared initialization/message hooks and introduced the response-complete and stop hooks. This review comment explicitly describes lifting the snapshot restriction once Bidi implements the required surface.
- feat: add snapshot session manager to python #3283 introduced
SnapshotSessionManager; feat(session-py): support snapshot session management for Graph and Swarm #4240 subsequently added Graph/Swarm support, which this change should preserve.
Implementation references inspected on September 24, 2026, at upstream/main commit 18becd79d70d131f5417631916ab154b099df0b9:
Metadata
Metadata
Assignees
Labels
Type
Fields
Language
Activity
refactor(bidi): Limit snapshot change to new methods
refactor(bidi): Keep snapshot field resolution in BidiAgent
fix(bidi): Match Agent restore semantics and document field contract
refactor(session): Type SnapshotSessionManager as SessionManager[Loca…
refactor(session): Restore save_snapshot as the single write path
test(session): Use BidiResponseStopEvent after upstream rename
test(session): Use tmp_path for the storage fallback test
test(session): Cover Agent storage resolution alongside Bidi
1 remaining item
docs(site): Rewind to the most recent checkpoint in the Bidi example
refactor(session): Make SnapshotSessionManager generic over the agent
docs(site): Type the Bidi snapshot manager as SnapshotSessionManager[…
docs(site): Tighten the SnapshotSessionManager typing note
refactor(session): Resolve agent-level storage from any LocalAgent
refactor(bidi): Provide sandbox and cancel_signal instead of raising
refactor(bidi): Raise NotImplementedError for unsupported LocalAgent …
refactor(session): Type SnapshotSessionManager on LocalAgent
docs(site): Drop the SnapshotSessionManager type subscript from Bidi …
refactor(bidi): Provide a real cancel_signal on BidiAgent
chore(tools): Leave decorator.py unchanged
fix(session): Preserve final Bidi state in snapshots
refactor(session): Keep snapshot manager extensible
refactor(session): Use the LocalAgent surface directly