From e27c6aadc1564db3bb0ff5b13532c922c5767ced Mon Sep 17 00:00:00 2001 From: Eric Wheeler Date: Fri, 7 Mar 2025 12:48:30 -0800 Subject: [PATCH] fix: terminal process isolation between parallel Cline tasks These changes ensure proper isolation by preventing terminal process output from one Cline task appearing in another task's context when multiple Cline instances are running in parallel. - Add taskId parameter to TerminalRegistry.getTerminals to filter terminals by Cline task ID - Update Cline.ts to use taskId-filtered terminals Signed-off-by: Eric Wheeler --- src/core/Cline.ts | 6 +++--- src/integrations/terminal/Terminal.ts | 11 ++++++++++ src/integrations/terminal/TerminalRegistry.ts | 20 +++++++++++++++---- 3 files changed, 30 insertions(+), 7 deletions(-) diff --git a/src/core/Cline.ts b/src/core/Cline.ts index 15116387fa..285f2e9899 100644 --- a/src/core/Cline.ts +++ b/src/core/Cline.ts @@ -3472,8 +3472,8 @@ export class Cline { details += "\n(No open tabs)" } - const busyTerminals = TerminalRegistry.getTerminals(true) - const inactiveTerminals = TerminalRegistry.getTerminals(false) + const busyTerminals = TerminalRegistry.getTerminals(true, this.taskId) + const inactiveTerminals = TerminalRegistry.getTerminals(false, this.taskId) if (busyTerminals.length > 0 && this.didEditFile) { await delay(300) // delay after saving file to let terminals catch up @@ -3522,7 +3522,7 @@ export class Cline { } } - // First check if any inactive terminals have completed processes with output + // First check if any inactive terminals in this task have completed processes with output const terminalsWithOutput = inactiveTerminals.filter((terminal) => { const completedProcesses = terminal.getProcessesWithOutput() return completedProcesses.length > 0 diff --git a/src/integrations/terminal/Terminal.ts b/src/integrations/terminal/Terminal.ts index a483fb1d2f..4c4edaf5ff 100644 --- a/src/integrations/terminal/Terminal.ts +++ b/src/integrations/terminal/Terminal.ts @@ -93,8 +93,19 @@ export class Terminal { /** * Cleans the process queue by removing processes that no longer have unretrieved output + * or don't belong to the current task */ public cleanCompletedProcessQueue(): void { + // If this terminal has no task ID, it's not associated with any active task + // In this case, we should remove all processes to prevent their output from appearing + // in any task's context + if (this.taskId === undefined) { + this.completedProcesses = [] + return + } + + // If the terminal is associated with a task, keep only processes with unretrieved output + // This ensures that when a task is active, it only sees output from its own processes this.completedProcesses = this.completedProcesses.filter((process) => process.hasUnretrievedOutput()) } diff --git a/src/integrations/terminal/TerminalRegistry.ts b/src/integrations/terminal/TerminalRegistry.ts index 04611ccc3f..32c301fb54 100644 --- a/src/integrations/terminal/TerminalRegistry.ts +++ b/src/integrations/terminal/TerminalRegistry.ts @@ -167,14 +167,26 @@ export class TerminalRegistry { } return terminal.process ? terminal.process.isHot : false } - /** - * Gets terminals filtered by busy state + * Gets terminals filtered by busy state and optionally by task ID * @param busy Whether to get busy or non-busy terminals + * @param taskId Optional task ID to filter terminals by * @returns Array of Terminal objects */ - static getTerminals(busy: boolean): Terminal[] { - return this.getAllTerminals().filter((t) => t.busy === busy) + static getTerminals(busy: boolean, taskId?: string): Terminal[] { + return this.getAllTerminals().filter((t) => { + // Filter by busy state + if (t.busy !== busy) { + return false + } + + // If taskId is provided, also filter by taskId + if (taskId !== undefined && t.taskId !== taskId) { + return false + } + + return true + }) } static cleanup() {