2025-12-27 17:56:40 +08:00
import { tool , type PluginInput , type ToolDefinition } from "@opencode-ai/plugin"
2025-12-12 18:39:53 +09:00
import { ALLOWED_AGENTS , CALL_OMO_AGENT_DESCRIPTION } from "./constants"
2026-02-08 21:11:07 +09:00
import type { AllowedAgentType , CallOmoAgentArgs , ToolContextWithMetadata } from "./types"
2025-12-12 18:39:53 +09:00
import type { BackgroundManager } from "../../features/background-agent"
2026-03-04 20:43:55 +01:00
import type { CategoriesConfig , AgentOverrides } from "../../config/schema"
2026-03-27 13:59:06 +09:00
import type { DelegatedModelConfig } from "../../shared/model-resolution-types"
2026-03-04 20:43:55 +01:00
import type { FallbackEntry } from "../../shared/model-requirements"
import { AGENT_MODEL_REQUIREMENTS } from "../../shared/model-requirements"
import { getAgentConfigKey } from "../../shared/agent-display-names"
2026-03-27 13:59:06 +09:00
import { normalizeModelFormat } from "../../shared/model-format-normalizer"
2026-03-04 20:43:55 +01:00
import { normalizeFallbackModels } from "../../shared/model-resolver"
import { buildFallbackChainFromModels } from "../../shared/fallback-chain-from-models"
2026-02-08 16:24:13 +09:00
import { log } from "../../shared"
2026-02-08 21:11:07 +09:00
import { executeBackground } from "./background-executor"
import { executeSync } from "./sync-executor"
2025-12-28 17:00:32 +09:00
2026-03-27 13:59:06 +09:00
function resolveModelAndFallbackChain ( args : {
2026-03-04 20:43:55 +01:00
subagentType : string
agentOverrides? : AgentOverrides
userCategories? : CategoriesConfig
2026-03-27 13:59:06 +09:00
} ) : { model : DelegatedModelConfig | undefined ; fallbackChain : FallbackEntry [ ] | undefined } {
2026-03-04 20:43:55 +01:00
const { subagentType , agentOverrides , userCategories } = args
const agentConfigKey = getAgentConfigKey ( subagentType )
const agentRequirement = AGENT_MODEL_REQUIREMENTS [ agentConfigKey ]
const agentOverride = agentOverrides ? . [ agentConfigKey as keyof AgentOverrides ]
? ? ( agentOverrides
? Object . entries ( agentOverrides ) . find ( ( [ key ] ) = > key . toLowerCase ( ) === agentConfigKey ) ? . [ 1 ]
: undefined )
2026-03-31 17:03:42 -07:00
const agentCategoryModel = agentOverride ? . category
? userCategories ? . [ agentOverride . category ] ? . model
: undefined
const agentCategoryVariant = agentOverride ? . category
? userCategories ? . [ agentOverride . category ] ? . variant
: undefined
2026-03-04 20:43:55 +01:00
2026-03-27 13:59:06 +09:00
let model : DelegatedModelConfig | undefined
if ( agentOverride ? . model ) {
const normalized = normalizeModelFormat ( agentOverride . model )
if ( normalized ) {
model = agentOverride . variant ? { . . . normalized , variant : agentOverride.variant } : normalized
log ( "[call_omo_agent] Resolved model override from agent config" , {
agent : subagentType ,
model : agentOverride.model ,
variant : agentOverride.variant ,
} )
}
2026-03-31 17:03:42 -07:00
} else if ( agentCategoryModel ) {
const normalized = normalizeModelFormat ( agentCategoryModel )
if ( normalized ) {
const variantToUse = agentOverride ? . variant ? ? agentCategoryVariant
model = variantToUse ? { . . . normalized , variant : variantToUse } : normalized
log ( "[call_omo_agent] Resolved model override from agent category" , {
agent : subagentType ,
category : agentOverride?.category ,
model : agentCategoryModel ,
variant : variantToUse ,
} )
}
2026-03-27 13:59:06 +09:00
}
2026-03-04 20:43:55 +01:00
const normalizedFallbackModels = normalizeFallbackModels (
agentOverride ? . fallback_models
? ? ( agentOverride ? . category ? userCategories ? . [ agentOverride . category ] ? . fallback_models : undefined )
)
2026-03-27 13:59:06 +09:00
const defaultProviderID = model ? . providerID
? ? agentRequirement ? . fallbackChain ? . [ 0 ] ? . providers ? . [ 0 ]
? ? "opencode"
2026-03-04 20:43:55 +01:00
const configuredFallbackChain = buildFallbackChainFromModels ( normalizedFallbackModels , defaultProviderID )
2026-03-27 13:59:06 +09:00
return {
model ,
fallbackChain : configuredFallbackChain ? ? agentRequirement ? . fallbackChain ,
}
2026-03-04 20:43:55 +01:00
}
2025-12-12 18:39:53 +09:00
export function createCallOmoAgent (
ctx : PluginInput ,
2026-02-10 18:44:45 +01:00
backgroundManager : BackgroundManager ,
2026-03-04 20:43:55 +01:00
disabledAgents : string [ ] = [ ] ,
agentOverrides? : AgentOverrides ,
userCategories? : CategoriesConfig ,
2025-12-27 17:56:40 +08:00
) : ToolDefinition {
2025-12-12 18:39:53 +09:00
const agentDescriptions = ALLOWED_AGENTS . map (
( name ) = > ` - ${ name } : Specialized agent for ${ name } tasks `
) . join ( "\n" )
const description = CALL_OMO_AGENT_DESCRIPTION . replace ( "{agents}" , agentDescriptions )
return tool ( {
description ,
args : {
description : tool.schema.string ( ) . describe ( "A short (3-5 words) description of the task" ) ,
prompt : tool.schema.string ( ) . describe ( "The task for the agent to perform" ) ,
subagent_type : tool.schema
2026-01-23 00:55:01 +09:00
. string ( )
2026-02-04 11:38:24 +09:00
. describe ( "The type of specialized agent to use for this task (explore or librarian only)" ) ,
2025-12-12 18:39:53 +09:00
run_in_background : tool.schema
. boolean ( )
. describe ( "REQUIRED. true: run asynchronously (use background_output to get results), false: run synchronously and wait for completion" ) ,
session_id : tool.schema.string ( ) . describe ( "Existing Task session to continue" ) . optional ( ) ,
} ,
async execute ( args : CallOmoAgentArgs , toolContext ) {
2025-12-28 17:00:32 +09:00
const toolCtx = toolContext as ToolContextWithMetadata
2025-12-12 18:39:53 +09:00
log ( ` [call_omo_agent] Starting with agent: ${ args . subagent_type } , background: ${ args . run_in_background } ` )
2026-02-08 21:11:07 +09:00
// Case-insensitive agent validation - allows "Explore", "EXPLORE", "explore" etc.
if (
! ALLOWED_AGENTS . some (
( name ) = > name . toLowerCase ( ) === args . subagent_type . toLowerCase ( ) ,
)
) {
2025-12-12 18:39:53 +09:00
return ` Error: Invalid agent type " ${ args . subagent_type } ". Only ${ ALLOWED_AGENTS . join ( ", " ) } are allowed. `
}
2026-02-08 16:24:13 +09:00
2026-02-08 21:11:07 +09:00
const normalizedAgent = args . subagent_type . toLowerCase ( ) as AllowedAgentType
2026-01-23 00:55:01 +09:00
args = { . . . args , subagent_type : normalizedAgent }
2025-12-12 18:39:53 +09:00
2026-02-10 18:44:45 +01:00
// Check if agent is disabled
if ( disabledAgents . some ( ( disabled ) = > disabled . toLowerCase ( ) === normalizedAgent ) ) {
return ` Error: Agent " ${ normalizedAgent } " is disabled via disabled_agents configuration. Remove it from disabled_agents in your oh-my-opencode.json to use it. `
}
2026-03-27 13:59:06 +09:00
const { model : resolvedModel , fallbackChain } = resolveModelAndFallbackChain ( {
2026-03-09 13:11:03 +09:00
subagentType : args.subagent_type ,
agentOverrides ,
userCategories ,
} )
2025-12-12 18:39:53 +09:00
if ( args . run_in_background ) {
if ( args . session_id ) {
return ` Error: session_id is not supported in background mode. Use run_in_background=false to continue an existing session. `
}
2026-03-27 13:59:06 +09:00
return await executeBackground ( args , toolCtx , backgroundManager , ctx . client , fallbackChain , resolvedModel )
2025-12-12 18:39:53 +09:00
}
2026-03-11 17:46:04 +09:00
if ( ! args . session_id ) {
2026-03-11 18:44:20 +09:00
let spawnReservation : Awaited < ReturnType < BackgroundManager [ "reserveSubagentSpawn" ] > > | undefined
2026-03-11 17:46:04 +09:00
try {
2026-03-11 18:44:20 +09:00
spawnReservation = await backgroundManager . reserveSubagentSpawn ( toolCtx . sessionID )
2026-03-27 13:59:06 +09:00
return await executeSync ( args , toolCtx , ctx , undefined , fallbackChain , spawnReservation , resolvedModel )
2026-03-11 17:46:04 +09:00
} catch ( error ) {
2026-03-11 18:44:20 +09:00
spawnReservation ? . rollback ( )
2026-03-11 17:46:04 +09:00
return ` Error: ${ error instanceof Error ? error.message : String ( error ) } `
}
}
2026-03-27 13:59:06 +09:00
return await executeSync ( args , toolCtx , ctx , undefined , fallbackChain , undefined , resolvedModel )
2025-12-12 18:39:53 +09:00
} ,
} )
}