feat(desktop,web): a working agent's line counts how long its run has gone
What
While a coding agent works, its tab's last line now counts how long the run has been going: "Working… 1m 12s", ticking once a second. The Work tab's running turn counts the same way after its state ("Working 3m 4s").
The format is the compact one Codex and Cursor use: 42s, 1m 12s, 1h 3m.
Screenshots
Rendered from the real components in headless Chrome on Linux, light theme. This is not the Mac app.
| Agent tab | Work tab running turn |
|---|---|
![]() | ![]() |
How
-
Wire/service:
CodingAgentSummarygains an optionalrunStartedAt(epoch ms) for when the current run began. The service fills it from the newest turn's start, or from when the turn was queued if eve has not started it yet. Before the agent's first turn it uses the agent's own start. It is left out while a follow-up message is still waiting for its turn, because that run has not begun. An older service never sends it, and the desktop then shows plain "Working…".latestTurnsOfnow also readsqueued_atandstarted_at. -
The transcript page carries it too. The messages answer gains the same optional
runStartedAt. The desktop updates an agent's status and run start together (withAgentRun) from every page, Stop, and message answer. Without this, an open tab following a new turn would go on counting from the old run until the list was read again (Devin's finding). -
Desktop: one shared module,
planning/working-time.tsx, holds:workingTime, the formatterworkingClock, aStream.fromEffectSchedule(Clock.currentTimeMillis, Schedule.spaced(1s))- one atom on the renderer runtime that every count reads
WorkingTimeandWorkingLine
There is no raw
setInterval. The atom runs only while a count is mounted, so a run that ended, a tab that closed, a tab off screen (showngates the agent tab and the Work tab), or a line with no known start leaves nothing ticking. The count isrole="timer", so it is not read aloud every second inside the line's polite live region.
Skipped (agreed with the orchestrator)
- Subagent tabs (coding agent workers and Luke's planning workers) stay plain "Working…". No start time for a subagent reaches the window. Adding one means stamping the delegation's start in both the coder host and plan-work-store. They already use the shared
WorkingLine, so that change only needs to pass astartedAt. - The "+" list's agent rows carry no count, per Dean. Their status word and dot are left as on main, since the "+ picker polish" PR removes them.
- "Worked for 1m 18s" when a run finishes: no end time reaches the desktop.
WorkingTimeis where it would go.
Tests
apps/web/tests/coding-agents-app.test.ts(real store, PGlite): in both the list and the transcript page,runStartedAtuses the agent's start before any turn, then the turn's queueing, then its start. It is absent while a message waits for its turn, and is the new turn's start once that turn begins.apps/desktop/src/renderer/planning/working-time.test.ts(TestClock; the registry's runtime is seeded with the test's own context, so the mounted line counts on the clock the test moves):- the formatter across the minute and hour boundaries
- the clock ticks once a second
- a mounted line counts up across the minute
- a line with no start shows no count
- the agent transcript's count goes away once the run ends
- the Work tab counts only a running turn on a shown tab
use-coding-agents.test.tsanduse-agent-transcript.test.ts: a page's run start reaches the agent list, moves to a new turn's start, and is cleared while a message waits for its turn.- JSON Schema goldens for the seven coding agent wire schemas were re-recorded under
LUKE_UPDATE_FIXTURES=1. The only change is the new optionalrunStartedAtkey, which no model is shown.
Mac verification
CI could not verify the Mac UI, and ./scripts/verify.sh was not run: this was built on a Linux cloud VM, and CI builds nothing for the Mac (LUKE-159 is the manual Mac pass). ./scripts/check.sh passes. The web side needs a deploy before the count appears on coding agent tabs.
🤖 Generated with Claude Code

