feat(background-task): add 4 background task tools

🤖 GENERATED WITH ASSISTANCE OF [OhMyOpenCode](https://github.com/code-yeongyu/oh-my-opencode)
This commit is contained in:
YeonGyu-Kim
2025-12-11 16:04:27 +09:00
parent 698cdb6744
commit 2fad28d552
4 changed files with 301 additions and 0 deletions

View File

@@ -0,0 +1,51 @@
export const BACKGROUND_TASK_DESCRIPTION = `Launch a background agent task that runs asynchronously.
The task runs in a separate session while you continue with other work. The system will notify you when the task completes.
Use this for:
- Long-running research tasks
- Complex analysis that doesn't need immediate results
- Parallel workloads to maximize throughput
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.`
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
Arguments:
- taskId: Optional task ID. If omitted, lists all tasks for current session.`
export const BACKGROUND_RESULT_DESCRIPTION = `Retrieve the result of a completed background task.
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.`
export const BACKGROUND_CANCEL_DESCRIPTION = `Cancel a running background task.
Only works for tasks with status "running". Aborts the background session and marks the task as cancelled.
Arguments:
- taskId: Required task ID to cancel.`

View File

@@ -0,0 +1,9 @@
export {
createBackgroundTask,
createBackgroundStatus,
createBackgroundResult,
createBackgroundCancel,
} from "./tools"
export type * from "./types"
export * from "./constants"

View File

@@ -0,0 +1,223 @@
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"
type OpencodeClient = PluginInput["client"]
function formatDuration(start: Date, end?: Date): string {
const duration = (end ?? new Date()).getTime() - start.getTime()
const seconds = Math.floor(duration / 1000)
const minutes = Math.floor(seconds / 60)
const hours = Math.floor(minutes / 60)
if (hours > 0) {
return `${hours}h ${minutes % 60}m ${seconds % 60}s`
} else if (minutes > 0) {
return `${minutes}m ${seconds % 60}s`
} else {
return `${seconds}s`
}
}
export function createBackgroundTask(manager: BackgroundManager) {
return tool({
description: BACKGROUND_TASK_DESCRIPTION,
args: {
description: tool.schema.string().describe("Short task description (shown in status)"),
prompt: tool.schema.string().describe("Full detailed prompt for the agent"),
agent: tool.schema.string().describe("Agent type to use (any agent allowed)"),
session_id: tool.schema.string().describe("Parent session ID (auto-detected if omitted)").optional(),
},
async execute(args: BackgroundTaskArgs, toolContext) {
try {
const task = await manager.launch({
description: args.description,
prompt: args.prompt,
agent: args.agent,
parentSessionID: args.session_id ?? toolContext.sessionID,
parentMessageID: toolContext.messageID ?? "unknown",
})
return `✅ Background task launched successfully!
Task ID: ${task.id}
Session ID: ${task.sessionID}
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.`
} catch (error) {
const message = error instanceof Error ? error.message : String(error)
return `❌ Failed to launch background task: ${message}`
}
},
})
}
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}`
}
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
Task ID: ${task.id}
Description: ${task.description}
Agent: ${task.agent}
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.`
}
const messagesResult = await client.session.messages({
path: { id: task.sessionID },
})
if (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(
(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}
Description: ${task.description}
Duration: ${duration}
Session ID: ${task.sessionID}
---
${textContent}`
} catch (error) {
return `❌ Error retrieving result: ${error instanceof Error ? error.message : String(error)}`
}
},
})
}
export function createBackgroundCancel(manager: BackgroundManager, client: OpencodeClient) {
return tool({
description: BACKGROUND_CANCEL_DESCRIPTION,
args: {
taskId: tool.schema.string().describe("Task ID to cancel"),
},
async execute(args: BackgroundCancelArgs) {
try {
const task = manager.getTask(args.taskId)
if (!task) {
return `❌ Task not found: ${args.taskId}`
}
if (task.status !== "running") {
return `❌ Cannot cancel task: current status is "${task.status}".
Only running tasks can be cancelled.`
}
const abortResult = await client.session.abort({
path: { id: task.sessionID },
})
if (abortResult.error) {
return `❌ Failed to abort session: ${(abortResult.error as any).message || String(abortResult.error)}`
}
task.status = "cancelled"
task.completedAt = new Date()
await manager.persist()
return `✅ Task cancelled successfully
Task ID: ${task.id}
Description: ${task.description}
Session ID: ${task.sessionID}
Status: ${task.status}`
} catch (error) {
return `❌ Error cancelling task: ${error instanceof Error ? error.message : String(error)}`
}
},
})
}

View File

@@ -0,0 +1,18 @@
export interface BackgroundTaskArgs {
description: string
prompt: string
agent: string
session_id?: string
}
export interface BackgroundStatusArgs {
taskId?: string
}
export interface BackgroundResultArgs {
taskId: string
}
export interface BackgroundCancelArgs {
taskId: string
}