feat(athena): use Question tool TUI for council member selection with dynamic member list
This commit is contained in:
parent
a43d2bd98f
commit
9a69478d8e
@ -32,19 +32,27 @@ 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.
|
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
|
## CRITICAL: Council Member Selection (Your First Action)
|
||||||
|
|
||||||
Before calling athena_council, you MUST ask the user which council members to consult. Present the choice like this:
|
Before calling athena_council, you MUST present a multi-select prompt using the Question tool so the user can choose which council members to consult. The athena_council tool description lists all available members.
|
||||||
|
|
||||||
"I'll consult the council on this. Which members should I ask?
|
Use the Question tool like this:
|
||||||
1. **All members** (default) — consult everyone
|
|
||||||
2. **Select specific members** — choose from: [list member names from config]
|
|
||||||
|
|
||||||
Reply with member names, numbers, or just say 'all'."
|
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 the athena_council tool description's "Available council members" list
|
||||||
|
],
|
||||||
|
multiple: true
|
||||||
|
}]
|
||||||
|
})
|
||||||
|
|
||||||
**Shortcut:** If the user already specified models in their message (e.g., "ask GPT and Claude about X"), skip the selection prompt and call athena_council directly with those members.
|
**Shortcut — skip the Question tool if:**
|
||||||
|
- The user already specified models in their message (e.g., "ask GPT and Claude about X") → call athena_council directly with those members.
|
||||||
**Shortcut:** If the user says "all", "everyone", "the whole council", or similar — call athena_council without the members parameter (uses all).
|
- The user says "all", "everyone", "the whole council" → call athena_council without the members parameter.
|
||||||
|
|
||||||
DO NOT:
|
DO NOT:
|
||||||
- Read files yourself
|
- Read files yourself
|
||||||
@ -57,7 +65,7 @@ You are an ORCHESTRATOR, not an analyst. Your council members do the analysis. Y
|
|||||||
|
|
||||||
## Workflow
|
## Workflow
|
||||||
|
|
||||||
Step 1: Ask the user which council members to consult (see above). Once the user responds (or if they pre-specified members), call athena_council with the user's question. Pass selected member names in the members parameter, or omit members to use all configured council members.
|
Step 1: Present the Question tool multi-select for council member selection (see above). Once the user responds, call athena_council with the user's question. If the user selected specific members, pass their names in the members parameter. If the user selected "All Members", omit the members parameter.
|
||||||
|
|
||||||
Step 2: After athena_council returns, synthesize all council member responses:
|
Step 2: After athena_council returns, synthesize all council member responses:
|
||||||
- Group findings by agreement level: unanimous, majority, minority, solo
|
- Group findings by agreement level: unanimous, majority, minority, solo
|
||||||
@ -77,7 +85,7 @@ When the user confirms planning, output:
|
|||||||
3. Tell the user: "Switch to Prometheus to start planning. It will see this conversation and can ask you questions."
|
3. Tell the user: "Switch to Prometheus to start planning. It will see this conversation and can ask you questions."
|
||||||
|
|
||||||
## Constraints
|
## Constraints
|
||||||
- Ask which members to consult BEFORE calling athena_council (unless user pre-specified).
|
- Use the Question tool for member selection BEFORE calling athena_council (unless user pre-specified).
|
||||||
- Do NOT write or edit files directly.
|
- Do NOT write or edit files directly.
|
||||||
- Do NOT delegate without explicit user confirmation.
|
- Do NOT delegate without explicit user confirmation.
|
||||||
- Do NOT ignore solo finding false-positive warnings.
|
- Do NOT ignore solo finding false-positive warnings.
|
||||||
@ -93,7 +101,7 @@ export function createAthenaAgent(model: string): AgentConfig {
|
|||||||
mode: MODE,
|
mode: MODE,
|
||||||
model,
|
model,
|
||||||
temperature: 0.1,
|
temperature: 0.1,
|
||||||
...restrictions,
|
permission: { ...restrictions.permission, question: "allow" as const },
|
||||||
prompt: ATHENA_SYSTEM_PROMPT,
|
prompt: ATHENA_SYSTEM_PROMPT,
|
||||||
color: "#1F8EFA",
|
color: "#1F8EFA",
|
||||||
} as AgentConfig
|
} as AgentConfig
|
||||||
|
|||||||
@ -1,8 +1,8 @@
|
|||||||
export const ATHENA_COUNCIL_TOOL_DESCRIPTION = `Execute Athena's multi-model council. Sends the question to all configured council members in parallel and returns their collected responses.
|
export const ATHENA_COUNCIL_TOOL_DESCRIPTION_TEMPLATE = `Execute Athena's multi-model council. Sends the question to all configured council members in parallel and returns their collected responses.
|
||||||
|
|
||||||
Optionally pass a members array of member names or model IDs to consult only specific council members. If omitted, all configured members are consulted.
|
Optionally pass a members array of member names or model IDs to consult only specific council members. If omitted, all configured members are consulted.
|
||||||
|
|
||||||
This tool reads council member configuration from the plugin config (agents.athena.council.members). Each member runs as an independent background agent with their configured model, variant, and temperature.
|
{members}
|
||||||
|
|
||||||
Returns council member responses with status, response text, and timing. Use this output for synthesis.
|
Returns council member responses with status, response text, and timing. Use this output for synthesis.
|
||||||
|
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
import { describe, expect, test } from "bun:test"
|
import { describe, expect, test } from "bun:test"
|
||||||
import type { BackgroundManager } from "../../features/background-agent"
|
import type { BackgroundManager } from "../../features/background-agent"
|
||||||
import { ATHENA_COUNCIL_TOOL_DESCRIPTION } from "./constants"
|
import { ATHENA_COUNCIL_TOOL_DESCRIPTION_TEMPLATE } from "./constants"
|
||||||
import { createAthenaCouncilTool, filterCouncilMembers } from "./tools"
|
import { createAthenaCouncilTool, filterCouncilMembers } from "./tools"
|
||||||
|
|
||||||
const mockManager = {
|
const mockManager = {
|
||||||
@ -127,8 +127,9 @@ describe("createAthenaCouncilTool", () => {
|
|||||||
councilConfig: { members: [{ model: "openai/gpt-5.3-codex" }] },
|
councilConfig: { members: [{ model: "openai/gpt-5.3-codex" }] },
|
||||||
})
|
})
|
||||||
|
|
||||||
// #then
|
// #then - description should be dynamic and include the member model
|
||||||
expect(athenaCouncilTool.description).toBe(ATHENA_COUNCIL_TOOL_DESCRIPTION)
|
expect(athenaCouncilTool.description).toContain("openai/gpt-5.3-codex")
|
||||||
|
expect(athenaCouncilTool.description).toContain("Available council members:")
|
||||||
expect((athenaCouncilTool as { args: Record<string, unknown> }).args.question).toBeDefined()
|
expect((athenaCouncilTool as { args: Record<string, unknown> }).args.question).toBeDefined()
|
||||||
expect((athenaCouncilTool as { args: Record<string, unknown> }).args.members).toBeDefined()
|
expect((athenaCouncilTool as { args: Record<string, unknown> }).args.members).toBeDefined()
|
||||||
})
|
})
|
||||||
|
|||||||
@ -2,7 +2,7 @@ import { tool, type ToolDefinition } from "@opencode-ai/plugin"
|
|||||||
import { executeCouncil } from "../../agents/athena/council-orchestrator"
|
import { executeCouncil } from "../../agents/athena/council-orchestrator"
|
||||||
import type { CouncilConfig, CouncilMemberConfig, CouncilMemberResponse } from "../../agents/athena/types"
|
import type { CouncilConfig, CouncilMemberConfig, CouncilMemberResponse } from "../../agents/athena/types"
|
||||||
import type { BackgroundManager, BackgroundTask, BackgroundTaskStatus } from "../../features/background-agent"
|
import type { BackgroundManager, BackgroundTask, BackgroundTaskStatus } from "../../features/background-agent"
|
||||||
import { ATHENA_COUNCIL_TOOL_DESCRIPTION } from "./constants"
|
import { ATHENA_COUNCIL_TOOL_DESCRIPTION_TEMPLATE } from "./constants"
|
||||||
import { createCouncilLauncher } from "./council-launcher"
|
import { createCouncilLauncher } from "./council-launcher"
|
||||||
import type { AthenaCouncilToolArgs } from "./types"
|
import type { AthenaCouncilToolArgs } from "./types"
|
||||||
|
|
||||||
@ -152,14 +152,23 @@ export function filterCouncilMembers(
|
|||||||
return { members: filteredMembers }
|
return { members: filteredMembers }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function buildToolDescription(councilConfig: CouncilConfig | undefined): string {
|
||||||
|
const memberList = councilConfig?.members.length
|
||||||
|
? councilConfig.members.map((m) => `- ${m.name ?? m.model}`).join("\n")
|
||||||
|
: "No members configured."
|
||||||
|
|
||||||
|
return ATHENA_COUNCIL_TOOL_DESCRIPTION_TEMPLATE.replace("{members}", `Available council members:\n${memberList}`)
|
||||||
|
}
|
||||||
|
|
||||||
export function createAthenaCouncilTool(args: {
|
export function createAthenaCouncilTool(args: {
|
||||||
backgroundManager: BackgroundManager
|
backgroundManager: BackgroundManager
|
||||||
councilConfig: CouncilConfig | undefined
|
councilConfig: CouncilConfig | undefined
|
||||||
}): ToolDefinition {
|
}): ToolDefinition {
|
||||||
const { backgroundManager, councilConfig } = args
|
const { backgroundManager, councilConfig } = args
|
||||||
|
const description = buildToolDescription(councilConfig)
|
||||||
|
|
||||||
return tool({
|
return tool({
|
||||||
description: ATHENA_COUNCIL_TOOL_DESCRIPTION,
|
description,
|
||||||
args: {
|
args: {
|
||||||
question: tool.schema.string().describe("The question to send to all council members"),
|
question: tool.schema.string().describe("The question to send to all council members"),
|
||||||
members: tool.schema
|
members: tool.schema
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user