Subagents
# Subagents
Kit supports multi-agent orchestration through both subprocess spawning and in-process subagents.
## Subprocess pattern
Spawn Kit as a subprocess for isolated agent execution:
```bash
kit "Analyze codebase" \
--json \
--no-session \
--no-extensions \
--quiet \
--model anthropic/claude-haiku-latest
```
Key flags for subprocess usage:
| Flag | Purpose |
|------|---------|
| `--quiet` | Stdout only, no TUI |
| `--no-session` | Ephemeral, no persistence |
| `--no-extensions` | Prevent recursive extension loading |
| `--json` | Machine-readable output |
| `--system-prompt` | Custom system prompt (string or file path) |
Positional arguments are the prompt. `@file` arguments attach file content as context.
## Built-in subagent tool
Kit includes a built-in `subagent` tool that the LLM can use to delegate tasks to independent child agents:
```
subagent(
task: "Analyze the test files and summarize coverage",
model: "anthropic/claude-haiku-latest", // optional
system_prompt: "You are a test analysis expert.", // optional
timeout_seconds: 300 // optional, max 1800
)
```
Subagents run as separate in-process Kit instances with full tool access (except spawning further subagents, to prevent infinite recursion). They can run in parallel.
## Extension subagents
Extensions can spawn subagents programmatically:
```go
result := ctx.SpawnSubagent(ext.SubagentConfig{
Task: "Review this code for security issues",
Model: "anthropic/claude-sonnet-latest",
SystemPrompt: "You are a security auditor.",
})
```
### Monitoring subagents from extensions
When the LLM (not the extension itself) spawns a subagent using the `subagent` tool, extensions can monitor its activity in real-time using three lifecycle event handlers:
```go
// Track active subagents and display their output
var subagentWidgets map[string]*SubagentWidget
func Init(api ext.API) {
// Subagent started by the main agent
api.OnSubagentStart(func(e ext.SubagentStartEvent, ctx ext.Context) {
// e.ToolCallID — unique ID for this subagent invocation
// e.Task — the task/prompt sent to the subagent
widget := NewWidget(e.ToolCallID, e.Task)
subagentWidgets[e.ToolCallID] = widget
ctx.SetWidget(widget.Config())
})
// Real-time streaming from subagent
api.OnSubagentChunk(func(e ext.SubagentChunkEvent, ctx ext.Context) {
// e.ToolCallID — matches the start event
// e.ChunkType — "text", "tool_call", "tool_execution_start", "tool_result"
// e.Content — text content
// e.ToolName — tool name (for tool chunks)
// e.IsError — true if tool result failed
widget := subagentWidgets[e.ToolCallID]
if widget != nil {
widget.AddOutput(e)
ctx.SetWidget(widget.Config())
}
})
// Subagent completed
api.OnSubagentEnd(func(e ext.SubagentEndEvent, ctx ext.Context) {
// e.Response — final response from subagent
// e.ErrorMsg — error message if subagent failed
widget := subagentWidgets[e.ToolCallID]
if widget != nil {
widget.MarkComplete(e.Response, e.ErrorMsg)
ctx.SetWidget(widget.Config())
delete(subagentWidgets, e.ToolCallID)
}
})
}
```
**Event structs:**
```go
type SubagentStartEvent struct {
ToolCallID string // Unique ID for this subagent invocation
Task string // The task/prompt sent to subagent
}
type SubagentChunkEvent struct {
ToolCallID string // Matches SubagentStartEvent.ToolCallID
Task string // Task description
ChunkType string // "text", "tool_call", "tool_execution_start", "tool_result"
Content string // For text chunks
ToolName string // For tool-related chunks
IsError bool // For tool_result chunks
}
type SubagentEndEvent struct {
ToolCallID string // Matches start event
Task string // Task description
Response string // Final response from subagent
ErrorMsg string // Error message if failed
}
```
This enables building monitoring widgets that display real-time activity from all subagents spawned by the main agent.
## Go SDK subagents
The SDK provides in-process subagent spawning:
```go
result, err := host.Subagent(ctx, kit.SubagentConfig{
Task: "Summarize the changes in this PR",
Model: "anthropic/claude-haiku-latest",
SystemPrompt: "You are a code reviewer.",
Timeout: 5 * time.Minute,
})
```
### Real-time subagent events
Use `SubscribeSubagent` to receive real-time events from LLM-initiated subagents (i.e., when the model uses the `subagent` tool). Register inside an `OnToolCall` handler using the tool call ID:
```go
host.OnToolCall(func(e kit.ToolCallEvent) {
if e.ToolName == "subagent" {
host.SubscribeSubagent(e.ToolCallID, func(event kit.Event) {
switch ev := event.(type) {
case kit.MessageUpdateEvent:
fmt.Print(ev.Chunk) // streaming text from child
case kit.ToolCallEvent:
fmt.Printf("Child calling: %s\n", ev.ToolName)
case kit.ToolResultEvent:
fmt.Printf("Child result: %s\n", ev.ToolName)
}
})
}
})
```
The listener receives the same event types as `Subscribe()` (`ToolCallEvent`, `MessageUpdateEvent`, `ReasoningDeltaEvent`, etc.) but scoped to the child agent's activity. Listeners are cleaned up automatically when the subagent completes.
If no listeners are registered for a tool call, no event dispatching overhead is incurred.