Repository navigation
Description
Before submitting
- I searched existing issues and did not find a duplicate.
- I included enough detail to reproduce or investigate the problem.
Area
Server (remote environment over SSH), orchestration / delegated subagents
Steps to reproduce
- Connect the desktop app to a remote environment over SSH (server launched via
~/.t3/ssh-launch/<id>/run-t3.sh, which ends inexec "$T3_RUNTIME_DIR/t3" serve --host 127.0.0.1 --port 3773 …). - In a thread (Claude, Opus 5.5, full-access) have the agent call
delegate_task(modeasync) to start a long-running Codex child (gpt-6.1-sol, full-access). - While the child is working, disconnect / close the local machine.
- Reconnect later.
Expected behavior
The remote server and its agents (parent and delegated children) keep running on the remote host after the local client disconnects — the point of a remote environment is that work continues without the laptop.
Actual behavior
- The remote
t3 serveprocess exits together with the SSH session (~2 s before sshd logs the client disconnect). Its children die with it: the parent Claude session and the sharedcodex app-serverthat runs the delegated child. - The Codex child stops mid-turn. The provider event log ends with an ordinary
item/completed(agentMessage) — no error, no interrupt event. - After reconnecting a new server starts, and
task_statusreportsstatus: "cancelled",workState: "result_available", withsummary= the child's last progress message and no error/reason. Neither the parent agent nor the user can tell this was a server shutdown rather than an intentional cancel.
Process tree on the remote host (the local machine only hosts the UI):
t3 serve (SSH-launched)
├─ claude … (parent agent)
└─ codex app-server (shared, runs delegated Codex children)
└─ codex-code-mode
Impact
Long-running delegated work on a remote box is silently lost whenever the laptop sleeps or disconnects, and it is reported as a user/agent cancel. Workaround today is to never disconnect.
Version or commit
Server 0.0.46-nightly.20261004.2644; codex-cli 0.160.0.
Environment
Remote: Ubuntu 24.04 (noble) linux-x64, server launched by the desktop app over SSH. Parent provider Claude (Opus 5.5); child provider codex (gpt-6.1-sol), shared app-server session provider-session:provider-instance:codex:shared.
Logs or stack traces
Timeline (UTC, 2026-10-04):
- 11:09:46.802 — delegated child run started.
- 11:21:04.285 — last Codex provider event (
item/completed, agentMessage). - 11:21:40.09 — server trace: root spans exit
Interrupted(InterruptError: All fibers interrupted without errorinThreadSettlementServiceV2.sweep,ThreadPullRequestServiceV2.synchronize);UPDATE orchestration_v2_effect_outbox SET status = 'cancelled' …. - 11:21:40.112 — child run
completedAt, statuscancelled. - 11:21:42 / 11:21:45 — sshd:
Received disconnect … disconnected by userfor the two SSH sessions. - 11:39:10 — new
t3 servestarted on reconnect (ssh-launch pid/port/log rewritten).
Workaround
Keep the local machine connected; make children commit intermediate work so a disconnect loses less.
Suggestions:
- Detach the remote server from the SSH session lifetime (e.g.
setsid/systemd user unit), or an opt-in "keep running after disconnect"; at minimum document that remote work stops on disconnect. - If the server stops, record interrupted runs with a distinct reason (e.g.
interrupted: server_shutdown) instead of plaincancelled, and surface it intask_status. - Optionally offer to resume/re-queue interrupted delegated tasks after restart.
Activity
juliusmarminge commentedon Oct 4, 2026
Note
Grok responding on behalf of Julius.
Triage
Thanks for the careful report, @mb-hermes-bot. The sshd timestamps next to the server log made the order of events easy to follow.
What I found
I checked current
main(5a96895a85). There are two separate behaviors here, and the nightly you're on has both.1. Quitting the desktop app stops the SSH-managed server. Your timeline fits a desktop shutdown, not a network drop or a SIGHUP from the launch session. The server had already outlived the launch SSH because it was started with
nohup. It was interrupted at 11:21:40, and sshd loggeddisconnected by userfor the two sessions only afterward, at 11:21:42 and 11:21:45.When the desktop app quits,
DesktopApp's scope (apps/desktop/src/app/DesktopApp.ts) closesSshEnvironmentManager, and the tunnel finalizer inpackages/ssh/src/tunnel.tscallsstopRemoteServer(). The stop script SIGTERMs the recorded PID unless~/.t3/ssh-launch/<state>/managedisexternal. An explicit Disconnect and turning the environment off go through the same stop, while a server the launcher found already running is left alone.This is the same lifecycle as #10934, which was closed as completed on 2026-10-02 after #9843 made the finalizer stop the real
t3 serveinstead of an npm wrapper. That finalizer call is still there. #10951 tried keeping the remote server running across quit and was closed unmerged. Right now, quit and window-close stop a managed server, while the user docs only mention removing the connection.Continue threads after restartsin Settings doesn't keep this work alive. It's off by default, and even when it's on, shutdown cancels the live provider session first, so any continuation is a new turn.Workaround: on the remote host, install the background service (
t3 service install) and reconnect, so the desktop adopts that process asexternaland the stop script leaves it running. You'll also need user lingering enabled, or the user service stops when you log out (see the background-service docs).2. A shutdown is recorded as a plain cancel. On graceful SIGTERM,
ProviderRuntimeRecoveryService.reconcile("shutdown")(apps/server/src/serverRuntimeStartup.ts) marks the in-flight runstatus: "cancelled"and only setscompletedAt. The messageCancelled because the server shut down before the provider work completed.is stored on the effect outbox row, not on the run, andtask_statushas no reason field. For a run that didn't fail,subagentResultForRunuses the last assistant message assummary, which is why you saw the child's last progress update withworkState: "result_available". An intentionaltask_cancelends in the same status. Aninterruptedstatus exists, but shutdown doesn't use it. So after you reconnect, neither the parent nortask_statuscan tell a server shutdown apart from a cancel.Likely fix area
packages/ssh/src/tunnel.tsand theSshEnvironmentManagerteardown, depending on how quit and window-close should treat a managed remote server (the question [Bug]: Restarting the desktop client breaks running remote Codex threads over SSH #10934 left open).ProviderRuntimeRecoveryService.reconcile("shutdown")andsubagentResultForRun. Options include recording shutdown asinterrupted, or carrying a cancel reason through to the run andtask_status.A maintainer will decide on the fix direction.
DigitalWestern commentedon Oct 6, 2026
Same symptom here, plus two findings not covered above: the stop also fires on wake from sleep (not only on quit), and the documented workaround (
t3 service install) did not help on its own, even with the service already installed and lingering enabled.Environment
0.0.46-nightly.20261005.2689(later2702), macOS arm64t3code.serviceinstalled,Linger=yes, running0.0.46-nightly.20261004.26571. Wake from sleep stops the remote server too
When the Mac wakes, the stale tunnel fails the 2 s readiness probe in
ensureTunnelEntry. That path callscloseTunnelEntry, which runs the entry finalizer, which callsstopRemoteServer(). No quit is involved.From the desktop trace (span chain
desktop.ipc.sshEnvironment.ensureEnvironment > ssh/tunnel.ensureEnvironment > ssh/tunnel.ensureTunnelEntry > ssh/tunnel.closeTunnelEntry > ssh/tunnel.stopRemoteServer), on 2026-10-05 in UTC:stopRemoteServer)launchOrReuseRemoteServer)There were at least 11 stale-tunnel stops that day (sleep/wake or network blips). The user returned to an interrupted turn and had to send "continue". The agent then reported that "the T3 restart killed" its background watcher.
Code at
v0.0.46-nightly.20261005.2689(3e6b45028cee):packages/ssh/src/tunnel.ts:1609-1631: stale-entry branch callscloseTunnelEntry(entry)packages/ssh/src/tunnel.ts:1541-1580: entry finalizer callsstopRemoteServer()whenever the entry is still the registered oneSame logic on current
main.2. Why the service workaround did not take effect
The service was already running on
127.0.0.1:3773. The desktop still ran its own managed server on:3774, with the same--base-dir ~/.t3(so the same database), and stopped that one on every wake and quit. What I found:~/.t3/userdata/server-runtime.json(tunnel.ts:627-662). Everyt3 servewrites that file on startup. Once a managed server was running, the file pointed at:3774, so the service was never found.server-runtime.jsonon shutdown, even when the file describes a different server (apps/server/src/server.ts:722-728, unconditionalclearPersistedServerRuntimeState). I pointed the file at the service, then stopped the managed server. Seconds later the desktop launched a fresh managed server and the file described it again. That matches the dying server deleting my edit.systemctl --user restart t3code.service. The desktop reconnected during the ~1 s the service was down, found nothing on:3773, and launched a new managed server on:3774.Together, a managed server, once started, keeps replacing itself, and the installed service stays unused.
Recovery that worked
server-runtime.json).server-runtime.jsonwith the service's pid, port 3773, and originhttp://127.0.0.1:3773.~/.t3/ssh-launch/<key>/managedtoexternalandportto3773, and removepid.After that the desktop adopted the service as external. A lid-close test kept the turn running.
Possible fix directions
server-runtime.jsonon shutdown if itspidmatches the exiting process.stopRemoteServer()for an explicit Disconnect.t3code.serviceover launching a managed server, for example by checking the unit beforepick_port.Triage by Claude Opus 5.5 (Claude Code) via
t3 triage, reviewed by the reporter.t3dotgg commentedon Oct 21, 2026
Note
🤖 Claude Opus 5.5 responding on behalf of Theo
Thanks for the report. One part of this is fixed on main in #19432. Please test on the nightly built from main on 2026-10-21, or on the next stable release. One part is still open: desktop quit still stops the managed remote server; shutdown is still reported as cancelled. If that part still matters to you, please open a new issue for that part only.