From c323aca1d86b46d05daf1ce8b5015e8b7c91aa1d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 2 Apr 2026 15:28:04 +0000 Subject: [PATCH 1/3] Initial plan From 4ca8211277ddaffd8fb67c17cefc469d149ea778 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 2 Apr 2026 16:49:07 +0000 Subject: [PATCH 2/3] Fix user-edited terminal command being ignored in Sessions and ConfirmTerminalCommandTool 1. Add userEditedInput field to ISessionToolCallApprovedAction protocol 2. Update reducer to use userEditedInput when present 3. Send userEditedInput in agentHostSessionHandler's _awaitToolConfirmation 4. Return user-edited command in ConfirmTerminalCommandTool.invoke() result 5. Add tests for ConfirmTerminalCommandTool user-edit behavior Agent-Logs-Url: https://github.com/microsoft/vscode/sessions/a1ae137d-c533-4696-b7fe-4e2580ce340b Co-authored-by: meganrogge <29464607+meganrogge@users.noreply.github.com> --- .../common/state/protocol/actions.ts | 2 + .../common/state/protocol/reducers.ts | 2 +- .../agentHost/agentHostSessionHandler.ts | 7 ++- .../tools/runInTerminalConfirmationTool.ts | 13 ++++- .../runInTerminalTool.test.ts | 49 +++++++++++++++++++ 5 files changed, 70 insertions(+), 3 deletions(-) diff --git a/src/vs/platform/agentHost/common/state/protocol/actions.ts b/src/vs/platform/agentHost/common/state/protocol/actions.ts index 9a9b6cfb8719a..2dcd27477588d 100644 --- a/src/vs/platform/agentHost/common/state/protocol/actions.ts +++ b/src/vs/platform/agentHost/common/state/protocol/actions.ts @@ -282,6 +282,8 @@ export interface ISessionToolCallApprovedAction extends IToolCallActionBase { approved: true; /** How the tool was confirmed */ confirmed: ToolCallConfirmationReason; + /** If the user edited the tool input before approving, this is the new input */ + userEditedInput?: string; } /** diff --git a/src/vs/platform/agentHost/common/state/protocol/reducers.ts b/src/vs/platform/agentHost/common/state/protocol/reducers.ts index 128074285b2e7..c3e84af7e2e39 100644 --- a/src/vs/platform/agentHost/common/state/protocol/reducers.ts +++ b/src/vs/platform/agentHost/common/state/protocol/reducers.ts @@ -344,7 +344,7 @@ export function sessionReducer(state: ISessionState, action: ISessionAction, log status: ToolCallStatus.Running, ...base, invocationMessage: tc.invocationMessage, - toolInput: tc.toolInput, + toolInput: action.userEditedInput ?? tc.toolInput, confirmed: action.confirmed, }; } diff --git a/src/vs/workbench/contrib/chat/browser/agentSessions/agentHost/agentHostSessionHandler.ts b/src/vs/workbench/contrib/chat/browser/agentSessions/agentHost/agentHostSessionHandler.ts index 3dc1a2ef8abab..465067bbccc10 100644 --- a/src/vs/workbench/contrib/chat/browser/agentSessions/agentHost/agentHostSessionHandler.ts +++ b/src/vs/workbench/contrib/chat/browser/agentSessions/agentHost/agentHostSessionHandler.ts @@ -27,7 +27,7 @@ import { IInstantiationService } from '../../../../../../platform/instantiation/ import { ILogService } from '../../../../../../platform/log/common/log.js'; import { IProductService } from '../../../../../../platform/product/common/productService.js'; import { IWorkspaceContextService } from '../../../../../../platform/workspace/common/workspace.js'; -import { ChatRequestQueueKind, IChatProgress, IChatService, IChatToolInvocation, ToolConfirmKind } from '../../../common/chatService/chatService.js'; +import { ChatRequestQueueKind, IChatProgress, IChatService, IChatToolInvocation, ToolConfirmKind, type IChatTerminalToolInvocationData } from '../../../common/chatService/chatService.js'; import { IChatSession, IChatSessionContentProvider, IChatSessionHistoryItem, IChatSessionItem, IChatSessionRequestHistoryItem } from '../../../common/chatSessionsService.js'; import { ChatAgentLocation, ChatModeKind } from '../../../common/constants.js'; import { IChatEditingService } from '../../../common/editing/chatEditingService.js'; @@ -1029,6 +1029,10 @@ export class AgentHostSessionHandler extends Disposable implements IChatSessionC const approved = reason.type !== ToolConfirmKind.Denied && reason.type !== ToolConfirmKind.Skipped; this._logService.info(`[AgentHost] Tool confirmation: toolCallId=${toolCallId}, approved=${approved}`); if (approved) { + // Check if the user edited the command in the confirmation UI + const terminalData = invocation.toolSpecificData?.kind === 'terminal' ? invocation.toolSpecificData as IChatTerminalToolInvocationData : undefined; + const userEditedInput = terminalData?.commandLine.userEdited; + const confirmAction = { type: ActionType.SessionToolCallConfirmed as const, session: session.toString(), @@ -1036,6 +1040,7 @@ export class AgentHostSessionHandler extends Disposable implements IChatSessionC toolCallId, approved: true as const, confirmed: ToolCallConfirmationReason.UserAction, + ...(userEditedInput ? { userEditedInput } : {}), }; const seq = this._clientState.applyOptimistic(confirmAction); this._config.connection.dispatchAction(confirmAction, this._clientState.clientId, seq); diff --git a/src/vs/workbench/contrib/terminalContrib/chatAgentTools/browser/tools/runInTerminalConfirmationTool.ts b/src/vs/workbench/contrib/terminalContrib/chatAgentTools/browser/tools/runInTerminalConfirmationTool.ts index 0e92d061c5139..02a3ed6dd9439 100644 --- a/src/vs/workbench/contrib/terminalContrib/chatAgentTools/browser/tools/runInTerminalConfirmationTool.ts +++ b/src/vs/workbench/contrib/terminalContrib/chatAgentTools/browser/tools/runInTerminalConfirmationTool.ts @@ -7,6 +7,7 @@ import { CancellationToken } from '../../../../../../base/common/cancellation.js import { Codicon } from '../../../../../../base/common/codicons.js'; import { localize } from '../../../../../../nls.js'; import { CountTokensCallback, IPreparedToolInvocation, IToolData, IToolInvocation, IToolInvocationPreparationContext, IToolResult, ToolDataSource, ToolInvocationPresentation, ToolProgress } from '../../../../chat/common/tools/languageModelToolsService.js'; +import { IChatTerminalToolInvocationData } from '../../../../chat/common/chatService/chatService.js'; import { RunInTerminalTool } from './runInTerminalTool.js'; import { TerminalToolId } from './toolIds.js'; @@ -76,7 +77,17 @@ export class ConfirmTerminalCommandTool extends RunInTerminalTool { return preparedInvocation; } override async invoke(invocation: IToolInvocation, countTokens: CountTokensCallback, progress: ToolProgress, token: CancellationToken): Promise { - // This is a confirmation-only tool - just return success + // This is a confirmation-only tool - return the user-edited command if they modified it + const toolSpecificData = invocation.toolSpecificData as IChatTerminalToolInvocationData | undefined; + const userEdited = toolSpecificData?.commandLine.userEdited; + if (userEdited && userEdited !== toolSpecificData?.commandLine.original) { + return { + content: [{ + kind: 'text', + value: `The user approved the command but edited it to: \`${userEdited}\`. Use this exact command when executing.` + }] + }; + } return { content: [{ kind: 'text', diff --git a/src/vs/workbench/contrib/terminalContrib/chatAgentTools/test/electron-browser/runInTerminalTool.test.ts b/src/vs/workbench/contrib/terminalContrib/chatAgentTools/test/electron-browser/runInTerminalTool.test.ts index df9449835f71b..d4c6faa7f1a45 100644 --- a/src/vs/workbench/contrib/terminalContrib/chatAgentTools/test/electron-browser/runInTerminalTool.test.ts +++ b/src/vs/workbench/contrib/terminalContrib/chatAgentTools/test/electron-browser/runInTerminalTool.test.ts @@ -2030,6 +2030,55 @@ suite('RunInTerminalTool', () => { const result = await confirmTool.prepareToolInvocation(context, CancellationToken.None); assertConfirmationRequired(result); }); + + test('should return user-edited command when user edits the command', async () => { + sandboxEnabled = false; + setAutoApprove({}); + + const { ConfirmTerminalCommandTool } = await import('../../browser/tools/runInTerminalConfirmationTool.js'); + const confirmTool = store.add(instantiationService.createInstance(ConfirmTerminalCommandTool)); + + const toolSpecificData: IChatTerminalToolInvocationData = { + kind: 'terminal', + commandLine: { + original: 'git commit -m "original message"', + userEdited: 'git commit -m "edited message"', + }, + language: 'sh', + }; + + const invocation = { + toolSpecificData, + } as any; + + const result = await confirmTool.invoke(invocation, async () => 0, { report: () => { } }, CancellationToken.None); + const value = (result.content[0] as { kind: 'text'; value: string }).value; + ok(value.includes('edited message')); + ok(value.includes('git commit -m "edited message"')); + }); + + test('should return yes when user does not edit the command', async () => { + sandboxEnabled = false; + setAutoApprove({}); + + const { ConfirmTerminalCommandTool } = await import('../../browser/tools/runInTerminalConfirmationTool.js'); + const confirmTool = store.add(instantiationService.createInstance(ConfirmTerminalCommandTool)); + + const toolSpecificData: IChatTerminalToolInvocationData = { + kind: 'terminal', + commandLine: { + original: 'echo hello', + }, + language: 'sh', + }; + + const invocation = { + toolSpecificData, + } as any; + + const result = await confirmTool.invoke(invocation, async () => 0, { report: () => { } }, CancellationToken.None); + strictEqual((result.content[0] as { kind: 'text'; value: string }).value, 'yes'); + }); }); }); From 8b864b844498dadd4784e2097532d4a265c7d99e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 2 Apr 2026 16:59:00 +0000 Subject: [PATCH 3/3] Refactor: remove unnecessary type assertion in agentHostSessionHandler Agent-Logs-Url: https://github.com/microsoft/vscode/sessions/a1ae137d-c533-4696-b7fe-4e2580ce340b Co-authored-by: meganrogge <29464607+meganrogge@users.noreply.github.com> --- .../agentSessions/agentHost/agentHostSessionHandler.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/chat/browser/agentSessions/agentHost/agentHostSessionHandler.ts b/src/vs/workbench/contrib/chat/browser/agentSessions/agentHost/agentHostSessionHandler.ts index 465067bbccc10..2744b07ed36bb 100644 --- a/src/vs/workbench/contrib/chat/browser/agentSessions/agentHost/agentHostSessionHandler.ts +++ b/src/vs/workbench/contrib/chat/browser/agentSessions/agentHost/agentHostSessionHandler.ts @@ -27,7 +27,7 @@ import { IInstantiationService } from '../../../../../../platform/instantiation/ import { ILogService } from '../../../../../../platform/log/common/log.js'; import { IProductService } from '../../../../../../platform/product/common/productService.js'; import { IWorkspaceContextService } from '../../../../../../platform/workspace/common/workspace.js'; -import { ChatRequestQueueKind, IChatProgress, IChatService, IChatToolInvocation, ToolConfirmKind, type IChatTerminalToolInvocationData } from '../../../common/chatService/chatService.js'; +import { ChatRequestQueueKind, IChatProgress, IChatService, IChatToolInvocation, ToolConfirmKind } from '../../../common/chatService/chatService.js'; import { IChatSession, IChatSessionContentProvider, IChatSessionHistoryItem, IChatSessionItem, IChatSessionRequestHistoryItem } from '../../../common/chatSessionsService.js'; import { ChatAgentLocation, ChatModeKind } from '../../../common/constants.js'; import { IChatEditingService } from '../../../common/editing/chatEditingService.js'; @@ -1030,8 +1030,8 @@ export class AgentHostSessionHandler extends Disposable implements IChatSessionC this._logService.info(`[AgentHost] Tool confirmation: toolCallId=${toolCallId}, approved=${approved}`); if (approved) { // Check if the user edited the command in the confirmation UI - const terminalData = invocation.toolSpecificData?.kind === 'terminal' ? invocation.toolSpecificData as IChatTerminalToolInvocationData : undefined; - const userEditedInput = terminalData?.commandLine.userEdited; + const toolSpecificData = invocation.toolSpecificData; + const userEditedInput = toolSpecificData?.kind === 'terminal' ? toolSpecificData.commandLine.userEdited : undefined; const confirmAction = { type: ActionType.SessionToolCallConfirmed as const,