feat(athena): add dedicated council-member agent for multi-model council
Replace oracle as the agent for council background tasks with a purpose-built council-member agent. This avoids coupling to oracle's config/prompt and provides proper read-only tool restrictions (deny write, edit, task, athena_council). - New council-member-agent.ts with analysis-oriented system prompt - Registered in agentSources (hidden from Sisyphus delegation table) - Added to type system, Zod schemas, display names, tool restrictions - Minimal model fallback (always overridden per council member at launch) - Council orchestrator now launches members as council-member agent Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
24
src/agents/athena/council-member-agent.ts
Normal file
24
src/agents/athena/council-member-agent.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import type { AgentConfig } from "@opencode-ai/sdk"
|
||||
import type { AgentMode } from "../types"
|
||||
|
||||
const MODE: AgentMode = "subagent"
|
||||
|
||||
const COUNCIL_MEMBER_PROMPT = `You are an independent code analyst in a multi-model council. Your role is to provide thorough, evidence-based analysis of the question you receive.
|
||||
|
||||
## Instructions
|
||||
- Search the codebase using available tools (Read, Grep, Glob, LSP)
|
||||
- Report findings with evidence: file paths, line numbers, code snippets
|
||||
- For each finding, state severity (critical/high/medium/low) and confidence (high/medium/low)
|
||||
- Focus on real issues backed by evidence, not hypothetical concerns
|
||||
- Be concise but thorough — quality over quantity`
|
||||
|
||||
export function createCouncilMemberAgent(model: string): AgentConfig {
|
||||
return {
|
||||
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,
|
||||
} as AgentConfig
|
||||
}
|
||||
createCouncilMemberAgent.mode = MODE
|
||||
@@ -65,7 +65,7 @@ describe("executeCouncil", () => {
|
||||
|
||||
for (const launch of launches) {
|
||||
expect(launch.prompt).toBe(expectedPrompt)
|
||||
expect(launch.agent).toBe("athena")
|
||||
expect(launch.agent).toBe("council-member")
|
||||
}
|
||||
|
||||
expect(launches[0]?.model).toEqual({ providerID: "openai", modelID: "gpt-5.3-codex" })
|
||||
|
||||
@@ -75,7 +75,7 @@ async function launchMember(
|
||||
return launcher.launch({
|
||||
description: `Council member: ${memberName}`,
|
||||
prompt,
|
||||
agent: "athena",
|
||||
agent: "council-member",
|
||||
parentSessionID,
|
||||
parentMessageID,
|
||||
parentAgent,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
export * from "./types"
|
||||
export * from "./agent"
|
||||
export * from "./council-member-agent"
|
||||
export * from "./model-parser"
|
||||
export * from "./council-prompt"
|
||||
export * from "./council-orchestrator"
|
||||
|
||||
@@ -13,6 +13,7 @@ import { createAtlasAgent, atlasPromptMetadata } from "./atlas"
|
||||
import { createMomusAgent, momusPromptMetadata } from "./momus"
|
||||
import { createHephaestusAgent } from "./hephaestus"
|
||||
import { createAthenaAgent, ATHENA_PROMPT_METADATA } from "./athena/agent"
|
||||
import { createCouncilMemberAgent } from "./athena/council-member-agent"
|
||||
import type { AvailableCategory } from "./dynamic-agent-prompt-builder"
|
||||
import {
|
||||
fetchAvailableModels,
|
||||
@@ -40,6 +41,7 @@ const agentSources: Partial<Record<BuiltinAgentName, AgentSource>> = {
|
||||
metis: createMetisAgent,
|
||||
momus: createMomusAgent,
|
||||
athena: createAthenaAgent,
|
||||
"council-member": createCouncilMemberAgent,
|
||||
// Note: Atlas is handled specially in createBuiltinAgents()
|
||||
// because it needs OrchestratorContext, not just a model string
|
||||
atlas: createAtlasAgent as AgentFactory,
|
||||
|
||||
@@ -104,6 +104,7 @@ export type BuiltinAgentName =
|
||||
| "momus"
|
||||
| "atlas"
|
||||
| "athena"
|
||||
| "council-member"
|
||||
|
||||
export type OverridableAgentName =
|
||||
| "build"
|
||||
|
||||
@@ -12,6 +12,7 @@ export const BuiltinAgentNameSchema = z.enum([
|
||||
"momus",
|
||||
"atlas",
|
||||
"athena",
|
||||
"council-member",
|
||||
])
|
||||
|
||||
export const BuiltinSkillNameSchema = z.enum([
|
||||
@@ -38,6 +39,7 @@ export const OverridableAgentNameSchema = z.enum([
|
||||
"multimodal-looker",
|
||||
"atlas",
|
||||
"athena",
|
||||
"council-member",
|
||||
])
|
||||
|
||||
export const AgentNameSchema = BuiltinAgentNameSchema
|
||||
|
||||
@@ -16,6 +16,7 @@ export const AGENT_DISPLAY_NAMES: Record<string, string> = {
|
||||
librarian: "librarian",
|
||||
explore: "explore",
|
||||
"multimodal-looker": "multimodal-looker",
|
||||
"council-member": "council-member",
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -50,6 +50,14 @@ const AGENT_RESTRICTIONS: Record<string, Record<string, boolean>> = {
|
||||
},
|
||||
|
||||
athena: ATHENA_RESTRICTIONS,
|
||||
|
||||
"council-member": {
|
||||
write: false,
|
||||
edit: false,
|
||||
task: false,
|
||||
call_omo_agent: false,
|
||||
athena_council: false,
|
||||
},
|
||||
}
|
||||
|
||||
function permissionToToolBooleans(
|
||||
|
||||
@@ -100,6 +100,11 @@ export const AGENT_MODEL_REQUIREMENTS: Record<string, ModelRequirement> = {
|
||||
{ providers: ["google", "github-copilot", "opencode"], model: "gemini-3-pro", variant: "high" },
|
||||
],
|
||||
},
|
||||
"council-member": {
|
||||
fallbackChain: [
|
||||
{ providers: ["opencode"], model: "gpt-5-nano" },
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
export const CATEGORY_MODEL_REQUIREMENTS: Record<string, ModelRequirement> = {
|
||||
|
||||
Reference in New Issue
Block a user