Unify dynamic fallback chains for background subagents
This commit is contained in:
committed by
YeonGyu-Kim
parent
d0cc76c7e0
commit
f4a810288f
@@ -64,4 +64,26 @@ describe("executeBackground", () => {
|
||||
expect(result).toContain("interrupt")
|
||||
expect(result).toContain("test-task-id")
|
||||
})
|
||||
|
||||
test("passes fallbackChain to background manager launch", async () => {
|
||||
//#given
|
||||
const fallbackChain = [
|
||||
{ providers: ["quotio"], model: "kimi-k2.5", variant: undefined },
|
||||
{ providers: ["openai"], model: "gpt-5.2", variant: "high" },
|
||||
]
|
||||
launchMock.mockResolvedValueOnce({
|
||||
id: "test-task-id",
|
||||
sessionID: "sub-session",
|
||||
description: "Test task",
|
||||
agent: "test-agent",
|
||||
status: "pending",
|
||||
})
|
||||
|
||||
//#when
|
||||
await executeBackground(testArgs, testContext, mockManager, mockClient, fallbackChain)
|
||||
|
||||
//#then
|
||||
const launchArgs = launchMock.mock.calls.at(-1)?.[0]
|
||||
expect(launchArgs.fallbackChain).toEqual(fallbackChain)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -2,6 +2,7 @@ import type { CallOmoAgentArgs } from "./types"
|
||||
import type { BackgroundManager } from "../../features/background-agent"
|
||||
import type { PluginInput } from "@opencode-ai/plugin"
|
||||
import { log } from "../../shared"
|
||||
import type { FallbackEntry } from "../../shared/model-requirements"
|
||||
import { resolveMessageContext } from "../../features/hook-message-injector"
|
||||
import { getSessionAgent } from "../../features/claude-code-session-state"
|
||||
import { getMessageDir } from "./message-dir"
|
||||
@@ -17,7 +18,8 @@ export async function executeBackground(
|
||||
metadata?: (input: { title?: string; metadata?: Record<string, unknown> }) => void
|
||||
},
|
||||
manager: BackgroundManager,
|
||||
client: PluginInput["client"]
|
||||
client: PluginInput["client"],
|
||||
fallbackChain?: FallbackEntry[],
|
||||
): Promise<string> {
|
||||
try {
|
||||
const messageDir = getMessageDir(toolContext.sessionID)
|
||||
@@ -48,6 +50,7 @@ export async function executeBackground(
|
||||
parentMessageID: toolContext.messageID,
|
||||
parentAgent,
|
||||
parentTools: getSessionTools(toolContext.sessionID),
|
||||
fallbackChain,
|
||||
})
|
||||
|
||||
const WAIT_FOR_SESSION_INTERVAL_MS = 50
|
||||
|
||||
@@ -99,4 +99,48 @@ describe("createCallOmoAgent", () => {
|
||||
//#then
|
||||
expect(result).not.toContain("disabled via disabled_agents")
|
||||
})
|
||||
|
||||
test("uses agent override fallback_models when launching background subagent", async () => {
|
||||
//#given
|
||||
const launch = mock(() => Promise.resolve({
|
||||
id: "task-fallback",
|
||||
sessionID: "sub-session",
|
||||
description: "Test task",
|
||||
agent: "explore",
|
||||
status: "pending",
|
||||
}))
|
||||
const managerWithLaunch = {
|
||||
launch,
|
||||
getTask: mock(() => undefined),
|
||||
} as unknown as BackgroundManager
|
||||
const toolDef = createCallOmoAgent(
|
||||
mockCtx,
|
||||
managerWithLaunch,
|
||||
[],
|
||||
{
|
||||
explore: {
|
||||
fallback_models: ["quotio/kimi-k2.5", "openai/gpt-5.2(high)"],
|
||||
},
|
||||
},
|
||||
)
|
||||
const executeFunc = toolDef.execute as Function
|
||||
|
||||
//#when
|
||||
await executeFunc(
|
||||
{
|
||||
description: "Test fallback",
|
||||
prompt: "Test prompt",
|
||||
subagent_type: "explore",
|
||||
run_in_background: true,
|
||||
},
|
||||
{ sessionID: "test", messageID: "msg", agent: "test", abort: new AbortController().signal }
|
||||
)
|
||||
|
||||
//#then
|
||||
const launchArgs = launch.mock.calls[0]?.[0]
|
||||
expect(launchArgs.fallbackChain).toEqual([
|
||||
{ providers: ["quotio"], model: "kimi-k2.5", variant: undefined },
|
||||
{ providers: ["openai"], model: "gpt-5.2", variant: "high" },
|
||||
])
|
||||
})
|
||||
})
|
||||
|
||||
@@ -2,14 +2,46 @@ import { tool, type PluginInput, type ToolDefinition } from "@opencode-ai/plugin
|
||||
import { ALLOWED_AGENTS, CALL_OMO_AGENT_DESCRIPTION } from "./constants"
|
||||
import type { AllowedAgentType, CallOmoAgentArgs, ToolContextWithMetadata } from "./types"
|
||||
import type { BackgroundManager } from "../../features/background-agent"
|
||||
import type { CategoriesConfig, AgentOverrides } from "../../config/schema"
|
||||
import type { FallbackEntry } from "../../shared/model-requirements"
|
||||
import { AGENT_MODEL_REQUIREMENTS } from "../../shared/model-requirements"
|
||||
import { getAgentConfigKey } from "../../shared/agent-display-names"
|
||||
import { normalizeFallbackModels } from "../../shared/model-resolver"
|
||||
import { buildFallbackChainFromModels } from "../../shared/fallback-chain-from-models"
|
||||
import { log } from "../../shared"
|
||||
import { executeBackground } from "./background-executor"
|
||||
import { executeSync } from "./sync-executor"
|
||||
|
||||
function resolveFallbackChainForCallOmoAgent(args: {
|
||||
subagentType: string
|
||||
agentOverrides?: AgentOverrides
|
||||
userCategories?: CategoriesConfig
|
||||
}): FallbackEntry[] | undefined {
|
||||
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)
|
||||
|
||||
const normalizedFallbackModels = normalizeFallbackModels(
|
||||
agentOverride?.fallback_models
|
||||
?? (agentOverride?.category ? userCategories?.[agentOverride.category]?.fallback_models : undefined)
|
||||
)
|
||||
const defaultProviderID = agentRequirement?.fallbackChain?.[0]?.providers?.[0] ?? "opencode"
|
||||
const configuredFallbackChain = buildFallbackChainFromModels(normalizedFallbackModels, defaultProviderID)
|
||||
|
||||
return configuredFallbackChain ?? agentRequirement?.fallbackChain
|
||||
}
|
||||
|
||||
export function createCallOmoAgent(
|
||||
ctx: PluginInput,
|
||||
backgroundManager: BackgroundManager,
|
||||
disabledAgents: string[] = []
|
||||
disabledAgents: string[] = [],
|
||||
agentOverrides?: AgentOverrides,
|
||||
userCategories?: CategoriesConfig,
|
||||
): ToolDefinition {
|
||||
const agentDescriptions = ALLOWED_AGENTS.map(
|
||||
(name) => `- ${name}: Specialized agent for ${name} tasks`
|
||||
@@ -54,7 +86,12 @@ export function createCallOmoAgent(
|
||||
if (args.session_id) {
|
||||
return `Error: session_id is not supported in background mode. Use run_in_background=false to continue an existing session.`
|
||||
}
|
||||
return await executeBackground(args, toolCtx, backgroundManager, ctx.client)
|
||||
const fallbackChain = resolveFallbackChainForCallOmoAgent({
|
||||
subagentType: args.subagent_type,
|
||||
agentOverrides,
|
||||
userCategories,
|
||||
})
|
||||
return await executeBackground(args, toolCtx, backgroundManager, ctx.client, fallbackChain)
|
||||
}
|
||||
|
||||
return await executeSync(args, toolCtx, ctx)
|
||||
|
||||
Reference in New Issue
Block a user