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.
Gateway sessions preserve pending messages and in-flight executions across disconnects, restarts, and graceful shutdowns. The user continues a chat after reconnect; session continuity restores context on the same thread.
Resumed sessions also restore the handshake’s negotiated protocol_version and the client’s advertised capabilities — see Server-side state after handshake.

Quick Start

1

Start gateway with an agent

2

Enable graceful drain (opt-in)


How It Works

When a client reconnects, the gateway checks whether the session has queued inbox messages or an in-flight execution. If so, it sends a status frame, marks the session as executing before spawning the queue task (preventing duplicate workers), and processes pending messages in FIFO order.

Graceful Drain on Shutdown

Graceful drain is opt-in and default off. Without drain_timeout, shutdown cancels in-flight turns immediately — today’s behaviour is preserved. When enabled, shutdown runs two layers sequentially, sharing a single budget:

Layer 1 — Channel-Bot Drain (BotOS)

BotOS.drain() quiesces ingress and waits for running agent turns to finish.
BotOS drain properties (adapter authors):

Layer 2 — WebSocket Session Drain

After channel-bot drain, WebSocketGateway._drain_active_sessions persists sessions with pending inbox or in-flight execution — and now receives only the remaining budget from the total window. Force-closed SESSION_END payload:

How the Budget is Shared

drain_timeout bounds total shutdown time, not per-phase or per-bot.
Previously, the WebSocket-session drain received the full drain_timeout again after stop_channels already consumed up to that window — total shutdown could reach 2 × drain_timeout. The budget is now tracked across both phases so the ceiling is always honoured.

Reconnect and Auto-Resume

On resume, clients receive:
The gateway sets mark_executing(True) before launching asyncio.create_task(_run_session_queue(...)), so a race between reconnect and shutdown cannot spawn duplicate queue processors.

Persisted Shape

Gateway session snapshots include pending work:
The inbox is snapshotted non-destructively: items are drained into a list and put back so live processing is unaffected.

Configuration

drain_timeout accepts numbers or numeric strings from YAML/env (e.g. "30" is coerced to 30.0). Non-finite or non-numeric values log a warning and disable drain — no user action required.

Bypass / Disable

To keep today’s immediate-cancel behaviour, simply don’t set drain_timeout. No configuration change is needed.
To disable drain after enabling it in YAML, set drain_timeout: 0 or remove the key.

External Drain Trigger

drain_timeout bounds the drain phase but only fires when something calls gateway.stop(). For hosted / containerised deployments where an operator or deploy step needs to ask a running gateway to drain — without exposing an inbound control port and without a stale signal wedging a restarted instance — pair it with the port-less drain trigger. See Gateway Drain Trigger for the marker-file contract and the DrainMarkerPolicy / current_epoch() API.

Best Practices

A 30-second turn needs at least drain_timeout: 30 to drain cleanly. Short values cause turns to be abandoned.
Without persistence, drained sessions cannot be resumed after restart — only logged.
Listen for had_pending_work: true or was_executing: true in audit or observability hooks to detect force-closed sessions.
Send messages faster than the agent processes them, then drop the WebSocket — pending messages should resume in order on reconnect.
If you build a custom channel adapter, check bot_os.accepting before dispatching a new turn. When False, the gateway is draining and new turns should be rejected.

External drain trigger

drain_timeout bounds the drain phase but only fires when something calls gateway.stop(). For hosted / containerised deployments where an operator or deploy step needs to ask a running gateway to drain — without exposing an inbound control port and without a stale signal wedging a restarted instance — pair it with the port-less drain trigger. See Gateway Drain Trigger for the marker-file contract and the DrainMarkerPolicy / current_epoch() API.
Scale-to-Zero and session continuity work together. When the scale-to-zero policy quiesces the gateway (stops transports), session continuity is what ensures the conversation history is preserved so users can pick up exactly where they left off after the gateway wakes. See Scale-to-Zero Gateway.

Gateway

WebSocket control plane overview

Session Persistence

Persistent sessions and event replay

Error Handling

Reconnect and error recovery

Session Protocol

Session message format

Scale to Zero

Suspend when idle — session continuity makes resume work

Drain Trigger

Port-less external drain signal for hosted deployments