fix(batch): restore per-tool UI feedback + UX improvements (#4387)

This commit is contained in:
Baptiste Cavallo
2025-11-16 23:31:41 +01:00
committed by GitHub
parent 832be6e7eb
commit 72e604744d
2 changed files with 70 additions and 16 deletions

View File

@@ -1,4 +1,7 @@
{
"$schema": "https://opencode.ai/config.json",
"plugin": ["opencode-openai-codex-auth"]
"plugin": ["opencode-openai-codex-auth"],
"experimental": {
"batch_tool": true
}
}

View File

@@ -31,6 +31,7 @@ export const BatchTool = Tool.define("batch", async () => {
return `Invalid parameters for tool 'batch':\n${formattedErrors}\n\nExpected payload format:\n [{"tool": "tool_name", "parameters": {...}}, {...}]`
},
async execute(params, ctx) {
const { Session } = await import("../session")
const { Identifier } = await import("../id/id")
const toolCalls = params.tool_calls
@@ -39,26 +40,36 @@ export const BatchTool = Tool.define("batch", async () => {
const availableTools = await ToolRegistry.tools("", "")
const toolMap = new Map(availableTools.map((t) => [t.id, t]))
const partIDs = new Map<(typeof toolCalls)[0], string>()
for (const call of toolCalls) {
if (DISALLOWED.has(call.tool)) {
throw new Error(
`tool '${call.tool}' is not allowed in batch. Disallowed tools: ${Array.from(DISALLOWED).join(", ")}`,
)
}
if (!toolMap.has(call.tool)) {
const allowed = Array.from(toolMap.keys()).filter((name) => !FILTERED_FROM_SUGGESTIONS.has(name))
throw new Error(`tool '${call.tool}' is not available. Available tools: ${allowed.join(", ")}`)
}
const partID = Identifier.ascending("part")
partIDs.set(call, partID)
Session.updatePart({
id: partID,
messageID: ctx.messageID,
sessionID: ctx.sessionID,
type: "tool",
tool: call.tool,
callID: partID,
state: {
status: "pending",
input: call.parameters,
raw: JSON.stringify(call),
},
})
}
const executeCall = async (call: (typeof toolCalls)[0]) => {
if (ctx.abort.aborted) {
return { success: false as const, tool: call.tool, error: new Error("Aborted") }
}
const partID = Identifier.ascending("part")
const callStartTime = Date.now()
const partID = partIDs.get(call)!
try {
if (DISALLOWED.has(call.tool)) {
throw new Error(
`Tool '${call.tool}' is not allowed in batch. Disallowed tools: ${Array.from(DISALLOWED).join(", ")}`,
)
}
const tool = toolMap.get(call.tool)
if (!tool) {
const availableToolsList = Array.from(toolMap.keys()).filter((name) => !FILTERED_FROM_SUGGESTIONS.has(name))
@@ -68,13 +79,53 @@ export const BatchTool = Tool.define("batch", async () => {
const result = await tool.execute(validatedParams, { ...ctx, callID: partID })
await Session.updatePart({
id: partID,
messageID: ctx.messageID,
sessionID: ctx.sessionID,
type: "tool",
tool: call.tool,
callID: partID,
state: {
status: "completed",
input: call.parameters,
output: result.output,
title: result.title,
metadata: result.metadata,
attachments: result.attachments,
time: {
start: callStartTime,
end: Date.now(),
},
},
})
return { success: true as const, tool: call.tool, result }
} catch (error) {
await Session.updatePart({
id: partID,
messageID: ctx.messageID,
sessionID: ctx.sessionID,
type: "tool",
tool: call.tool,
callID: partID,
state: {
status: "error",
input: call.parameters,
error: error instanceof Error ? error.message : String(error),
time: {
start: callStartTime,
end: Date.now(),
},
},
})
return { success: false as const, tool: call.tool, error }
}
}
const results = await Promise.all(toolCalls.flatMap((call) => executeCall(call)))
const results = await Promise.all(toolCalls.map((call) => executeCall(call)))
const successfulCalls = results.filter((r) => r.success).length
const failedCalls = toolCalls.length - successfulCalls