Skip to content

feat(desktop): observer-driven activity bar in the thread composer - #4663

Open
TolgaCinisli wants to merge 4 commits into
block:mainfrom
TolgaCinisli:cnsl/pr-c-thread-composer-bar
Open

feat(desktop): observer-driven activity bar in the thread composer#4663
TolgaCinisli wants to merge 4 commits into
block:mainfrom
TolgaCinisli:cnsl/pr-c-thread-composer-bar

Conversation

@TolgaCinisli

Copy link
Copy Markdown

Summary

Depends on #4662 (feat(desktop): stable Slack-style status line in the composer activity bar). This branch is stacked on it, so the first two
commits here are #4662's and the diff shrinks to the last two once that
merges. Please review/merge #4662 first.

The thread composer's activity accessory was fed only by thread-scoped typing
entries. Once observer frames became the primary working signal, opening a
thread showed a dark bar for the entire turn even while the channel bar was
live.

  • Store the NIP-AO envelope sessionId (the thread root, shortened) on each
    active turn, and scope working agents to the open thread by prefix match —
    folded with the existing typing fallback in a new
    useThreadComposerWorkingPubkeys.
  • A thread bar locks its status detail onto its own turn via the same prefix
    (new threadRootId prop), so two threads in one channel no longer show each
    other's tool rows.
  • With more than one live turn in a channel, the channel bar aggregates to
    2 threads · 1m 04s instead of interleaving two turns' details in one line.
    Each thread's own bar still carries its own detail.

getActiveTurnCountForChannel returns a primitive so the useSyncExternalStore
subscription is reference-stable for free.

Related issue

None found — searched open issues and PRs for "thread composer" / "activity
bar" / "parallel turns".

Testing

  • activeAgentTurnsStore.test.mjs — new cases for
    getWorkingAgentPubkeysForThread (prefix match, missing sessionId, turn
    completion) and getActiveTurnCountForChannel (per-agent/per-channel
    counting, null inputs, completed turns).
  • botActivityStatus.test.mjs — new case for threadRootId locking the status
    onto its own turn.
  • pnpm -C desktop test — 4058 passing
  • pnpm -C desktop typecheck — clean
  • biome check on the touched files — clean
  • Manual: two agent turns running in parallel in one channel. Channel bar shows
    "2 threads · elapsed"; each open thread's composer bar shows that thread's own
    tool and counters.

CNSL00 added 4 commits August 4, 2026 03:51
The inline bot activity bar rotated through the last 5 distinct transcript
headlines every 2.2s, which read as flicker during long agent turns. Replace
the rotation with one line that updates in place:

  Assistant · Bash: npm test · 2m 10s · 12 tools · ctx 118K/1M

Current activity (running tool + clamped preview, or phase), ticking elapsed
anchored to the agent working signal, per-turn tool count, and the latest
usage reading. Popover and session panel are untouched.

Signed-off-by: Tolga Cinisli <tolgacinisli@gmail.com>
…line

Shell rows rendered the raw command text, so the composer bar showed noise
like `sed -n '420,432p' src/…` instead of what the agent was doing. Claude
Code (and other harnesses) send a human-authored `description` argument with
every shell call — prefer it when present, falling back to the descriptor
preview otherwise. The session panel still shows the full command; the bar is
a status line, not a terminal.

Signed-off-by: Tolga Cinisli <tolgacinisli@gmail.com>
The thread composer's activity accessory was fed only by thread-scoped
typing entries, so once observer frames became the primary working signal
the thread bar stayed dark for the whole turn. Store the NIP-AO envelope
sessionId (thread root shortened) on each active turn and scope working
agents to the open thread by prefix match, folded with the existing typing
fallback via useThreadComposerWorkingPubkeys.

Signed-off-by: Tolga Cinisli <tolgacinisli@gmail.com>
…urns

Two turns running in one channel interleaved in a single status line, so the
bar flipped between unrelated work. Give each bar a scope instead:

- `threadRootId` locks a thread composer's bar onto its own turn by matching
  the observer frame sessionId as a prefix of the thread root.
- Past one live turn the channel bar aggregates to "N threads · elapsed"
  rather than rendering one turn's detail as if it were the whole channel.

New `getActiveTurnCountForChannel` returns a primitive, so the store
subscription stays reference-stable under `useSyncExternalStore`.

Signed-off-by: Tolga Cinisli <tolgacinisli@gmail.com>
@TolgaCinisli
TolgaCinisli requested a review from a team as a code owner August 4, 2026 04:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants