v0.7.1-beta.2 is out · 22 Octsee the release
Browse guides / Insights
DOCS

Insights

docs/insights.md on GitHub

Insights are statistics about what your agents did over a date range: runs launched and how they ended, swarms, time spent, cost and pull requests. The server computes them from its database (<data-dir>/aether.db) on each insights.get call; nothing is sent anywhere else.

A run is one agent session in its own container, started in a workspace (a repository plus its environment). A swarm is a group of runs: one integrator run splits the work into tasks and launches a worker run for each, retrying a worker when it fails.

The request

CODE
POST /api/v1/insights.get
Content-Type: application/json
Authorization: Bearer <local-gateway-token>

{"workspace_id":"","scope":"mine","from":"2026-10-09T00:00:00-07:00","to":"2026-10-16T00:00:00-07:00","time_zone":"America/Los_Angeles"}
Param Meaning
workspace_id One workspace, or "" for every workspace. An unknown workspace is error -32000.
scope mine: runs you own and swarms you are accountable for. everyone: every member's, summed; never broken down per member. Any member who can see runs can ask for either.
from, to RFC3339 times; the range is [from, to). to must be after from and the range at most 400 local days.
time_zone IANA name, such as Europe/Berlin, that days are counted in, DST included. "" is UTC; Local and unknown names are rejected. The server binary carries its own zone database, so a host without system tzdata resolves every name.

A bad param is error -32602 with the reason, for example range spans 401 days; the limit is 400. The range, its comparison period and the heatmap must fall between 1677 and 2262, the times the database can store.

The local gateway lists the result shape. The rest of this guide says what each number means.

Which runs count

Run counts, the series, the heatmap, by_workspace, by_agent, durations, cost and recent count only top-level runs: runs that are not a swarm worker and not a swarm integrator of any generation. A swarm's runs are counted once, under swarms, so a worker retry or a replaced integrator never adds a run of its own. needs_you and pull_requests count swarm runs as well.

Whether a run belongs to a swarm is stored on the run (runs.swarm_run) when the run is created, so deleting the swarm does not turn its runs into top-level runs. Upgrading marks existing runs from the swarm records still in the database; the runs of a swarm deleted before the upgrade count as top-level.

Numbers

Field Meaning
totals.launched Runs created in the range.
totals.completed, merged, failed, interrupted, abandoned Runs that finished in the range with that status. A run launched before the range and finished in it counts here.
totals.killed Abandoned runs whose reason is killed: a member stopped them. Part of abandoned.
totals.finished completed + merged.
totals.active Runs launched in the range that are queued, provisioning, running or waiting for you now.
previous The same totals for the period just before from: the same number of local days when from and to are both local midnights, otherwise the same length of time, so "the last 7 days ending now" is compared with the 168 hours before it.
bucket, series One entry per local day, or per week starting Monday when the range is over 92 days, zero-filled. start is the local date the bucket starts; the first and last buckets are cut at from and to.
heatmap The 182 local days ending on the local day of to minus one nanosecond, whatever from is: a to at local midnight ends it on the day before, a to of now ends it today. runs is how many runs were active that day: started (or created, if never started) before the day ended and finished (or still running) after it began.
by_workspace, by_agent Runs launched, finished (completed or merged) and failed in the range, per workspace and per agent and model, most launched first. A model of "" is the agent's default model.
swarms Swarms created in the range, scoped by their accountable member: how many, how many are now completed or cancelled, agents (distinct worker and integrator runs they started that still exist) and tasks_accepted.
needs_you Runs in scope waiting for a member right now, as on the Board, swarm runs included. Ignores the range.
recent Up to 50 runs that finished in the range, newest first, with the last pull request the run recorded. title is the run's title, or the first non-blank line of its task when it has none, cut to 120 characters, as the dashboard names runs.

Durations

durations covers runs that finished in the range.

  • median_run_seconds, p90_run_seconds: from start (or creation, if the run never started) to finish. runs_measured is how many runs.
  • median_working_seconds: a run's total time working.
  • median_waiting_seconds: a run's total time waiting on you.
  • median_response_seconds: every stretch of waiting on you that ended with the run working again, so how long a run waited for an answer.

The last three come from the status and activity changes the server records. A run's activity is what the scheduler sees it doing while it is running or needs-attention, published as the run's state (run activity). Where a change recorded the activity:

Activity Counts as
working, waiting-agents, waiting-ci, waiting-tools working
needs-you waiting on you
waiting (or legacy idle), finished, failed, disconnected neither

So a swarm integrator parked in needs-attention while its workers run is working, not waiting on you. Where a change did not record an activity, as on every change before this version, time in running is working and time in needs-attention is waiting on you.

They cover a run's last session that spent time working: from leaving queued, or from a relaunch, to finishing. A relaunch that failed and was rolled back before the agent ran leaves the session before it measured. A run whose record starts mid-session is left out; state_runs_measured is how many runs are in. Percentiles are nearest-rank, and every duration is 0 when nothing was measured.

Cost

cost sums the recorded usage of runs that finished in the range, across every account that paid for a run that moved between accounts. A run's owner, not the paying account, decides whether it is in scope. Only headless Claude runs are metered; any other run is counted in unmetered_runs. An unmetered run's cost is unknown, not zero, so cost_usd and the token counts are a floor.

Pull requests

pull_requests.opened counts pull requests opened through run.pr.create in the range, each repository and number once however many runs recorded it. merged counts those whose last observed state is merged and whose GitHub merge time is in the range. A pull request an agent opened with gh directly is not counted.

The server learns a merge only when run.pr.status is called for the run; it does not poll GitHub, and insights.get never calls it. While the run's container is up, the call runs gh in it, as before. Once the run has finished (completed, merged, failed or abandoned) and its container is gone, run.pr.status {"run_id":"<run-id>"} answers from the recorded pull requests instead: each one not yet seen merged, open or closed, since a closed pull request can be reopened and merged, is read again from GET /repos/<owner>/<name>/pulls/<number> with the gh login the container used (~/.config/gh/hosts.yml in the home it mounted, which is the launching member's even on a shared agent login), and the new state is recorded. A recorded merge is never set back to open. target.repository narrows it to one repository; expected is not checked. A finished run with nothing recorded, and a queued, provisioning, paused or stopping run, still fails with -32002 because it has no live environment. The dashboard's publish dialog calls run.pr.status only for a live run, so a pull request merged after its run finished stays open here until a client makes that call.

Recorded from this version on

Older servers kept only each run's current status and no pull requests, so these measurements start when the server is upgraded:

  • run_state_transitions: one row per status, reason or activity change of a run, written in the same transaction as the change. Its activity column is the activity the run moved to, "" on rows written before it was added and on runs that are not running or needs-attention.
  • run_pull_requests: one row per pull request opened through run.pr.create, with the state the server last observed.
  • runs.swarm_run: set when a swarm creates the run, and at upgrade from the swarm records that exist then.

coverage.state_times_since and coverage.pull_requests_since are the oldest such rows, "" while there are none. Numbers before those times leave out what was not recorded. Deleting a run deletes its rows.