Skip to content
This repository was archived by the owner on Apr 1, 2026. It is now read-only.

Commit 4e549b1

Browse files
authored
fix: allow user to configure doom loop & external dir perms (anomalyco#4095)
1 parent 7be8e16 commit 4e549b1

8 files changed

Lines changed: 95 additions & 62 deletions

File tree

packages/opencode/src/agent/agent.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ export namespace Agent {
2020
edit: Config.Permission,
2121
bash: z.record(z.string(), Config.Permission),
2222
webfetch: Config.Permission.optional(),
23+
doom_loop: Config.Permission.optional(),
24+
external_directory: Config.Permission.optional(),
2325
}),
2426
model: z
2527
.object({
@@ -45,6 +47,8 @@ export namespace Agent {
4547
"*": "allow",
4648
},
4749
webfetch: "allow",
50+
doom_loop: "ask",
51+
external_directory: "ask",
4852
}
4953
const agentPermission = mergeAgentPermissions(defaultPermission, cfg.permission ?? {})
5054

@@ -244,6 +248,8 @@ function mergeAgentPermissions(basePermission: any, overridePermission: any): Ag
244248
edit: merged.edit ?? "allow",
245249
webfetch: merged.webfetch ?? "allow",
246250
bash: mergedBash ?? { "*": "allow" },
251+
doom_loop: merged.doom_loop,
252+
external_directory: merged.external_directory,
247253
}
248254

249255
return result

packages/opencode/src/config/config.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -360,6 +360,8 @@ export namespace Config {
360360
edit: Permission.optional(),
361361
bash: z.union([Permission, z.record(z.string(), Permission)]).optional(),
362362
webfetch: Permission.optional(),
363+
doom_loop: Permission.optional(),
364+
external_directory: Permission.optional(),
363365
})
364366
.optional(),
365367
})
@@ -574,6 +576,8 @@ export namespace Config {
574576
edit: Permission.optional(),
575577
bash: z.union([Permission, z.record(z.string(), Permission)]).optional(),
576578
webfetch: Permission.optional(),
579+
doom_loop: Permission.optional(),
580+
external_directory: Permission.optional(),
577581
})
578582
.optional(),
579583
tools: z.record(z.string(), z.boolean()).optional(),

packages/opencode/src/session/prompt.ts

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1115,18 +1115,21 @@ export namespace SessionPrompt {
11151115
JSON.stringify(p.state.input) === JSON.stringify(value.input),
11161116
)
11171117
) {
1118-
await Permission.ask({
1119-
type: "doom-loop",
1120-
pattern: value.toolName,
1121-
sessionID: assistantMsg.sessionID,
1122-
messageID: assistantMsg.id,
1123-
callID: value.toolCallId,
1124-
title: `Possible doom loop: "${value.toolName}" called ${DOOM_LOOP_THRESHOLD} times with identical arguments`,
1125-
metadata: {
1126-
tool: value.toolName,
1127-
input: value.input,
1128-
},
1129-
})
1118+
const permission = await Agent.get(input.agent).then((x) => x.permission)
1119+
if (permission.doom_loop === "ask") {
1120+
await Permission.ask({
1121+
type: "doom_loop",
1122+
pattern: value.toolName,
1123+
sessionID: assistantMsg.sessionID,
1124+
messageID: assistantMsg.id,
1125+
callID: value.toolCallId,
1126+
title: `Possible doom loop: "${value.toolName}" called ${DOOM_LOOP_THRESHOLD} times with identical arguments`,
1127+
metadata: {
1128+
tool: value.toolName,
1129+
input: value.input,
1130+
},
1131+
})
1132+
}
11301133
}
11311134
}
11321135
break

