Skip to content

Commit cd13a25

Browse files
authored
Merge pull request #260594 from microsoft/tyriar/260573_2
Add message about auto approve rule being added and add links
2 parents a3090d9 + bddefd0 commit cd13a25

File tree

7 files changed

+83
-18
lines changed

7 files changed

+83
-18
lines changed

src/vs/workbench/contrib/chat/browser/chatContentParts/chatMarkdownContentPart.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
*--------------------------------------------------------------------------------------------*/
55

66
import * as dom from '../../../../../base/browser/dom.js';
7-
import { allowedMarkdownHtmlAttributes, MarkdownRendererMarkedOptions } from '../../../../../base/browser/markdownRenderer.js';
7+
import { allowedMarkdownHtmlAttributes, MarkdownRendererMarkedOptions, type MarkdownRenderOptions } from '../../../../../base/browser/markdownRenderer.js';
88
import { StandardMouseEvent } from '../../../../../base/browser/mouseEvent.js';
99
import { HoverPosition } from '../../../../../base/browser/ui/hover/hoverWidget.js';
1010
import { DomScrollableElement } from '../../../../../base/browser/ui/scrollbar/scrollableElement.js';
@@ -86,6 +86,7 @@ export class ChatMarkdownContentPart extends Disposable implements IChatContentP
8686
fillInIncompleteTokens = false,
8787
codeBlockStartIndex = 0,
8888
renderer: MarkdownRenderer,
89+
markdownRenderOptions: MarkdownRenderOptions | undefined,
8990
currentWidth: number,
9091
private readonly codeBlockModelCollection: CodeBlockModelCollection,
9192
private readonly rendererOptions: IChatMarkdownContentPartOptions,
@@ -244,6 +245,7 @@ export class ChatMarkdownContentPart extends Disposable implements IChatContentP
244245
asyncRenderCallback: () => this._onDidChangeHeight.fire(),
245246
markedOptions: markedOpts,
246247
markedExtensions,
248+
...markdownRenderOptions,
247249
}, this.domNode));
248250

249251
const markdownDecorationsRenderer = instantiationService.createInstance(ChatMarkdownDecorationsRenderer);

src/vs/workbench/contrib/chat/browser/chatContentParts/toolInvocationParts/chatTerminalMarkdownProgressPart.ts

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,13 @@
55

66
import { Codicon } from '../../../../../../base/common/codicons.js';
77
import { MarkdownString } from '../../../../../../base/common/htmlContent.js';
8+
import { DisposableStore } from '../../../../../../base/common/lifecycle.js';
89
import { ThemeIcon } from '../../../../../../base/common/themables.js';
910
import { MarkdownRenderer } from '../../../../../../editor/browser/widget/markdownRenderer/browser/markdownRenderer.js';
11+
import { ConfigurationTarget } from '../../../../../../platform/configuration/common/configuration.js';
1012
import { IInstantiationService } from '../../../../../../platform/instantiation/common/instantiation.js';
13+
import { IPreferencesService, type IOpenSettingsOptions } from '../../../../../services/preferences/common/preferences.js';
14+
import { TerminalContribSettingId } from '../../../../terminal/terminalContribExports.js';
1115
import { migrateLegacyTerminalToolSpecificData } from '../../../common/chat.js';
1216
import { IChatMarkdownContent, IChatToolInvocation, IChatToolInvocationSerialized, type IChatTerminalToolInvocationData, type ILegacyChatTerminalToolInvocationData } from '../../../common/chatService.js';
1317
import { CodeBlockModelCollection } from '../../../common/codeBlockModelCollection.js';
@@ -36,6 +40,7 @@ export class ChatTerminalMarkdownProgressPart extends BaseChatToolInvocationSubP
3640
codeBlockStartIndex: number,
3741
codeBlockModelCollection: CodeBlockModelCollection,
3842
@IInstantiationService instantiationService: IInstantiationService,
43+
@IPreferencesService preferencesService: IPreferencesService,
3944
) {
4045
super(toolInvocation);
4146

@@ -61,7 +66,43 @@ export class ChatTerminalMarkdownProgressPart extends BaseChatToolInvocationSubP
6166
wordWrap: 'on'
6267
}
6368
};
64-
this.markdownPart = this._register(instantiationService.createInstance(ChatMarkdownContentPart, chatMarkdownContent, context, editorPool, false, codeBlockStartIndex, renderer, currentWidthDelegate(), codeBlockModelCollection, { codeBlockRenderOptions }));
69+
this.markdownPart = this._register(instantiationService.createInstance(ChatMarkdownContentPart, chatMarkdownContent, context, editorPool, false, codeBlockStartIndex, renderer, {
70+
actionHandler: {
71+
callback: (content) => {
72+
const [type, scopeRaw] = content.split('_');
73+
switch (type) {
74+
case 'settings': {
75+
const scope = parseInt(scopeRaw);
76+
const target = !isNaN(scope) ? scope as ConfigurationTarget : undefined;
77+
const options: IOpenSettingsOptions = {
78+
jsonEditor: true,
79+
revealSetting: {
80+
key: TerminalContribSettingId.AutoApprove
81+
}
82+
};
83+
switch (target) {
84+
case ConfigurationTarget.APPLICATION: preferencesService.openApplicationSettings(options); break;
85+
case ConfigurationTarget.USER:
86+
case ConfigurationTarget.USER_LOCAL: preferencesService.openUserSettings(options); break;
87+
case ConfigurationTarget.USER_REMOTE: preferencesService.openRemoteSettings(options); break;
88+
case ConfigurationTarget.WORKSPACE:
89+
case ConfigurationTarget.WORKSPACE_FOLDER: preferencesService.openWorkspaceSettings(options); break;
90+
default: {
91+
// Fallback if something goes wrong
92+
preferencesService.openSettings({
93+
target: ConfigurationTarget.USER,
94+
query: `@id:${TerminalContribSettingId.AutoApprove}`,
95+
});
96+
break;
97+
}
98+
}
99+
break;
100+
}
101+
}
102+
},
103+
disposables: new DisposableStore(),
104+
},
105+
}, currentWidthDelegate(), codeBlockModelCollection, { codeBlockRenderOptions }));
65106
this._register(this.markdownPart.onDidChangeHeight(() => this._onDidChangeHeight.fire()));
66107
const icon = !toolInvocation.isConfirmed ?
67108
Codicon.error :

