Session state hook events for multi-tab terminal integration

Resolved 💬 2 comments Opened Apr 3, 2026 by jordandm Closed Apr 3, 2026

Summary

When running multiple Claude Code sessions in parallel terminal tabs (e.g., Ghostty with 4+ tabs), there's no reliable way to show at-a-glance which session is working, available for input, or blocked on a permission prompt. The information exists internally (mobile and desktop Claude Code show session state), but it's not exposed to terminal integrations via hooks.

Three related gaps

1. Missing session state hook events

Current state: We infer session state from PreToolUse (working), Stop (available), PermissionRequest (needs permission), and Notification (needs attention). This is unreliable:

  • No event fires when the agent starts thinking (between UserPromptSubmit and first PreToolUse) — gap of 5-15 seconds where the tab looks idle but the agent is processing
  • Stop fires but the state gets overwritten (see #2 below)
  • Permission prompts and elicitation dialogs need to be inferred from Notification.notification_type

Request: Explicit session state events that mirror what mobile/desktop Claude Code already shows:

| Event | Fires when | Use case |
|-------|-----------|----------|
| AgentWorking | Agent starts processing (thinking or tool use) | Tab → green "working" |
| AgentAvailable | Agent finished, waiting for user input | Tab → blue "available" |
| AgentNeedsInput | Elicitation dialog or other input request | Tab → red "needs attention" |
| AgentNeedsPermission | Permission prompt shown | Tab → red "needs attention" |

These are the four states visible in mobile/desktop Claude Code. Exposing them as hook events would let terminal integrations (Ghostty, iTerm2, tmux, etc.) show the same status indicators.

2. Claude Code overwrites user-set OSC sequences after Stop

Reproduction:

  1. Configure a Stop hook that writes OSC 2 (tab title) and OSC 11 (background color) to /dev/tty
  2. Run a Claude Code session in Ghostty
  3. Send a message, wait for the agent to respond and stop
  4. The Stop hook fires and writes the new title/bg — confirmed via event logging
  5. Claude Code's own rendering immediately overwrites the title/bg

Evidence: We built an event logger that confirms Stop fires reliably (within 11 seconds of last tool call). But the tab title and background color set by the Stop hook are immediately clobbered by Claude Code's own OSC 2 writes. The hook runs, the write lands, and then Claude Code overwrites it.

Impact: This makes it impossible to build terminal tab indicators that reflect session state. The "working" state persists visually even after the agent stops, because the hook's "available" write gets overwritten.

Expected behavior: After Stop, Claude Code should either (a) not write OSC 2/11 sequences, or (b) respect user-set values, or (c) provide an official mechanism for terminal integrations to set tab appearance.

3. No external session state query

There's no way to query session state from outside the session:

  • No claude --session-status <id> command
  • No state file (e.g., ~/.claude/sessions/{id}/state.json)
  • No Unix socket or local API

For multi-session setups (4-6 terminal tabs running different agents), the only option is hook-based inference, which is unreliable per #1 and #2 above.

Request: Any of:

  • A claude sessions --status command that shows running sessions and their state
  • A state file per session that terminal integrations can poll
  • A way for hooks to read the authoritative session state

Environment

  • Claude Code v2.1.91
  • Ghostty terminal with 4-6 tabs, each running a Claude Code session
  • macOS Darwin 25.4.0
  • Model: Claude Opus 4.6 (1M context)
  • Hooks configured in .claude/settings.json for SessionStart, PreToolUse, PostToolUse, Stop, Notification, SessionEnd, PermissionRequest, UserPromptSubmit

Evidence

Event log from a live session showing the state transitions:

18:30:02  PostToolUse   (last tool call finished)
18:30:13  Stop          (agent finished — 11s later)
18:31:13  Notification  type=idle_prompt msg="Claude is waiting for your input"
                        ^^^ 60 seconds after Stop — timer, not immediate

19:06:25  UserPromptSubmit  (user sends message)
                            ^^^ 12 seconds of "thinking" with NO events
19:06:37  PreToolUse    tool=Bash (first tool call)

This confirms: Stop fires, idle_prompt is delayed 60s, and there's a 12-second gap after UserPromptSubmit where no state event fires.

Use case

OrdinaryFolk runs a 6:1 agent-to-human development workflow with 4+ parallel Claude Code sessions. Each session runs in a Ghostty tab. The engineer needs to see at a glance which tab is working (leave it alone), available (ready for input), or blocked (needs attention). This is the same information shown in mobile/desktop Claude Code — we just need it exposed for terminal integrations.

Related issues

  • #42670 — Scrollback history loss (we also experience this, likely from rapid OSC writes)
  • #41136 — Dynamic tab title updates
  • #41484 — Ghostty notification support (OSC 9/99)

---

Reporter: Jordan Dea-Mattson (@jordan-of)
Email: jordan@ordinaryfolk.co

View original on GitHub ↗

This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