From 9fbb7b870847322097301203a1ba7aa318c1decf Mon Sep 17 00:00:00 2001 From: dileepyavan <52841896+dileepyavan@users.noreply.github.com> Date: Thu, 29 Jan 2026 13:14:59 -0800 Subject: [PATCH 1/2] In sandbox mode for chatUI, the command should be displayed without sandbox command and environment variables (#291395) changes --- .../toolInvocationParts/chatTerminalToolProgressPart.ts | 2 +- .../workbench/contrib/chat/common/chatService/chatService.ts | 2 ++ .../browser/tools/commandLineRewriter/commandLineRewriter.ts | 2 ++ .../tools/commandLineRewriter/commandLineSandboxRewriter.ts | 3 ++- .../chatAgentTools/browser/tools/runInTerminalTool.ts | 3 +++ 5 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/chat/browser/widget/chatContentParts/toolInvocationParts/chatTerminalToolProgressPart.ts b/src/vs/workbench/contrib/chat/browser/widget/chatContentParts/toolInvocationParts/chatTerminalToolProgressPart.ts index c5eae902688..47580c82591 100644 --- a/src/vs/workbench/contrib/chat/browser/widget/chatContentParts/toolInvocationParts/chatTerminalToolProgressPart.ts +++ b/src/vs/workbench/contrib/chat/browser/widget/chatContentParts/toolInvocationParts/chatTerminalToolProgressPart.ts @@ -274,7 +274,7 @@ export class ChatTerminalToolProgressPart extends BaseChatToolInvocationSubPart ]); this._titleElement = elements.title; - const command = (terminalData.commandLine.userEdited ?? terminalData.commandLine.toolEdited ?? terminalData.commandLine.original).trimStart(); + const command = (terminalData.commandLine.forDisplay ?? terminalData.commandLine.userEdited ?? terminalData.commandLine.toolEdited ?? terminalData.commandLine.original).trimStart(); this._commandText = command; this._terminalOutputContextKey = ChatContextKeys.inChatTerminalToolOutput.bindTo(this._contextKeyService); diff --git a/src/vs/workbench/contrib/chat/common/chatService/chatService.ts b/src/vs/workbench/contrib/chat/common/chatService/chatService.ts index 313657bbe42..75b4f81832a 100644 --- a/src/vs/workbench/contrib/chat/common/chatService/chatService.ts +++ b/src/vs/workbench/contrib/chat/common/chatService/chatService.ts @@ -413,6 +413,8 @@ export interface IChatTerminalToolInvocationData { original: string; userEdited?: string; toolEdited?: string; + // command to show in the chat UI (potentially different from what is actually run in the terminal) + forDisplay?: string; }; /** The working directory URI for the terminal */ cwd?: UriComponents; diff --git a/src/vs/workbench/contrib/terminalContrib/chatAgentTools/browser/tools/commandLineRewriter/commandLineRewriter.ts b/src/vs/workbench/contrib/terminalContrib/chatAgentTools/browser/tools/commandLineRewriter/commandLineRewriter.ts index 1ba417cd3fe..3a3c4a3c955 100644 --- a/src/vs/workbench/contrib/terminalContrib/chatAgentTools/browser/tools/commandLineRewriter/commandLineRewriter.ts +++ b/src/vs/workbench/contrib/terminalContrib/chatAgentTools/browser/tools/commandLineRewriter/commandLineRewriter.ts @@ -22,4 +22,6 @@ export interface ICommandLineRewriterOptions { export interface ICommandLineRewriterResult { rewritten: string; reasoning: string; + //for scenarios where we want to show a different command in the chat UI than what is actually run in the terminal + forDisplay?: string; } diff --git a/src/vs/workbench/contrib/terminalContrib/chatAgentTools/browser/tools/commandLineRewriter/commandLineSandboxRewriter.ts b/src/vs/workbench/contrib/terminalContrib/chatAgentTools/browser/tools/commandLineRewriter/commandLineSandboxRewriter.ts index a85cb243ba9..030afe5a76a 100644 --- a/src/vs/workbench/contrib/terminalContrib/chatAgentTools/browser/tools/commandLineRewriter/commandLineSandboxRewriter.ts +++ b/src/vs/workbench/contrib/terminalContrib/chatAgentTools/browser/tools/commandLineRewriter/commandLineSandboxRewriter.ts @@ -29,7 +29,8 @@ export class CommandLineSandboxRewriter extends Disposable implements ICommandLi const wrappedCommand = this._sandboxService.wrapCommand(options.commandLine); return { rewritten: wrappedCommand, - reasoning: 'Wrapped command for sandbox execution' + reasoning: 'Wrapped command for sandbox execution', + forDisplay: options.commandLine, // show the command that is passed as input. In this case, the output from CommandLinePreventHistoryRewriter }; } } diff --git a/src/vs/workbench/contrib/terminalContrib/chatAgentTools/browser/tools/runInTerminalTool.ts b/src/vs/workbench/contrib/terminalContrib/chatAgentTools/browser/tools/runInTerminalTool.ts index 3f07cbaadeb..7a986811d1e 100644 --- a/src/vs/workbench/contrib/terminalContrib/chatAgentTools/browser/tools/runInTerminalTool.ts +++ b/src/vs/workbench/contrib/terminalContrib/chatAgentTools/browser/tools/runInTerminalTool.ts @@ -456,6 +456,7 @@ export class RunInTerminalTool extends Disposable implements IToolImpl { const terminalCommandId = `tool-${generateUuid()}`; let rewrittenCommand: string | undefined = args.command; + let forDisplayCommand: string | undefined = undefined; for (const rewriter of this._commandLineRewriters) { const rewriteResult = await rewriter.rewrite({ commandLine: rewrittenCommand, @@ -465,6 +466,7 @@ export class RunInTerminalTool extends Disposable implements IToolImpl { }); if (rewriteResult) { rewrittenCommand = rewriteResult.rewritten; + forDisplayCommand = rewriteResult.forDisplay; this._logService.info(`RunInTerminalTool: Command rewritten by ${rewriter.constructor.name}: ${rewriteResult.reasoning}`); } } @@ -476,6 +478,7 @@ export class RunInTerminalTool extends Disposable implements IToolImpl { commandLine: { original: args.command, toolEdited: rewrittenCommand === args.command ? undefined : rewrittenCommand, + forDisplay: forDisplayCommand, }, cwd, language, From 218de2609fa95470b639b7490b26d2cff3d8b983 Mon Sep 17 00:00:00 2001 From: Justin Chen <54879025+justschen@users.noreply.github.com> Date: Thu, 29 Jan 2026 13:16:57 -0800 Subject: [PATCH 2/2] thinking header fix: styling and better generic text (#291462) * thinking header fix: styling and better generic text * only increment in constructor if there is thinking text * reset the timer, accidentally set to 1 for testing --- .../chatThinkingContentPart.ts | 32 ++++++++++++++++--- .../media/chatThinkingContent.css | 14 ++++++++ 2 files changed, 42 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/contrib/chat/browser/widget/chatContentParts/chatThinkingContentPart.ts b/src/vs/workbench/contrib/chat/browser/widget/chatContentParts/chatThinkingContentPart.ts index 89a4d81bbf0..0d29e172d50 100644 --- a/src/vs/workbench/contrib/chat/browser/widget/chatContentParts/chatThinkingContentPart.ts +++ b/src/vs/workbench/contrib/chat/browser/widget/chatContentParts/chatThinkingContentPart.ts @@ -27,7 +27,7 @@ import { Lazy } from '../../../../../../base/common/lazy.js'; import { Emitter } from '../../../../../../base/common/event.js'; import { DisposableMap, DisposableStore, IDisposable } from '../../../../../../base/common/lifecycle.js'; import { autorun } from '../../../../../../base/common/observable.js'; -import { CancellationToken } from '../../../../../../base/common/cancellation.js'; +import { CancellationTokenSource } from '../../../../../../base/common/cancellation.js'; import { IChatMarkdownAnchorService } from './chatMarkdownAnchorService.js'; import { ChatMessageRole, ILanguageModelsService } from '../../../common/languageModels.js'; import { ExtensionIdentifier } from '../../../../../../platform/extensions/common/extensions.js'; @@ -189,6 +189,10 @@ export class ChatThinkingContentPart extends ChatCollapsibleContentPart implemen } this.currentThinkingValue = initialText; + if (initialText.trim()) { + this.appendedItemCount++; + } + // Alert screen reader users that thinking has started alert(localize('chat.thinking.started', 'Thinking')); @@ -618,6 +622,9 @@ export class ChatThinkingContentPart extends ChatCollapsibleContentPart implemen } private async generateTitleViaLLM(): Promise { + const cts = new CancellationTokenSource(); + const timeout = setTimeout(() => cts.cancel(), 5000); + try { let models = await this.languageModelsService.selectLanguageModels({ vendor: 'copilot', id: 'copilot-fast' }); if (!models.length) { @@ -628,6 +635,11 @@ export class ChatThinkingContentPart extends ChatCollapsibleContentPart implemen return; } + if (cts.token.isCancellationRequested) { + this.setFallbackTitle(); + return; + } + let context: string; if (this.extractedTitles.length > 0) { context = this.extractedTitles.join(', '); @@ -720,11 +732,14 @@ export class ChatThinkingContentPart extends ChatCollapsibleContentPart implemen new ExtensionIdentifier('core'), [{ role: ChatMessageRole.User, content: [{ type: 'text', value: prompt }] }], {}, - CancellationToken.None + cts.token ); let generatedTitle = ''; for await (const part of response.stream) { + if (cts.token.isCancellationRequested) { + break; + } if (Array.isArray(part)) { for (const p of part) { if (p.type === 'text') { @@ -736,6 +751,11 @@ export class ChatThinkingContentPart extends ChatCollapsibleContentPart implemen } } + if (cts.token.isCancellationRequested) { + this.setFallbackTitle(); + return; + } + await response.result; generatedTitle = generatedTitle.trim(); @@ -755,6 +775,9 @@ export class ChatThinkingContentPart extends ChatCollapsibleContentPart implemen } } catch (error) { // fall through to default title + } finally { + clearTimeout(timeout); + cts.dispose(); } this.setFallbackTitle(); @@ -784,8 +807,8 @@ export class ChatThinkingContentPart extends ChatCollapsibleContentPart implemen } private setFallbackTitle(): void { - const finalLabel = this.toolInvocationCount > 0 - ? localize('chat.thinking.finished.withTools', 'Finished working and invoked {0} tool{1}', this.toolInvocationCount, this.toolInvocationCount === 1 ? '' : 's') + const finalLabel = this.appendedItemCount > 0 + ? localize('chat.thinking.finished.withSteps', 'Finished with {0} step{1}', this.appendedItemCount, this.appendedItemCount === 1 ? '' : 's') : localize('chat.thinking.finished', 'Finished Working'); this.currentTitle = finalLabel; @@ -1150,6 +1173,7 @@ export class ChatThinkingContentPart extends ChatCollapsibleContentPart implemen if (this._store.isDisposed) { return; } + this.appendedItemCount++; this.textContainer = $('.chat-thinking-item.markdown-content'); if (content.value) { // Use lazy rendering when collapsed to preserve order with tool items diff --git a/src/vs/workbench/contrib/chat/browser/widget/chatContentParts/media/chatThinkingContent.css b/src/vs/workbench/contrib/chat/browser/widget/chatContentParts/media/chatThinkingContent.css index 6b310d88306..2a250a99d7a 100644 --- a/src/vs/workbench/contrib/chat/browser/widget/chatContentParts/media/chatThinkingContent.css +++ b/src/vs/workbench/contrib/chat/browser/widget/chatContentParts/media/chatThinkingContent.css @@ -17,6 +17,20 @@ margin: 0px; } + > .chat-used-context-label .monaco-button.monaco-icon-button { + line-height: 1.5em; + font-size: var(--vscode-chat-font-size-body-m); + margin-top: 1px; + + .codicon { + font-size: 13px; + } + + .codicon::before { + font-size: var(--vscode-chat-font-size-body-s); + } + } + /* shimmer animation stuffs */ .chat-thinking-spinner-item .chat-thinking-spinner-label { background: linear-gradient(90deg,