106 lines
4.4 KiB
TypeScript
106 lines
4.4 KiB
TypeScript
import { tool, type ToolDefinition } from "@opencode-ai/plugin"
|
|
import type { BackgroundManager } from "../../../features/background-agent"
|
|
import type { BackgroundTaskArgs } from "../types"
|
|
import { BACKGROUND_TASK_DESCRIPTION } from "../constants"
|
|
import { findNearestMessageWithFields, findFirstMessageWithAgent } from "../../../features/hook-message-injector"
|
|
import { getSessionAgent } from "../../../features/claude-code-session-state"
|
|
import { log } from "../../../shared/logger"
|
|
import { storeToolMetadata } from "../../../features/tool-metadata-store"
|
|
import { getMessageDir, delay, type ToolContextWithMetadata } from "./utils"
|
|
|
|
export function createBackgroundTask(manager: BackgroundManager): ToolDefinition {
|
|
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 registered agent)"),
|
|
},
|
|
async execute(args: BackgroundTaskArgs, toolContext) {
|
|
const ctx = toolContext as ToolContextWithMetadata
|
|
|
|
if (!args.agent || args.agent.trim() === "") {
|
|
return `[ERROR] Agent parameter is required. Please specify which agent to use (e.g., "explore", "librarian", "build", etc.)`
|
|
}
|
|
|
|
try {
|
|
const messageDir = getMessageDir(ctx.sessionID)
|
|
const prevMessage = messageDir ? findNearestMessageWithFields(messageDir) : null
|
|
const firstMessageAgent = messageDir ? findFirstMessageWithAgent(messageDir) : null
|
|
const sessionAgent = getSessionAgent(ctx.sessionID)
|
|
const parentAgent = ctx.agent ?? sessionAgent ?? firstMessageAgent ?? prevMessage?.agent
|
|
|
|
log("[background_task] parentAgent resolution", {
|
|
sessionID: ctx.sessionID,
|
|
ctxAgent: ctx.agent,
|
|
sessionAgent,
|
|
firstMessageAgent,
|
|
prevMessageAgent: prevMessage?.agent,
|
|
resolvedParentAgent: parentAgent,
|
|
})
|
|
|
|
const parentModel = prevMessage?.model?.providerID && prevMessage?.model?.modelID
|
|
? {
|
|
providerID: prevMessage.model.providerID,
|
|
modelID: prevMessage.model.modelID,
|
|
...(prevMessage.model.variant ? { variant: prevMessage.model.variant } : {})
|
|
}
|
|
: undefined
|
|
|
|
const task = await manager.launch({
|
|
description: args.description,
|
|
prompt: args.prompt,
|
|
agent: args.agent.trim(),
|
|
parentSessionID: ctx.sessionID,
|
|
parentMessageID: ctx.messageID,
|
|
parentModel,
|
|
parentAgent,
|
|
})
|
|
|
|
const WAIT_FOR_SESSION_INTERVAL_MS = 50
|
|
const WAIT_FOR_SESSION_TIMEOUT_MS = 30000
|
|
const waitStart = Date.now()
|
|
let sessionId = task.sessionID
|
|
while (!sessionId && Date.now() - waitStart < WAIT_FOR_SESSION_TIMEOUT_MS) {
|
|
if (ctx.abort?.aborted) {
|
|
await manager.cancelTask(task.id)
|
|
return `Task aborted and cancelled while waiting for session to start.\n\nTask ID: ${task.id}`
|
|
}
|
|
await delay(WAIT_FOR_SESSION_INTERVAL_MS)
|
|
const updated = manager.getTask(task.id)
|
|
if (!updated || updated.status === "error") {
|
|
return `Task ${!updated ? "was deleted" : `entered error state`}.\n\nTask ID: ${task.id}`
|
|
}
|
|
sessionId = updated?.sessionID
|
|
}
|
|
|
|
const bgMeta = {
|
|
title: args.description,
|
|
metadata: { sessionId: sessionId ?? "pending" } as Record<string, unknown>,
|
|
}
|
|
await ctx.metadata?.(bgMeta)
|
|
const callID = (ctx as any).callID as string | undefined
|
|
if (callID) {
|
|
storeToolMetadata(ctx.sessionID, callID, bgMeta)
|
|
}
|
|
|
|
return `Background task launched successfully.
|
|
|
|
Task ID: ${task.id}
|
|
Session ID: ${sessionId ?? "pending"}
|
|
Description: ${task.description}
|
|
Agent: ${task.agent}
|
|
Status: ${task.status}
|
|
|
|
The system will notify you when the task completes.
|
|
Use \`background_output\` tool with task_id="${task.id}" to check progress:
|
|
- block=false (default): Check status immediately - returns full status info
|
|
- block=true: Wait for completion (rarely needed since system notifies)`
|
|
} catch (error) {
|
|
const message = error instanceof Error ? error.message : String(error)
|
|
return `[ERROR] Failed to launch background task: ${message}`
|
|
}
|
|
},
|
|
})
|
|
}
|