refactor(athena): strip audit bias from council member prompt + add intent addendums + write boundary hook + relax restrictions
This commit is contained in:
@@ -0,0 +1,167 @@
|
|||||||
|
export const COUNCIL_INTENT_ADDENDUMS: Record<string, string> = {
|
||||||
|
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:**
|
||||||
|
\`\`\`
|
||||||
|
<COUNCIL_MEMBER_RESPONSE>
|
||||||
|
## 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.]
|
||||||
|
</COUNCIL_MEMBER_RESPONSE>
|
||||||
|
\`\`\``,
|
||||||
|
|
||||||
|
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:**
|
||||||
|
\`\`\`
|
||||||
|
<COUNCIL_MEMBER_RESPONSE>
|
||||||
|
## 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]
|
||||||
|
</COUNCIL_MEMBER_RESPONSE>
|
||||||
|
\`\`\``,
|
||||||
|
|
||||||
|
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:**
|
||||||
|
\`\`\`
|
||||||
|
<COUNCIL_MEMBER_RESPONSE>
|
||||||
|
## 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]
|
||||||
|
</COUNCIL_MEMBER_RESPONSE>
|
||||||
|
\`\`\``,
|
||||||
|
|
||||||
|
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:**
|
||||||
|
\`\`\`
|
||||||
|
<COUNCIL_MEMBER_RESPONSE>
|
||||||
|
## 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]
|
||||||
|
</COUNCIL_MEMBER_RESPONSE>
|
||||||
|
\`\`\``,
|
||||||
|
}
|
||||||
@@ -4,33 +4,26 @@ import { createAgentToolAllowlist } from "../../shared"
|
|||||||
|
|
||||||
const MODE: AgentMode = "subagent"
|
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
|
## Your Role
|
||||||
- You are one of several AI models analyzing the same question independently
|
- You are one of several AI models analyzing the same question independently
|
||||||
- Your analysis should be thorough and evidence-based
|
- Your analysis should be thorough and evidence-based
|
||||||
- You are read-only — you cannot modify any files, only analyze
|
- You are read-only — you cannot modify any files, only analyze
|
||||||
- Focus on finding real issues, not hypothetical ones
|
|
||||||
|
|
||||||
## Instructions
|
## Instructions
|
||||||
1. Analyze the question carefully
|
1. Analyze the question carefully
|
||||||
2. Search the codebase thoroughly using available tools (Read, Grep, Glob, LSP)
|
2. Use available tools to gather evidence relevant to the question
|
||||||
3. Report your findings with evidence (file paths, line numbers, code snippets)
|
3. For each point, state what you observed, where (if applicable), and your confidence level
|
||||||
4. For each finding, state:
|
4. Be concise but thorough — quality over quantity
|
||||||
- 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
|
|
||||||
|
|
||||||
## Response Format (MANDATORY)
|
## Response Format (MANDATORY)
|
||||||
|
|
||||||
You MUST wrap your final analysis in <COUNCIL_MEMBER_RESPONSE> tags. This is how the system extracts your findings.
|
You MUST wrap your final analysis in <COUNCIL_MEMBER_RESPONSE> tags. This is how the system extracts your findings.
|
||||||
|
|
||||||
**Include inside tags:**
|
**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)
|
- Confidence levels for each finding (high/medium/low)
|
||||||
- Severity assessments (critical/high/medium/low)
|
|
||||||
- Concerns and caveats
|
- Concerns and caveats
|
||||||
|
|
||||||
**Exclude from tags (keep outside):**
|
**Exclude from tags (keep outside):**
|
||||||
@@ -41,14 +34,12 @@ You MUST wrap your final analysis in <COUNCIL_MEMBER_RESPONSE> tags. This is how
|
|||||||
**Example structure:**
|
**Example structure:**
|
||||||
\`\`\`
|
\`\`\`
|
||||||
<COUNCIL_MEMBER_RESPONSE>
|
<COUNCIL_MEMBER_RESPONSE>
|
||||||
## Finding 1: [Title]
|
## Point 1: [Title]
|
||||||
- **Location**: src/path/to/file.ts:42-56
|
- **Observation**: [what you found]
|
||||||
- **Severity**: high
|
|
||||||
- **Confidence**: high
|
- **Confidence**: high
|
||||||
- **Issue**: [description]
|
- **Details**: [supporting evidence or reasoning]
|
||||||
- **Evidence**: [code snippet or reference]
|
|
||||||
|
|
||||||
## Finding 2: [Title]
|
## Point 2: [Title]
|
||||||
...
|
...
|
||||||
|
|
||||||
## Summary
|
## Summary
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
import { ATHENA_AGENT } from "./constants"
|
||||||
|
|
||||||
|
export function isAthenaAgent(agentName: string | undefined): boolean {
|
||||||
|
return agentName?.toLowerCase().includes(ATHENA_AGENT) ?? false
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
export const HOOK_NAME = "athena-sisyphus-only"
|
||||||
|
|
||||||
|
export const ATHENA_AGENT = "athena"
|
||||||
|
|
||||||
|
export const BLOCKED_TOOLS = ["Write", "Edit", "write", "edit"]
|
||||||
@@ -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<string, unknown>; message?: string }
|
||||||
|
): Promise<void> => {
|
||||||
|
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,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
export * from "./constants"
|
||||||
|
export { createAthenaSisyphusOnlyHook } from "./hook"
|
||||||
@@ -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
|
||||||
|
}
|
||||||
@@ -48,8 +48,6 @@ const AGENT_RESTRICTIONS: Record<string, Record<string, boolean>> = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
athena: {
|
athena: {
|
||||||
write: false,
|
|
||||||
edit: false,
|
|
||||||
call_omo_agent: false,
|
call_omo_agent: false,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user