fix: sync council-member tool restrictions across all layers, optimize athena guards

- Add switch_agent/background_wait to agent-tool-restrictions.ts (boolean format)
- Add dynamic council member name matching via COUNCIL_MEMBER_KEY_PREFIX
- Move athena question permission from hardcoded to tool-config-handler (CLI-mode aware)
- Rename appendMissingCouncilPrompt -> applyMissingCouncilGuard
- Optimize tool-execute-before: check hasPendingCouncilMembers before resolving session agent
- Add fallback_models to council-member/athena in schema.json
- Remove unused createAthenaAgent export from agents/index.ts
- Add cross-reference comments for restriction sync points
This commit is contained in:
ismeth
2026-02-21 00:19:49 +01:00
committed by YeonGyu-Kim
parent 9edb75c02f
commit 636c66ca62
7 changed files with 52 additions and 13 deletions
+17 -1
View File
@@ -15,12 +15,28 @@ describe("agent-tool-restrictions", () => {
expect(restrictions.call_omo_agent).toBe(false)
})
test("council-member restrictions include call_omo_agent", () => {
test("council-member restrictions include all denied tools", () => {
//#given
//#when
const restrictions = getAgentToolRestrictions("council-member")
//#then
expect(restrictions.call_omo_agent).toBe(false)
expect(restrictions.switch_agent).toBe(false)
expect(restrictions.background_wait).toBe(false)
})
test("#given dynamic council member name #when getAgentToolRestrictions #then returns council-member restrictions", () => {
//#given
const dynamicName = "Council: Claude Opus 4.6"
//#when
const restrictions = getAgentToolRestrictions(dynamicName)
//#then
expect(restrictions.write).toBe(false)
expect(restrictions.edit).toBe(false)
expect(restrictions.task).toBe(false)
expect(restrictions.call_omo_agent).toBe(false)
expect(restrictions.switch_agent).toBe(false)
expect(restrictions.background_wait).toBe(false)
})
test("hasAgentToolRestrictions returns true for athena", () => {
+13
View File
@@ -6,6 +6,8 @@ import { stripInvisibleAgentCharacters } from "./agent-display-names"
* true = tool allowed, false = tool denied.
*/
import { COUNCIL_MEMBER_KEY_PREFIX } from "../agents/builtin-agents/council-member-agents"
const EXPLORATION_AGENT_DENYLIST: Record<string, boolean> = {
write: false,
edit: false,
@@ -51,11 +53,18 @@ const AGENT_RESTRICTIONS: Record<string, Record<string, boolean>> = {
call_omo_agent: false,
},
// NOTE: Athena/council tool restrictions are also defined in:
// - src/agents/athena/agent.ts (AgentConfig permission format)
// - src/agents/athena/council-member-agent.ts (AgentConfig permission format)
// - src/plugin-handlers/tool-config-handler.ts (allow/deny string format)
// Keep all three in sync when modifying.
"council-member": {
write: false,
edit: false,
task: false,
call_omo_agent: false,
switch_agent: false,
background_wait: false,
},
}
@@ -63,6 +72,10 @@ export function getAgentToolRestrictions(agentName: string): Record<string, bool
// Custom/unknown agents get no restrictions (empty object), matching Claude Code's
// trust model where project-registered agents retain full tool access including bash.
const stripped = stripInvisibleAgentCharacters(agentName)
if (stripped.startsWith(COUNCIL_MEMBER_KEY_PREFIX)) {
return AGENT_RESTRICTIONS["council-member"] ?? {}
}
return AGENT_RESTRICTIONS[stripped]
?? Object.entries(AGENT_RESTRICTIONS).find(([key]) => key.toLowerCase() === stripped.toLowerCase())?.[1]
?? {}