Skip to content

Commit 612b656

Browse files
rekram1-nodethdxr
authored andcommitted
fix: adjust resolve parts so that when messages with multiple @ references occur, the tool calls are properly ordered
1 parent cb6ec0a commit 612b656

File tree

5 files changed

+60
-33
lines changed

5 files changed

+60
-33
lines changed

packages/opencode/src/session/processor.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,14 @@ export namespace SessionProcessor {
172172
case "tool-result": {
173173
const match = toolcalls[value.toolCallId]
174174
if (match && match.state.status === "running") {
175+
const attachments = value.output.attachments?.map(
176+
(attachment: Omit<MessageV2.FilePart, "id" | "messageID" | "sessionID">) => ({
177+
...attachment,
178+
id: Identifier.ascending("part"),
179+
messageID: match.messageID,
180+
sessionID: match.sessionID,
181+
}),
182+
)
175183
await Session.updatePart({
176184
...match,
177185
state: {
@@ -184,7 +192,7 @@ export namespace SessionProcessor {
184192
start: match.state.time.start,
185193
end: Date.now(),
186194
},
187-
attachments: value.output.attachments,
195+
attachments,
188196
},
189197
})
190198

packages/opencode/src/session/prompt.ts

Lines changed: 43 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -187,47 +187,52 @@ export namespace SessionPrompt {
187187
text: template,
188188
},
189189
]
190-
const files = ConfigMarkdown.files(template)
190+
const matches = ConfigMarkdown.files(template)
191191
const seen = new Set<string>()
192-
await Promise.all(
193-
files.map(async (match) => {
194-
const name = match[1]
195-
if (seen.has(name)) return
192+
const names = matches
193+
.map((match) => match[1])
194+
.filter((name) => {
195+
if (seen.has(name)) return false
196196
seen.add(name)
197+
return true
198+
})
199+
const resolved = await Promise.all(
200+
names.map(async (name) => {
197201
const filepath = name.startsWith("~/")
198202
? path.join(os.homedir(), name.slice(2))
199203
: path.resolve(Instance.worktree, name)
200204

201205
const stats = await fs.stat(filepath).catch(() => undefined)
202206
if (!stats) {
203207
const agent = await Agent.get(name)
204-
if (agent) {
205-
parts.push({
206-
type: "agent",
207-
name: agent.name,
208-
})
209-
}
210-
return
208+
if (!agent) return undefined
209+
return {
210+
type: "agent",
211+
name: agent.name,
212+
} satisfies PromptInput["parts"][number]
211213
}
212214

213215
if (stats.isDirectory()) {
214-
parts.push({
216+
return {
215217
type: "file",
216218
url: `file://${filepath}`,
217219
filename: name,
218220
mime: "application/x-directory",
219-
})
220-
return
221+
} satisfies PromptInput["parts"][number]
221222
}
222223

223-
parts.push({
224+
return {
224225
type: "file",
225226
url: `file://${filepath}`,
226227
filename: name,
227228
mime: "text/plain",
228-
})
229+
} satisfies PromptInput["parts"][number]
229230
}),
230231
)
232+
for (const item of resolved) {
233+
if (!item) continue
234+
parts.push(item)
235+
}
231236
return parts
232237
}
233238

@@ -424,6 +429,12 @@ export namespace SessionPrompt {
424429
assistantMessage.time.completed = Date.now()
425430
await Session.updateMessage(assistantMessage)
426431
if (result && part.state.status === "running") {
432+
const attachments = result.attachments?.map((attachment) => ({
433+
...attachment,
434+
id: Identifier.ascending("part"),
435+
messageID: assistantMessage.id,
436+
sessionID: assistantMessage.sessionID,
437+
}))
427438
await Session.updatePart({
428439
...part,
429440
state: {
@@ -432,7 +443,7 @@ export namespace SessionPrompt {
432443
title: result.title,
433444
metadata: result.metadata,
434445
output: result.output,
435-
attachments: result.attachments,
446+
attachments,
436447
time: {
437448
...part.state.time,
438449
end: Date.now(),
@@ -771,16 +782,13 @@ export namespace SessionPrompt {
771782
)
772783

773784
const textParts: string[] = []
774-
const attachments: MessageV2.FilePart[] = []
785+
const attachments: Omit<MessageV2.FilePart, "id" | "messageID" | "sessionID">[] = []
775786

776787
for (const contentItem of result.content) {
777788
if (contentItem.type === "text") {
778789
textParts.push(contentItem.text)
779790
} else if (contentItem.type === "image") {
780791
attachments.push({
781-
id: Identifier.ascending("part"),
782-
sessionID: input.session.id,
783-
messageID: input.processor.message.id,
784792
type: "file",
785793
mime: contentItem.mimeType,
786794
url: `data:${contentItem.mimeType};base64,${contentItem.data}`,
@@ -792,9 +800,6 @@ export namespace SessionPrompt {
792800
}
793801
if (resource.blob) {
794802
attachments.push({
795-
id: Identifier.ascending("part"),
796-
sessionID: input.session.id,
797-
messageID: input.processor.message.id,
798803
type: "file",
799804
mime: resource.mimeType ?? "application/octet-stream",
800805
url: `data:${resource.mimeType ?? "application/octet-stream"};base64,${resource.blob}`,
@@ -1043,6 +1048,7 @@ export namespace SessionPrompt {
10431048
pieces.push(
10441049
...result.attachments.map((attachment) => ({
10451050
...attachment,
1051+
id: Identifier.ascending("part"),
10461052
synthetic: true,
10471053
filename: attachment.filename ?? part.filename,
10481054
messageID: info.id,
@@ -1180,7 +1186,18 @@ export namespace SessionPrompt {
11801186
},
11811187
]
11821188
}),
1183-
).then((x) => x.flat())
1189+
)
1190+
.then((x) => x.flat())
1191+
.then((drafts) =>
1192+
drafts.map(
1193+
(part): MessageV2.Part => ({
1194+
...part,
1195+
id: Identifier.ascending("part"),
1196+
messageID: info.id,
1197+
sessionID: input.sessionID,
1198+
}),
1199+
),
1200+
)
11841201

11851202
await Plugin.trigger(
11861203
"chat.message",

packages/opencode/src/tool/batch.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,12 @@ export const BatchTool = Tool.define("batch", async () => {
7777
})
7878

7979
const result = await tool.execute(validatedParams, { ...ctx, callID: partID })
80+
const attachments = result.attachments?.map((attachment) => ({
81+
...attachment,
82+
id: Identifier.ascending("part"),
83+
messageID: ctx.messageID,
84+
sessionID: ctx.sessionID,
85+
}))
8086

8187
await Session.updatePart({
8288
id: partID,
@@ -91,7 +97,7 @@ export const BatchTool = Tool.define("batch", async () => {
9197
output: result.output,
9298
title: result.title,
9399
metadata: result.metadata,
94-
attachments: result.attachments,
100+
attachments,
95101
time: {
96102
start: callStartTime,
97103
end: Date.now(),

packages/opencode/src/tool/read.ts

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import { LSP } from "../lsp"
66
import { FileTime } from "../file/time"
77
import DESCRIPTION from "./read.txt"
88
import { Instance } from "../project/instance"
9-
import { Identifier } from "../id/id"
109
import { assertExternalDirectory } from "./external-directory"
1110
import { InstructionPrompt } from "../session/instruction"
1211

@@ -79,9 +78,6 @@ export const ReadTool = Tool.define("read", {
7978
},
8079
attachments: [
8180
{
82-
id: Identifier.ascending("part"),
83-
sessionID: ctx.sessionID,
84-
messageID: ctx.messageID,
8581
type: "file",
8682
mime,
8783
url: `data:${mime};base64,${Buffer.from(await file.bytes()).toString("base64")}`,

packages/opencode/src/tool/tool.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ export namespace Tool {
3636
title: string
3737
metadata: M
3838
output: string
39-
attachments?: MessageV2.FilePart[]
39+
attachments?: Omit<MessageV2.FilePart, "id" | "sessionID" | "messageID">[]
4040
}>
4141
formatValidationError?(error: z.ZodError): string
4242
}>

0 commit comments

Comments
 (0)