Run status
The run-status surface shows what's happening with the agent runs you dispatch to your own local runner daemon — connection state, the current dispatch, live stdout, and the active runtime controls AuditTrail has applied to your gateway-routed traffic. Execution stays on your runner; AuditTrail orchestrates and observes it.
Connection & last dispatch
GET /api/v1/runner/status returns whether a daemon is currently connected, the
most-recent open session row, and a summary of the most-recent dispatch
(correlation_id, template_id, status, timestamps, resolved trace_id).
{
"connected": true,
"session": { "id": "…", "token_id": "…", "connected_at": "…", "client_info": {} },
"last_dispatch": {
"id": "…", "correlation_id": "…", "template_id": "quickstart",
"status": "completed", "created_at": "…", "completed_at": "…", "trace_id": "…"
}
}Dispatch lifecycle
A dispatch row moves pending → running → {completed | failed | cancelled}. It
flips to running on the daemon's first output line and to a terminal status on
the daemon's exit frame (exit code 0 → completed, else failed). The dispatch
keeps a 500-line FIFO ring buffer of stdout/stderr (stdout_tail_json) and, on
completion, the trace_id the run reported so you can jump straight to the trace.
REST surface
routes/runner.py — prefix /api/v1/runner.
| Method | Path | Purpose |
|---|---|---|
GET | /runner/status | Connection + last-dispatch summary |
POST | /runner/dispatch | Send a run to the connected daemon |
GET | /runner/dispatches/{dispatch_id} | Poll status + recent stdout tail |
GET | /runner/dispatches/{dispatch_id}/stream | SSE relay of newly-appended output lines |
DELETE | /runner/dispatches/{dispatch_id} | Cancel (marks cancelled, signals the daemon) |
Live output stream
GET /runner/dispatches/{dispatch_id}/stream is a Server-Sent Events relay over
the ring buffer. It yields newly-appended lines roughly every 500ms and emits a
terminal data: [DONE] frame when the dispatch reaches a terminal status. The
loop is bounded (10 minutes) so a runaway dispatch can't hold the connection open
forever. The dashboard's run-output tile consumes this stream and auto-scrolls
while you're near the bottom.
Cancel
A DELETE marks the dispatch cancelled (if not already terminal) and
best-effort sends a cancel frame to the daemon over the WebSocket link, so the
daemon can stop the subprocess.
Active runtime controls
The run-status page also surfaces the runtime controls the deployment-action
executor currently has applied to your gateway-routed traffic — the read-only
projection of switch_model / throttle / disable_flag actions you approved.
GET /api/v1/deployments/actions/runtime-controls returns only your active
controls, newest first, each linking back to the deployment_actions row that
created it. See Executable actions for how those get
written, and the gateway proxy for where they're enforced.
Each control has a Deactivate button on the Run Status page. It calls
POST /api/v1/deployments/actions/runtime-controls/{id}/deactivate, which flips
the control inactive so the gateway stops enforcing it immediately and it leaves
the active list — the operator counterpart to the automatic TTL reaper below,
for a control you want to lift by hand. It's a typed-confirm action, and it only
affects your own controls (a control owned by another tenant returns 404).
Time-limited controls (TTL)
A control can carry an optional expiry. When the approved action's outcome
includes a positive ttl_seconds, the control is written with an expires_at
that many seconds in the future; with no ttl_seconds the control is permanent
until you deactivate it. Each row in the response exposes expires_at (null
for a permanent control).
Expiry is enforced two ways, so it is both immediate and durable:
- The gateway stops applying an expired control on the very next request —
it is skipped fail-open the instant
expires_atpasses, even before any background sweep runs. - A background reaper then flips the expired control's
activeflag tofalseand broadcastsruntime_control.expired, so the row drops out of this list and open dashboards update live.
This lets you approve, say, a 15-minute throttle on a misbehaving model that lifts itself automatically — no second action needed to undo it.
Live updates
The daemon link (/api/v1/runner/ws) broadcasts runner.connected /
runner.disconnected when the daemon attaches/detaches, and
runner.dispatch.output / runner.dispatch.final as a run streams and finishes,
so open dashboard tabs update without polling.
Data model
| Table | Holds |
|---|---|
runner_dispatches | One row per dispatch (template, prompt, status, 500-line stdout ring, trace id) |
runner_sessions | Per-connection daemon session (disconnected_at stamped on close) |
runtime_controls | Active gateway-enforced controls written by the action executor |