StateChangeNotification over WebSocket every time a probe cycle detects that an application’s state has changed. The Python SDK wraps this in an async generator — you iterate over it with async for and receive events the moment they arrive, without any polling. Streaming is the right approach when your agent needs to react quickly to changes, build monitoring loops, or trigger actions as soon as a condition appears in state.
Basic streaming with session.watch()
session.watch() is the recommended entry point. It returns an async context manager that yields StateChangeNotification objects.
streaming_basic.py
StateChangeNotification fields
Every event the stream yields is aStateChangeNotification with these fields:
Refreshing state on each event
Events tell you which keys changed, but they don’t carry the new values. Callsession.refresh() inside the loop to pull the updated state, or enable auto_refresh_on_event=True to have the SDK do it automatically.
client.watch(pid) vs session.watch()
Both entry points subscribe to the same underlying WebSocket endpoint. The difference is context: session.watch() knows the session’s PID and app name, and can optionally keep the session’s cached state updated. client.watch(pid) is a raw stream — use it when you need to stream events for a PID without a full session object.
Handling disconnects and reconnection
By default,session.watch() reconnects automatically if the WebSocket connection drops. You can detect reconnections by checking for gaps in event.sequence.
reconnect_detection.py
manual_reconnect.py
Sequence gaps
event.sequence is a monotonically increasing integer. If you observe event.sequence > last_sequence + 1, one of the following occurred:
- The internal event queue overflowed and the oldest events were dropped (see queue configuration below).
- The WebSocket connection dropped and was re-established, causing the daemon to start a new sequence.
- The daemon restarted.
await session.refresh() and re-read current state rather than attempting to reconstruct what changed during the gap.
Watching multiple apps simultaneously
Combineasyncio.gather with per-session watch tasks to monitor several apps at the same time.
watch_all.py
Queue configuration
Events are buffered in an internal queue. If your consumer processes events more slowly than the daemon emits them, the oldest events are dropped to make room. Increasemax_queue if your agent does significant work inside the loop.
Watch events do not count against your action rate limit. You can keep a stream open indefinitely without affecting how many actions per second your agent can execute.
Bounding a watch session by time
Useasyncio.timeout (Python 3.11+) to stop watching after a fixed duration:
bounded_watch.py
