Architecture
AgentDuet is a connector client. The application process maintains one
control connection, opens short-lived sessions per subscriber, and drives
per-call media. Speech models run outside the SDK and exchange PCM with each
Call.
Three objects
1. SessionManager
One persistent WebSocket to the AgentDuet connector for the life of your process.
- Authenticates (API key + connector UUID, or mTLS)
- Heartbeats and reconnects with exponential backoff
- Delivers inbound call and WhatsApp notifications
- Buffers notifications that arrive between connect and
start()/run_forever(), so nothing is dropped while you register handlers
2. Session
Ephemeral, per-subscriber handle from sm.open_session(session_id, subscriber).
open_session is get-or-create. Reuse a session_id to continue a conversation; create a new one (new_session_id()) to start fresh. Never share one session id across different subscribers.
Server sessions have a ~30-minute sliding idle TTL. The next process_call / make_call / send_message transparently re-opens if needed.
3. Call (voice connection)
Each Call owns a lazy voice WebSocket. The SDK opens media the first time you need audio (answer, dial, connect, send_audio, …) and tears it down when the call ends. You never manage that socket yourself.
Audio is always per party: call.caller.audio_stream() and call.callee.audio_stream(). The agent speaks with call.send_audio(...).
What AgentDuet owns vs what you own
Delivery model
Inbound notifications are connector-wide competing-consumer: they may arrive concurrently and out of order. Correlate by (subscriber, participant) yourself. Established voice calls are unaffected when the control link reconnects; if the voice link drops, you get hangup + CallClosedError.
