summaryrefslogtreecommitdiffhomepage
path: root/packages
diff options
context:
space:
mode:
authorAiden Cline <[email protected]>2026-01-30 15:55:00 -0600
committerDax Raad <[email protected]>2026-02-01 19:26:23 -0500
commit612b656d3670f252541be79f96bfda31d78dcf73 (patch)
tree124ac833f9fbfecd6c1b39a63f3bb139230ee7f0 /packages
parentcb6ec0a564cfc332e7af3c844157e145c1d86a60 (diff)
downloadopencode-612b656d3670f252541be79f96bfda31d78dcf73.tar.gz
opencode-612b656d3670f252541be79f96bfda31d78dcf73.zip
fix: adjust resolve parts so that when messages with multiple @ references occur, the tool calls are properly ordered
Diffstat (limited to 'packages')
-rw-r--r--packages/opencode/src/session/processor.ts10
-rw-r--r--packages/opencode/src/session/prompt.ts69
-rw-r--r--packages/opencode/src/tool/batch.ts8
-rw-r--r--packages/opencode/src/tool/read.ts4
-rw-r--r--packages/opencode/src/tool/tool.ts2
5 files changed, 60 insertions, 33 deletions
diff --git a/packages/opencode/src/session/processor.ts b/packages/opencode/src/session/processor.ts
index 270710561..ada6f8314 100644
--- a/packages/opencode/src/session/processor.ts
+++ b/packages/opencode/src/session/processor.ts
@@ -172,6 +172,14 @@ export namespace SessionProcessor {
case "tool-result": {
const match = toolcalls[value.toolCallId]
if (match && match.state.status === "running") {
+ const attachments = value.output.attachments?.map(
+ (attachment: Omit<MessageV2.FilePart, "id" | "messageID" | "sessionID">) => ({
+ ...attachment,
+ id: Identifier.ascending("part"),
+ messageID: match.messageID,
+ sessionID: match.sessionID,
+ }),
+ )
await Session.updatePart({
...match,
state: {
@@ -184,7 +192,7 @@ export namespace SessionProcessor {
start: match.state.time.start,
end: Date.now(),
},
- attachments: value.output.attachments,
+ attachments,
},
})
diff --git a/packages/opencode/src/session/prompt.ts b/packages/opencode/src/session/prompt.ts
index ba77cd7ca..c6d040f2f 100644
--- a/packages/opencode/src/session/prompt.ts
+++ b/packages/opencode/src/session/prompt.ts
@@ -187,13 +187,17 @@ export namespace SessionPrompt {
text: template,
},
]
- const files = ConfigMarkdown.files(template)
+ const matches = ConfigMarkdown.files(template)
const seen = new Set<string>()
- await Promise.all(
- files.map(async (match) => {
- const name = match[1]
- if (seen.has(name)) return
+ const names = matches
+ .map((match) => match[1])
+ .filter((name) => {
+ if (seen.has(name)) return false
seen.add(name)
+ return true
+ })
+ const resolved = await Promise.all(
+ names.map(async (name) => {
const filepath = name.startsWith("~/")
? path.join(os.homedir(), name.slice(2))
: path.resolve(Instance.worktree, name)
@@ -201,33 +205,34 @@ export namespace SessionPrompt {
const stats = await fs.stat(filepath).catch(() => undefined)
if (!stats) {
const agent = await Agent.get(name)
- if (agent) {
- parts.push({
- type: "agent",
- name: agent.name,
- })
- }
- return
+ if (!agent) return undefined
+ return {
+ type: "agent",
+ name: agent.name,
+ } satisfies PromptInput["parts"][number]
}
if (stats.isDirectory()) {
- parts.push({
+ return {
type: "file",
url: `file://${filepath}`,
filename: name,
mime: "application/x-directory",
- })
- return
+ } satisfies PromptInput["parts"][number]
}
- parts.push({
+ return {
type: "file",
url: `file://${filepath}`,
filename: name,
mime: "text/plain",
- })
+ } satisfies PromptInput["parts"][number]
}),
)
+ for (const item of resolved) {
+ if (!item) continue
+ parts.push(item)
+ }
return parts
}
@@ -424,6 +429,12 @@ export namespace SessionPrompt {
assistantMessage.time.completed = Date.now()
await Session.updateMessage(assistantMessage)
if (result && part.state.status === "running") {
+ const attachments = result.attachments?.map((attachment) => ({
+ ...attachment,
+ id: Identifier.ascending("part"),
+ messageID: assistantMessage.id,
+ sessionID: assistantMessage.sessionID,
+ }))
await Session.updatePart({
...part,
state: {
@@ -432,7 +443,7 @@ export namespace SessionPrompt {
title: result.title,
metadata: result.metadata,
output: result.output,
- attachments: result.attachments,
+ attachments,
time: {
...part.state.time,
end: Date.now(),
@@ -771,16 +782,13 @@ export namespace SessionPrompt {
)
const textParts: string[] = []
- const attachments: MessageV2.FilePart[] = []
+ const attachments: Omit<MessageV2.FilePart, "id" | "messageID" | "sessionID">[] = []
for (const contentItem of result.content) {
if (contentItem.type === "text") {
textParts.push(contentItem.text)
} else if (contentItem.type === "image") {
attachments.push({
- id: Identifier.ascending("part"),
- sessionID: input.session.id,
- messageID: input.processor.message.id,
type: "file",
mime: contentItem.mimeType,
url: `data:${contentItem.mimeType};base64,${contentItem.data}`,
@@ -792,9 +800,6 @@ export namespace SessionPrompt {
}
if (resource.blob) {
attachments.push({
- id: Identifier.ascending("part"),
- sessionID: input.session.id,
- messageID: input.processor.message.id,
type: "file",
mime: resource.mimeType ?? "application/octet-stream",
url: `data:${resource.mimeType ?? "application/octet-stream"};base64,${resource.blob}`,
@@ -1043,6 +1048,7 @@ export namespace SessionPrompt {
pieces.push(
...result.attachments.map((attachment) => ({
...attachment,
+ id: Identifier.ascending("part"),
synthetic: true,
filename: attachment.filename ?? part.filename,
messageID: info.id,
@@ -1180,7 +1186,18 @@ export namespace SessionPrompt {
},
]
}),
- ).then((x) => x.flat())
+ )
+ .then((x) => x.flat())
+ .then((drafts) =>
+ drafts.map(
+ (part): MessageV2.Part => ({
+ ...part,
+ id: Identifier.ascending("part"),
+ messageID: info.id,
+ sessionID: input.sessionID,
+ }),
+ ),
+ )
await Plugin.trigger(
"chat.message",
diff --git a/packages/opencode/src/tool/batch.ts b/packages/opencode/src/tool/batch.ts
index ba34eb48f..b5c3ad0a1 100644
--- a/packages/opencode/src/tool/batch.ts
+++ b/packages/opencode/src/tool/batch.ts
@@ -77,6 +77,12 @@ export const BatchTool = Tool.define("batch", async () => {
})
const result = await tool.execute(validatedParams, { ...ctx, callID: partID })
+ const attachments = result.attachments?.map((attachment) => ({
+ ...attachment,
+ id: Identifier.ascending("part"),
+ messageID: ctx.messageID,
+ sessionID: ctx.sessionID,
+ }))
await Session.updatePart({
id: partID,
@@ -91,7 +97,7 @@ export const BatchTool = Tool.define("batch", async () => {
output: result.output,
title: result.title,
metadata: result.metadata,
- attachments: result.attachments,
+ attachments,
time: {
start: callStartTime,
end: Date.now(),
diff --git a/packages/opencode/src/tool/read.ts b/packages/opencode/src/tool/read.ts
index f230cdf44..13236d44d 100644
--- a/packages/opencode/src/tool/read.ts
+++ b/packages/opencode/src/tool/read.ts
@@ -6,7 +6,6 @@ import { LSP } from "../lsp"
import { FileTime } from "../file/time"
import DESCRIPTION from "./read.txt"
import { Instance } from "../project/instance"
-import { Identifier } from "../id/id"
import { assertExternalDirectory } from "./external-directory"
import { InstructionPrompt } from "../session/instruction"
@@ -79,9 +78,6 @@ export const ReadTool = Tool.define("read", {
},
attachments: [
{
- id: Identifier.ascending("part"),
- sessionID: ctx.sessionID,
- messageID: ctx.messageID,
type: "file",
mime,
url: `data:${mime};base64,${Buffer.from(await file.bytes()).toString("base64")}`,
diff --git a/packages/opencode/src/tool/tool.ts b/packages/opencode/src/tool/tool.ts
index 3d17ea192..0e78ba665 100644
--- a/packages/opencode/src/tool/tool.ts
+++ b/packages/opencode/src/tool/tool.ts
@@ -36,7 +36,7 @@ export namespace Tool {
title: string
metadata: M
output: string
- attachments?: MessageV2.FilePart[]
+ attachments?: Omit<MessageV2.FilePart, "id" | "sessionID" | "messageID">[]
}>
formatValidationError?(error: z.ZodError): string
}>