Skip to content

Show which sessions have an agent actively working, plus an activity timeline across sessions #154

Open

Description

@AThraen

This was generated by AI during triage.

Summary

Two connected parts:

  1. A "working" state per session: the agent is actively doing something (thinking, running tools, streaming a reply). It sits alongside the existing waiting for input (green) and waiting for approval (orange) states, with its own graphical indicator in the sidebar row and the terminal toolbar.
  2. An activity timeline / dashboard: which sessions were active, when, and for how long. For example, one swimlane or heatmap row per session across the day or week.

Motivation

With many sessions open, the question "which agents are busy right now, and which have been idle all afternoon?" has no answer at a glance. Today:

Proposed behaviour

Part 1: working-state detection + indicator

  • Add a SessionViewModel.IsWorking (or an AgentState enum: Idle | Working | WaitingForInput | WaitingForApproval). Bind the sidebar and toolbar dots to it.
  • The indicator should be visibly distinct from the waiting dots, e.g. an animated accent ring or spinner glyph. Remember that nothing WPF can be drawn over a terminal pane (WebView2 is an HwndHost; see CLAUDE.md "Session Spinners"), so it has to live in the sidebar row or the toolbar chrome.
  • The state clears when an alert fires (the agent stopped and is now waiting) or after N seconds of silence.
  • Make Subtle pulse animation on sidebar tabs when their session is emitting output #60's pulse use this state instead of a byte-rate threshold, with the byte-rate version as the fallback for non-agent sessions (see the comment on Subtle pulse animation on sidebar tabs when their session is emitting output #60).

Part 2: activity timeline

  • v1, no new collection needed: session_output already stores session_id + ts for every indexed line (Terminal/OutputIndexer.cs). Bucket lines per session per N minutes, and render a heatmap or swimlane view (e.g. last 24h / 7d) with one row per session, including dormant ones.
  • v2, true working time: record Working intervals from Part 1 (session_id, start, end) in a small new table, so the timeline shows agent time rather than "bytes were printed". It could also total "agent-hours per session / per group / per day".
  • Where it lives is open: a section of Add Workspace Overview view: status cards with activity + output preview #57's Workspace Overview, a tab in feat: usage statistics view — analyse Claude Code agent logs #151's statistics view, or its own panel.

Open decisions (why this is not ready for an agent yet)

  1. How to detect "working". Two candidates:
    • Screen heuristic: Claude Code paints a recognisable live status line while working (spinner + an interrupt hint). Matching it in AlertDetector needs no setup, but it breaks whenever the CLI changes its TUI wording, and it is Claude-specific.
    • Claude Code hooks (UserPromptSubmit → working, Stop / Notification → idle): precise and robust to UI changes. But they need configuration in the user's Claude settings, and a way to get the signal back to CSM. Hook stdout goes to Claude, not to the terminal, so the OSC 9001 channel isn't directly reachable. Options: a local named pipe or file keyed by session id, or the MCP server from Host one MCP server for all sessions: self-control, fleet overview, and orchestration #132.
    • Possibly both: hooks when present, the heuristic otherwise.
  2. Non-Claude sessions: do they get a "working" state at all, or only the Subtle pulse animation on sidebar tabs when their session is emitting output #60 byte-rate pulse?
  3. Dashboard placement and scope: standalone, inside Add Workspace Overview view: status cards with activity + output preview #57, or inside feat: usage statistics view — analyse Claude Code agent logs #151? A time window of 24h or 7d? Live and dormant sessions only, or also closed ones from session_history?
  4. Retention: v1 inherits output.db's 30-day prune (and Cap output.db by size with FIFO eviction — the 30-day prune never reclaims disk space #124's size cap), so the timeline can't reach further back than that.

Related

