Skip to main content
The gateway now ships in the praisonai-bot package. praisonai serve gateway still works exactly as documented here; for a standalone install see praisonai-bot Migration.
GatewayClient is a reconnecting WebSocket client that handles version negotiation, exponential backoff with jitter, and event-sequence gap detection — so your integration stays connected without you writing the socket loop.
The user’s integration connects with GatewayClient; the client negotiates the handshake, streams events, and reconnects after disconnects.

Quick Start

1

Simplest Usage

2

With Backoff Tuning

3

With Gap and State Callbacks


How It Works

Calling client.connect() on an instance whose previous loop had a server-supplied retry_after floor clears that floor before the new loop starts. Reusing a GatewayClient after a terminal failure will not inherit a stale backoff from the prior connection cycle — the clear happens on every connect() call, not only the first.

Configuration Options

GatewayClient constructor: BackoffConfig fields: Connection states (from ConnectionState): Callbacks (set as attributes on the client instance):

Protocol Version Negotiation

The client and server negotiate a protocol version during the join handshake.
  • Constants in protocols.py: PROTOCOL_VERSION = 1, MIN_PROTOCOL_VERSION = 1, MAX_PROTOCOL_VERSION = 1.
  • Client sends min_version and max_version in the join message.
  • Server replies in joined with protocol_version, server_min_version, server_max_version.
version_unsupported is a permanent error. When the server returns {"type": "error", "code": "version_unsupported"}, GatewayClient.connect() raises ValueError and does not retry. Wrap your connect() call in a try/except ValueError and do not loop on this error.
Invalid min_version/max_version fields (non-integer, or min > max) produce code: "invalid_protocol_hello" and raise ConnectionError.

Terminal vs Transient Connect Errors

Not every connect failure should be retried. The client uses a single classifier — is_recoverable(code) from praisonaiagents.gateway.protocols — to decide whether to back off and try again, or stop the reconnect loop and surface the failure through on_reconnect_paused.
agent_not_found is terminal because the gateway emits it with a do_not_retry recovery step — the agent id in your hello frame does not exist on the server, so retrying will not help until a human fixes the id or registers the agent. The client used to loop forever on this code; it now pauses immediately.
Surface a terminal reason and drive re-pairing without silently looping:
Once on_reconnect_paused fires, client.connect() has already set _running = False. To resume connecting after the operator fixes the underlying condition (re-auth, re-pair, correct agent_id), call await client.connect() again — it will start a fresh loop with a cleared backoff floor.

Gap Detection

Every event carries a monotonic sequence field; the client tracks _expected_sequence and fires on_gap when there’s a mismatch.
on_gap is a synchronous Callable[[int, int], None]. You cannot await inside it. To call client.resync() from within the callback, schedule it as a task:
client.resync() resets the cursor to 0 and reconnects, triggering a full state reload from the server.

Resume Snapshot

When reconnecting with a stored session_id and since=cursor, the server replies with a single joined payload that restores full client state in one round trip. The joined payload includes: This means a reconnecting client learns current presence and health without extra requests — one round trip restores all state.

Common Patterns


Network Blip: What Users See

When a network blip occurs, the client handles reconnection silently — users see no errors and events resume from the cursor automatically. The bot keeps responding because:
  1. Events are buffered until the cursor is confirmed
  2. The since=cursor on reconnect replays any events delivered while offline
  3. Users see continuous responses with no error messages

Best Practices

The defaults (initial=1.0, max=30.0, multiplier=2.0) suit residential and cellular networks. For LAN-only deployments with fast recovery, tighten initial to 0.1 and max to 5.0.
The reconnect loop stops on terminal codes (auth_*, pairing_required, protocol_unsupported, agent_not_found, origin_not_allowed, configuration_error). Without a callback you have no way to know it stopped — logs are the only signal. Always set on_reconnect_paused before connect() and route the code to re-auth, re-pair, or an ops alert. See Terminal vs Transient Connect Errors for the full classification.
connect() raises ValueError on version_unsupported and stops retrying. Wrap it and alert your ops team — this means the server and client are incompatible and need a coordinated upgrade. This is the protocol-negotiation counterpart to the other terminal codes covered in Terminal vs Transient Connect Errors.
If your app already has its own snapshot mechanism, prefer it over resync(). A targeted snapshot is faster than a full cursor reset.
Long-running batch jobs should not retry forever on a dead gateway. Set max_reconnect_attempts so the job fails fast and can be requeued.

Gateway

WebSocket control plane for multi-agent coordination

Gateway Overview

Architecture and deployment patterns

Session Persistence

How sessions survive across processes

Push Notifications

Channel-based pub/sub and delivery guarantees