Skip to content

Commit d4c72ff

Browse files
committed
Simplify output monitor
1 parent 6e2b55c commit d4c72ff

File tree

3 files changed

+6
-380
lines changed

3 files changed

+6
-380
lines changed

src/vs/workbench/contrib/terminalContrib/chatAgentTools/browser/outputMonitor.ts

Lines changed: 4 additions & 106 deletions
Original file line numberDiff line numberDiff line change
@@ -3,26 +3,14 @@
33
* Licensed under the MIT License. See License.txt in the project root for license information.
44
*--------------------------------------------------------------------------------------------*/
55

6-
import { timeout } from '../../../../../base/common/async.js';
76
import { CancellationToken } from '../../../../../base/common/cancellation.js';
87
import { Emitter, Event } from '../../../../../base/common/event.js';
98
import { Disposable } from '../../../../../base/common/lifecycle.js';
109
import { ILanguageModelsService } from '../../../chat/common/languageModels.js';
1110
import { IChatService } from '../../../chat/common/chatService.js';
12-
import { assessOutputForErrors, PollingConsts, racePollingOrPrompt, promptForMorePolling } from './bufferOutputPolling.js';
13-
14-
export const enum OutputMonitorAction {
15-
PollingStarted = 'polling_started',
16-
OutputReceived = 'output_received',
17-
IdleDetected = 'idle_detected',
18-
TimeoutReached = 'timeout_reached',
19-
CancellationRequested = 'cancellation_requested',
20-
ExtendedPollingStarted = 'extended_polling_started',
21-
AssessmentCompleted = 'assessment_completed'
22-
}
11+
import { racePollingOrPrompt, promptForMorePolling, pollForOutputAndIdle } from './bufferOutputPolling.js';
2312

