fix: ensure parallel tool calls dont double load AGENTS.md
This commit is contained in:
@@ -41,6 +41,32 @@ async function resolveRelative(instruction: string): Promise<string[]> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export namespace InstructionPrompt {
|
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() {
|
export async function systemPaths() {
|
||||||
const config = await Config.get()
|
const config = await Config.get()
|
||||||
const paths = new Set<string>()
|
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 system = await systemPaths()
|
||||||
const already = loaded(messages)
|
const already = loaded(messages)
|
||||||
const results: { filepath: string; content: string }[] = []
|
const results: { filepath: string; content: string }[] = []
|
||||||
@@ -147,7 +173,8 @@ export namespace InstructionPrompt {
|
|||||||
|
|
||||||
while (current.startsWith(root)) {
|
while (current.startsWith(root)) {
|
||||||
const found = await find(current)
|
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)
|
const content = await Bun.file(found)
|
||||||
.text()
|
.text()
|
||||||
.catch(() => undefined)
|
.catch(() => undefined)
|
||||||
|
|||||||
@@ -551,6 +551,7 @@ export namespace SessionPrompt {
|
|||||||
model,
|
model,
|
||||||
abort,
|
abort,
|
||||||
})
|
})
|
||||||
|
using _ = defer(() => InstructionPrompt.clear(processor.message.id))
|
||||||
|
|
||||||
// Check if user explicitly invoked an agent via @ in this turn
|
// Check if user explicitly invoked an agent via @ in this turn
|
||||||
const lastUserMsg = msgs.findLast((m) => m.info.role === "user")
|
const lastUserMsg = msgs.findLast((m) => m.info.role === "user")
|
||||||
@@ -839,6 +840,7 @@ export namespace SessionPrompt {
|
|||||||
system: input.system,
|
system: input.system,
|
||||||
variant: input.variant,
|
variant: input.variant,
|
||||||
}
|
}
|
||||||
|
using _ = defer(() => InstructionPrompt.clear(info.id))
|
||||||
|
|
||||||
const parts = await Promise.all(
|
const parts = await Promise.all(
|
||||||
input.parts.map(async (part): Promise<MessageV2.Part[]> => {
|
input.parts.map(async (part): Promise<MessageV2.Part[]> => {
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ export const ReadTool = Tool.define("read", {
|
|||||||
throw new Error(`File not found: ${filepath}`)
|
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)
|
// Exclude SVG (XML-based) and vnd.fastbidsheet (.fbs extension, commonly FlatBuffers schema files)
|
||||||
const isImage =
|
const isImage =
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ describe("InstructionPrompt.resolve", () => {
|
|||||||
const system = await InstructionPrompt.systemPaths()
|
const system = await InstructionPrompt.systemPaths()
|
||||||
expect(system.has(path.join(tmp.path, "AGENTS.md"))).toBe(true)
|
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([])
|
expect(results).toEqual([])
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
@@ -37,7 +37,11 @@ describe("InstructionPrompt.resolve", () => {
|
|||||||
const system = await InstructionPrompt.systemPaths()
|
const system = await InstructionPrompt.systemPaths()
|
||||||
expect(system.has(path.join(tmp.path, "subdir", "AGENTS.md"))).toBe(false)
|
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.length).toBe(1)
|
||||||
expect(results[0].filepath).toBe(path.join(tmp.path, "subdir", "AGENTS.md"))
|
expect(results[0].filepath).toBe(path.join(tmp.path, "subdir", "AGENTS.md"))
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user