Normalize agent name comparisons to handle display name keys
Hooks and tools now use getAgentConfigKey() to resolve agent names (which may be display names like 'Atlas (Plan Executor)') to lowercase config keys before comparison. - session-utils: orchestrator check uses getAgentConfigKey - atlas event-handler: boulder agent matching uses config keys - category-skill-reminder: target agent check uses config keys - todo-continuation-enforcer: skipAgents comparison normalized - subagent-resolver: resolves 'metis' -> 'Metis (Plan Consultant)' for lookup
This commit is contained in:
parent
d94a739203
commit
560d13dc70
@ -2,6 +2,7 @@ import type { PluginInput } from "@opencode-ai/plugin"
|
|||||||
import { getPlanProgress, readBoulderState } from "../../features/boulder-state"
|
import { getPlanProgress, readBoulderState } from "../../features/boulder-state"
|
||||||
import { subagentSessions } from "../../features/claude-code-session-state"
|
import { subagentSessions } from "../../features/claude-code-session-state"
|
||||||
import { log } from "../../shared/logger"
|
import { log } from "../../shared/logger"
|
||||||
|
import { getAgentConfigKey } from "../../shared/agent-display-names"
|
||||||
import { HOOK_NAME } from "./hook-name"
|
import { HOOK_NAME } from "./hook-name"
|
||||||
import { isAbortError } from "./is-abort-error"
|
import { isAbortError } from "./is-abort-error"
|
||||||
import { injectBoulderContinuation } from "./boulder-continuation-injector"
|
import { injectBoulderContinuation } from "./boulder-continuation-injector"
|
||||||
@ -88,11 +89,12 @@ export function createAtlasEventHandler(input: {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const lastAgent = await getLastAgentFromSession(sessionID, ctx.client)
|
const lastAgent = await getLastAgentFromSession(sessionID, ctx.client)
|
||||||
const requiredAgent = (boulderState.agent ?? "atlas").toLowerCase()
|
const lastAgentKey = getAgentConfigKey(lastAgent ?? "")
|
||||||
const lastAgentMatchesRequired = lastAgent === requiredAgent
|
const requiredAgent = getAgentConfigKey(boulderState.agent ?? "atlas")
|
||||||
|
const lastAgentMatchesRequired = lastAgentKey === requiredAgent
|
||||||
const boulderAgentWasNotExplicitlySet = boulderState.agent === undefined
|
const boulderAgentWasNotExplicitlySet = boulderState.agent === undefined
|
||||||
const boulderAgentDefaultsToAtlas = requiredAgent === "atlas"
|
const boulderAgentDefaultsToAtlas = requiredAgent === "atlas"
|
||||||
const lastAgentIsSisyphus = lastAgent === "sisyphus"
|
const lastAgentIsSisyphus = lastAgentKey === "sisyphus"
|
||||||
const allowSisyphusWhenDefaultAtlas = boulderAgentWasNotExplicitlySet && boulderAgentDefaultsToAtlas && lastAgentIsSisyphus
|
const allowSisyphusWhenDefaultAtlas = boulderAgentWasNotExplicitlySet && boulderAgentDefaultsToAtlas && lastAgentIsSisyphus
|
||||||
const agentMatches = lastAgentMatchesRequired || allowSisyphusWhenDefaultAtlas
|
const agentMatches = lastAgentMatchesRequired || allowSisyphusWhenDefaultAtlas
|
||||||
if (!agentMatches) {
|
if (!agentMatches) {
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import type { PluginInput } from "@opencode-ai/plugin"
|
|||||||
import type { AvailableSkill } from "../../agents/dynamic-agent-prompt-builder"
|
import type { AvailableSkill } from "../../agents/dynamic-agent-prompt-builder"
|
||||||
import { getSessionAgent } from "../../features/claude-code-session-state"
|
import { getSessionAgent } from "../../features/claude-code-session-state"
|
||||||
import { log } from "../../shared"
|
import { log } from "../../shared"
|
||||||
|
import { getAgentConfigKey } from "../../shared/agent-display-names"
|
||||||
import { buildReminderMessage } from "./formatter"
|
import { buildReminderMessage } from "./formatter"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -75,11 +76,11 @@ export function createCategorySkillReminderHook(
|
|||||||
function isTargetAgent(sessionID: string, inputAgent?: string): boolean {
|
function isTargetAgent(sessionID: string, inputAgent?: string): boolean {
|
||||||
const agent = getSessionAgent(sessionID) ?? inputAgent
|
const agent = getSessionAgent(sessionID) ?? inputAgent
|
||||||
if (!agent) return false
|
if (!agent) return false
|
||||||
const agentLower = agent.toLowerCase()
|
const agentKey = getAgentConfigKey(agent)
|
||||||
return (
|
return (
|
||||||
TARGET_AGENTS.has(agentLower) ||
|
TARGET_AGENTS.has(agentKey) ||
|
||||||
agentLower.includes("sisyphus") ||
|
agentKey.includes("sisyphus") ||
|
||||||
agentLower.includes("atlas")
|
agentKey.includes("atlas")
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -9,6 +9,7 @@ import {
|
|||||||
} from "../../features/hook-message-injector"
|
} from "../../features/hook-message-injector"
|
||||||
import { log } from "../../shared/logger"
|
import { log } from "../../shared/logger"
|
||||||
import { isSqliteBackend } from "../../shared/opencode-storage-detection"
|
import { isSqliteBackend } from "../../shared/opencode-storage-detection"
|
||||||
|
import { getAgentConfigKey } from "../../shared/agent-display-names"
|
||||||
|
|
||||||
import {
|
import {
|
||||||
CONTINUATION_PROMPT,
|
CONTINUATION_PROMPT,
|
||||||
@ -103,7 +104,7 @@ export async function injectContinuation(args: {
|
|||||||
tools = tools ?? previousMessage?.tools
|
tools = tools ?? previousMessage?.tools
|
||||||
}
|
}
|
||||||
|
|
||||||
if (agentName && skipAgents.includes(agentName)) {
|
if (agentName && skipAgents.some(s => getAgentConfigKey(s) === getAgentConfigKey(agentName))) {
|
||||||
log(`[${HOOK_NAME}] Skipped: agent in skipAgents list`, { sessionID, agent: agentName })
|
log(`[${HOOK_NAME}] Skipped: agent in skipAgents list`, { sessionID, agent: agentName })
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import type { BackgroundManager } from "../../features/background-agent"
|
|||||||
import type { ToolPermission } from "../../features/hook-message-injector"
|
import type { ToolPermission } from "../../features/hook-message-injector"
|
||||||
import { normalizeSDKResponse } from "../../shared"
|
import { normalizeSDKResponse } from "../../shared"
|
||||||
import { log } from "../../shared/logger"
|
import { log } from "../../shared/logger"
|
||||||
|
import { getAgentConfigKey } from "../../shared/agent-display-names"
|
||||||
|
|
||||||
import {
|
import {
|
||||||
ABORT_WINDOW_MS,
|
ABORT_WINDOW_MS,
|
||||||
@ -162,8 +163,9 @@ export async function handleSessionIdle(args: {
|
|||||||
|
|
||||||
log(`[${HOOK_NAME}] Agent check`, { sessionID, agentName: resolvedInfo?.agent, skipAgents, hasCompactionMessage })
|
log(`[${HOOK_NAME}] Agent check`, { sessionID, agentName: resolvedInfo?.agent, skipAgents, hasCompactionMessage })
|
||||||
|
|
||||||
if (resolvedInfo?.agent && skipAgents.includes(resolvedInfo.agent)) {
|
const resolvedAgentName = resolvedInfo?.agent
|
||||||
log(`[${HOOK_NAME}] Skipped: agent in skipAgents list`, { sessionID, agent: resolvedInfo.agent })
|
if (resolvedAgentName && skipAgents.some(s => getAgentConfigKey(s) === getAgentConfigKey(resolvedAgentName))) {
|
||||||
|
log(`[${HOOK_NAME}] Skipped: agent in skipAgents list`, { sessionID, agent: resolvedAgentName })
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (hasCompactionMessage && !resolvedInfo?.agent) {
|
if (hasCompactionMessage && !resolvedInfo?.agent) {
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import { findNearestMessageWithFields, findNearestMessageWithFieldsFromSDK } fro
|
|||||||
import { getMessageDir } from "./opencode-message-dir"
|
import { getMessageDir } from "./opencode-message-dir"
|
||||||
import { isSqliteBackend } from "./opencode-storage-detection"
|
import { isSqliteBackend } from "./opencode-storage-detection"
|
||||||
import { log } from "./logger"
|
import { log } from "./logger"
|
||||||
|
import { getAgentConfigKey } from "./agent-display-names"
|
||||||
import type { PluginInput } from "@opencode-ai/plugin"
|
import type { PluginInput } from "@opencode-ai/plugin"
|
||||||
|
|
||||||
export async function isCallerOrchestrator(sessionID?: string, client?: PluginInput["client"]): Promise<boolean> {
|
export async function isCallerOrchestrator(sessionID?: string, client?: PluginInput["client"]): Promise<boolean> {
|
||||||
@ -10,7 +11,7 @@ export async function isCallerOrchestrator(sessionID?: string, client?: PluginIn
|
|||||||
if (isSqliteBackend() && client) {
|
if (isSqliteBackend() && client) {
|
||||||
try {
|
try {
|
||||||
const nearest = await findNearestMessageWithFieldsFromSDK(client, sessionID)
|
const nearest = await findNearestMessageWithFieldsFromSDK(client, sessionID)
|
||||||
return nearest?.agent?.toLowerCase() === "atlas"
|
return getAgentConfigKey(nearest?.agent ?? "") === "atlas"
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
log("[session-utils] SDK orchestrator check failed", { sessionID, error: String(error) })
|
log("[session-utils] SDK orchestrator check failed", { sessionID, error: String(error) })
|
||||||
return false
|
return false
|
||||||
@ -20,5 +21,5 @@ export async function isCallerOrchestrator(sessionID?: string, client?: PluginIn
|
|||||||
const messageDir = getMessageDir(sessionID)
|
const messageDir = getMessageDir(sessionID)
|
||||||
if (!messageDir) return false
|
if (!messageDir) return false
|
||||||
const nearest = findNearestMessageWithFields(messageDir)
|
const nearest = findNearestMessageWithFields(messageDir)
|
||||||
return nearest?.agent?.toLowerCase() === "atlas"
|
return getAgentConfigKey(nearest?.agent ?? "") === "atlas"
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import { isPlanFamily } from "./constants"
|
|||||||
import { SISYPHUS_JUNIOR_AGENT } from "./sisyphus-junior-agent"
|
import { SISYPHUS_JUNIOR_AGENT } from "./sisyphus-junior-agent"
|
||||||
import { parseModelString } from "./model-string-parser"
|
import { parseModelString } from "./model-string-parser"
|
||||||
import { AGENT_MODEL_REQUIREMENTS } from "../../shared/model-requirements"
|
import { AGENT_MODEL_REQUIREMENTS } from "../../shared/model-requirements"
|
||||||
|
import { getAgentDisplayName, getAgentConfigKey } from "../../shared/agent-display-names"
|
||||||
import { normalizeSDKResponse } from "../../shared"
|
import { normalizeSDKResponse } from "../../shared"
|
||||||
import { getAvailableModelsForDelegateTask } from "./available-models"
|
import { getAvailableModelsForDelegateTask } from "./available-models"
|
||||||
import { resolveModelForDelegateTask } from "./model-selection"
|
import { resolveModelForDelegateTask } from "./model-selection"
|
||||||
@ -54,13 +55,16 @@ Create the work plan directly - that's your job as the planning agent.`,
|
|||||||
|
|
||||||
const callableAgents = agents.filter((a) => a.mode !== "primary")
|
const callableAgents = agents.filter((a) => a.mode !== "primary")
|
||||||
|
|
||||||
|
const resolvedDisplayName = getAgentDisplayName(agentToUse)
|
||||||
const matchedAgent = callableAgents.find(
|
const matchedAgent = callableAgents.find(
|
||||||
(agent) => agent.name.toLowerCase() === agentToUse.toLowerCase()
|
(agent) => agent.name.toLowerCase() === agentToUse.toLowerCase()
|
||||||
|
|| agent.name.toLowerCase() === resolvedDisplayName.toLowerCase()
|
||||||
)
|
)
|
||||||
if (!matchedAgent) {
|
if (!matchedAgent) {
|
||||||
const isPrimaryAgent = agents
|
const isPrimaryAgent = agents
|
||||||
.filter((a) => a.mode === "primary")
|
.filter((a) => a.mode === "primary")
|
||||||
.find((agent) => agent.name.toLowerCase() === agentToUse.toLowerCase())
|
.find((agent) => agent.name.toLowerCase() === agentToUse.toLowerCase()
|
||||||
|
|| agent.name.toLowerCase() === resolvedDisplayName.toLowerCase())
|
||||||
|
|
||||||
if (isPrimaryAgent) {
|
if (isPrimaryAgent) {
|
||||||
return {
|
return {
|
||||||
@ -83,10 +87,10 @@ Create the work plan directly - that's your job as the planning agent.`,
|
|||||||
|
|
||||||
agentToUse = matchedAgent.name
|
agentToUse = matchedAgent.name
|
||||||
|
|
||||||
const agentNameLower = agentToUse.toLowerCase()
|
const agentConfigKey = getAgentConfigKey(agentToUse)
|
||||||
const agentOverride = agentOverrides?.[agentNameLower as keyof typeof agentOverrides]
|
const agentOverride = agentOverrides?.[agentConfigKey as keyof typeof agentOverrides]
|
||||||
?? (agentOverrides ? Object.entries(agentOverrides).find(([key]) => key.toLowerCase() === agentNameLower)?.[1] : undefined)
|
?? (agentOverrides ? Object.entries(agentOverrides).find(([key]) => key.toLowerCase() === agentConfigKey)?.[1] : undefined)
|
||||||
const agentRequirement = AGENT_MODEL_REQUIREMENTS[agentNameLower]
|
const agentRequirement = AGENT_MODEL_REQUIREMENTS[agentConfigKey]
|
||||||
|
|
||||||
if (agentOverride?.model || agentRequirement || matchedAgent.model) {
|
if (agentOverride?.model || agentRequirement || matchedAgent.model) {
|
||||||
const availableModels = await getAvailableModelsForDelegateTask(client)
|
const availableModels = await getAvailableModelsForDelegateTask(client)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user