Skip to content

Commit cbfcac6

Browse files
authored
Merge pull request #259806 from microsoft/tyriar/258480
Move output monitoring for background terminals into separate class
2 parents cd6ca63 + d4c72ff commit cbfcac6

File tree

2 files changed

+77
-18
lines changed

2 files changed

+77
-18
lines changed
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
/*---------------------------------------------------------------------------------------------
2+
* Copyright (c) Microsoft Corporation. All rights reserved.
3+
* Licensed under the MIT License. See License.txt in the project root for license information.
4+
*--------------------------------------------------------------------------------------------*/
5+
6+
import { CancellationToken } from '../../../../../base/common/cancellation.js';
7+
import { Emitter, Event } from '../../../../../base/common/event.js';
8+
import { Disposable } from '../../../../../base/common/lifecycle.js';
9+
import { ILanguageModelsService } from '../../../chat/common/languageModels.js';
10+
import { IChatService } from '../../../chat/common/chatService.js';
11+
import { racePollingOrPrompt, promptForMorePolling, pollForOutputAndIdle } from './bufferOutputPolling.js';
12+
13+
export interface IOutputMonitor extends Disposable {
14+
readonly isIdle: boolean;
15+
16+
readonly onDidFinishCommand: Event<void>;
17+
readonly onDidIdle: Event<void>;
18+
readonly onDidTimeout: Event<void>;
19+
20+
startMonitoring(
21+
chatService: IChatService,
22+
command: string,
23+
invocationContext: any,
24+
token: CancellationToken
25+
): Promise<{ terminalExecutionIdleBeforeTimeout: boolean; output: string; pollDurationMs?: number; modelOutputEvalResponse?: string }>;
26+
}
27+
28+
export class OutputMonitor extends Disposable implements IOutputMonitor {
29+
private _isIdle = false;
30+
31+
private readonly _onDidFinishCommand = this._register(new Emitter<void>());
32+
readonly onDidFinishCommand = this._onDidFinishCommand.event;
33+
private readonly _onDidIdle = this._register(new Emitter<void>());
34+
readonly onDidIdle = this._onDidIdle.event;
35+
private readonly _onDidTimeout = this._register(new Emitter<void>());
36+
readonly onDidTimeout = this._onDidTimeout.event;
37+
38+
get isIdle(): boolean {
39+
return this._isIdle;
40+
}
41+
42+
constructor(
43+
private readonly _execution: { getOutput: () => string; isActive?: () => Promise<boolean> },
44+
@ILanguageModelsService private readonly _languageModelsService: ILanguageModelsService
45+
) {
46+
super();
47+
}
48+
49+
async startMonitoring(
50+
chatService: IChatService,
51+
command: string,
52+
invocationContext: any,
53+
token: CancellationToken
54+
): Promise<{ terminalExecutionIdleBeforeTimeout: boolean; output: string; pollDurationMs?: number; modelOutputEvalResponse?: string }> {
55+
let result = await pollForOutputAndIdle(this._execution, false, token, this._languageModelsService);
56+
57+
if (!result.terminalExecutionIdleBeforeTimeout) {
58+
result = await racePollingOrPrompt(
59+
() => pollForOutputAndIdle(this._execution, true, token, this._languageModelsService),
60+
() => promptForMorePolling(command, token, invocationContext, chatService),
61+
result,
62+
token,
63+
this._languageModelsService,
64+
this._execution
65+
);
66+
}
67+
68+
return result;
69+
}
70+
}

src/vs/workbench/contrib/terminalContrib/chatAgentTools/browser/tools/runInTerminalTool.ts

