Skip to content

HTTP & WebSocket APIs

Runner HTTP API (local + hybrid)

Every deployment exposes an Express server on the configured port (default 3000). The dashboard static assets mount under /dashboard/.

Core job + health

MethodPathPurpose
GET/healthLiveness
POST/jobsCreate job (type, workflowPath, params)
GET/jobsList jobs (filters, pagination — see handler)
GET/jobs/:jobIdFull job snapshot
GET/jobs/:jobId/streamSSE log stream
GET/jobs/:jobId/artifacts/:artifactId/contentDownload artefact bytes
POST/intake/streamCoro plan mode — SSE stream for investigative run intake (see below)
GET/intake/sessionsList investigations (summaries). Query: limit (default 5, max 50), offset
GET/intake/sessions/:sessionIdFull investigation; rehydrates the runner’s in-memory LLM cache
PUT/intake/sessions/:sessionIdDashboard transcript snapshot (items, readiness, model, counters). Empty chats are not inserted.
DELETE/intake/sessions/:sessionIdDiscard a plan-mode conversation (in-memory cache and durable row)
POST/jobs/:jobId/resumeManual / synthetic resume
POST/jobs/:jobId/cancelCooperative cancel
POST/jobs/:jobId/pausePause scheduling
POST/jobs/:jobId/messageMid-flight developer message

Campaign child controls

MethodPath
POST/jobs/:jobId/children/:name/skip
POST/jobs/:jobId/children/:name/rerun
POST/jobs/:jobId/children/:name/cancel
POST/jobs/:jobId/children/:name/abandon
POST/jobs/:jobId/children/:name/resume
POST/jobs/:jobId/children/:name/start

Intelligence editing (dashboard)

MethodPath
GET/PUT/DELETE/intelligence/file
GET/intelligence/layers
POST/intelligence/preflight

Plugins + diagnostics

MethodPath
GET/plugins
POST/plugins/install
DELETE/plugins/:id
GET/plugins/:id/models
POST/plugins/:id/healthcheck
POST/test/plugin/:id
POST/config/plugins/:id/auth/detect
POST/config/plugins/:id/auth/detect/apply

Config + proposals

MethodPath
GET/PUT/config
GET/config/claude-code-mcps
GET/proposals
GET/jobs/:jobId/insights
DELETE/jobs/:jobId/insights/:insightId
POST/jobs/:jobId/phases/:phase/rerun

Static UI

MethodPath
GET/dashboard/*
GET/

POST /intake/stream (Coro plan mode)

Server-Sent Events endpoint used by the New Run chat. Requires a healthy LLM executor plugin; returns 503 with reason: no-llm when plugins are not initialised.

The conversation is durable runner state keyed by sessionId. The dashboard posts the new message plus an optional transcript fallback. Successful turns are written to the investigations table; the UI transcript is saved with PUT /intake/sessions/:id.

Request body:

{
"sessionId": "uuid-or-stable-browser-id",
"message": "Add rate limiting to /api/users",
"transcript": [{ "role": "user", "content": "earlier turn" }],
"model": "claude-sonnet-4-6",
"provider": "anthropic",
"context": {
"recentRepos": ["org/api-service"],
"recentReviewers": ["alice"],
"availableWorkflows": [
{
"id": "job",
"name": "Implementation Job",
"workflowPath": "workflows/job/workflow.md",
"description": "Default lane for scoped feature work…"
}
],
"userLocale": "en"
}
}

message is the new developer turn. transcript is the client’s copy of the earlier turns. It seeds a session the runner does not have (restart mid-investigation) and fills in turns the server never recorded (rate-limit, empty output, abort). It is a fallback, not the store — investigations live in SQLite (local) or Postgres (hybrid). A body that omits message but supplies a full messages array is still accepted: the last user entry becomes the message and the rest seeds the session.

model and provider are optional per-session overrides from the dashboard model picker. When omitted, the handler resolves the planning tier via settings.llm.aliases.

SSE events (JSON payloads on message frames):

typeMeaning
token{ "text": "…" } assistant delta
thinking{ "text": "…" } model reasoning (same live feed as a running job)
tool_start / tool_endRead-only tracker/SCM lookups while intake.toolsEnabled is on
doneTurn complete; may include usage, plus contextTokens, sessionTokens, and turns
error{ "message": "…", "reason?": "…" } provider failure

The handler calls the executor’s optional chat() method. Continuity uses the same dual-shape session state as a job: Claude Code persists a sessionId and resumes it; OpenAI replays conversationHistory. messages (with tool <evidence>) is the fallback after a restart or provider switch. When tools are enabled, chat() runs a read-only tool-use loop bounded at 25 rounds per turn.

No session budgets. Turn and token caps were removed: every turn is developer-initiated, so there is no autonomous loop to bound, and a cap would end an investigation mid-way with nothing to dispatch. The counters on the done frame are for display only. The per-turn tool-round ceiling remains, since that is the only unattended spend.

Each assistant turn ends with a <readiness>{ "state": "investigating" | "ready" | "no-run-needed", "openQuestions": [], "note": "" }</readiness> block. A markdown <findings>…</findings> write-up is parsed into a Findings card and stored on the investigation as findings; the <run>{…}</run> payload appears only when the developer requests it or readiness is ready. All three are parsed client-side and hidden from the visible transcript.

Plan-mode streams intentionally do not abort when the HTTP request emits close immediately after body parsing (Express 4 + Node 20 quirk) — the LLM call runs to completion.

GET / PUT / DELETE /intake/sessions

Investigations are listed as summaries (GET /intake/sessions?limit=5&offset=0 returns { sessions, total, limit, offset }). GET /intake/sessions/:sessionId returns the full record (items, turns, resume blob) and rehydrates the in-memory LLM cache so the next chat turn can continue. PUT stores the dashboard transcript (items, readiness, findings — the current Findings card markdown — model choice, counters); the runner fills turns from the live session when it is present. Empty chats are never inserted.

DELETE drops both the hot cache and the durable row. The dashboard does not call it on New conversation or dispatch — those persist the current investigation and start a fresh sessionId.

See Coro plan mode for UX and troubleshooting.


Cloud control plane (hybrid mode)

When cloud.url + cloud.token are configured, a separate cloud service (packages/runner/src/cloud) fronts team operations:

SurfacePurpose
GET /healthLiveness
/auth/*OAuth / JWT for dashboard users
/teams/*Team CRUD
/teams/:teamId/jobs/*Remote job APIs (fan-out to connected runners)
/teams/:teamId/proposals/*Proposal review
POST /webhook/:teamId/:pluginIdHMAC-verified ingress; forwards { pluginId, headers, rawBody } to runners
GET /teams/:teamId/runnersConnection inventory
WS /ws/runnerAuthenticated channel for dispatch, state sync, forwarded webhooks

Plugin manifests contribute webhook.algorithm, header, and format so the cloud verifier stays provider-agnostic.


Local vs hybrid event delivery

  • Local mode: PollingTransport polls SCM for PR approvals/comments and injects synthetic events.
  • Hybrid mode: Real provider webhooks hit the cloud edge, verify HMAC, then flow across the runner WebSocket to parked jobs.

Full narrative + sequence diagrams live in docs/agent-host-spec.md within the Coro developer framework repository.