[FEATURE] surface live context % in claude agents pane (statusLine / hook)

Status Open
Maintainer reply None cached
Activity 3 comments · opened May 12, 2026

Preflight Checklist

Problem Statement

i use a custom statusLine command that writes per-pane ctx% / 5h% / 7d% to a sidecar file, and a tmux HUD reads from it. its the single most useful signal i have for catching compaction risk during long sessions before it fires.

works fine for normal claude invocations. but theres no path to get the same data inside a claude agents pane:

  • statusLine never fires for the pane (neither for the orchestrator picker nor when a sub-chat is focused)
  • the focused sub-chats are backgrounded by the daemon (claude --bg-pty-host / --bg-spare), so their process tree roots at launchd. no tmux ancestor anywhere in the chain
  • the only tmux-attached process is the agents picker itself, which fires SessionStart once and then nothing per-turn

i verified this empirically with a probe hook that logged session_id, transcript_path, $TMUX_PANE, and the full PPID chain for every hook event. confirmed the topology split: the process with pane context fires only SessionStart, the processes with per-turn hooks have no pane context.

Proposed Solution

any of these would solve it (in order of preference):

  1. fire statusLine in the agents-view pane on each focused-session turn — simplest, matches the existing per-pane contract. JSON payload already has everything needed.
  2. expose the currently-focused session_id via env var or sidecar file owned by the picker process — i can do the rest myself (read transcript_path, scrape usage). env var on the picker process is fine; a /tmp/.claude_agents_focus_<picker_pid>.json file would also work.
  3. fire a hook event when focus changes inside agents-view — same data, different shape. would let me write the sidecar from hook context.

Alternative Solutions

  • tried a per-turn hook (UserPromptSubmit / PostToolUse) thinking i could walk up to the tmux pane from the hooks pid — process tree roots at launchd via --bg-pty-host, no tmux ever reachable
  • considered a "most recent activity globally within N seconds" heuristic across all live sessions, but fragile if multiple sessions are concurrently active
  • current workaround is rendering blank ctx% in agents-view panes (honest "i cant tell" beats confidently-wrong, but loses the signal)

Priority

Medium - Would be very helpful

Feature Category

Interactive mode (TUI)

Use Case Example

  1. tmux pane running claude agents
  2. drill into a sub-chat with right-arrow, work through a long task
  3. context window fills up; HUD ideally shows ctx% creeping up so i can compact before forced compaction fires
  4. today: HUD column is blank in this pane, no way to see compaction risk approaching
  5. with any of the proposals above: HUD updates per turn, same as a normal claude pane

Additional Context

CC 2.x is iterating agents-view actively — happy to test patches behind a feature flag if helpful. the statusLine contract is already well-documented; option 1 is probably a few-line change in whichever process owns the agents-view pane render loop.

View original on GitHub ↗

3 Comments

moBekir · 3 months ago

Thanks for using Claude — and especially for pushing on advanced workflows like this. The tmux/HUD integration pattern is genuinely clever, and the context-pressure visibility problem you’re solving is exactly the kind of operational signal that becomes critical in long-running agent sessions.

This matches my understanding of how Agents is currently wired.

In a normal claude session, statusLine and turn hooks execute in the foreground process tree attached to the tmux pane, so deriving pane identity is straightforward. In Agents mode, the actual conversational workers are detached behind the background PTY host (claude --bg-pty-host / --bg-spare), and the visible tmux-attached process is effectively just the orchestrator UI. Once the agent sessions are daemonized, there’s no stable ancestry path back to the originating pane, so hooks lose all pane locality.

I verified the same behavior on my side:

  • statusLine never fires for focused agent sub-sessions
  • turn hooks execute under the detached daemon tree rather than the interactive pane
  • walking PPIDs from hooks dead-ends at launchd/system roots
  • the picker pane itself emits only the initial lifecycle events

So today there’s effectively no supported mechanism for:

  • mapping a focused agent session back to a tmux pane
  • tracking active sub-chat focus transitions
  • surfacing per-agent runtime metrics (ctx%, token pressure, compaction risk, etc.) into external HUDs

I strongly agree this is a gap. Context pressure telemetry is materially more important in long-running agent workflows than in single sessions, because compaction becomes both more frequent and less visible.

