Simplify auto-approving code and make it work better with browser actions (#21)

This commit is contained in:
Matt Rubens
2024-11-28 13:44:02 -05:00
committed by GitHub
parent b1c0e9be41
commit d632e621be
6 changed files with 224 additions and 347 deletions

View File

@@ -56,16 +56,6 @@ type UserContent = Array<
Anthropic.TextBlockParam | Anthropic.ImageBlockParam | Anthropic.ToolUseBlockParam | Anthropic.ToolResultBlockParam
>
const ALLOWED_AUTO_EXECUTE_COMMANDS = [
'npm',
'npx',
'tsc',
'git log',
'git diff',
'git show',
'list'
] as const
export class Cline {
readonly taskId: string
api: ApiHandler
@@ -74,10 +64,6 @@ export class Cline {
private browserSession: BrowserSession
private didEditFile: boolean = false
customInstructions?: string
alwaysAllowReadOnly: boolean
alwaysAllowWrite: boolean
alwaysAllowExecute: boolean
alwaysAllowBrowser: boolean
apiConversationHistory: Anthropic.MessageParam[] = []
clineMessages: ClineMessage[] = []
@@ -107,10 +93,6 @@ export class Cline {
provider: ClineProvider,
apiConfiguration: ApiConfiguration,
customInstructions?: string,
alwaysAllowReadOnly?: boolean,
alwaysAllowWrite?: boolean,
alwaysAllowExecute?: boolean,
alwaysAllowBrowser?: boolean,
task?: string,
images?: string[],
historyItem?: HistoryItem,
@@ -122,10 +104,6 @@ export class Cline {
this.browserSession = new BrowserSession(provider.context)
this.diffViewProvider = new DiffViewProvider(cwd)
this.customInstructions = customInstructions
this.alwaysAllowReadOnly = alwaysAllowReadOnly ?? false
this.alwaysAllowWrite = alwaysAllowWrite ?? false
this.alwaysAllowExecute = alwaysAllowExecute ?? false
this.alwaysAllowBrowser = alwaysAllowBrowser ?? false
if (historyItem) {
this.taskId = historyItem.id
@@ -138,25 +116,6 @@ export class Cline {
}
}
private isAllowedCommand(command?: string): boolean {
if (!command) {
return false;
}
// Check for command chaining characters
if (command.includes('&&') ||
command.includes(';') ||
command.includes('||') ||
command.includes('|') ||
command.includes('$(') ||
command.includes('`')) {
return false;
}
const trimmedCommand = command.trim().toLowerCase();
return ALLOWED_AUTO_EXECUTE_COMMANDS.some(prefix =>
trimmedCommand.startsWith(prefix.toLowerCase())
);
}
// Storing task to disk for history
private async ensureTaskDirectoryExists(): Promise<string> {
@@ -1101,11 +1060,7 @@ export class Cline {
if (block.partial) {
// update gui message
const partialMessage = JSON.stringify(sharedMessageProps)
if (this.alwaysAllowWrite) {
await this.say("tool", partialMessage, undefined, block.partial)
} else {
await this.ask("tool", partialMessage, block.partial).catch(() => {})
}
await this.ask("tool", partialMessage, block.partial).catch(() => {})
// update editor
if (!this.diffViewProvider.isEditing) {
// open the editor and prepare to stream content in
@@ -1135,11 +1090,7 @@ export class Cline {
if (!this.diffViewProvider.isEditing) {
// show gui message before showing edit animation
const partialMessage = JSON.stringify(sharedMessageProps)
if (this.alwaysAllowWrite) {
await this.say("tool", partialMessage, undefined, true)
} else {
await this.ask("tool", partialMessage, true).catch(() => {}) // sending true for partial even though it's not a partial, this shows the edit row before the content is streamed into the editor
}
await this.ask("tool", partialMessage, true).catch(() => {}) // sending true for partial even though it's not a partial, this shows the edit row before the content is streamed into the editor
await this.diffViewProvider.open(relPath)
}
await this.diffViewProvider.update(newContent, true)
@@ -1158,7 +1109,7 @@ export class Cline {
)
: undefined,
} satisfies ClineSayTool)
const didApprove = this.alwaysAllowWrite || (await askApproval("tool", completeMessage))
const didApprove = await askApproval("tool", completeMessage)
if (!didApprove) {
await this.diffViewProvider.revertChanges()
break
@@ -1211,11 +1162,7 @@ export class Cline {
...sharedMessageProps,
content: undefined,
} satisfies ClineSayTool)
if (this.alwaysAllowReadOnly) {
await this.say("tool", partialMessage, undefined, block.partial)
} else {
await this.ask("tool", partialMessage, block.partial).catch(() => {})
}
await this.ask("tool", partialMessage, block.partial).catch(() => {})
break
} else {
if (!relPath) {
@@ -1229,13 +1176,9 @@ export class Cline {
...sharedMessageProps,
content: absolutePath,
} satisfies ClineSayTool)
if (this.alwaysAllowReadOnly) {
await this.say("tool", completeMessage, undefined, false) // need to be sending partialValue bool, since undefined has its own purpose in that the message is treated neither as a partial or completion of a partial, but as a single complete message
} else {
const didApprove = await askApproval("tool", completeMessage)
if (!didApprove) {
break
}
const didApprove = await askApproval("tool", completeMessage)
if (!didApprove) {
break
}
// now execute the tool like normal
const content = await extractTextFromFile(absolutePath)
@@ -1261,11 +1204,7 @@ export class Cline {
...sharedMessageProps,
content: "",
} satisfies ClineSayTool)
if (this.alwaysAllowReadOnly) {
await this.say("tool", partialMessage, undefined, block.partial)
} else {
await this.ask("tool", partialMessage, block.partial).catch(() => {})
}
await this.ask("tool", partialMessage, block.partial).catch(() => {})
break
} else {
if (!relDirPath) {
@@ -1281,13 +1220,9 @@ export class Cline {
...sharedMessageProps,
content: result,
} satisfies ClineSayTool)
if (this.alwaysAllowReadOnly) {
await this.say("tool", completeMessage, undefined, false)
} else {
const didApprove = await askApproval("tool", completeMessage)
if (!didApprove) {
break
}
const didApprove = await askApproval("tool", completeMessage)
if (!didApprove) {
break
}
pushToolResult(result)
break
@@ -1309,11 +1244,7 @@ export class Cline {
...sharedMessageProps,
content: "",
} satisfies ClineSayTool)
if (this.alwaysAllowReadOnly) {
await this.say("tool", partialMessage, undefined, block.partial)
} else {
await this.ask("tool", partialMessage, block.partial).catch(() => {})
}
await this.ask("tool", partialMessage, block.partial).catch(() => {})
break
} else {
if (!relDirPath) {
@@ -1330,13 +1261,9 @@ export class Cline {
...sharedMessageProps,
content: result,
} satisfies ClineSayTool)
if (this.alwaysAllowReadOnly) {
await this.say("tool", completeMessage, undefined, false)
} else {
const didApprove = await askApproval("tool", completeMessage)
if (!didApprove) {
break
}
const didApprove = await askApproval("tool", completeMessage)
if (!didApprove) {
break
}
pushToolResult(result)
break
@@ -1362,11 +1289,7 @@ export class Cline {
...sharedMessageProps,
content: "",
} satisfies ClineSayTool)
if (this.alwaysAllowReadOnly) {
await this.say("tool", partialMessage, undefined, block.partial)
} else {
await this.ask("tool", partialMessage, block.partial).catch(() => {})
}
await this.ask("tool", partialMessage, block.partial).catch(() => {})
break
} else {
if (!relDirPath) {
@@ -1386,13 +1309,9 @@ export class Cline {
...sharedMessageProps,
content: results,
} satisfies ClineSayTool)
if (this.alwaysAllowReadOnly) {
await this.say("tool", completeMessage, undefined, false)
} else {
const didApprove = await askApproval("tool", completeMessage)
if (!didApprove) {
break
}
const didApprove = await askApproval("tool", completeMessage)
if (!didApprove) {
break
}
pushToolResult(results)
break
@@ -1421,24 +1340,11 @@ export class Cline {
try {
if (block.partial) {
if (action === "launch") {
if (this.alwaysAllowBrowser) {
await this.say(
"browser_action",
JSON.stringify({
action: action as BrowserAction,
coordinate: undefined,
text: undefined
} satisfies ClineSayBrowserAction),
undefined,
block.partial
)
} else {
await this.ask(
"browser_action_launch",
removeClosingTag("url", url),
block.partial
).catch(() => {})
}
await this.ask(
"browser_action_launch",
removeClosingTag("url", url),
block.partial
).catch(() => {})
} else {
await this.say(
"browser_action",
@@ -1464,7 +1370,7 @@ export class Cline {
break
}
this.consecutiveMistakeCount = 0
const didApprove = this.alwaysAllowBrowser || await askApproval("browser_action_launch", url)
const didApprove = await askApproval("browser_action_launch", url)
if (!didApprove) {
break
}
@@ -1565,13 +1471,9 @@ export class Cline {
const command: string | undefined = block.params.command
try {
if (block.partial) {
if (this.alwaysAllowExecute && this.isAllowedCommand(command)) {
await this.say("command", command, undefined, block.partial)
} else {
await this.ask("command", removeClosingTag("command", command), block.partial).catch(
() => {}
)
}
await this.ask("command", removeClosingTag("command", command), block.partial).catch(
() => {}
)
break
} else {
if (!command) {
@@ -1583,8 +1485,7 @@ export class Cline {
}
this.consecutiveMistakeCount = 0
const didApprove = (this.alwaysAllowExecute && this.isAllowedCommand(command)) ||
(await askApproval("command", command))
const didApprove = await askApproval("command", command)
if (!didApprove) {
break
}