refactor(background-task): unify background_result and background_status into background_output tool
- Merge background_status into background_output with block parameter - Replace background_result references with background_output throughout codebase - Update tool descriptions to reflect new unified API - Remove background-tasks.json (memory-based only) - Simplify notification messages and tool usage instructions
This commit is contained in:
@@ -1,27 +0,0 @@
|
|||||||
[
|
|
||||||
{
|
|
||||||
"id": "bg_wzsdt60b",
|
|
||||||
"sessionID": "ses_4f3e89f0dffeooeXNVx5QCifse",
|
|
||||||
"parentSessionID": "ses_4f3e8d141ffeyfJ1taVVOdQTzx",
|
|
||||||
"parentMessageID": "msg_b0c172ee1001w2B52VSZrP08PJ",
|
|
||||||
"description": "Explore opencode in codebase",
|
|
||||||
"agent": "explore",
|
|
||||||
"status": "completed",
|
|
||||||
"startedAt": "2025-12-11T06:26:57.395Z",
|
|
||||||
"completedAt": "2025-12-11T06:27:36.778Z"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "bg_392b9c9b",
|
|
||||||
"sessionID": "ses_4f38ebf4fffeJZBocIn3UVv7vE",
|
|
||||||
"parentSessionID": "ses_4f38eefa0ffeKV0pVNnwT37P5L",
|
|
||||||
"parentMessageID": "msg_b0c7110d2001TMBlPeEYIrByvs",
|
|
||||||
"description": "Test explore agent",
|
|
||||||
"agent": "explore",
|
|
||||||
"status": "running",
|
|
||||||
"startedAt": "2025-12-11T08:05:07.378Z",
|
|
||||||
"progress": {
|
|
||||||
"toolCalls": 0,
|
|
||||||
"lastUpdate": "2025-12-11T08:05:07.378Z"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
@@ -232,7 +232,7 @@ export class BackgroundManager {
|
|||||||
}).catch(() => {})
|
}).catch(() => {})
|
||||||
}
|
}
|
||||||
|
|
||||||
const message = `[BACKGROUND TASK COMPLETED] Task "${task.description}" finished in ${duration} (${toolCalls} tool calls). Use background_result with taskId="${task.id}" to get results.`
|
const message = `[BACKGROUND TASK COMPLETED] Task "${task.description}" finished in ${duration} (${toolCalls} tool calls). Use background_output with task_id="${task.id}" to get results.`
|
||||||
|
|
||||||
const mainSessionID = getMainSessionID()
|
const mainSessionID = getMainSessionID()
|
||||||
if (!mainSessionID) {
|
if (!mainSessionID) {
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ function formatNotifications(tasks: BackgroundTask[]): string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (tasks.length > 1) {
|
if (tasks.length > 1) {
|
||||||
let message = `✅ **Background Tasks Complete (${tasks.length})**\n\n`
|
let message = `**Background Tasks Complete (${tasks.length})**\n\n`
|
||||||
|
|
||||||
for (const task of tasks) {
|
for (const task of tasks) {
|
||||||
const duration = formatDuration(task.startedAt, task.completedAt)
|
const duration = formatDuration(task.startedAt, task.completedAt)
|
||||||
@@ -50,7 +50,7 @@ function formatNotifications(tasks: BackgroundTask[]): string {
|
|||||||
message += ` Duration: ${duration} | Tool calls: ${toolCalls}\n\n`
|
message += ` Duration: ${duration} | Tool calls: ${toolCalls}\n\n`
|
||||||
}
|
}
|
||||||
|
|
||||||
message += `Use \`background_result\` tool to retrieve results.`
|
message += `Use \`background_output\` tool to retrieve results.`
|
||||||
|
|
||||||
return message
|
return message
|
||||||
}
|
}
|
||||||
@@ -59,14 +59,14 @@ function formatNotifications(tasks: BackgroundTask[]): string {
|
|||||||
const duration = formatDuration(task.startedAt, task.completedAt)
|
const duration = formatDuration(task.startedAt, task.completedAt)
|
||||||
const toolCalls = task.progress?.toolCalls ?? 0
|
const toolCalls = task.progress?.toolCalls ?? 0
|
||||||
|
|
||||||
return `✅ **Background Task Complete**
|
return `**Background Task Complete**
|
||||||
|
|
||||||
**Task ID:** ${task.id}
|
**Task ID:** ${task.id}
|
||||||
**Description:** ${task.description}
|
**Description:** ${task.description}
|
||||||
**Duration:** ${duration}
|
**Duration:** ${duration}
|
||||||
**Tool calls:** ${toolCalls}
|
**Tool calls:** ${toolCalls}
|
||||||
|
|
||||||
The background task has finished. Use \`background_result\` tool with task ID \`${task.id}\` to retrieve the full result.`
|
Use \`background_output\` tool with task_id="${task.id}" to retrieve the full result.`
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createBackgroundNotificationHook(manager: BackgroundManager) {
|
export function createBackgroundNotificationHook(manager: BackgroundManager) {
|
||||||
|
|||||||
@@ -11,37 +11,24 @@ Arguments:
|
|||||||
- description: Short task description (shown in status)
|
- description: Short task description (shown in status)
|
||||||
- prompt: Full detailed prompt for the agent
|
- prompt: Full detailed prompt for the agent
|
||||||
- agent: Agent type to use (any agent allowed)
|
- agent: Agent type to use (any agent allowed)
|
||||||
- session_id: Optional parent session ID (auto-detected if omitted)
|
|
||||||
|
|
||||||
Returns immediately with task ID and session info. Use \`background_status\` to check progress.`
|
Returns immediately with task ID and session info. Use \`background_output\` to check progress or retrieve results.`
|
||||||
|
|
||||||
export const BACKGROUND_STATUS_DESCRIPTION = `Check the status of background tasks.
|
export const BACKGROUND_OUTPUT_DESCRIPTION = `Get output from a background task.
|
||||||
|
|
||||||
If taskId is provided, returns status for that specific task.
|
|
||||||
If taskId is omitted, returns status for all tasks in the current session.
|
|
||||||
|
|
||||||
Status includes:
|
|
||||||
- Task description
|
|
||||||
- Current status (pending/running/completed/error/cancelled)
|
|
||||||
- Duration
|
|
||||||
- Number of tool calls made
|
|
||||||
- Last tool used
|
|
||||||
|
|
||||||
Arguments:
|
Arguments:
|
||||||
- taskId: Optional task ID. If omitted, lists all tasks for current session.`
|
- task_id: Required task ID to get output from
|
||||||
|
- block: If true (default), wait for task completion. If false, return current status immediately.
|
||||||
|
- timeout: Max wait time in ms when blocking (default: 60000, max: 600000)
|
||||||
|
|
||||||
export const BACKGROUND_RESULT_DESCRIPTION = `Retrieve the result of a completed background task.
|
Returns:
|
||||||
|
- When blocking: Waits for completion, then returns full result
|
||||||
|
- When not blocking: Returns current status and progress
|
||||||
|
|
||||||
Only works for tasks with status "completed". For running tasks, use \`background_status\` to check progress.
|
Use this to:
|
||||||
|
- Check task progress (block=false)
|
||||||
Returns the full assistant output from the background session, including:
|
- Wait for and retrieve task result (block=true)
|
||||||
- Task description
|
- Set custom timeout for long tasks`
|
||||||
- Duration
|
|
||||||
- Complete response content
|
|
||||||
- Session ID for reference
|
|
||||||
|
|
||||||
Arguments:
|
|
||||||
- taskId: Required task ID to retrieve result for.`
|
|
||||||
|
|
||||||
export const BACKGROUND_CANCEL_DESCRIPTION = `Cancel a running background task.
|
export const BACKGROUND_CANCEL_DESCRIPTION = `Cancel a running background task.
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
export {
|
export {
|
||||||
createBackgroundTask,
|
createBackgroundTask,
|
||||||
createBackgroundStatus,
|
createBackgroundOutput,
|
||||||
createBackgroundResult,
|
|
||||||
createBackgroundCancel,
|
createBackgroundCancel,
|
||||||
} from "./tools"
|
} from "./tools"
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { tool, type PluginInput } from "@opencode-ai/plugin"
|
import { tool, type PluginInput } from "@opencode-ai/plugin"
|
||||||
import type { BackgroundManager } from "../../features/background-agent"
|
import type { BackgroundManager, BackgroundTask } from "../../features/background-agent"
|
||||||
import type { BackgroundTaskArgs, BackgroundStatusArgs, BackgroundResultArgs, BackgroundCancelArgs } from "./types"
|
import type { BackgroundTaskArgs, BackgroundOutputArgs, BackgroundCancelArgs } from "./types"
|
||||||
import { BACKGROUND_TASK_DESCRIPTION, BACKGROUND_STATUS_DESCRIPTION, BACKGROUND_RESULT_DESCRIPTION, BACKGROUND_CANCEL_DESCRIPTION } from "./constants"
|
import { BACKGROUND_TASK_DESCRIPTION, BACKGROUND_OUTPUT_DESCRIPTION, BACKGROUND_CANCEL_DESCRIPTION } from "./constants"
|
||||||
|
|
||||||
type OpencodeClient = PluginInput["client"]
|
type OpencodeClient = PluginInput["client"]
|
||||||
|
|
||||||
@@ -38,7 +38,7 @@ export function createBackgroundTask(manager: BackgroundManager) {
|
|||||||
parentMessageID: toolContext.messageID,
|
parentMessageID: toolContext.messageID,
|
||||||
})
|
})
|
||||||
|
|
||||||
return `✅ Background task launched successfully!
|
return `Background task launched successfully.
|
||||||
|
|
||||||
Task ID: ${task.id}
|
Task ID: ${task.id}
|
||||||
Session ID: ${task.sessionID}
|
Session ID: ${task.sessionID}
|
||||||
@@ -46,8 +46,9 @@ Description: ${task.description}
|
|||||||
Agent: ${task.agent}
|
Agent: ${task.agent}
|
||||||
Status: ${task.status}
|
Status: ${task.status}
|
||||||
|
|
||||||
Use \`background_status\` tool to check progress.
|
Use \`background_output\` tool with task_id="${task.id}" to check progress or retrieve results.
|
||||||
Use \`background_result\` tool to retrieve results when complete.`
|
- block=false: Check status without waiting
|
||||||
|
- block=true (default): Wait for completion and get result`
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const message = error instanceof Error ? error.message : String(error)
|
const message = error instanceof Error ? error.message : String(error)
|
||||||
return `❌ Failed to launch background task: ${message}`
|
return `❌ Failed to launch background task: ${message}`
|
||||||
@@ -56,26 +57,17 @@ Use \`background_result\` tool to retrieve results when complete.`
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createBackgroundStatus(manager: BackgroundManager) {
|
function delay(ms: number): Promise<void> {
|
||||||
return tool({
|
return new Promise(resolve => setTimeout(resolve, ms))
|
||||||
description: BACKGROUND_STATUS_DESCRIPTION,
|
}
|
||||||
args: {
|
|
||||||
taskId: tool.schema.string().optional().describe("Task ID to check. If omitted, lists all tasks for current session."),
|
|
||||||
},
|
|
||||||
async execute(args: BackgroundStatusArgs, toolContext) {
|
|
||||||
try {
|
|
||||||
if (args.taskId) {
|
|
||||||
const task = manager.getTask(args.taskId)
|
|
||||||
if (!task) {
|
|
||||||
return `❌ Task not found: ${args.taskId}`
|
|
||||||
}
|
|
||||||
|
|
||||||
const duration = formatDuration(task.startedAt, task.completedAt)
|
function formatTaskStatus(task: BackgroundTask): string {
|
||||||
const progress = task.progress
|
const duration = formatDuration(task.startedAt, task.completedAt)
|
||||||
? `\nTool calls: ${task.progress.toolCalls}\nLast tool: ${task.progress.lastTool ?? "N/A"}`
|
const progress = task.progress
|
||||||
: ""
|
? `\nTool calls: ${task.progress.toolCalls}\nLast tool: ${task.progress.lastTool ?? "N/A"}`
|
||||||
|
: ""
|
||||||
|
|
||||||
return `📊 Task Status
|
return `Task Status
|
||||||
|
|
||||||
Task ID: ${task.id}
|
Task ID: ${task.id}
|
||||||
Description: ${task.description}
|
Description: ${task.description}
|
||||||
@@ -84,83 +76,31 @@ Status: ${task.status}
|
|||||||
Duration: ${duration}${progress}
|
Duration: ${duration}${progress}
|
||||||
|
|
||||||
Session ID: ${task.sessionID}`
|
Session ID: ${task.sessionID}`
|
||||||
} else {
|
|
||||||
const tasks = manager.getTasksByParentSession(toolContext.sessionID)
|
|
||||||
|
|
||||||
if (tasks.length === 0) {
|
|
||||||
return "No background tasks found for this session."
|
|
||||||
}
|
|
||||||
|
|
||||||
let output = `📊 Background Tasks (${tasks.length})\n\n`
|
|
||||||
|
|
||||||
for (const task of tasks) {
|
|
||||||
const duration = formatDuration(task.startedAt, task.completedAt)
|
|
||||||
const progress = task.progress ? ` | ${task.progress.toolCalls} tools` : ""
|
|
||||||
|
|
||||||
output += `• ${task.id} - ${task.status} (${duration}${progress})\n`
|
|
||||||
output += ` ${task.description}\n\n`
|
|
||||||
}
|
|
||||||
|
|
||||||
return output
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
return `❌ Error checking status: ${error instanceof Error ? error.message : String(error)}`
|
|
||||||
}
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createBackgroundResult(manager: BackgroundManager, client: OpencodeClient) {
|
async function formatTaskResult(task: BackgroundTask, client: OpencodeClient): Promise<string> {
|
||||||
return tool({
|
const messagesResult = await client.session.messages({
|
||||||
description: BACKGROUND_RESULT_DESCRIPTION,
|
path: { id: task.sessionID },
|
||||||
args: {
|
})
|
||||||
taskId: tool.schema.string().describe("Task ID to retrieve result from"),
|
|
||||||
},
|
|
||||||
async execute(args: BackgroundResultArgs) {
|
|
||||||
try {
|
|
||||||
const task = manager.getTask(args.taskId)
|
|
||||||
if (!task) {
|
|
||||||
return `❌ Task not found: ${args.taskId}`
|
|
||||||
}
|
|
||||||
|
|
||||||
if (task.status !== "completed") {
|
if (messagesResult.error) {
|
||||||
return `⏳ Task is still ${task.status}. Wait for completion.
|
return `Error fetching messages: ${messagesResult.error}`
|
||||||
|
}
|
||||||
|
|
||||||
Use \`background_status\` tool to check progress.`
|
const messages = messagesResult.data
|
||||||
}
|
const assistantMessages = messages.filter(
|
||||||
|
(m: any) => m.info?.role === "assistant"
|
||||||
|
)
|
||||||
|
|
||||||
const messagesResult = await client.session.messages({
|
const lastMessage = assistantMessages[assistantMessages.length - 1]
|
||||||
path: { id: task.sessionID },
|
const textParts = lastMessage?.parts?.filter(
|
||||||
})
|
(p: any) => p.type === "text"
|
||||||
|
) ?? []
|
||||||
|
const textContent = textParts.map((p: any) => p.text).join("\n")
|
||||||
|
|
||||||
if (messagesResult.error) {
|
const duration = formatDuration(task.startedAt, task.completedAt)
|
||||||
return `❌ Error fetching messages: ${messagesResult.error}`
|
|
||||||
}
|
|
||||||
|
|
||||||
const messages = messagesResult.data
|
return `Task Result
|
||||||
|
|
||||||
const assistantMessages = messages.filter(
|
|
||||||
(m: any) => m.info?.role === "assistant"
|
|
||||||
)
|
|
||||||
|
|
||||||
if (assistantMessages.length === 0) {
|
|
||||||
return `⚠️ Task completed but no output found.
|
|
||||||
|
|
||||||
Task ID: ${task.id}
|
|
||||||
Session ID: ${task.sessionID}`
|
|
||||||
}
|
|
||||||
|
|
||||||
const lastMessage = assistantMessages[assistantMessages.length - 1]
|
|
||||||
|
|
||||||
const textParts = lastMessage.parts?.filter(
|
|
||||||
(p: any) => p.type === "text"
|
|
||||||
) ?? []
|
|
||||||
|
|
||||||
const textContent = textParts.map((p: any) => p.text).join("\n")
|
|
||||||
|
|
||||||
const duration = formatDuration(task.startedAt, task.completedAt)
|
|
||||||
|
|
||||||
return `✅ Task Result
|
|
||||||
|
|
||||||
Task ID: ${task.id}
|
Task ID: ${task.id}
|
||||||
Description: ${task.description}
|
Description: ${task.description}
|
||||||
@@ -169,9 +109,70 @@ Session ID: ${task.sessionID}
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
${textContent}`
|
${textContent || "(No output)"}`
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createBackgroundOutput(manager: BackgroundManager, client: OpencodeClient) {
|
||||||
|
return tool({
|
||||||
|
description: BACKGROUND_OUTPUT_DESCRIPTION,
|
||||||
|
args: {
|
||||||
|
task_id: tool.schema.string().describe("Task ID to get output from"),
|
||||||
|
block: tool.schema.boolean().optional().describe("Wait for completion (default: true)"),
|
||||||
|
timeout: tool.schema.number().optional().describe("Max wait time in ms (default: 60000, max: 600000)"),
|
||||||
|
},
|
||||||
|
async execute(args: BackgroundOutputArgs) {
|
||||||
|
try {
|
||||||
|
const task = manager.getTask(args.task_id)
|
||||||
|
if (!task) {
|
||||||
|
return `Task not found: ${args.task_id}`
|
||||||
|
}
|
||||||
|
|
||||||
|
const shouldBlock = args.block !== false
|
||||||
|
const timeoutMs = Math.min(args.timeout ?? 60000, 600000)
|
||||||
|
|
||||||
|
// Non-blocking: return status immediately
|
||||||
|
if (!shouldBlock) {
|
||||||
|
return formatTaskStatus(task)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Already completed: return result immediately
|
||||||
|
if (task.status === "completed") {
|
||||||
|
return await formatTaskResult(task, client)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error or cancelled: return status immediately
|
||||||
|
if (task.status === "error" || task.status === "cancelled") {
|
||||||
|
return formatTaskStatus(task)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Blocking: poll until completion or timeout
|
||||||
|
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") {
|
||||||
|
return formatTaskStatus(currentTask)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Timeout exceeded: return current status
|
||||||
|
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)}`
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return `❌ Error retrieving result: ${error instanceof Error ? error.message : String(error)}`
|
return `Error getting output: ${error instanceof Error ? error.message : String(error)}`
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -4,12 +4,10 @@ export interface BackgroundTaskArgs {
|
|||||||
agent: string
|
agent: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface BackgroundStatusArgs {
|
export interface BackgroundOutputArgs {
|
||||||
taskId?: string
|
task_id: string
|
||||||
}
|
block?: boolean
|
||||||
|
timeout?: number
|
||||||
export interface BackgroundResultArgs {
|
|
||||||
taskId: string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface BackgroundCancelArgs {
|
export interface BackgroundCancelArgs {
|
||||||
|
|||||||
@@ -24,8 +24,7 @@ import { skill } from "./skill"
|
|||||||
|
|
||||||
import {
|
import {
|
||||||
createBackgroundTask,
|
createBackgroundTask,
|
||||||
createBackgroundStatus,
|
createBackgroundOutput,
|
||||||
createBackgroundResult,
|
|
||||||
createBackgroundCancel,
|
createBackgroundCancel,
|
||||||
} from "./background-task"
|
} from "./background-task"
|
||||||
|
|
||||||
@@ -39,8 +38,7 @@ export { createOmoTask } from "./omo-task"
|
|||||||
export function createBackgroundTools(manager: BackgroundManager, client: OpencodeClient) {
|
export function createBackgroundTools(manager: BackgroundManager, client: OpencodeClient) {
|
||||||
return {
|
return {
|
||||||
background_task: createBackgroundTask(manager),
|
background_task: createBackgroundTask(manager),
|
||||||
background_status: createBackgroundStatus(manager),
|
background_output: createBackgroundOutput(manager, client),
|
||||||
background_result: createBackgroundResult(manager, client),
|
|
||||||
background_cancel: createBackgroundCancel(manager, client),
|
background_cancel: createBackgroundCancel(manager, client),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user