feat(athena-junior): add athena_council bulk launch tool

- Create src/tools/athena-council/ with council-launcher, tool factory, types
- Launch all council members in parallel via BackgroundManager with writeOutputToFile: true
- Wire tool into registry with backgroundManager + councilConfig dependencies
- Grant athena-junior athena_council permission, remove task permission
- Update non-interactive prompt to use athena_council instead of task tool
- Add explicit stop-after-result instruction to prevent post-output text
- 23 tests (14 tool + 9 launcher) using real temp files, no mock.module
This commit is contained in:
ismeth
2026-03-03 11:47:37 +01:00
committed by YeonGyu-Kim
parent ffde7a6853
commit 7ffdd15b52
9 changed files with 653 additions and 12 deletions
@@ -0,0 +1,52 @@
import type { BackgroundManager } from "../../features/background-agent"
import type { BackgroundTask } from "../../features/background-agent/types"
import type { CouncilMemberConfig } from "../../config/schema/athena"
import { parseModelString } from "../delegate-task/model-string-parser"
import { COUNCIL_MEMBER_KEY_PREFIX } from "../../agents/builtin-agents/council-member-agents"
export interface CouncilLaunchContext {
parentSessionID: string
parentMessageID: string
parentAgent?: string
}
interface LaunchOutcome {
member: CouncilMemberConfig
task: BackgroundTask
}
/**
* Launches a single council member as a background task.
* The agent key follows the "Council: <name>" pattern used by council-member-agents.ts.
*/
export async function launchCouncilMember(
member: CouncilMemberConfig,
prompt: string,
manager: BackgroundManager,
context: CouncilLaunchContext,
): Promise<LaunchOutcome> {
const parsed = parseModelString(member.model)
if (!parsed) {
throw new Error(`Invalid model format: "${member.model}" (expected "provider/model-id")`)
}
const agentKey = `${COUNCIL_MEMBER_KEY_PREFIX}${member.name}`
const memberName = member.name ?? member.model
const task = await manager.launch({
description: `Council member: ${memberName}`,
prompt,
agent: agentKey,
parentSessionID: context.parentSessionID,
parentMessageID: context.parentMessageID,
parentAgent: context.parentAgent,
writeOutputToFile: true,
model: {
providerID: parsed.providerID,
modelID: parsed.modelID,
...(member.variant ? { variant: member.variant } : {}),
},
})
return { member, task }
}