BridgeClient
BridgeClient
Socket.IO-based client for real-time events. Handles automatic reconnection, room management, and typed event emission.
Constructor
import { BridgeClient } from '@oro.ad/bridge-sdk'
const client = new BridgeClient({
url: 'http://localhost:3000',
auth: {
clientId: 'my-adapter',
clientType: 'adapter',
nickname: 'TG Adapter'
},
autoReconnect: true, // default: true
reconnectionDelayMs: 1000, // default: 1000
reconnectionDelayMaxMs: 30000, // default: 30000
transports: ['websocket'], // default: ['websocket']
})
| Option | Type | Default | Description |
|---|---|---|---|
url | string | required | Bridge server URL |
path | string | — | Custom Socket.IO path |
auth | object | — | Authentication payload |
autoConnect | boolean | false | Connect immediately on construction |
autoReconnect | boolean | true | Reconnect on disconnect |
reconnectionDelayMs | number | 1000 | Initial reconnection delay |
reconnectionDelayMaxMs | number | 30000 | Max reconnection delay |
transports | string[] | ['websocket'] | Socket.IO transports |
logger | Logger | — | Structured logger |
Lifecycle
client.connect()
// Wait for connection (with timeout)
await client.connected(10_000)
console.log(client.isConnected()) // true
client.close()
Room Management
Each bridge session is a "room". You must join a room to receive its stream events.
client.join('claude_abc123')
client.leave('claude_abc123')
client.isJoined('claude_abc123') // boolean
client.getJoinedRooms() // string[]
Outgoing Events
// Send a message through the socket (alternative to REST)
client.emitSendMessage({
conversationId: 'claude_abc123',
content: 'Hello!',
attachments: []
})
// Stop generation
client.emitStopGeneration('claude_abc123')
// Register for shared session
client.emitRegisterShare({
conversationId: 'claude_abc123',
clientId: 'my-client',
nickname: 'John'
})
Stream Events
All stream events include a conversationId field for routing.
Connection Events
client.on('connect', () => {
console.log('Connected to bridge')
})
client.on('disconnect', (reason: string) => {
console.log('Disconnected:', reason)
})
client.on('connect_error', (err: Error) => {
console.error('Connection failed:', err.message)
})
Session List
Broadcasted when sessions change (created, deleted, status update).
client.on('sessions:list', (sessions: SessionSummary[]) => {
// Full session list — reconcile your containers
})
Server Status
Periodic heartbeat across all sessions. Useful for "any activity?" dashboards and liveness probes.
client.on('server:status', (status) => {
// status.anyProcessing: boolean
// status.activeSessions: number
// status.uptime: number (seconds)
})
Stream Lifecycle
// User message accepted by bridge
client.on('stream:user_message', (e) => {
// e.id, e.content, e.senderId, e.senderNickname, e.conversationId
})
// Message buffered (multi-user, waiting to flush)
client.on('stream:buffered', (e) => {
// e.id, e.content, e.senderId, e.timestamp, e.conversationId
})
// Buffer flushed into a composed prompt
client.on('stream:buffer_flushed', (e) => {
// e.messages[], e.composedPrompt, e.conversationId
})
// Assistant turn begins
client.on('stream:message_start', (e) => {
// e.id, e.conversationId, e.triggerMessageId
})
// Incremental text
client.on('stream:text_delta', (e) => {
// e.text, e.conversationId
})
// Tool invocation
client.on('stream:tool_use', (e) => {
// e.id, e.name, e.input, e.conversationId
})
// Tool result
client.on('stream:tool_result', (e) => {
// e.tool_use_id, e.name, e.content, e.is_error, e.conversationId
})
// Turn complete (full response available)
client.on('stream:message_complete', (e) => {
// e.id, e.model, e.content, e.contentBlocks, e.conversationId
})
// Final result with usage/cost
client.on('stream:result', (e) => {
// e.subtype, e.cost_usd, e.usage, e.duration_ms, e.conversationId
})
// Generation stopped (user or system)
client.on('stream:stopped', (e) => {
// e.message, e.partialContent, e.conversationId
})
// User message queued behind an in-flight turn
client.on('stream:queued', (e) => {
// e.messageId, e.position (0-based), e.triggerMessageId, e.conversationId
})
// Mid-turn state snapshot for late joiners / reconnects
client.on('stream:sync', (e) => {
// e.messageId, e.triggerMessageId, e.contentBlocks[], e.accumulatedText,
// e.model, e.status ('processing'), e.conversationId
})
Session Status
client.on('session:status', (e) => {
// e.processing: boolean, e.conversationId
})
client.on('session:error', (e) => {
// e.error: string, e.conversationId, e.exitCode?
})
Loop Mode Events
client.on('session:loop_started', (e) => {
// e.conversationId, e.config: LoopConfig
})
client.on('session:loop_paused', (e) => {
// e.conversationId, e.reason: string
})
client.on('session:loop_stopped', (e) => {
// e.conversationId
})
client.on('session:loop_iteration', (e) => {
// e.conversationId, e.iteration, e.totalCostUsd
})
Shared Session Events
client.on('share:users', (users: ConnectedUser[]) => { })
client.on('share:user_joined', (e) => { })
client.on('share:user_left', (e) => { })
client.on('share:registered', (e) => { })
client.on('share:nickname_taken', (e) => { })
Raw Socket Access
For advanced use cases, access the underlying Socket.IO instance:
client.rawSocket.on('custom:event', handler)
Reconnection behavior
BridgeClient enables automatic reconnection via Socket.IO's built-in
exponential backoff (reconnectionDelayMs → reconnectionDelayMaxMs).
Joined rooms are tracked in memory and re-emitted as session:join
automatically on every connect event — you don't need to re-join manually
after a disconnect.
To tell "fresh connect" from "reconnect", track state yourself:
let firstConnect = true
client.on('connect', () => {
if (!firstConnect) console.log('reconnected')
firstConnect = false
})
Graceful shutdown
async function shutdown() {
// Unsubscribe from rooms so the server stops pushing events.
for (const room of client.getJoinedRooms()) client.leave(room)
client.close()
}
process.on('SIGTERM', shutdown)