Skip to content

Commit 92d979b

Browse files
authored
fix: correct terminal sandbox icon in thinking dropdown (#304320)
When sandbox is enabled, the tool-level icon was set to terminalSecure (lock) at registration time, which leaked into every rendering path during streaming before we knew if a specific command was actually sandboxed. This caused unsandboxed commands (requestUnsandboxedExecution=true) to show the lock icon. Fix: - Set toolData.icon to always be Codicon.terminal (no lock). The per-command isSandboxWrapped flag in toolSpecificData is the authoritative source. - In the existing autorun in trackToolMetadata, update the icon element when the tool transitions out of streaming and toolSpecificData becomes available. - Store icon elements in toolIconsByCallId map for direct access. Fixes #303505
1 parent 4f95860 commit 92d979b

File tree

2 files changed

+18
-2
lines changed

2 files changed

+18
-2
lines changed

src/vs/workbench/contrib/chat/browser/widget/chatContentParts/chatThinkingContentPart.ts

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import { $, clearNode, getWindow, hide, scheduleAtNextAnimationFrame } from '../
77
import { alert } from '../../../../../../base/browser/ui/aria/aria.js';
88
import { DomScrollableElement } from '../../../../../../base/browser/ui/scrollbar/scrollableElement.js';
99
import { ScrollbarVisibility } from '../../../../../../base/common/scrollable.js';
10-
import { IChatMarkdownContent, IChatThinkingPart, IChatToolInvocation, IChatToolInvocationSerialized } from '../../../common/chatService/chatService.js';
10+
import { IChatMarkdownContent, IChatTerminalToolInvocationData, IChatThinkingPart, IChatToolInvocation, IChatToolInvocationSerialized } from '../../../common/chatService/chatService.js';
1111
import { IChatContentPartRenderContext, IChatContentPart } from './chatContentParts.js';
1212
import { IChatRendererContent } from '../../../common/model/chatViewModel.js';
1313
import { ChatConfiguration, ThinkingDisplayMode } from '../../../common/constants.js';
@@ -228,6 +228,7 @@ export class ChatThinkingContentPart extends ChatCollapsibleContentPart implemen
228228
private workingSpinnerLabel: HTMLElement | undefined;
229229
private availableMessagesByCategory = new Map<WorkingMessageCategory, string[]>();
230230
private readonly toolWrappersByCallId = new Map<string, HTMLElement>();
231+
private readonly toolIconsByCallId = new Map<string, HTMLElement>();
231232
private readonly toolLabelsByCallId = new Map<string, string>();
232233
private readonly toolDisposables = this._register(new DisposableMap<string, DisposableStore>());
233234
private readonly ownedToolParts = new Map<string, IDisposable>();
@@ -1224,6 +1225,7 @@ ${this.hookCount > 0 ? `EXAMPLES WITH BLOCKED CONTENT (from hooks):
12241225
const wrapper = this.toolWrappersByCallId.get(toolCallId);
12251226
if (wrapper) {
12261227
this.toolWrappersByCallId.delete(toolCallId);
1228+
this.toolIconsByCallId.delete(toolCallId);
12271229
}
12281230

12291231
this.appendedItemCount = Math.max(0, this.appendedItemCount - 1);
@@ -1342,6 +1344,7 @@ ${this.hookCount > 0 ? `EXAMPLES WITH BLOCKED CONTENT (from hooks):
13421344
if (wrapper) {
13431345
wrapper.remove();
13441346
this.toolWrappersByCallId.delete(toolCallId);
1347+
this.toolIconsByCallId.delete(toolCallId);
13451348
}
13461349

13471350
// make sure to remove any lazy item as well
@@ -1470,6 +1473,18 @@ ${this.hookCount > 0 ? `EXAMPLES WITH BLOCKED CONTENT (from hooks):
14701473
// queue item to be removed if it was streaming and presentation is hidden
14711474
if (isStreaming && currentState.type !== IChatToolInvocation.StateKind.Streaming) {
14721475
isStreaming = false;
1476+
1477+
// Update terminal tool icon based on sandbox wrapping state
1478+
const termData = toolInvocationOrMarkdown.toolSpecificData as IChatTerminalToolInvocationData | undefined;
1479+
if (termData?.kind === 'terminal') {
1480+
const iconEl = this.toolIconsByCallId.get(toolCallId);
1481+
if (iconEl) {
1482+
const newIcon = termData.commandLine?.isSandboxWrapped ? Codicon.terminalSecure : Codicon.terminal;
1483+
iconEl.className = 'chat-thinking-icon';
1484+
iconEl.classList.add(...ThemeIcon.asClassNameArray(newIcon));
1485+
}
1486+
}
1487+
14731488
if (toolInvocationOrMarkdown.presentation === 'hidden') {
14741489
this.pendingRemovals.push({ toolCallId: toolInvocationOrMarkdown.toolCallId, toolLabel: currentToolLabel });
14751490
this.schedulePendingRemovalsFlush();
@@ -1628,6 +1643,7 @@ ${this.hookCount > 0 ? `EXAMPLES WITH BLOCKED CONTENT (from hooks):
16281643
const isToolInvocation = toolInvocationOrMarkdown && (toolInvocationOrMarkdown.kind === 'toolInvocation' || toolInvocationOrMarkdown.kind === 'toolInvocationSerialized');
16291644
if (isToolInvocation && toolInvocationOrMarkdown.toolCallId) {
16301645
this.toolWrappersByCallId.set(toolInvocationOrMarkdown.toolCallId, itemWrapper);
1646+
this.toolIconsByCallId.set(toolInvocationOrMarkdown.toolCallId, iconElement);
16311647
}
16321648

16331649
this.appendToWrapper(itemWrapper);

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -274,7 +274,7 @@ export async function createRunInTerminalToolData(
274274
modelDescription,
275275
userDescription: localize('runInTerminalTool.userDescription', 'Run commands in the terminal'),
276276
source: ToolDataSource.Internal,
277-
icon: isSandboxEnabled ? Codicon.terminalSecure : Codicon.terminal,
277+
icon: Codicon.terminal,
278278
inputSchema: {
279279
type: 'object',
280280
properties: {

0 commit comments

Comments
 (0)