DOCS

Sleep, travel, and reconnects

Keep executor ownership separate from laptop connections, and adopt native sharing without interrupting active work.

About 7 min read · 5 sections

On this page

Laptop sleep and offline periods are expected. Recovery must not depend on changing power settings, keeping a desktop window open, or moving a workspace.

Client loses its connection
  → its transport detaches
  → the execution machine keeps its owned SDK and turn
  → a fresh client reads and joins that existing turn

The execution machine must itself be running to perform work. Sleep suspends its processes; a shutdown ends them. On reconnect, the saved chat remains the source of truth. A missing connection alone is not proof that a command failed, and the adapter must not resend turn/start or repeat a tool command to make the interface look active.

When a shared gateway executor loses only an adapter socket during an active turn, the adapter reattaches the chat and verifies the SDK's saved turn state. It never accepts a pending approval or repeats a caller action; unavailable or unverified state remains available through the ordinary explicit resume path.

Discover running chats after restarting the app

A fresh desktop connection discovers recent chats through thread/list. The adapter overlays the owning shared executor's current status on saved native notLoaded rows. An active status lets the desktop show its running indicator without starting or resuming a turn.

If the first list misses the executor or its metadata deadline, the adapter retries the listed candidates in the background. Once the engine answers, a verified thread/status/changed snapshot updates the running indicator without requiring another manual list refresh. Missing or unavailable engines do not prove a chat idle. Candidate counts, reads, and deadlines are bounded; real SDK status events take precedence over earlier metadata reads. The adapter remembers up to 512 listed candidates and rotates through four at a time on its five-second refresh. Successful snapshots can arrive while a slow neighbour is still pending, and an unsuccessful delivery remains eligible for retry. Large lists can therefore take several refreshes to recover fully.

The indicator and an attached transcript are separate. Opening a chat uses the normal resume path to join its stream. The gateway does not replay turn/start, commands, approvals, or completion notifications to make it look connected. This recovery covers rows the client has actually listed; a chat outside that list, a host with auto-connect disabled, or the desktop's catalog-history mode needs its own discovery path. It does not turn API-mode app-owned engines into durable service-owned executors.

Verify cold and delayed discovery with the isolated SDK fixture:

node scripts/e2e-chat-state.mjs --package /absolute/verified/package \
  --root /tmp/task-cold-reconnect --restart-discovery
node scripts/e2e-chat-state.mjs --package /absolute/verified/package \
  --root /tmp/task-delayed-reconnect --restart-discovery --delayed-discovery

The previous application client disconnects during synthetic inference. The new client starts with an ordinary state-database list; it must find the same running turn and later its real completion without starting inference again. The delayed case makes the first executor lookup unavailable and requires automatic status recovery without a second list. No account or paid inference is used.

Native and gateway ownership

Shared gateway executors already belong to the service. Shared native execution uses the app's original, signature-verified SDK and its existing native home. Disconnecting a client of either shared executor closes only that client's socket. A private executor belongs to its adapter and ends when that adapter ends, so native sharing must be active before a client disconnect can preserve that executor.

The native SDK must pass the real shared-native checks before its version is accepted. Those checks cover every frontend disappearing during inference and during a pending user approval. Reconnect must preserve the SDK process and turn identity, recover saved results, and never answer an approval automatically. See native connected machines for verified versions and verification for the fixture.

Managed private native launches whose configuration and home match an existing native relay service plan reserve that home before spawning the SDK. Private engines may share that reservation with compatible private engines, but a shared host cannot claim the home until those private writers have ended. A live or uncertain owner is never displaced.

Adopt sharing without closing active chats

Use gateway service shared-native --on-restart to stage native sharing for the existing managed service. The command records intent separately from the active service plan. It preserves gateway sharing, tunnels, components, authentication, native configuration, and running connections.

Only a subsequent primary LaunchAgent daemon start may apply that intent. Startup verifies the current plan, SDK, and writer census again. A changed plan, unsupported SDK, surviving old adapter, or unknown writer leaves the intent pending and keeps the current valid service plan authoritative. Startup changes only the native sharing flag and saves the original plan first. A manual daemon invocation cannot activate the intent.

Staged, installed, and active are separate states. Staging is not evidence that an existing private executor has become durable. After natural adoption, check gateway service status. For combined native and gateway sharing, also require gateway rollout --verify; its direct-tool canary requires gateway sharing.

Recovery boundaries

Explicit Stop, user-paused or blocked goals, approval requests, authentication and billing failures, and usage limits retain their meaning. Shared ownership does not clear them or create another inference turn.

The SDK handles provider stream reconnection. A request whose retries are exhausted remains a real failure, and queued work remains available for an explicit continuation. The gateway never replays uncertain side effects.

Unread dot after sleep

For Gateway-owned reporting that does not depend on receiving a live completion in the Codex window, use Activity. It distinguishes sampled SDK state from recorded outcomes and unknown execution. It does not change Codex's unread state or automatically continue failed work.

The desktop renderer sets its unread dot and completion alert only when it receives a live terminal event for a loaded thread. After sleep, it resumes the thread but can miss a terminal that the shared executor emitted while no app session was attached; its durable SDK read receipt does not repair that state.

The adapter keeps a private host-wide ledger at <state>/completion-replay.json. It records a followed turn separately for each device and marks it delivered when that device's adapter forwards its terminal frame. The ledger is retained for a future explicit client integration whose receipt and read semantics make replay safe. Stock adapters deliberately do not read it after thread/resume, query the SDK for an old turn, or synthesize a historical terminal. The stock renderer can apply such a terminal after a foreground read and mark the chat unread again; a resume does not prove focus or read intent. The record is shared by every local adapter process and is bounded to 512 device/thread entries and seven days.

device follows turn → app disconnects → SDK finishes → that device resumes
                                            ↓
                      SDK receipts and history remain authoritative

The ledger's bounded claim machinery remains available only to that future explicit integration. A normally connected app still receives its own live terminal event. Offline completion does not currently guarantee a stock renderer unread dot or alert, and the adapter never infers or changes read state from thread/resume.

An SSH proxy identifies its client by hashing its SSH client address with a local, random private salt, so the ledger receives only a stable local pseudonym instead of an address. It carries that pseudonym only in the private Unix WebSocket upgrade's X-Harness-Gateway-Device header; after the upgrade the listener validates one 32-hex value and synthesizes the internal gateway/client notification before decoded client frames. This leaves the native HTTP/WebSocket byte pipe intact. Sessions without that header are local; relay registration identifies only the host's remote-control route, not where an inbound app session originated. The pseudonym never establishes focus or read intent, and stock adapters do not use it for resume replay. If a future explicit client integration uses the ledger, an SSH address change would be a distinct device identity under that integration's contract.

See the isolated unread prototype for the renderer evidence and the separate client-side testing boundary.

Mosh's terminal reconnection does not provide these JSON-RPC ownership or approval guarantees. The existing SSH and connected-machine transports remain in place; the executor lifetime is independent of those connections.