Repository navigation
Description
Summary
Session tab shows no spinner while a background shell in that session is still running, even though the shell row itself shows a spinner and a Background badge.
Environment
- opencode version: 2.1.3
- OS: Darwin MacBook-Pro-2.local 27.2.0 Darwin Kernel Version 27.2.0: Wed Oct 14 23:46:57 PDT 2026; root:xnu-13432.41.4~25/RELEASE_ARM64_T8103 arm64
- Terminal: TERM=xterm-256color, TERM_PROGRAM=Apple_Terminal, COLORTERM=truecolor (macOS Terminal.app, see screenshot)
- Shell: /bin/bash
- Install/channel: Homebrew
anomalyco/tap/opencode-v2, latest - Active plugins: None found in config (
~/.config/opencode/opencode.jsonhas only$schema,autoupdate,permission,agent.build.prompt)
Reproduction
- Open
opencodeTUI with tabs enabled and open two sessions (two tabs). - In one session, run a long shell command in the background, e.g. via shell tool with
run_in_background=true, or via the wrapper from the screenshot:
SKILL=~/.config/opencode/skills/bg-task; "$SKILL/task_run.sh" run --title "test" -- python3 -c "import time; time.sleep(60)" & - Wait for the session execution to go idle while the shell keeps running. Observe the session transcript row vs. the tab strip at the top.
Expected Behavior
The tab for the session with the still-running background shell should keep animating (spinner / working state), same as the shell row does, so background work is visible while looking at another tab.
Actual Behavior
The shell row in the transcript keeps animating and shows a Background badge, but the corresponding session tab shows no spinner and looks idle. In the screenshot: bottom row shows : spinner + command + Background, top tab Downloading YouTube Music pl... shows no animation.
Additional Context
- Frequency: consistently reproducible.
- Workaround tried: none; have to switch back into the session to see the shell row spinner.
- Screenshot: macOS Terminal window
opencode — 315x69, top red box = tab strip without animation, bottom red box =:spinner +SKILL=... task_run.sh run ...+Backgroundbadge. Text below screenshot says the tab position was just corrected, so annotation arrows are accurate.
Root-cause hypothesis from dev source inspection:
packages/tui/src/routes/session/index.tsxShellDisplay:isRunning = props.status === "running" || backgroundRunning()wherebackgroundRunning = Boolean(id && data.shell.get(id)). So a backgrounded shell (status === "completed" && metadata.status === "running") still renders<Spinner>and<StatusBadge>Background</StatusBadge>.packages/tui/src/context/session-tabs.tsxstatus():busy = members.some((id) => data.session.status(id) === "running" || data.session.pending.list(id).some((item) => item.type !== "synthetic")). This only looks at sessionactive+ inboxpending, never atdata.shell.listBySession(id).- Once the execution goes idle after backgrounding,
busybecomesfalse, sopackages/tui/src/component/session-tabs.tsxTabIndicatortakes the non-runs()path and shows no spinner, while the shell row still spins.
Suggested fix: include running shells in the tab busy check, e.g. data.shell.listBySession(id).length > 0 for each family member (same data.shell store already used by ShellDisplay). Background subagents are already covered via family active, only background shells are missed. Same status() also feeds createProgramStatus, so terminal title / program status would be fixed together.
Activity
opencodejr commentedon Nov 7, 2026
Thanks for the detailed report and the root-cause analysis, @naihongzhou1029!
I think this is a duplicate of #51810. That issue covers TUI activity indicators ignoring running background shells once the turn has ended. It was filed for the
/sessionslist, but the reply there notes that the session tabs' busy indicator has the same gap:status()incontext/session-tabs.tsxchecks session status and pending items but neverdata.shell, which matches what you found.Possible duplicates and related issues, ordered by likelihood:
status().Your suggested fix (counting
data.shell.listBySession(id)for each family member) would be a useful addition on #51810.The bot will close this issue as a duplicate in 1 day unless you reply explaining how your problem is different.
naihongzhou1029 commentedon Nov 7, 2026
Illustration note (reporter confirmed screenshot may be used):
Screenshot shows macOS Terminal running
opencodeTUI:Downloading YouTube Music pl...andGoogle OAuth login error in g +— no spinner/activity on the active tab.Edit yt-dlp-test/.normalize_to_minus14.py.:spinner frame +SKILL=.../bg-task/"$SKILL/task_run.sh" run --title "..." -- python3 yt-dlp-test/.normalize_to_minus14.py+Backgroundbadge.So the same background shell animates in the transcript row but not in the tab strip.
Reporter will attach the original PNG if needed; keeping this textual illustration until the file is uploaded via web UI.
naihongzhou1029 commentedon Nov 7, 2026
Agree this is a duplicate of #51810 — same root cause.
My tab-strip case matches the session-list gap described there:
status()incontext/session-tabs.tsxonly checks sessionactive+ non-syntheticpending, neverdata.shell. The transcript shell row keeps spinning viadata.shell.get(id)while the tab goes idle.Happy to close this in favor of #51810. I’ll add the tab-specific reproduction there so list + tabs can be fixed together.
opencodejr commentedon Nov 7, 2026
Thanks for confirming, @naihongzhou1029. Adding the tab-strip reproduction to #51810 is a good idea: that way the fix can cover the session list, the tabs and the terminal title together, since they all get their state from
status(). This issue will be closed as a duplicate of #51810 as planned.opencodejr commentedon Nov 8, 2026
Closing this as a duplicate of #51810, as agreed above. Thanks again, @naihongzhou1029, for the clear report and root-cause analysis. Please keep following #51810, where adding your tab-strip reproduction will help the session list, tabs and terminal title get fixed together. Related: #56660 (the same gap in the web app) and #62365 (Subagents tab).