@@ -11,12 +11,14 @@ vi.mock('@/lib/internal/workflows/read-tool-enrichment', () => ({
1111 readWorkflowMetadataForTool : workflowMetadataMocks . readWorkflowMetadataForTool ,
1212} ) )
1313
14+ import { getParamsForToolOperation } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/tool-input/param-dependents'
1415import { GreenhouseV2Block } from '@/blocks/blocks/greenhouse'
1516import { IntuneBlock } from '@/blocks/blocks/intune'
1617import { MicrosoftExcelV2Block } from '@/blocks/blocks/microsoft_excel'
1718import { RampBlock } from '@/blocks/blocks/ramp'
1819import { RevenueCatBlock } from '@/blocks/blocks/revenuecat'
1920import { VideoGeneratorV3Block } from '@/blocks/blocks/video_generator'
21+ import * as blockRegistry from '@/blocks/registry'
2022import { normalizeFileInput } from '@/blocks/utils'
2123import {
2224 findProviderFromModel ,
@@ -45,15 +47,21 @@ import {
4547} from '@/providers/utils'
4648import { useProvidersStore } from '@/stores/providers/store'
4749import { greenhouseConvertProspectV2Tool } from '@/tools/greenhouse/convert_prospect_v2'
50+ import { greenhouseCreateApplicationV2Tool } from '@/tools/greenhouse/create_application_v2'
51+ import { greenhouseHireApplicationV2Tool } from '@/tools/greenhouse/hire_application_v2'
4852import { greenhouseListCandidatesV2Tool } from '@/tools/greenhouse/list_candidates_v2'
4953import { greenhouseListJobOwnersV2Tool } from '@/tools/greenhouse/list_job_owners_v2'
5054import { greenhouseListJobsV2Tool } from '@/tools/greenhouse/list_jobs_v2'
5155import { greenhouseListNotesV2Tool } from '@/tools/greenhouse/list_notes_v2'
5256import { greenhouseListOffersV2Tool } from '@/tools/greenhouse/list_offers_v2'
5357import type {
5458 GreenhouseConvertProspectV2Params ,
59+ GreenhouseCreateApplicationV2Params ,
60+ GreenhouseHireApplicationV2Params ,
5561 GreenhouseListCandidatesV2Params ,
62+ GreenhouseUpdateApplicationV2Params ,
5663} from '@/tools/greenhouse/types'
64+ import { greenhouseUpdateApplicationV2Tool } from '@/tools/greenhouse/update_application_v2'
5765import { intuneListDetectedAppsTool } from '@/tools/intune/list-detected-apps'
5866import { intuneListDevicesTool } from '@/tools/intune/list-devices'
5967import * as toolMetadata from '@/tools/metadata'
@@ -71,6 +79,184 @@ const mockGetRotatingApiKey = vi.fn().mockReturnValue('rotating-server-key')
7179const originalRequire = module . require
7280
7381describe ( 'integration filters through agent execution' , ( ) => {
82+ it . each ( [
83+ {
84+ operation : 'list_jobs' ,
85+ field : 'status' ,
86+ staleValue : 'active' ,
87+ tool : greenhouseListJobsV2Tool ,
88+ } ,
89+ {
90+ operation : 'list_job_owners' ,
91+ field : 'type' ,
92+ staleValue : 'NOTE' ,
93+ tool : greenhouseListJobOwnersV2Tool ,
94+ } ,
95+ {
96+ operation : 'list_notes' ,
97+ field : 'visibility' ,
98+ staleValue : 'private' ,
99+ tool : greenhouseListNotesV2Tool ,
100+ } ,
101+ {
102+ operation : 'list_candidates' ,
103+ field : 'created_at' ,
104+ staleValue : '2026-01-01' ,
105+ tool : greenhouseListCandidatesV2Tool ,
106+ } ,
107+ ] ) (
108+ 'clears incompatible $field when switching to $operation' ,
109+ async ( { operation, field, staleValue, tool } ) => {
110+ vi . spyOn ( blockRegistry , 'getBlock' ) . mockReturnValue ( GreenhouseV2Block )
111+ const previousParams = {
112+ [ field ] : staleValue ,
113+ per_page : '25' ,
114+ credential : 'fixture-credential' ,
115+ }
116+ const params = getParamsForToolOperation (
117+ { type : 'greenhouse_v2' , params : previousParams } ,
118+ tool
119+ )
120+ const providerTool = await transformBlockTool (
121+ { type : 'greenhouse_v2' , params } ,
122+ {
123+ selectedOperation : operation ,
124+ getAllBlocks : ( ) => [ GreenhouseV2Block ] ,
125+ getTool : ( ) => tool ,
126+ }
127+ )
128+ if ( ! providerTool ) throw new Error ( 'Greenhouse agent tool was not created' )
129+ const { executionParams } = prepareToolExecution ( providerTool , { } , { } , undefined )
130+ const url =
131+ typeof tool . request . url === 'function'
132+ ? tool . request . url ( { ...executionParams , accessToken : 'fixture-token' } )
133+ : tool . request . url
134+ expect ( new URL ( url ) . searchParams . has ( field ) ) . toBe ( false )
135+ expect ( new URL ( url ) . searchParams . get ( 'per_page' ) ) . toBe ( '25' )
136+ expect ( params . credential ) . toBe ( 'fixture-credential' )
137+ expect ( previousParams [ field ] ) . toBe ( staleValue )
138+ }
139+ )
140+
141+ it ( 'uses the newly supplied application target and fields after leaving candidate operations' , async ( ) => {
142+ const tool = greenhouseUpdateApplicationV2Tool
143+ vi . spyOn ( blockRegistry , 'getBlock' ) . mockReturnValue ( GreenhouseV2Block )
144+ const params = getParamsForToolOperation (
145+ {
146+ type : 'greenhouse_v2' ,
147+ params : {
148+ id : '42' ,
149+ custom_fields : '[{"custom_field_id":7,"value":"candidate value"}]' ,
150+ source_id : '81' ,
151+ } ,
152+ } ,
153+ tool
154+ )
155+ const providerTool = await transformBlockTool (
156+ { type : 'greenhouse_v2' , params } ,
157+ {
158+ selectedOperation : 'update_application' ,
159+ getAllBlocks : ( ) => [ GreenhouseV2Block ] ,
160+ getTool : ( ) => tool ,
161+ }
162+ )
163+ if ( ! providerTool ) throw new Error ( 'Greenhouse agent tool was not created' )
164+ const { executionParams } = prepareToolExecution ( providerTool , { id : 99 } , { } , undefined )
165+ const execution = {
166+ ...executionParams ,
167+ accessToken : 'fixture-token' ,
168+ } as GreenhouseUpdateApplicationV2Params
169+ const url =
170+ typeof tool . request . url === 'function' ? tool . request . url ( execution ) : tool . request . url
171+ expect ( new URL ( url ) . pathname ) . toBe ( '/v3/applications/99' )
172+ expect ( tool . request . body ?.( execution ) ) . toEqual ( { source_id : 81 } )
173+ } )
174+
175+ it ( 'does not carry a list-filter job CSV into a prospect creation' , async ( ) => {
176+ const tool = greenhouseCreateApplicationV2Tool
177+ vi . spyOn ( blockRegistry , 'getBlock' ) . mockReturnValue ( GreenhouseV2Block )
178+ const params = getParamsForToolOperation (
179+ {
180+ type : 'greenhouse_v2' ,
181+ params : {
182+ job_ids : '11,12' ,
183+ candidate_id : '42' ,
184+ } ,
185+ } ,
186+ tool
187+ )
188+ const providerTool = await transformBlockTool (
189+ { type : 'greenhouse_v2' , params } ,
190+ {
191+ selectedOperation : 'create_application' ,
192+ getAllBlocks : ( ) => [ GreenhouseV2Block ] ,
193+ getTool : ( ) => tool ,
194+ }
195+ )
196+ if ( ! providerTool ) throw new Error ( 'Greenhouse agent tool was not created' )
197+ const { executionParams } = prepareToolExecution (
198+ providerTool ,
199+ { prospect : true , job_ids : [ 81 ] } ,
200+ { } ,
201+ undefined
202+ )
203+ expect (
204+ tool . request . body ?.( {
205+ ...executionParams ,
206+ accessToken : 'fixture-token' ,
207+ } as GreenhouseCreateApplicationV2Params )
208+ ) . toEqual ( { candidate_id : 42 , prospect : true , job_ids : [ 81 ] } )
209+ } )
210+
211+ it ( 'uses a newly supplied internal opening ID after leaving opening list filters' , async ( ) => {
212+ const tool = greenhouseHireApplicationV2Tool
213+ vi . spyOn ( blockRegistry , 'getBlock' ) . mockReturnValue ( GreenhouseV2Block )
214+ const params = getParamsForToolOperation (
215+ { type : 'greenhouse_v2' , params : { opening_id : 'external-opening' } } ,
216+ tool
217+ )
218+ const providerTool = await transformBlockTool (
219+ { type : 'greenhouse_v2' , params } ,
220+ {
221+ selectedOperation : 'hire_application' ,
222+ getAllBlocks : ( ) => [ GreenhouseV2Block ] ,
223+ getTool : ( ) => tool ,
224+ }
225+ )
226+ if ( ! providerTool ) throw new Error ( 'Greenhouse agent tool was not created' )
227+ const { executionParams } = prepareToolExecution (
228+ providerTool ,
229+ { id : 99 , opening_id : 81 } ,
230+ { } ,
231+ undefined
232+ )
233+ expect (
234+ tool . request . body ?.( {
235+ ...executionParams ,
236+ accessToken : 'fixture-token' ,
237+ } as GreenhouseHireApplicationV2Params )
238+ ) . toEqual ( { opening_id : 81 } )
239+ } )
240+
241+ it . each ( [ 'credential' , 'oauthCredential' ] ) (
242+ 'preserves configured %s but not a resolved token across OAuth operations' ,
243+ ( credentialId ) => {
244+ vi . spyOn ( blockRegistry , 'getBlock' ) . mockReturnValue ( GreenhouseV2Block )
245+ const params = getParamsForToolOperation (
246+ {
247+ type : 'greenhouse_v2' ,
248+ params : {
249+ [ credentialId ] : 'fixture-credential' ,
250+ accessToken : 'expired-resolved-token' ,
251+ } ,
252+ } ,
253+ greenhouseListJobsV2Tool
254+ )
255+ expect ( params [ credentialId ] ) . toBe ( 'fixture-credential' )
256+ expect ( params ) . not . toHaveProperty ( 'accessToken' )
257+ }
258+ )
259+
74260 it . each ( [
75261 { operation : 'list_jobs' , field : 'status' , tool : greenhouseListJobsV2Tool } ,
76262 { operation : 'list_offers' , field : 'status' , tool : greenhouseListOffersV2Tool } ,
0 commit comments