Skip to content

bug(session): prompts to restored sessions after app restart are rejected never-owned — no wake on the composer send path #706

Closed

Description

@Juliusolsson05

Expected behavior

After an app restart, a restored pane (tile leaf or Grid Dispatch lane selection) that shows a composer accepts a prompt: if the backend for that session id is not alive in this app run, the send path wakes/recovers it first (same-SessionId recovery, as agent-index navigation and #691's lane-placement path already do) and then delivers.

Actual behavior

Typing into a restored pane and pressing Enter fails with Cannot deliver prompt: <id> is not a live agent session, journaled by main as delivery.reject { code: not-ready, registryHit: false, reason: "never-owned" } — an id main has never spawned in this run. The pane looks perfectly live; the user has no reason to know the backend was never started.

Evidence

Debug bundle ~/.config/agent-code/debug-bundles/manual/2026-08-30T23-51-06-471-9bd68e14 (build b4d64c1c, app run started 23:41:21 after a prior force-quit/power-loss shutdown):

  • 23:42:57.179 delivery.reject sessionId=82fa0abe… reason=never-owned
  • 23:45:21.812 delivery.reject sessionId=39e22d50… reason=never-owned

39e22d50… is verifiably a session id from the previous app run — it is the subject of the earlier bundle 2026-08-30T22-36-11-605-39e22d50 (the PR #702 acceptance-timeout investigation). Its pane was restored at rehydrate, the user typed into it, and main correctly reported it had never owned that id this run.

Why the existing guards don't cover this

  • rehydrate.ts deliberately does not respawn detached (hibernated) sessions — the wake is supposed to happen at first use.
  • PR fix(dispatch): wake a hibernated agent before placing it in a Grid Dispatch lane #691 (selectTiledLaneSession) wakes on every lane placement gesture (index click, strip click, ⌥↑/↓, ⌘N). But a restored workspace re-establishes lane selections without any placement gesture, so the wake never runs; the first gesture is typing into the composer, which goes straight to deliverPromptToAgent.
  • PR fix(dispatch): wake a hibernated agent before placing it in a Grid Dispatch lane #691 explicitly deferred the sibling gap: "grid-placed-but-dead sessions still rely on the pane's own Retry" — same class, reachable from classic tiles too, but most visible in Grid Dispatch because lanes surface many hibernated agents after a restart.
  • The reject result is already shaped for recovery (code: not-ready, retrySafe: true, disposition: session-unusable) but the composer send path does not act on it.

Fix direction

Make the composer send path liveness-aware, mirroring #691's contract at the delivery boundary instead of only at placement: on submit, if the runtime/backing session is not live this run (or when delivery rejects with not-ready + never-owned/entry-lost-after-owned), run ensureSessionLive(sessionId, 'composer.send') (same-SessionId recover, joining any in-flight wake) and retry the delivery once. Refuse with the existing toast if the wake fails. This removes the last "typing lands on a dead backend" path regardless of which surface restored the session.

Notes

Environment

agent-code b4d64c1c production build, 2026-08-30, macOS; two windows open (multi-window #692 active) — ownership routing not implicated: the registry is process-wide and the reject is registry-membership, not window routing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions