Skip to content

Operator Protocol Reference

The Operator Protocol defines the wire format and interaction model between the Construct frontend and the Go operator backend over TCP.

Connection Details

  • Port: 60100
  • Protocol: TCP
  • Wire Format: Newline-delimited JSON (NDJSON)
  • Encoding: UTF-8

Message Format

All messages are JSON objects terminated with a newline character (\n). Each line is a complete, independent JSON object.

Request Types

send_context_request

Synchronously sends context to an agent and waits for a response. Used for one-shot requests with minimal state overhead.

json
{
  "type": "send_context_request",
  "context": {
    "project": { "name": "my-project", "path": "/path/to/project" },
    "tools": ["bash", "read", "write"],
    "userMessage": "analyze this code"
  }
}

query

Sends a request and retrieves the complete dispatch result synchronously.

json
{
  "type": "query",
  "sessionID": "sess_abc123",
  "messages": [
    {
      "role": "user",
      "content": [{ "type": "text", "text": "What does this function do?" }]
    }
  ]
}

dispatch

Sends a request and returns immediately with a sessionID for streaming results.

json
{
  "type": "dispatch",
  "messages": [
    {
      "role": "user",
      "content": [{ "type": "text", "text": "Run the tests" }]
    }
  ],
  "tools": ["bash"],
  "agentID": "architect"
}

stream

Opens a streaming connection for a session. Messages arrive as DispatchResult objects with sessionID matching the requested session.

json
{
  "type": "stream",
  "sessionID": "sess_abc123"
}

Response Format

All responses are DispatchResult objects with the following structure:

typescript
interface DispatchResult {
  sessionID: string;           // Unique session identifier
  agentID: string;             // Agent that produced this response
  content: ResponseBlock[];    // Response content blocks
  turns: Turn[];               // Complete turn history
  usage: {
    inputTokens: number;       // LLM input token count
    outputTokens: number;      // LLM output token count
  };
  stopReason: string;          // "end_turn" | "tool_use" | "max_tokens"
}

Turn Structure

A turn represents a complete request-response cycle:

typescript
interface Turn {
  request: Request;
  response: Response;
  toolCalls: ToolExecution[];
}

interface Request {
  role: "user" | "assistant";
  content: RequestBlock[];
}

interface Response {
  role: "assistant";
  content: ResponseBlock[];
}

interface ToolExecution {
  id: string;
  name: string;
  input: Record<string, unknown>;
  result: string;
  error?: string;
  executedAt: string; // ISO 8601 timestamp
}

Block Types

RequestBlock

Represents input provided by the user or system:

typescript
type RequestBlock =
  | { type: "text"; text: string }
  | { type: "image"; url: string; mimeType: string }
  | { type: "file"; path: string; size: number };

ResponseBlock

Represents output generated by the agent:

typescript
type ResponseBlock =
  | { type: "text"; text: string }
  | { type: "tool"; id: string; name: string; input: Record<string, unknown> }
  | { type: "code"; language: string; source: string }
  | { type: "svg"; content: string; width?: number; height?: number }
  | { type: "image"; url: string; alt?: string }
  | { type: "error"; message: string; code?: string };

Stream Events

When using stream request type, events arrive as complete DispatchResult objects. The frontend useAgentSession composable handles:

  1. Stream Start: First event with sessionID and empty content
  2. Content Streaming: Events with incremental content blocks
  3. Tool Execution: Events with toolCalls array populated
  4. Turn Completion: Events with stopReason set to "end_turn"
  5. Stream End: Final event indicating completion

Example stream sequence:

{ sessionID: "sess_abc", agentID: "architect", content: [], turns: [], stopReason: "stream_start" }
{ sessionID: "sess_abc", agentID: "architect", content: [{ type: "text", text: "Analyzing..." }], turns: [] }
{ sessionID: "sess_abc", agentID: "architect", content: [{ type: "tool", ... }], turns: [...] }
{ sessionID: "sess_abc", agentID: "architect", content: [...], turns: [...], stopReason: "end_turn" }

Connection Lifecycle

Connect

javascript
const socket = new WebSocket("ws://localhost:60100");
socket.addEventListener("open", () => {
  // Connection established
  socket.send(JSON.stringify({ type: "stream", sessionID: "sess_abc" }));
});

Reconnect

If connection drops during a stream:

  1. Detect disconnect via close event
  2. Reconnect to the server
  3. Send stream request with same sessionID
  4. Continue receiving from last received turn
javascript
socket.addEventListener("close", () => {
  setTimeout(() => {
    const newSocket = new WebSocket("ws://localhost:60100");
    // Resume streaming
  }, 1000);
});

Error Handling

Errors are communicated as ResponseBlock objects with type "error":

json
{
  "sessionID": "sess_abc",
  "agentID": "architect",
  "content": [
    {
      "type": "error",
      "message": "Tool 'bash' not available in this context",
      "code": "TOOL_NOT_AVAILABLE"
    }
  ],
  "turns": [],
  "stopReason": "error"
}

Common error codes:

  • TOOL_NOT_AVAILABLE: Requested tool not in available tools list
  • TOOL_EXECUTION_FAILED: Tool executed but returned error
  • AGENT_NOT_FOUND: Specified agent ID does not exist
  • SESSION_NOT_FOUND: Session ID does not exist or has expired
  • INVALID_REQUEST: Request JSON is malformed or missing required fields
  • RATE_LIMIT_EXCEEDED: Too many requests in short time window
  • CONTEXT_TOO_LARGE: Context or message size exceeds limits

Frontend Integration

The useAgentSession composable from @construct-space/sdk handles the protocol details:

typescript
import { useAgentSession } from "@construct-space/sdk";

const { dispatch, stream, loading, result } = useAgentSession();

// For immediate response
const result = await dispatch({
  messages: [{ role: "user", content: [{ type: "text", text: "help" }] }],
  agentID: "architect"
});

// For streaming
const sessionID = await stream({
  messages: [...],
  agentID: "architect"
});

See SDK Reference for complete composable documentation.

Construct Team — Internal Developer Documentation