2413
export interface IOutputMonitor extends Disposable {
25-
readonly actions: OutputMonitorAction[];
2614
readonly isIdle: boolean;
2715

2816
readonly onDidFinishCommand: Event<void>;
@@ -38,7 +26,6 @@ export interface IOutputMonitor extends Disposable {
3826
}
3927

4028
export class OutputMonitor extends Disposable implements IOutputMonitor {
41-
private readonly _actions: OutputMonitorAction[] = [];
4229
private _isIdle = false;
4330

4431
private readonly _onDidFinishCommand = this._register(new Emitter<void>());
@@ -48,17 +35,13 @@ export class OutputMonitor extends Disposable implements IOutputMonitor {
4835
private readonly _onDidTimeout = this._register(new Emitter<void>());
4936
readonly onDidTimeout = this._onDidTimeout.event;
5037

51-
get actions(): OutputMonitorAction[] {
52-
return [...this._actions];
53-
}
54-
5538
get isIdle(): boolean {
5639
return this._isIdle;
5740
}
5841

5942
constructor(
6043
private readonly _execution: { getOutput: () => string; isActive?: () => Promise<boolean> },
61-
private readonly _languageModelsService: ILanguageModelsService
44+
@ILanguageModelsService private readonly _languageModelsService: ILanguageModelsService
6245
) {
6346
super();
6447
}
@@ -69,11 +52,11 @@ export class OutputMonitor extends Disposable implements IOutputMonitor {
6952
invocationContext: any,
7053
token: CancellationToken
7154
): Promise<{ terminalExecutionIdleBeforeTimeout: boolean; output: string; pollDurationMs?: number; modelOutputEvalResponse?: string }> {
72-
let result = await this._startMonitoring(false, token);
55+
let result = await pollForOutputAndIdle(this._execution, false, token, this._languageModelsService);
7356

7457
if (!result.terminalExecutionIdleBeforeTimeout) {
7558
result = await racePollingOrPrompt(
76-
() => this._startMonitoring(true, token),
59+
() => pollForOutputAndIdle(this._execution, true, token, this._languageModelsService),
7760
() => promptForMorePolling(command, token, invocationContext, chatService),
7861
result,
7962
token,
@@ -84,89 +67,4 @@ export class OutputMonitor extends Disposable implements IOutputMonitor {
8467

8568
return result;
8669
}
87-
88-
protected async _startMonitoring(extendedPolling: boolean, token: CancellationToken): Promise<{ terminalExecutionIdleBeforeTimeout: boolean; output: string; pollDurationMs?: number; modelOutputEvalResponse?: string }> {
89-
this._addAction(OutputMonitorAction.PollingStarted);
90-
if (extendedPolling) {
91-
this._addAction(OutputMonitorAction.ExtendedPollingStarted);
92-
}
93-
94-
const maxWaitMs = extendedPolling ? PollingConsts.ExtendedPollingMaxDuration : PollingConsts.FirstPollingMaxDuration;
95-
const maxInterval = PollingConsts.MaxPollingIntervalDuration;
96-
let currentInterval = PollingConsts.MinPollingDuration;
97-
const pollStartTime = Date.now();
98-
99-
let lastBufferLength = 0;
100-
let noNewDataCount = 0;
101-
let buffer = '';
102-
let terminalExecutionIdleBeforeTimeout = false;
103-
104-
while (true) {
105-
if (token.isCancellationRequested) {
106-
this._addAction(OutputMonitorAction.CancellationRequested);
107-
break;
108-
}
109-
const now = Date.now();
110-
const elapsed = now - pollStartTime;
111-
const timeLeft = maxWaitMs - elapsed;
112-
113-
if (timeLeft <= 0) {
114-
this._addAction(OutputMonitorAction.TimeoutReached);
115-
this._onDidTimeout.fire();
116-
break;
117-
}
118-
119-
// Cap the wait so we never overshoot timeLeft
120-
const waitTime = Math.min(currentInterval, timeLeft);
121-
await timeout(waitTime);
122-
123-
// Check again immediately after waking
124-
if (Date.now() - pollStartTime >= maxWaitMs) {
125-
this._addAction(OutputMonitorAction.TimeoutReached);
126-
this._onDidTimeout.fire();
127-
break;
128-
}
129-
130-
currentInterval = Math.min(currentInterval * 2, maxInterval);
131-
132-
buffer = this._execution.getOutput();
133-
const currentBufferLength = buffer.length;
134-
135-
if (currentBufferLength !== lastBufferLength) {
136-
this._addAction(OutputMonitorAction.OutputReceived);
137-
}
138-
139-
if (currentBufferLength === lastBufferLength) {
140-
noNewDataCount++;
141-
} else {
142-
noNewDataCount = 0;
143-
lastBufferLength = currentBufferLength;
144-
}
145-
146-
if (noNewDataCount >= PollingConsts.MinNoDataEvents) {
147-
if (this._execution.isActive && ((await this._execution.isActive()) === true)) {
148-
noNewDataCount = 0;
149-
lastBufferLength = currentBufferLength;
150-
continue;
151-
}
152-
this._addAction(OutputMonitorAction.IdleDetected);
153-
this._isIdle = true;
154-
this._onDidIdle.fire();
155-
terminalExecutionIdleBeforeTimeout = true;
156-
157-
this._addAction(OutputMonitorAction.AssessmentCompleted);
158-
const modelOutputEvalResponse = await assessOutputForErrors(buffer, token, this._languageModelsService);
159-
160-
this._onDidFinishCommand.fire();
161-
return { modelOutputEvalResponse, terminalExecutionIdleBeforeTimeout, output: buffer, pollDurationMs: Date.now() - pollStartTime + (extendedPolling ? PollingConsts.FirstPollingMaxDuration : 0) };
162-
}
163-
}
164-
165-
this._onDidFinishCommand.fire();
166-
return { terminalExecutionIdleBeforeTimeout: false, output: buffer, pollDurationMs: Date.now() - pollStartTime + (extendedPolling ? PollingConsts.FirstPollingMaxDuration : 0) };
167-
}
168-
169-
private _addAction(action: OutputMonitorAction): void {
170-
this._actions.push(action);
171-
}
17270
}

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

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ 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';
@@ -38,7 +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';
41-
import { OutputMonitor, type OutputMonitorAction } from '../outputMonitor.js';
40+
import { OutputMonitor } from '../outputMonitor.js';
4241
import type { TerminalNewAutoApproveButtonData } from '../../../../chat/browser/chatContentParts/toolInvocationParts/chatTerminalToolSubPart.js';
4342

4443
const TERMINAL_SESSION_STORAGE_KEY = 'chat.terminalSessions';
@@ -160,7 +159,6 @@ export class RunInTerminalTool extends Disposable implements IToolImpl {
160159
@IRemoteAgentService private readonly _remoteAgentService: IRemoteAgentService,
161160
@IChatService private readonly _chatService: IChatService,
162161
@IWorkspaceContextService private readonly _workspaceContextService: IWorkspaceContextService,
163-
@ILanguageModelsService private readonly _languageModelsService: ILanguageModelsService
164162
) {
165163
super();
166164

@@ -422,7 +420,7 @@ export class RunInTerminalTool extends Disposable implements IToolImpl {
422420
const execution = new BackgroundTerminalExecution(toolTerminal.instance, xterm, command);
423421
RunInTerminalTool._backgroundExecutions.set(termId, execution);
424422

425-
outputMonitor = new OutputMonitor(execution, this._languageModelsService);
423+
outputMonitor = this._instantiationService.createInstance(OutputMonitor, execution);
426424
store.add(outputMonitor);
427425

428426
outputAndIdle = await outputMonitor.startMonitoring(this._chatService, command, invocation.context!, token);
@@ -475,7 +473,6 @@ export class RunInTerminalTool extends Disposable implements IToolImpl {
475473
pollDurationMs: outputAndIdle?.pollDurationMs,
476474
inputUserChars,
477475
inputUserSigint,
478-
outputMonitorActions: outputMonitor?.actions,
479476
});
480477
}
481478
} else {
@@ -808,7 +805,6 @@ export class RunInTerminalTool extends Disposable implements IToolImpl {
808805
exitCode: number | undefined;
809806
inputUserChars: number;
810807
inputUserSigint: boolean;
811-
outputMonitorActions?: OutputMonitorAction[];
812808
}) {
813809
type TelemetryEvent = {
814810
terminalSessionId: string;

0 commit comments

Comments
 (0)