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:
YeonGyu-Kim
2025-12-12 10:54:24 +09:00
parent 550322cb0c
commit 01f935f074
8 changed files with 121 additions and 165 deletions

View File

@@ -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"
}
}
]

View File

@@ -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) {

View File

@@ -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) {

View File

@@ -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.

View File

@@ -1,7 +1,6 @@
export {
createBackgroundTask,
createBackgroundStatus,
createBackgroundResult,
createBackgroundOutput,
createBackgroundCancel,
} from "./tools"

View File

@@ -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)}`
}
},
})

View File

@@ -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 {

View File

@@ -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),
}
}