Skip to content

Commit 8b9b9ad

Browse files
authored
fix: ensure images read by agent dont count against quota (#22168)
1 parent 3729fd5 commit 8b9b9ad

2 files changed

Lines changed: 22 additions & 4 deletions

File tree

packages/opencode/src/plugin/github-copilot/copilot.ts

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { iife } from "@/util/iife"
55
import { Log } from "../../util/log"
66
import { setTimeout as sleep } from "node:timers/promises"
77
import { CopilotModels } from "./models"
8+
import { MessageV2 } from "@/session/message-v2"
89

910
const log = Log.create({ service: "plugin.copilot" })
1011

@@ -27,6 +28,21 @@ function base(enterpriseUrl?: string) {
2728
return enterpriseUrl ? `https://copilot-api.${normalizeDomain(enterpriseUrl)}` : "https://api.githubcopilot.com"
2829
}
2930

31+
// Check if a message is a synthetic user msg used to attach an image from a tool call
32+
function imgMsg(msg: any): boolean {
33+
if (msg?.role !== "user") return false
34+
35+
// Handle the 3 api formats
36+
37+
const content = msg.content
38+
if (typeof content === "string") return content === MessageV2.SYNTHETIC_ATTACHMENT_PROMPT
39+
if (!Array.isArray(content)) return false
40+
return content.some(
41+
(part: any) =>
42+
(part?.type === "text" || part?.type === "input_text") && part.text === MessageV2.SYNTHETIC_ATTACHMENT_PROMPT,
43+
)
44+
}
45+
3046
function fix(model: Model, url: string): Model {
3147
return {
3248
...model,
@@ -90,7 +106,7 @@ export async function CopilotAuthPlugin(input: PluginInput): Promise<Hooks> {
90106
(msg: any) =>
91107
Array.isArray(msg.content) && msg.content.some((part: any) => part.type === "image_url"),
92108
),
93-
isAgent: last?.role !== "user",
109+
isAgent: last?.role !== "user" || imgMsg(last),
94110
}
95111
}
96112

@@ -102,7 +118,7 @@ export async function CopilotAuthPlugin(input: PluginInput): Promise<Hooks> {
102118
(item: any) =>
103119
Array.isArray(item?.content) && item.content.some((part: any) => part.type === "input_image"),
104120
),
105-
isAgent: last?.role !== "user",
121+
isAgent: last?.role !== "user" || imgMsg(last),
106122
}
107123
}
108124

@@ -124,7 +140,7 @@ export async function CopilotAuthPlugin(input: PluginInput): Promise<Hooks> {
124140
part.content.some((nested: any) => nested?.type === "image")),
125141
),
126142
),
127-
isAgent: !(last?.role === "user" && hasNonToolCalls),
143+
isAgent: !(last?.role === "user" && hasNonToolCalls) || imgMsg(last),
128144
}
129145
}
130146
} catch {}

packages/opencode/src/session/message-v2.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ interface FetchDecompressionError extends Error {
2525
}
2626

2727
export namespace MessageV2 {
28+
export const SYNTHETIC_ATTACHMENT_PROMPT = "Attached image(s) from tool result:"
29+
2830
export function isMedia(mime: string) {
2931
return mime.startsWith("image/") || mime === "application/pdf"
3032
}
@@ -808,7 +810,7 @@ export namespace MessageV2 {
808810
parts: [
809811
{
810812
type: "text" as const,
811-
text: "Attached image(s) from tool result:",
813+
text: SYNTHETIC_ATTACHMENT_PROMPT,
812814
},
813815
...media.map((attachment) => ({
814816
type: "file" as const,

0 commit comments

Comments
 (0)