diff --git a/src/agents/athena/agent.ts b/src/agents/athena/agent.ts index 47d10763..d9145682 100644 --- a/src/agents/athena/agent.ts +++ b/src/agents/athena/agent.ts @@ -78,10 +78,13 @@ Step 3: Call athena_council ONCE PER MEMBER (member per tool call): - Launch all selected members first (one athena_council call per member) so they run in parallel. - Track every returned task_id and member mapping. -Step 4: Collect all member outputs after launch: -- For each tracked task_id, call background_output with block=true. -- Gather each member's final output/status. -- Do not proceed until every launched member has reached a terminal status (completed, error, cancelled, timeout). +Step 4: Wait for all member outputs (DO NOT POLL): +- After launching all members, STOP generating and end your turn. Do NOT call background_output yet. +- The system will automatically notify you when tasks complete via system-reminder messages. +- You will receive individual "[BACKGROUND TASK COMPLETED]" notifications as each member finishes. +- When all members finish, you will receive an "[ALL BACKGROUND TASKS COMPLETE]" notification listing all task IDs. +- ONLY after receiving the all-complete notification, call background_output(task_id="") ONCE per member to retrieve their final output. +- Do NOT loop or repeatedly call background_output. One call per task_id, only after the system notification. - Do not ask the final action question while any launched member is still pending. - Do not present interim synthesis from partial results. Wait for all members first. @@ -115,7 +118,7 @@ The switch_agent tool switches the active agent. After you call it, end your res ## Constraints - Use the Question tool for member selection BEFORE calling athena_council (unless user pre-specified). - Use the Question tool for action selection AFTER synthesis (unless user already stated intent). -- Use background_output (block=true) to collect member outputs after launch. +- Do NOT poll background_output in a loop. Wait for system notifications, then collect once per member. - Do NOT call athena_council with multiple members in one call. - Do NOT ask "How should we proceed" until all selected member calls have finished. - Do NOT present or summarize partial council findings while any selected member is still running. @@ -127,21 +130,26 @@ The switch_agent tool switches the active agent. After you call it, end your res export function createAthenaAgent(model: string): AgentConfig { const restrictions = createAgentToolRestrictions(["write", "edit"]) + const permission = { + ...restrictions.permission, + question: "allow", + } as AgentConfig["permission"] + const base = { description: - "Primary synthesis strategist for multi-model council outputs. Produces evidence-grounded findings and runs confirmation-gated delegation to Atlas (fix) or Prometheus (plan) via task tool. (Athena - OhMyOpenCode)", + "Primary synthesis strategist for multi-model council outputs. Produces evidence-grounded findings and runs confirmation-gated delegation to Atlas (fix) or Prometheus (plan) via switch_agent. (Athena - OhMyOpenCode)", mode: MODE, model, temperature: 0.1, - permission: { ...restrictions.permission, question: "allow" as const }, + permission, prompt: ATHENA_SYSTEM_PROMPT, color: "#1F8EFA", - } as AgentConfig - - if (isGptModel(model)) { - return { ...base, reasoningEffort: "medium" } as AgentConfig } - return { ...base, thinking: { type: "enabled", budgetTokens: 32000 } } as AgentConfig + if (isGptModel(model)) { + return { ...base, reasoningEffort: "medium" } + } + + return { ...base, thinking: { type: "enabled", budgetTokens: 32000 } } } createAthenaAgent.mode = MODE diff --git a/src/agents/athena/council-member-agent.ts b/src/agents/athena/council-member-agent.ts index 32b94c73..167c9918 100644 --- a/src/agents/athena/council-member-agent.ts +++ b/src/agents/athena/council-member-agent.ts @@ -1,5 +1,6 @@ import type { AgentConfig } from "@opencode-ai/sdk" import type { AgentMode } from "../types" +import { isGptModel } from "../types" import { createAgentToolRestrictions } from "../../shared/permission-compat" const MODE: AgentMode = "subagent" @@ -16,13 +17,20 @@ export function createCouncilMemberAgent(model: string): AgentConfig { "athena_council", ]) - return { - description: "Independent code analyst for Athena multi-model council. Read-only, evidence-based analysis. (Council Member - OhMyOpenCode)", + const base = { + description: + "Independent code analyst for Athena multi-model council. Read-only, evidence-based analysis. (Council Member - OhMyOpenCode)", mode: MODE, model, temperature: 0.1, prompt: COUNCIL_MEMBER_PROMPT, ...restrictions, - } as AgentConfig + } + + if (isGptModel(model)) { + return { ...base, reasoningEffort: "medium" } + } + + return { ...base, thinking: { type: "enabled", budgetTokens: 32000 } } } createCouncilMemberAgent.mode = MODE diff --git a/src/agents/athena/council-orchestrator.ts b/src/agents/athena/council-orchestrator.ts index ae9a5b4c..f5877d96 100644 --- a/src/agents/athena/council-orchestrator.ts +++ b/src/agents/athena/council-orchestrator.ts @@ -1,4 +1,5 @@ import type { LaunchInput, BackgroundTask } from "../../features/background-agent/types" +import { log } from "../../shared/logger" import { buildCouncilPrompt } from "./council-prompt" import { parseModelString } from "./model-parser" import type { CouncilConfig, CouncilLaunchFailure, CouncilLaunchedMember, CouncilLaunchResult, CouncilMemberConfig } from "./types" @@ -25,6 +26,9 @@ export interface CouncilExecutionInput { */ export async function executeCouncil(input: CouncilExecutionInput): Promise { const { question, council, launcher, parentSessionID, parentMessageID, parentAgent } = input + + log("[athena-council] Executing council", { memberCount: council.members.length }) + const prompt = buildCouncilPrompt(question) const launchResults = await Promise.allSettled( @@ -50,6 +54,8 @@ export async function executeCouncil(input: CouncilExecutionInput): Promise { - const slashIndex = model.indexOf("/") - return slashIndex > 0 && slashIndex < model.length - 1 - }, + (model) => parseModelString(model) !== null, { message: 'Model must be in "provider/model-id" format (e.g., "openai/gpt-5.3-codex")' } ) @@ -20,9 +18,9 @@ export const CouncilMemberSchema = z.object({ export const CouncilConfigSchema = z.object({ members: z.array(CouncilMemberSchema).min(2), -}) +}).strict() export const AthenaConfigSchema = z.object({ model: z.string().optional(), council: CouncilConfigSchema, -}) +}).strict() diff --git a/src/shared/agent-tool-restrictions.ts b/src/shared/agent-tool-restrictions.ts index 8a7ade8b..cfe58163 100644 --- a/src/shared/agent-tool-restrictions.ts +++ b/src/shared/agent-tool-restrictions.ts @@ -14,7 +14,7 @@ const EXPLORATION_AGENT_DENYLIST: Record = { } const ATHENA_RESTRICTIONS = permissionToToolBooleans( - createAgentToolRestrictions(["write", "edit", "athena_council"]).permission + createAgentToolRestrictions(["write", "edit"]).permission ) const AGENT_RESTRICTIONS: Record> = {