Repository navigation
fix(opencode): show opencode run status when it starts in a hidden tab - #24238
brennanb2025 wants to merge 5 commits into
Conversation
…tab or survives a restart An OpenCode 2 `opencode run` typed into a pane showed no status at all when the command started while the pane's tab was hidden (sent by the CLI or an agent to a background tab, or typed ahead before a tab switch). While a tab is hidden the terminal daemon owns the OSC 133 scan and relayed only the command finish, so main's run producer never saw the start. A run already in flight when Orca restarted had the same gap: its start was printed before main was listening, so it never showed Working and its Done never came. - The daemon's background scanner now relays `command-started`, and main routes it to the existing run producer. Daemon protocol goes to v39 so new panes get a daemon that relays it; panes on a daemon preserved across the update keep the old behaviour until they close. - On a reattach or adoption commit, the producer checks the pane's foreground once (no retry ladder) unless it already tracks the pane's current command.
…mand is running The reattach check armed Working from the foreground command line alone. In a pane whose shell prints no OSC 133 markers (cmd.exe, sh, dash, ksh, `exec sh`) nothing ever ends that Working, and the check ran on every remount, not just after a restart. It also never ran when a phone or paired client was the first to attach a restored terminal. The daemon already scans every session's OSC 133 markers. Its reply to an attach of an existing session now carries that session's state (running / at-prompt / unmarked), read atomically with the attach, on protocol v39. Main checks the foreground only when the daemon says a command is running; older daemons omit the field and get no check. Both paths that attach a surviving session (a pane mount's spawn commit and a remote viewer's provider attach) report through one runtime method.
This PR now fixes only a run that starts while its tab is hidden: the daemon relays the command-start fact, protocol v39. Re-deriving a run in flight when Orca reattaches a surviving pane needs a liveness check behind its arm, so it moves to its own PR (branch brennanb2025/opencode-run-reattach-liveness). Restores the reattach files to their main versions and drops the reattach tests; the relay, its tests and the comment fixes stay.
Review status: ready (hidden-tab start fix only)The problem. Since #23722, What changes for you. A run that starts in a hidden tab now shows Working while it runs, on the right pane. When it exits back to the shell, the row clears, the same as for a visible tab. Visible tabs are unchanged. A run in flight across an Orca restart behaves as on main today. Done on exit. On main today, the run detector also posts Done when What changed in review:
Problems found that this PR does not cause (both exist on main today):
Deferred (not fixed here):
Merge note. This bumps the daemon protocol to 39. Other open PRs touch the protocol version too. Check origin/main is still at 38 right before merging; if 39 is taken, move this to the next free number. Verified:
Not verified: Windows, Linux, SSH and WSL panes (SSH and WSL have no Screenshots (remote Mac, real OpenCode 2.0.21; the sidebar's agent rows are expanded. The "Done - OpenCode" row in some shots belongs to an earlier, visible run in Terminal 1. Posting Done on exit is today's behaviour, which a separate PR removes.) Before, origin/main: After, this PR: the same send to hidden Terminal 2 shows Working while it runs. A new terminal after the update, hidden while its run starts: Working. A terminal opened before the update stays on the old daemon and stays silent for a hidden start, as described. Nested |
|
There was a problem hiding this comment.
ℹ️ Minor suggestions only — the implementation is correct; two documentation nits below.
Reviewed changes
- Relay emits the command-start fact —
daemon-background-transient-facts.tswiresonCommandStartedinto the existing shared OSC-133 scanner and emits{ kind: 'command-started' }in byte order. No new scanner, no per-chunk cost. - Fact union widened —
command-startedadded toDaemonTransientFact(daemon-stream-events.ts) andPtyTransientFact(pty-provider-events.ts); the adapter passes it through unchanged. - Main handles it —
orca-runtime-emit-daemon-pty-transient-fact.tsroutescommand-startedtoopenCodeRunLifetime.onCommandStarted, mirroring the visible-byte path inorca-runtime-get-unpersisted-tracked-title-for-pty.ts:146(no side-effect fact, same as visible behavior). - Protocol bump 38 → 39 —
daemon-protocol-version.ts,daemon-protocol-version.test.ts,daemon-foreground-confirmation-protocol.test.ts, and the local-build compatibility contract (.ts+.json) all updated consistently. - Tests — relay unit tests (byte order, split chunks, handoff split, non-backgrounded session) and runtime tests (hidden-tab Working→Done, no double-arm while delegated, Done from main's scanner after un-hide). I ran the four touched/related suites (43 tests) and
pnpm tc:node; both pass.
ℹ️ PR title claims a restart fix that was moved out of this PR
The head commit 5252676 reverts the reattach re-derivation ("survives a restart") into a separate branch, and the body correctly states that a run in flight across a restart behaves as on main and is a follow-up. The title still advertises "or survives a restart", which no longer matches the shipped scope.
Technical details
# Title overstates scope
## Affected sites
- PR title (#24238) — "show opencode run status when it starts in a hidden tab or survives a restart"
- Author description § Known gaps / TEMPORARY — restart gives no status, deferred to a follow-up PR
## Required outcome
- Title reflects only the hidden-tab fix, or the restart re-derivation is restored.
## Suggested approach (optional)
- Edit the title to drop "or survives a restart".ℹ️ Transient-fact contract doc omits the new command-started fact
pty-provider-contract.ts:175 enumerates the transient facts a thinning transport may interleave (bell/command-finished/pr-link/2031). This PR adds a fifth (command-started), so the roster is now incomplete.
Technical details
# Stale fact roster in provider contract doc
## Affected sites
- `src/main/providers/pty-provider-contract.ts:175` — "(bell/command-finished/pr-link/2031)" omits `command-started`
## Required outcome
- Doc comment lists every `PtyTransientFact` kind the transport may emit.
## Suggested approach (optional)
- Add `command-started` to the parenthesised roster.DeepSeek Flash (free via Pullfrog for OSS) | 𝕏
|
Navigate logical layers of code changes, visualize relationships, and explore their blast radius. No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Advanced Run ID: 📒 Files selected for processing (1)
Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 2 remain after this review. 📝 WalkthroughWalkthroughThe daemon now emits a Priority: ⬇️ Low Merge Risk: ⚪ Minimal · up to Hidden-tab OpenCode runs can show Working when the v39 daemon relays a command-start marker. The supplied context identifies no new material failure, so the change is ready for normal merge checks. Architecture SummaryArchitecture risk: 🔵 Low · up to The change affects 1 system. Changed systems: Architecture concerns Review detailsSystems and components
Before / after behavior
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches 💡 1📝 Generate docstrings 💡
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
On hold, back to draft: Orca should not special-case one agent's one-shot command. Status should come only from the agent's own hooks, plus the normal rule that clears a row when the process exits. The |
|
Closing: Orca should not special-case one agent's one-shot command, so the |







ELI5
When you run
opencode run "<prompt>"in an Orca terminal, the pane should show Working while it runs. When it exits back to the shell, the row clears. #23722 added that, but Working only appeared if the tab was visible at the moment the command started. If the tab was hidden at that moment, the pane showed nothing for the whole run. That happens when a script or another agent sends the command to a background tab, or when you type ahead and switch tabs before it starts. Now those runs show Working while they run, and the row clears when they exit.What it fixes and scope
opencode runthat starts while its tab is hidden. This is a follow-up to fix(opencode): report OpenCode 2 status from each pane's own TUI #23722, which addedopencode runstatus and labelled the hidden-tab case temporary.opencode rundetection is not enabled yet; this PR doesn't change that.What Changed
Before (user): send
opencode run "…"to a tab you're not looking at, or type it ahead and switch tabs before it starts. That pane's sidebar row never shows Working.After (user): the pane shows Working while the command runs, whether or not its tab is visible. When the command exits back to the shell, the row clears, the same as for a visible tab.
What happens on exit today. On main, the run detector also posts Done when
opencode runexits. That isn't the intended behaviour: when a command exits back to the shell, the pane should show nothing. A separate PR stops the detector from posting Done on exit. This PR adds no Done of its own; it only lets the existing detector see a hidden-tab start.Mechanism, before:
opencode run. When the shell reports "a command started" (a standard terminal marker, OSC 133;C), main checks the pane's foreground process. If it isopencode run, main marks the pane Working. The shell's "command finished" marker (OSC 133;D) ends it.Mechanism, after:
daemon-background-transient-facts.ts). It uses the same shared scanner main uses, and main stops its own copy at the same handoff point, so each marker is still acted on exactly once.orca-runtime-emit-daemon-pty-transient-fact.ts). The detector itself is unchanged.Why
How this compares with the reference mechanisms
opencode runstatus, as before.opencode runstatus.Known gaps (exist on main today; not caused by this PR)
opencode runexits, instead of the row clearing. A separate PR removes that.opencode runinside a nested shell can stay Working./bin/shand typeopencode runwithin a few seconds. The pane can show Working until you leave that nested shell, even after the run has ended.Linked Issue
Follow-up to Linear STA-8629 (OpenCode2 shows done while still working) and #23722.
Visual Proof
Screenshots (remote Mac, real OpenCode 2.0.21; the sidebar's agent rows are expanded. The "Done - OpenCode" row in some shots belongs to an earlier, visible run in Terminal 1. Posting Done on exit is today's behaviour, which a separate PR removes.)
Before, origin/main:

opencode runsent to hidden Terminal 2 while it runs. There is no row for Terminal 2.After, this PR: the same send to hidden Terminal 2 shows Working while it runs.

A new terminal after the update, hidden while its run starts: Working.

A terminal opened before the update stays on the old daemon and stays silent for a hidden start, as described.

Nested


/bin/sh, 2 minutes after the run ended: still Working on origin/main and on this PR alike. This exists on main today.Testing
Manually tested on a remote macOS machine with real OpenCode 2, against origin/main (see Visual Proof)
Automated tests added/updated, or explained why not below
New tests:
Ablations: reverting the daemon relay, main's handling of the fact, or main's hidden-scan guard each makes its tests fail (3, 3 and 1 tests).
25 related daemon, IPC and runtime test files pass (1707 tests, 1 skipped), including the
orca-runtime.test.tsharness and all daemon-pty-adapter suites.pnpm tc:nodepasses.Review
Agent skill upstream boundary
docs/reference/agent-skill-sharing-upstream-boundary.mdand copies or mechanically translates no upstream skill-installer source, tests, fixtures, registry entries, path tables, comments, or documentation.Notes
Checklist
N/Awith reasonpnpm lint,pnpm typecheck,pnpm test, andpnpm buildpass (or CI will cover; local preferred)