Skip to content

fix(client): a cloud box idles when its user leaves, not when its last tab closes - #139

Merged
andrewcai8 merged 4 commits into
mainfrom
feat/box-idle-follows-user
Oct 1, 2026
Merged

andrewcai8 merged 4 commits into
mainfrom
feat/box-idle-follows-user

Conversation

@andrewcai8

Copy link
Copy Markdown
Owner

Problem

After #137, every client connected to a cloud box renewed the box's lease every 4 minutes for as long as the client stayed open. A forgotten tab, or a minimized desktop app, kept the box's Mac running around the clock, at about $3.60/hour on Namespace.

Fix

A box's idleness now follows its user, not an open connection.

  • Presence model. UserPresence in packages/client-runtime decides whether the user is here: the app is visible (in the foreground on mobile) and the user touched it within the last 60 minutes. Coming back on screen counts as input.
  • Signals per surface.
    • Web and desktop report document visibility, plus pointer, key, wheel, scroll, touch and focus input, sampled at most every 30 s.
    • Mobile reports app state, plus touches seen at the app root.
    • A client that provides no signals counts as always present.
  • Heartbeat. The registry renews a box's lease only while the user is here. Their return renews it at once, without waiting for the next 4-minute beat.
  • Wake. A paused box is woken only while the user is here. Their return retries every box that is down, which also clears the wake throttle, so a paused chat still wakes by itself, as in fix(client): a paused cloud chat wakes its box and reconnects with no clicks #137.
  • Long turns. The host never pauses a box whose agent is busy, whatever the clients do. The reaper reads the box's own thread state through the same remote access for E2B, Namespace devbox and Namespace instance boxes. A running or starting session, pending approvals and working background activity all count as busy, so the agent provider doesn't matter. Two limits that already existed still apply:
    • A box past its retention deadline is paused even if busy.
    • An instance-engine Mac is rotated near its Namespace lifetime deadline even if busy (8 minutes before the deadline). Its chat is saved and resumes on a new Mac.

Verification

  • New tests in packages/client-runtime. The two registry tests failed before the fix:
    • A box's lease is renewed only while its user is here: renewals stop while the app is hidden, resume at once on return, stop after an hour without input, and resume on the next input.
    • A box paused while its user is away is not woken, and its first wake comes when they return, after which it connects.
    • Presence itself: here while visible and touched within the hour, away while hidden whatever the input.
  • src/connection/ (145 tests), web src/connection/ and mobile src/connection pass.
  • scripts/cloud/verify-box-reconnect.ts --away-seconds 120 runs on E2B against a local manager. It hides the app as the box is paused, checks the box stays asleep, then comes back.
    • Result: not woken in 120 s away, then waking → connected 2.1 s after the user returned.
    • Without --away-seconds, the box reconnected 7.7 s after the pause, as before.

Opus 5.5 in Claude Code.

🤖 Generated with Claude Code

andrewcai8 and others added 3 commits October 1, 2026 14:19
…er is here

Adds the shared presence model (visible, and touched within the hour) with
its own tests. The registry tests fail today: a hidden or idle client keeps
renewing a box's lease every 4 minutes, and wakes a paused box at once.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Any connected client renewed its box's lease every 4 minutes for as long as
it stayed open, so a forgotten tab or a minimized desktop app kept a Mac
running around the clock.

Presence is now shared in client-runtime: the app is visible (in the
foreground on mobile) and the user touched it within the hour. Web and
desktop report document visibility and input; mobile reports app state and
touches at its root. The registry renews a box's lease and wakes a paused
box only while the user is here, and their return retries every box that is
down, so a paused chat still wakes by itself. The host never pauses a box
whose agent is busy, so long turns stay covered.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ed, then come back

--away-seconds checks nothing wakes the box while its user is away, and
that it reconnects by itself once they return.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@github-actions github-actions Bot added vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. size:L labels Oct 1, 2026
@github-actions

github-actions Bot commented Oct 1, 2026 •

Copy link
Copy Markdown

Thread transfer impact

✅ Thread transfer remains within every enforced ceiling.

Provider Metric Main baseline This PR Impact PR ceiling
Codex Total thread wire 13.5 KiB 13.5 KiB +22 B (+0.2%) 15.1 KiB ✅
Codex Thread snapshot wire 7.1 KiB 7.1 KiB −1 B (−0.0%) 7.3 KiB ✅
Codex Live turn WebSocket wire 6.4 KiB 6.5 KiB +23 B (+0.3%) 7.8 KiB ✅
Codex Live turn WebSocket decoded 56.2 KiB 56.2 KiB 0 B (0.0%) 66.4 KiB ✅
Codex Live turn messages 9 9 0 (0.0%) 21 ✅
Claude Total thread wire 13.5 KiB 13.5 KiB −5 B (−0.0%) 15.1 KiB ✅
Claude Thread snapshot wire 7.1 KiB 7.1 KiB +1 B (+0.0%) 7.3 KiB ✅
Claude Live turn WebSocket wire 6.4 KiB 6.4 KiB −6 B (−0.1%) 7.8 KiB ✅
Claude Live turn WebSocket decoded 57.0 KiB 57.0 KiB 0 B (0.0%) 66.4 KiB ✅
Claude Live turn messages 9 9 0 (0.0%) 21 ✅

Baseline: 05d0c23 · PR result: b5c1cc5 · Source CI: success

Scenario and decoded snapshot size

10 historical turns, 5 command tools per turn, 878.9 KiB retained MCP result per historical turn, and a 1.05 MiB retained result in the measured turn.

  • Codex decoded thread snapshot: 114.0 KiB
  • Claude decoded thread snapshot: 114.6 KiB

Updated in place by a trusted workflow. PR artifacts are strictly validated and never executed.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@andrewcai8

Copy link
Copy Markdown
Owner Author

Verdict: PASS. Reviewed by an agent that did not write this change. Presence (visible, plus input within 60 min) is derived once in client-runtime from per-surface signals. The heartbeat and wake run only while the user is present, and a return renews and wakes at once. Busy boxes stay awake through the reaper's busy check for every provider. The new registry tests failed first. The harness confirms a box doesn't wake while away and wakes 2.1 s after return. CI is green at b5c1cc5604.

@andrewcai8
andrewcai8 merged commit 8c435be into main Oct 1, 2026
24 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:L vouch:trusted PR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant