@@ -247,6 +247,149 @@ function CopyRootCauseButton({
247247 ) ;
248248}
249249
250+ type CodingAgentIntegration = {
251+ id : string ;
252+ name : string ;
253+ provider : string ;
254+ } ;
255+
256+ function SolutionActionButton ( {
257+ cursorIntegrations,
258+ preferredAction,
259+ primaryButtonPriority,
260+ isSelectingRootCause,
261+ isLaunchingAgent,
262+ isLoadingAgents,
263+ submitFindSolution,
264+ handleLaunchCodingAgent,
265+ findSolutionTitle,
266+ } : {
267+ cursorIntegrations : CodingAgentIntegration [ ] ;
268+ findSolutionTitle : string ;
269+ handleLaunchCodingAgent : ( integrationId : string , integrationName : string ) => void ;
270+ isLaunchingAgent : boolean ;
271+ isLoadingAgents : boolean ;
272+ isSelectingRootCause : boolean ;
273+ preferredAction : string ;
274+ primaryButtonPriority : React . ComponentProps < typeof Button > [ 'priority' ] ;
275+ submitFindSolution : ( ) => void ;
276+ } ) {
277+ const preferredIntegration = preferredAction . startsWith ( 'cursor:' )
278+ ? cursorIntegrations . find ( i => i . id === preferredAction . replace ( 'cursor:' , '' ) )
279+ : null ;
280+
281+ const effectivePreference =
282+ preferredAction === 'seer_solution' || ! preferredIntegration
283+ ? 'seer_solution'
284+ : preferredAction ;
285+
286+ const isSeerPreferred = effectivePreference === 'seer_solution' ;
287+
288+ // Check if there are duplicate names among integrations (need to show ID to distinguish)
289+ const hasDuplicateNames =
290+ cursorIntegrations . length > 1 &&
291+ new Set ( cursorIntegrations . map ( i => i . name ) ) . size < cursorIntegrations . length ;
292+
293+ // If no integrations, show simple Seer button
294+ if ( cursorIntegrations . length === 0 ) {
295+ return (
296+ < Button
297+ size = "sm"
298+ priority = { primaryButtonPriority }
299+ busy = { isSelectingRootCause }
300+ onClick = { submitFindSolution }
301+ title = { findSolutionTitle }
302+ >
303+ { t ( 'Find Solution' ) }
304+ </ Button >
305+ ) ;
306+ }
307+
308+ const dropdownItems = [
309+ ...( isSeerPreferred
310+ ? [ ]
311+ : [
312+ {
313+ key : 'seer_solution' ,
314+ label : t ( 'Find Solution with Seer' ) ,
315+ onAction : submitFindSolution ,
316+ disabled : isSelectingRootCause ,
317+ } ,
318+ ] ) ,
319+ // Show all integrations except the currently preferred one
320+ ...cursorIntegrations
321+ . filter ( integration => `cursor:${ integration . id } ` !== effectivePreference )
322+ . map ( integration => ( {
323+ key : `cursor:${ integration . id } ` ,
324+ label : (
325+ < Flex gap = "md" align = "center" >
326+ < PluginIcon pluginId = "cursor" size = { 20 } />
327+ < div > { t ( 'Send to %s' , integration . name ) } </ div >
328+ { hasDuplicateNames && (
329+ < SmallIntegrationIdText > ({ integration . id } )</ SmallIntegrationIdText >
330+ ) }
331+ </ Flex >
332+ ) ,
333+ onAction : ( ) => handleLaunchCodingAgent ( integration . id , integration . name ) ,
334+ disabled : isLoadingAgents || isLaunchingAgent ,
335+ } ) ) ,
336+ ] ;
337+
338+ const primaryButtonLabel = isSeerPreferred
339+ ? t ( 'Find Solution with Seer' )
340+ : hasDuplicateNames
341+ ? t ( 'Send to %s (%s)' , preferredIntegration ! . name , preferredIntegration ! . id )
342+ : t ( 'Send to %s' , preferredIntegration ! . name ) ;
343+
344+ const primaryButtonProps = isSeerPreferred
345+ ? {
346+ onClick : submitFindSolution ,
347+ busy : isSelectingRootCause ,
348+ icon : undefined ,
349+ children : primaryButtonLabel ,
350+ }
351+ : {
352+ onClick : ( ) =>
353+ handleLaunchCodingAgent ( preferredIntegration ! . id , preferredIntegration ! . name ) ,
354+ busy : isLaunchingAgent ,
355+ icon : < PluginIcon pluginId = "cursor" size = { 16 } /> ,
356+ children : primaryButtonLabel ,
357+ } ;
358+
359+ return (
360+ < ButtonBar merged gap = "0" >
361+ < Button
362+ size = "sm"
363+ priority = { primaryButtonPriority }
364+ disabled = { isLoadingAgents }
365+ { ...primaryButtonProps }
366+ >
367+ { primaryButtonProps . children }
368+ </ Button >
369+ < DropdownMenu
370+ items = { dropdownItems }
371+ trigger = { ( triggerProps , isOpen ) => (
372+ < DropdownTrigger
373+ { ...triggerProps }
374+ size = "sm"
375+ priority = { primaryButtonPriority }
376+ busy = { isSelectingRootCause || isLaunchingAgent }
377+ disabled = { isLoadingAgents }
378+ aria-label = { t ( 'More solution options' ) }
379+ icon = {
380+ isSelectingRootCause || isLaunchingAgent ? (
381+ < LoadingIndicator size = { 12 } />
382+ ) : (
383+ < IconChevron direction = { isOpen ? 'up' : 'down' } size = "xs" />
384+ )
385+ }
386+ />
387+ ) }
388+ />
389+ </ ButtonBar >
390+ ) ;
391+ }
392+
250393function AutofixRootCauseDisplay ( {
251394 causes,
252395 groupId,
@@ -276,9 +419,11 @@ function AutofixRootCauseDisplay({
276419 runId
277420 ) ;
278421
279- const [ preferredAction , setPreferredAction ] = useLocalStorageState <
280- 'seer_solution' | 'cursor_background_agent'
281- > ( 'autofix:rootCauseActionPreference' , 'seer_solution' ) ;
422+ // Stores 'seer_solution' or an integration ID (e.g., 'cursor:123')
423+ const [ preferredAction , setPreferredAction ] = useLocalStorageState < string > (
424+ 'autofix:rootCauseActionPreference' ,
425+ 'seer_solution'
426+ ) ;
282427
283428 const handleSelectDescription = ( ) => {
284429 if ( descriptionRef . current ) {
@@ -323,27 +468,29 @@ function AutofixRootCauseDisplay({
323468 } ) ;
324469 } ;
325470
326- // Find Cursor integration specifically
327- const cursorIntegration = codingAgentIntegrations . find (
471+ const cursorIntegrations = codingAgentIntegrations . filter (
328472 integration => integration . provider === 'cursor'
329473 ) ;
330474
331- const handleLaunchCodingAgent = ( ) => {
332- if ( ! cursorIntegration ) {
475+ const handleLaunchCodingAgent = ( integrationId : string , integrationName : string ) => {
476+ const targetIntegration = cursorIntegrations . find ( i => i . id === integrationId ) ;
477+
478+ if ( ! targetIntegration ) {
333479 return ;
334480 }
335481
336- // Save user preference
337- setPreferredAction ( 'cursor_background_agent' ) ;
482+ // Save user preference with specific integration ID
483+ setPreferredAction ( `cursor: ${ integrationId } ` ) ;
338484
339- // Show immediate loading toast
340- addLoadingMessage ( t ( 'Launching %s...' , cursorIntegration . name ) , { duration : 60000 } ) ;
485+ addLoadingMessage ( t ( 'Launching %s...' , integrationName ) , {
486+ duration : 60000 ,
487+ } ) ;
341488
342489 const instruction = solutionText . trim ( ) ;
343490
344491 launchCodingAgent ( {
345- integrationId : cursorIntegration . id ,
346- agentName : cursorIntegration . name ,
492+ integrationId : targetIntegration . id ,
493+ agentName : targetIntegration . name ,
347494 triggerSource : 'root_cause' ,
348495 instruction : instruction || undefined ,
349496 } ) ;
@@ -478,107 +625,17 @@ function AutofixRootCauseDisplay({
478625 />
479626 < ButtonBar >
480627 < CopyRootCauseButton cause = { cause } event = { event } />
481- { cursorIntegration ? (
482- < ButtonBar merged gap = "0" >
483- { preferredAction === 'cursor_background_agent' ? (
484- < Fragment >
485- < Button
486- size = "sm"
487- priority = { primaryButtonPriority }
488- busy = { isLaunchingAgent }
489- disabled = { isLoadingAgents }
490- onClick = { handleLaunchCodingAgent }
491- title = { t ( 'Send to Cursor Cloud Agent' ) }
492- icon = { < PluginIcon pluginId = "cursor" size = { 16 } /> }
493- >
494- { t ( 'Send to Cursor Cloud Agent' ) }
495- </ Button >
496- < DropdownMenu
497- items = { [
498- {
499- key : 'seer-solution' ,
500- label : t ( 'Find Solution with Seer' ) ,
501- onAction : submitFindSolution ,
502- disabled : isSelectingRootCause ,
503- } ,
504- ] }
505- trigger = { ( triggerProps , isOpen ) => (
506- < DropdownTrigger
507- { ...triggerProps }
508- size = "sm"
509- priority = { primaryButtonPriority }
510- busy = { isSelectingRootCause }
511- disabled = { isLoadingAgents }
512- aria-label = { t ( 'More solution options' ) }
513- icon = {
514- isSelectingRootCause ? (
515- < LoadingIndicator size = { 12 } />
516- ) : (
517- < IconChevron direction = { isOpen ? 'up' : 'down' } size = "xs" />
518- )
519- }
520- />
521- ) }
522- />
523- </ Fragment >
524- ) : (
525- < Fragment >
526- < Button
527- size = "sm"
528- priority = { primaryButtonPriority }
529- busy = { isSelectingRootCause }
530- disabled = { isLoadingAgents }
531- onClick = { submitFindSolution }
532- title = { findSolutionTitle }
533- >
534- { t ( 'Find Solution with Seer' ) }
535- </ Button >
536- < DropdownMenu
537- items = { [
538- {
539- key : 'cursor-agent' ,
540- label : (
541- < Flex gap = "md" align = "center" >
542- < PluginIcon pluginId = "cursor" size = { 20 } />
543- < div > { t ( 'Send to Cursor Cloud Agent' ) } </ div >
544- </ Flex >
545- ) ,
546- onAction : handleLaunchCodingAgent ,
547- disabled : isLoadingAgents || isLaunchingAgent ,
548- } ,
549- ] }
550- trigger = { ( triggerProps , isOpen ) => (
551- < DropdownTrigger
552- { ...triggerProps }
553- size = "sm"
554- priority = { primaryButtonPriority }
555- busy = { isLaunchingAgent }
556- disabled = { isLoadingAgents }
557- aria-label = { t ( 'More solution options' ) }
558- icon = {
559- isLaunchingAgent ? (
560- < LoadingIndicator size = { 12 } />
561- ) : (
562- < IconChevron direction = { isOpen ? 'up' : 'down' } size = "xs" />
563- )
564- }
565- />
566- ) }
567- />
568- </ Fragment >
569- ) }
570- </ ButtonBar >
571- ) : (
572- < Button
573- size = "sm"
574- priority = { primaryButtonPriority }
575- busy = { isSelectingRootCause }
576- onClick = { submitFindSolution }
577- title = { findSolutionTitle }
578- >
579- { t ( 'Find Solution' ) }
580- </ Button >
581- ) }
628+ < SolutionActionButton
629+ cursorIntegrations = { cursorIntegrations }
630+ preferredAction = { preferredAction }
631+ primaryButtonPriority = { primaryButtonPriority }
632+ isSelectingRootCause = { isSelectingRootCause }
633+ isLaunchingAgent = { isLaunchingAgent }
634+ isLoadingAgents = { isLoadingAgents }
635+ submitFindSolution = { submitFindSolution }
636+ handleLaunchCodingAgent = { handleLaunchCodingAgent }
637+ findSolutionTitle = { findSolutionTitle }
638+ />
582639 </ ButtonBar >
583640 { status === AutofixStatus . COMPLETED && (
584641 < AutofixStepFeedback stepType = "root_cause" groupId = { groupId } runId = { runId } />
@@ -697,3 +754,8 @@ const DropdownTrigger = styled(Button)`
697754 border-radius: 0 ${ p => p . theme . borderRadius } ${ p => p . theme . borderRadius } 0;
698755 border-left: none;
699756` ;
757+
758+ const SmallIntegrationIdText = styled ( 'div' ) `
759+ font-size: ${ p => p . theme . fontSize . sm } ;
760+ color: ${ p => p . theme . subText } ;
761+ ` ;
0 commit comments