Skip to content

Make durable-terminal exit observable by its owning thread and retain bounded post-exit output #2520

Description

@ryanbbrown

The workflow

  1. A child agent starts a thread-scoped durable terminal for work that will outlive its current turn.
  2. The agent becomes idle while the terminal continues.
  3. When the process exits, the owning thread should get one opt-in completion delivery so the agent can inspect the result and continue.
  4. The agent should be able to read bounded final output after exit.

The concrete run was terminal term_ewu75yjnwb, owned by child thread thr_ky9svesucn in environment env_yvc7c9ytrp. The process exited with code 2. The child thread stayed idle and did not resume.

What happens today

Observed with bb desktop 0.39.0 on macOS 15.7.7 arm64:

$ bb terminal show term_ewu75yjnwb --json
{
  "id": "term_ewu75yjnwb",
  "threadId": "thr_ky9svesucn",
  "environmentId": "env_yvc7c9ytrp",
  "status": "exited",
  "exitCode": 2,
  "closeReason": "process-exit"
}

$ bb terminal output term_ewu75yjnwb --json
Error: HTTP 409: Terminal output is unavailable because the session is not running

I also audited release 0.40.0 and current main at 31a190ddb5f0ffb86cb8dcd80482497663de2685. The behavior remains:

  • The daemon flushes final bytes to clients that are already attached, deletes the in-memory terminal session and scrollback, then sends terminal.exited (terminal manager).
  • The output route rejects non-running sessions with HTTP 409 (terminal lifecycle).
  • A thread-scoped terminal change emits only terminals-changed, which is a client cache invalidation. It does not append a thread event or send a provider turn (terminal notification).
  • bb terminal wait --exit polls terminal metadata while the caller remains alive. Its JSON result does not include exit code or close reason (CLI wait).
  • Plugin events expose thread lifecycle only; there is no terminal completion event or hook (plugin event contract).

What you would expect

Provide an opt-in terminal completion contract that still works after the creating turn becomes idle.

Acceptance criteria:

  • Final output is committed before terminal status becomes observably exited.
  • bb terminal output <id>, sdk.terminals.output, and the HTTP output route can return bounded retained output for an exited terminal. The byte limit, retention period, nextSeq, and truncated behavior are documented.
  • bb terminal wait --exit --json returns exit code, close reason, and final sequence, not only matched: "exit".
  • Output waits distinguish timeout from unmatched process exit and preserve match state across polling boundaries.
  • A supported terminal lifecycle subscription or plugin event carries terminal ID, scope, status, exit code, and close reason for thread, environment, and host scopes.
  • A thread-scoped terminal can opt in to one exact-owner completion delivery. An idle owner starts one turn; an active owner gets one queued delivery. Delivery is durably reserved and deduplicated.
  • The UI shows unseen terminal completion or failure attention until observed. Non-zero exit is distinguishable from successful exit.
  • Recent exited terminals and their metadata are discoverable without already knowing the terminal ID.
  • Tests cover exit code 0 and non-zero exit, final-output ordering, truncation, daemon disconnect or restart, idle and active owners, duplicate exit events, and environment or host scopes.
  • Increment HOST_DAEMON_PROTOCOL_VERSION if the server-to-daemon contract changes.

Context and alternatives

Current workarounds write output and status to durable files, then either keep the agent turn blocked on bb terminal wait --exit, or run a wrapper or polling service that calls bb thread tell. Those workarounds must provide their own output retention, exact-owner routing, retry policy, and deduplication.

Related work covers separate parts of the need:

  • PR #273 added thread-scoped terminals and explicitly notes that exited-terminal output remains unavailable.
  • Issue #1075 and PR #1076 guarantee final-output ordering for attached clients, but not post-exit retention or owner wake.
  • Issue #1681 implements wake projection for Pi extension-managed process notifications, not native bb terminal sessions.
  • Issue #2461 requests an analogous agent-wake path for ACP background work, not terminals.

I searched open and closed issues, pull requests, Discussions, releases, and current code for native terminal completion notification, callback or hook, owner wake, exit attention, and post-exit scrollback. I found no duplicate that covers this workflow. Investigation thread: thr_kyc9qbivsh.

Suggested priority: normal feature work with a confirmed failure-mode case. There is a manual workaround, but a non-zero result can remain unnoticed and its terminal output is lost unless the caller separately persists it.

AGENT GENERATED

Metadata

Metadata

Assignees

No one assigned

    Labels

    clibb CLIthreadsTurns, timeline, messaging, forksworkspacesWorktrees, environments, git, shells

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions