From db32a6185ba1a356bb5be7e45fac8f18043e6210 Mon Sep 17 00:00:00 2001 From: Adam Weidman Date: Sun, 8 Mar 2026 20:19:00 -0400 Subject: [PATCH] refactor: align remote background wiring with execution ID naming --- .../core/src/agents/remote-invocation.test.ts | 33 ++++++++++------- packages/core/src/agents/remote-invocation.ts | 35 ++++++++++--------- 2 files changed, 39 insertions(+), 29 deletions(-) diff --git a/packages/core/src/agents/remote-invocation.test.ts b/packages/core/src/agents/remote-invocation.test.ts index 8f2d3157de..5d712b5d71 100644 --- a/packages/core/src/agents/remote-invocation.test.ts +++ b/packages/core/src/agents/remote-invocation.test.ts @@ -611,20 +611,23 @@ describe('RemoteAgentInvocation', () => { }, ); - let pid: number | undefined; + let executionId: number | undefined; const onExit = vi.fn(); let unsubscribeOnExit: (() => void) | undefined; const streamedOutputChunks: string[] = []; let unsubscribeStream: (() => void) | undefined; const updateOutput = vi.fn((output: unknown) => { - if (output === 'Chunk 1' && pid) { - ShellExecutionService.background(pid); - unsubscribeStream = ShellExecutionService.subscribe(pid, (event) => { - if (event.type === 'data' && typeof event.chunk === 'string') { - streamedOutputChunks.push(event.chunk); - } - }); + if (output === 'Chunk 1' && executionId) { + ShellExecutionService.background(executionId); + unsubscribeStream = ShellExecutionService.subscribe( + executionId, + (event) => { + if (event.type === 'data' && typeof event.chunk === 'string') { + streamedOutputChunks.push(event.chunk); + } + }, + ); } }); @@ -638,19 +641,23 @@ describe('RemoteAgentInvocation', () => { new AbortController().signal, updateOutput, undefined, - (newPid) => { - pid = newPid; - unsubscribeOnExit = ShellExecutionService.onExit(newPid, onExit); + (newExecutionId) => { + executionId = newExecutionId; + unsubscribeOnExit = ShellExecutionService.onExit( + newExecutionId, + onExit, + ); }, ); const result = await resultPromise; - expect(pid).toBeDefined(); + expect(executionId).toBeDefined(); expect(result.returnDisplay).toContain( 'Remote agent moved to background', ); expect(result.data).toMatchObject({ - pid, + executionId, + pid: executionId, initialOutput: 'Chunk 1', }); diff --git a/packages/core/src/agents/remote-invocation.ts b/packages/core/src/agents/remote-invocation.ts index 728cdc8fb6..da1ff3128e 100644 --- a/packages/core/src/agents/remote-invocation.ts +++ b/packages/core/src/agents/remote-invocation.ts @@ -6,6 +6,7 @@ import { BaseToolInvocation, + type BackgroundExecutionData, type ToolConfirmationOutcome, type ToolResult, type ToolCallConfirmationDetails, @@ -147,7 +148,7 @@ export class RemoteAgentInvocation extends BaseToolInvocation< } private publishBackgroundDelta( - pid: number, + executionId: number, previousOutput: string, nextOutput: string, ): string { @@ -157,7 +158,7 @@ export class RemoteAgentInvocation extends BaseToolInvocation< if (nextOutput.startsWith(previousOutput)) { ExecutionLifecycleService.appendOutput( - pid, + executionId, nextOutput.slice(previousOutput.length), ); return nextOutput; @@ -166,7 +167,7 @@ export class RemoteAgentInvocation extends BaseToolInvocation< // If the reassembled output changes non-monotonically, resync by appending // the full latest snapshot with a clear separator. ExecutionLifecycleService.appendOutput( - pid, + executionId, `\n\n[Output updated]\n${nextOutput}`, ); return nextOutput; @@ -176,7 +177,7 @@ export class RemoteAgentInvocation extends BaseToolInvocation< _signal: AbortSignal, updateOutput?: (output: string | AnsiOutput) => void, _shellExecutionConfig?: unknown, - setPidCallback?: (pid: number) => void, + setExecutionIdCallback?: (executionId: number) => void, ): Promise { const reassembler = new A2AResultReassembler(); const executionController = new AbortController(); @@ -199,8 +200,8 @@ export class RemoteAgentInvocation extends BaseToolInvocation< }, }; } - const backgroundPid = pid; - setPidCallback?.(backgroundPid); + const backgroundExecutionId = pid; + setExecutionIdCallback?.(backgroundExecutionId); const run = async () => { let lastOutput = ''; @@ -244,7 +245,7 @@ export class RemoteAgentInvocation extends BaseToolInvocation< const currentOutput = reassembler.toString(); lastOutput = this.publishBackgroundDelta( - backgroundPid, + backgroundExecutionId, lastOutput, currentOutput, ); @@ -273,20 +274,20 @@ export class RemoteAgentInvocation extends BaseToolInvocation< `[RemoteAgent] Final response from ${this.definition.name}:\n${JSON.stringify(finalResponse, null, 2)}`, ); - ExecutionLifecycleService.completeExecution(backgroundPid, { + ExecutionLifecycleService.completeExecution(backgroundExecutionId, { exitCode: 0, }); } catch (error: unknown) { const partialOutput = reassembler.toString(); lastOutput = this.publishBackgroundDelta( - backgroundPid, + backgroundExecutionId, lastOutput, partialOutput, ); const errorMessage = `Error calling remote agent: ${ error instanceof Error ? error.message : String(error) }`; - ExecutionLifecycleService.completeExecution(backgroundPid, { + ExecutionLifecycleService.completeExecution(backgroundExecutionId, { error: new Error(errorMessage), aborted: executionController.signal.aborted, exitCode: executionController.signal.aborted ? 130 : 1, @@ -306,15 +307,17 @@ export class RemoteAgentInvocation extends BaseToolInvocation< if (executionResult.backgrounded) { const command = `${this.getDescription()}: ${this.params.query}`; - const backgroundMessage = `Remote agent moved to background (PID: ${backgroundPid}). Output hidden. Press Ctrl+B to view.`; + const backgroundMessage = `Remote agent moved to background (PID: ${backgroundExecutionId}). Output hidden. Press Ctrl+B to view.`; + const data: BackgroundExecutionData = { + executionId: backgroundExecutionId, + pid: backgroundExecutionId, + command, + initialOutput: executionResult.output, + }; return { llmContent: [{ text: backgroundMessage }], returnDisplay: backgroundMessage, - data: { - pid: backgroundPid, - command, - initialOutput: executionResult.output, - }, + data, }; }