Skip to content

Commit 5585907

Browse files
committed
fix: ensure parallel tool calls dont double load AGENTS.md
1 parent d76e144 commit 5585907

4 files changed

Lines changed: 38 additions & 5 deletions

File tree

packages/opencode/src/session/instruction.ts

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,32 @@ async function resolveRelative(instruction: string): Promise<string[]> {
4141
}
4242

4343
export namespace InstructionPrompt {
44+
const state = Instance.state(() => {
45+
return {
46+
claims: new Map<string, Set<string>>(),
47+
}
48+
})
49+
50+
function isClaimed(messageID: string, filepath: string) {
51+
const claimed = state().claims.get(messageID)
52+
if (!claimed) return false
53+
return claimed.has(filepath)
54+
}
55+
56+
function claim(messageID: string, filepath: string) {
57+
const current = state()
58+
let claimed = current.claims.get(messageID)
59+
if (!claimed) {
60+
claimed = new Set()
61+
current.claims.set(messageID, claimed)
62+
}
63+
claimed.add(filepath)
64+
}
65+
66+
export function clear(messageID: string) {
67+
state().claims.delete(messageID)
68+
}
69+
4470
export async function systemPaths() {
4571
const config = await Config.get()
4672
const paths = new Set<string>()
@@ -137,7 +163,7 @@ export namespace InstructionPrompt {
137163
}
138164
}
139165

140-
export async function resolve(messages: MessageV2.WithParts[], filepath: string) {
166+
export async function resolve(messages: MessageV2.WithParts[], filepath: string, messageID: string) {
141167
const system = await systemPaths()
142168
const already = loaded(messages)
143169
const results: { filepath: string; content: string }[] = []
@@ -147,7 +173,8 @@ export namespace InstructionPrompt {
147173

148174
while (current.startsWith(root)) {
149175
const found = await find(current)
150-
if (found && !system.has(found) && !already.has(found)) {
176+
if (found && !system.has(found) && !already.has(found) && !isClaimed(messageID, found)) {
177+
claim(messageID, found)
151178
const content = await Bun.file(found)
152179
.text()
153180
.catch(() => undefined)

packages/opencode/src/session/prompt.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -551,6 +551,7 @@ export namespace SessionPrompt {
551551
model,
552552
abort,
553553
})
554+
using _ = defer(() => InstructionPrompt.clear(processor.message.id))
554555

555556
// Check if user explicitly invoked an agent via @ in this turn
556557
const lastUserMsg = msgs.findLast((m) => m.info.role === "user")
@@ -839,6 +840,7 @@ export namespace SessionPrompt {
839840
system: input.system,
840841
variant: input.variant,
841842
}
843+
using _ = defer(() => InstructionPrompt.clear(info.id))
842844

843845
const parts = await Promise.all(
844846
input.parts.map(async (part): Promise<MessageV2.Part[]> => {

packages/opencode/src/tool/read.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ export const ReadTool = Tool.define("read", {
6060
throw new Error(`File not found: ${filepath}`)
6161
}
6262

63-
const instructions = await InstructionPrompt.resolve(ctx.messages, filepath)
63+
const instructions = await InstructionPrompt.resolve(ctx.messages, filepath, ctx.messageID)
6464

6565
// Exclude SVG (XML-based) and vnd.fastbidsheet (.fbs extension, commonly FlatBuffers schema files)
6666
const isImage =

packages/opencode/test/session/instruction.test.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ describe("InstructionPrompt.resolve", () => {
1818
const system = await InstructionPrompt.systemPaths()
1919
expect(system.has(path.join(tmp.path, "AGENTS.md"))).toBe(true)
2020

21-
const results = await InstructionPrompt.resolve([], path.join(tmp.path, "src", "file.ts"))
21+
const results = await InstructionPrompt.resolve([], path.join(tmp.path, "src", "file.ts"), "test-message-1")
2222
expect(results).toEqual([])
2323
},
2424
})
@@ -37,7 +37,11 @@ describe("InstructionPrompt.resolve", () => {
3737
const system = await InstructionPrompt.systemPaths()
3838
expect(system.has(path.join(tmp.path, "subdir", "AGENTS.md"))).toBe(false)
3939

40-
const results = await InstructionPrompt.resolve([], path.join(tmp.path, "subdir", "nested", "file.ts"))
40+
const results = await InstructionPrompt.resolve(
41+
[],
42+
path.join(tmp.path, "subdir", "nested", "file.ts"),
43+
"test-message-2",
44+
)
4145
expect(results.length).toBe(1)
4246
expect(results[0].filepath).toBe(path.join(tmp.path, "subdir", "AGENTS.md"))
4347
},

0 commit comments

Comments
 (0)