Conversation
C-K-Loan
marked this pull request as draft
August 10, 2026 23:45
C-K-Loan
force-pushed
the
pr/spawn-agent-core
branch
from
August 11, 2026 03:46
399a7f3 to
ac0dbad
Compare
This was referenced Aug 11, 2026
C-K-Loan
force-pushed
the
pr/spawn-agent-core
branch
from
August 11, 2026 05:03
ac0dbad to
62971ee
Compare
Contributor
Author
|
Superseded by #416, which includes everything here (spawn_agent core) plus background execution, list_agents/check_agent/steer_agent, and concurrent batching. Closing in favor of that one. |
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
Depends on #267 (Adopt MCP tools via a consent-gating adapter) — this branch is stacked on top of it, so the diff includes #267's commit until that one merges.
Adds
spawn_agent, a tool that lets the model delegate a focused sub-task to a fresh instance of itself — a coordinator can fan work out to sub-agents instead of doing everything serially in one conversation. Each sub-agent gets its own tool-call loop with the same tools and MCP access as the coordinator, can optionally run on a different model for a call, and streams its tokens live into a nested cell in the transcript rather than jumping from a spinner straight to finished text.How it works
Dispatch. Within a single round, any
spawn_agentcalls the model makes are collected rather than run inline, then dispatched together after every other tool call in that round has already resolved. All pending spawns in the batch (capped at 5 per round) run concurrently via a realTaskGroup. Each child's outcome — success, failure, or its own cancellation — is captured as aResultinside the task instead of being rethrown, so one sub-agent failing or being cancelled can't discard the others' already-settled results.Model resolution. A sub-agent defaults to the coordinator's own model; a call can optionally request a different one explicitly. Same model as the coordinator → no reload, the request just hits the already-resident weights. A different, explicit model → the server (as it exists today) holds exactly one resident text-generation model, so a mismatch evicts whatever's loaded and loads the requested one; the coordinator's next round then evicts it right back to reload its own — the same reload cost
switch_modelalready pays, not a new regression. That eviction decision is based on live, per-device memory (Apple's recommended GPU working-set ceiling minus what's actually active right now), not a static config value. A separate, independent PR (memory-aware model cache) lets multiple models coexist when there's room instead of always evicting on any mismatch.Isolation. A sub-agent is a fresh, private in-memory message array (
ChatAgentLoop), not a server-side session — completions are stateless and resend the full message list every request, so two sub-agents' histories never interact regardless of what model they're on.Consent gating. A sub-agent can itself call
switch_modelor an MCP tool, and both still require the same human approval via the same consent-card UI as the coordinator's own tool calls — the model is never in that loop. This uses a separate implementation from the coordinator's own inline consent handling (not shared code) — reusing the coordinator's proven loop was judged higher regression risk than a focused reimplementation. Along the way, also fixes the consent card itself: it was showingswitch_model's hardcoded description ("The model wants to switch to...") for every consent-gated call, including real MCP ones — now an MCP call's card names the actual tool and server (e.g. "The model wants to run search (websearch).").Streaming UI. A sub-agent's nested transcript updates at a capped ~15fps cadence per sub-agent (the same rate as token streaming), so several concurrent sub-agents can't saturate the main thread with SwiftUI diff/layout passes. Any buffered-but-not-yet-flushed content is force-flushed the moment a sub-agent finishes, so nothing streamed right before completion is lost to the throttle window. A completed sub-agent's step cell also stays expanded instead of auto-collapsing, so its work stays visible while the coordinator continues.
Cancellation. Stopping generation (or one sub-agent's own consent being cancelled) cascades to every other still-queued tool call and every other pending spawn in the same round, rather than leaving orphaned work running.
Repetition-loop detection. A degenerate-token-repetition detector catches a model spiraling into repeating the same phrase indefinitely during streaming — a failure mode that showed up in practice once multiple sub-agents could return synthesis-worthy but low-quality results in the same round.
Blocking, by round. All spawns within one round run concurrently with each other, but the coordinator's own next turn always waits for the entire batch to settle before continuing — a tool call's result has to be back in the message history before the model can meaningfully take its next turn. See Follow-up below for a fire-and-forget alternative.
Demos
Two sub-agents running concurrently, streaming live, both step cells staying expanded after they settle:
Full-quality .mp4
Two sub-agents each independently using the web-search MCP tool, own consent gating, concurrent approval, real results:
Full-quality .mp4
Follow-up in this draft
A sub-agent's nested step cell currently stays blank while it's reasoning/thinking, only showing anything once a tool call appears. We'll push an update to this same PR that streams a sub-agent's own thinking live, plus tighter timing between concurrent consent approvals, along with a re-recorded demo — before this comes out of draft. The current clips still accurately show the core functionality (concurrent spawn, concurrent MCP consent, real results), just not that specific UI polish yet.
Future work (not planned for this PR)
spawn_agentcurrently always blocks the coordinator's round until the whole batch of sub-agents in that round finishes — they run concurrently with each other, but the coordinator can't do anything else until all of them settle. A fire-and-forget mode (spawn returns immediately, coordinator's turn ends, sub-agent result gets injected back into the transcript asynchronously once done) would let the coordinator/user keep interacting while long-running sub-agents work in the background. Real design surface (persistent tracking beyond one round, async result delivery, UI for in-flight background spawns) — a genuinely separate future feature, not attempted here.Test plan