refactor(tools/background-task): split tools.ts into focused modules under 200 LOC
- Create modules/ directory with 6 focused modules:
- background-task.ts: task creation logic
- background-output.ts: output retrieval logic
- background-cancel.ts: cancellation logic
- formatters.ts: message formatting utilities
- message-processing.ts: message extraction utilities
- utils.ts: shared utility functions
- Reduce tools.ts from ~798 to ~30 lines (barrel pattern)
- Add new types to types.ts for module interfaces
- Update index.ts for clean re-exports
- Follow modular code architecture (200 LOC limit)
🤖 Generated with assistance of OhMyOpenCode
This commit is contained in:
@@ -0,0 +1,105 @@
|
||||
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, MESSAGE_STORAGE } 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}`
|
||||
}
|
||||
},
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user