1use crate::provider::{ContentBlock, Msg};
2
3#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
6pub enum ReasoningDepth {
7 Fast,
8 Adaptive,
9 Deep,
10}
11
12impl Default for ReasoningDepth {
13 fn default() -> Self {
14 ReasoningDepth::Adaptive
15 }
16}
17
18pub struct AntiSimulationGuard;
25
26impl AntiSimulationGuard {
27 const CLAIM_KEYWORDS: &'static [&'static str] = &[
29 "all tests pass",
30 "tests pass",
31 "build succeeds",
32 "build successful",
33 "output:",
34 "returns:",
35 "returned:",
36 "the result is",
37 "got:",
38 "passed",
39 "succeeded",
40 "compiled",
41 "ran successfully",
42 "no errors",
43 "0 errors",
44 "zero failures",
45 "green",
46 ];
47
48 pub fn check(turn_messages: &[Msg], tool_outputs_in_turn: bool) -> Option<String> {
50 if tool_outputs_in_turn {
51 return None; }
53
54 for msg in turn_messages.iter().filter(|m| m.role == "assistant") {
55 for block in &msg.content {
56 if let ContentBlock::Text { text } = block {
57 let lower = text.to_lowercase();
58 for kw in Self::CLAIM_KEYWORDS {
59 if lower.contains(kw) {
60 return Some(format!(
61 "anti-simulation: assistant claimed result (matched '{}') without executing a tool. Execute the tool first, then report the RAW output.",
62 kw
63 ));
64 }
65 }
66 }
67 }
68 }
69 None
70 }
71
72 pub fn check_code_claim(text: &str, had_fs_read: bool, had_search: bool) -> Option<String> {
74 if had_fs_read || had_search {
75 return None;
76 }
77 let lower = text.to_lowercase();
78 let code_claims = [
79 "function", "struct", "impl", "trait", "fn ", "pub fn", "mod ", "class ",
80 ];
81 let assertion_patterns = [
82 "exists",
83 "is defined",
84 "takes",
85 "returns",
86 "has a",
87 "contains",
88 ];
89
90 let has_code_ref = code_claims.iter().any(|c| lower.contains(c));
91 let has_assertion = assertion_patterns.iter().any(|a| lower.contains(a));
92
93 if has_code_ref && has_assertion {
94 return Some(
95 "hallucination-guard: you made an assertion about the code without reading it first. Use fs_read or search to verify before claiming.".into()
96 );
97 }
98 None
99 }
100}
101
102pub struct HallucinationGuard;
105
106impl HallucinationGuard {
107 pub fn verify(text: &str, tools_called: &[String]) -> Option<String> {
110 let has_read = tools_called.iter().any(|t| t == "fs_read" || t == "search");
111 AntiSimulationGuard::check_code_claim(text, has_read, has_read)
112 }
113}
114
115pub struct SelfCritique;
118
119impl SelfCritique {
120 pub fn pre_mutation_review(diffs: &[crate::event::FileDiff], spec: Option<&str>) -> String {
122 let mut review = String::from("## Self-critique (pre-mutation review)\n\n");
123
124 if diffs.is_empty() {
125 review.push_str("No diffs to review.\n");
126 return review;
127 }
128
129 review.push_str(&format!("Files to change: {}\n", diffs.len()));
130 for d in diffs {
131 review.push_str(&format!("- {} (+{} -{})\n", d.file, d.plus, d.minus));
132 }
133
134 review.push_str("\n### Checklist\n");
135 review.push_str("- [ ] Each change addresses the spec/requirement\n");
136 review.push_str("- [ ] No unnecessary formatting changes\n");
137 review.push_str("- [ ] Tests still pass after changes\n");
138 review.push_str("- [ ] No secrets or credentials exposed\n");
139 review.push_str("- [ ] Edge cases considered\n");
140
141 if let Some(s) = spec {
142 review.push_str(&format!("\nRefer to spec: {}\n", s));
143 }
144
145 review
146 }
147}
148
149pub struct UncertaintyEstimator;
152
153impl UncertaintyEstimator {
154 pub fn confidence(has_read_files: bool, has_run_tests: bool, task_complexity: &str) -> f64 {
156 let mut confidence: f64 = 0.3;
157
158 if has_read_files {
159 confidence += 0.3;
160 }
161 if has_run_tests {
162 confidence += 0.3;
163 }
164
165 match task_complexity {
166 "trivial" => confidence += 0.1,
167 "small" => confidence += 0.05,
168 "medium" => confidence += 0.0,
169 "hard" => confidence -= 0.1,
170 "vision" => confidence -= 0.05,
171 _ => {}
172 }
173
174 confidence.max(0.0).min(1.0)
175 }
176
177 pub fn uncertain_message() -> &'static str {
179 "I'm not sure about this — let me verify before proceeding."
180 }
181}
182
183pub struct StopAndAsk;
186
187impl StopAndAsk {
188 pub fn should_ask(
190 ambiguity_level: f64,
191 is_irreversible: bool,
192 autonomy_allows: bool,
193 constraints_missing: bool,
194 ) -> Option<String> {
195 if ambiguity_level > 0.7 && !autonomy_allows {
196 return Some(
197 "High ambiguity detected. Could you clarify:\n- What exact behavior do you expect?\n- Are there specific constraints I should know?".into()
198 );
199 }
200
201 if is_irreversible && !autonomy_allows {
202 return Some(
203 "This action is irreversible. Before proceeding, please confirm:\n- Is this the expected outcome?\n- Are there any backups or safeguards in place?".into()
204 );
205 }
206
207 if constraints_missing {
208 return Some(
209 "Missing constraints. Could you specify:\n- Target environment/OS?\n- Performance requirements?\n- Compatibility constraints?".into()
210 );
211 }
212
213 None
214 }
215
216 pub fn ask_single(question: &str) -> String {
218 format!("❓ {}", question)
219 }
220}
221
222pub struct ReasoningEngine {
225 pub depth: ReasoningDepth,
226 pub anti_simulation: bool,
227 pub hallucination_guard: bool,
228 pub self_critique: bool,
229}
230
231impl Default for ReasoningEngine {
232 fn default() -> Self {
233 Self {
234 depth: ReasoningDepth::Adaptive,
235 anti_simulation: true,
236 hallucination_guard: true,
237 self_critique: true,
238 }
239 }
240}
241
242impl ReasoningEngine {
243 pub fn plan_depth(&self, task: &str) -> usize {
245 let tier = crate::router::TaskTier::from_str(if task.len() > 100 {
246 "hard"
247 } else if task.len() > 30 {
248 "medium"
249 } else {
250 "small"
251 });
252 match self.depth {
253 ReasoningDepth::Fast => 1,
254 ReasoningDepth::Deep => 5,
255 ReasoningDepth::Adaptive => match tier {
256 crate::router::TaskTier::Trivial => 1,
257 crate::router::TaskTier::Small => 2,
258 crate::router::TaskTier::Medium => 3,
259 crate::router::TaskTier::Hard => 4,
260 crate::router::TaskTier::Vision => 3,
261 },
262 }
263 }
264
265 pub fn guard_turn(&self, messages: &[Msg], tool_outputs_in_turn: bool) -> Option<String> {
267 if self.anti_simulation {
268 AntiSimulationGuard::check(messages, tool_outputs_in_turn)
269 } else {
270 None
271 }
272 }
273}
274
275#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
279pub enum ReasoningEvent {
280 Planning { steps: Vec<String> },
282 SelfCritique { review: String },
284 UncertaintyCheck { message: String },
286 AskUser { question: String },
288 FabricationRejected { reason: String },
290 ClaimRejected { reason: String },
292}