StateChangeNotification over WebSocket every time a tracked application’s state changes. Instead of polling session.refresh() in a loop and burning through your rate limit budget, you open a single persistent connection and react to changes the instant they arrive.
Why Streaming Beats Polling
The
/watch WebSocket endpoint is not subject to the daemon’s rate limiter. You can hold as many concurrent streams open as you need without affecting your API key’s request quota.The StateChangeNotification Object
Every event your stream yields is a StateChangeNotification with these fields:
OS process ID of the app that changed.
Stable semantic identifier for the app (e.g.
"gmail-chrome-12847").Monotonically increasing counter. A gap between two consecutive sequence numbers means events were dropped (queue overflow) or a reconnect occurred.
State keys that changed since the previous notification. Use this to filter work — only process events relevant to your agent.
Timestamp at which the daemon captured the changed state.
Basic Streaming with session.watch()
The recommended pattern is session.watch(auto_refresh_on_event=True). This refreshes session.state automatically on every event, so you can call session.get_*() directly inside the loop without a separate refresh() call:
Filtering with changed_keys
Always check event.changed_keys before doing any work. Most state change events will touch fields your agent doesn’t care about:
Reconnection Handling
By defaultsession.watch() reconnects automatically if the WebSocket connection drops. You can detect reconnections by watching for gaps in event.sequence:
Combining Streaming with session.refresh()
For most agents, auto_refresh_on_event=True is all you need. If you need to force a full re-probe (for example, after acting on the state), call refresh() explicitly:
Watching Multiple Apps Simultaneously
Open one stream per app and gather them withasyncio.gather:
Streaming + Periodic Polling Together
Sometimes you want real-time reaction to events and a periodic summary. Run both patterns concurrently withasyncio.gather:
Low-Level Access: open_state_stream()
If you need direct WebSocket access — for example, to forward events to a non-Python consumer or to build your own buffering layer — use the low-level client.open_state_stream() function:
Raw WebSocket endpoint
The underlying WebSocket URL is:websocat:
session.watch() Parameter Reference
When
true, the session’s cached state is automatically refreshed every time an event arrives. This is the most ergonomic option for most agents.Automatically re-establishes the WebSocket connection if it drops. Sequence gaps indicate where reconnects occurred.
Size of the internal event buffer. When the buffer is full, the oldest event is dropped. Increase this if your handler is slower than the daemon’s emit rate.
