fix: pass image parts to custom commands (#6525)

Co-authored-by: Melih Mucuk <melih@monkeysteam.com>
This commit is contained in:
Melih Mucuk 2026-01-05 22:06:57 +03:00 committed by GitHub
parent bf9ee32d4a
commit a38e1701ee
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 81 additions and 3 deletions

View File

@ -1111,6 +1111,13 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
agent,
model: `${model.providerID}/${model.modelID}`,
variant,
parts: images.map((attachment) => ({
id: Identifier.ascending("part"),
type: "file" as const,
mime: attachment.mime,
url: attachment.dataUrl,
filename: attachment.filename,
})),
})
.catch((err) => {
showToast({
@ -1206,6 +1213,7 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
filename: attachment.filename,
}))
const messageID = Identifier.ascending("message")
const textPart = {
id: Identifier.ascending("part"),

View File

@ -559,6 +559,12 @@ export function Prompt(props: PromptProps) {
model: `${selectedModel.providerID}/${selectedModel.modelID}`,
messageID,
variant,
parts: nonTextParts
.filter((x) => x.type === "file")
.map((x) => ({
id: Identifier.ascending("part"),
...x,
})),
})
} else {
sdk.client.session.prompt({

View File

@ -1422,10 +1422,23 @@ export namespace SessionPrompt {
arguments: z.string(),
command: z.string(),
variant: z.string().optional(),
parts: z
.array(
z.discriminatedUnion("type", [
MessageV2.FilePart.omit({
messageID: true,
sessionID: true,
}).partial({
id: true,
}),
]),
)
.optional(),
})
export type CommandInput = z.infer<typeof CommandInput>
const bashRegex = /!`([^`]+)`/g
const argsRegex = /(?:[^\s"']+|"[^"]*"|'[^']*')+/g
// Match [Image N] as single token, quoted strings, or non-space sequences
const argsRegex = /(?:\[Image\s+\d+\]|"[^"]*"|'[^']*'|[^\s"']+)/gi
const placeholderRegex = /\$(\d+)/g
const quoteTrimRegex = /^["']|["']$/g
/**
@ -1516,6 +1529,7 @@ export namespace SessionPrompt {
throw error
}
const templateParts = await resolvePromptParts(template)
const parts =
(agent.mode === "subagent" && command.subtask !== false) || command.subtask === true
? [
@ -1525,10 +1539,10 @@ export namespace SessionPrompt {
description: command.description ?? "",
command: input.command,
// TODO: how can we make task tool accept a more complex input?
prompt: await resolvePromptParts(template).then((x) => x.find((y) => y.type === "text")?.text ?? ""),
prompt: templateParts.find((y) => y.type === "text")?.text ?? "",
},
]
: await resolvePromptParts(template)
: [...templateParts, ...(input.parts ?? [])]
const result = (await prompt({
sessionID: input.sessionID,

View File

@ -24,6 +24,7 @@ import type {
ExperimentalResourceListResponses,
FileListResponses,
FilePartInput,
FilePartSource,
FileReadResponses,
FileStatusResponses,
FindFilesResponses,
@ -1451,6 +1452,14 @@ export class Session extends HeyApiClient {
arguments?: string
command?: string
variant?: string
parts?: Array<{
id?: string
type: "file"
mime: string
filename?: string
url: string
source?: FilePartSource
}>
},
options?: Options<never, ThrowOnError>,
) {
@ -1467,6 +1476,7 @@ export class Session extends HeyApiClient {
{ in: "body", key: "arguments" },
{ in: "body", key: "command" },
{ in: "body", key: "variant" },
{ in: "body", key: "parts" },
],
},
],

View File

@ -3292,6 +3292,14 @@ export type SessionCommandData = {
arguments: string
command: string
variant?: string
parts?: Array<{
id?: string
type: "file"
mime: string
filename?: string
url: string
source?: FilePartSource
}>
}
path: {
/**

View File

@ -2667,6 +2667,38 @@
},
"variant": {
"type": "string"
},
"parts": {
"type": "array",
"items": {
"anyOf": [
{
"type": "object",
"properties": {
"id": {
"type": "string"
},
"type": {
"type": "string",
"const": "file"
},
"mime": {
"type": "string"
},
"filename": {
"type": "string"
},
"url": {
"type": "string"
},
"source": {
"$ref": "#/components/schemas/FilePartSource"
}
},
"required": ["type", "mime", "url"]
}
]
}
}
},
"required": ["arguments", "command"]