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
First off, love what you've done. I use cmux on my mac and found this repo as I'm forced to use Windows at work.
Thought I'd post something that I've found after installed. The statuses to not seem to line up with with what Claude is doing very often. More times than not the workspace will say Idle when in fact an agent is running some stuff. Please see screenshot. In addition when you first start a Claude session it will say 'Running' even though it is a brand new instance that is doing nothing. Also, after going to a 'Needs you' workspace, answering questions or following up. The window does not come out of that state. It seems to be stuck in a 'Needs you' status even after resuming work. I find myself having to just click through tabs to see where things are instead of being able to rely on the actual status of the workspace.
Not sure if interested but I forked the code and working through some updates on the status handling now. Will test through for a work day or two and can provide a PR back to you if that helps you out.
Thank you — this is a good report, and all three symptoms turned out to be one root cause plus three smaller bugs sitting on top of it. Fixed in 0.48.0.
The root cause: wmux only listened for things ENDING
wmux drives its status from Claude Code hooks rather than from scraping the TUI. It registered four of them:
PostToolUse a tool finished
Notification Claude wants you
Stop the turn finished
SubagentStop a subagent finished
Three of those are terminal events. Nothing ever said work had started. So the state machine only learned a turn was in flight when the first tool completed — and everything before that resolved to idle:
you send a message, Claude thinks for 40 s before touching a tool → Idle
Claude runs one three-minute npm test → Idle until it finishes
a turn that reasons and writes prose without calling a tool at all → Idle, start to finish
That's your "more times than not it says Idle when an agent is running". It was never a timing or a refresh problem; the event that would have said otherwise was not being listened for.
0.48.0 registers the opening half of the lifecycle:
hook
what it now tells wmux
SessionStart
this pane is an agent session, currently idle
UserPromptSubmit
the human replied — turn started, and any wait is over
PreToolUse
a tool is starting (matcher-less, so it covers every tool, not just the tracked ones)
SessionEnd
Claude Code exited — release the pane
"Brand new session says Running"
Different path, and a fair bit dumber than it looks. With no session known for a pane, the sidebar falls back to the shell's own state — and claudeis a foreground command, so shell integration is entirely correct to report the pane as "running" for the whole life of the session. There was simply nothing outranking it.
SessionStart fixes it by registering the pane as a known agent session at depth 0 the moment Claude launches (also on /clear, /compact and resume), so it reads Idle instead of borrowing the shell's verdict.
"Needs you" that never clears
Two separate causes, both fixed:
1. Following up didn't clear it. The block was only cleared by PostToolUse or Stop — so after you replied, the pane kept saying "Needs you" until the next tool happened to finish. UserPromptSubmit now ends it the instant you hit Enter.
2. Approving a permission prompt didn't clear it either — and this one has no hook at all. The order is PreToolUse → permission prompt → Notification → you approve → tool runs → PostToolUse. PreToolUse has already fired by the time the prompt appears, so between your approval and the tool finishing, Claude Code emits nothing. Approve a long command and the pane sits on "Needs you" for its entire runtime.
For that one wmux now uses a fact it owns rather than one it has to infer: it owns the PTY, so it knows with certainty that a human pressed a key in that pane. Keystrokes that commit something — typing, Enter, Escape — clear the block. Arrow keys, page-up and mouse reports do not, because being sent to a pane and scrolling to see what it wants is the normal first move and must not clear the flag that sent you there.
To be explicit, since it bends a rule I wrote deliberately: this is not a relaxation of "answering never clears blocked". That rule stops wmux declaring a prompt answered on its own say-so — if the sidebar's relayed keystroke were wrong, the pane would silently stop asking for help while the agent was still stuck. Here the human answered it themselves, in the pane, and wmux is reporting its own observation rather than guessing at Claude Code's UI. The failure mode is bounded and self-healing in the safe direction: if the keystroke didn't satisfy the agent, its very next report — a tool, a turn end, or the 60-second idle nudge — puts the pane straight back to asking.
Two more bugs found while in there
Subagents collapsed the status to Idle.SubagentStop decremented a refcount that PostToolUse pins to the absolute value 1. Subagent hooks inherit the parent's WMUX_SURFACE_ID, so every subagent reports to one pane — meaning the first subagent to finish drained the count to zero and the pane read Idle with the parent and its siblings still working. Any /wmux:orchestrate or multi-Task turn hit this. Only the parent's Stop ends a turn, and it was already decisive; the refcount was doing nothing but lying. This one probably accounted for a good share of the false Idles in your screenshot.
Racing hook processes could pin a pane on "Running" forever. Every hook is its own short-lived node process, and they race to the pipe. PostToolUse fires before Stop, but if its process is the slower of the two to connect, its "a run is in flight" lands afterStop cleared the turn — and the pane then claims Running for the full 15-minute trust window with nothing running. Invisible from the outside, because both reports are individually correct. The hook helper now stamps the fire time at process start and wmux orders reports by that instead of by arrival.
Upgrading
Hooks are rewritten into ~/.claude/settings.json on startup, so 0.48.0 installs the four new ones by itself — your own hooks in those arrays are preserved untouched, and declining the agent integration still removes every wmux entry cleanly. Restart any Claude Code sessions that were already open: hooks are read at session start, so a session running from before the upgrade keeps the old set until you restart it.
One honest note on cost: PreToolUse means one extra short-lived node process per tool call. Each is ~100 ms and exits (that was tightened in 0.42.0), but under several very busy agents it's roughly double the hook process churn of 0.47.0. If you notice it, tell me — the fallback is to give PreToolUse the same tracked-tool matcher PostToolUse uses, which cuts the volume at the cost of missing turns spent entirely in untracked tools.
On your fork
Yes, genuinely interested — please do open the PR. You're using this on a real work day across many panes, which is exactly the shape of usage that surfaced all of the above and that I can't reproduce reliably here. If your fork solved any of these differently I'd rather take your version than defend mine, and anything it covers that 0.48.0 doesn't is worth having on its own. Rebasing onto 0.48.0 first will save you some noise, since it touches most of agent-hook-bridge.ts and agent-state.ts.
0.48.0 is building now and will be on the releases page shortly. Please reopen this — or just say so here — if any of the three is still misbehaving for you.
Sounds good. I've pulled in your latest release code and we're aligned on quite a few things. I'm still hammering on it and finding some edge cases that I'm working through. Once I'm feeling good about how it all is flowing, responding, and showing statuses, I'll get a PR out to you for review. Thanks.
Status update, since 0.50.0 is out today: it contains no changes to agent-state handling. The fix for the three symptoms you reported is still exactly what shipped in 0.48.0 — the missing "work started" signal, plus the run-refcount, the seq de-dupe and the rule that answering never clears blocked on its own.
So if you're testing your fork against a release, 0.50.0 is a safe base to rebase onto: this release is a CLI shim for PowerShell argument quoting (#154), CDP target recovery when a browser renderer dies (#155), browser command timeouts (#153), and the global agent-context block (#152). Nothing under agent-state.ts, agent-state-rpc.ts or agent-hook-bridge.ts moved.
Still very interested in the PR. The edge cases are where this gets hard — nested subagents, a turn that ends while a subagent is still running, and a session that is answered from another pane via wmux answer-agent are the three that have historically been wrong in different ways. If your work converges on those, that is exactly the part worth reviewing.
Leaving this open until your PR lands rather than closing on the 0.48.0 fix.
Sounds good. And that does sound like the cases that I am working through and testing against. I'll pull in the 0.50.0 stuff today and have a full work day on this and I 'think' I'm pretty close to having things all ironed out. I hope to have a PR out to you by tomorrow.
PR is up — ended up splitting it in two. The status work is in #159, and a separate notification-ring thing I found while testing is in #160, since it doesn't share any files with the rest.
Short version of #159: the single-session path in the sidebar doesn't reach workingSessions yet, so one agent pane reads Idle mid-turn. Three edge cases in the hook bridge — the bare-tool PostToolUse form, the 60s idle nudge reading as "Needs you" on a pane nobody touched, and Escape leaving working set after an interrupt. A guard for SubagentStop arriving after Stop, which is the order I traced on two panes here. And the one that finally explained my original report: background shells and parallel subagents inherit the pane's surface id, so their tool lifecycle was clearing the block while the question was still on screen, and nothing put it back until the turn ended.
All of it sits on top of 0.48.0 rather than changing the approach — that rewrite is what made these findable in the first place. Once the turn-start signal was there, the readings that were still off had a shape instead of feeling random. I've been running it as my daily driver since, on 0.48.0 and now 0.50.0, usually with several panes going.
Happy to split any of it up, reorder, or drop pieces. One thing worth your eyes specifically: commit 4 changes behaviour on the answer-agent relay path, and I called out the trade-off in the PR — easy to drop that one and keep the other three if you'd rather leave it as is.
Your PRs are merged — #159 (all four commits) and #160 — and both are in 1.0.0, out now.
Closing this, finally, on your fix rather than mine. The detailed review is on each PR; the short version of what your four commits changed since 0.48.0:
symptom from your original report
single-session path never reached workingSessions
one agent pane reading Idle mid-turn
bare-tool PostToolUse never reached agent state
long turns aging out of working on the clock
60s idle nudge treated as a prompt
Needs you on a pane nobody had touched
bare Escape left runDepth asserted
Running after an interrupt
SubagentStop arriving after Stop
Running with the agent idle at its prompt
tool events clearing awaitingHuman
a question on screen while the row said Running…
Two of those turned on facts I had asserted in comments and never measured — that Claude Code fires the parent's Stop only after every subagent stops (it doesn't; you traced 1.8–2.2 s the other way on two panes), and that a pending permission dialog would have stopped a tool from running (true for an agent doing one thing at a time, false for background shells and parallel subagents, which is what agents actually do). You were right about both, with traces.
Reopen if anything is still misreading on 1.0.0 — you've got more hours on this subsystem across more simultaneous panes than I do, and that's how all of the above surfaced.
Activity
derekneely commentedon Aug 11, 2026
Not sure if interested but I forked the code and working through some updates on the status handling now. Will test through for a work day or two and can provide a PR back to you if that helps you out.
fix(agent-state): report work starting, not only work ending (#151)
amirlehmam commentedon Aug 12, 2026
Thank you — this is a good report, and all three symptoms turned out to be one root cause plus three smaller bugs sitting on top of it. Fixed in 0.48.0.
The root cause: wmux only listened for things ENDING
wmux drives its status from Claude Code hooks rather than from scraping the TUI. It registered four of them:
Three of those are terminal events. Nothing ever said work had started. So the state machine only learned a turn was in flight when the first tool completed — and everything before that resolved to
idle:npm test→ Idle until it finishesThat's your "more times than not it says Idle when an agent is running". It was never a timing or a refresh problem; the event that would have said otherwise was not being listened for.
0.48.0 registers the opening half of the lifecycle:
SessionStartUserPromptSubmitPreToolUseSessionEnd"Brand new session says Running"
Different path, and a fair bit dumber than it looks. With no session known for a pane, the sidebar falls back to the shell's own state — and
claudeis a foreground command, so shell integration is entirely correct to report the pane as "running" for the whole life of the session. There was simply nothing outranking it.SessionStartfixes it by registering the pane as a known agent session at depth 0 the moment Claude launches (also on/clear,/compactand resume), so it reads Idle instead of borrowing the shell's verdict."Needs you" that never clears
Two separate causes, both fixed:
1. Following up didn't clear it. The block was only cleared by
PostToolUseorStop— so after you replied, the pane kept saying "Needs you" until the next tool happened to finish.UserPromptSubmitnow ends it the instant you hit Enter.2. Approving a permission prompt didn't clear it either — and this one has no hook at all. The order is
PreToolUse→ permission prompt →Notification→ you approve → tool runs →PostToolUse.PreToolUsehas already fired by the time the prompt appears, so between your approval and the tool finishing, Claude Code emits nothing. Approve a long command and the pane sits on "Needs you" for its entire runtime.For that one wmux now uses a fact it owns rather than one it has to infer: it owns the PTY, so it knows with certainty that a human pressed a key in that pane. Keystrokes that commit something — typing, Enter, Escape — clear the block. Arrow keys, page-up and mouse reports do not, because being sent to a pane and scrolling to see what it wants is the normal first move and must not clear the flag that sent you there.
To be explicit, since it bends a rule I wrote deliberately: this is not a relaxation of "answering never clears blocked". That rule stops wmux declaring a prompt answered on its own say-so — if the sidebar's relayed keystroke were wrong, the pane would silently stop asking for help while the agent was still stuck. Here the human answered it themselves, in the pane, and wmux is reporting its own observation rather than guessing at Claude Code's UI. The failure mode is bounded and self-healing in the safe direction: if the keystroke didn't satisfy the agent, its very next report — a tool, a turn end, or the 60-second idle nudge — puts the pane straight back to asking.
Two more bugs found while in there
Subagents collapsed the status to Idle.
SubagentStopdecremented a refcount thatPostToolUsepins to the absolute value1. Subagent hooks inherit the parent'sWMUX_SURFACE_ID, so every subagent reports to one pane — meaning the first subagent to finish drained the count to zero and the pane read Idle with the parent and its siblings still working. Any/wmux:orchestrateor multi-Taskturn hit this. Only the parent'sStopends a turn, and it was already decisive; the refcount was doing nothing but lying. This one probably accounted for a good share of the false Idles in your screenshot.Racing hook processes could pin a pane on "Running" forever. Every hook is its own short-lived
nodeprocess, and they race to the pipe.PostToolUsefires beforeStop, but if its process is the slower of the two to connect, its "a run is in flight" lands afterStopcleared the turn — and the pane then claims Running for the full 15-minute trust window with nothing running. Invisible from the outside, because both reports are individually correct. The hook helper now stamps the fire time at process start and wmux orders reports by that instead of by arrival.Upgrading
Hooks are rewritten into
~/.claude/settings.jsonon startup, so 0.48.0 installs the four new ones by itself — your own hooks in those arrays are preserved untouched, and declining the agent integration still removes every wmux entry cleanly. Restart any Claude Code sessions that were already open: hooks are read at session start, so a session running from before the upgrade keeps the old set until you restart it.One honest note on cost:
PreToolUsemeans one extra short-livednodeprocess per tool call. Each is ~100 ms and exits (that was tightened in 0.42.0), but under several very busy agents it's roughly double the hook process churn of 0.47.0. If you notice it, tell me — the fallback is to givePreToolUsethe same tracked-tool matcherPostToolUseuses, which cuts the volume at the cost of missing turns spent entirely in untracked tools.On your fork
Yes, genuinely interested — please do open the PR. You're using this on a real work day across many panes, which is exactly the shape of usage that surfaced all of the above and that I can't reproduce reliably here. If your fork solved any of these differently I'd rather take your version than defend mine, and anything it covers that 0.48.0 doesn't is worth having on its own. Rebasing onto 0.48.0 first will save you some noise, since it touches most of
agent-hook-bridge.tsandagent-state.ts.0.48.0 is building now and will be on the releases page shortly. Please reopen this — or just say so here — if any of the three is still misbehaving for you.
fix(agent-state): SubagentStop fires AFTER the parent Stop (amirlehma…
chore(release): 0.48.1-test.3 — local test build, not for upstream
derekneely commentedon Aug 12, 2026
Sounds good. I've pulled in your latest release code and we're aligned on quite a few things. I'm still hammering on it and finding some edge cases that I'm working through. Once I'm feeling good about how it all is flowing, responding, and showing statuses, I'll get a PR out to you for review. Thanks.
fix(agent-state): background work must not retract a live question (a…
amirlehmam commentedon Aug 13, 2026
Status update, since 0.50.0 is out today: it contains no changes to agent-state handling. The fix for the three symptoms you reported is still exactly what shipped in 0.48.0 — the missing "work started" signal, plus the run-refcount, the
seqde-dupe and the rule that answering never clearsblockedon its own.So if you're testing your fork against a release, 0.50.0 is a safe base to rebase onto: this release is a CLI shim for PowerShell argument quoting (#154), CDP target recovery when a browser renderer dies (#155), browser command timeouts (#153), and the global agent-context block (#152). Nothing under
agent-state.ts,agent-state-rpc.tsoragent-hook-bridge.tsmoved.Still very interested in the PR. The edge cases are where this gets hard — nested subagents, a turn that ends while a subagent is still running, and a session that is answered from another pane via
wmux answer-agentare the three that have historically been wrong in different ways. If your work converges on those, that is exactly the part worth reviewing.Leaving this open until your PR lands rather than closing on the 0.48.0 fix.
derekneely commentedon Aug 13, 2026
Sounds good. And that does sound like the cases that I am working through and testing against. I'll pull in the 0.50.0 stuff today and have a full work day on this and I 'think' I'm pretty close to having things all ironed out. I hope to have a PR out to you by tomorrow.
derekneely commentedon Aug 13, 2026
PR is up — ended up splitting it in two. The status work is in #159, and a separate notification-ring thing I found while testing is in #160, since it doesn't share any files with the rest.
Short version of #159: the single-session path in the sidebar doesn't reach
workingSessionsyet, so one agent pane reads Idle mid-turn. Three edge cases in the hook bridge — the bare-tool PostToolUse form, the 60s idle nudge reading as "Needs you" on a pane nobody touched, and Escape leavingworkingset after an interrupt. A guard for SubagentStop arriving after Stop, which is the order I traced on two panes here. And the one that finally explained my original report: background shells and parallel subagents inherit the pane's surface id, so their tool lifecycle was clearing the block while the question was still on screen, and nothing put it back until the turn ended.All of it sits on top of 0.48.0 rather than changing the approach — that rewrite is what made these findable in the first place. Once the turn-start signal was there, the readings that were still off had a shape instead of feeling random. I've been running it as my daily driver since, on 0.48.0 and now 0.50.0, usually with several panes going.
Happy to split any of it up, reorder, or drop pieces. One thing worth your eyes specifically: commit 4 changes behaviour on the
answer-agentrelay path, and I called out the trade-off in the PR — easy to drop that one and keep the other three if you'd rather leave it as is.fix(sidebar): read declared session state in the single-session case (#…
fix(agent-state): three edge cases in the hook bridge (#151)
fix(agent-state): guard against a trailing SubagentStop after Stop (#151
fix(agent-state): keep a live question while background work runs (#151)
Merge PR #159: agent-state edge cases (#151)
amirlehmam commentedon Aug 14, 2026
Your PRs are merged — #159 (all four commits) and #160 — and both are in 1.0.0, out now.
Closing this, finally, on your fix rather than mine. The detailed review is on each PR; the short version of what your four commits changed since 0.48.0:
workingSessionsPostToolUsenever reached agent stateworkingon the clockrunDepthassertedSubagentStoparriving afterStopawaitingHumanTwo of those turned on facts I had asserted in comments and never measured — that Claude Code fires the parent's
Stoponly after every subagent stops (it doesn't; you traced 1.8–2.2 s the other way on two panes), and that a pending permission dialog would have stopped a tool from running (true for an agent doing one thing at a time, false for background shells and parallel subagents, which is what agents actually do). You were right about both, with traces.Reopen if anything is still misreading on 1.0.0 — you've got more hours on this subsystem across more simultaneous panes than I do, and that's how all of the above surfaced.
perf(windows): take the dead wait out of every tool call, hook and pr…
fix(agent-state): an idle reminder is not "Needs you" (#253)
fix(opencode): serialize TUI working/idle reports so a turn's end can…
fix(agent-state): clear a permission block once the agent moves past …