The WebSocket message protocol between Codmir clients and the daemon's agent system
Agent Protocol
The daemon's agent system uses a bidirectional WebSocket protocol. Clients send commands and receive events. All messages are JSON with a type field that determines the shape.
Commands (client → daemon)
Start a session
{
"type": "agent:start",
"task": "Fix the login bug in auth.ts",
"cwd": "/Users/you/projects/myapp",
"model": "claude-sonnet-4-20250514",
"maxIterations": 50,
"autoApprove": ["safe"]
}| Field | Type | Required | Description |
|---|---|---|---|
task | string | yes | The user's instruction |
cwd | string | no | Working directory (defaults to daemon's cwd) |
model | string | no | Model to use (default: claude-sonnet-4-20250514) |
maxIterations | number | no | Max tool-use loops (default: 50) |
autoApprove | string[] | no | Danger levels to auto-approve: safe, moderate, dangerous |
Send a follow-up message
{
"type": "agent:message",
"sessionId": "sess_abc123",
"content": "Actually, also fix the logout flow"
}Approve a tool call
{
"type": "agent:approve",
"sessionId": "sess_abc123",
"toolCallId": "tc_xyz789"
}Deny a tool call
{
"type": "agent:deny",
"sessionId": "sess_abc123",
"toolCallId": "tc_xyz789",
"reason": "Don't run rm -rf"
}Cancel a session
{
"type": "agent:cancel",
"sessionId": "sess_abc123"
}List sessions / Get session
{ "type": "agent:list" }
{ "type": "agent:get", "sessionId": "sess_abc123" }Events (daemon → client)
Events are broadcast to all connected WebSocket clients. Both the desktop app and IDE extension receive every event.
agent:started
Fired when a new agent session begins.
{
"type": "agent:started",
"session": {
"id": "sess_abc123",
"task": "Fix the login bug",
"status": "running",
"iteration": 0,
"maxIterations": 50,
"createdAt": 1718000000000,
"cwd": "/Users/you/projects/myapp",
"model": "claude-sonnet-4-20250514"
}
}agent:thinking
Streamed thinking/reasoning content. Arrives in chunks as the LLM generates.
{
"type": "agent:thinking",
"sessionId": "sess_abc123",
"content": "Let me look at the auth.ts file first..."
}agent:tool_call
The agent wants to use a tool.
{
"type": "agent:tool_call",
"sessionId": "sess_abc123",
"toolCall": {
"id": "tc_xyz789",
"tool": "read_file",
"input": { "path": "src/auth.ts" },
"danger": "safe",
"requiresApproval": false,
"approved": true,
"result": null
}
}agent:approval_required
The agent wants to use a dangerous tool and needs user approval. The session pauses until an agent:approve or agent:deny command is received.
{
"type": "agent:approval_required",
"sessionId": "sess_abc123",
"toolCall": {
"id": "tc_xyz789",
"tool": "shell",
"input": { "command": "npm test" },
"danger": "dangerous",
"requiresApproval": true,
"approved": null,
"result": null
}
}agent:shell_output
Real-time output chunks from a running tool (shell commands, Python scripts). Streamed as the process produces output.
{
"type": "agent:shell_output",
"sessionId": "sess_abc123",
"toolCallId": "tc_xyz789",
"chunk": "PASS src/auth.test.ts\n"
}agent:tool_result
A tool call has completed.
{
"type": "agent:tool_result",
"sessionId": "sess_abc123",
"result": {
"toolCallId": "tc_xyz789",
"tool": "read_file",
"success": true,
"output": "import { verify } from 'jsonwebtoken';\n...",
"error": null,
"durationMs": 12
}
}agent:message
The agent has produced a response message (complete, not streamed).
{
"type": "agent:message",
"sessionId": "sess_abc123",
"message": {
"role": "assistant",
"content": "I've fixed the login bug. The issue was...",
"timestamp": 1718000005000
}
}agent:completed
The session has finished successfully.
{
"type": "agent:completed",
"session": { "id": "sess_abc123", "status": "completed", "..." : "..." }
}agent:failed / agent:cancelled
{
"type": "agent:failed",
"sessionId": "sess_abc123",
"error": "Token budget exceeded (500000 tokens)"
}{
"type": "agent:cancelled",
"sessionId": "sess_abc123",
"message": "Session cancelled by user"
}agent:list
Response to an agent:list command.
{
"type": "agent:list",
"sessions": [
{ "id": "sess_abc123", "task": "Fix login", "status": "completed", "..." : "..." },
{ "id": "sess_def456", "task": "Add tests", "status": "running", "..." : "..." }
]
}Event flow diagram
A typical agent interaction follows this pattern:
Client Daemon LLM
│ │ │
│ agent:start(task) │ │
├──────────────────────────────→│ │
│ │ messages.create(task) │
│ ├─────────────────────────────→│
│ agent:started(session) │ │
│←──────────────────────────────┤ │
│ │ thinking chunks │
│ agent:thinking(chunk) │←─────────────────────────────┤
│←──────────────────────────────┤ │
│ │ tool_use: read_file │
│ agent:tool_call(tc) │←─────────────────────────────┤
│←──────────────────────────────┤ │
│ │ executeTool(read_file) │
│ agent:tool_result(r) │──→ local filesystem │
│←──────────────────────────────┤ │
│ │ tool_use: shell (danger) │
│ agent:approval_required │←─────────────────────────────┤
│←──────────────────────────────┤ │
│ │ ⏸ waiting_approval │
│ agent:approve(toolCallId) │ │
│──────────────────────────────→│ │
│ │ executeTool(shell) │
│ agent:shell_output(chunk) │──→ spawns bash process │
│←──────────────────────────────┤ │
│ agent:tool_result(r) │ │
│←──────────────────────────────┤ │
│ │ messages.create(results) │
│ ├─────────────────────────────→│
│ │ end_turn + text │
│ agent:message(content) │←─────────────────────────────┤
│←──────────────────────────────┤ │
│ agent:completed(session) │ │
│←──────────────────────────────┤ │Session states
starting → running ⇄ waiting_approval → completed
→ failed
→ cancelled| State | Description |
|---|---|
starting | Session created, first LLM call about to begin |
running | Agent loop active — thinking, calling tools, or waiting for LLM response |
waiting_approval | A dangerous tool call needs user approval before execution |
completed | Agent finished its task (LLM returned end_turn with no tool calls) |
failed | An error occurred (token budget, timeout, LLM error) |
cancelled | User sent agent:cancel |
Limits
| Limit | Default | Description |
|---|---|---|
| Max iterations | 50 | Maximum tool-use loop cycles per session |
| Token budget | 500,000 | Maximum total tokens (input + output) |
| Runtime | 30 minutes | Maximum wall-clock time per session |
| Shell timeout | 30 seconds | Per-command timeout for shell tool |
| Python timeout | 60 seconds | Per-execution timeout for python tool |
Using KernelClient
The @codmir/kernel package exports a KernelClient class that wraps the WebSocket protocol:
import { KernelClient } from '@codmir/kernel/client';
const client = new KernelClient(7700);
client.connect();
// Listen for events
client.onAgentSession((session) => {
console.log('Session started:', session.id);
});
client.onAgentThinking((sessionId, content) => {
process.stdout.write(content);
});
client.onAgentToolCall((sessionId, toolCall) => {
console.log(`Tool: ${toolCall.tool}`, toolCall.input);
});
client.onAgentApproval((sessionId, toolCall) => {
// Auto-approve safe tools, prompt for dangerous ones
if (toolCall.danger === 'safe') {
client.agentApprove(sessionId, toolCall.id);
}
});
client.onAgentMessage((sessionId, message) => {
console.log('Response:', message.content);
});
// Start a session
client.agentStart('Fix the login bug in auth.ts', {
cwd: '/path/to/project',
});