feat(plugin): sub-agent (Invoke Agent) CLI panel#486
Merged
Conversation
added 7 commits
June 17, 2026 10:48
… collection Probe the real mcp package (including client.session/sse/streamable_http) before falling back to MagicMock stubs, so plugin test collection works in both full dev envs and slim CI images without breaking pydantic_ai.mcp imports.
Harden the console spinner against concurrent teardown of its Rich Live display so racing stop/refresh calls during streaming no longer raise.
Detect image file paths on the clipboard and surface them so they can be attached to a message, including platform-specific handling.
Clarify that load_image should only be used for concrete filesystem paths the user provided, not for guessed paths when an image is already attached to the conversation.
Append the advisor-tool-2026-03-01 beta header when a model config sets advisor_tool_enabled. No model enables it yet, so the flag stays inert until explicitly turned on.
Bridge the agent cancel callback into the shell SIGINT handler so one Ctrl+C while shells are running kills the shells and then cancels every agent/sub-agent task, instead of only stopping the current batch of shells. The shell handler requests a forced cancel (it has already killed the shells, so the anti-orphan guard no longer applies) and tears down any live status panels first so the cancel banner is not immediately repainted. The cancel callback is registered for the duration of a run and cleared in a finally block.
Ship a built-in plugin that renders a live INVOKE AGENT status tree for sub-agent runs, with stream-event coalescing and resume repaint support. The plugin loader now skips a user plugin when a built-in (or project) plugin of the same name has already loaded, preventing duplicate callback registration. Includes a regression test for the duplicate-skip behavior.
…ing for subagent models
AndrewTilson
approved these changes
Jun 17, 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.
feat(plugin): sub-agent (Invoke Agent) CLI panel
The whole PR
Consolidates the sub-agent status UX into a single built-in plugin
(
code_puppy/plugins/subagent_panel/) — replacing the older splitcp_steer_overlay+cp_subagent_statusexperiment — and hardens theinterrupt/teardown path around it so a single Ctrl+C cleanly stops a whole
sub-agent swarm instead of forcing you to mash it once per nested agent.
Along the way it fixes a spinner teardown race, makes the clipboard image path
Windows-aware, and gates the spinner's raw ANSI writes on
isatty()soredirected / non-VT consoles (including legacy Windows shells) don't get escape
garbage. macOS interactive behavior is byte-for-byte unchanged.
Why
the spinner's Rich
Live(repaints ~20×/sec). On Ctrl+C the cancel bannerprinted once and the next Live frame painted the panel right back over it, so
a single press looked like it did nothing. Worse, the SIGINT handler killed
shells before tearing down the panel — and
_kill_process_groupblocks upto
2s per nested shell — so in a deep swarm the UI froze for N×2s whilethe panel kept repainting. Fix: tear down the Live panel + emit the banner
before the slow blocking kill, then force-cancel the whole agent tree.
reach the active run's cancel callback, so it only ever killed the current
batch of shells. Added a
register_agent_cancel/clear_agent_cancelbridge(set at run start, cleared in
finally) so one Ctrl+C collapses the entireagent/sub-agent swarm.
pause()/stop()can nullself._livefromanother thread between the refresh guard and the refresh call — and the new
cancel path triggers exactly that. Snapshot
_livelocally + guard the frame.ImageGrab.grabclipboard()returning a file-path list (the Windows / Finder behavior), and the
spinner's raw
\x1b[Kwrites are gated onsys.stdout.isatty().Files changed (22)
code_puppy/plugins/subagent_panel/(new plugin: register_callbacks, state, coalesce_patch, resume_repaint, README, init)code_puppy/tools/command_runner.py_tear_down_live_panels)code_puppy/messaging/spinner/console_spinner.pyisatty()ANSI gate)code_puppy/command_line/clipboard.pycode_puppy/agents/_run_signals.pyforce=cancel param, panel teardown)code_puppy/agents/_runtime.pycode_puppy/plugins/__init__.pycode_puppy/model_factory.pyadvisor_tool_enabledbeta header gate)code_puppy/tools/image_tools.pytests/**(swarm-cancel, spinner races, clipboard file-list, plugin-skip, image-tool guardrail, pause-guard, conftest)~2,134 insertions across 22 files.
Tests
tests/tools/test_command_runner_swarm_cancel.py— kill-before-cancelordering (
teardown → banner → kill → cancel), one-shot dedupe on mashedpresses, headless fallback, force-cancel, panel-teardown resilience.
tests/test_console_spinner_races.py— live-snapshot teardown race,pause()clears_liveeven whenstop()raises, frame-skip on stopped Live.tests/command_line/test_clipboard_image_files.py—ImageGrabfile-list path + non-image suffix rejection on the live helper.
tests/plugins/test_duplicate_user_plugin_skip.py— builtin namesuppresses same-named user plugin.
tests/tools/test_image_tool_guardrails.py— load-image docstringdiscourages guessed paths.
tests/agents/test_run_signals_pause_guard.py— cancel tears downthe panel before the banner; no teardown when no sub-agents.
110 passedacross the affected modules).Manual validation
each holding a
sleep). Press Ctrl+C once → banner shows instantly, thepanel vanishes, and the whole tree cancels (no per-agent mashing).
←[Kescape garbage.os.killpg/SIGKILLevaluated —taskkill /F /Tearly-returns; clipboard file-list path works for ScreenClip paste).
Platform status
isatty()gate + clipboardfile-list make the new code Windows-clean).
History
This is the consolidation of the earlier
cp_steer_overlay+cp_subagent_statusplugins into one
subagent_panelplugin. Key pivots:before hiding the panel; in a deep swarm the blocking per-process kill froze the
UI for seconds while the panel repainted. Reordered to hide + announce before
the slow kill.
register/clear_agent_cancelbridge so Ctrl+C reaches every sub-agent, not justthe current shell batch.
capture_image_file_to_pending. Its only prod consumer wasthe removed
cp_steer_overlay; the reusableget_image_file_as_pnghelper stayslive via
get_clipboard_image's file-list branch.advisor_tool_enabledis intentionally dormant (no config sets it yet) —scaffolding for a future Anthropic beta opt-in, gated so it stays inert.
PR Type
Enhancement, Bug fix
Description
Consolidate sub-agent status UX into built-in plugin with live two-line panel
Fix Ctrl+C to cleanly stop entire sub-agent swarm instead of requiring N presses
Harden spinner teardown race and gate ANSI writes on
isatty()for WindowsSupport Windows clipboard image-file paths and add steer overlay image paste
Diagram Walkthrough
File Walkthrough
11 files
Add force-cancel parameter to bypass shell-running guardRegister and clear agent cancel callback around runSupport Windows image-file paths and extract PNG helpersAdd dormant advisor-tool beta flag supportSkip user plugins when built-in name already loadedNew plugin module docstring and exportsBatch sub-agent stream events to prevent steer lagInstall monkeypatches for live panel rendering and stateRepaint panel after Ctrl+T steer overlay resumesThread-safe registry of active sub-agents and statusImplement responsive Ctrl+C swarm-cancel with panel teardown1 files
Fix teardown race and gate ANSI writes on isatty2 files
Documentation for sub-agent panel pluginAdd guardrail to discourage guessed image paths7 files
Add tests for cancel panel teardown and force parameterNew tests for Windows image-file clipboard handlingImprove MCP mock setup to prefer real packageNew regression test for duplicate plugin skip behaviorNew tests for spinner teardown race conditionsNew comprehensive tests for Ctrl+C swarm-cancel behaviorNew test for load-image tool docstring guardrails