2026-02-01 16:47:50 +09:00
import { tool , type ToolDefinition } from "@opencode-ai/plugin"
2026-04-18 01:52:12 +09:00
import type { DelegatedModelConfig , ToolContextWithMetadata , DelegateTaskToolOptions } from "./types"
2026-02-08 18:03:15 +09:00
import { log } from "../../shared/logger"
2026-02-01 16:47:50 +09:00
import { buildSystemContent } from "./prompt-builder"
import {
resolveSkillContent ,
resolveParentContext ,
executeBackgroundContinuation ,
executeSyncContinuation ,
resolveCategoryExecution ,
resolveSubagentExecution ,
executeUnstableAgentTask ,
executeBackgroundTask ,
executeSyncTask ,
} from "./executor"
2026-04-18 01:52:12 +09:00
import { prepareDelegateTaskArgs } from "./tool-argument-preparation"
import { createDelegateTaskPresentation } from "./tool-description"
2026-02-01 16:47:50 +09:00
export { resolveCategoryConfig } from "./categories"
export type { SyncSessionCreatedEvent , DelegateTaskToolOptions , BuildSystemContentInput } from "./types"
2026-03-06 22:56:51 +09:00
export { buildSystemContent , buildTaskPrompt } from "./prompt-builder"
2026-01-09 02:24:43 +09:00
2026-04-18 01:52:12 +09:00
const delegateTaskArgsSchema = {
load_skills : tool.schema.array ( tool . schema . string ( ) ) . describe ( "Skill names to inject. REQUIRED - pass [] if no skills needed." ) ,
description : tool.schema.string ( ) . optional ( ) . describe ( "Short task description (3-5 words). Auto-generated from prompt if omitted." ) ,
prompt : tool.schema.string ( ) . describe ( "Full detailed prompt for the agent" ) ,
run_in_background : tool.schema.boolean ( ) . describe ( "REQUIRED. true=async (returns task_id), false=sync (waits). Use false for task delegation, true ONLY for parallel exploration." ) ,
category : tool.schema.string ( ) . optional ( ) . describe ( "REQUIRED if subagent_type not provided. Do NOT provide both category and subagent_type." ) ,
subagent_type : tool.schema.string ( ) . optional ( ) . describe ( "REQUIRED if category not provided. Do NOT provide both category and subagent_type." ) ,
task_id : tool.schema.string ( ) . optional ( ) . describe ( "Existing task to continue. Canonical resume identifier." ) ,
command : tool.schema.string ( ) . optional ( ) . describe ( "The command that triggered this task" ) ,
}
2026-01-22 22:46:23 +09:00
2026-04-18 01:52:12 +09:00
export function createDelegateTask ( options : DelegateTaskToolOptions ) : ToolDefinition {
const { availableCategories , availableSkills , categoryExamples , description } = createDelegateTaskPresentation ( options )
2026-01-22 22:46:23 +09:00
2026-01-09 02:24:43 +09:00
return tool ( {
2026-01-22 22:46:23 +09:00
description ,
2026-04-18 01:52:12 +09:00
args : delegateTaskArgsSchema ,
async execute ( args , toolContext ) {
2026-01-09 02:24:43 +09:00
const ctx = toolContext as ToolContextWithMetadata
2026-04-18 01:52:12 +09:00
const delegateTaskArgs = await prepareDelegateTaskArgs ( args , ctx )
2026-02-01 16:47:50 +09:00
2026-04-18 01:52:12 +09:00
const runInBackground = delegateTaskArgs . run_in_background === true
2026-01-09 02:24:43 +09:00
2026-04-18 01:52:12 +09:00
const { content : skillContent , contents : skillContents , error : skillError } = await resolveSkillContent ( delegateTaskArgs . load_skills , {
2026-02-01 16:47:50 +09:00
gitMasterConfig : options.gitMasterConfig ,
browserProvider : options.browserProvider ,
2026-02-01 23:54:32 +07:00
disabledSkills : options.disabledSkills ,
2026-02-21 17:07:29 +09:00
directory : options.directory ,
2026-02-01 16:47:50 +09:00
} )
if ( skillError ) {
return skillError
2026-01-09 02:24:43 +09:00
}
2026-02-15 15:09:59 +09:00
const parentContext = await resolveParentContext ( ctx , options . client )
2026-01-09 02:24:43 +09:00
2026-04-18 01:52:12 +09:00
if ( delegateTaskArgs . task_id ) {
2026-01-09 02:24:43 +09:00
if ( runInBackground ) {
2026-04-18 01:52:12 +09:00
return executeBackgroundContinuation ( delegateTaskArgs , ctx , options , parentContext )
2026-01-09 02:24:43 +09:00
}
2026-04-18 01:52:12 +09:00
return executeSyncContinuation ( delegateTaskArgs , ctx , options , parentContext )
2026-01-09 02:24:43 +09:00
}
2026-04-18 01:52:12 +09:00
if ( ! delegateTaskArgs . category && ! delegateTaskArgs . subagent_type ) {
2026-01-17 20:40:35 +09:00
return ` Invalid arguments: Must provide either category or subagent_type. `
2026-01-09 02:24:43 +09:00
}
2026-02-01 16:47:50 +09:00
let systemDefaultModel : string | undefined
try {
const openCodeConfig = await options . client . config . get ( )
systemDefaultModel = ( openCodeConfig as { data ? : { model? : string } } ) ? . data ? . model
} catch {
systemDefaultModel = undefined
}
2026-01-27 09:28:40 +09:00
2026-02-01 16:47:50 +09:00
const inheritedModel = parentContext . model
? ` ${ parentContext . model . providerID } / ${ parentContext . model . modelID } `
: undefined
2026-01-27 09:28:40 +09:00
2026-02-01 16:47:50 +09:00
let agentToUse : string
2026-03-18 20:37:50 +01:00
let categoryModel : DelegatedModelConfig | undefined
2026-02-01 16:47:50 +09:00
let categoryPromptAppend : string | undefined
let modelInfo : import ( "../../features/task-toast-manager/types" ) . ModelFallbackInfo | undefined
let actualModel : string | undefined
let isUnstableAgent = false
2026-02-19 04:41:00 +02:00
let fallbackChain : import ( "../../shared/model-requirements" ) . FallbackEntry [ ] | undefined
2026-02-25 14:03:47 +09:00
let maxPromptTokens : number | undefined
2026-02-01 16:47:50 +09:00
2026-04-18 01:52:12 +09:00
if ( delegateTaskArgs . category ) {
const resolution = await resolveCategoryExecution ( delegateTaskArgs , options , inheritedModel , systemDefaultModel )
2026-02-01 16:47:50 +09:00
if ( resolution . error ) {
return resolution . error
}
agentToUse = resolution . agentToUse
categoryModel = resolution . categoryModel
categoryPromptAppend = resolution . categoryPromptAppend
modelInfo = resolution . modelInfo
actualModel = resolution . actualModel
isUnstableAgent = resolution . isUnstableAgent
2026-02-19 04:41:00 +02:00
fallbackChain = resolution . fallbackChain
2026-02-25 14:03:47 +09:00
maxPromptTokens = resolution . maxPromptTokens
2026-01-14 14:45:01 -05:00
2026-04-18 01:52:12 +09:00
const isRunInBackgroundExplicitlyFalse = isExplicitSyncRun ( delegateTaskArgs . run_in_background )
2026-01-14 14:45:01 -05:00
2026-02-06 16:01:54 +09:00
log ( "[task] unstable agent detection" , {
2026-04-18 01:52:12 +09:00
category : delegateTaskArgs.category ,
2026-01-22 22:46:23 +09:00
actualModel ,
isUnstableAgent ,
2026-04-18 01:52:12 +09:00
run_in_background_value : delegateTaskArgs.run_in_background ,
run_in_background_type : typeof delegateTaskArgs . run_in_background ,
2026-01-22 22:46:23 +09:00
isRunInBackgroundExplicitlyFalse ,
willForceBackground : isUnstableAgent && isRunInBackgroundExplicitlyFalse ,
} )
2026-01-13 08:58:47 +07:00
2026-01-22 22:46:23 +09:00
if ( isUnstableAgent && isRunInBackgroundExplicitlyFalse ) {
2026-02-06 17:31:13 +09:00
const systemContent = buildSystemContent ( {
skillContent ,
2026-02-25 14:03:47 +09:00
skillContents ,
2026-02-06 17:31:13 +09:00
categoryPromptAppend ,
agentName : agentToUse ,
2026-02-25 14:03:47 +09:00
maxPromptTokens ,
model : categoryModel ,
2026-02-06 17:31:13 +09:00
availableCategories ,
availableSkills ,
} )
2026-04-18 01:52:12 +09:00
return executeUnstableAgentTask ( delegateTaskArgs , ctx , options , parentContext , agentToUse , categoryModel , systemContent , actualModel )
2026-01-20 18:48:13 +09:00
}
2026-01-09 02:24:43 +09:00
} else {
2026-04-18 01:52:12 +09:00
const resolution = await resolveSubagentExecution ( delegateTaskArgs , options , parentContext . agent , categoryExamples )
2026-02-01 16:47:50 +09:00
if ( resolution . error ) {
return resolution . error
2026-01-22 22:46:23 +09:00
}
2026-02-01 16:47:50 +09:00
agentToUse = resolution . agentToUse
categoryModel = resolution . categoryModel
2026-02-19 04:41:00 +02:00
fallbackChain = resolution . fallbackChain
2026-01-09 02:24:43 +09:00
}
2026-02-06 17:31:13 +09:00
const systemContent = buildSystemContent ( {
skillContent ,
2026-02-25 14:03:47 +09:00
skillContents ,
2026-02-06 17:31:13 +09:00
categoryPromptAppend ,
agentName : agentToUse ,
2026-02-25 14:03:47 +09:00
maxPromptTokens ,
model : categoryModel ,
2026-02-06 17:31:13 +09:00
availableCategories ,
availableSkills ,
} )
2026-01-09 02:24:43 +09:00
if ( runInBackground ) {
2026-04-18 01:52:12 +09:00
return executeBackgroundTask ( delegateTaskArgs , ctx , options , parentContext , agentToUse , categoryModel , systemContent , fallbackChain )
2026-01-09 02:24:43 +09:00
}
2026-04-18 01:52:12 +09:00
return executeSyncTask ( delegateTaskArgs , ctx , options , parentContext , agentToUse , categoryModel , systemContent , modelInfo , fallbackChain )
2026-01-09 02:24:43 +09:00
} ,
} )
}
2026-04-18 01:52:12 +09:00
function isExplicitSyncRun ( runInBackground : unknown ) : boolean {
return runInBackground === false || runInBackground === "false"
}