Skip to content

feat(tui): async delegation view — docked agents panel + live steering - #8

Closed
JoaoMarcos44 wants to merge 2 commits into
mainfrom
feat/async-delegation-view
Closed

JoaoMarcos44 wants to merge 2 commits into
mainfrom
feat/async-delegation-view

Conversation

@JoaoMarcos44

@JoaoMarcos44 JoaoMarcos44 commented Jul 24, 2026

Copy link
Copy Markdown
Owner

Summary

Adds an inline, docked agents panel above the composer plus a live steering channel so a running subagent can be redirected by id (@b7c2 switch approach) without killing it. Watch background delegations next to the conversation; steer one mid-flight.

Closes #9.

Motivation

Background delegation and the subagent tree were disconnected: the parked turn was a one-line hint, and the rich tree was a full-screen /agents modal that hid the conversation. There was no way to watch background agents inline, and the only lever on a wrong-headed background fixer was x (kill). This adds visibility and a steering lever.

~80% of the work is reprojection of data that already exists. The one genuinely new capability — inbound steering — reuses the child's existing AIAgent.steer() drain and iteration-boundary interrupt check, so no new core-loop code is introduced.

What changed

Phase 1 — docked read-only panel (low risk)

  • delegation.async_list RPC: pure read projection of the async-delegation registry (interrupt_fn already stripped server-side).
  • $asyncDelegations store + applyAsyncList; the existing 5s gateway tick populates it alongside delegation.status.
  • agentsPanel.tsx: merges live in-turn subagents (turnStore.subagents, freshest tool + elapsed) with background/async rows into one list. Renders nothing when there are no agents.
  • Extracted STATUS_GLYPH into a shared module so the panel and the /agents overlay never diverge visually.
  • Mounted above the composer next to LiveTodoPanel; header click collapses; ^a opens the full tree.

Phase 2 — live steering (@id)

  • send_to_subagent mirrors interrupt_subagent: looks the child up in the live registry and calls AIAgent.steer(text). The child drains it at its next iteration boundary and appends it as a delimited out-of-band user turn on the last tool message — never spliced mid-tool — so role alternation and the prompt cache stay intact.
  • subagent.send RPC.
  • Composer routes @<id> text to steering only when the token resolves to a live subagent (resolveSteerTargetId), so an ordinary prompt like email @john later falls through to a normal turn untouched. Feedback toast: delivered → @b7c2 / @b7c2 already finished.

Design decisions (deliberate scope calls)

  • No per-session ownership guard on send_to_subagent — the live registry carries no session key and interrupt_subagent is already unguarded (holding the id is authority). Same trust model; a guard would require a new registry field. Noted as follow-up.
  • Panel actions = read-only + open-tree. Full keyboard kill/inject would hijack the global input router (high risk). Kill stays reachable via the existing /agents overlay; the docked panel delivers the core value — visibility — without that risk.

Testing

53 new tests, all green.

Suite Covers Tests
tests/test_async_delegation_view.py RPC shapes · send_to_subagent · 50-thread concurrency · role-legality · status transitions 15
agentRows.test.ts merge logic · running/done counts · elapsed clocking · status aliases · fallbacks 16
subagentSteer.test.ts @id parsing · id/prefix resolution · ambiguity refusal · normal-text passthrough 12
agentsPanel.test.tsx header counts · empty state · collapse · result ready ⏎ · ^a tree click 6
delegationStoreAsync.test.ts applyAsyncList populate · null tolerance · replace-not-append · end-to-end 4

Regression: frontend 1385 passed; backend view suite 15/15. The pre-existing failures (terminal*/editor/memory TS suites and 19 test_delegate.py cases) were confirmed identical with this branch reverted — a Python 3.14 _initializer stdlib incompatibility and unrelated suites, with zero references to the new code in any traceback. Build ✓ · lint ✓ · typecheck (touched files) clean.

Files (18 changed · +1101 / −25)

Backend: tools/delegate_tool.py, tui_gateway/server.py
Frontend: components/agentsPanel.tsx · lib/agentRows.ts · lib/subagentGlyph.ts · lib/subagentSteer.ts · app/delegationStore.ts · app/turnStore.ts · app/useSubmission.ts · app/createGatewayEventHandler.ts · components/agentsOverlay.tsx · components/appLayout.tsx · gatewayTypes.ts
Tests: the 5 suites above.


🖼️ Architecture at a glance

Async Delegation View — technical architecture: TUI composer → resolveSteerTargetId → subagent.send / delegation.async_list RPCs → AIAgent.steer() queue drained at the tool iteration boundary, preserving role alternation and prompt cache, feeding the LiveAgentsPanel status glyphs

Phase 1 (read-only docked panel):
- delegation.async_list RPC projects the async registry to the TUI
- $asyncDelegations store + applyAsyncList; 5s gateway tick populates it
- agentsPanel.tsx merges live in-turn subagents with background async rows
- extract shared STATUS_GLYPH so panel and /agents overlay never diverge
- mount above the composer next to LiveTodoPanel; ^a opens the full tree

Phase 2 (live steering, @id):
- send_to_subagent reuses the child's existing AIAgent.steer() drain, so the
  steer lands as a fresh out-of-band user turn on the last tool message at the
  iteration boundary — role alternation and prompt cache stay intact. No new
  core-loop code.
- subagent.send RPC; composer routes "@<id> text" only when the token resolves
  to a live subagent, so ordinary prompts fall through untouched.

Tests: 53 new (backend 15 incl. 50-thread concurrency + role-legality;
frontend 38 across merge logic, steer resolution, panel view, store).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@JoaoMarcos44

Copy link
Copy Markdown
Owner Author

Superseded by the upstream PR NousResearch#70899 (opened against NousResearch/hermes-agent so maintainers can review/edit/merge). Consolidating there.

JoaoMarcos44 pushed a commit that referenced this pull request Aug 18, 2026
docs(relay): explain canonical operation migration
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.

Async Delegation View — docked agents panel + live steering

1 participant