feat: surface CLI subagent/skill events in chat + /agent command#445
feat: surface CLI subagent/skill events in chat + /agent command#445
Conversation
PureWeen
left a comment
There was a problem hiding this comment.
Review: PR #445 — feat: surface CLI subagent/skill events in chat + /agent command
⚠️ Breaking change: /reflect command removed
The diff removes the /reflect case from the command switch and replaces it with /fleet. The HandleReflectCommand method is deleted from Dashboard.razor. The InsertReflectCommand shortcut button is also removed from ExpandedSessionView.razor.
This is a significant behavioral removal. /reflect was the user-facing entry point for reflection cycles. However:
- The underlying reflection cycle infrastructure (
StopReflectionCycle,ReflectionCyclemodel,OrchestratorReflectmode) still exists and is referenced in both files - The reflection pill UI (showing iteration progress, stop button) still renders in
ExpandedSessionView.razor /reflectis removed from autocomplete inindex.htmland from the/helptext
The user can no longer start a reflection cycle via /reflect, but the existing infrastructure for running them (e.g., via multi-agent orchestrator mode) remains. If this removal is intentional, the leftover ReflectionCycle UI and references should be cleaned up. If it was accidental, /reflect needs to be re-added.
✅ Event handling — correct
The new subagent/skill event cases are well-structured:
SubagentSelectedEvent: SetsActiveAgentName/ActiveAgentDisplayNameonAgentSessionInfoand adds a system message. Correct.SubagentDeselectedEvent: Clears both fields. Correct.SubagentStartedEvent: Adds a▶️ message. The description is appended only when non-empty. Correct.SubagentCompletedEvent/SubagentFailedEvent: ClearsActiveAgentNameonly when the name matches (OrdinalIgnoreCase). This is a nice correctness guard against stale state from rapid sequential agent invocations.SkillInvokedEvent: Adds ⚡ message with plugin name. Correct.CommandsChangedEvent: Added asTimelineOnly— a reasonable default for an event that has no user-visible meaning yet.
All cases use Invoke(() => ...) for correct UI-thread marshaling. All use NotifyStateChangedCoalesced() consistently with surrounding code.
✅ /agent command — correct
HandleAgentCommand in Dashboard.razor correctly handles three cases:
- Empty arg → list agents (SDK API + local discovery)
deselect→ callsDeselectAgentAsync- Any other arg → calls
SelectAgentAsync
The service methods (ListAgentsFromApiAsync, SelectAgentAsync, DeselectAgentAsync) all have proper null guards and exception handling (catch-and-log). ✅
✅ /fleet command — correct but minimal
HandleFleetCommand calls StartFleetAsync which calls state.Session.Rpc.Fleet.StartAsync. The "fleet started" message fires before the API result is confirmed:
session.History.Add(ChatMessage.SystemMessage($"🚀 Starting fleet for: *{arg}*"));
var started = await CopilotService.StartFleetAsync(sessionName, arg);
session.History.Add(ChatMessage.ErrorMessage("Failed to start fleet mode."));This is a minor UX issue — "Starting fleet..." appears briefly even on failure. Low severity, but worth noting.
✅ Active agent badge — correct
The badge in ExpandedSessionView.razor uses Session.ActiveAgentDisplayName ?? Session.ActiveAgentName with a null guard. CSS uses var(--type-footnote) — consistent with font-sizing conventions. ✅
Minor: AgentSessionInfo.ActiveAgentName not persisted
ActiveAgentName/ActiveAgentDisplayName are not included in session persistence/bridge sync. After an app restart or remote-mode reconnect, the badge won't show. This is acceptable (the agent state is runtime-only and the SDK will re-emit events on reconnect), but worth documenting.
Summary
| Issue | Severity |
|---|---|
/reflect removed — breaking change, or leftover UI needs cleanup |
High — needs explicit decision |
| "Starting fleet" message before API confirms | Low |
ActiveAgentName not persisted |
Low/Info |
All new event handling, API methods, and UI additions are correct. The /reflect removal is the only issue requiring a decision before merging.
|
Thanks for the thorough review @PureWeen! On On fleet message before API confirms: Fixed in 00bff88 — the 🚀 message now only appears after On |
- Show 🤖/▶️ /✅/❌ system messages when CLI subagents activate (SubagentSelectedEvent, SubagentStartedEvent, SubagentCompletedEvent, SubagentFailedEvent, SubagentDeselectedEvent) - Show ⚡ system message when SkillInvokedEvent fires - Update SdkEventMatrix: subagent + skill events → ChatVisible - Handle CommandsChangedEvent: store CLI commands in AgentSessionInfo.CliSlashCommands - Add ActiveAgentName/ActiveAgentDisplayName to AgentSessionInfo - Show active CLI agent badge in ExpandedSessionView info panel - Add /agent slash command: list agents (local + SDK API) or select/deselect - Wire AgentApi: ListAgentsFromApiAsync, SelectAgentAsync, DeselectAgentAsync - Add /agent to index.html autocomplete + SlashCommandAutocompleteTests Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Remove CommandsChangedEvent case handler (type not exported from SDK 0.2.0) SdkEventMatrix string entry remains for future use when SDK exposes the type - Replace session.AvailableAgents (nonexistent property) with CopilotService.DiscoverAvailableAgents(session.WorkingDirectory) in /agent handler Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
CommandsChangedEvent handler was removed (SDK 0.2.0 doesn't export the type), so this field was never set. Remove to avoid dead code. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Add /fleet <prompt> slash command that calls FleetApi.StartAsync() via the SDK, enabling parallel subagent execution. Shows a system message on success/failure. Added to index.html autocomplete and /help output. Added CopilotService.StartFleetAsync() wrapping state.Session.Rpc.Fleet.StartAsync(). - Remove /reflect slash command: remove case handler, delete HandleReflectCommand() method (~130 lines), remove from COMMANDS array and /help. The underlying reflection machinery (ReflectionCycle model, StartReflectionCycleAsync, OrchestratorReflect multi-agent mode, all reflection UI in ChatMessageItem/SessionCard) is UNTOUCHED -- reflection still works via multi-agent orchestration. - Update SlashCommandAutocompleteTests: replace /reflect with /fleet in both the minimum-commands and the hasArgs assertions. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Remove 'Reflect' button from ExpandedSessionView toolbar - Remove dead InsertReflectCommand() method - Remove '/reflect stop' suggestion from context-full warning message Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Only show 🚀 success message after StartFleetAsync returns true, show error on false. Avoids premature success indicator on failure. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…leet guard - SubagentCompleted/Failed: always clear ActiveAgent state even when displayName is empty (prevents permanently stuck badge) - DeselectAgentAsync: return bool, surface failures in /agent deselect - StartFleetAsync: reject when IsProcessing to prevent concurrent turns Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
00bff88 to
09bc16e
Compare
Summary
Improves Copilot CLI feature parity by making the CLI's built-in agent system visible to PolyPilot users.
What's new
1. Subagent activity shown in chat
When the Copilot CLI automatically invokes a specialized agent (e.g. code-review, security-review) in response to a prompt, PolyPilot now shows system messages in the chat thread:
Agent: **Code Review**— agent selected by CLIStarting agent: **Code Review** — description— agent invocation beganAgent completed: **Code Review**— successAgent failed: **Code Review**: error— failure with error detailThe active agent name/display name is also tracked on
AgentSessionInfoand cleared on completion/deselect.Previously these events fell through to
LogUnhandledSessionEventwith no user-visible feedback.2. SkillInvokedEvent shown in chat
When the CLI invokes a skill plugin, a ⚡ system message is shown:
Skill: **skillName (plugin)**.3. Active agent badge in session info panel
When a subagent is active, a
🤖 AgentNamebadge appears in theExpandedSessionViewinfo strip alongside the model selector.4. /agent slash command
New
/agentcommand with three modes:/agent— lists available agents from both local discovery (.github/agents/,.claude/agents/,.copilot/agents/) and the live SDKAgentApi.ListAsync()/agent <name>— selects an agent viaAgentApi.SelectAsync(name)/agent deselect— deselects the current agent viaAgentApi.DeselectAsync()Added to autocomplete in
index.htmland updated/helpoutput.SdkEventMatrix updates
SubagentSelectedEvent,SubagentDeselectedEvent,SubagentStartedEvent,SubagentCompletedEvent,SubagentFailedEvent:TimelineOnly→ChatVisibleSkillInvokedEvent:TimelineOnly→ChatVisibleTests
SlashCommandAutocompleteTestsupdated to include/agentin thewithArgslist