feat(desktop): add MCP settings and live subagent tree

Surface configured MCP servers in Settings with JSON edit/save and a gateway-backed reload action so users can manage tool servers without falling back to slash commands.

Track live subagent gateway events in a desktop store, show active subagent counts in the Agents statusbar item, and replace the Agents overlay stub with a live spawn tree for the active session.
This commit is contained in:
Brooklyn Nicholson
2026-05-13 12:12:12 -04:00
parent ca2c3d4ab4
commit 17e86dddc7
10 changed files with 620 additions and 12 deletions
@@ -31,6 +31,7 @@ import {
setCurrentUsage,
setTurnStartedAt
} from '@/store/session'
import { clearSessionSubagents, upsertSubagent } from '@/store/subagents'
import { recordToolDiff } from '@/store/tool-diffs'
import type { RpcEvent } from '@/types/hermes'
@@ -59,6 +60,14 @@ interface QueuedStreamDeltas {
}
const STREAM_DELTA_FLUSH_MS = 16
const SUBAGENT_EVENT_TYPES = new Set([
'subagent.spawn_requested',
'subagent.start',
'subagent.thinking',
'subagent.tool',
'subagent.progress',
'subagent.complete'
])
// Anonymous progress events that carry todos but no name still belong to the
// todo stream; named todo events are obviously routed there too.
@@ -506,6 +515,7 @@ export function useMessageStream({
}
flushQueuedDeltas(sessionId)
clearSessionSubagents(sessionId)
if (isActiveEvent) {
triggerHaptic('streamStart')
@@ -575,6 +585,10 @@ export function useMessageStream({
if (typeof payload?.inline_diff === 'string' && payload.inline_diff.trim()) {
recordToolDiff(payload.tool_id || payload.name || '', payload.inline_diff)
}
} else if (SUBAGENT_EVENT_TYPES.has(event.type)) {
if (sessionId && payload) {
upsertSubagent(sessionId, payload as Record<string, unknown>, event.type === 'subagent.spawn_requested')
}
} else if (event.type === 'clarify.request') {
if (!isActiveEvent) {
return