Lines changed: 7 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,12 @@ import { ITerminalLogService } from '../../../../../../platform/terminal/common/
2222
import { IWorkspaceContextService } from '../../../../../../platform/workspace/common/workspace.js';
2323
import { IRemoteAgentService } from '../../../../../services/remote/common/remoteAgentService.js';
2424
import { IChatService, type IChatTerminalToolInvocationData } from '../../../../chat/common/chatService.js';
25-
import { ILanguageModelsService } from '../../../../chat/common/languageModels.js';
2625
import { CountTokensCallback, ILanguageModelToolsService, IPreparedToolInvocation, IToolData, IToolImpl, IToolInvocation, IToolInvocationPreparationContext, IToolResult, ToolDataSource, ToolProgress, type IToolConfirmationAction, type IToolConfirmationMessages } from '../../../../chat/common/languageModelToolsService.js';
2726
import { ITerminalService, type ITerminalInstance } from '../../../../terminal/browser/terminal.js';
2827
import type { XtermTerminal } from '../../../../terminal/browser/xterm/xtermTerminal.js';
2928
import { ITerminalProfileResolverService } from '../../../../terminal/common/terminal.js';
3029
import { getRecommendedToolsOverRunInTerminal } from '../alternativeRecommendation.js';
31-
import { getOutput, pollForOutputAndIdle, promptForMorePolling, racePollingOrPrompt } from '../bufferOutputPolling.js';
30+
import { getOutput } from '../bufferOutputPolling.js';
3231
import { CommandLineAutoApprover, type ICommandApprovalResultWithReason } from '../commandLineAutoApprover.js';
3332
import { BasicExecuteStrategy } from '../executeStrategy/basicExecuteStrategy.js';
3433
import type { ITerminalExecuteStrategy } from '../executeStrategy/executeStrategy.js';
@@ -38,6 +37,7 @@ import { isPowerShell } from '../runInTerminalHelpers.js';
3837
import { extractInlineSubCommands, splitCommandLineIntoSubCommands } from '../subCommands.js';
3938
import { ShellIntegrationQuality, ToolTerminalCreator, type IToolTerminal } from '../toolTerminalCreator.js';
4039
import { Codicon } from '../../../../../../base/common/codicons.js';
40+
import { OutputMonitor } from '../outputMonitor.js';
4141
import type { TerminalNewAutoApproveButtonData } from '../../../../chat/browser/chatContentParts/toolInvocationParts/chatTerminalToolSubPart.js';
4242

4343
const TERMINAL_SESSION_STORAGE_KEY = 'chat.terminalSessions';
@@ -159,7 +159,6 @@ export class RunInTerminalTool extends Disposable implements IToolImpl {
159159
@IRemoteAgentService private readonly _remoteAgentService: IRemoteAgentService,
160160
@IChatService private readonly _chatService: IChatService,
161161
@IWorkspaceContextService private readonly _workspaceContextService: IWorkspaceContextService,
162-
@ILanguageModelsService private readonly _languageModelsService: ILanguageModelsService
163162
) {
164163
super();
165164

@@ -414,31 +413,21 @@ export class RunInTerminalTool extends Disposable implements IToolImpl {
414413

415414
if (args.isBackground) {
416415
let outputAndIdle: { terminalExecutionIdleBeforeTimeout: boolean; output: string; pollDurationMs?: number; modelOutputEvalResponse?: string } | undefined = undefined;
416+
let outputMonitor: OutputMonitor | undefined = undefined;
417417
try {
418418
this._logService.debug(`RunInTerminalTool: Starting background execution \`${command}\``);
419419

420420
const execution = new BackgroundTerminalExecution(toolTerminal.instance, xterm, command);
421421
RunInTerminalTool._backgroundExecutions.set(termId, execution);
422422

423-
outputAndIdle = await pollForOutputAndIdle(execution, false, token, this._languageModelsService);
423+
outputMonitor = this._instantiationService.createInstance(OutputMonitor, execution);
424+
store.add(outputMonitor);
425+
426+
outputAndIdle = await outputMonitor.startMonitoring(this._chatService, command, invocation.context!, token);
424427
if (token.isCancellationRequested) {
425428
throw new CancellationError();
426429
}
427430

428-
if (!outputAndIdle.terminalExecutionIdleBeforeTimeout) {
429-
outputAndIdle = await racePollingOrPrompt(
430-
() => pollForOutputAndIdle(execution, true, token, this._languageModelsService),
431-
() => promptForMorePolling(command, token, invocation.context!, this._chatService),
432-
outputAndIdle,
433-
token,
434-
this._languageModelsService,
435-
execution
436-
);
437-
if (token.isCancellationRequested) {
438-
throw new CancellationError();
439-
}
440-
}
441-
442431
let resultText = (
443432
didUserEditCommand
444433
? `Note: The user manually edited the command to \`${command}\`, and that command is now running in terminal with ID=${termId}`

0 commit comments

Comments
 (0)