SocketIO Real-Time Events
AiderDesk uses SocketIO to provide real-time event streaming, allowing external applications to receive live updates about project activities, AI responses, and system events.
Overview
The SocketIO server runs on the same port as the REST API (default: 24337, configurable via AIDER_DESK_PORT). Clients can connect and subscribe to specific event types to receive real-time updates.
Connection Setup
import io from 'socket.io-client';
// Connect to AiderDesk
const socket = io('http://localhost:24337', {
autoConnect: true,
forceNew: true,
});
// Subscribe to events
socket.on('connect', () => {
socket.emit('message', {
action: 'subscribe-events',
eventTypes: ['response-chunk', 'response-completed', 'log', 'context-files-updated']
});
});
Event Filtering
Events can be filtered by project directory and task ID when applicable. The system compares base directories and task IDs to ensure events are only sent to relevant subscribers.
Project-level filtering: Events are filtered by baseDir to match the project directory.
Task-level filtering: Task-specific events are also filtered by taskId for granular control.
Event Types
Response Events
response-chunk
Emitted during AI response streaming for real-time updates.
Data Structure:
{
"messageId": "unique-message-id",
"baseDir": "/path/to/project",
"taskId": "task-uuid",
"chunk": "AI response text chunk",
"reflectedMessage": "optional reflected message",
"promptContext": {
"id": "context-uuid",
"group": {
"id": "group-uuid",
"name": "optional group name",
"color": "optional group color",
"finished": false
}
}
}
response-completed
Emitted when an AI response is fully completed.
Data Structure:
{
"type": "response-completed",
"messageId": "unique-message-id",
"baseDir": "/path/to/project",
"taskId": "task-uuid",
"content": "Complete AI response",
"reflectedMessage": "optional reflected message",
"editedFiles": ["file1.ts", "file2.ts"],
"commitHash": "abc123",
"commitMessage": "Changes committed",
"diff": "diff content",
"usageReport": {
"model": "gpt-4",
"sentTokens": 100,
"receivedTokens": 50,
"messageCost": 0.0023,
"cacheWriteTokens": 10,
"cacheReadTokens": 5,
"aiderTotalCost": 0.002,
"agentTotalCost": 0.0003
},
"sequenceNumber": 1,
"promptContext": {
"id": "context-uuid",
"group": {
"id": "group-uuid",
"name": "optional group name",
"color": "optional group color",
"finished": false
}
}
}
Context Events
context-files-updated
Emitted when the project's context files are modified.
Data Structure:
{
"baseDir": "/path/to/project",
"taskId": "task-uuid",
"files": [
{
"path": "src/main.ts",
"readOnly": false
},
{
"path": "src/utils.ts",
"readOnly": true
}
]
}
custom-commands-updated
Emitted when custom commands are updated.
Data Structure:
{
"baseDir": "/path/to/project",
"taskId": "task-uuid",
"commands": [
{
"name": "format-code",
"description": "Format code using prettier",
"arguments": [
{
"description": "File to format",
"required": false
}
],
"template": "prettier --write {{file}}",
"includeContext": false,
"autoApprove": false
}
]
}
AI and Model Events
ask-question
Emitted when the AI needs to ask the user a question.
Data Structure:
{
"baseDir": "/path/to/project",
"taskId": "task-uuid",
"text": "What framework would you like to use?",
"subject": "Framework Selection",
"isGroupQuestion": false,
"answers": [
{
"text": "React",
"shortkey": "r"
},
{
"text": "Vue",
"shortkey": "v"
}
],
"defaultAnswer": "React",
"internal": false,
"key": "framework-choice"
}
update-aider-models
Emitted when AI model information is updated.
Data Structure:
{
"baseDir": "/path/to/project",
"taskId": "task-uuid",
"mainModel": "gpt-4",
"weakModel": "gpt-3.5-turbo",
"architectModel": "gpt-4-turbo",
"reasoningEffort": "medium",
"thinkingTokens": "1000",
"editFormat": "diff",
"info": {
"max_input_tokens": 128000,
"max_output_tokens": 4096,
"input_cost_per_token": 0.000003,
"output_cost_per_token": 0.000015,
"supports_function_calling": true,
"supports_tool_choice": true,
"litellm_provider": "openai"
},
"error": "Optional error message"
}
Tool and Command Events
tool
Emitted during tool execution.
Data Structure:
{
"type": "tool",
"baseDir": "/path/to/project",
"taskId": "task-uuid",
"id": "tool-execution-id",
"serverName": "server-name",
"toolName": "run_terminal_cmd",
"args": ["npm", "install"],
"response": "Installing dependencies...",
"usageReport": {
"model": "gpt-4",
"sentTokens": 50,
"receivedTokens": 25,
"messageCost": 0.001,
"cacheWriteTokens": 5,
"cacheReadTokens": 2,
"aiderTotalCost": 0.0008,
"agentTotalCost": 0.0002
},
"promptContext": {
"id": "context-uuid",
"group": {
"id": "group-uuid",
"name": "optional group name",
"color": "optional group color",
"finished": false
}
}
}
command-output
Emitted when a command is executed.
Data Structure:
{
"baseDir": "/path/to/project",
"taskId": "task-uuid",
"command": "npm install",
"output": "Installing dependencies...\nDone."
}
terminal-data
Emitted when terminal data is received.
Data Structure:
{
"terminalId": "term-123",
"baseDir": "/path/to/project",
"taskId": "task-uuid",
"data": "npm install"
}
terminal-exit
Emitted when a terminal process exits.
Data Structure:
{
"terminalId": "term-123",
"baseDir": "/path/to/project",
"taskId": "task-uuid",
"exitCode": 0,
"signal": 15
}
System Events
log
Emitted for logging information.
Data Structure:
{
"baseDir": "/path/to/project",
"taskId": "task-uuid",
"level": "info",
"message": "Project initialized successfully",
"finished": true,
"promptContext": {
"id": "context-uuid",
"group": {
"id": "group-uuid",
"name": "optional group name",
"color": "optional group color",
"finished": false
}
}
}
update-autocompletion
Emitted when autocompletion data is updated.
Data Structure:
{
"baseDir": "/path/to/project",
"taskId": "task-uuid",
"words": [
"/api/",
"/src/",
"/tests/"
],
"allFiles": [
"src/main.ts",
"src/utils.ts"
],
"models": [
"gpt-4",
"gpt-3.5-turbo"
]
}
versions-info-updated
Emitted when version information is updated.
Data Structure:
{
"aiderDeskCurrentVersion": "1.0.0",
"aiderCurrentVersion": "0.45.0",
"aiderDeskAvailableVersion": "1.1.0",
"aiderAvailableVersion": "0.46.0",
"aiderDeskDownloadProgress": 0.75,
"aiderDeskNewVersionReady": false,
"releaseNotes": "New features and bug fixes..."
}
Session and Message Events
user-message
Emitted when a user sends a message.
Data Structure:
{
"type": "user",
"id": "message-uuid",
"baseDir": "/path/to/project",
"taskId": "task-uuid",
"content": "Implement user authentication",
"promptContext": {
"id": "context-uuid",
"group": {
"id": "group-uuid",
"name": "optional group name",
"color": "optional group color",
"finished": false
}
}
}
input-history-updated
Emitted when input history is updated.
Data Structure:
{
"baseDir": "/path/to/project",
"taskId": "task-uuid",
"inputHistory": [
"Create login component",
"Add user validation",
"Implement authentication"
]
}
Project Management Events
clear-task
Emitted when a task is cleared.
Data Structure:
{
"baseDir": "/path/to/project",
"taskId": "task-uuid",
"clearMessages": true,
"clearSession": false
}
project-started
Emitted when a project is started.
Data Structure:
{
"baseDir": "/path/to/project"
}
Task Lifecycle Events
task-created
Emitted when a new task is created.
Data Structure:
{
"id": "task-uuid",
"baseDir": "/path/to/project",
"name": "Task Name",
"createdAt": "2024-01-01T00:00:00.000Z",
"updatedAt": "2024-01-01T00:00:00.000Z",
"startedAt": "2024-01-01T00:00:00.000Z",
"completedAt": "2024-01-01T00:00:00.000Z",
"aiderTotalCost": 0.0,
"agentTotalCost": 0.0
}
task-initialized
Emitted when a task is initialized with context.
Data Structure:
{
"id": "task-uuid",
"baseDir": "/path/to/project",
"name": "Task Name",
"createdAt": "2024-01-01T00:00:00.000Z",
"updatedAt": "2024-01-01T00:00:00.000Z",
"startedAt": "2024-01-01T00:00:00.000Z",
"completedAt": "2024-01-01T00:00:00.000Z",
"aiderTotalCost": 0.0,
"agentTotalCost": 0.0
}
task-updated
Emitted when task metadata is updated.
Data Structure:
{
"id": "task-uuid",
"baseDir": "/path/to/project",
"name": "Updated Task Name",
"createdAt": "2024-01-01T00:00:00.000Z",
"updatedAt": "2024-01-01T01:00:00.000Z",
"startedAt": "2024-01-01T00:00:00.000Z",
"completedAt": "2024-01-01T00:00:00.000Z",
"aiderTotalCost": 0.0,
"agentTotalCost": 0.0
}
task-started
Emitted when a task is started.
Data Structure:
{
"id": "task-uuid",
"baseDir": "/path/to/project",
"name": "Task Name",
"createdAt": "2024-01-01T00:00:00.000Z",
"updatedAt": "2024-01-01T00:00:00.000Z",
"startedAt": "2024-01-01T00:00:00.000Z",
"completedAt": "2024-01-01T00:00:00.000Z",
"aiderTotalCost": 0.0,
"agentTotalCost": 0.0
}
task-completed
Emitted when a task is completed.
Data Structure:
{
"id": "task-uuid",
"baseDir": "/path/to/project",
"name": "Task Name",
"createdAt": "2024-01-01T00:00:00.000Z",
"updatedAt": "2024-01-01T00:00:00.000Z",
"startedAt": "2024-01-01T00:00:00.000Z",
"completedAt": "2024-01-01T01:00:00.000Z",
"aiderTotalCost": 0.025,
"agentTotalCost": 0.005
}
task-cancelled
Emitted when a task is cancelled.
Data Structure:
{
"id": "task-uuid",
"baseDir": "/path/to/project",
"name": "Task Name",
"createdAt": "2024-01-01T00:00:00.000Z",
"updatedAt": "2024-01-01T00:00:00.000Z",
"startedAt": "2024-01-01T00:00:00.000Z",
"completedAt": "2024-01-01T00:30:00.000Z",
"aiderTotalCost": 0.015,
"agentTotalCost": 0.003
}
task-deleted
Emitted when a task is deleted.
Data Structure:
{
"id": "task-uuid",
"baseDir": "/path/to/project",
"name": "Task Name",
"createdAt": "2024-01-01T00:00:00.000Z",
"updatedAt": "2024-01-01T00:00:00.000Z",
"startedAt": "2024-01-01T00:00:00.000Z",
"completedAt": "2024-01-01T00:00:00.000Z",
"aiderTotalCost": 0.0,
"agentTotalCost": 0.0
}
Settings and Provider Events
settings-updated
Emitted when application settings are updated.
Data Structure:
{
"onboardingFinished": true,
"language": "en",
"startupMode": "last",
"zoomLevel": 1.0,
"notificationsEnabled": true,
"theme": "dark",
"font": "Inter",
"fontSize": 14,
"renderMarkdown": true,
"virtualizedRendering": true,
"aiderDeskAutoUpdate": true,
"aider": {
"options": "--auto-commits",
"environmentVariables": "",
"addRuleFiles": true,
"autoCommits": true,
"cachingEnabled": true,
"watchFiles": true,
"confirmBeforeEdit": false
},
"preferredModels": ["gpt-4", "claude-3-sonnet"],
"agentProfiles": [],
"mcpServers": {},
"llmProviders": {},
"telemetryEnabled": true,
"telemetryInformed": true,
"promptBehavior": {
"suggestionMode": "automatically",
"suggestionDelay": 500,
"requireCommandConfirmation": {
"add": false,
"readOnly": false,
"model": true,
"modeSwitching": true
},
"useVimBindings": false
},
"server": {
"enabled": false,
"basicAuth": {
"enabled": false,
"username": "",
"password": ""
}
}
}
providers-updated
Emitted when provider profiles are updated.
Data Structure:
{
"providers": [
{
"id": "openai-default",
"name": "OpenAI",
"provider": "openai",
"headers": {}
},
{
"id": "anthropic-default",
"name": "Anthropic",
"provider": "anthropic",
"headers": {}
}
]
}
provider-models-updated
Emitted when provider models are updated.
Data Structure:
{
"models": [
{
"id": "gpt-4",
"providerId": "openai-default",
"maxInputTokens": 128000,
"maxOutputTokens": 4096,
"inputCostPerToken": 0.000003,
"outputCostPerToken": 0.000015,
"cacheWriteInputTokenCost": 0.00000375,
"cacheReadInputTokenCost": 0.000000375,
"supportsTools": true,
"isCustom": false,
"isHidden": false,
"hasModelOverrides": false,
"providerOverrides": {}
}
],
"loading": false,
"errors": {}
}
Token and Usage Events
update-tokens-info
Emitted when token usage information is updated.
Data Structure:
{
"baseDir": "/path/to/project",
"taskId": "task-uuid",
"chatHistory": {
"tokens": 500,
"tokensEstimated": false,
"cost": 0.01
},
"files": {
"src/main.ts": {
"tokens": 200,
"tokensEstimated": false,
"cost": 0.004
}
},
"repoMap": {
"tokens": 150,
"tokensEstimated": false,
"cost": 0.003
},
"systemMessages": {
"tokens": 100,
"tokensEstimated": false,
"cost": 0.002
},
"agent": {
"tokens": 300,
"tokensEstimated": false,
"cost": 0.006
}
}
Usage Examples
Complete Client Implementation
import io from 'socket.io-client';
class AiderDeskClient {
constructor(port = 24337) {
this.socket = io(`http://localhost:${port}`);
this.setupEventHandlers();
}
connect() {
this.socket.on('connect', () => {
console.log('Connected to AiderDesk');
this.subscribeToEvents([
'response-chunk',
'response-completed',
'log',
'context-files-updated',
'task-completed',
'settings-updated'
]);
});
}
subscribeToEvents(eventTypes) {
this.socket.emit('message', {
action: 'subscribe-events',
eventTypes: eventTypes
});
}
setupEventHandlers() {
// Handle response streaming
this.socket.on('response-chunk', (data) => {
console.log(`AI Response chunk for task ${data.taskId}:`, data.chunk);
process.stdout.write(data.chunk);
});
// Handle completion
this.socket.on('response-completed', (data) => {
console.log(`Response completed for task ${data.taskId} in ${data.baseDir}`);
if (data.usageReport) {
console.log(`Tokens: ${data.usageReport.sentTokens + data.usageReport.receivedTokens}, Cost: ${data.usageReport.messageCost}`);
}
});
// Handle context updates
this.socket.on('context-files-updated', (data) => {
console.log(`Context updated for task ${data.taskId} in ${data.baseDir}`);
console.log(`Files in context: ${data.files.length}`);
});
// Handle task lifecycle
this.socket.on('task-completed', (data) => {
console.log(`Task ${data.name} completed in ${data.baseDir}`);
console.log(`Total cost: ${data.aiderTotalCost + data.agentTotalCost}`);
});
// Handle settings updates
this.socket.on('settings-updated', (data) => {
console.log('Settings updated:', data);
});
// Handle logs
this.socket.on('log', (data) => {
console.log(`[${data.level.toUpperCase()}] ${data.baseDir} [${data.taskId}]: ${data.message}`);
});
}
disconnect() {
this.socket.disconnect();
}
}
// Usage
const client = new AiderDeskClient();
client.connect();
React Hook for Real-Time Updates
import { useEffect, useState } from 'react';
import io from 'socket.io-client';
export function useAiderDeskEvents(projectDir, taskId = null, eventTypes = []) {
const [isConnected, setIsConnected] = useState(false);
const [events, setEvents] = useState([]);
useEffect(() => {
const socket = io('http://localhost:24337');
socket.on('connect', () => {
setIsConnected(true);
socket.emit('message', {
action: 'subscribe-events',
eventTypes: eventTypes
});
});
socket.on('disconnect', () => {
setIsConnected(false);
});
// Listen to all subscribed events
eventTypes.forEach(eventType => {
socket.on(eventType, (data) => {
// Filter by project directory
if (data.baseDir !== projectDir) {
return;
}
// Filter by task ID if specified
if (taskId && data.taskId && data.taskId !== taskId) {
return;
}
setEvents(prev => [...prev, { type: eventType, data, timestamp: Date.now() }]);
});
});
return () => {
socket.disconnect();
};
}, [projectDir, taskId, eventTypes]);
return { isConnected, events };
}
// Usage example
function TaskComponent({ projectDir, taskId }) {
const { isConnected, events } = useAiderDeskEvents(
projectDir,
taskId,
['response-chunk', 'response-completed', 'task-completed']
);
return (
<div>
<div>Connected: {isConnected ? 'Yes' : 'No'}</div>
<div>Events: {events.length}</div>
</div>
);
}
Task-Specific Event Filtering
// Listen to events for a specific task
socket.on('connect', () => {
socket.emit('message', {
action: 'subscribe-events',
eventTypes: ['response-chunk', 'response-completed', 'tool', 'log']
});
});
// Filter events by task ID
socket.on('response-chunk', (data) => {
if (data.taskId === 'my-specific-task-id') {
console.log('Response for my task:', data.chunk);
}
});
socket.on('tool', (data) => {
if (data.taskId === 'my-specific-task-id') {
console.log(`Tool ${data.toolName} executed:`, data.response);
}
});
Error Handling
Handle connection errors and disconnections gracefully:
socket.on('connect_error', (error) => {
console.error('SocketIO connection error:', error);
// Implement retry logic or show error message to user
});
socket.on('disconnect', (reason) => {
console.log('Disconnected:', reason);
if (reason === 'io server disconnect') {
// Server disconnected, manual reconnection needed
socket.connect();
} else if (reason === 'ping timeout' || reason === 'transport close') {
// Network issues, socket will try to reconnect automatically
console.log('Network issue detected, attempting to reconnect...');
}
});
// Handle connection timeout
socket.on('connect_timeout', () => {
console.error('Connection timeout');
});
Best Practices
- Event Filtering: Always filter events by
baseDirandtaskIdto only process relevant updates - Connection Management: Implement proper connection lifecycle management with reconnection logic
- Error Handling: Handle network errors and connection drops gracefully with user feedback
- Resource Cleanup: Always disconnect when the component/application unmounts
- Selective Subscriptions: Only subscribe to the events you need to minimize network traffic
- Task-Level Filtering: Use
taskIdfiltering for granular event handling in multi-task scenarios - Type Safety: Use TypeScript interfaces for event data structures when possible
- Event Ordering: Handle
sequenceNumberin response events for proper message ordering - Usage Tracking: Monitor
usageReportdata for cost tracking and optimization - Context Awareness: Use
promptContextto understand the context of events
Event Subscription Patterns
Project-Level Events
// Subscribe to project-wide events
socket.emit('message', {
action: 'subscribe-events',
eventTypes: ['project-started', 'settings-updated', 'providers-updated']
});
Task-Level Events
// Subscribe to task-specific events
socket.emit('message', {
action: 'subscribe-events',
eventTypes: ['response-chunk', 'response-completed', 'tool', 'task-completed']
});
// Filter by taskId in event handlers
socket.on('response-chunk', (data) => {
if (data.taskId === currentTaskId) {
handleResponseChunk(data);
}
});
System-Level Events
// Subscribe to system-wide events
socket.emit('message', {
action: 'subscribe-events',
eventTypes: ['versions-info-updated', 'log']
});