src/vs/workbench/contrib/chat/browser/chatContentParts/toolInvocationParts/chatTerminalToolSubPart.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -190,11 +190,15 @@ export class TerminalConfirmationWidgetSubPart extends BaseChatToolInvocationSub
190190
throw new ErrorNoTelemetry(`Cannot add new rule, existing setting is unexpected format`);
191191
}
192192
await this.configurationService.updateValue(TerminalContribSettingId.AutoApprove, newValue);
193+
if (newRules.length === 1) {
194+
terminalData.autoApproveInfo = new MarkdownString(localize('newRule', 'Auto approve rule {0} added', `[\`${newRules[0].key}\`](settings_a)`));
195+
} else if (newRules.length > 1) {
196+
terminalData.autoApproveInfo = new MarkdownString(localize('newRule.plural', 'Auto approve rules {0} added', newRules.map(r => `[\`${r.key}\`](settings_a)`).join(', ')));
197+
}
193198
break;
194199
}
195200
case 'configure': {
196201
this.preferencesService.openSettings({
197-
jsonEditor: false,
198202
target: ConfigurationTarget.USER,
199203
query: `@id:${TerminalContribSettingId.AutoApprove}`,
200204
});
@@ -233,6 +237,7 @@ export class TerminalConfirmationWidgetSubPart extends BaseChatToolInvocationSub
233237
false,
234238
this.codeBlockStartIndex,
235239
this.renderer,
240+
undefined,
236241
this.currentWidthDelegate(),
237242
this.codeBlockModelCollection,
238243
{ codeBlockRenderOptions }

src/vs/workbench/contrib/chat/browser/chatContentParts/toolInvocationParts/chatToolConfirmationSubPart.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -316,7 +316,7 @@ export class ToolConfirmationSubPart extends BaseChatToolInvocationSubPart {
316316
}
317317

318318
private _makeMarkdownPart(container: HTMLElement, message: string | IMarkdownString, codeBlockRenderOptions: ICodeBlockRenderOptions) {
319-
const part = this._register(this.instantiationService.createInstance(ChatMarkdownContentPart, { kind: 'markdownContent', content: typeof message === 'string' ? new MarkdownString().appendText(message) : message }, this.context, this.editorPool, false, this.codeBlockStartIndex, this.renderer, this.currentWidthDelegate(), this.codeBlockModelCollection, { codeBlockRenderOptions }));
319+
const part = this._register(this.instantiationService.createInstance(ChatMarkdownContentPart, { kind: 'markdownContent', content: typeof message === 'string' ? new MarkdownString().appendText(message) : message }, this.context, this.editorPool, false, this.codeBlockStartIndex, this.renderer, undefined, this.currentWidthDelegate(), this.codeBlockModelCollection, { codeBlockRenderOptions }));
320320
renderFileWidgets(part.domNode, this.instantiationService, this.chatMarkdownAnchorService, this._store);
321321
container.append(part.domNode);
322322

src/vs/workbench/contrib/chat/browser/chatListRenderer.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1385,7 +1385,7 @@ export class ChatListItemRenderer extends Disposable implements ITreeRenderer<Ch
13851385
const element = context.element;
13861386
const fillInIncompleteTokens = isResponseVM(element) && (!element.isComplete || element.isCanceled || element.errorDetails?.responseIsFiltered || element.errorDetails?.responseIsIncomplete || !!element.renderData);
13871387
const codeBlockStartIndex = this.getCodeBlockStartIndex(context);
1388-
const markdownPart = templateData.instantiationService.createInstance(ChatMarkdownContentPart, markdown, context, this._editorPool, fillInIncompleteTokens, codeBlockStartIndex, this.renderer, this._currentLayoutWidth, this.codeBlockModelCollection, {});
1388+
const markdownPart = templateData.instantiationService.createInstance(ChatMarkdownContentPart, markdown, context, this._editorPool, fillInIncompleteTokens, codeBlockStartIndex, this.renderer, undefined, this._currentLayoutWidth, this.codeBlockModelCollection, {});
13891389
if (isRequestVM(element)) {
13901390
markdownPart.domNode.tabIndex = 0;
13911391
if (this.configService.getValue<string>('chat.editRequests') === 'inline' && this.rendererOptions.editable) {

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

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,15 @@ import type { OperatingSystem } from '../../../../../base/common/platform.js';
88
import { escapeRegExpCharacters, regExpLeadsToEndlessLoop } from '../../../../../base/common/strings.js';
99
import { isObject } from '../../../../../base/common/types.js';
1010
import { structuralEquals } from '../../../../../base/common/equals.js';
11-
import { IConfigurationService, type IConfigurationValue } from '../../../../../platform/configuration/common/configuration.js';
11+
import { ConfigurationTarget, IConfigurationService, type IConfigurationValue } from '../../../../../platform/configuration/common/configuration.js';
1212
import { TerminalChatAgentToolsSettingId } from '../common/terminalChatAgentToolsConfiguration.js';
1313
import { isPowerShell } from './runInTerminalHelpers.js';
1414

1515
interface IAutoApproveRule {
1616
regex: RegExp;
1717
regexCaseInsensitive: RegExp;
1818
sourceText: string;
19+
sourceTarget: ConfigurationTarget;
1920
isDefaultRule: boolean;
2021
}
2122

@@ -199,13 +200,29 @@ export class CommandLineAutoApprover extends Disposable {
199200
key in defaultValue &&
200201
structuralEquals((defaultValue as Record<string, unknown>)[key], value)
201202
);
203+
function checkTarget(inspectValue: Readonly<unknown> | undefined): boolean {
204+
return (
205+
isObject(inspectValue) &&
206+
key in inspectValue &&
207+
structuralEquals((inspectValue as Record<string, unknown>)[key], value)
208+
);
209+
}
210+
const sourceTarget = (
211+
checkTarget(configInspectValue.workspaceFolder) ? ConfigurationTarget.WORKSPACE_FOLDER
212+
: checkTarget(configInspectValue.workspaceValue) ? ConfigurationTarget.WORKSPACE
213+
: checkTarget(configInspectValue.userRemoteValue) ? ConfigurationTarget.USER_REMOTE
214+
: checkTarget(configInspectValue.userLocalValue) ? ConfigurationTarget.USER_LOCAL
215+
: checkTarget(configInspectValue.userValue) ? ConfigurationTarget.USER
216+
: checkTarget(configInspectValue.applicationValue) ? ConfigurationTarget.APPLICATION
217+
: ConfigurationTarget.DEFAULT
218+
);
202219
if (typeof value === 'boolean') {
203220
const { regex, regexCaseInsensitive } = this._convertAutoApproveEntryToRegex(key);
204221
// IMPORTANT: Only true and false are used, null entries need to be ignored
205222
if (value === true) {
206-
allowListRules.push({ regex, regexCaseInsensitive, sourceText: key, isDefaultRule });
223+
allowListRules.push({ regex, regexCaseInsensitive, sourceText: key, sourceTarget, isDefaultRule });
207224
} else if (value === false) {
208-
denyListRules.push({ regex, regexCaseInsensitive, sourceText: key, isDefaultRule });
225+
denyListRules.push({ regex, regexCaseInsensitive, sourceText: key, sourceTarget, isDefaultRule });
209226
}
210227
} else if (typeof value === 'object' && value !== null) {
211228
// Handle object format like { approve: true/false, matchCommandLine: true/false }
@@ -214,15 +231,15 @@ export class CommandLineAutoApprover extends Disposable {
214231
const { regex, regexCaseInsensitive } = this._convertAutoApproveEntryToRegex(key);
215232
if (objectValue.approve === true) {
216233
if (objectValue.matchCommandLine === true) {
217-
allowListCommandLineRules.push({ regex, regexCaseInsensitive, sourceText: key, isDefaultRule });
234+
allowListCommandLineRules.push({ regex, regexCaseInsensitive, sourceText: key, sourceTarget, isDefaultRule });
218235
} else {
219-
allowListRules.push({ regex, regexCaseInsensitive, sourceText: key, isDefaultRule });
236+
allowListRules.push({ regex, regexCaseInsensitive, sourceText: key, sourceTarget, isDefaultRule });
220237
}
221238
} else if (objectValue.approve === false) {
222239
if (objectValue.matchCommandLine === true) {
223-
denyListCommandLineRules.push({ regex, regexCaseInsensitive, sourceText: key, isDefaultRule });
240+
denyListCommandLineRules.push({ regex, regexCaseInsensitive, sourceText: key, sourceTarget, isDefaultRule });
224241
} else {
225-
denyListRules.push({ regex, regexCaseInsensitive, sourceText: key, isDefaultRule });
242+
denyListRules.push({ regex, regexCaseInsensitive, sourceText: key, sourceTarget, isDefaultRule });
226243
}
227244
}
228245
}

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

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -257,15 +257,15 @@ export class RunInTerminalTool extends Disposable implements IToolImpl {
257257
switch (autoApproveReason) {
258258
case 'commandLine': {
259259
if (commandLineResult.rule) {
260-
autoApproveInfo = new MarkdownString(`_${localize('autoApprove.rule', 'Auto approved by rule {0}', `\`${commandLineResult.rule.sourceText}\``)}_`);
260+
autoApproveInfo = new MarkdownString(`_${localize('autoApprove.rule', 'Auto approved by rule {0}', `[\`${commandLineResult.rule.sourceText}\`](settings_${commandLineResult.rule.sourceTarget})`)}_`);
261261
}
262262
break;
263263
}
264264
case 'subCommand': {
265265
if (subCommandResults.length === 1) {
266-
autoApproveInfo = new MarkdownString(`_${localize('autoApprove.rule', 'Auto approved by rule {0}', subCommandResults.map(e => `\`${e.rule!.sourceText}\``).join(', '))}_`);
266+
autoApproveInfo = new MarkdownString(`_${localize('autoApprove.rule', 'Auto approved by rule {0}', subCommandResults.map(e => `[\`${e.rule!.sourceText}\`](settings_${e.rule!.sourceTarget})`).join(', '))}_`);
267267
} else if (subCommandResults.length > 1) {
268-
autoApproveInfo = new MarkdownString(`_${localize('autoApprove.rules', 'Auto approved by rules {0}', subCommandResults.map(e => `\`${e.rule!.sourceText}\``).join(', '))}_`);
268+
autoApproveInfo = new MarkdownString(`_${localize('autoApprove.rules', 'Auto approved by rules {0}', subCommandResults.map(e => `[\`${e.rule!.sourceText}\`](settings_${e.rule!.sourceTarget})`).join(', '))}_`);
269269
}
270270
break;
271271
}
@@ -274,16 +274,16 @@ export class RunInTerminalTool extends Disposable implements IToolImpl {
274274
switch (autoApproveReason) {
275275
case 'commandLine': {
276276
if (commandLineResult.rule) {
277-
autoApproveInfo = new MarkdownString(`_${localize('autoApproveDenied.rule', 'Auto approval denied by rule {0}', `\`${commandLineResult.rule.sourceText}\``)}_`);
277+
autoApproveInfo = new MarkdownString(`_${localize('autoApproveDenied.rule', 'Auto approval denied by rule {0}', `[\`${commandLineResult.rule.sourceText}\`](settings_${commandLineResult.rule.sourceTarget})`)}_`);
278278
}
279279
break;
280280
}
281281
case 'subCommand': {
282282
const deniedRules = subCommandResults.filter(e => e.result === 'denied');
283283
if (deniedRules.length === 1) {
284-
autoApproveInfo = new MarkdownString(`_${localize('autoApproveDenied.rule', 'Auto approval denied by rule {0}', deniedRules.map(e => `\`${e.rule!.sourceText}\``).join(', '))}_`);
284+
autoApproveInfo = new MarkdownString(`_${localize('autoApproveDenied.rule', 'Auto approval denied by rule {0}', deniedRules.map(e => `[\`${e.rule!.sourceText}\`](settings_${e.rule!.sourceTarget})`).join(', '))}_`);
285285
} else if (deniedRules.length > 1) {
286-
autoApproveInfo = new MarkdownString(`_${localize('autoApproveDenied.rules', 'Auto approval denied by rules {0}', deniedRules.map(e => `\`${e.rule!.sourceText}\``).join(', '))}_`);
286+
autoApproveInfo = new MarkdownString(`_${localize('autoApproveDenied.rules', 'Auto approval denied by rules {0}', deniedRules.map(e => `[\`${e.rule!.sourceText}\`](settings_${e.rule!.sourceTarget})`).join(', '))}_`);
287287
}
288288
break;
289289
}

0 commit comments

Comments
 (0)