Repository navigation
Description
Summary
After the laptop sleeps, an agent pane's in-feed work indicator can read Thinking · 20h03m. The counter is WorkIndicator painting Date.now() - turnStartedAt (useElapsedSeconds), and nothing in the session pipeline knows the machine slept. Reported by the user on 2026-09-12 against the Thinking counter specifically.
Expected
- A turn whose stream died while the machine was asleep does not keep showing a live counter after wake.
- A turn that genuinely continues after wake does not count the sleep as working time.
- A prompt sent after wake starts its own clock.
Actual (reproduced against the real adapters and reducers)
The reproduction drives the real ClaudeProxyAdapter / CodexResponsesAdapter and the renderer's real foldSemanticEvent + reduceStreamPhase, in the order useIpcSubscriptions runs them, with only the wall clock simulated. Timings come from a real recording (below).
| Scenario | Claude proxy | Codex proxy |
|---|---|---|
| Stream severed by sleep, provider never retries | Stuck Thinking forever on the pre-sleep clock (still 20h03m the next morning). No timer exists that can close the flow; the only stale-flow reap runs inside the next flow's first chunk. A prompt sent now "joins live work" (submitJoinsLiveWork), so it inherits the old clock. |
Shows the sleep until the watchdog's first tick after wake (≤10 s), then clears. |
| Client tool running across sleep, never returns | — | Stuck awaiting-tool on the pre-sleep clock: the watchdog seals the flow but the turn_completed bridge refuses to leave awaiting-tool. |
| Tool returns / turn continues after wake | Counter includes the whole sleep (turnStartedAt is only stamped while null). |
Counter includes the whole sleep. |
| Stream retried after wake | Correct: reap publishes idle, clock restarts. |
— |
| Turn finished before sleep | Correct: no counter. | — |
Mechanism, by file:
src/renderer/src/session-runtime/semantic/streamPhaseMachine.ts:63-72:turnStartedAtis set only while null and cleared only on anidlephase or a cleanturn_completed.packages/claude-code-headless/src/proxy/ClaudeProxyAdapter.ts:1009-1022: stale-flow reap only on a new flow's first chunk; with the proxy on, screen idle is suppressed.packages/codex-headless/src/proxy/CodexResponsesAdapter.ts:983-1021: watchdog seals the turn but publishes no idle phase.src/main/index.ts:672-673:powerMonitorsuspend/resume feeds only the performance monitor; no session code reacts to sleep.- All activity clocks use
Date.now(); the run journal'smonotonicMs(performance.now()) also advanced through sleep in real recordings, so it cannot detect sleep either.
Evidence (real, on disk)
- Turn across a confirmed clamshell sleep, 2026-08-31 → 09-01: Claude transcript (prompt 20:57:09 PDT, last assistant 22:45:08,
turn_duration39,888,429 ms = 11.08 h written after wake), the matching Codex rollout (turn_aborted: wall span 11.62 h vsduration_ms1.48 h), the Agent Code run journal (app alive through the sleep; wall and monotonic deltas equal), and/var/log/powermanagementsleep 23:43:59 → wake 08:01:40. - Unclean shutdown, 2026-08-29 → 08-30: 20.7 h gap in a Claude transcript with a
tool_usethat never got a result; next app run reportsapp.prior_unclean_shutdown. (A cold relaunch starts every runtime empty, so this shape cannot itself produce the counter; sleep with the app running is the reproduced cause.)
Impact
Every agent pane after a laptop sleep; Dispatch rows, Close Old Agents and Close Idle Orchestration Agents read the same phase, so a stuck phase also shows the agent as working and keeps it from being judged idle.
Environment
macOS (darwin 25.6), Agent Code at d12cd347, Claude and Codex with the proxy enabled.
Reproduction test and plan: branch feat/agent-working-time, src/renderer/src/session-runtime/semantic/turnClockAcrossSleep.test.ts and docs/decomposition/agent-working-time.md.
Activity
Juliusolsson05 commentedon Sep 13, 2026
Blocks #964 (agent working-hours analytics), which must record working time from the fixed substrate rather than the broken counter. Plan and decisions:
docs/decomposition/agent-working-time.mdonfeat/agent-working-time.docs(sessions): link the working-time plan to its PRs
chore(sessions): point the provider packages at their merged sleep fixes