You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
web: session icon shows no progress indicator while a background shell runs #56660
In the web app, the session icon (titlebar tab and home list row) turns into the progress indicator while the session or one of its subagents is working. It does not while a background shell is working. When the agent's turn ends, the icon goes back to the project avatar, so the session looks finished with a command still running.
A background subagent shows the indicator because it is a child session with status running. A background shell is not a session. It only lives in data.shell, which useSessionTabAvatarState (packages/app/src/shell/layout/project-avatar-state.ts) never reads, and the server marks the parent idle as soon as its turn ends.
The TUI session list has the same gap (#51810). I found no issue for the web app. Related: #45560, and #53211, which adds a waiting status that this check would still need to count.
I reproduced it in the app's e2e harness against a mock server and traced the cause in the code. I have not run it against a live agent.
Thanks for the detailed report. I can confirm this on 2.0.32 and on the latest dev (63c1a27).
What I checked:
Server: the simulated model starts sleep 300 with the shell tool's background: true option, and the turn ends. The server then reports the session as idle (session.active() is empty, and the session has an idle timestamp). The shell list still shows the shell as running, with metadata.sessionID pointing at that session. The script below reproduces this.
Web app: I used the app's e2e harness with that same data. A tab whose session is running shows the progress indicator. A tab whose session is idle but owns a running background shell shows the project avatar instead, as in the screenshot below.
This matches your analysis: useSessionTabAvatarState only looks at session status and pending inbox items, never at data.shell. Related:
// Reproduces #56660 at the server/data level the web app reads:// after a turn that started a background shell ends, the session is no longer// reported as active/running, while the shell is still running. The web app's// useSessionTabAvatarState only checks session status + pending inbox, so the// tab/home icon falls back to the project avatar.import{Effect}from"effect"import{Llm,OpenCodeDriver}from"opencode-drive"exportdefaultOpenCodeDriver.use({ ...(process.env.OC_DEV ? {opencode: {dev: process.env.OC_DEV}} : {}),project: {git: true}},({ ui, llm, opencode })=>Effect.gen(function*(){yield*llm.queue(Llm.toolCall({index: 0,id: "call_bg",name: "shell",input: {command: "sleep 300",description: "Sleep in background",background: true},}),Llm.finish("tool-calls"),)yield*llm.queue(Llm.text("Started sleep 300 in the background. Turn over."))yield*ui.submit("Run sleep 300 in the background")yield*ui.waitFor("Turn over.")yield*Effect.sleep(3000)constactive=yield*Effect.orDie(opencode.session.active())constshells=yield*Effect.orDie(opencode.shell.list())constsessions=yield*Effect.orDie(opencode.session.list())yield*Effect.log("SESSIONS "+JSON.stringify(sessions).slice(0,600))yield*Effect.log("ACTIVE "+JSON.stringify(active))yield*Effect.log("SHELLS "+JSON.stringify(shells).slice(0,1500))}),)
Run it with opencode-drive: opencode-drive run ./drive.ts. It drives opencode from your PATH; set OC_DEV to the path of an OpenCode checkout to run that from source instead.
This also affects OpenCode Desktop 2.2.5 and session API consumers, not just the web session icon. In Desktop, I see a session appear stopped while a shell script is still running in the background. API clients that derive their Working/idle indicator from session.active() can also show the session as stopped when it is omitted after the agent turn ends, even though its shell remains active.
Please consider the impact on session API consumers as well as the built-in UI. If background shell activity is intentionally separate from agent execution status, an accessible way for API clients to distinguish it would help avoid displaying the session as fully stopped.
Activity
opencode-agent commentedon Oct 18, 2026
Thanks for the detailed report. I can confirm this on 2.0.32 and on the latest
dev(63c1a27).What I checked:
sleep 300with the shell tool'sbackground: trueoption, and the turn ends. The server then reports the session as idle (session.active()is empty, and the session has an idle timestamp). The shell list still shows the shell asrunning, withmetadata.sessionIDpointing at that session. The script below reproduces this.runningshows the progress indicator. A tab whose session is idle but owns a running background shell shows the project avatar instead, as in the screenshot below.This matches your analysis:
useSessionTabAvatarStateonly looks at session status and pending inbox items, never atdata.shell. Related:Reproduction script (drive.ts)
Run it with opencode-drive:
opencode-drive run ./drive.ts. It drivesopencodefrom yourPATH; setOC_DEVto the path of an OpenCode checkout to run that from source instead.dluc commentedon Nov 8, 2026
This also affects OpenCode Desktop 2.2.5 and session API consumers, not just the web session icon. In Desktop, I see a session appear stopped while a shell script is still running in the background. API clients that derive their Working/idle indicator from
session.active()can also show the session as stopped when it is omitted after the agent turn ends, even though its shell remains active.Please consider the impact on session API consumers as well as the built-in UI. If background shell activity is intentionally separate from agent execution status, an accessible way for API clients to distinguish it would help avoid displaying the session as fully stopped.