packages/opencode/src/tool/edit.ts

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -35,24 +35,27 @@ export const EditTool = Tool.define("edit", {
3535
throw new Error("oldString and newString must be different")
3636
}
3737

38+
const agent = await Agent.get(ctx.agent)
39+
3840
const filePath = path.isAbsolute(params.filePath) ? params.filePath : path.join(Instance.directory, params.filePath)
3941
if (!Filesystem.contains(Instance.directory, filePath)) {
4042
const parentDir = path.dirname(filePath)
41-
await Permission.ask({
42-
type: "external-directory",
43-
pattern: parentDir,
44-
sessionID: ctx.sessionID,
45-
messageID: ctx.messageID,
46-
callID: ctx.callID,
47-
title: `Edit file outside working directory: ${filePath}`,
48-
metadata: {
49-
filepath: filePath,
50-
parentDir,
51-
},
52-
})
43+
if (agent.permission.external_directory === "ask") {
44+
await Permission.ask({
45+
type: "external_directory",
46+
pattern: parentDir,
47+
sessionID: ctx.sessionID,
48+
messageID: ctx.messageID,
49+
callID: ctx.callID,
50+
title: `Edit file outside working directory: ${filePath}`,
51+
metadata: {
52+
filepath: filePath,
53+
parentDir,
54+
},
55+
})
56+
}
5357
}
5458

55-
const agent = await Agent.get(ctx.agent)
5659
let diff = ""
5760
let contentOld = ""
5861
let contentNew = ""

packages/opencode/src/tool/patch.ts

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -55,18 +55,20 @@ export const PatchTool = Tool.define("patch", {
5555

5656
if (!Filesystem.contains(Instance.directory, filePath)) {
5757
const parentDir = path.dirname(filePath)
58-
await Permission.ask({
59-
type: "external-directory",
60-
pattern: parentDir,
61-
sessionID: ctx.sessionID,
62-
messageID: ctx.messageID,
63-
callID: ctx.callID,
64-
title: `Patch file outside working directory: ${filePath}`,
65-
metadata: {
66-
filepath: filePath,
67-
parentDir,
68-
},
69-
})
58+
if (agent.permission.external_directory === "ask") {
59+
await Permission.ask({
60+
type: "external_directory",
61+
pattern: parentDir,
62+
sessionID: ctx.sessionID,
63+
messageID: ctx.messageID,
64+
callID: ctx.callID,
65+
title: `Patch file outside working directory: ${filePath}`,
66+
metadata: {
67+
filepath: filePath,
68+
parentDir,
69+
},
70+
})
71+
}
7072
}
7173

7274
switch (hunk.type) {

packages/opencode/src/tool/read.ts

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { Instance } from "../project/instance"
1010
import { Provider } from "../provider/provider"
1111
import { Identifier } from "../id/id"
1212
import { Permission } from "../permission"
13+
import { Agent } from "@/agent/agent"
1314

1415
const DEFAULT_READ_LIMIT = 2000
1516
const MAX_LINE_LENGTH = 2000
@@ -27,21 +28,24 @@ export const ReadTool = Tool.define("read", {
2728
filepath = path.join(process.cwd(), filepath)
2829
}
2930
const title = path.relative(Instance.worktree, filepath)
31+
const agent = await Agent.get(ctx.agent)
3032

3133
if (!ctx.extra?.["bypassCwdCheck"] && !Filesystem.contains(Instance.directory, filepath)) {
3234
const parentDir = path.dirname(filepath)
33-
await Permission.ask({
34-
type: "external-directory",
35-
pattern: parentDir,
36-
sessionID: ctx.sessionID,
37-
messageID: ctx.messageID,
38-
callID: ctx.callID,
39-
title: `Access file outside working directory: ${filepath}`,
40-
metadata: {
41-
filepath,
42-
parentDir,
43-
},
44-
})
35+
if (agent.permission.external_directory === "ask") {
36+
await Permission.ask({
37+
type: "external_directory",
38+
pattern: parentDir,
39+
sessionID: ctx.sessionID,
40+
messageID: ctx.messageID,
41+
callID: ctx.callID,
42+
title: `Access file outside working directory: ${filepath}`,
43+
metadata: {
44+
filepath,
45+
parentDir,
46+
},
47+
})
48+
}
4549
}
4650

4751
const file = Bun.file(filepath)

packages/opencode/src/tool/write.ts

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -18,28 +18,31 @@ export const WriteTool = Tool.define("write", {
1818
filePath: z.string().describe("The absolute path to the file to write (must be absolute, not relative)"),
1919
}),
2020
async execute(params, ctx) {
21+
const agent = await Agent.get(ctx.agent)
22+
2123
const filepath = path.isAbsolute(params.filePath) ? params.filePath : path.join(Instance.directory, params.filePath)
2224
if (!Filesystem.contains(Instance.directory, filepath)) {
2325
const parentDir = path.dirname(filepath)
24-
await Permission.ask({
25-
type: "external-directory",
26-
pattern: parentDir,
27-
sessionID: ctx.sessionID,
28-
messageID: ctx.messageID,
29-
callID: ctx.callID,
30-
title: `Write file outside working directory: ${filepath}`,
31-
metadata: {
32-
filepath,
33-
parentDir,
34-
},
35-
})
26+
if (agent.permission.external_directory === "ask") {
27+
await Permission.ask({
28+
type: "external_directory",
29+
pattern: parentDir,
30+
sessionID: ctx.sessionID,
31+
messageID: ctx.messageID,
32+
callID: ctx.callID,
33+
title: `Write file outside working directory: ${filepath}`,
34+
metadata: {
35+
filepath,
36+
parentDir,
37+
},
38+
})
39+
}
3640
}
3741

3842
const file = Bun.file(filepath)
3943
const exists = await file.exists()
4044
if (exists) await FileTime.assert(ctx.sessionID, filepath)
4145

42-
const agent = await Agent.get(ctx.agent)
4346
if (agent.permission.edit === "ask")
4447
await Permission.ask({
4548
type: "write",

packages/sdk/js/src/gen/types.gen.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,8 @@ export type AgentConfig = {
198198
[key: string]: "ask" | "allow" | "deny"
199199
}
200200
webfetch?: "ask" | "allow" | "deny"
201+
doom_loop?: "ask" | "allow" | "deny"
202+
external_directory?: "ask" | "allow" | "deny"
201203
}
202204
[key: string]:
203205
| unknown
@@ -216,6 +218,8 @@ export type AgentConfig = {
216218
[key: string]: "ask" | "allow" | "deny"
217219
}
218220
webfetch?: "ask" | "allow" | "deny"
221+
doom_loop?: "ask" | "allow" | "deny"
222+
external_directory?: "ask" | "allow" | "deny"
219223
}
220224
| undefined
221225
}
@@ -463,6 +467,8 @@ export type Config = {
463467
[key: string]: "ask" | "allow" | "deny"
464468
}
465469
webfetch?: "ask" | "allow" | "deny"
470+
doom_loop?: "ask" | "allow" | "deny"
471+
external_directory?: "ask" | "allow" | "deny"
466472
}
467473
tools?: {
468474
[key: string]: boolean
@@ -1043,6 +1049,8 @@ export type Agent = {
10431049
[key: string]: "ask" | "allow" | "deny"
10441050
}
10451051
webfetch?: "ask" | "allow" | "deny"
1052+
doom_loop?: "ask" | "allow" | "deny"
1053+
external_directory?: "ask" | "allow" | "deny"
10461054
}
10471055
model?: {
10481056
modelID: string

0 commit comments

Comments
 (0)