Skip to main content

lean_ctx/core/agents/
roles.rs

1use super::ScratchpadEntry;
2use crate::core::a2a::message::{A2AMessage, MessageCategory};
3use serde::{Deserialize, Serialize};
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
6#[serde(rename_all = "snake_case")]
7pub enum AgentRole {
8    Coder,
9    Reviewer,
10    Planner,
11    Explorer,
12    Debugger,
13    Tester,
14    Orchestrator,
15}
16
17impl AgentRole {
18    pub fn from_str_loose(s: &str) -> Self {
19        match s.to_lowercase().as_str() {
20            "review" | "reviewer" | "code_review" => Self::Reviewer,
21            "plan" | "planner" | "architect" => Self::Planner,
22            "explore" | "explorer" | "research" => Self::Explorer,
23            "debug" | "debugger" => Self::Debugger,
24            "test" | "tester" | "qa" => Self::Tester,
25            "orchestrator" | "coordinator" | "manager" => Self::Orchestrator,
26            _ => Self::Coder,
27        }
28    }
29}
30
31#[derive(Debug, Clone)]
32pub struct ContextDepthConfig {
33    pub max_files_full: usize,
34    pub max_files_signatures: usize,
35    pub preferred_mode: &'static str,
36    pub include_graph: bool,
37    pub include_knowledge: bool,
38    pub include_gotchas: bool,
39    pub context_budget_ratio: f64,
40}
41
42impl ContextDepthConfig {
43    pub fn for_role(role: AgentRole) -> Self {
44        match role {
45            AgentRole::Coder => Self {
46                max_files_full: 5,
47                max_files_signatures: 15,
48                preferred_mode: "full",
49                include_graph: true,
50                include_knowledge: true,
51                include_gotchas: true,
52                context_budget_ratio: 0.7,
53            },
54            AgentRole::Reviewer => Self {
55                max_files_full: 3,
56                max_files_signatures: 20,
57                preferred_mode: "signatures",
58                include_graph: true,
59                include_knowledge: true,
60                include_gotchas: true,
61                context_budget_ratio: 0.5,
62            },
63            AgentRole::Planner => Self {
64                max_files_full: 1,
65                max_files_signatures: 10,
66                preferred_mode: "map",
67                include_graph: true,
68                include_knowledge: true,
69                include_gotchas: false,
70                context_budget_ratio: 0.3,
71            },
72            AgentRole::Explorer => Self {
73                max_files_full: 2,
74                max_files_signatures: 8,
75                preferred_mode: "map",
76                include_graph: true,
77                include_knowledge: false,
78                include_gotchas: false,
79                context_budget_ratio: 0.4,
80            },
81            AgentRole::Debugger => Self {
82                max_files_full: 8,
83                max_files_signatures: 5,
84                preferred_mode: "full",
85                include_graph: false,
86                include_knowledge: true,
87                include_gotchas: true,
88                context_budget_ratio: 0.8,
89            },
90            AgentRole::Tester => Self {
91                max_files_full: 4,
92                max_files_signatures: 10,
93                preferred_mode: "full",
94                include_graph: false,
95                include_knowledge: false,
96                include_gotchas: true,
97                context_budget_ratio: 0.6,
98            },
99            AgentRole::Orchestrator => Self {
100                max_files_full: 0,
101                max_files_signatures: 5,
102                preferred_mode: "map",
103                include_graph: true,
104                include_knowledge: true,
105                include_gotchas: false,
106                context_budget_ratio: 0.2,
107            },
108        }
109    }
110
111    pub fn mode_for_rank(&self, rank: usize) -> &'static str {
112        if rank < self.max_files_full {
113            "full"
114        } else if rank < self.max_files_full + self.max_files_signatures {
115            "signatures"
116        } else {
117            "map"
118        }
119    }
120}
121
122impl From<ScratchpadEntry> for A2AMessage {
123    fn from(entry: ScratchpadEntry) -> Self {
124        Self {
125            id: entry.id,
126            from_agent: entry.from_agent,
127            to_agent: entry.to_agent,
128            task_id: entry.task_id,
129            category: MessageCategory::parse_str(&entry.category),
130            priority: entry.priority,
131            privacy: entry.privacy,
132            content: entry.message,
133            metadata: entry.metadata,
134            project_root: entry.project_root,
135            timestamp: entry.timestamp,
136            read_by: entry.read_by,
137            expires_at: entry.expires_at,
138        }
139    }
140}
141
142impl From<A2AMessage> for ScratchpadEntry {
143    fn from(msg: A2AMessage) -> Self {
144        Self {
145            id: msg.id,
146            from_agent: msg.from_agent,
147            to_agent: msg.to_agent,
148            task_id: msg.task_id,
149            category: msg.category.to_string(),
150            priority: msg.priority,
151            privacy: msg.privacy,
152            message: msg.content,
153            metadata: msg.metadata,
154            project_root: msg.project_root,
155            timestamp: msg.timestamp,
156            read_by: msg.read_by,
157            expires_at: msg.expires_at,
158        }
159    }
160}