Skip to content

Multi Agent Pool

Sia edited this page Jun 2, 2026 · 6 revisions

Real Multi-Agent Sub-Agent Pool

The Agent dispatch UX wrapper prefixes prompts with Use the <agent> sub-agent to …, but that sub-agent runs inside the same single Claude child process as the main project console — so truly parallel work (e.g. reviewer reading files while a frontend agent writes Compose UI in the same project) is impossible through it alone.

The sub-agent pool is an independent process pool: SubAgentSessionManager keyed by (projectId, agentName). Each agent gets its own:

  • claude child process
  • session-id file (<workspace>/<projectId>/.vibecoder/agent-sessions/<agent>.id)
  • LogHub WebSocket topic (console-agent-<projectId>-<agentName>)
  • console UI

All sub-agent processes share the same project workspace, so they collaborate on the same source tree.

Why a separate manager

The main ClaudeSessionManager keeps a single child per project. Re-keying it to (projectId, agentName) would touch every caller of sendPrompt(projectId, text) (history persistence, action handler, publish services, status service, etc.) for marginal gain. A parallel SubAgentSessionManager is ~200 LOC and keeps the existing console path untouched.

Lifecycle

Event Behaviour
First prompt to a fresh (projectId, agentName) Spawns new claude child in the project root. Auto-prefixes the prompt with Use the <agentName> sub-agent to so Claude Code dispatches its standard sub-agent.
Second+ prompt Stdin write into the same persistent child. Prefix not re-added (firstPromptSent flag in AgentSession).
Idle 30 minutes SIGTERM (5 s grace → SIGKILL). Session-id is preserved; the next prompt spawns a new child with --resume <savedId>.
User clicks "새 세션" startNew(...) terminates + deletes the session-id file + resets the console ring.
User clicks ■ 중지 cancelTurn(...) terminates the alive child but keeps the session-id so the next prompt resumes the same conversation.
Process exits unexpectedly process_crashed system frame is broadcast; next prompt attempts resume.

The 30-minute reaper is shared with ClaudeSessionManager design (same idle timeout default) so operator memory budgets stay predictable.

Shared concurrency cap

Sub-agent turns share the same ClaudeConcurrencyGate as the main project console — one Anthropic account is one in-flight pool, so they must be counted together to avoid bursting into a 429. The cap is claude.maxConcurrentTurns (default 3); excess turns queue (with a "동시 작업 한도 도달 — 대기 중" console notice) and proceed in order as turns finish. Adjustable live in /settings (no restart, v1.90.0+ — setLimit). See Troubleshooting → Rate limited (429) for tuning guidance.

Web UI

Route Purpose
/projects/{id}/agents Index — every registered .agents/*.md with a live running / idle badge and an "Open console →" link. Header link from the main console (@ sub-agents → chip).
/projects/{id}/agents/{agent}/console Per-agent console. Trimmed view of the main console — no slash chips, no template picker, no agent dispatch dropdown (you're already inside one agent). ■ stop + 새 세션 buttons present.

JSON API

All routes require authentication (Bearer token or vibe_session cookie). vibe-coder is single-admin — see Security Model.

TOKEN=…   # Bearer from POST /api/auth/login

# Send a prompt to a sub-agent
curl -X POST -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"text":"List the Compose files you would touch to add a settings screen."}' \
  http://localhost:17880/api/projects/myapp/agents/frontend/console/prompt
# → {"ok":true}

# Cancel current turn (SIGTERM child, keep session-id)
curl -X POST -H "Authorization: Bearer $TOKEN" \
  http://localhost:17880/api/projects/myapp/agents/frontend/console/cancel

# List active agents for a project
curl -H "Authorization: Bearer $TOKEN" \
  http://localhost:17880/api/projects/myapp/agents/active
# → {"projectId":"myapp","agents":["frontend","reviewer"]}

WebSocket

/ws/projects/{projectId}/agents/{agentName}/console/logs — same protocol as the main console:

  • Cookie-based auth on handshake (Origin check applied); Bearer first-frame fallback for non-browser clients
  • Replay + live merge with monotonic seq
  • Frames: ConsoleSessionStarted, ConsoleAssistant, ConsoleToolUse, ConsoleToolResult, ConsoleError, ConsoleDone, ConsoleSystem, ConsoleReplayBegin/End

Unlike the main console, this WebSocket is read-only on the client side. Prompts are sent via the REST endpoint above. Incoming text frames from the client are drained but ignored — keeps the WebSocket healthy without duplicating prompt dispatch.

Workspace layout

<workspace>/<projectId>/
├── … source files …
└── .vibecoder/
    ├── claude-session.id          # main console session
    └── agent-sessions/
        ├── reviewer.id            # @reviewer session
        ├── frontend.id            # @frontend session
        └── backend.id             # @backend session

A single workspace, multiple session files, multiple live processes.

Persistence & registry

  • Persistent history. The conversation_turns.agent_name column distinguishes main-console turns (null) from sub-agent turns ('<name>'). Restart-safe. See Conversation History for the schema.
  • Agent filter UI. /projects/{id}/history has a dropdown ((main only) / (all) / @<name>) plus a row badge so sub-agent origin is visible at a glance. Pagination preserves the choice via the ?agent= query parameter.
  • Agent definitions go through ~/.claude/agents/*.md. Manage them at /agents. The pool reads the same registry — remove an agent there and its existing process keeps running until terminated; the next-prompt path falls back to the trivial "agent not registered" 404.

Related

  • Custom Agents — managing ~/.claude/agents/*.md files (the registry the pool reads).
  • Multi-Console — N-pane iframe approach for parallel project consoles. Multi-Agent-Pool is the corresponding story at the agent level inside one project.
  • Architecture Overview — where SubAgentSessionManager fits in the bigger picture.

Clone this wiki locally