Skip to content

feat(ui): inline steer composer in the conversation viewer#121

Merged
tintinweb merged 3 commits into
masterfrom
feat/steer-conversation
Jun 30, 2026
Merged

feat(ui): inline steer composer in the conversation viewer#121
tintinweb merged 3 commits into
masterfrom
feat/steer-conversation

Conversation

@tintinweb

@tintinweb tintinweb commented Jun 30, 2026

Copy link
Copy Markdown
Owner

Steer agents from the conversation viewer

Adds an inline input box to the agent conversation viewer so you can steer a running subagent without leaving the view.

What

  • Press Enter in an agent's conversation viewer to open a composer, type a message, Enter to send. Esc or an empty submit just returns.
  • The message is delivered through the existing session.steer / pendingSteers path — it appears as a user message and redirects the agent after its current tool, identical to the steer_subagent tool.
  • Works in both the FleetView overlay and the /agents viewer.
  • Offered only while the agent is still running/queued (mirrors the existing x/stop affordance).

Why

steer_subagent already existed for the LLM, but a human watching an agent had no way to redirect it. This wires the same mechanism to the keyboard. Feedback is the steered message itself appearing in the live conversation you're already watching — no extra UI. Enter is the natural "interact" key, so the viewer stays modal and every existing shortcut (x/x stop, arrows/j/k scroll, q close) is untouched.

How

  • New AgentManager.steer(id, message) centralizes routing: live session → session.steer; session not yet created → queued onto pendingSteers, flushed on creation.
  • ConversationViewer gains an optional onSteer callback, a lazily-created single-line Input composer, and a canSteer() gate symmetric with isStoppable(). While composing, the input owns all keys; chrome reserves one extra row.
  • onSteer is wired to manager.steer(record.id, …) at both viewer call sites (fleet-list.ts, index.ts).
  • Idle footer reworked to actions-left / nav-right so the full scroll-key hint (↑↓ scroll · PgUp/PgDn or Shift+↑↓ · Esc close) stays visible down to 80-col terminals. The N lines · % position counter was dropped to make room.

Testing

Covered at three layers:

  • Managersteer() routing: unknown id, live session, pendingSteers queuing, finished-agent rejection.
  • Viewer — composer open (Enter) / send / cancel / empty-returns, status + missing-handler gating, width safety.
  • Wiring seam — drives the real ConversationViewer through the FleetList overlay (Enter → type → Enter) and asserts manager.steer is called with the right agent id.

Full suite green (675 passed / 4 skipped), typecheck + lint clean.

Notes

  • The N lines · % scroll-position readout hides if the width is too small.
  • The Enter binding is hardcoded, consistent with the existing x/q keys in the same component (not routed through the keybindings manager).
  • README updated (FleetView + Conversation viewer sections).

Render example

Idle — note Enter steer in the footer and the full scroll hint:


╭──────────────────────────────────────────────────────────────────────╮
│ ● Agent (twin)  audit auth · 3 tools · 12.0s (running)               │
│ ──────────────────────────────────────────────────────────────────── │
│ [User]                                                               │
│ Find and summarize the auth middleware.                              │
│ ───                                                                  │
│ [Assistant]                                                          │
│ Looking for the auth middleware now.                                 │
│   [Tool: grep]                                                       │
│ ───                                                                  │
│ [Result]                                                             │
│ src/auth/middleware.ts: requireUser()                                │
│                            ⋮                                          │
│ ──────────────────────────────────────────────────────────────────── │
│ Enter steer · x stop   ↑↓ scroll · PgUp/PgDn or Shift+↑↓ · Esc close │
╰──────────────────────────────────────────────────────────────────────╯

Composing — after pressing Enter and typing (█ is the live block cursor):

╭──────────────────────────────────────────────────────────────────────╮
│ ● Agent (twin)  audit auth · 3 tools · 12.0s (running)               │
│ ──────────────────────────────────────────────────────────────────── │
│ [User]                                                               │
│ Find and summarize the auth middleware.                              │
│ ───                                                                  │
│ [Assistant]                                                          │
│ Looking for the auth middleware now.                                 │
│   [Tool: grep]                                                       │
│ ───                                                                  │
│ [Result]                                                             │
│ src/auth/middleware.ts: requireUser()                                │
│                            ⋮                                          │
│ ──────────────────────────────────────────────────────────────────── │
│ > focus on the session cookie handling█                              │
│ ✎ steer                                      Enter send · Esc cancel │
╰──────────────────────────────────────────────────────────────────────╯

Press `i` in an agent's conversation viewer to open an input box, type a
message, Enter to send (Esc cancels). The message is delivered via the
existing session.steer / pendingSteers path — it appears as a user
message and redirects the agent after its current tool, same as the
steer_subagent tool. Works in both the FleetView overlay and /agents.

Adds AgentManager.steer(id, message) to centralize the running-vs-pending
routing, wires onSteer at both viewer call sites, and gates the affordance
on the agent still being running/queued (mirrors the x/stop affordance).

Tested at three layers: manager routing, composer interaction, and the
fleet-list wiring seam (real ConversationViewer → manager.steer).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

Shorter alternative if you prefer a one-liner subject + minimal body:

feat(ui): steer agents from the conversation viewer

`i` opens an input box to send a steering message to a running agent
(Enter send, Esc cancel), delivered via session.steer. Adds
AgentManager.steer() and tests across manager, viewer, and wiring.
  Press Enter in an agent's conversation viewer to open an input box, type
  a message, Enter to send (Esc or an empty submit returns). The message is
  delivered via the existing session.steer / pendingSteers path — it appears
  as a user message and redirects the agent after its current tool, same as
  the steer_subagent tool. Works in the FleetView overlay and /agents.

  Adds AgentManager.steer(id, message) to centralize running-vs-pending
  routing, wires onSteer at both viewer call sites, and gates the affordance
  on the agent still being running/queued (mirrors the x/stop affordance).
  The idle footer keeps the full scroll-key hint by splitting into
  actions-left / nav-right.
@tintinweb tintinweb marked this pull request as ready for review June 30, 2026 19:46
@tintinweb tintinweb merged commit c32beeb into master Jun 30, 2026
1 check passed
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.

1 participant