feat(runtimes): maf subagent delegation demo with queue-merged SUBAGENT_* emission - #957
Merged
Conversation
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…alist The expense copilot gains a tool-less policy_researcher specialist Agent and an async research_policy tool that streams the specialist's deltas through the delegation_* emitter helpers (SUBAGENT_STARTED / attributed TEXT_MESSAGE_* / SUBAGENT_FINISHED|ERROR, ids derived from the delegation toolCallId with a generated sub-<hex8> fallback). The helpers are pure no-ops outside a wrapped run; the queue-merge run wrapper that puts these events on the wire lands in the next commit. Adds the pytest dev-group and asyncio_mode infra plus registration tests (no live model calls). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… at the bridge boundary SubagentEmittingAgent wraps AgentFrameworkAgent.run (the exact method the FastAPI endpoint consumes) with the pump-task merge queue the Task-0 spike named: a task drains the inner bridge generator into an asyncio.Queue the ContextVar shares with the tool body, so a delta enqueued mid-tool wakes the outer consumer immediately (live interleaving; a drain-between-yields design would batch every child delta until the tool returned). The pump records each TOOL_CALL_START toolCallId by name, which is what makes current_tool_call_id deterministic before the tool body runs. Pump exceptions propagate to the consumer; consumer break / client disconnect cancels and awaits the pump and closes the inner generator — no orphaned tasks. server.py mounts the wrapped agent. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ocstring The name→tid map overwrote on a same-tool double call (reachable: MAF runs multi-tool batches concurrently via asyncio.gather and the bridge streams all TOOL_CALL_STARTs first), making both bodies share the second tid and one delegation. The correlation map is now a per-name FIFO: the pump appends each TOOL_CALL_START id, current_tool_call_id pops the oldest, so every invocation gets its own tid, _Delegation, and message id (new identity-separation test). Also corrects the module docstring — the measured wire shows TOOL_CALL_END arrives AFTER the tool returns (SUBAGENT_* lands between ARGS and END), so correlation relies on START alone — and aligns the unit-test fake's event order with that wire. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Three fixture entries for the delegation turn (primary tool call, specialist matched on its 'expense-policy researcher' system prompt, hasToolResult continuation ordered first) plus a spec asserting the delegation renders as one <chat-subagent-card> naming policy_researcher. Requires the cherry-picked toolCallId anchoring fix — AG-UI native SUBAGENT_* keys the adapter map by subagentRunId (`<toolCallId>-sub`). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Live run (real key) on :5330/:4330 driven headlessly with Playwright: the delegation prompt renders an inline <chat-subagent-card> anchored to the research_policy call, and 150ms innerText polling shows the specialist's transcript growing monotonically (66 -> 622 chars) while the badge reads running, confirming progressive delta rendering. Screenshot follows the Strands e2e/manual convention. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
blove
enabled auto-merge (squash)
September 2, 2026 16:56
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Contributor
This was referenced Sep 2, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Third PR of the runtime-subagents arc (adapter #955, Strands #956).
policy_researcherspecialist (agents-as-tools; handoff autonomous mode avoided as experimental).src/subagent_emitter.pywrapsAgentFrameworkAgent.runwith a ContextVar-carried queue merge: a pump task drains the bridge generator into the queue while the tool body enqueuesSUBAGENT_STARTED→ token-granular attributed child deltas →SUBAGENT_FINISHED(SUBAGENT_ERROR on failure). Composition-built subclass shares config + approval store, so the interrupt/resume flows ride the same wrapped instance untouched.TOOL_CALL_STARTid in a per-tool-name FIFO; each delegation pops its own id — safe under MAF's concurrent tool batches (identity-separation test included). Clean shutdown proven (pump cancelled+awaited, zero pending tasks, ContextVar reset).docs/wire-capture-subagents.md: pre-emitter capture (specialist invisible on the wire), post-emitter capture (100 streamed child deltas), and live browser verification — card text grows monotonically whilerunning(two runs sampled), screenshot committed.deployments/ag-ui-devregenerated.Testing
🤖 Generated with Claude Code