Of the options you listed, I think these make the most sense architecturally:

  1. Fire statusLine for the currently focused agent session inside the agents-view pane
  • preserves the existing contract
  • zero new API surface
  • automatically fixes tmux HUD integrations
  • likely the least disruptive behaviorally
  1. Expose focused session_id from the picker/orchestrator process
  • env var, UNIX socket, or sidecar file would all work
  • enough for external tooling to correlate telemetry independently
  • cleaner separation between UI and worker processes
  1. Emit explicit focus-change hooks
  • probably the most extensible long term
  • useful beyond tmux (IDE integrations, dashboards, observability tooling)
  • could expose {previous_session, current_session} payloads

One additional shape that could work well:

  • a lightweight local event stream from the orchestrator process, e.g.
  • ~/.claude/agents/events.sock
  • newline-delimited JSON
  • emits:
  • focus changes
  • session lifecycle
  • context/compaction thresholds
  • token usage deltas

That would make external HUDs and integrations dramatically easier without coupling tooling to internal process topology.

As a workaround today, the only semi-viable approach I found was polling the orchestrator TUI state indirectly (screen scraping / tty capture), but that’s brittle and loses the nice hook-based model entirely.

I’d definitely be happy to test patches or prototype integrations if you expose even a minimal focus signal from the agents-view parent process.

kcarriedo · 1 month ago

The process-tree topology issue you're running into is real -- the background PTY daemon that hosts sub-chat workers has no tmux ancestor, so there is no reliable path to correlate a hook event back to a pane. The PPID chain really does dead-end at launchd.

Of the three options you proposed, option 2 (expose focused session_id from the picker process) is probably the easiest to wire at the tooling layer today, even before a native fix ships:

If the agents picker process writes its own PID to a known path on start (~/.claude/agents/picker.pid or similar), external tooling can use lsof or proc_info to find which TTY that process is attached to, and correlate from there. Fragile, but it works on macOS where lsof -p is reliable.

A slightly more robust interim approach: a wrapper script that starts claude agents and before exec writes its own PID and $TMUX_PANE to a sidecar file. The hook probe you already wrote (logging PPID chains) has everything needed to close the loop -- the hook runs in the daemon process, the sidecar has the picker PID, and you can do a one-time "is the daemon a descendant of the picker?" walk at hook invocation time.

The statusLine gap in agents view is distinct from the hook scope issue and worth tracking separately. The problem is that statusLine fires on the per-turn hook in the conversational worker (daemon process, no pane), not in the picker process (has pane, fires only SessionStart). A minimal fix would be: when the picker process receives a per-turn event from the focused worker, re-fire statusLine in the picker's own process context. That keeps the existing contract without needing to expose a new API surface.

For what it's worth: running context pressure as a metric in an external monitoring layer (polling session JSONL files for cumulative token counts) has been a workable fallback for catching compaction risk, but the latency is 5-10s per poll interval versus the per-turn freshness you'd get from a native statusLine fix. Not the same thing, but it's something until the daemon/picker topology gets cleaner telemetry routing.

thebestmensch · 1 month ago

Thanks, this is genuinely useful. The confirmation that the PPID chain dead-ends at launchd is worth a lot on its own, bc I'd been assuming I was holding it wrong and there was some ancestor I just hadn't found.

Your statusLine re-fire suggestion is a sharper version of my option 1, and I want to call that out specifically. I'd framed it as "fire statusLine in the agents-view pane," which quietly assumes the pane-owning process can see the focused worker's per-turn events. Framing it as "when the picker receives a per-turn event from the focused worker, re-fire statusLine in the picker's own process context" keeps the existing contract intact and needs no new API surface. That's the one I'd want.

On the sidecar, I'll build the wrapper-script variant, bc it removes the lsof guesswork. I already have $TMUX_PANE at picker start, so writing pane and PID before exec is close to free, and the probe hook I wrote for this report already walks the PPID chain, so the descendant check is a small addition on top. The part I'm less sure about is multiple concurrent claude agents panes, which is my actual setup: two pickers means two sidecars, and the hook has to pick the right one. A descendant walk should disambiguate correctly since the daemons really are parented differently, but I'll find out.

The JSONL polling fallback is the piece I dismissed too early. I'd written it off on freshness grounds, but you're right that 5-10s is fine for what I actually care about, which is noticing compaction risk while there's still room to act rather than watching the number tick. That's a much weaker requirement than per-turn, and it works today, so I'll probably run it while the sidecar gets built. Beats the blank column I have now.

Happy to keep the statusLine gap on this issue and split the hook-scope topology into a separate one if that's cleaner to track. The offer to test a patch behind a flag stands.