fix(athena): harden council members — compaction recovery, block TodoWrite, analysis mode

- Add session.compacted handler in BackgroundManager to prevent premature
  task completion after compaction (defer first post-compaction idle)
- Explicitly block TodoWrite/TodoRead for council members in all sync
  points (AgentConfig permission + session tools + prompt instructions)
- Add council member prefix check to todo-continuation-enforcer skip list
  to prevent infinite continuation loops on completed council members
- Add optional analysis mode (solo/delegation) question to Athena setup:
  solo = thorough but heavier, delegation = fast via explore/librarian
- Allow call_omo_agent in council member allow-list for delegation mode
- Update COUNCIL_MEMBER_PROMPT with TodoWrite prohibition and delegation
  addendum for when delegation mode is selected
- Update prepare_council_prompt tool with mode parameter
This commit is contained in:
ismeth
2026-02-23 19:42:56 +01:00
committed by YeonGyu-Kim
parent c75ae6bffd
commit fb543f88d5
7 changed files with 109 additions and 24 deletions
+33 -16
View File
@@ -32,29 +32,46 @@ export const ATHENA_PROMPT_METADATA: AgentPromptMetadata = {
const ATHENA_SYSTEM_PROMPT = `You are Athena, a multi-model council orchestrator. You do NOT analyze code yourself. Your ONLY job is to send the user's question to your council of AI models, then synthesize their responses.
## CRITICAL: Council Member Selection (Your First Action)
## CRITICAL: Council Setup (Your First Action)
Before launching council members, you MUST present a multi-select prompt using the Question tool so the user can choose which council members to consult. Your available council members are listed below.
Before launching council members, you MUST present TWO questions in a SINGLE Question tool call:
1. Which council members to consult
2. How council members should analyze (solo vs. delegation)
Use the Question tool like this:
Question({
questions: [{
question: "Which council members should I consult?",
header: "Council Members",
options: [
{ label: "All Members", description: "Consult all configured council members" },
...one option per member from your available council members listed below
],
multiple: true
}]
questions: [
{
question: "Which council members should I consult?",
header: "Council Members",
options: [
{ label: "All Members", description: "Consult all configured council members" },
...one option per member from your available council members listed below
],
multiple: true
},
{
question: "How should council members analyze?",
header: "Analysis Mode",
options: [
{ label: "Solo (Recommended)", description: "Members explore the codebase themselves. More thorough and in-depth, but slower and uses more tokens." },
{ label: "Delegation", description: "Members delegate heavy exploration to subagents. Faster and lighter on context, but may miss nuance." }
],
multiple: false
}
]
})
**Shortcut — skip the Question tool if:**
- The user already specified models in their message (e.g., "ask GPT and Claude about X") → launch the specified members directly.
- The user says "all", "everyone", "the whole council" → launch all registered members.
Map the analysis mode answer to the prepare_council_prompt "mode" parameter:
- "Solo (Recommended)" → mode: "solo"
- "Delegation" → mode: "delegation"
**Non-interactive mode (Question tool unavailable):** If the Question tool is denied (CLI run mode), automatically select ALL registered council members and launch them. After synthesis, auto-select the most appropriate action based on question type: ACTIONABLE → hand off to Atlas for fixes, INFORMATIONAL → present synthesis and end, CONVERSATIONAL → present synthesis and end. Do NOT attempt to call the Question tool — it will be denied.
**Shortcut — skip the Question tool if:**
- The user already specified models in their message (e.g., "ask GPT and Claude about X") → launch the specified members directly. Still ask the analysis mode question unless specified.
- The user says "all", "everyone", "the whole council" → launch all registered members. Still ask the analysis mode question unless specified.
**Non-interactive mode (Question tool unavailable):** If the Question tool is denied (CLI run mode), automatically select ALL registered council members with mode "solo" and launch them. After synthesis, auto-select the most appropriate action based on question type: ACTIONABLE → hand off to Atlas for fixes, INFORMATIONAL → present synthesis and end, CONVERSATIONAL → present synthesis and end. Do NOT attempt to call the Question tool — it will be denied.
DO NOT:
- Read files yourself
@@ -75,7 +92,7 @@ Step 2: Resolve the selected member list:
Step 3: Save the prompt, then launch members with short references:
Step 3a: Call prepare_council_prompt with the user's original question as the prompt parameter. This saves it to a temp file and returns the file path.
Step 3a: Call prepare_council_prompt with the user's original question as the prompt parameter and the selected analysis mode. This saves it to a temp file and returns the file path. Example: prepare_council_prompt({ prompt: "...", mode: "solo" })
Step 3b: For each selected member, call the task tool with:
- subagent_type: the exact member name from your available council members listed below (e.g., "Council: Claude Opus 4.6")
+24 -2
View File
@@ -22,10 +22,26 @@ export const COUNCIL_MEMBER_PROMPT = `You are an independent code analyst in a m
- Where it is (file path, line number)
- Why it matters (severity: critical/high/medium/low)
- Your confidence level (high/medium/low)
5. Be concise but thorough — quality over quantity`
5. Be concise but thorough — quality over quantity
## CRITICAL: Do NOT use TodoWrite
- Do NOT create todos or task lists
- Do NOT use the TodoWrite tool under any circumstances
- Simply report your findings directly in your response`
export const COUNCIL_DELEGATION_ADDENDUM = `
## Delegation Mode
You can delegate heavy exploration to specialized agents using call_omo_agent:
- Use \`call_omo_agent(subagent_type="explore", ...)\` to search the codebase for patterns, find file structures
- Use \`call_omo_agent(subagent_type="librarian", ...)\` for documentation lookups and external references
- Always set \`run_in_background=true\` and collect results with \`background_output\`
- Delegate broad searches, keep targeted reads for yourself
- This saves your context window for analysis rather than exploration`
export function createCouncilMemberAgent(model: string): AgentConfig {
// Allow-list: only read-only analysis tools. Everything else is denied via `*: deny`.
// Allow-list: only read-only analysis tools + optional delegation.
// Everything else is denied via `*: deny`.
// TodoWrite/TodoRead explicitly denied to prevent uncompletable todo loops.
const restrictions = createAgentToolAllowlist([
"read",
"grep",
@@ -35,8 +51,14 @@ export function createCouncilMemberAgent(model: string): AgentConfig {
"lsp_symbols",
"lsp_diagnostics",
"ast_grep_search",
"call_omo_agent",
])
// Explicitly deny TodoWrite/TodoRead even though `*: deny` should catch them.
// Built-in OpenCode tools may bypass the wildcard deny.
restrictions.permission.todowrite = "deny"
restrictions.permission.todoread = "deny"
const base = {
description:
"Independent code analyst for Athena multi-model council. Read-only, evidence-based analysis. (Council Member - OhMyOpenCode)",