Docs

Running and streaming

A defined agent does nothing until a conversation drives it. You drive it over HTTP — create a thread, post a message, follow the run — and stream the run live over a websocket.

Threads, runs, and messages

  • A thread is one conversation with one agent, owned by a principal. It has a status (active or archived) and an ordered list of messages.
  • A run is one turn. Posting a user message to a thread triggers a run.
  • A message has a role (system, user, assistant) and structured parts: text, reasoning, step-start, tool-call, and file. The assistant message is persisted once the run finishes.

HTTP API

MethodPathPurpose
GET/api/agentsList agents you can run.
GET/api/agents/:agentIdGet one agent.
GET/api/agent-threadsList threads (filter by agentId, status).
POST/api/agent-threadsCreate a thread ({ agentId, title?, threadId? }).
GET/api/agent-threads/:threadIdGet a thread.
GET/api/agent-threads/:threadId/messagesList a thread's messages.
POST/api/agent-threads/:threadId/messagesPost a user message — triggers a run.
GET/api/agent-runs/:runIdGet a run's status.
POST/api/agent-threads/:threadId/cancelCancel the thread's active run ({ runId }).
GET/api/agent-threads/:threadId/messages/:messageId/files/contentDownload a file attached to a message.

Trigger a run

There is no "run" endpoint — posting a message is the trigger. The 202 response returns the ids you need to follow it:

JSON
// POST /api/agent-threads/:threadId/messages   { "text": "Which invoices are overdue?" }
{
  "threadId": "thr_...",
  "runId": "run_...",
  "triggerMessageId": "msg_...",
  "streamId": "agents.runs.run_...",
  "createdThread": false
}

The runId and streamId come back right away, so you can open the stream before the turn starts. A thread runs one turn at a time — posting while a run is active returns 409; wait for it to finish first.

Attachments

A user message can carry files: pass attachments — an array of FileRefs, the same blob references objects use — alongside text.

JSON
// POST /api/agent-threads/:threadId/messages
{ "text": "Summarize this contract", "attachments": [ /* FileRef */ ] }

Attachments — and any files the agent produces — appear as file parts on the stored message ({ "type": "file", "fileRef": … }). Download the bytes from GET /api/agent-threads/:threadId/messages/:messageId/files/content.

Run status

GET /api/agent-runs/:runId returns the run record.

StatusMeaning
runningThe turn is in progress.
succeededThe turn completed and the reply was persisted.
failedA model/tool error or the turn timeout ended it (error has details).
cancelledThe run was aborted.

A finished run also carries finishReason (stop, length, tool-calls, content-filter, error, other, unknown), usage token counts (inputTokens, outputTokens, totalTokens, reasoningTokens, cachedInputTokens), and modelId.

Cancel an in-flight run with POST /api/agent-threads/:threadId/cancel (body { runId }); it ends as cancelled.

Stream a run

Connect to /ws/agents and send JSON commands; the server replies with JSON events.

CommandFieldsPurpose
subscriberunId, threadId?, afterCursor?Follow a run live.
replayrunId, threadId?, afterCursor?, limit?Read past records once.
unsubscriberunId?Stop the subscription.
JSON
// follow from the start (pass threadId so the server can authorize you before the run row exists)
{ "type": "subscribe", "runId": "run_...", "threadId": "thr_..." }

// resume after a disconnect, from the last cursor you saw
{ "type": "subscribe", "runId": "run_...", "afterCursor": "..." }

Each record frame carries an AgentRunStreamEvent:

EventWhenFields
agent.run.startedThe turn began.modelId
agent.ui.chunkLive output as the model streams.chunkIndex, chunk
agent.message.finalizedThe assistant message was persisted.messageId
agent.run.finishedThe run ended.status, finishReason, error

Records carry a cursor. Persist the last one you saw and pass it as afterCursor to resume where you left off — a client that reconnects mid-run replays the gap. agent.ui.chunk events are live; the durable copy of the turn is the persisted assistant message, read back with GET /api/agent-threads/:threadId/messages.

A ready-made chat UI

You rarely need to wire this HTTP + WebSocket flow by hand. @sixb/agent-ui ships a turnkey React chat: AgentChat (a full thread with composer and live transcript), AgentsHome (an agent picker), and the Composer, Transcript, and streaming hooks as building blocks. For a React-Router app, @sixb/agent-ui/react-router exposes a drop-in AgentChatPage:

TSX
import { AgentChatPage } from "@sixb/agent-ui/react-router"

export default function Agents() {
  return <AgentChatPage routeBase="/agents" />
}

Import @sixb/agent-ui/globals.css once for styling. These components call the same /api/agent-* routes and /ws/agents stream documented above, so anything they do is reachable from your own client too.

Search docs

Search the documentation