Skip to content

Commit 094ae8a

Browse files
committed
fix(terminal): await onCompleted callback when exit details are missing to resolve E2E race condition
1 parent c203126 commit 094ae8a

3 files changed

Lines changed: 15 additions & 3 deletions

File tree

apps/vscode-e2e/fixtures/fast-exit-shell-race.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
"toolCalls": [
99
{
1010
"name": "execute_command",
11-
"arguments": "{\"command\":\"python3 -c \\\"\\nimport sys\\nprint('boom', file=sys.stderr)\\nsys.exit(1)\\n\\\"\"}",
11+
"arguments": "{\"command\":\"echo boom >&2\\nfalse\"}",
1212
"id": "call_fast_exit_shell_race_001"
1313
}
1414
]

src/core/tools/ExecuteCommandTool.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -340,6 +340,8 @@ export async function executeCommandInTerminal(
340340
resolveOnCompleted = resolve
341341
})
342342

343+
let isCompletedTriggered = false
344+
343345
const callbacks: RooTerminalCallbacks = {
344346
onLine: async (lines: string, process: RooTerminalProcess) => {
345347
accumulatedOutput += lines
@@ -379,6 +381,7 @@ export async function executeCommandInTerminal(
379381
}
380382
},
381383
onCompleted: async (output: string | undefined) => {
384+
isCompletedTriggered = true
382385
try {
383386
clearTimeout(pendingCommandOutputEmitTimer)
384387
pendingCommandOutputEmitTimer = undefined
@@ -511,7 +514,7 @@ export async function executeCommandInTerminal(
511514
// Wait for onCompleted callback to finish if shell execution completed.
512515
// This ensures persistedResult is set before we try to use it, fixing the race
513516
// condition where exitDetails is set (sync) before the async onCompleted finishes.
514-
if (exitDetails && onCompletedPromise) {
517+
if ((exitDetails || isCompletedTriggered) && onCompletedPromise) {
515518
await onCompletedPromise
516519
}
517520

src/integrations/terminal/Terminal.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ export class Terminal extends BaseTerminal {
1515

1616
public cmdCounter: number = 0
1717

18+
private isFirstCommand: boolean = true
19+
1820
public activeShellExecution?: vscode.TerminalShellExecution
1921

2022
constructor(id: number, terminal: vscode.Terminal | undefined, cwd: string) {
@@ -121,10 +123,17 @@ export class Terminal extends BaseTerminal {
121123
pWaitFor(() => this.terminal.shellIntegration !== undefined, {
122124
timeout: Terminal.getShellIntegrationTimeout(),
123125
})
124-
.then(() => {
126+
.then(async () => {
125127
// Clean up temporary directory if shell integration is available, zsh did its job:
126128
ShellIntegrationManager.zshCleanupTmpDir(this.id)
127129

130+
// If this is a newly created terminal, give VS Code's shell integration script
131+
// a brief moment to finish activating in the PTY before submitting the command.
132+
if (this.isFirstCommand) {
133+
this.isFirstCommand = false
134+
await new Promise((resolve) => setTimeout(resolve, 1000))
135+
}
136+
128137
// Run the command in the terminal
129138
process.run(command)
130139
})

0 commit comments

Comments
 (0)