-
-
Notifications
You must be signed in to change notification settings - Fork 0
Subagents
M31 Autonomous (M31A) supports parallel subagents that run in isolated git worktrees. Subagents allow the LLM to spawn child agents for independent tasks, each with their own dispatcher and working directory.
Parent Agent (main TUI session)
│
├── Subagent 1 (isolated worktree, own dispatcher)
├── Subagent 2 (isolated worktree, own dispatcher)
└── Subagent 3 (isolated worktree, own dispatcher)
Each subagent:
- Gets its own git worktree (created via
git worktree add) - Gets its own tool dispatcher (with independent permissions)
- Runs in a background goroutine
- Reports results via an event channel
Source: internal/tools/subagent/manager.go
type Dependencies struct {
WorkDir string
Registry *provider.Registry
ActiveModel *types.ModelInfo
Logger *slog.Logger
Worktrees WorktreeProvider
NewDispatcher DispatcherFactory
}The Manager:
- Tracks active subagents by ID
- Provides
Spawn()to create new subagents - Provides
Cancel()andShutdown()for cleanup - Exposes an
Events()channel for TUI integration
Source: internal/tools/subagent/loop.go
Each subagent runs its own agent loop:
- Builds system prompt and tool definitions
- Sends messages to the LLM
- Parses tool calls from the response
- Executes tools via its own dispatcher
- Feeds results back to the LLM
- Repeats until the task is complete or context is exhausted
Source: internal/tools/subagent/loop_parse.go
Parses LLM responses for tool calls, handling both native tool_call chunks and text-embedded tool invocations.
Source: internal/tools/subagent/worktree.go
Each subagent gets an isolated working directory:
git worktree add .m31a/worktrees/agent-<id> -b m31a/agent-<id>Benefits:
- File edits don't conflict between parallel agents
- Each agent can commit independently
- Cleanup on completion:
git worktree remove+ branch deletion
On startup, Sweep() removes leftover worktrees and branches from previous crashes:
- Removes orphaned
.m31a/worktrees/directories - Cleans up
m31a/agent-*branches
Source: internal/tools/subagent/events.go
Subagent events are streamed to the TUI:
| Event | Description |
|---|---|
SubagentStartedMsg |
Agent spawned with ID and description |
SubagentProgressMsg |
Intermediate output from the agent |
SubagentCompleteMsg |
Agent finished with result or error |
SubagentToolCallMsg |
Tool invocation within a subagent |
The TUI listens via subagentListenerCmd() and renders events as toast notifications or sidebar entries.
Source: internal/tools/agent.go
The Agent tool is registered on the parent dispatcher, allowing the LLM to spawn subagents:
{
"name": "Agent",
"description": "Spawn a parallel subagent...",
"params": {
"description": "Short task description",
"prompt": "Detailed instructions for the subagent"
}
}Child agents are created with isChild=true, which prevents them from spawning grandchildren in background. This avoids runaway recursion.
| Command | Description |
|---|---|
/agent [desc]: [prompt] |
Spawn a new subagent |
/agent |
List active subagents |
/agent-cancel <id|all> |
Cancel a running subagent |
Source: internal/tui/subagents_model.go, internal/tui/subagent_bridge.go
The TUI maintains a SubagentsModel that:
- Lists active subagents with status
- Shows progress and results
- Provides cancel functionality
- Integrates with the sidebar for status display