fix(tools/background-task): respect block=true even when full_session=true
Move blocking/polling logic before full_session branch so that
block=true waits for task completion regardless of output format.
🤖 Generated with assistance of oh-my-opencode
This commit is contained in:
@@ -77,13 +77,40 @@ export function createBackgroundOutput(manager: BackgroundOutputManager, client:
|
||||
storeToolMetadata(ctx.sessionID, callID, meta)
|
||||
}
|
||||
|
||||
const isActive = task.status === "pending" || task.status === "running"
|
||||
const shouldBlock = args.block === true
|
||||
const timeoutMs = Math.min(args.timeout ?? 60000, 600000)
|
||||
const fullSession = args.full_session ?? true
|
||||
|
||||
let resolvedTask = task
|
||||
|
||||
if (shouldBlock && (task.status === "pending" || task.status === "running")) {
|
||||
const startTime = Date.now()
|
||||
while (Date.now() - startTime < timeoutMs) {
|
||||
await delay(1000)
|
||||
|
||||
const currentTask = manager.getTask(args.task_id)
|
||||
if (!currentTask) {
|
||||
return `Task was deleted: ${args.task_id}`
|
||||
}
|
||||
|
||||
if (currentTask.status !== "pending" && currentTask.status !== "running") {
|
||||
resolvedTask = currentTask
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
const finalCheck = manager.getTask(args.task_id)
|
||||
if (finalCheck) {
|
||||
resolvedTask = finalCheck
|
||||
}
|
||||
}
|
||||
|
||||
const isActive = resolvedTask.status === "pending" || resolvedTask.status === "running"
|
||||
const includeThinking = isActive || (args.include_thinking ?? false)
|
||||
const includeToolResults = isActive || (args.include_tool_results ?? false)
|
||||
|
||||
if (fullSession) {
|
||||
return await formatFullSession(task, client, {
|
||||
return await formatFullSession(resolvedTask, client, {
|
||||
includeThinking,
|
||||
messageLimit: args.message_limit,
|
||||
sinceMessageId: args.since_message_id,
|
||||
@@ -92,44 +119,15 @@ export function createBackgroundOutput(manager: BackgroundOutputManager, client:
|
||||
})
|
||||
}
|
||||
|
||||
const shouldBlock = args.block === true
|
||||
const timeoutMs = Math.min(args.timeout ?? 60000, 600000)
|
||||
|
||||
if (task.status === "completed") {
|
||||
return await formatTaskResult(task, client)
|
||||
if (resolvedTask.status === "completed") {
|
||||
return await formatTaskResult(resolvedTask, client)
|
||||
}
|
||||
|
||||
if (task.status === "error" || task.status === "cancelled" || task.status === "interrupt") {
|
||||
return formatTaskStatus(task)
|
||||
if (resolvedTask.status === "error" || resolvedTask.status === "cancelled" || resolvedTask.status === "interrupt") {
|
||||
return formatTaskStatus(resolvedTask)
|
||||
}
|
||||
|
||||
if (!shouldBlock) {
|
||||
return formatTaskStatus(task)
|
||||
}
|
||||
|
||||
const startTime = Date.now()
|
||||
while (Date.now() - startTime < timeoutMs) {
|
||||
await delay(1000)
|
||||
|
||||
const currentTask = manager.getTask(args.task_id)
|
||||
if (!currentTask) {
|
||||
return `Task was deleted: ${args.task_id}`
|
||||
}
|
||||
|
||||
if (currentTask.status === "completed") {
|
||||
return await formatTaskResult(currentTask, client)
|
||||
}
|
||||
|
||||
if (currentTask.status === "error" || currentTask.status === "cancelled" || currentTask.status === "interrupt") {
|
||||
return formatTaskStatus(currentTask)
|
||||
}
|
||||
}
|
||||
|
||||
const finalTask = manager.getTask(args.task_id)
|
||||
if (!finalTask) {
|
||||
return `Task was deleted: ${args.task_id}`
|
||||
}
|
||||
return `Timeout exceeded (${timeoutMs}ms). Task still ${finalTask.status}.\n\n${formatTaskStatus(finalTask)}`
|
||||
return formatTaskStatus(resolvedTask)
|
||||
} catch (error) {
|
||||
return `Error getting output: ${error instanceof Error ? error.message : String(error)}`
|
||||
}
|
||||
|
||||
@@ -339,6 +339,48 @@ describe("background_output full_session", () => {
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
describe("background_output blocking", () => {
|
||||
test("block=true waits for task completion even with default full_session=true", async () => {
|
||||
// #given a task that transitions running → completed after 2 polls
|
||||
let pollCount = 0
|
||||
const task = createTask({ status: "running" })
|
||||
const manager: BackgroundOutputManager = {
|
||||
getTask: (id: string) => {
|
||||
if (id !== task.id) return undefined
|
||||
pollCount++
|
||||
if (pollCount >= 3) {
|
||||
task.status = "completed"
|
||||
}
|
||||
return task
|
||||
},
|
||||
}
|
||||
const client = createMockClient({
|
||||
"ses-1": [
|
||||
{
|
||||
id: "m1",
|
||||
info: { role: "assistant", time: "2026-01-01T00:00:00Z" },
|
||||
parts: [{ type: "text", text: "completed result" }],
|
||||
},
|
||||
],
|
||||
})
|
||||
const tool = createBackgroundOutput(manager, client)
|
||||
|
||||
// #when block=true, full_session not specified (defaults to true)
|
||||
const output = await tool.execute({
|
||||
task_id: "task-1",
|
||||
block: true,
|
||||
timeout: 10000,
|
||||
}, mockContext)
|
||||
|
||||
// #then should have waited and returned full session output
|
||||
expect(task.status).toBe("completed")
|
||||
expect(pollCount).toBeGreaterThanOrEqual(3)
|
||||
expect(output).toContain("# Full Session Output")
|
||||
expect(output).toContain("completed result")
|
||||
})
|
||||
})
|
||||
|
||||
describe("background_cancel", () => {
|
||||
test("cancels a running task via manager", async () => {
|
||||
// #given
|
||||
|
||||
Reference in New Issue
Block a user