@hexos/runtimeCentral orchestrator for multi-agent conversations with LLM providers, tool execution, and human-in-the-loop approvals.
AgentRuntime manages the complete lifecycle of agent interactions: routing messages to the appropriate LLM provider (LLMProvider), executing tools with concurrency limits (Semaphore) and timeouts (withTimeout), coordinating agent handoffs via the swarm pattern (generateHandoffTools), and managing pending approval workflows (ToolApproval).
The runtime operates in streaming mode via stream(), yielding RuntimeEvent objects
as the conversation progresses. It enforces resource limits: max concurrent streams,
rate limiting (SlidingWindowRateLimiter), tool execution concurrency, and approval
timeouts. Configuration is validated and normalized at construction via normalizeRuntimeConfig.
Typical flow:
- Client calls
stream(input)with user message - Runtime selects active agent and builds tool list (local + handoff + MCP)
- LLM provider streams response, emitting text-delta and tool-call events
- Tools execute with approval checks and guards
- Handoff tools trigger agent switches, continuing the loop
- Stream completes with final message content
Related: RuntimeConfig configures the runtime, RuntimeEvent is the event output, SSETransport consumes events on the frontend, createAgentHandler wraps this for Next.js, MCPManager provides external tool integration.
class AgentRuntime {
constructor(config: RuntimeConfig)
initialize() => Promise<void>;
shutdown() => Promise<void>;
submitApproval(decision: ApprovalDecision) => boolean;
getPendingApprovals(conversationId?: string) => PendingApproval[];
stream(input: RuntimeInput) => AsyncGenerator<RuntimeEvent>;
invoke(input: RuntimeInput) => Promise<RuntimeOutput>;
resumeWithApproval(conversationId: string, approvals: ApprovalDecision[]) => AsyncGenerator<RuntimeEvent>;
getAgents() => AgentDefinition[];
getAgent(id: string) => AgentDefinition | undefined;
getMCPManager() => MCPManager | null;
}constructor
(config: RuntimeConfig) => AgentRuntimeinitialize
() => PromiseInitializes the runtime by connecting to configured MCP servers.
Must be called before stream() or invoke(). Connects to non-lazy MCP servers
and caches their tool lists. Idempotent — subsequent calls are no-ops.
Related: MCPManager.initialize() handles server connections.
shutdown
() => PromiseShuts down the runtime by disconnecting all MCP servers.
Call this during application teardown to clean up child processes and connections.
submitApproval
(decision: ApprovalDecision) => booleanSubmits an approval or rejection decision for a pending tool call.
Resolves the promise created by waitForApproval(), allowing the streaming
conversation to continue. Returns false if no pending approval matches the
provided toolCallId.
Related: ApprovalDecision is the input, createApprovalHandler wraps this for HTTP endpoints, useToolApproval calls this from the frontend.
getPendingApprovals
(conversationId?: string) => PendingApproval[]Returns all pending tool approval requests, optionally filtered by conversation.
stream
(input: RuntimeInput) => AsyncGenerator<RuntimeEvent>Streams a conversation turn, yielding RuntimeEvents in real-time.
Supports multi-agent handoffs: if a tool returns a HandoffResult, the runtime switches to the target agent and continues streaming. The loop terminates when no handoff occurs or the max handoff limit is reached.
Resource limits enforced: max active streams (global and per-conversation), rate limiting, tool execution concurrency via Semaphore, and approval timeouts.
invoke
(input: RuntimeInput) => Promise<RuntimeOutput>Executes a single conversation turn without streaming, returning the final result.
Internally calls stream() and collects all events into a RuntimeOutput.
Useful for server-to-server calls where streaming is not needed.
resumeWithApproval
(conversationId: string, approvals: ApprovalDecision[]) => AsyncGenerator<RuntimeEvent>Resolves pending approvals for a conversation and signals completion.
Applies each ApprovalDecision to its matching pending approval. Emits an error event if no approvals matched. Used when the client batches multiple approval decisions.
getAgents
() => AgentDefinition[]Returns all registered agent definitions.
getAgent
(id: string) => AgentDefinition | undefinedReturns a specific agent definition by ID, or undefined if not found.
getMCPManager
() => MCPManager | nullReturns the MCPManager instance, or null if no MCP servers are configured.
Create Agent Handler Create Approval Handler Create Express Handler