Skip to content

Add: Parallel tool call support#62

Merged
FL4TLiN3 merged 30 commits intomainfrom
feature/parallel-tool-calls
Dec 8, 2025
Merged

Add: Parallel tool call support#62
FL4TLiN3 merged 30 commits intomainfrom
feature/parallel-tool-calls

Conversation

@FL4TLiN3
Copy link
Contributor

@FL4TLiN3 FL4TLiN3 commented Dec 8, 2025

Summary

Implement parallel tool call support in the Perstack runtime. The runtime now processes all tool calls from a single LLM response in parallel using Promise.all, instead of processing only the first one.

Changes

@perstack/core

  • Step.toolCallStep.toolCalls (array)
  • Step.toolResultStep.toolResults (array)
  • callTool event → callTools event
  • resolveToolResult event → resolveToolResults event

@perstack/runtime

  • Updated generatingToolCallLogic to process multiple tool calls
  • Updated callingToolLogic to execute MCP tools in parallel with Promise.all
  • Updated resolvingToolResultLogic to aggregate multiple tool results
  • Special tools (think, attemptCompletion, readPdfFile, readImageFile) are prioritized
  • Delegate and interactive tools continue to be handled individually

@perstack/tui & CLI

  • Updated to correctly display multiple tool calls and results in a single step

Documentation

  • Updated runtime README with new event names and state machine diagram

Testing

  • ✅ All unit tests pass (345 tests)
  • ✅ E2E tests pass
  • ✅ Manual verification with Exa Web Search (3 parallel calls executed and returned successfully)

Closes #61
Closes #63

- Change Step.toolCall to Step.toolCalls (array)
- Change Step.toolResult to Step.toolResults (array)
- Rename callTool event to callTools
- Rename resolveToolResult event to resolveToolResults
- Update event payloads for arrays
- Update state machine for parallel tool calls
- Execute MCP tools in parallel with Promise.all
- Handle special tools (think, attemptCompletion, PDF, image) with priority
- Update all resolve logic for multiple results
- Update use-step-store.ts to handle callTools and resolveToolResults events
- Update CLI event listener for multiple tool calls
- Update api-client test data
@vercel
Copy link

vercel bot commented Dec 8, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Preview Comments Updated (UTC)
perstack Ignored Ignored Preview Dec 8, 2025 5:43pm

Extend ToolResultPart.contents type to support FileInlinePart (PDF)
in addition to TextPart and ImageInlinePart.
- Execute think, readPdfFile, readImageFile in parallel with other MCP tools
- Only attemptCompletion remains special (completion means done)
- Process file results (PDF/image) during tool execution
- Include PDF content in toolMessage instead of userMessage
- Add partialToolResults to state machine transitions

Closes #64
- Update calling-tool tests to expect resolveToolResults for think/readPdfFile/readImageFile
- Update resolving-pdf-file tests for new message format
- Fix mixed-tools E2E test expectations for pendingToolCalls count
Verify that think tool executes in parallel with other MCP tools
and both results are returned together in resolveToolResults.
Test parallel execution of think, readPdfFile, readImageFile with
regular MCP tools using small test fixtures (test.pdf, test.gif).
- Set step.toolResults from checkpoint.partialToolResults in
  finishAllToolCalls transition
- Add fileInlinePart to content filter in resolving-tool-result,
  preparing-for-step, and generating-run-result
- Add tests for preparing-for-step (pendingToolCalls, partialToolResults)
- Add tests for calling-interactive-tool (error cases, partialToolResults)
- Add tests for calling-tool (interactive skill, mixed tool types)
- Add tests for resolving-image-file/pdf-file (error cases, invalid JSON)
step.partialToolResults = toolResults
step.pendingToolCalls = remainingToolCalls
return callDelegate(setting, checkpoint, {
newMessage: checkpoint.messages[checkpoint.messages.length - 1] as never,
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: Stale message passed to delegate/interactive events after resume

When callingToolLogic emits callDelegate or callInteractiveTool events, it passes checkpoint.messages[checkpoint.messages.length - 1] as newMessage. After a resumeToolCalls transition (when continuing after a previous delegate/interactive stop), the checkpoint messages haven't been updated in that transition, so this retrieves a stale message from before the run was stopped rather than the relevant tool call message. While the state machine currently ignores this field when transitioning from CallingTool state (making it functionally harmless), event listeners receiving these events will see incorrect newMessage data.

Additional Locations (1)

Fix in Cursor Fix in Web

@FL4TLiN3 FL4TLiN3 merged commit 3b64f88 into main Dec 8, 2025
9 checks passed
@github-actions github-actions bot mentioned this pull request Dec 8, 2025
@github-actions github-actions bot mentioned this pull request Dec 8, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add: Support mixed tool calls (MCP + Delegate + Interactive) Add: Parallel tool call support

1 participant