219
* provides the exit code and cleanly delineated output.
220
*/
222
>
target: IShellCommandTarget,
223
>
command: string,
224
>
timeoutMs: number,
225
>
terminalManager: IAgentHostTerminalManager,
226
>
logService: ILogService,
227
>
): Promise<IShellCommandResult> {
228
>
const disposables = new DisposableStore();
229
>
230
>
const result = new Promise<IShellCommandResult>(resolve => {
231
>
let resolved = false;
232
>
const finish = (result: IShellCommandResult) => {
233
>
if (resolved) {
234
return;
235
}
237
>
disposables.dispose();
238
>
resolve(result);
239
>
};
240
>
241
>
disposables.add(terminalManager.onCommandFinished(target.terminalUri, event => {
242
const output = prepareOutputForModel(event.output);
243
const exitCode = event.exitCode ?? 0;
244
logService.info(`[ShellCommand] Command completed (shell integration) with exit code ${exitCode}`);
245
finish({ status: 'completed', exitCode, output });
247
>
248
>
registerAltBufferHandler(target, terminalManager, logService, disposables, finish);
249
>
250
>
disposables.add(terminalManager.onExit(target.terminalUri, (exitCode: number) => {
251
logService.info(`[ShellCommand] Shell exited unexpectedly with code ${exitCode}`);
252
const fullContent = terminalManager.getContent(target.terminalUri) ?? '';
253
finish({ status: 'shellExited', exitCode, output: prepareOutputForModel(fullContent) });
255
>
256
>
disposables.add(terminalManager.onClaimChanged(target.terminalUri, (claim) => {
257
if (claim.kind === TerminalClaimKind.Session && !claim.toolCallId) {
258
logService.info(`[ShellCommand] Continuing in background (claim narrowed)`);
259
finish({ status: 'background', output: '' });
260
}
262
>
263
>
const timer = setTimeout(() => {
264
logService.warn(`[ShellCommand] Command timed out after ${timeoutMs}ms`);
265
const fullContent = terminalManager.getContent(target.terminalUri) ?? '';
266
finish({ status: 'timeout', output: prepareOutputForModel(fullContent) });
268
>
disposables.add(toDisposable(() => clearTimeout(timer)));
269
>
270
>
});
271
>
272
>
try {
273
>
await terminalManager.sendText(target.terminalUri, `${prefixForHistorySuppression(target.shellType)}${command}`, {
274
>
shouldExecute: true,
275
>
bracketedPasteMode: shouldUseBracketedPasteMode(command),
276
>
});
277
>
} catch (err) {
278
disposables.dispose();
279
throw err;
280
}
282
>
return result;
283
>
}
284
285
/**