v0.184.0Major release

Agents can now measure their own working time, not just their token spend

Until now, telemetry for an ordinary terminal session depended entirely on a provider lifecycle hook firing at the end. That works when the provider decides a session is over promptly. It works badly when it does not: a Codex thread can sit open for half an hour after the useful work stopped, and a killed process never fires its hook at all. The result was runs that arrived late, or never, and no honest measure of how long an agent actually worked.

Any MCP-connected agent can now bracket its own work explicitly. Call start_run before beginning a unit of work, heartbeat_run about every 60 seconds while working, and end_run when the work succeeds, fails, is cancelled, or needs a person. The server owns every timestamp, so the measurement is comparable across agents and machines rather than dependent on whoever had the most optimistic clock.

The design point is that a run is one active work period, not the lifetime of a chat. Ending with needs_input closes the run, so the time a human spends thinking is never counted as agent work. When the answer arrives, the agent opens a new run linked to the previous one: two intervals, with the wait belonging to neither. A run that stops heartbeating for ten minutes is recorded as interrupted, and its measured time is capped at the last confirmed heartbeat rather than at the moment cleanup happened to notice, so an abandoned run costs at most one stale window instead of accruing hours it never worked. That sweep runs at startup too, so runs abandoned while Pekan was closed are settled on the next launch.

Work with no task association is stored with no task id and shown as Unscoped work, with its own cost and active time. Pekan still does not create a synthetic board card to hold it, because inventing a task would corrupt task cost, flow, and review attribution for a card nobody asked for.

Replays are safe throughout. A stable idempotency key makes a repeated start_run resolve to the same run rather than a second one, and repeating end_run never extends elapsed time. The same key is how a provider hook that fires later enriches the run the lifecycle already created, adding the transcript's exact token usage without writing a duplicate row and without touching the measured time. record_run is unchanged and remains the right tool for hooks and integrations that already hold a finished-run payload.

Exactly one recorder owns each run. Pekan-launched Agent Actions stay desktop-owned, their prompts say so explicitly, and the server refuses lifecycle calls against a run that already has another owner. Usage numbers are still only ever recorded when a provider reported them: unknown stays unknown, never zero, never estimated.

The telemetry view and the CSV exports now report active working time and wall-clock elapsed as separate, separately-labelled figures, alongside how many runs are active, waiting on a person, or interrupted. Agent setup also separates a configured hook from an actual capture, naming the last successful capture and flagging reports queued but undelivered. Finally, the OpenCode guidance is corrected: only Pekan-launched OpenCode runs are captured automatically, since OpenCode has no project lifecycle hook, and a session you start yourself should use the run lifecycle instead.


← Browse all Pekan releases