ai icon indicating copy to clipboard operation
ai copied to clipboard

feature request: V6 needsApproval mid-execute instead of before execute

Open robertlong opened this issue 2 months ago • 6 comments

Description

I'm working on an AI SDK powered app right now, I'm very excited to see the new needsApproval flag in the V6 beta. However, I just implemented something similar for my own app and this flag wouldn't be enough for my use case. I need something like the following:

export const myTool = tool({
  description: 'Does a thing',
  inputSchema: z.object({
    input: z.string(),
  }),
  execute: async ({ input }, { waitForApproval }) => {
    const data = await fetchPrerequisteData(input);
    sendToClient(data);
    await waitForApproval();
    const result = await executeTool(input, data);
    return result;
  },
});

In my tool I want to fetch some content from my database and update a component on the client with that data to be shown when the user is asked to Accept/Deny a tool call. I think it's a pretty common pattern if your agent's tool call components render more than just the data provided by the tool call.

AI SDK Version

No response

Code of Conduct

  • [x] I agree to follow this project's Code of Conduct

robertlong avatar Oct 28 '25 18:10 robertlong

Thanks for sharing, that's a great use case. I'll see if we can have first-class support for it as part of v6

gr2m avatar Oct 28 '25 20:10 gr2m

want to fetch some content from my database and update a component on the client with that data to be shown when the user is asked to Accept/Deny a tool call

can you split it up into two tool calls, one to fetch the data one to execute, and add the approval in-between?

gr2m avatar Oct 29 '25 02:10 gr2m

It would be useful to be able to attach additional information to the approval request after fetching some data in the tool. For example in a tool like this, it would fetch the product information to display a better approval message.

buy_product: tool({
   title: 'Buy Product',
   description: 'Purchase a product by its ID.',
   inputSchema: z.object({
     product_id: z.string().describe('The ID of the product to purchase'),
   }),
   needsApproval: true,
   execute: async ({ product_id }) => {
  ...

TimoWilhelm avatar Nov 07 '25 13:11 TimoWilhelm

Would love to have this feature - have very similar use case for this on my application, would much prefer to avoid a 2nd step just to trigger the execution.

noobmaster19 avatar Nov 08 '25 15:11 noobmaster19

I also have a fairly similar use case but with the possibility to modify the data by the user within a UI form.

export const myTool = tool({
  description: 'Does a thing',
  inputSchema: z.object({
    input: z.string(),
  }),
  execute: async ({ input }, { waitForApproval }) => {
    const data = await fetchPrerequisteData(input);
    sendToClient(data);
    modifiedData = await waitForApproval();
    const result = await executeTool(input, modifiedData);
    return result;
  },
});

christophebe avatar Nov 10 '25 14:11 christophebe

want to fetch some content from my database and update a component on the client with that data to be shown when the user is asked to Accept/Deny a tool call

can you split it up into two tool calls, one to fetch the data one to execute, and add the approval in-between?

I don't think we want to do that. It's more expensive and error prone. There's a part of the function that is deterministic and doesn't need the LLM at all.

robertlong avatar Nov 11 '25 21:11 robertlong

This feature request breaks the approval loop assumptions. I do not think it is feasible for AI SDK 6, and I am also unsure it should be implemented as a tool approval or something else. Tool approvals means approval for running the tool. Here the tool is already running and then gets some input.

lgrammel avatar Nov 29 '25 10:11 lgrammel

The tool call input often does not contain enough information to build an actionable approval dialog in the UI and I don't believe it would need to break the approval loop.

If the needsApproval property would accept an async function in addition to a simple boolean you could easily fetch the required data for the approval without needing to implement additional API endpoints.

buy_product: tool({
   title: 'Buy Product',
   description: 'Purchase a product by its ID.',
   inputSchema: z.object({
     product_id: z.string().describe('The ID of the product to purchase'),
   }),
   needsApproval: async ({ product_id }) => {
      const productInfo = await lookupProduct(product_id);
      return productInfo;
    },
   execute: async ({ product_id }) => {
  ...

This could then be exposed as part.approval.data to the UI components.

TimoWilhelm avatar Nov 29 '25 12:11 TimoWilhelm

Yup, that would work. A little duplicative if the needs approval function uses an async method whose result is also needed in the execute function but I can cache it in the outer scope.

robertlong avatar Dec 01 '25 16:12 robertlong

Ideally this functions in a similar way to how Mastra workflows work with a resumeSchema and a resumeData.

Would make this functionality a lot more usable than purely approve/deny.

https://mastra.ai/docs/workflows/human-in-the-loop

monobyte avatar Dec 03 '25 10:12 monobyte

Oh yeah that's exactly what I've been looking for!

robertlong avatar Dec 03 '25 18:12 robertlong