refactor(athena): remove type assertions and improve agent factories
- Replace 'as AgentConfig' casts with proper typing in agent.ts and council-member-agent.ts - Extract permission into typed variable following Sisyphus pattern - Add GPT/non-GPT model branching to council-member-agent - Use parseModelString for schema validation instead of inline logic - Add strict() to council and athena config schemas - Fix athena restriction list (remove redundant athena_council deny) - Add orchestrator logging for council execution - Update system prompt to notification-based workflow Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
parent
c8af90715a
commit
f04b73fae3
@ -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="<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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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<CouncilLaunchResult> {
|
||||
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<Coun
|
||||
})
|
||||
})
|
||||
|
||||
log("[athena-council] Council execution complete", { launched: launched.length, failures: failures.length })
|
||||
|
||||
return {
|
||||
question,
|
||||
launched,
|
||||
|
||||
@ -4,4 +4,4 @@ export * from "./council-member-agent"
|
||||
export * from "./model-parser"
|
||||
export * from "./council-prompt"
|
||||
export * from "./council-orchestrator"
|
||||
export * from "../../config/schema/athena"
|
||||
|
||||
|
||||
@ -1,14 +1,12 @@
|
||||
import { z } from "zod"
|
||||
import { parseModelString } from "../../agents/athena/model-parser"
|
||||
|
||||
/** Validates model string format: "provider/model-id" (e.g., "openai/gpt-5.3-codex"). */
|
||||
const ModelStringSchema = z
|
||||
.string()
|
||||
.min(1)
|
||||
.refine(
|
||||
(model) => {
|
||||
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()
|
||||
|
||||
@ -14,7 +14,7 @@ const EXPLORATION_AGENT_DENYLIST: Record<string, boolean> = {
|
||||
}
|
||||
|
||||
const ATHENA_RESTRICTIONS = permissionToToolBooleans(
|
||||
createAgentToolRestrictions(["write", "edit", "athena_council"]).permission
|
||||
createAgentToolRestrictions(["write", "edit"]).permission
|
||||
)
|
||||
|
||||
const AGENT_RESTRICTIONS: Record<string, Record<string, boolean>> = {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user