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(() => {})
|
||||
}
|
||||
|
||||
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()
|
||||
if (!mainSessionID) {
|
||||
|
||||
@@ -40,7 +40,7 @@ function formatNotifications(tasks: BackgroundTask[]): string {
|
||||
}
|
||||
|
||||
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) {
|
||||
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 += `Use \`background_result\` tool to retrieve results.`
|
||||
message += `Use \`background_output\` tool to retrieve results.`
|
||||
|
||||
return message
|
||||
}
|
||||
@@ -59,14 +59,14 @@ function formatNotifications(tasks: BackgroundTask[]): string {
|
||||
const duration = formatDuration(task.startedAt, task.completedAt)
|
||||
const toolCalls = task.progress?.toolCalls ?? 0
|
||||
|
||||
return `✅ **Background Task Complete**
|
||||
return `**Background Task Complete**
|
||||
|
||||
**Task ID:** ${task.id}
|
||||
**Description:** ${task.description}
|
||||
**Duration:** ${duration}
|
||||
**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) {
|
||||
|
||||
@@ -11,37 +11,24 @@ Arguments:
|
||||
- description: Short task description (shown in status)
|
||||
- prompt: Full detailed prompt for the agent
|
||||
- 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.
|
||||
|
||||
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
|
||||
export const BACKGROUND_OUTPUT_DESCRIPTION = `Get output from a background task.
|
||||
|
||||
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.
|
||||
|
||||
Returns the full assistant output from the background session, including:
|
||||
- Task description
|
||||
- Duration
|
||||
- Complete response content
|
||||
- Session ID for reference
|
||||
|
||||
Arguments:
|
||||
- taskId: Required task ID to retrieve result for.`
|
||||
Use this to:
|
||||
- Check task progress (block=false)
|
||||
- Wait for and retrieve task result (block=true)
|
||||
- Set custom timeout for long tasks`
|
||||
|
||||
export const BACKGROUND_CANCEL_DESCRIPTION = `Cancel a running background task.
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
export {
|
||||
createBackgroundTask,
|
||||
createBackgroundStatus,
|
||||
createBackgroundResult,
|
||||
createBackgroundOutput,
|
||||
createBackgroundCancel,
|
||||
} from "./tools"
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { tool, type PluginInput } from "@opencode-ai/plugin"
|
||||
import type { BackgroundManager } from "../../features/background-agent"
|
||||
import type { BackgroundTaskArgs, BackgroundStatusArgs, BackgroundResultArgs, BackgroundCancelArgs } from "./types"
|
||||
import { BACKGROUND_TASK_DESCRIPTION, BACKGROUND_STATUS_DESCRIPTION, BACKGROUND_RESULT_DESCRIPTION, BACKGROUND_CANCEL_DESCRIPTION } from "./constants"
|
||||
import type { BackgroundManager, BackgroundTask } from "../../features/background-agent"
|
||||
import type { BackgroundTaskArgs, BackgroundOutputArgs, BackgroundCancelArgs } from "./types"
|
||||
import { BACKGROUND_TASK_DESCRIPTION, BACKGROUND_OUTPUT_DESCRIPTION, BACKGROUND_CANCEL_DESCRIPTION } from "./constants"
|
||||
|
||||
type OpencodeClient = PluginInput["client"]
|
||||
|
||||
@@ -38,7 +38,7 @@ export function createBackgroundTask(manager: BackgroundManager) {
|
||||
parentMessageID: toolContext.messageID,
|
||||
})
|
||||
|
||||
return `✅ Background task launched successfully!
|
||||
return `Background task launched successfully.
|
||||
|
||||
Task ID: ${task.id}
|
||||
Session ID: ${task.sessionID}
|
||||
@@ -46,8 +46,9 @@ Description: ${task.description}
|
||||
Agent: ${task.agent}
|
||||
Status: ${task.status}
|
||||
|
||||
Use \`background_status\` tool to check progress.
|
||||
Use \`background_result\` tool to retrieve results when complete.`
|
||||
Use \`background_output\` tool with task_id="${task.id}" to check progress or retrieve results.
|
||||
- block=false: Check status without waiting
|
||||
- block=true (default): Wait for completion and get result`
|
||||
} catch (error) {
|
||||
const message = error instanceof Error ? error.message : String(error)
|
||||
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) {
|
||||
return tool({
|
||||
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}`
|
||||
}
|
||||
function delay(ms: number): Promise<void> {
|
||||
return new Promise(resolve => setTimeout(resolve, ms))
|
||||
}
|
||||
|
||||
function formatTaskStatus(task: BackgroundTask): string {
|
||||
const duration = formatDuration(task.startedAt, task.completedAt)
|
||||
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}
|
||||
Description: ${task.description}
|
||||
@@ -84,83 +76,31 @@ Status: ${task.status}
|
||||
Duration: ${duration}${progress}
|
||||
|
||||
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) {
|
||||
return tool({
|
||||
description: BACKGROUND_RESULT_DESCRIPTION,
|
||||
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") {
|
||||
return `⏳ Task is still ${task.status}. Wait for completion.
|
||||
|
||||
Use \`background_status\` tool to check progress.`
|
||||
}
|
||||
|
||||
async function formatTaskResult(task: BackgroundTask, client: OpencodeClient): Promise<string> {
|
||||
const messagesResult = await client.session.messages({
|
||||
path: { id: task.sessionID },
|
||||
})
|
||||
|
||||
if (messagesResult.error) {
|
||||
return `❌ Error fetching messages: ${messagesResult.error}`
|
||||
return `Error fetching messages: ${messagesResult.error}`
|
||||
}
|
||||
|
||||
const messages = messagesResult.data
|
||||
|
||||
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(
|
||||
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
|
||||
return `Task Result
|
||||
|
||||
Task ID: ${task.id}
|
||||
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) {
|
||||
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
|
||||
}
|
||||
|
||||
export interface BackgroundStatusArgs {
|
||||
taskId?: string
|
||||
}
|
||||
|
||||
export interface BackgroundResultArgs {
|
||||
taskId: string
|
||||
export interface BackgroundOutputArgs {
|
||||
task_id: string
|
||||
block?: boolean
|
||||
timeout?: number
|
||||
}
|
||||
|
||||
export interface BackgroundCancelArgs {
|
||||
|
||||
@@ -24,8 +24,7 @@ import { skill } from "./skill"
|
||||
|
||||
import {
|
||||
createBackgroundTask,
|
||||
createBackgroundStatus,
|
||||
createBackgroundResult,
|
||||
createBackgroundOutput,
|
||||
createBackgroundCancel,
|
||||
} from "./background-task"
|
||||
|
||||
@@ -39,8 +38,7 @@ export { createOmoTask } from "./omo-task"
|
||||
export function createBackgroundTools(manager: BackgroundManager, client: OpencodeClient) {
|
||||
return {
|
||||
background_task: createBackgroundTask(manager),
|
||||
background_status: createBackgroundStatus(manager),
|
||||
background_result: createBackgroundResult(manager, client),
|
||||
background_output: createBackgroundOutput(manager, client),
|
||||
background_cancel: createBackgroundCancel(manager, client),
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user