Skip to content

feat(ag-ui): subagent cards over AG-UI via native ACTIVITY events (F5)#668

Merged
blove merged 18 commits into
mainfrom
ag-ui-subagent-cards
Jun 16, 2026
Merged

feat(ag-ui): subagent cards over AG-UI via native ACTIVITY events (F5)#668
blove merged 18 commits into
mainfrom
ag-ui-subagent-cards

Conversation

@blove

@blove blove commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

Closes finding F5 from the Phase-2 capability audit: surface a live subagent card over the AG-UI transport. Spec: `docs/superpowers/specs/2026-06-15-ag-ui-subagent-cards-design.md`; plan: `docs/superpowers/plans/2026-06-15-ag-ui-subagent-cards.md`.

Approach — speak the protocol natively

AG-UI's wire is flat (no native subagent identity), and the LangGraph subgraph namespace the canonical adapter keys on doesn't survive the bridge. Rather than invent a private CUSTOM convention, this models a subagent run as the protocol's native ACTIVITY events (`activityType: "subagent"`) — so `@threadplane/ag-ui` consumes the protocol primitive and any compliant AG-UI server that emits subagent activities lights up the card, no private convention required. The generic activity infrastructure is reusable for future activity types.

Three layers + a cockpit capability

  • L3 graph (`examples/ag-ui/python`): the `research` tool emits `subagent_activity` intent (started → message-per-token with accumulated `text_so_far` → finished) via `adispatch_custom_event`; a `SubagentStreamHandler` taps the child subgraph's tokens. The child stays a compiled subgraph, so its tokens stay out of the main bubble.
  • L2 transport (owned, server-side): `ActivityEmittingAgent` overrides the bridge's `_dispatch_event` to convert each `subagent_activity` CUSTOM event into a native `ACTIVITY_SNAPSHOT`/`ACTIVITY_DELTA` — a pure, stateless 1:1 transform.
  • L1 library (`libs/ag-ui`): a generic activities store fed by `ACTIVITY_SNAPSHOT`/`ACTIVITY_DELTA` (reducer stays activityType-agnostic); `toAgent` projects `activityType==='subagent'` to `Agent.subagents` with stable per-id wrapper identity (pruned on reset). `chat-subagents` renders it unchanged.
  • Cockpit: new standalone `cockpit/ag-ui/subagents` capability (registry 4326/5326) + Railway deploy-bundle regen.

A T1 spike de-risked the load-bearing seam and corrected the emission API: this bridge drives the graph with `astream_events`, so `adispatch_custom_event` (on_custom_event → CUSTOM → `_dispatch_event`) is required — `get_stream_writer` surfaces only as RAW.

Consumer-visible surface change (`@threadplane/ag-ui`)

`AgUiAgent` now exposes a non-optional `subagents: Signal<Map<string, Subagent>>` (was inherited optional from `Agent`). `libs/ag-ui` has no CHANGELOG file (only `libs/chat` does, per repo convention) — flagging here so the next patch release documents the new `subagents` surface.

Verification

  • `lint/test/build` green for `ag-ui` + `chat`; example build green; both python suites `9 passed`; deploy-generator spec `30/30`, bundle idempotent.
  • examples/ag-ui e2e (durable-proof: subagent populated + settled + child-text isolation — the live card transits running→complete sub-frame under aimock instant replay, matching the cockpit/chat/subagents precedent); cockpit `ag-ui/subagents` e2e green.
  • Live-LLM Chrome smoke (real backend): captured 416 native `ACTIVITY_*` frames on the wire (`activityType: "subagent"`), the subagent populated and settled `complete` with its streamed child text, and the child summary stayed out of the main assistant bubble.

Out of scope (logged follow-ups)

Migrating the existing CUSTOM events (`state_update`/`on_interrupt`/`a2ui-partial`) to native `STATE_*`/HITL/`TOOL_CALL_ARGS` — a separate brainstorm; the L2 module is where they'd land. Plus the residual NG0956 during json-render spec assembly and a2ui icon-catalog support.

🤖 Generated with Claude Code

blove and others added 17 commits June 15, 2026 08:19
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Subagent cards over AG-UI via the protocol's native ACTIVITY events
(activityType=subagent), not a private CUSTOM convention. Three layers:
generic ACTIVITY reducer + subagent projection (libs/ag-ui); owned
server-side LangGraphAgent.run() transform mapping subagent intent to
ACTIVITY events; reference graph emits the intent. Reusable activity
infra; CUSTOM->native migration of state/interrupt/a2ui deferred.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…+ stateless transform

Override the bridge's 1:1 _dispatch_event to convert the subagent_activity
custom event to an ACTIVITY event; the handler sends accumulated text_so_far
(like A2uiPartialHandler's args_so_far) so the transform stays stateless.
run()-wrap / custom-route are documented fallbacks.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…(8 tasks)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… ACTIVITY (_dispatch_event)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ot get_stream_writer)

The installed ag_ui_langgraph bridge drives the graph with astream_events and
converts on_custom_event callbacks to AG-UI CUSTOM events; get_stream_writer
surfaces only as RAW. Correct L3 emission in spec + plan T2.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… value (review)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… via adispatch_custom_event

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…dling

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ble identity)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Unbounded growth + stale-binding if a tool-call-id is reused after a
RUN_STARTED reset. Prune wrappers not present in the current activities.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ld text isolated

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…iew)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…cards

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…vent, not get_stream_writer)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@vercel

vercel Bot commented Jun 16, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
threadplane Ready Ready Preview, Comment Jun 16, 2026 7:31am

Request Review

@blove blove enabled auto-merge (squash) June 16, 2026 07:27
@blove blove merged commit b42dac7 into main Jun 16, 2026
18 of 19 checks 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