Skip to content

feat(tui): live "Background agents (N)" sidebar panel#3118

Open
aheritier wants to merge 2 commits into
feat/tui-background-agent-renderersfrom
feat/tui-background-agent-live-status
Open

feat(tui): live "Background agents (N)" sidebar panel#3118
aheritier wants to merge 2 commits into
feat/tui-background-agent-renderersfrom
feat/tui-background-agent-live-status

Conversation

@aheritier

@aheritier aheritier commented Jun 15, 2026

Copy link
Copy Markdown
Contributor

Summary

Until now, a dispatched run_background_agent fleet was essentially invisible in the TUI: events are dropped by design (runCollecting), so the human only saw the initial "task started" line and whatever the model chose to poll. This adds a live, at-a-glance surface:

  • Runtime read-model. Handler.Snapshot() []TaskInfo over the existing tasks concurrent map — strictly read-only, race-free, no new locks, never exposes *task, and no change to execution / event-drop semantics.
  • App→TUI accessor via an optional interface, so a remote/client runtime that can't provide it simply reports none.
  • ~1s poll (tea.Tick) that runs while a stream is working or any task is still running (background tasks outlive the turn), and self-stops when idle.
  • Sidebar panel Background agents (N): one row per running task — ● <agent> <elapsed> (accent dot + name, right-aligned elapsed), hidden entirely when nothing runs.
  • Fills the Phase-2 +N background breadcrumb count (the TODO(#3103) left in TUI: make sequential / multiple delegations legible #3102).

Why polling, not event-push

The background goroutines outlive the per-turn event sink (that's why events are dropped today), so there's no live stream to forward safely. Polling a lock-safe snapshot is simple, self-limiting (stops when empty), and adds no runtime coupling.

Screenshots

c2-panel-running

Background agents (3) panel while three researchers run in parallel — plus the Phase-2 breadcrumb gaining +3 background during a concurrent transfer_task:

c2-panel-and-breadcrumb

The panel is live: the count drains (3 → 1) and the section disappears as tasks finish:

c2-panel-drain

Tests

  • pkg/tools/builtin/agentSnapshot() table test (running/completed/stopped/empty); passes go test -race.
  • pkg/tui/page/chat — poll idempotency, continue/stop lifecycle, running-task detection.
  • pkg/tui/components/sidebar — panel render/hide, filter/sort/drain, +N background.

task build / task lint clean; touched-package tests green (only the unrelated pkg/teamloader/TestLoadExamples DMR 416 fails). Cleared local review (incl. a -race pass on the snapshot).

Deviations (deliberate)

  1. Rows omit a literal "running" word — only running tasks are listed, and the colored conveys state.
  2. Optional completion notice (C.2.3) skipped to keep the diff tight; trivial to add.
  3. +N background is appended only to the depth>1 delegation breadcrumb; the standalone panel covers the no-delegation case.

Stack

4th in the delegation-readability stack, stacked on #3117 (Phase 3 C.1): #3115#3116#3117 ← this. Tracking #3104. C.3 (depth indentation) remains an out-of-scope stretch.

Refs #3103

@docker-agent docker-agent left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Assessment: 🟢 APPROVE

This PR adds a live "Background agents (N)" sidebar panel with a ~1s poll loop, a race-free Snapshot() read-model, and the Phase-2 breadcrumb count (+N background). The implementation is solid — no CONFIRMED or LIKELY bugs were found.

Areas reviewed:

  • Snapshot() race safety — Fields read are immutable after task creation; atomic load for status. Race-free by design, confirmed clean. ✅
  • Poll lifecycle (startBackgroundAgentPoll / handleBackgroundAgentPoll) — The backgroundPollActive guard works correctly in Bubbletea's serialised Elm loop. Each tea.Tick fires exactly once; the loop is strictly sequential (no stale in-flight ticks possible). ✅
  • SetBackgroundAgents no-op guard — Correctly skips cache invalidation only when the panel was already empty before the call; drains properly when tasks finish. ✅
  • delegationBreadcrumb width arithmetic — The avail <= 0 guard fires before any ansi.Truncate call; no negative-width truncation possible. ✅
  • Optional Snapshoter interface assertion (app.go) — Safe two-value type assertion (l, ok := ...), no panic path. ✅
  • Sidebar renderingbackgroundAgentsSection gap calculation uses lipgloss.Width which correctly strips ANSI; layout is correct. ✅
  • FormatDuration with negative durations — Clock skew could produce a display glitch (-1s) but no crash; acceptable for a live elapsed timer. ✅

No action required — the implementation matches the design intent described in the PR and the Phase 3 C.2 spec.

@aheritier aheritier self-assigned this Jun 15, 2026
@aheritier aheritier changed the title feat(tui): live "Background agents (N)" sidebar panel (#3103, Phase 3 C.2) feat(tui): live "Background agents (N)" sidebar panel Jun 15, 2026
@aheritier aheritier added area/agent For work that has to do with the general agent loop/agentic features of the app area/tools For features/issues/fixes related to the usage of built-in and MCP tools area/tui For features/issues/fixes related to the TUI kind/feat PR adds a new feature (maps to feat: commit prefix) labels Jun 15, 2026
@aheritier aheritier marked this pull request as ready for review June 15, 2026 11:33
@aheritier aheritier requested a review from a team as a code owner June 15, 2026 11:33
@docker-agent

Copy link
Copy Markdown

PR Review Failed — The review agent encountered an error and could not complete the review. View logs.

@aheritier aheritier force-pushed the feat/tui-background-agent-renderers branch from cf40afa to d0f18c0 Compare June 15, 2026 12:12
@aheritier aheritier force-pushed the feat/tui-background-agent-live-status branch from b0f9f58 to 5f1c3ef Compare June 15, 2026 12:13
@aheritier aheritier force-pushed the feat/tui-background-agent-renderers branch from d0f18c0 to d432398 Compare June 15, 2026 12:36
@aheritier aheritier force-pushed the feat/tui-background-agent-live-status branch from 5f1c3ef to 8d9af10 Compare June 15, 2026 12:36
The background-agent Handler keeps its tasks in an unexported concurrent
map, so the TUI had no way to observe the genuinely-concurrent fleet
spawned by run_background_agent. Expose a public, lock-safe read model
without touching task lifecycle:

- agent.Handler.Snapshot() returns []TaskInfo (id, agent, task text,
  status string, start time). It ranges the tasks map reading only
  fields fixed at creation plus the atomic status, so it is safe to call
  concurrently with running task goroutines and never leaks the internal
  *task. runCollecting's event-drop semantics are unchanged.
- LocalRuntime.BackgroundAgents() surfaces the snapshot; the App reaches
  it through an optional interface so remote runtimes report none, like
  the other read-only runtime accessors.

This is the seam the TUI's live background-agent surface polls.

Refs #3103
Concurrent run_background_agent tasks were invisible in the TUI: their
events are dropped by runCollecting and the goroutines outlive the
per-turn event sink, so there is no live stream to forward. Poll the
runtime snapshot instead and surface the fleet:

- The chat page starts a 1s tea.Tick when a stream begins and keeps it
  running while a stream works or any background task is still running
  (tasks can outlive the turn), feeding sidebar.SetBackgroundAgents. The
  loop self-stops when idle, so ordinary single-agent turns pay nothing.
- The sidebar renders a "Background agents (N)" panel: one row per
  running task with a colored activity dot, the sub-agent's name in its
  accent color, and the elapsed run time. It keeps only running tasks
  (sorted by start time) and hides when empty.
- The Phase 2 delegation breadcrumb gains the deferred "+N background"
  count, fitted so the chain elides before the count is clipped.
- Export toolcommon.FormatDuration to reuse the shared elapsed-time
  formatter for the per-row run time.

Refs #3103
@aheritier aheritier force-pushed the feat/tui-background-agent-renderers branch from d432398 to 0ed315d Compare June 15, 2026 13:18
@aheritier aheritier force-pushed the feat/tui-background-agent-live-status branch from 8d9af10 to 9588e5f Compare June 15, 2026 13:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/agent For work that has to do with the general agent loop/agentic features of the app area/tools For features/issues/fixes related to the usage of built-in and MCP tools area/tui For features/issues/fixes related to the TUI kind/feat PR adds a new feature (maps to feat: commit prefix)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants