Add tool approval integration for Vercel AI adapter #3771
Closed
+301
−7
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Adds tool approval integration for the Vercel AI adapter, enabling human-in-the-loop workflows with the AI SDK.
This PR builds on #3760 and implements the basic plumbing for tool approval. The integration is functional for emitting approval requests and parsing approval responses, but does not yet automatically wire up the full flow.
Changes
ToolApprovalRequestedandToolApprovalRespondedtypes to represent approval stateapprovalfield to allToolUIPartandDynamicToolUIPartvariantsToolApprovalRequestChunkwhen agent returnsDeferredToolRequestsdeferred_tool_resultsproperty onVercelAIAdapterto extract approval responses from incoming tool partsextract_deferred_tool_results()class method for parsing approvalsHow It Works
Approval Request (server → client):
requires_approval=TrueDeferredToolRequestsVercelAIEventStream.handle_run_result()emitstool-approval-requestchunkApproval Response (client → server):
approvalfield with{id, approved, reason?}SubmitMessageincludes tool parts with approval responsesVercelAIAdapter.deferred_tool_resultsextracts these intoDeferredToolResultsOpen Questions
1. Automatic vs Manual Integration
Currently, users must manually wire up the deferred tool results:
Question: Should
run_stream()automatically useself.deferred_tool_resultswhen not explicitly provided? This would make the integration seamless but less explicit.2. Approval ID Tracking
We generate a UUID for
approval_idinToolApprovalRequestChunk, but the AI SDK uses this ID to track the approval lifecycle. TheToolApprovalResponded.idfield should match the originalapproval_id.Question: Do we need to store/track these approval IDs to validate they match on response? Currently we don't validate this.
3. ToolOutputDeniedChunk
The
ToolOutputDeniedChunktype exists but isn't emitted anywhere. In Pydantic AI, denied tool results flow as regularToolReturnPartwith the denial message as content.Question: Should we emit
ToolOutputDeniedChunkwhen the deferred tool result is aToolDenied? This would require tracking which tool calls were denied through the agent run.4. Message History Handling
When continuing with deferred tool results, the message history needs to include the original messages. Currently the adapter's
messagesproperty processes all incoming messages, but:Question: Should tool parts with pending approvals (only
ToolApprovalRequested, notToolApprovalResponded) be filtered out or handled specially when building message history?Testing
ToolApprovalRequestChunkemission whenrequires_approval=Truetool is calledextract_deferred_tool_results()covering approved, denied, and no-approval casesReferences