Skip to content

Commit 940a2b2

Browse files
authored
Fix workflow tool not executing when requireApproval is true and tool-call is approved (#11538)
## Description <!-- Provide a brief description of the changes in this PR --> ## Related Issue(s) <!-- Link to the issue(s) this PR addresses, using hashtag notation: #123 --> Fixes #11531 ## Type of Change - [x] Bug fix (non-breaking change that fixes an issue) - [ ] New feature (non-breaking change that adds functionality) - [ ] Breaking change (fix or feature that would cause existing functionality to change) - [ ] Documentation update - [ ] Code refactoring - [ ] Performance improvement - [x] Test update ## Checklist - [ ] I have made corresponding changes to the documentation (if applicable) - [ ] I have added tests that prove my fix is effective or that my feature works <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * Fixed workflow tools failing to execute when requireApproval is enabled and the tool call is approved. <sub>✏️ Tip: You can customize this high-level summary in your review settings.</sub> <!-- end of auto-generated comment: release notes by coderabbit.ai -->
1 parent 67fa366 commit 940a2b2

File tree

3 files changed

+84
-1
lines changed

3 files changed

+84
-1
lines changed

.changeset/big-fans-care.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@mastra/core': patch
3+
---
4+
5+
Fix workflow tool not executing when requireApproval is true and tool call is approved

packages/core/src/agent/__tests__/tool-approval.test.ts

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,78 @@ export function toolApprovalAndSuspensionTests(version: 'v1' | 'v2') {
205205
expect(name).toBe('Dero Israel');
206206
}, 500000);
207207

208+
it('should call findUserWorkflow with requireToolApproval on workflow', async () => {
209+
const findUserStep = createStep({
210+
id: 'find-user-step',
211+
description: 'This is a test step that returns the name and email',
212+
inputSchema: z.object({
213+
name: z.string(),
214+
}),
215+
outputSchema: z.object({
216+
name: z.string(),
217+
email: z.string(),
218+
}),
219+
execute: async ({ inputData }) => {
220+
return mockFindUser(inputData) as Promise<Record<string, any>>;
221+
},
222+
});
223+
224+
const findUserWorkflow = createWorkflow({
225+
id: 'find-user-workflow',
226+
description: 'This is a test tool that returns the name and email',
227+
inputSchema: z.object({
228+
name: z.string(),
229+
}),
230+
outputSchema: z.object({
231+
name: z.string(),
232+
email: z.string(),
233+
}),
234+
})
235+
.then(findUserStep)
236+
.commit();
237+
238+
const userAgent = new Agent({
239+
id: 'user-agent',
240+
name: 'User Agent',
241+
instructions: 'You are an agent that can get list of users using findUserWorkflow.',
242+
model: openaiModel,
243+
workflows: { findUserWorkflow },
244+
defaultOptions: {
245+
requireToolApproval: true,
246+
},
247+
});
248+
249+
const mastra = new Mastra({
250+
agents: { userAgent },
251+
logger: false,
252+
storage: mockStorage,
253+
});
254+
255+
const agentOne = mastra.getAgent('userAgent');
256+
257+
let toolCall;
258+
const stream = await agentOne.stream('Find the user with name - Dero Israel');
259+
let toolCallId = '';
260+
for await (const _chunk of stream.fullStream) {
261+
if (_chunk.type === 'tool-call-approval') {
262+
toolCallId = _chunk.payload.toolCallId;
263+
}
264+
}
265+
await new Promise(resolve => setTimeout(resolve, 1000));
266+
const resumeStream = await agentOne.approveToolCall({ runId: stream.runId, toolCallId });
267+
for await (const _chunk of resumeStream.fullStream) {
268+
}
269+
270+
const toolResults = await resumeStream.toolResults;
271+
272+
toolCall = toolResults?.find((result: any) => result.payload.toolName === 'workflow-findUserWorkflow')?.payload;
273+
274+
const name = toolCall?.result?.result?.name;
275+
276+
expect(mockFindUser).toHaveBeenCalled();
277+
expect(name).toBe('Dero Israel');
278+
}, 500000);
279+
208280
it('should call findUserTool with requireToolApproval on tool and resume via stream when autoResumeSuspendedTools is true', async () => {
209281
const findUserTool = createTool({
210282
id: 'Find user tool',

packages/core/src/loop/workflows/agentic-execution/tool-call-step.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -343,6 +343,12 @@ export function createToolCallStep<
343343
await removeToolMetadata(inputData.toolName, 'suspension');
344344
}
345345

346+
//this is to avoid passing resume data to the tool if it's not needed
347+
const resumeDataToPassToToolOptions =
348+
toolRequiresApproval && Object.keys(resumeData).length === 1 && 'approved' in resumeData
349+
? undefined
350+
: resumeData;
351+
346352
const toolOptions: MastraToolInvocationOptions = {
347353
abortSignal: options?.abortSignal,
348354
toolCallId: inputData.toolCallId,
@@ -389,7 +395,7 @@ export function createToolCallStep<
389395
},
390396
);
391397
},
392-
resumeData,
398+
resumeData: resumeDataToPassToToolOptions,
393399
};
394400

395401
const result = await tool.execute(args, toolOptions);

0 commit comments

Comments
 (0)