Activity

  1. AThraen commented on Nov 5, 2026

    @AThraen
    ContributorAuthor

    This was generated by AI during triage.

    Input from Herdr v0.9.x for the "how to detect working" decision

    Herdr combines both of the candidate approaches listed above, and its experience suggests we need both too:

    • Screen detection by default, driven by per-agent data rules, with an agent explain debug view. Its Claude Code integration reports only session identity via a SessionStart hook; working/idle/blocked still come from the screen.
    • Self-reported state on top, for agents and hooks that can report it (pane report-agent --state working|idle|blocked --seq N). It wins while the reporter holds the pane.
    • done vs idle: "finished, not yet looked at" vs "finished and seen". This is a cleaner attention signal than a single alert badge, and it slots straight into this issue's indicator.

    Filed as separate issues so each can move on its own: self-reported state + env vars, data-driven detection rules, and the env-leak bug they both build on (links below).

  2. mortenaslo commented on Nov 6, 2026

    @mortenaslo
    Contributor

    Detecting "working": OSC 7501 (Program Status Protocol)

    This answers open decision 1. It is a better third option than either the screen heuristic or hooks.

    What it is. OSC 7501 is a terminal escape sequence a program uses to report its own state through the PTY. It was proposed in October 2026; spec: https://www.superlogical.com/rex/docs/build/program-status

    ESC ] 7501 ; state=working:progress=40:id=build/test:app=cargo:msg=<base64> ST
    
    • state: idle, working or blocked (each with optional progress 0–100 except idle), done, error, or clear.
    • kind: only with blocked; one of permission, question, auth.
    • Records: one record per id, a slash-separated path. Each report replaces its record, and clear removes a record and its children.
    • Safe to parse: a strict value alphabet [A-Za-z0-9_.,+/=-], msg/title in base64, hard size limits, and a report that breaks any rule is dropped whole.

    Claude Code already supports it, but only if the terminal answers. Checked in the installed claude.exe 2.1.319: it contains PROGRAM_STATUS:7501. At startup it sends the probe OSC 7501 ; ?. If nothing answers it logs probe: no reply to OSC 7501 ; ? and never reports state. CSM doesn't answer today, so we have never received any. Supporting it means replying to the probe, not just listening for reports.

    No hooks or user configuration are needed, and the TUI wording no longer matters. It also works for any other program that adopts it.

    Mapping (agreed)

    OSC 7501 CSM
    working (+ progress) new working state + progress in the sidebar row / toolbar (this issue's Part 1)
    blocked + permission IsWaitingForApproval (orange)
    blocked + question / auth IsWaitingForInput (green)
    done / error NeedsAttention badge / toast
    idle / clear clear the indicators

    It also gives Part 2 v2 exact Working intervals to record, and #60 a real signal instead of byte rate.

    Fallback: keep today's detection

    OSC 7501 is new, and Rex is the only terminal the spec names as implementing it. So keep AlertDetector's regex detection as the fallback:

    • A session that has sent a 7501 report trusts it, and the heuristic stands down for that session. This is the same pattern as the git poller standing down after an OSC 9001 branch.
    • A session that never sends one (older Claude, other CLIs) behaves exactly as today.

    Either way the information can only improve, especially for Claude sessions.

    Implementation sketch

    1. terminal-init.js: add registerOscHandler(7501, …) next to the 9001 one. Reply to ? with OSC 7501 ; ? ST (written back to the PTY by the host: app-authored, so SendToTerminal). Post reports to the host.
    2. Services/ProgramStatusPayload: a WPF-free parser and validator with tests, like ShellIntegrationPayload. It handles the alphabet, base64 + control-character rejection, the limits, and the record tree with clear semantics.
    3. SessionViewModel: keeps an AgentState derived from the records, and stands AlertDetector down once 7501 is seen.
    4. Lifetime: per the spec, working/blocked records drop when the process exits or a new prompt starts (OSC 133 A).
    5. UI: the working indicator lives in the sidebar row and toolbar chrome, never over a pane (HwndHost).
  3. AThraen commented on Nov 10, 2026

    @AThraen
    ContributorAuthor

    This was generated by AI during triage.

    Status update: Part 1 shipped in #190 (v0.11.0). Sessions now report their state through OSC 7501, and the regex detection is kept as a fallback when a session reports nothing. The sidebar and toolbar show ⟳ working · N%, with exact waiting dots and badges when a session becomes blocked, done or errors. Claude Code reports its state on its own, because CSM now answers its probe.

    Still open: Part 2, the activity timeline and dashboard. The data it needs now exists for each session (AgentState, WorkProgress, ProgramStatusMessage), but nothing records it over time. A timeline needs those state transitions stored with timestamps, for example as a small table in output.db next to the output index. The per-project board in #161 reads the same state.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions