summaryrefslogtreecommitdiffhomepage
path: root/packages
diff options
context:
space:
mode:
authorAiden Cline <[email protected]>2026-01-28 01:38:10 -0500
committerAiden Cline <[email protected]>2026-01-28 01:38:10 -0500
commit558590712d271fd6b66eacfcf240f4e5e8beb764 (patch)
tree2871aa35d3f0c641862ae89a66e0308b81f6663e /packages
parentd76e1448f0319dbf706118f1615c1d19972d03cf (diff)
downloadopencode-558590712d271fd6b66eacfcf240f4e5e8beb764.tar.gz
opencode-558590712d271fd6b66eacfcf240f4e5e8beb764.zip
fix: ensure parallel tool calls dont double load AGENTS.md
Diffstat (limited to 'packages')
-rw-r--r--packages/opencode/src/session/instruction.ts31
-rw-r--r--packages/opencode/src/session/prompt.ts2
-rw-r--r--packages/opencode/src/tool/read.ts2
-rw-r--r--packages/opencode/test/session/instruction.test.ts8
4 files changed, 38 insertions, 5 deletions
diff --git a/packages/opencode/src/session/instruction.ts b/packages/opencode/src/session/instruction.ts
index d413e80f6..723439a3f 100644
--- a/packages/opencode/src/session/instruction.ts
+++ b/packages/opencode/src/session/instruction.ts
@@ -41,6 +41,32 @@ async function resolveRelative(instruction: string): Promise<string[]> {
}
export namespace InstructionPrompt {
+ const state = Instance.state(() => {
+ return {
+ claims: new Map<string, Set<string>>(),
+ }
+ })
+
+ function isClaimed(messageID: string, filepath: string) {
+ const claimed = state().claims.get(messageID)
+ if (!claimed) return false
+ return claimed.has(filepath)
+ }
+
+ function claim(messageID: string, filepath: string) {
+ const current = state()
+ let claimed = current.claims.get(messageID)
+ if (!claimed) {
+ claimed = new Set()
+ current.claims.set(messageID, claimed)
+ }
+ claimed.add(filepath)
+ }
+
+ export function clear(messageID: string) {
+ state().claims.delete(messageID)
+ }
+
export async function systemPaths() {
const config = await Config.get()
const paths = new Set<string>()
@@ -137,7 +163,7 @@ export namespace InstructionPrompt {
}
}
- export async function resolve(messages: MessageV2.WithParts[], filepath: string) {
+ export async function resolve(messages: MessageV2.WithParts[], filepath: string, messageID: string) {
const system = await systemPaths()
const already = loaded(messages)
const results: { filepath: string; content: string }[] = []
@@ -147,7 +173,8 @@ export namespace InstructionPrompt {
while (current.startsWith(root)) {
const found = await find(current)
- if (found && !system.has(found) && !already.has(found)) {
+ if (found && !system.has(found) && !already.has(found) && !isClaimed(messageID, found)) {
+ claim(messageID, found)
const content = await Bun.file(found)
.text()
.catch(() => undefined)
diff --git a/packages/opencode/src/session/prompt.ts b/packages/opencode/src/session/prompt.ts
index 23ca47354..94eabdef7 100644
--- a/packages/opencode/src/session/prompt.ts
+++ b/packages/opencode/src/session/prompt.ts
@@ -551,6 +551,7 @@ export namespace SessionPrompt {
model,
abort,
})
+ using _ = defer(() => InstructionPrompt.clear(processor.message.id))
// Check if user explicitly invoked an agent via @ in this turn
const lastUserMsg = msgs.findLast((m) => m.info.role === "user")
@@ -839,6 +840,7 @@ export namespace SessionPrompt {
system: input.system,
variant: input.variant,
}
+ using _ = defer(() => InstructionPrompt.clear(info.id))
const parts = await Promise.all(
input.parts.map(async (part): Promise<MessageV2.Part[]> => {
diff --git a/packages/opencode/src/tool/read.ts b/packages/opencode/src/tool/read.ts
index 746e0b173..f230cdf44 100644
--- a/packages/opencode/src/tool/read.ts
+++ b/packages/opencode/src/tool/read.ts
@@ -60,7 +60,7 @@ export const ReadTool = Tool.define("read", {
throw new Error(`File not found: ${filepath}`)
}
- const instructions = await InstructionPrompt.resolve(ctx.messages, filepath)
+ const instructions = await InstructionPrompt.resolve(ctx.messages, filepath, ctx.messageID)
// Exclude SVG (XML-based) and vnd.fastbidsheet (.fbs extension, commonly FlatBuffers schema files)
const isImage =
diff --git a/packages/opencode/test/session/instruction.test.ts b/packages/opencode/test/session/instruction.test.ts
index 2c44a266e..67719fa33 100644
--- a/packages/opencode/test/session/instruction.test.ts
+++ b/packages/opencode/test/session/instruction.test.ts
@@ -18,7 +18,7 @@ describe("InstructionPrompt.resolve", () => {
const system = await InstructionPrompt.systemPaths()
expect(system.has(path.join(tmp.path, "AGENTS.md"))).toBe(true)
- const results = await InstructionPrompt.resolve([], path.join(tmp.path, "src", "file.ts"))
+ const results = await InstructionPrompt.resolve([], path.join(tmp.path, "src", "file.ts"), "test-message-1")
expect(results).toEqual([])
},
})
@@ -37,7 +37,11 @@ describe("InstructionPrompt.resolve", () => {
const system = await InstructionPrompt.systemPaths()
expect(system.has(path.join(tmp.path, "subdir", "AGENTS.md"))).toBe(false)
- const results = await InstructionPrompt.resolve([], path.join(tmp.path, "subdir", "nested", "file.ts"))
+ const results = await InstructionPrompt.resolve(
+ [],
+ path.join(tmp.path, "subdir", "nested", "file.ts"),
+ "test-message-2",
+ )
expect(results.length).toBe(1)
expect(results[0].filepath).toBe(path.join(tmp.path, "subdir", "AGENTS.md"))
},