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:
ismeth
2026-02-17 12:33:49 +01:00
committed by YeonGyu-Kim
parent 46c26f9ff5
commit 597a9069bb
10 changed files with 46 additions and 2 deletions

View 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

View File

@@ -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" })

View File

@@ -75,7 +75,7 @@ async function launchMember(
return launcher.launch({
description: `Council member: ${memberName}`,
prompt,
agent: "athena",
agent: "council-member",
parentSessionID,
parentMessageID,
parentAgent,

View File

@@ -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"

View File

@@ -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,

View File

@@ -104,6 +104,7 @@ export type BuiltinAgentName =
| "momus"
| "atlas"
| "athena"
| "council-member"
export type OverridableAgentName =
| "build"

View File

@@ -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

View File

@@ -16,6 +16,7 @@ export const AGENT_DISPLAY_NAMES: Record<string, string> = {
librarian: "librarian",
explore: "explore",
"multimodal-looker": "multimodal-looker",
"council-member": "council-member",
}
/**

View File

@@ -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(

View File

@@ -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> = {