fix(tmux-subagent): wait for session readiness before spawning attach pane
`opencode attach` was invoked inside a freshly-split tmux pane before the child session appeared in the opencode server's status map. The process exited immediately (session not found), tmux auto-closed the pane, and the subagent ran invisibly in the background — the race documented in #3505. Fix: call `waitForSessionReady` *before* `executeActions` in `session-created-handler.ts`, mirroring the guard already present in `TmuxSessionManager.ensureSessionReadyBeforeSpawn()`. If the session does not become attachable within the timeout the handler returns early without spawning a pane at all, eliminating the transient-pane and silent-close failure modes. The now-unreachable post-spawn readiness-check / pane-close cleanup branch is removed. Adds a regression test suite (session-created-handler.test.ts) covering: - not-ready session → no pane spawned, no polling started - duplicate session.created → idempotent - non session.created event type → no action - already-tracked session → idempotent Closes #3505 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -102,6 +102,19 @@ export async function handleSessionCreated(
|
||||
return
|
||||
}
|
||||
|
||||
// Wait for the child session to be registered in the opencode server's status
|
||||
// map BEFORE spawning the tmux pane. If we spawn first, `opencode attach`
|
||||
// exits immediately (session not yet visible), tmux auto-closes the pane, and
|
||||
// the subagent runs invisibly in the background — the bug described in #3505.
|
||||
const sessionReady = await deps.waitForSessionReady(sessionId)
|
||||
if (!sessionReady) {
|
||||
log("[tmux-session-manager] session readiness failed before spawn", {
|
||||
sessionId,
|
||||
stage: "session.created",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
const result = await executeActions(decision.actions, {
|
||||
config: deps.tmuxConfig,
|
||||
directory: deps.directory,
|
||||
@@ -137,26 +150,6 @@ export async function handleSessionCreated(
|
||||
return
|
||||
}
|
||||
|
||||
const sessionReady = await deps.waitForSessionReady(sessionId)
|
||||
if (!sessionReady) {
|
||||
log("[tmux-session-manager] session not ready after timeout, closing spawned pane", {
|
||||
sessionId,
|
||||
paneId: result.spawnedPaneId,
|
||||
})
|
||||
|
||||
await executeActions(
|
||||
[{ type: "close", paneId: result.spawnedPaneId, sessionId }],
|
||||
{
|
||||
config: deps.tmuxConfig,
|
||||
directory: deps.directory,
|
||||
serverUrl: deps.serverUrl,
|
||||
windowState: state,
|
||||
},
|
||||
)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
deps.sessions.set(
|
||||
sessionId,
|
||||
createTrackedSession({
|
||||
|
||||
Reference in New Issue
Block a user