State Changes & Events
How your app communicates with HUMA - sending events and receiving tool calls.
Overview
HUMA uses an event-driven architecture. Your app sends events when state changes, and HUMA responds with tool calls when it wants to take action.
Events (You → HUMA)
"Something happened in my app, here's the current state"
Tool Calls (HUMA → You)
"I want to do something, please execute this action"
Event Schema
Events are sent via WebSocket using socket.emit('message', ...):
// Event structure
socket.emit('message', {
type: 'context-update', // Required: identifies this as a client event
triggering: true, // true = agent responds, false = informational only
name: string, // Event name (e.g., "player-asked-for-cards")
context: object, // Complete current state
description: string // Human-readable description
});| Field | Type | Description |
|---|---|---|
| type | string | Always "context-update" |
| triggering | boolean | true = agent responds, false = informational only |
| name | string | Event identifier (kebab-case recommended) |
| context | object | Full state (from Phase 3) |
| description | string | What happened, in plain English |
Message Events
Chat messages use the new-message event type. Messages can include optional image attachments:
socket.emit('message', {
type: 'context-update',
triggering: true,
name: 'new-message',
context: {
message: {
id: 'msg-123', // Unique message ID
content: 'Hello!', // Message text
images: [ // Optional: attached images
{
url: 'data:image/png;base64,...', // Base64 data URL
alt: 'Screenshot' // Optional description
}
],
participant: {
id: 'user-1',
nickname: 'Alice'
},
createdAt: '2024-01-15T10:30:00Z',
type: 'message'
}
},
description: 'Alice sent a message: "Hello!"'
});| Field | Type | Description |
|---|---|---|
| id | string | Unique message identifier |
| content | string | Message text |
| images | ImageAttachment[] | Optional array of attached images |
| images[].url | string | Base64 data URL (data:image/png;base64,...) |
| images[].alt | string? | Optional description for context |
| participant.id | string | Sender's unique ID |
| participant.nickname | string | Sender's display name |
Image Support
For detailed information on sending images, including context images and format requirements, see Working with Images.
Event Naming Conventions
Use consistent, descriptive names for your events:
Good Names
player-asked-for-cardsturn-startedmessage-sentgame-ended
Avoid
event1- not descriptiveUPDATE- too vagueplayerAskedForCards- use kebab-case
// Define your event names as constants
const EVENT_NAMES = {
GAME_STARTED: 'game-started',
TURN_STARTED: 'turn-started',
PLAYER_ASKED: 'player-asked-for-cards',
CARDS_RECEIVED: 'cards-received',
GO_FISH: 'go-fish',
BOOK_COMPLETED: 'book-completed',
MESSAGE_SENT: 'message-sent',
GAME_ENDED: 'game-ended',
};Writing Good Descriptions
The description tells the agent what just happened. Write it like you're telling a friend:
✓ "Alice asked Bob for 7s"
✓ "Bob gave 2 sevens to Alice"
✓ "Bob said 'Go Fish!' - Alice drew a card and gets another turn!"
✓ "Alice asked Bob for Queens"
Example: Go Fish Events
Game Started
socket.emit('message', {
type: 'context-update',
triggering: false,
name: 'game-started',
context: { /* full state */ },
description: 'Game started! Alice goes first. You have 7 cards.'
});Player Asked for Cards
socket.emit('message', {
type: 'context-update',
triggering: false,
name: 'player-asked-for-cards',
context: { /* full state */ },
description: 'Alice asked Bob for 7s.'
});Go Fish Result
socket.emit('message', {
type: 'context-update',
triggering: true,
name: 'go-fish',
context: { /* full state with updated hands */ },
description: "Bob said 'Go Fish!' - Alice drew a card."
});Chat Message
socket.emit('message', {
type: 'context-update',
triggering: true,
name: 'message-sent',
context: { /* full state with new chat message */ },
description: 'Alice said: "Nice move!"'
});Responding to Tool Calls
When HUMA sends a tool call, execute the action in your app, then send back the result:
// Receiving a tool call from HUMA
socket.on('event', (data) => {
if (data.type === 'tool-call') {
const { toolCallId, toolName, arguments: args } = data;
// Execute the tool in your app
try {
const result = executeToolInYourApp(toolName, args);
// Send success result
socket.emit('message', {
type: 'tool-result',
triggering: true,
toolCallId: toolCallId,
toolName: toolName,
outcome: 'success',
result: result
});
} catch (error) {
// Send error result
socket.emit('message', {
type: 'tool-result',
triggering: true,
toolCallId: toolCallId,
toolName: toolName,
outcome: 'failure',
error: error.message
});
}
}
});ask_for_cards, send the tool result AND a go-fish or cards-received event.Tool Result Schema
| Field | Type | Description |
|---|---|---|
| type | string | Always "tool-result" |
| triggering | boolean | Whether the agent should respond after receiving this result |
| toolCallId | string | ID from the tool-call event |
| toolName | string | Name of the tool that was called |
| outcome | "success" | "failure" | "canceled" | Result of the tool execution |
| result | any (optional) | Result data when outcome is "success" |
| error | string (optional) | Error message when outcome is "failure" |
// Success result
{ type: 'tool-result', triggering: true, toolCallId: 'tc_123', toolName: 'ask_for_cards', outcome: 'success', result: 'Go Fish! Drew a 7.' }
// Error result
{ type: 'tool-result', triggering: true, toolCallId: 'tc_123', toolName: 'ask_for_cards', outcome: 'failure', error: 'Not your turn' }
// Canceled result (response to cancel-tool-call)
{ type: 'tool-result', triggering: true, toolCallId: 'tc_123', toolName: 'ask_for_cards', outcome: 'canceled' }Complete Flow Example
Here's what happens during a single turn in Go Fish:
name: "turn-started", description: "It's your turn!"toolName: "ask_for_cards", arguments: { target: "Bob", rank: "7" }game.askForCards("Finn", "Bob", "7") → Go Fish!Tool result: outcome: "success", result: "Go Fish!"Event: name: "go-fish", description: "Bob said Go Fish!"name: "turn-started", description: "It's Bob's turn."