Routing — Message Dispatch & Session Management
Traffic Controller for OpenClaw
The Routing subsystem dispatches authenticated RPC requests to the correct handler, manages session lifecycles across 32+ fields of state, resolves assistant identities, and coordinates broadcast events to connected clients via WebSocket.
TypeScript RPC WebSocket Stateful
What Is This & Why Does It Exist?
Once a client is authenticated, its requests need to reach the right handler with the right session context. The Routing subsystem is the dispatcher that makes this happen.
💡 Real-world analogy
Think of Routing as a hotel concierge desk. The GatewayRequestHandlers map is the service directory — each RPC method name is a service (room service, spa, restaurant) and each GatewayRequestHandler is the staff member who fulfills it. GatewaySessionRow is the guest’s folio tracking everything from check-in time to room charges. resolveAssistantIdentity() assigns the personal butler (assistant) with the right name badge and uniform.
Table of Contents
Architecture
The Routing layer sits after authentication and protocol validation. It maps RPC method names to handlers, manages session state, and coordinates broadcasts.
flowchart TB
subgraph Input
REQ["Validated RequestFrame
method + params"]
end
subgraph Routing["Routing Layer"]
DISPATCH["GatewayRequestHandlers
Record of string to GatewayRequestHandler"]
OPTS["GatewayRequestOptions
req, client, respond, context"]
subgraph Session["Session Manager"]
CREATE["Create Session"]
RESUME["Resume Session"]
PATCH["Patch Session"]
LIFE["Lifecycle Events"]
end
subgraph Identity["Identity Resolver"]
RESOLVE["resolveAssistantIdentity()"]
DEFAULT["DEFAULT_ASSISTANT_IDENTITY"]
end
end
subgraph Targets["Target Systems"]
AGENT["Agent Runtime"]
WIZARD["Wizard Sessions"]
CONFIG["Config Handlers"]
PRESENCE["Presence System"]
end
REQ --> DISPATCH
DISPATCH --> OPTS
OPTS --> Session
OPTS --> Identity
OPTS --> AGENT
OPTS --> WIZARD
OPTS --> CONFIG
OPTS --> PRESENCE
Session --> LIFE
LIFE --> BROADCAST["broadcast()"]
Data Flow
sequenceDiagram
participant C as Client
participant D as Dispatcher
participant H as Handler
participant S as Session Manager
participant I as Identity Resolver
participant A as Agent Runtime
participant B as Broadcast
C->>D: RequestFrame {method, params, id}
D->>D: lookup GatewayRequestHandlers[method]
D->>H: handler(GatewayRequestHandlerOptions)
H->>I: resolveAssistantIdentity(cfg, agentId)
I-->>H: AssistantIdentity {name, avatar, emoji}
H->>S: create/resume GatewaySessionRow
S-->>H: session {key, kind, status}
H->>A: forward to agent runtime
A-->>H: response payload
H->>S: persistGatewaySessionLifecycleEvent()
S->>B: broadcast lifecycle event
H-->>C: ResponseFrame {ok, payload}
Key Types & Functions
| Name | File | Description |
|---|---|---|
GatewayRequestHandler | server-methods/types.ts | Function: (opts: GatewayRequestHandlerOptions) => Promise<void> | void |
GatewayRequestHandlers | server-methods/types.ts | Record<string, GatewayRequestHandler> — RPC method dispatch map |
GatewayRequestOptions | server-methods/types.ts | Contains req, client, respond, context, isWebchatConnect |
GatewayClient | server-methods/types.ts | Client info: connect, connId, clientIp, canvasCapability |
RespondFn | server-methods/types.ts | (ok, payload?, error?, meta?) => void |
GatewaySessionRow | session-utils.types.ts | 32+ field session: key, kind, status, tokens, cost, displayName |
SessionRunStatus | session-utils.types.ts | Union: "running" | "done" | "failed" | "killed" | "timeout" |
GatewaySessionsDefaults | session-utils.types.ts | Defaults: modelProvider, model, contextTokens |
SessionsListResult | session-utils.types.ts | List result with defaults and session rows |
SessionsPatchResult | session-utils.types.ts | Patch result: entry, resolved model info |
AssistantIdentity | assistant-identity.ts | Identity: agentId, name, avatar, emoji |
resolveAssistantIdentity() | assistant-identity.ts | Resolve identity from config + workspace files |
DEFAULT_ASSISTANT_IDENTITY | assistant-identity.ts | Default: {agentId:"main", name:"Assistant", avatar:"A"} |
GatewaySessionLifecycleSnapshot | session-lifecycle-state.ts | Partial lifecycle shape for event snapshots |
persistGatewaySessionLifecycleEvent() | session-lifecycle-state.ts | Persist lifecycle event to storage |
Session Kinds
| Kind | Description | Use Case |
|---|---|---|
"direct" | Direct 1:1 conversation session | CLI or Web UI single chat |
"group" | Group/multi-agent session | Multi-agent collaboration |
"global" | Global broadcast session | System-wide announcements |
"unknown" | Fallback for unrecognized session types | Legacy or custom sessions |
Session Row Key Fields
| Field | Type | Description |
|---|---|---|
key | string | Unique session identifier |
kind | "direct" | "group" | "global" | "unknown" | Session type |
status | SessionRunStatus | Current run status |
spawnedBy | string? | Parent session that spawned this one |
displayName | string? | User-facing session name |
derivedTitle | string? | Auto-generated title from conversation |
lastMessagePreview | string? | Preview of most recent message |
inputTokens | number? | Total input tokens consumed |
outputTokens | number? | Total output tokens generated |
estimatedCostUsd | number? | Estimated cost in USD |
startedAt | number? | Session start timestamp |
endedAt | number? | Session end timestamp |
runtimeMs | number? | Total runtime in milliseconds |
Code Examples
Defining a Request Handler
import { GatewayRequestHandler } from './server-methods/types';
const handleSessionsList: GatewayRequestHandler = async (opts) => {
const { req, client, respond, context } = opts;
const params = req.params as SessionsListParams;
const result: SessionsListResult = await context.deps.sessionStore.list({
limit: params.limit ?? 50,
offset: params.offset ?? 0,
});
respond(true, result);
};
// Register in the handler map
const handlers: GatewayRequestHandlers = {
'sessions.list': handleSessionsList,
'sessions.send': handleSessionsSend,
'config.get': handleConfigGet,
// ... 80+ more methods
};
Resolving Assistant Identity
import {
resolveAssistantIdentity,
AssistantIdentity,
DEFAULT_ASSISTANT_IDENTITY,
} from './assistant-identity';
const identity: AssistantIdentity = resolveAssistantIdentity({
cfg: openClawConfig,
agentId: 'main',
workspaceDir: '/home/user/.openclaw',
});
// identity.name => "Assistant" (or custom name, max 50 chars)
// identity.avatar => "A" (or URL, max 200 chars)
// identity.emoji => undefined (or custom, max 16 chars)
Persisting Session Lifecycle Events
import {
persistGatewaySessionLifecycleEvent,
deriveGatewaySessionLifecycleSnapshot,
} from './session-lifecycle-state';
const snapshot = deriveGatewaySessionLifecycleSnapshot({
session: currentSession,
event: { ts: Date.now(), data: { phase: 'end', status: 'done' } },
});
await persistGatewaySessionLifecycleEvent({
sessionKey: 'sess_abc123',
event: { ts: Date.now(), data: { phase: 'end' } },
});
Configuration
| Parameter | Type | Default | Description |
|---|---|---|---|
assistant.name | string | "Assistant" | Display name (max 50 chars) |
assistant.avatar | string | "A" | Avatar char or URL (max 200) |
assistant.emoji | string | — | Emoji badge (max 16 chars) |
session.defaultModel | string | — | Default model for new sessions |
session.defaultProvider | string | — | Default provider for new sessions |
session.contextTokens | number | — | Default context window size |
protocol.version | number | PROTOCOL_VERSION | Current protocol version |
Deep-Dive Focus Pages
📤 Message Dispatch Focus
How RPC method names are mapped to handlers, request validation, and the respond/error pattern.
📋 Session Management Focus
The full session lifecycle: creation, status transitions, token tracking, cost estimation, and cleanup.
🤖 Assistant Identity Focus
How assistant names, avatars, and emojis are resolved from config, workspace files, and defaults.
Directory Map
Source files for Routing
src/gateway/
├── assistant-identity.ts ← AssistantIdentity, resolveAssistantIdentity
├── session-utils.types.ts ← GatewaySessionRow (32+), SessionRunStatus
├── session-lifecycle-state.ts ← Lifecycle snapshots, persistence
├── server-methods/
│ └── types.ts ← GatewayRequestHandler, GatewayRequestContext
├── server/
│ └── ws-types.ts ← GatewayWsClient (WebSocket client)
└── protocol/
└── index.ts ← 80+ validators, ConnectParams, RequestFrame