tweak: drop ids from attachments in tools, assign them in prompt.ts instead (#13890)

This commit is contained in:
Aiden Cline
2026-02-16 14:59:57 -06:00
committed by GitHub
parent 9d3c81a683
commit a580fb47d2
7 changed files with 37 additions and 20 deletions

View File

@@ -445,6 +445,12 @@ export namespace SessionPrompt {
log.error("subtask execution failed", { error, agent: task.agent, description: task.description }) log.error("subtask execution failed", { error, agent: task.agent, description: task.description })
return undefined return undefined
}) })
const attachments = result?.attachments?.map((attachment) => ({
...attachment,
id: Identifier.ascending("part"),
sessionID,
messageID: assistantMessage.id,
}))
await Plugin.trigger( await Plugin.trigger(
"tool.execute.after", "tool.execute.after",
{ {
@@ -467,7 +473,7 @@ export namespace SessionPrompt {
title: result.title, title: result.title,
metadata: result.metadata, metadata: result.metadata,
output: result.output, output: result.output,
attachments: result.attachments, attachments,
time: { time: {
...part.state.time, ...part.state.time,
end: Date.now(), end: Date.now(),
@@ -797,6 +803,15 @@ export namespace SessionPrompt {
}, },
) )
const result = await item.execute(args, ctx) const result = await item.execute(args, ctx)
const output = {
...result,
attachments: result.attachments?.map((attachment) => ({
...attachment,
id: Identifier.ascending("part"),
sessionID: ctx.sessionID,
messageID: input.processor.message.id,
})),
}
await Plugin.trigger( await Plugin.trigger(
"tool.execute.after", "tool.execute.after",
{ {
@@ -805,9 +820,9 @@ export namespace SessionPrompt {
callID: ctx.callID, callID: ctx.callID,
args, args,
}, },
result, output,
) )
return result return output
}, },
}) })
} }
@@ -855,16 +870,13 @@ export namespace SessionPrompt {
) )
const textParts: string[] = [] const textParts: string[] = []
const attachments: MessageV2.FilePart[] = [] const attachments: Omit<MessageV2.FilePart, "id" | "sessionID" | "messageID">[] = []
for (const contentItem of result.content) { for (const contentItem of result.content) {
if (contentItem.type === "text") { if (contentItem.type === "text") {
textParts.push(contentItem.text) textParts.push(contentItem.text)
} else if (contentItem.type === "image") { } else if (contentItem.type === "image") {
attachments.push({ attachments.push({
id: Identifier.ascending("part"),
sessionID: input.session.id,
messageID: input.processor.message.id,
type: "file", type: "file",
mime: contentItem.mimeType, mime: contentItem.mimeType,
url: `data:${contentItem.mimeType};base64,${contentItem.data}`, url: `data:${contentItem.mimeType};base64,${contentItem.data}`,
@@ -876,9 +888,6 @@ export namespace SessionPrompt {
} }
if (resource.blob) { if (resource.blob) {
attachments.push({ attachments.push({
id: Identifier.ascending("part"),
sessionID: input.session.id,
messageID: input.processor.message.id,
type: "file", type: "file",
mime: resource.mimeType ?? "application/octet-stream", mime: resource.mimeType ?? "application/octet-stream",
url: `data:${resource.mimeType ?? "application/octet-stream"};base64,${resource.blob}`, url: `data:${resource.mimeType ?? "application/octet-stream"};base64,${resource.blob}`,
@@ -1157,6 +1166,7 @@ export namespace SessionPrompt {
pieces.push( pieces.push(
...result.attachments.map((attachment) => ({ ...result.attachments.map((attachment) => ({
...attachment, ...attachment,
id: Identifier.ascending("part"),
synthetic: true, synthetic: true,
filename: attachment.filename ?? part.filename, filename: attachment.filename ?? part.filename,
messageID: info.id, messageID: info.id,

View File

@@ -77,6 +77,12 @@ export const BatchTool = Tool.define("batch", async () => {
}) })
const result = await tool.execute(validatedParams, { ...ctx, callID: partID }) const result = await tool.execute(validatedParams, { ...ctx, callID: partID })
const attachments = result.attachments?.map((attachment) => ({
...attachment,
id: Identifier.ascending("part"),
sessionID: ctx.sessionID,
messageID: ctx.messageID,
}))
await Session.updatePart({ await Session.updatePart({
id: partID, id: partID,
@@ -91,7 +97,7 @@ export const BatchTool = Tool.define("batch", async () => {
output: result.output, output: result.output,
title: result.title, title: result.title,
metadata: result.metadata, metadata: result.metadata,
attachments: result.attachments, attachments,
time: { time: {
start: callStartTime, start: callStartTime,
end: Date.now(), end: Date.now(),

View File

@@ -6,7 +6,6 @@ import { LSP } from "../lsp"
import { FileTime } from "../file/time" import { FileTime } from "../file/time"
import DESCRIPTION from "./read.txt" import DESCRIPTION from "./read.txt"
import { Instance } from "../project/instance" import { Instance } from "../project/instance"
import { Identifier } from "../id/id"
import { assertExternalDirectory } from "./external-directory" import { assertExternalDirectory } from "./external-directory"
import { InstructionPrompt } from "../session/instruction" import { InstructionPrompt } from "../session/instruction"
@@ -127,9 +126,6 @@ export const ReadTool = Tool.define("read", {
}, },
attachments: [ attachments: [
{ {
id: Identifier.ascending("part"),
sessionID: ctx.sessionID,
messageID: ctx.messageID,
type: "file", type: "file",
mime, mime,
url: `data:${mime};base64,${Buffer.from(await file.bytes()).toString("base64")}`, url: `data:${mime};base64,${Buffer.from(await file.bytes()).toString("base64")}`,

View File

@@ -36,7 +36,7 @@ export namespace Tool {
title: string title: string
metadata: M metadata: M
output: string output: string
attachments?: MessageV2.FilePart[] attachments?: Omit<MessageV2.FilePart, "id" | "sessionID" | "messageID">[]
}> }>
formatValidationError?(error: z.ZodError): string formatValidationError?(error: z.ZodError): string
}> }>

View File

@@ -3,7 +3,6 @@ import { Tool } from "./tool"
import TurndownService from "turndown" import TurndownService from "turndown"
import DESCRIPTION from "./webfetch.txt" import DESCRIPTION from "./webfetch.txt"
import { abortAfterAny } from "../util/abort" import { abortAfterAny } from "../util/abort"
import { Identifier } from "../id/id"
const MAX_RESPONSE_SIZE = 5 * 1024 * 1024 // 5MB const MAX_RESPONSE_SIZE = 5 * 1024 * 1024 // 5MB
const DEFAULT_TIMEOUT = 30 * 1000 // 30 seconds const DEFAULT_TIMEOUT = 30 * 1000 // 30 seconds
@@ -103,9 +102,6 @@ export const WebFetchTool = Tool.define("webfetch", {
metadata: {}, metadata: {},
attachments: [ attachments: [
{ {
id: Identifier.ascending("part"),
sessionID: ctx.sessionID,
messageID: ctx.messageID,
type: "file", type: "file",
mime, mime,
url: `data:${mime};base64,${base64Content}`, url: `data:${mime};base64,${base64Content}`,

View File

@@ -349,6 +349,9 @@ describe("tool.read truncation", () => {
expect(result.metadata.truncated).toBe(false) expect(result.metadata.truncated).toBe(false)
expect(result.attachments).toBeDefined() expect(result.attachments).toBeDefined()
expect(result.attachments?.length).toBe(1) expect(result.attachments?.length).toBe(1)
expect(result.attachments?.[0]).not.toHaveProperty("id")
expect(result.attachments?.[0]).not.toHaveProperty("sessionID")
expect(result.attachments?.[0]).not.toHaveProperty("messageID")
}, },
}) })
}) })
@@ -363,6 +366,9 @@ describe("tool.read truncation", () => {
expect(result.attachments).toBeDefined() expect(result.attachments).toBeDefined()
expect(result.attachments?.length).toBe(1) expect(result.attachments?.length).toBe(1)
expect(result.attachments?.[0].type).toBe("file") expect(result.attachments?.[0].type).toBe("file")
expect(result.attachments?.[0]).not.toHaveProperty("id")
expect(result.attachments?.[0]).not.toHaveProperty("sessionID")
expect(result.attachments?.[0]).not.toHaveProperty("messageID")
}, },
}) })
}) })

View File

@@ -46,6 +46,9 @@ describe("tool.webfetch", () => {
expect(result.attachments?.[0].type).toBe("file") expect(result.attachments?.[0].type).toBe("file")
expect(result.attachments?.[0].mime).toBe("image/png") expect(result.attachments?.[0].mime).toBe("image/png")
expect(result.attachments?.[0].url.startsWith("data:image/png;base64,")).toBe(true) expect(result.attachments?.[0].url.startsWith("data:image/png;base64,")).toBe(true)
expect(result.attachments?.[0]).not.toHaveProperty("id")
expect(result.attachments?.[0]).not.toHaveProperty("sessionID")
expect(result.attachments?.[0]).not.toHaveProperty("messageID")
}, },
}) })
}, },