Skip to main content

Client to Server Events

Events sent from CLI to hub.

message

Send a message to a session. Payload: Example:
socket.emit('message', {
  sid: 'abc123',
  message: {
    type: 'text',
    text: 'Hello from CLI'
  },
  localId: 'local-456'
})
Behavior:
  • Validates namespace access
  • Stores message in database
  • Assigns sequence number
  • Broadcasts to session room via update event
  • Publishes to SSE subscribers
  • Extracts todos from TodoWrite tool calls

session-alive

Keep session active and update state. Payload: Example:
socket.emit('session-alive', {
  sid: 'abc123',
  time: Date.now(),
  thinking: false,
  mode: 'remote',
  permissionMode: 'default'
})
Behavior:
  • Updates session’s activeAt timestamp
  • Updates thinking state
  • Prevents session timeout
  • Send every 5 seconds

session-end

Mark session as ended. Payload: Example:
socket.emit('session-end', {
  sid: 'abc123',
  time: Date.now()
})
Behavior:
  • Marks session as inactive
  • Notifies web clients via SSE
  • Stops timeout tracking

update-metadata

Update session metadata with optimistic locking. Payload: Callback Response:
type Response = 
  | { result: 'success', version: number, metadata: any }
  | { result: 'version-mismatch', version: number, metadata: any }
  | { result: 'error', reason?: string }
Example:
socket.emit('update-metadata', {
  sid: 'abc123',
  expectedVersion: 5,
  metadata: {
    path: '/home/user/project',
    host: 'laptop',
    name: 'My Project'
  }
}, (response) => {
  if (response.result === 'success') {
    console.log('Metadata updated to version', response.version)
  } else if (response.result === 'version-mismatch') {
    console.log('Version mismatch, server at', response.version)
  }
})
Behavior:
  • Validates expected version
  • Increments version on success
  • Broadcasts update to session room
  • Returns new version or conflict

update-state

Update session agent state with optimistic locking. Payload: Callback Response:
type Response = 
  | { result: 'success', version: number, agentState: any }
  | { result: 'version-mismatch', version: number, agentState: any }
  | { result: 'error', reason?: string }
Example:
socket.emit('update-state', {
  sid: 'abc123',
  expectedVersion: 3,
  agentState: {
    controlledByUser: true,
    requests: {
      'req_456': {
        tool: 'edit',
        arguments: { path: 'src/main.ts' },
        createdAt: Date.now()
      }
    }
  }
}, (response) => {
  if (response.result === 'success') {
    console.log('State updated to version', response.version)
  }
})

machine-alive

Keep machine online. Payload: Example:
socket.emit('machine-alive', {
  machineId: 'machine_xyz',
  time: Date.now()
})
Behavior:
  • Updates machine’s lastSeen timestamp
  • Send every 10 seconds

machine-update-metadata

Update machine metadata. Payload: Callback: Same as update-metadata

machine-update-state

Update machine runner state. Payload: Callback: Same as update-state

rpc-register

Register an RPC method handler. Payload: Example:
socket.emit('rpc-register', {
  method: 'uploadFile'
})

// Handle RPC requests
socket.on('rpc-request', async (data, callback) => {
  if (data.method === 'uploadFile') {
    const params = JSON.parse(data.params)
    const result = await handleUploadFile(params)
    callback(JSON.stringify(result))
  }
})

rpc-unregister

Unregister an RPC method handler. Payload: Example:
socket.emit('rpc-unregister', {
  method: 'uploadFile'
})

terminal:ready

Notify that terminal is ready. Payload:

terminal:output

Send terminal output. Payload:

terminal:exit

Notify terminal exit. Payload:

terminal:error

Notify terminal error. Payload:

ping

Test connection latency. Callback: Immediate acknowledgment Example:
const start = Date.now()
socket.emit('ping', () => {
  const latency = Date.now() - start
  console.log('Latency:', latency, 'ms')
})

usage-report

Report usage statistics (internal). Payload: Arbitrary usage data

Server to Client Events

Events sent from hub to CLI.

update

Broadcast session/machine/message updates. Payload:
id
string
Update ID
seq
number
Sequence number
createdAt
number
Unix timestamp (ms)
body
object
Update body (varies by type)
Update Types:

new-message

{
  t: 'new-message',
  sid: 'abc123',
  message: {
    id: 'msg_xyz',
    seq: 42,
    createdAt: 1709856000000,
    localId: 'local-123',
    content: {...}
  }
}

update-session

{
  t: 'update-session',
  sid: 'abc123',
  metadata: {
    version: 6,
    value: {...}
  },
  agentState: {
    version: 4,
    value: {...}
  }
}

update-machine

{
  t: 'update-machine',
  machineId: 'machine_xyz',
  metadata: {
    version: 2,
    value: {...}
  },
  runnerState: {
    version: 1,
    value: {...}
  }
}
Example:
socket.on('update', (update) => {
  if (update.body.t === 'new-message') {
    console.log('New message:', update.body.message)
  } else if (update.body.t === 'update-session') {
    console.log('Session updated:', update.body.sid)
  }
})

rpc-request

Receive RPC method call from hub. Payload:
method
string
RPC method name
params
string
JSON-encoded parameters
Callback: Return JSON-encoded result Example:
socket.on('rpc-request', async (data, callback) => {
  const { method, params } = data
  const parsedParams = JSON.parse(params)
  
  try {
    let result
    if (method === 'uploadFile') {
      result = await handleUploadFile(parsedParams)
    } else if (method === 'checkPathExists') {
      result = await handleCheckPath(parsedParams)
    } else {
      result = { success: false, error: 'Unknown method' }
    }
    callback(JSON.stringify(result))
  } catch (error) {
    callback(JSON.stringify({
      success: false,
      error: error.message
    }))
  }
})

terminal:open

Open a terminal for a session. Payload:
sessionId
string
Session ID
terminalId
string
Terminal ID
cols
number
Terminal columns
rows
number
Terminal rows

terminal:write

Write input to terminal. Payload:
sessionId
string
Session ID
terminalId
string
Terminal ID
data
string
Input data

terminal:resize

Resize terminal dimensions. Payload:
sessionId
string
Session ID
terminalId
string
Terminal ID
cols
number
New columns
rows
number
New rows

terminal:close

Close terminal. Payload:
sessionId
string
Session ID
terminalId
string
Terminal ID

error

Socket error notification. Payload:
message
string
Error message
code
string
Error code: namespace-missing, access-denied, or not-found
scope
string
Error scope: session or machine
id
string
Session or machine ID
Example:
socket.on('error', (data) => {
  console.error('Socket error:', data.message)
  if (data.code === 'access-denied') {
    console.error('Access denied to', data.scope, data.id)
  }
})

Build docs developers (and LLMs) love