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:
51
src/tools/background-task/constants.ts
Normal file
51
src/tools/background-task/constants.ts
Normal 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.`
|
||||||
9
src/tools/background-task/index.ts
Normal file
9
src/tools/background-task/index.ts
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
export {
|
||||||
|
createBackgroundTask,
|
||||||
|
createBackgroundStatus,
|
||||||
|
createBackgroundResult,
|
||||||
|
createBackgroundCancel,
|
||||||
|
} from "./tools"
|
||||||
|
|
||||||
|
export type * from "./types"
|
||||||
|
export * from "./constants"
|
||||||
223
src/tools/background-task/tools.ts
Normal file
223
src/tools/background-task/tools.ts
Normal 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)}`
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
18
src/tools/background-task/types.ts
Normal file
18
src/tools/background-task/types.ts
Normal 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
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user