fix(athena): provider-aware config + better council error messages

Use parsed.providerID/modelID in council member description instead of
raw model string (eliminates dead variable). Track skipped members with
reasons and surface them in the missing-council guard prompt so users
see why their council failed to register.
This commit is contained in:
ismeth 2026-02-20 14:40:44 +01:00 committed by YeonGyu-Kim
parent 9d0bafbe10
commit f0d0658eae
3 changed files with 32 additions and 14 deletions

View File

@ -203,8 +203,8 @@ export async function createBuiltinAgents(
result["atlas"] = atlasConfig result["atlas"] = atlasConfig
} }
if (councilConfig && councilConfig.members.length >= 2 && result["athena"]) { if (councilConfig?.members && councilConfig.members.length >= 2 && result["athena"]) {
const { agents: councilAgents, registeredKeys } = registerCouncilMemberAgents(councilConfig) const { agents: councilAgents, registeredKeys, skippedMembers } = registerCouncilMemberAgents(councilConfig)
for (const [key, config] of Object.entries(councilAgents)) { for (const [key, config] of Object.entries(councilAgents)) {
result[key] = config result[key] = config
} }
@ -217,9 +217,9 @@ export async function createBuiltinAgents(
prompt: (result["athena"].prompt ?? "") + councilTaskInstructions, prompt: (result["athena"].prompt ?? "") + councilTaskInstructions,
} }
} else { } else {
result["athena"] = appendMissingCouncilPrompt(result["athena"]) result["athena"] = appendMissingCouncilPrompt(result["athena"], skippedMembers)
} }
} else if (councilConfig && councilConfig.members.length >= 2 && !result["athena"]) { } else if (councilConfig?.members && councilConfig.members.length >= 2 && !result["athena"]) {
log("[builtin-agents] Skipping council member registration — Athena is disabled") log("[builtin-agents] Skipping council member registration — Athena is disabled")
} else if (result["athena"]) { } else if (result["athena"]) {
result["athena"] = appendMissingCouncilPrompt(result["athena"]) result["athena"] = appendMissingCouncilPrompt(result["athena"])

View File

@ -42,9 +42,16 @@ After informing the user, **end your turn**. Do NOT try to work around this by u
* Replaces Athena's prompt with a guard that tells the user to configure council members. * Replaces Athena's prompt with a guard that tells the user to configure council members.
* Used when Athena is registered but no valid council config exists. * Used when Athena is registered but no valid council config exists.
*/ */
export function appendMissingCouncilPrompt(athenaConfig: AgentConfig): AgentConfig { export function appendMissingCouncilPrompt(
return { athenaConfig: AgentConfig,
...athenaConfig, skippedMembers?: Array<{ name: string; reason: string }>,
prompt: (athenaConfig.prompt ?? "") + MISSING_COUNCIL_PROMPT, ): AgentConfig {
let prompt = (athenaConfig.prompt ?? "") + MISSING_COUNCIL_PROMPT
if (skippedMembers && skippedMembers.length > 0) {
const skipDetails = skippedMembers.map((m) => `- **${m.name}**: ${m.reason}`).join("\n")
prompt += `\n\n### Why Council Failed\n\nThe following members were skipped:\n${skipDetails}`
} }
return { ...athenaConfig, prompt }
} }

View File

@ -19,25 +19,33 @@ function getCouncilMemberAgentKey(member: CouncilMemberConfig): string {
* Each member becomes a separate agent callable via task(subagent_type="Council: <name>"). * Each member becomes a separate agent callable via task(subagent_type="Council: <name>").
* Returns a record of agent keys to configs and the list of registered keys. * Returns a record of agent keys to configs and the list of registered keys.
*/ */
type SkippedMember = { name: string; reason: string }
export function registerCouncilMemberAgents( export function registerCouncilMemberAgents(
councilConfig: CouncilConfig councilConfig: CouncilConfig
): { agents: Record<string, AgentConfig>; registeredKeys: string[] } { ): { agents: Record<string, AgentConfig>; registeredKeys: string[]; skippedMembers: SkippedMember[] } {
const agents: Record<string, AgentConfig> = {} const agents: Record<string, AgentConfig> = {}
const registeredKeys: string[] = [] const registeredKeys: string[] = []
const skippedMembers: SkippedMember[] = []
for (const member of councilConfig.members) { for (const member of councilConfig.members) {
const parsed = parseModelString(member.model) const parsed = parseModelString(member.model)
if (!parsed) { if (!parsed) {
skippedMembers.push({
name: member.name,
reason: `Invalid model format: '${member.model}' (expected 'provider/model-id')`,
})
log("[council-member-agents] Skipping member with invalid model", { model: member.model }) log("[council-member-agents] Skipping member with invalid model", { model: member.model })
continue continue
} }
const key = getCouncilMemberAgentKey(member) const key = getCouncilMemberAgentKey(member)
const config = createCouncilMemberAgent(member.model)
const description = `Council member: ${member.name} (${member.model}). Independent read-only code analyst for Athena council. (OhMyOpenCode)`
if (agents[key]) { if (agents[key]) {
skippedMembers.push({
name: member.name,
reason: `Duplicate name: '${member.name}' already registered`,
})
log("[council-member-agents] Skipping duplicate council member name", { log("[council-member-agents] Skipping duplicate council member name", {
name: member.name, name: member.name,
model: member.model, model: member.model,
@ -46,6 +54,9 @@ export function registerCouncilMemberAgents(
continue continue
} }
const config = createCouncilMemberAgent(member.model)
const description = `Council member: ${member.name} (${parsed.providerID}/${parsed.modelID}). Independent read-only code analyst for Athena council. (OhMyOpenCode)`
agents[key] = { agents[key] = {
...config, ...config,
description, description,
@ -65,8 +76,8 @@ export function registerCouncilMemberAgents(
if (registeredKeys.length < 2) { if (registeredKeys.length < 2) {
log("[council-member-agents] Fewer than 2 valid council members after model parsing — disabling council mode") log("[council-member-agents] Fewer than 2 valid council members after model parsing — disabling council mode")
return { agents: {}, registeredKeys: [] } return { agents: {}, registeredKeys: [], skippedMembers }
} }
return { agents, registeredKeys } return { agents, registeredKeys, skippedMembers }
} }