Skip to content

perf(agent): execute parallel tool calls concurrently #234

@cniska

Description

@cniska

What would you like?

When the model emits multiple tool calls in a single turn, execute them concurrently instead of awaiting each one sequentially. While tools are in flight, render an indicator per tool slot (pending, streaming, done) so the user sees exactly what is happening.

Motivation

The AI SDK already lets the model issue parallel tool calls — we receive them as a batch in pendingToolCalls. Today agent-stream.ts iterates that batch with await tool.execute(...) one call at a time, so throughput is bottlenecked on the slowest call. A turn with three reads serializes three round-trips for no reason. Claude Code and OpenCode both fan these out. Parallel execution also pairs with issue #233 (single-input tools) — when we drop array batching, the model's preferred way to do multi-item work is emitting N parallel calls.

The TUI is already mostly ready: tool output events carry toolCallId, chat state has toolRowIdByCallId: Map<string, string>, and each tool has its own contentByCallId buffer. The renderer does in-place updates per tool row.

Proposed approach

Replace the sequential for (const tc of pendingToolCalls) loop with Promise.allSettled to fan out tool execution concurrently. Preserve result order in toolResultParts via positional indexing so the message order the model sees stays stable. Add a per-row status (pending | streaming | done) to the tool row state and render an indicator next to each tool so the user can see what is in flight.

Scope

  • agent-stream.ts tool-execution loop
  • Per-row status field in tool output state (tool-output-render.ts, chat-message-handler-stream.ts)
  • Indicator glyph/render in the chat transcript
  • Tests for concurrent success, mixed success/failure, ordering stability, and cancellation behaviour
  • Verify no races on shared streamController.enqueue, consecutive-failure counter, and discovery-budget count under concurrency

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions