@@ -37,6 +37,7 @@ import {
3737 buildUserSteeringHintPrompt ,
3838 GeminiCliOperation ,
3939 getPlanModeExitMessage ,
40+ isBackgroundExecutionData ,
4041} from '@google/gemini-cli-core' ;
4142import type {
4243 Config ,
@@ -94,10 +95,10 @@ type ToolResponseWithParts = ToolCallResponseInfo & {
9495 llmContent ?: PartListUnion ;
9596} ;
9697
97- interface ShellToolData {
98- pid ? : number ;
99- command ? : string ;
100- initialOutput ? : string ;
98+ interface BackgroundedToolInfo {
99+ pid : number ;
100+ command : string ;
101+ initialOutput : string ;
101102}
102103
103104enum StreamProcessingStatus {
@@ -111,15 +112,32 @@ const SUPPRESSED_TOOL_ERRORS_NOTE =
111112const LOW_VERBOSITY_FAILURE_NOTE =
112113 'This request failed. Press F12 for diagnostics, or run /settings and change "Error Verbosity" to full for full details.' ;
113114
114- function isShellToolData ( data : unknown ) : data is ShellToolData {
115- if ( typeof data !== 'object' || data === null ) {
116- return false ;
115+ function getBackgroundedToolInfo (
116+ toolCall : TrackedCompletedToolCall | TrackedCancelledToolCall ,
117+ ) : BackgroundedToolInfo | undefined {
118+ const response = toolCall . response as ToolResponseWithParts ;
119+ const rawData : unknown = response ?. data ;
120+ if ( ! isBackgroundExecutionData ( rawData ) ) {
121+ return undefined ;
122+ }
123+
124+ if ( rawData . pid === undefined ) {
125+ return undefined ;
117126 }
118- const d = data as Partial < ShellToolData > ;
127+
128+ return {
129+ pid : rawData . pid ,
130+ command : rawData . command ?? toolCall . request . name ,
131+ initialOutput : rawData . initialOutput ?? '' ,
132+ } ;
133+ }
134+
135+ function isBackgroundableExecutingToolCall (
136+ toolCall : TrackedToolCall ,
137+ ) : toolCall is TrackedExecutingToolCall {
119138 return (
120- ( d . pid === undefined || typeof d . pid === 'number' ) &&
121- ( d . command === undefined || typeof d . command === 'string' ) &&
122- ( d . initialOutput === undefined || typeof d . initialOutput === 'string' )
139+ toolCall . status === CoreToolCallStatus . Executing &&
140+ typeof toolCall . pid === 'number'
123141 ) ;
124142}
125143
@@ -319,13 +337,11 @@ export const useGeminiStream = (
319337 getPreferredEditor ,
320338 ) ;
321339
322- const activeToolPtyId = useMemo ( ( ) => {
323- const executingShellTool = toolCalls . find (
324- ( tc ) =>
325- tc . status === 'executing' && tc . request . name === 'run_shell_command' ,
340+ const activeBackgroundExecutionId = useMemo ( ( ) => {
341+ const executingBackgroundableTool = toolCalls . find (
342+ isBackgroundableExecutingToolCall ,
326343 ) ;
327- // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
328- return ( executingShellTool as TrackedExecutingToolCall | undefined ) ?. pid ;
344+ return executingBackgroundableTool ?. pid ;
329345 } , [ toolCalls ] ) ;
330346
331347 const onExec = useCallback (
@@ -358,7 +374,7 @@ export const useGeminiStream = (
358374 setShellInputFocused ,
359375 terminalWidth ,
360376 terminalHeight ,
361- activeToolPtyId ,
377+ activeBackgroundExecutionId ,
362378 ) ;
363379
364380 const streamingState = useMemo (
@@ -536,7 +552,8 @@ export const useGeminiStream = (
536552 onComplete : ( result : { userSelection : 'disable' | 'keep' } ) => void ;
537553 } | null > ( null ) ;
538554
539- const activePtyId = activeShellPtyId || activeToolPtyId ;
555+ const activePtyId =
556+ activeShellPtyId ?? activeBackgroundExecutionId ?? undefined ;
540557
541558 const prevActiveShellPtyIdRef = useRef < number | null > ( null ) ;
542559 useEffect ( ( ) => {
@@ -1678,26 +1695,16 @@ export const useGeminiStream = (
16781695 ! processedMemoryToolsRef . current . has ( t . request . callId ) ,
16791696 ) ;
16801697
1681- // Handle backgrounded shell tools
1682- completedAndReadyToSubmitTools . forEach ( ( t ) => {
1683- const isShell = t . request . name === 'run_shell_command' ;
1684- // Access result from the tracked tool call response
1685- const response = t . response as ToolResponseWithParts ;
1686- const rawData = response ?. data ;
1687- const data = isShellToolData ( rawData ) ? rawData : undefined ;
1688-
1689- // Use data.pid for shell commands moved to the background.
1690- const pid = data ?. pid ;
1691-
1692- if ( isShell && pid ) {
1693- // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
1694- const command = ( data ?. [ 'command' ] as string ) ?? 'shell' ;
1695- // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
1696- const initialOutput = ( data ?. [ 'initialOutput' ] as string ) ?? '' ;
1697-
1698- registerBackgroundShell ( pid , command , initialOutput ) ;
1698+ for ( const toolCall of completedAndReadyToSubmitTools ) {
1699+ const backgroundedTool = getBackgroundedToolInfo ( toolCall ) ;
1700+ if ( backgroundedTool ) {
1701+ registerBackgroundShell (
1702+ backgroundedTool . pid ,
1703+ backgroundedTool . command ,
1704+ backgroundedTool . initialOutput ,
1705+ ) ;
16991706 }
1700- } ) ;
1707+ }
17011708
17021709 if ( newSuccessfulMemorySaves . length > 0 ) {
17031710 // Perform the refresh only if there are new ones.
0 commit comments