From 386645ea6be19820abcb20453181853be491db26 Mon Sep 17 00:00:00 2001 From: ismeth Date: Fri, 27 Feb 2026 16:46:23 +0100 Subject: [PATCH] refactor(athena): strip audit bias from council member prompt + add intent addendums + write boundary hook + relax restrictions --- src/agents/athena/council-intent-addendums.ts | 167 ++++++++++++++++++ src/agents/athena/council-member-agent.ts | 27 +-- .../athena-sisyphus-only/agent-matcher.ts | 5 + src/hooks/athena-sisyphus-only/constants.ts | 5 + src/hooks/athena-sisyphus-only/hook.ts | 49 +++++ src/hooks/athena-sisyphus-only/index.ts | 2 + src/hooks/athena-sisyphus-only/path-policy.ts | 30 ++++ src/shared/agent-tool-restrictions.ts | 2 - 8 files changed, 267 insertions(+), 20 deletions(-) create mode 100644 src/agents/athena/council-intent-addendums.ts create mode 100644 src/hooks/athena-sisyphus-only/agent-matcher.ts create mode 100644 src/hooks/athena-sisyphus-only/constants.ts create mode 100644 src/hooks/athena-sisyphus-only/hook.ts create mode 100644 src/hooks/athena-sisyphus-only/index.ts create mode 100644 src/hooks/athena-sisyphus-only/path-policy.ts diff --git a/src/agents/athena/council-intent-addendums.ts b/src/agents/athena/council-intent-addendums.ts new file mode 100644 index 000000000..83e767a9c --- /dev/null +++ b/src/agents/athena/council-intent-addendums.ts @@ -0,0 +1,167 @@ +export const COUNCIL_INTENT_ADDENDUMS: Record = { + AUDIT: ` +## Analysis Intent: AUDIT + +You are conducting an **audit** — your goal is to find discrete issues, risks, or violations. + +**Focus:** +- Search for problems, anti-patterns, security risks, correctness issues, or violations of stated requirements +- Each finding must be a distinct, actionable item with concrete evidence +- Severity determines priority: critical (blocks/breaks), high (significant risk), medium (should fix), low (nice to fix) +- For each finding, provide the specific location (reference, section, or component where it occurs) +- State your confidence: high (clear evidence), medium (likely but needs verification), low (suspicion, investigate further) + +**Required output fields per finding:** +- Title, Severity (critical/high/medium/low), Location, Confidence (high/medium/low) +- Issue description, Supporting evidence, Suggested fix + +**Structure your response as:** +\`\`\` + +## Finding 1: [Title] +- **Severity**: high +- **Location**: [specific reference — e.g. component, section, endpoint, rule] +- **Confidence**: high +- **Issue**: [what is wrong and why it matters] +- **Evidence**: [concrete reference, snippet, or observation that proves the issue] +- **Suggested Fix**: [actionable recommendation] + +## Finding 2: [Title] +... + +## Summary +[Total findings by severity. Overall risk assessment with confidence levels.] + +\`\`\``, + + EVALUATE: ` +## Analysis Intent: EVALUATE + +You are conducting an **evaluation** — your goal is to compare options against criteria and surface tradeoffs. + +**Focus:** +- Identify the options available (explicit or implied by the question) +- Define evaluation criteria relevant to the context (cost, complexity, performance, maintainability, risk, etc.) +- Assess each option against each criterion with evidence, not assumptions +- Surface tradeoffs clearly — where one option wins, another likely loses +- If the question implies a recommendation is needed, provide one with conditions + +**Required output fields:** +- Options identified, Criteria used, Per-option assessment, Tradeoff summary +- Conditional recommendation (if decision is required), Confidence per assessment + +**Structure your response as:** +\`\`\` + +## Options Identified +1. [Option A] — [brief description] +2. [Option B] — [brief description] + +## Criteria & Assessment +| Criterion | Option A | Option B | +|-----------|----------|----------| +| [e.g. Complexity] | [assessment] | [assessment] | + +## Tradeoff Summary +- Option A excels at [X] but sacrifices [Y] +- Option B excels at [Y] but sacrifices [X] + +## Recommendation +[If applicable: "Choose A if [condition]; choose B if [condition]"] +- **Confidence**: [level and reasoning] + +## Risks & Unknowns +[Uncertainties that could change the recommendation] + +\`\`\``, + + PLAN: ` +## Analysis Intent: PLAN + +You are conducting a **planning analysis** — your goal is to define current state, target state, and a phased path between them. + +**Focus:** +- Assess where things stand now (current state, constraints, existing assets) +- Define the target state clearly (what "done" looks like) +- Break the path into phases with sequencing, dependencies, and exit criteria +- Identify risks per phase and mitigation strategies +- Estimate effort where possible (relative sizing is fine: small/medium/large) + +**Required output fields:** +- Current state assessment, Target state definition +- Phases (each with: goal, tasks, exit criteria, dependencies, risks, effort estimate) +- Critical path, Key risks and mitigations + +**Structure your response as:** +\`\`\` + +## Current State +[What exists today, constraints, and starting conditions] + +## Target State +[What "done" looks like — measurable where possible] + +## Phase 1: [Name] +- **Goal**: [what this phase achieves] +- **Tasks**: [concrete work items] +- **Exit Criteria**: [how you know this phase is complete] +- **Dependencies**: [what must be true before starting] +- **Risks**: [what could go wrong, with mitigation] +- **Effort**: [small/medium/large or time estimate] + +## Phase 2: [Name] +... + +## Critical Path & Dependencies +[Which phases block others, what the minimum viable sequence is] + +## Summary +[Overall effort assessment, key risks, confidence in feasibility] + +\`\`\``, + + EXPLAIN: ` +## Analysis Intent: EXPLAIN + +You are conducting an **explanatory analysis** — your goal is to build understanding of how something works, why it exists, or what it means. + +**Focus:** +- Lead with a clear thesis statement — your one-sentence answer to the question +- Identify the key mechanisms, components, or concepts that drive the answer +- Provide evidence and references for each mechanism (not just assertions) +- Map relationships, data flows, or causal chains between components +- Acknowledge unknowns, gaps in evidence, and areas of uncertainty +- Works for any domain: architecture, external systems, abstract concepts, research topics + +**Required output fields:** +- Thesis statement, Key mechanisms/components (with evidence) +- Relationships and interactions, Evidence references +- Unknowns and knowledge gaps, Overall confidence + +**Structure your response as:** +\`\`\` + +## Thesis +[One clear sentence answering the core question] + +## Key Mechanisms +### 1. [Mechanism/Component Name] +- **What**: [description] +- **Evidence**: [reference, observation, or reasoning] +- **Role**: [how this contributes to the overall answer] + +### 2. [Mechanism/Component Name] +... + +## Relationships & Interactions +[How the mechanisms connect — data flows, dependencies, causal chains] + +## Unknowns & Gaps +- [What you could not determine or verify] +- [Areas where confidence is lower and why] + +## Summary +[Synthesized explanation with overall confidence level] + +\`\`\``, +} diff --git a/src/agents/athena/council-member-agent.ts b/src/agents/athena/council-member-agent.ts index aa4b42c3a..f099129d8 100644 --- a/src/agents/athena/council-member-agent.ts +++ b/src/agents/athena/council-member-agent.ts @@ -4,33 +4,26 @@ import { createAgentToolAllowlist } from "../../shared" const MODE: AgentMode = "subagent" -export const COUNCIL_MEMBER_PROMPT = `You are an independent code analyst in a multi-model analysis council. Your role is to provide thorough, evidence-based analysis. +export const COUNCIL_MEMBER_PROMPT = `You are an independent analyst in a multi-model analysis council. Your role is to provide thorough, evidence-based analysis. ## Your Role - You are one of several AI models analyzing the same question independently - Your analysis should be thorough and evidence-based - You are read-only — you cannot modify any files, only analyze -- Focus on finding real issues, not hypothetical ones ## Instructions 1. Analyze the question carefully -2. Search the codebase thoroughly using available tools (Read, Grep, Glob, LSP) -3. Report your findings with evidence (file paths, line numbers, code snippets) -4. For each finding, state: - - What the issue/observation is - - 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 +2. Use available tools to gather evidence relevant to the question +3. For each point, state what you observed, where (if applicable), and your confidence level +4. Be concise but thorough — quality over quantity ## Response Format (MANDATORY) You MUST wrap your final analysis in tags. This is how the system extracts your findings. **Include inside tags:** -- Key findings with evidence (file paths, line numbers, code snippets) +- Key findings with supporting evidence - Confidence levels for each finding (high/medium/low) -- Severity assessments (critical/high/medium/low) - Concerns and caveats **Exclude from tags (keep outside):** @@ -41,14 +34,12 @@ You MUST wrap your final analysis in tags. This is how **Example structure:** \`\`\` -## Finding 1: [Title] -- **Location**: src/path/to/file.ts:42-56 -- **Severity**: high +## Point 1: [Title] +- **Observation**: [what you found] - **Confidence**: high -- **Issue**: [description] -- **Evidence**: [code snippet or reference] +- **Details**: [supporting evidence or reasoning] -## Finding 2: [Title] +## Point 2: [Title] ... ## Summary diff --git a/src/hooks/athena-sisyphus-only/agent-matcher.ts b/src/hooks/athena-sisyphus-only/agent-matcher.ts new file mode 100644 index 000000000..0a34770db --- /dev/null +++ b/src/hooks/athena-sisyphus-only/agent-matcher.ts @@ -0,0 +1,5 @@ +import { ATHENA_AGENT } from "./constants" + +export function isAthenaAgent(agentName: string | undefined): boolean { + return agentName?.toLowerCase().includes(ATHENA_AGENT) ?? false +} diff --git a/src/hooks/athena-sisyphus-only/constants.ts b/src/hooks/athena-sisyphus-only/constants.ts new file mode 100644 index 000000000..d0195e7d4 --- /dev/null +++ b/src/hooks/athena-sisyphus-only/constants.ts @@ -0,0 +1,5 @@ +export const HOOK_NAME = "athena-sisyphus-only" + +export const ATHENA_AGENT = "athena" + +export const BLOCKED_TOOLS = ["Write", "Edit", "write", "edit"] diff --git a/src/hooks/athena-sisyphus-only/hook.ts b/src/hooks/athena-sisyphus-only/hook.ts new file mode 100644 index 000000000..e7c15294d --- /dev/null +++ b/src/hooks/athena-sisyphus-only/hook.ts @@ -0,0 +1,49 @@ +import type { PluginInput } from "@opencode-ai/plugin" +import { HOOK_NAME, BLOCKED_TOOLS } from "./constants" +import { log } from "../../shared/logger" +import { getAgentFromSession } from "../prometheus-md-only/agent-resolution" +import { isAthenaAgent } from "./agent-matcher" +import { isAllowedPath } from "./path-policy" + +export function createAthenaSisyphusOnlyHook(ctx: PluginInput) { + return { + "tool.execute.before": async ( + input: { tool: string; sessionID: string; callID: string }, + output: { args: Record; message?: string } + ): Promise => { + if (!BLOCKED_TOOLS.includes(input.tool)) { + return + } + + const agentName = await getAgentFromSession(input.sessionID, ctx.directory, ctx.client) + + if (!isAthenaAgent(agentName)) { + return + } + + const filePath = (output.args.filePath ?? output.args.path ?? output.args.file) as string | undefined + if (!filePath) { + return + } + + if (!isAllowedPath(filePath, ctx.directory)) { + log(`[${HOOK_NAME}] Blocked: Athena attempted write outside .sisyphus/`, { + sessionID: input.sessionID, + tool: input.tool, + filePath, + agent: agentName, + }) + throw new Error( + `[${HOOK_NAME}] Athena can only write/edit files inside .sisyphus/ directory. Attempted to modify: ${filePath}` + ) + } + + log(`[${HOOK_NAME}] Allowed: .sisyphus/ write permitted`, { + sessionID: input.sessionID, + tool: input.tool, + filePath, + agent: agentName, + }) + }, + } +} diff --git a/src/hooks/athena-sisyphus-only/index.ts b/src/hooks/athena-sisyphus-only/index.ts new file mode 100644 index 000000000..1ad4d26cc --- /dev/null +++ b/src/hooks/athena-sisyphus-only/index.ts @@ -0,0 +1,2 @@ +export * from "./constants" +export { createAthenaSisyphusOnlyHook } from "./hook" diff --git a/src/hooks/athena-sisyphus-only/path-policy.ts b/src/hooks/athena-sisyphus-only/path-policy.ts new file mode 100644 index 000000000..ccb7c2350 --- /dev/null +++ b/src/hooks/athena-sisyphus-only/path-policy.ts @@ -0,0 +1,30 @@ +import { relative, resolve, isAbsolute } from "node:path" + +/** + * Cross-platform path validator for Athena file writes. + * Uses path.resolve/relative instead of string matching to handle: + * - Windows backslashes (e.g., .sisyphus\\notepads\\x.yaml) + * - Mixed separators (e.g., .sisyphus\\plans/x.md) + * - Case-insensitive directory matching + * - Workspace confinement (blocks paths outside root or via traversal) + * - No extension restriction: any file type is allowed inside .sisyphus/ + */ +export function isAllowedPath(filePath: string, workspaceRoot: string): boolean { + // 1. Resolve to absolute path + const resolved = resolve(workspaceRoot, filePath) + + // 2. Get relative path from workspace root + const rel = relative(workspaceRoot, resolved) + + // 3. Reject if escapes root (starts with ".." or is absolute) + if (rel.startsWith("..") || isAbsolute(rel)) { + return false + } + + // 4. Check if .sisyphus/ or .sisyphus\ exists anywhere in the path (case-insensitive) + if (!/\.sisyphus[/\\]/i.test(rel)) { + return false + } + + return true +} diff --git a/src/shared/agent-tool-restrictions.ts b/src/shared/agent-tool-restrictions.ts index e862044fb..09517f817 100644 --- a/src/shared/agent-tool-restrictions.ts +++ b/src/shared/agent-tool-restrictions.ts @@ -48,8 +48,6 @@ const AGENT_RESTRICTIONS: Record> = { }, athena: { - write: false, - edit: false, call_omo_agent: false, },