@@ -123,7 +123,6 @@ describe('AgentBlockHandler', () => {
123123 let handler : AgentBlockHandler
124124 let mockBlock : SerializedBlock
125125 let mockContext : ExecutionContext
126- let originalPromiseAll : any
127126
128127 beforeEach ( ( ) => {
129128 handler = new AgentBlockHandler ( )
@@ -135,8 +134,6 @@ describe('AgentBlockHandler', () => {
135134 configurable : true ,
136135 } )
137136
138- originalPromiseAll = Promise . all
139-
140137 mockBlock = {
141138 id : 'test-agent-block' ,
142139 metadata : { id : BlockType . AGENT , name : 'Test Agent' } ,
@@ -209,8 +206,6 @@ describe('AgentBlockHandler', () => {
209206 } )
210207
211208 afterEach ( ( ) => {
212- Promise . all = originalPromiseAll
213-
214209 try {
215210 Object . defineProperty ( global , 'window' , {
216211 value : undefined ,
@@ -271,38 +266,7 @@ describe('AgentBlockHandler', () => {
271266 expect ( result ) . toEqual ( expectedOutput )
272267 } )
273268
274- it ( 'should preserve executeFunction for custom tools with different usageControl settings' , async ( ) => {
275- let capturedTools : any [ ] = [ ]
276-
277- Promise . all = vi . fn ( ) . mockImplementation ( ( promises : Promise < any > [ ] ) => {
278- const result = originalPromiseAll . call ( Promise , promises )
279-
280- result . then ( ( tools : any [ ] ) => {
281- if ( tools ?. length ) {
282- capturedTools = tools . filter ( ( t ) => t !== null )
283- }
284- } )
285-
286- return result
287- } )
288-
289- mockExecuteProviderRequest . mockResolvedValueOnce ( {
290- content : 'Using tools to respond' ,
291- model : 'mock-model' ,
292- tokens : { input : 10 , output : 20 , total : 30 } ,
293- toolCalls : [
294- {
295- name : 'auto_tool' ,
296- arguments : { input : 'test input for auto tool' } ,
297- } ,
298- {
299- name : 'force_tool' ,
300- arguments : { input : 'test input for force tool' } ,
301- } ,
302- ] ,
303- timing : { total : 100 } ,
304- } )
305-
269+ it ( 'should preserve usageControl for custom tools and filter out "none"' , async ( ) => {
306270 const inputs = {
307271 model : 'gpt-4o' ,
308272 userPrompt : 'Test custom tools with different usageControl settings' ,
@@ -372,51 +336,21 @@ describe('AgentBlockHandler', () => {
372336
373337 await handler . execute ( mockContext , mockBlock , inputs )
374338
375- expect ( Promise . all ) . toHaveBeenCalled ( )
339+ const providerCall = mockExecuteProviderRequest . mock . calls [ 0 ]
340+ const tools = providerCall [ 1 ] . tools
376341
377- expect ( capturedTools . length ) . toBe ( 2 )
342+ expect ( tools . length ) . toBe ( 2 )
378343
379- const autoTool = capturedTools . find ( ( t ) => t . name === 'auto_tool' )
380- const forceTool = capturedTools . find ( ( t ) => t . name === 'force_tool' )
381- const noneTool = capturedTools . find ( ( t ) => t . name === 'none_tool' )
344+ const autoTool = tools . find ( ( t : any ) => t . name === 'auto_tool' )
345+ const forceTool = tools . find ( ( t : any ) => t . name === 'force_tool' )
346+ const noneTool = tools . find ( ( t : any ) => t . name === 'none_tool' )
382347
383348 expect ( autoTool ) . toBeDefined ( )
384349 expect ( forceTool ) . toBeDefined ( )
385350 expect ( noneTool ) . toBeUndefined ( )
386351
387352 expect ( autoTool . usageControl ) . toBe ( 'auto' )
388353 expect ( forceTool . usageControl ) . toBe ( 'force' )
389-
390- expect ( typeof autoTool . executeFunction ) . toBe ( 'function' )
391- expect ( typeof forceTool . executeFunction ) . toBe ( 'function' )
392-
393- await autoTool . executeFunction ( { input : 'test input' } )
394- expect ( mockExecuteTool ) . toHaveBeenCalledWith (
395- 'function_execute' ,
396- expect . objectContaining ( {
397- code : 'return { result: "auto tool executed", input }' ,
398- input : 'test input' ,
399- } ) ,
400- false , // skipPostProcess
401- expect . any ( Object ) // execution context
402- )
403-
404- await forceTool . executeFunction ( { input : 'another test' } )
405- expect ( mockExecuteTool ) . toHaveBeenNthCalledWith (
406- 2 , // Check the 2nd call
407- 'function_execute' ,
408- expect . objectContaining ( {
409- code : 'return { result: "force tool executed", input }' ,
410- input : 'another test' ,
411- } ) ,
412- false , // skipPostProcess
413- expect . any ( Object ) // execution context
414- )
415-
416- const providerCall = mockExecuteProviderRequest . mock . calls [ 0 ]
417- const requestBody = providerCall [ 1 ]
418-
419- expect ( requestBody . tools . length ) . toBe ( 2 )
420354 } )
421355
422356 it ( 'should filter out tools with usageControl set to "none"' , async ( ) => {
@@ -1763,6 +1697,52 @@ describe('AgentBlockHandler', () => {
17631697 expect ( providerCallArgs [ 1 ] . tools [ 0 ] . name ) . toBe ( 'search_files' )
17641698 } )
17651699
1700+ it ( 'should pass callChain to executeProviderRequest for MCP cycle detection' , async ( ) => {
1701+ mockFetch . mockImplementation ( ( ) =>
1702+ Promise . resolve ( { ok : true , json : ( ) => Promise . resolve ( { } ) } )
1703+ )
1704+
1705+ const inputs = {
1706+ model : 'gpt-4o' ,
1707+ userPrompt : 'Search for files' ,
1708+ apiKey : 'test-api-key' ,
1709+ tools : [
1710+ {
1711+ type : 'mcp' ,
1712+ title : 'search_files' ,
1713+ schema : {
1714+ type : 'object' ,
1715+ properties : {
1716+ query : { type : 'string' , description : 'Search query' } ,
1717+ } ,
1718+ required : [ 'query' ] ,
1719+ } ,
1720+ params : {
1721+ serverId : 'mcp-search-server' ,
1722+ toolName : 'search_files' ,
1723+ serverName : 'search' ,
1724+ } ,
1725+ usageControl : 'auto' as const ,
1726+ } ,
1727+ ] ,
1728+ }
1729+
1730+ const contextWithCallChain = {
1731+ ...mockContext ,
1732+ workspaceId : 'test-workspace-123' ,
1733+ workflowId : 'test-workflow-456' ,
1734+ callChain : [ 'wf-parent' , 'test-workflow-456' ] ,
1735+ }
1736+
1737+ mockGetProviderFromModel . mockReturnValue ( 'openai' )
1738+
1739+ await handler . execute ( contextWithCallChain , mockBlock , inputs )
1740+
1741+ expect ( mockExecuteProviderRequest ) . toHaveBeenCalled ( )
1742+ const providerCallArgs = mockExecuteProviderRequest . mock . calls [ 0 ] [ 1 ]
1743+ expect ( providerCallArgs . callChain ) . toEqual ( [ 'wf-parent' , 'test-workflow-456' ] )
1744+ } )
1745+
17661746 it ( 'should handle multiple MCP tools from the same server efficiently' , async ( ) => {
17671747 const fetchCalls : any [ ] = [ ]
17681748
@@ -2139,21 +2119,10 @@ describe('AgentBlockHandler', () => {
21392119 expect ( tools . length ) . toBe ( 0 )
21402120 } )
21412121
2142- it ( 'should use DB code for executeFunction when customToolId resolves' , async ( ) => {
2122+ it ( 'should use DB schema when customToolId resolves' , async ( ) => {
21432123 const toolId = 'custom-tool-123'
21442124 mockFetchForCustomTool ( toolId )
21452125
2146- let capturedTools : any [ ] = [ ]
2147- Promise . all = vi . fn ( ) . mockImplementation ( ( promises : Promise < any > [ ] ) => {
2148- const result = originalPromiseAll . call ( Promise , promises )
2149- result . then ( ( tools : any [ ] ) => {
2150- if ( tools ?. length ) {
2151- capturedTools = tools . filter ( ( t ) => t !== null )
2152- }
2153- } )
2154- return result
2155- } )
2156-
21572126 const inputs = {
21582127 model : 'gpt-4o' ,
21592128 userPrompt : 'Format a report' ,
@@ -2174,19 +2143,12 @@ describe('AgentBlockHandler', () => {
21742143
21752144 await handler . execute ( mockContext , mockBlock , inputs )
21762145
2177- expect ( capturedTools . length ) . toBe ( 1 )
2178- expect ( typeof capturedTools [ 0 ] . executeFunction ) . toBe ( 'function' )
2179-
2180- await capturedTools [ 0 ] . executeFunction ( { title : 'Q1' , format : 'pdf' } )
2146+ expect ( mockExecuteProviderRequest ) . toHaveBeenCalled ( )
2147+ const providerCall = mockExecuteProviderRequest . mock . calls [ 0 ]
2148+ const tools = providerCall [ 1 ] . tools
21812149
2182- expect ( mockExecuteTool ) . toHaveBeenCalledWith (
2183- 'function_execute' ,
2184- expect . objectContaining ( {
2185- code : dbCode ,
2186- } ) ,
2187- false ,
2188- expect . any ( Object )
2189- )
2150+ expect ( tools . length ) . toBe ( 1 )
2151+ expect ( tools [ 0 ] . name ) . toBe ( 'formatReport' )
21902152 } )
21912153
21922154 it ( 'should not fetch from DB when no customToolId is present' , async ( ) => {
0 commit comments