Skip to content

Commit 75124da

Browse files
committed
Show original config text in reason
1 parent d0a3f4c commit 75124da

File tree

3 files changed

+58
-39
lines changed

3 files changed

+58
-39
lines changed

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

Lines changed: 54 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,16 @@ import { IConfigurationService } from '../../../../../platform/configuration/com
99
import { TerminalChatAgentToolsSettingId } from '../common/terminalChatAgentToolsConfiguration.js';
1010
import { isPowerShell } from './runInTerminalHelpers.js';
1111

12+
interface IAutoApproveRule {
13+
regex: RegExp;
14+
sourceText: string;
15+
}
16+
1217
export class CommandLineAutoApprover extends Disposable {
13-
private _denyListRegexes: RegExp[] = [];
14-
private _allowListRegexes: RegExp[] = [];
15-
private _allowListCommandLineRegexes: RegExp[] = [];
16-
private _denyListCommandLineRegexes: RegExp[] = [];
18+
private _denyListRules: IAutoApproveRule[] = [];
19+
private _allowListRules: IAutoApproveRule[] = [];
20+
private _allowListCommandLineRules: IAutoApproveRule[] = [];
21+
private _denyListCommandLineRules: IAutoApproveRule[] = [];
1722

1823
constructor(
1924
@IConfigurationService private readonly _configurationService: IConfigurationService,
@@ -28,25 +33,25 @@ export class CommandLineAutoApprover extends Disposable {
2833
}
2934

3035
updateConfiguration() {
31-
const { denyList, allowList, allowListCommandLine, denyListCommandLine } = this._mapAutoApproveConfigToRegexList(this._configurationService.getValue(TerminalChatAgentToolsSettingId.AutoApprove));
32-
this._allowListRegexes = allowList;
33-
this._denyListRegexes = denyList;
34-
this._allowListCommandLineRegexes = allowListCommandLine;
35-
this._denyListCommandLineRegexes = denyListCommandLine;
36+
const { denyListRules, allowListRules, allowListCommandLineRules, denyListCommandLineRules } = this._mapAutoApproveConfigToRules(this._configurationService.getValue(TerminalChatAgentToolsSettingId.AutoApprove));
37+
this._allowListRules = allowListRules;
38+
this._denyListRules = denyListRules;
39+
this._allowListCommandLineRules = allowListCommandLineRules;
40+
this._denyListCommandLineRules = denyListCommandLineRules;
3641
}
3742

3843
isCommandAutoApproved(command: string, shell: string, os: OperatingSystem): { isAutoApproved: boolean; reason: string } {
3944
// Check the deny list to see if this command requires explicit approval
40-
for (const regex of this._denyListRegexes) {
41-
if (this._commandMatchesRegex(regex, command, shell, os)) {
42-
return { isAutoApproved: false, reason: `Command '${command}' is denied by deny list rule: ${regex.source}` };
45+
for (const rule of this._denyListRules) {
46+
if (this._commandMatchesRegex(rule.regex, command, shell, os)) {
47+
return { isAutoApproved: false, reason: `Command '${command}' is denied by deny list rule: ${rule.sourceText}` };
4348
}
4449
}
4550

4651
// Check the allow list to see if the command is allowed to run without explicit approval
47-
for (const regex of this._allowListRegexes) {
48-
if (this._commandMatchesRegex(regex, command, shell, os)) {
49-
return { isAutoApproved: true, reason: `Command '${command}' is approved by allow list rule: ${regex.source}` };
52+
for (const rule of this._allowListRules) {
53+
if (this._commandMatchesRegex(rule.regex, command, shell, os)) {
54+
return { isAutoApproved: true, reason: `Command '${command}' is approved by allow list rule: ${rule.sourceText}` };
5055
}
5156
}
5257

@@ -58,16 +63,16 @@ export class CommandLineAutoApprover extends Disposable {
5863

5964
isCommandLineAutoApproved(commandLine: string): { isAutoApproved: boolean; reason: string } {
6065
// Check the deny list first to see if this command line requires explicit approval
61-
for (const regex of this._denyListCommandLineRegexes) {
62-
if (regex.test(commandLine)) {
63-
return { isAutoApproved: false, reason: `Command line '${commandLine}' is denied by deny list rule: ${regex.source}` };
66+
for (const rule of this._denyListCommandLineRules) {
67+
if (rule.regex.test(commandLine)) {
68+
return { isAutoApproved: false, reason: `Command line '${commandLine}' is denied by deny list rule: ${rule.sourceText}` };
6469
}
6570
}
6671

6772
// Check if the full command line matches any of the allow list command line regexes
68-
for (const regex of this._allowListCommandLineRegexes) {
69-
if (regex.test(commandLine)) {
70-
return { isAutoApproved: true, reason: `Command line '${commandLine}' is approved by allow list rule: ${regex.source}` };
73+
for (const rule of this._allowListCommandLineRules) {
74+
if (rule.regex.test(commandLine)) {
75+
return { isAutoApproved: true, reason: `Command line '${commandLine}' is approved by allow list rule: ${rule.sourceText}` };
7176
}
7277
}
7378
return { isAutoApproved: false, reason: `Command line '${commandLine}' has no matching auto approve entries` };
@@ -86,24 +91,34 @@ export class CommandLineAutoApprover extends Disposable {
8691
return false;
8792
}
8893

89-
private _mapAutoApproveConfigToRegexList(config: unknown): { denyList: RegExp[]; allowList: RegExp[]; allowListCommandLine: RegExp[]; denyListCommandLine: RegExp[] } {
94+
private _mapAutoApproveConfigToRules(config: unknown): {
95+
denyListRules: IAutoApproveRule[];
96+
allowListRules: IAutoApproveRule[];
97+
allowListCommandLineRules: IAutoApproveRule[];
98+
denyListCommandLineRules: IAutoApproveRule[];
99+
} {
90100
if (!config || typeof config !== 'object') {
91-
return { denyList: [], allowList: [], allowListCommandLine: [], denyListCommandLine: [] };
101+
return {
102+
denyListRules: [],
103+
allowListRules: [],
104+
allowListCommandLineRules: [],
105+
denyListCommandLineRules: []
106+
};
92107
}
93108

94-
const denyList: RegExp[] = [];
95-
const allowList: RegExp[] = [];
96-
const allowListCommandLine: RegExp[] = [];
97-
const denyListCommandLine: RegExp[] = [];
109+
const denyListRules: IAutoApproveRule[] = [];
110+
const allowListRules: IAutoApproveRule[] = [];
111+
const allowListCommandLineRules: IAutoApproveRule[] = [];
112+
const denyListCommandLineRules: IAutoApproveRule[] = [];
98113

99114
Object.entries(config).forEach(([key, value]) => {
100115
if (typeof value === 'boolean') {
101116
const regex = this._convertAutoApproveEntryToRegex(key);
102117
// IMPORTANT: Only true and false are used, null entries need to be ignored
103118
if (value === true) {
104-
allowList.push(regex);
119+
allowListRules.push({ regex, sourceText: key });
105120
} else if (value === false) {
106-
denyList.push(regex);
121+
denyListRules.push({ regex, sourceText: key });
107122
}
108123
} else if (typeof value === 'object' && value !== null) {
109124
// Handle object format like { approve: true/false, matchCommandLine: true/false }
@@ -112,22 +127,27 @@ export class CommandLineAutoApprover extends Disposable {
112127
const regex = this._convertAutoApproveEntryToRegex(key);
113128
if (objectValue.approve === true) {
114129
if (objectValue.matchCommandLine === true) {
115-
allowListCommandLine.push(regex);
130+
allowListCommandLineRules.push({ regex, sourceText: key });
116131
} else {
117-
allowList.push(regex);
132+
allowListRules.push({ regex, sourceText: key });
118133
}
119134
} else if (objectValue.approve === false) {
120135
if (objectValue.matchCommandLine === true) {
121-
denyListCommandLine.push(regex);
136+
denyListCommandLineRules.push({ regex, sourceText: key });
122137
} else {
123-
denyList.push(regex);
138+
denyListRules.push({ regex, sourceText: key });
124139
}
125140
}
126141
}
127142
}
128143
});
129144

130-
return { denyList, allowList, allowListCommandLine, denyListCommandLine };
145+
return {
146+
denyListRules,
147+
allowListRules,
148+
allowListCommandLineRules,
149+
denyListCommandLineRules
150+
};
131151
}
132152

133153
private _convertAutoApproveEntryToRegex(value: string): RegExp {

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

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -172,15 +172,14 @@ export class RunInTerminalTool extends Disposable implements IToolImpl {
172172
const inlineSubCommands = subCommands.map(e => Array.from(extractInlineSubCommands(e, shell, os))).flat();
173173
const allSubCommands = [...subCommands, ...inlineSubCommands];
174174
const subCommandResults = allSubCommands.map(e => this._commandLineAutoApprover.isCommandAutoApproved(e, shell, os));
175-
console.log({ subCommandResults });
176175
if (subCommandResults.every(e => e.isAutoApproved)) {
177176
confirmationMessages = undefined;
178177
} else {
179178
const commandLineResults = this._commandLineAutoApprover.isCommandLineAutoApproved(args.command);
180-
console.log({ commandLineResults });
181-
if (commandLineResults) {
179+
if (commandLineResults.isAutoApproved) {
182180
confirmationMessages = undefined;
183181
} else {
182+
// TODO: Surface reason in UI
184183
confirmationMessages = {
185184
title: args.isBackground
186185
? localize('runInTerminal.background', "Run command in background terminal")

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,11 +49,11 @@ suite('CommandLineAutoApprover', () => {
4949
}
5050

5151
function isAutoApproved(commandLine: string): boolean {
52-
return commandLineAutoApprover.isCommandAutoApproved(commandLine, shell, os);
52+
return commandLineAutoApprover.isCommandAutoApproved(commandLine, shell, os).isAutoApproved;
5353
}
5454

5555
function isCommandLineAutoApproved(commandLine: string): boolean {
56-
return commandLineAutoApprover.isCommandLineAutoApproved(commandLine);
56+
return commandLineAutoApprover.isCommandLineAutoApproved(commandLine).isAutoApproved;
5757
}
5858

5959
suite('autoApprove with allow patterns only', () => {

0 commit comments

Comments
 (0)