Skip to main content
A “run” is one execution — either an ad-hoc agent/chat run (orchestrator picks up an assignment, exec’s the CLI) or a routine/pipeline run — and ends with one of COMPLETED / FAILED / CANCELLED / TIMEOUT. Since PR #234 the legacy agent_runs table is gone; runs are reconstructed from the Crew Journal. The two engines key their journal entries differently, and GET /api/v1/runs and GET /api/v1/runs/{id} read both (#2284):
  • An ad-hoc run’s entries (run.started + one terminal run.{completed|failed|cancelled|timeout}) are grouped on trace_id, which equals the run id.
  • A routine/pipeline run’s entries (pipeline.run.started + one terminal pipeline.run.{completed|failed}) never set trace_id; they’re grouped on actor_id instead, which the pipeline engine stamps to the run’s own id. There is no dedicated pipeline.run.cancelled — a run cancelled mid-flight is still pipeline.run.failed, with the real outcome on payload.status.
Every row carries kind: "agent" | "pipeline" so a caller can tell which engine produced it. The HTTP shape otherwise stays the one frontend consumers already know — this widened what rows can appear, not the shape of a row. GET /api/v1/runs/insights and the stats object embedded in GET /api/v1/runs are not widened by #2284 — both still aggregate ad-hoc runs only (journal.RunInsights / journal.RunStats). That was a deliberate scope limit: unifying cost roll-ups and correlation across the two engines is a separate decision with its own consequences. Every endpoint is workspace-scoped via the session context.
Runs are read-only — there is no write endpoint. An ad-hoc run comes into existence when the orchestrator emits run.started and concludes when a terminal run.{completed|failed|cancelled|timeout} lands on the same trace_id. A routine run comes into existence when the pipeline engine emits pipeline.run.started and concludes when pipeline.run.{completed|failed} lands on the same actor_id.

Endpoints


List runs

Returns a paginated list of runs with KPI tiles (stats) and pagination metadata. Backed by journal.ListRuns — a CTE-grouped read over journal_entries, keyed on trace_id for an ad-hoc run and actor_id for a routine/pipeline run (COALESCE(trace_id, actor_id), #2284) — plus journal.RunStats for the tiles (ad-hoc runs only; see the note above). Query parameters: A kind: "pipeline" row also carries pipeline_slug, the routine it ran (empty for an agent run), and its trigger_type is derived from pipeline_runs.triggered_via in the same USER / CRON / WEBHOOK vocabulary as agent runs. | page | integer | 1-based page index, default 1. Values below 1 become 1; values above the server’s maximum page are capped. | | limit | integer | Page size, 1-100, default 50. Out-of-range values fall back to the default. | Response: 200 OK
mission_id and mission_identifier name the issue the run worked on (from the run.started entry the dispatcher stamps); both are omitted for a chat-only run. crew_slug is what the crew’s page is keyed on. The same fields ride on GET /api/v1/runs/{id}. Errors:

Fleet insights

Returns a fleet-operations aggregate over the workspace’s ad-hoc agent runs for a time window — every chat, CLI, webhook and cron invocation of an agent. Routine/pipeline runs are not included: the aggregate reads run.started traces only (journal.RunInsights, trace_id IS NOT NULL AND entry_type LIKE 'run.%'), and #2284 deliberately left it that way — see the note at the top of this page. The handler resolves per-agent counts into a crew rollup and a display-named top-agents leaderboard (one workspace-scoped agents→crews join). Query parameters: Response: 200 OK

Get a single run

Returns a single run by id. The response shape is identical to one element of the data[] array from GET /api/v1/runs — the handler reuses the same enrichment so dashboard detail views can share the row renderer. Path parameters: Response: 200 OK
The handler uses the workspace-scoped indexed journal.GetRunByID lookup. A trace ID from another workspace is treated as not found.

Tenancy

  • workspace_id is taken from the session context — never from a query parameter.
  • Cross-tenant trace IDs are filtered out at the journal store (journal.ListRuns requires a non-empty WorkspaceID).
  • Enrichment lookup (agents + crews) is workspace-scoped, so an agent ID collision across workspaces (test fixtures, restored backups) cannot attach foreign names to a row.