Major enhancements to voice assistant with conversation persistence and mobile app planning: - Add conversation management with multiple active conversations - Implement conversation persistence (save/load from disk) - Add conversation selector UI for switching between conversations - Enhance agent prompt with per-conversation tmux sessions - Add mobile app technical specification (React Native + Expo) - Improve UI with active processes view and agent stream - Add ACP (Agent Control Protocol) server implementation Mobile spec details: - React Native with Expo SDK 54+ - NativeWind v5 for styling - Share Zod schemas/types from server - Android in-call experience (InCallManager) - Direct WebSocket connection via Tailscale - Same audio quality as web prototype 🤖 Generated with [Claude Code](https://claude.com/claude-code) via [Happy](https://happy.engineering) Co-Authored-By: Claude <noreply@anthropic.com> Co-Authored-By: Happy <yesreply@happy.engineering>
7.4 KiB
Phase 2: ACP Integration - Session and MCP Tools
This document describes the Phase 2 implementation that integrates AgentManager with the Session and MCP tools infrastructure.
Overview
Phase 2 enables the orchestrator LLM to create and control Claude Code agents via function calls. Agents run as separate processes and can execute coding tasks autonomously, with their updates streamed back to the UI via WebSocket.
Architecture
Components
-
Agent MCP Server (
mcp-server.ts)- Exposes 6 tools to the LLM for agent management
- Each Session gets its own AgentManager instance
- Uses in-memory transport to connect to Session's MCP client
-
WebSocket Messages (
messages.ts)- Three new message types:
agent_created,agent_update,agent_status - Agent updates flow directly to WebSocket (NOT through LLM)
- Enables real-time UI feedback for agent work
- Three new message types:
-
Session Integration (
session.ts)- Each session owns an AgentManager
- Initializes agent MCP server on startup
- Subscribes to agent updates and forwards to WebSocket
- Cleans up all agents on session end
-
getAllTools (
llm-openai.ts)- Updated to accept agent tools parameter
- Merges terminal, agent, Playwright, and manual tools
- Per-session tool sets enable isolation
Tools Available to LLM
create_coding_agent
Creates a new Claude Code agent.
Input:
plan(optional): Initial task for the agent
Output:
agentId: Unique identifier for the agentstatus: Current agent status
Example:
{
plan: "Refactor the authentication module to use JWT tokens"
}
send_agent_prompt
Sends a task or instruction to an agent.
Input:
agentId: Agent to send prompt toprompt: Task or instruction
Output:
success: Whether prompt was sent
Example:
{
agentId: "abc-123",
prompt: "Add unit tests for the new functions"
}
get_agent_status
Gets current status and info about an agent.
Input:
agentId: Agent to query
Output:
status: Current statusinfo: Detailed agent information
list_agents
Lists all active agents in this session.
Input: None
Output:
agents: Array of agent info objects
cancel_agent
Cancels an agent's current task (keeps agent alive).
Input:
agentId: Agent to cancel
Output:
success: Whether cancellation succeeded
kill_agent
Terminates an agent completely.
Input:
agentId: Agent to kill
Output:
success: Whether kill succeeded
Data Flow
Agent Creation Flow
- LLM calls
create_coding_agenttool - Agent MCP server creates agent via AgentManager
- AgentManager spawns
npx @zed-industries/claude-code-acpprocess - Session intercepts tool result, sees
agentId - Session subscribes to agent updates
- Session emits
agent_createdmessage to WebSocket
Agent Update Flow
- Agent process sends ACP session updates
- AgentManager receives updates via ACP callback
- AgentManager notifies subscribers (including Session)
- Session forwards update to WebSocket as
agent_updatemessage - UI receives update and can display progress
Important: Agent updates bypass the LLM and go directly to the UI. This prevents the LLM from being overwhelmed with agent activity while still providing real-time feedback to the user.
Agent Cleanup Flow
- Session cleanup is triggered (client disconnect, timeout, etc.)
- Session iterates through all agents
- Calls
AgentManager.killAgent()for each - Unsubscribes from all agent updates
- Closes agent MCP client
Session Lifecycle
// On Session creation
constructor() {
this.agentManager = new AgentManager()
this.initializeAgentMcp() // Async initialization
}
// During LLM processing
async processWithLLM() {
// Wait for agent MCP to initialize
while (!this.agentTools && timeout) { await sleep(100) }
// Get all tools (includes agent tools)
const allTools = await getAllTools(this.terminalTools, this.agentTools)
// Stream with tools available
await streamText({ tools: allTools, ... })
}
// On create_coding_agent tool result
onChunk({ type: "tool-result", toolName: "create_coding_agent" }) {
const agentId = result.structuredContent.agentId
this.subscribeToAgent(agentId) // Start receiving updates
this.emit({ type: "agent_created", ... })
}
// On Session cleanup
async cleanup() {
// Kill all agents
for (const agent of this.agentManager.listAgents()) {
await this.agentManager.killAgent(agent.id)
}
// Unsubscribe from all updates
for (const unsubscribe of this.agentUpdateUnsubscribers.values()) {
unsubscribe()
}
// Close MCP clients
await this.agentMcpClient.close()
}
Testing
Integration Test
Run the integration test to verify the complete flow:
npx tsx src/server/acp/test-integration.ts
The test:
- Creates AgentManager
- Sets up Agent MCP server
- Connects MCP client
- Verifies all 6 tools are present
- Creates an agent
- Subscribes to agent updates
- Tests agent operations (status, prompt, cancel)
- Kills agent and verifies cleanup
Manual Testing
Once Phase 3 (UI) is complete, you can test via the voice interface:
- Start the server:
npm run dev - Connect via browser
- Say: "Create a coding agent to refactor the auth module"
- LLM will call
create_coding_agenttool - Watch agent updates in the UI as it works
- Say: "Check the agent status"
- LLM will call
get_agent_statustool
Key Design Decisions
Per-Session AgentManager
Each Session gets its own AgentManager instance. This ensures:
- Agent isolation between conversations
- Clean cleanup when session ends
- No shared state between users
Direct Update Streaming
Agent updates go directly to WebSocket, not through LLM:
- Prevents LLM prompt pollution
- Enables real-time UI feedback
- Reduces token usage
- Allows parallel agent and LLM work
Tool Result Interception
Session intercepts create_coding_agent tool results to:
- Automatically subscribe to new agents
- Emit
agent_createdmessage - No manual tracking needed by LLM
Graceful Initialization
Agent MCP initializes asynchronously:
- Session creation doesn't block on initialization
- LLM waits for tools before first call
- 5 second timeout with clear error
- Allows fast session creation
Files Modified
New Files
src/server/acp/mcp-server.ts- Agent MCP server with 6 toolssrc/server/acp/test-integration.ts- Integration test scriptsrc/server/acp/PHASE2-INTEGRATION.md- This documentation
Modified Files
src/server/messages.ts- Added 3 agent message typessrc/server/session.ts- Integrated AgentManagersrc/server/agent/llm-openai.ts- Updated getAllTools signature
Next Steps: Phase 3
Phase 3 will add UI components to:
- Display agent creation notifications
- Stream agent updates in real-time
- Show agent status and progress
- Allow manual agent control (cancel, kill)
- Display agent work artifacts
Troubleshooting
"Agent MCP failed to initialize"
Check:
@zed-industries/claude-code-acpis installed- Node version is compatible
- No firewall blocking spawned processes
"Agent not found"
Likely causes:
- Agent was killed or crashed
- Using wrong agentId
- Session was cleaned up
Agent updates not received
Check:
- Subscription was created after agent creation
- WebSocket connection is active
- Agent process is still running