use crate::provider::{ContentBlock, Msg};
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub enum ReasoningDepth {
Fast,
Adaptive,
Deep,
}
impl Default for ReasoningDepth {
fn default() -> Self {
ReasoningDepth::Adaptive
}
}
pub struct AntiSimulationGuard;
impl AntiSimulationGuard {
const CLAIM_KEYWORDS: &'static [&'static str] = &[
"all tests pass",
"tests pass",
"build succeeds",
"build successful",
"output:",
"returns:",
"returned:",
"the result is",
"got:",
"passed",
"succeeded",
"compiled",
"ran successfully",
"no errors",
"0 errors",
"zero failures",
"green",
];
pub fn check(turn_messages: &[Msg], tool_outputs_in_turn: bool) -> Option<String> {
if tool_outputs_in_turn {
return None; }
for msg in turn_messages.iter().filter(|m| m.role == "assistant") {
for block in &msg.content {
if let ContentBlock::Text { text } = block {
let lower = text.to_lowercase();
for kw in Self::CLAIM_KEYWORDS {
if lower.contains(kw) {
return Some(format!(
"anti-simulation: assistant claimed result (matched '{}') without executing a tool. Execute the tool first, then report the RAW output.",
kw
));
}
}
}
}
}
None
}
pub fn check_code_claim(text: &str, had_fs_read: bool, had_search: bool) -> Option<String> {
if had_fs_read || had_search {
return None;
}
let lower = text.to_lowercase();
let code_claims = [
"function", "struct", "impl", "trait", "fn ", "pub fn", "mod ", "class ",
];
let assertion_patterns = [
"exists",
"is defined",
"takes",
"returns",
"has a",
"contains",
];
let has_code_ref = code_claims.iter().any(|c| lower.contains(c));
let has_assertion = assertion_patterns.iter().any(|a| lower.contains(a));
if has_code_ref && has_assertion {
return Some(
"hallucination-guard: you made an assertion about the code without reading it first. Use fs_read or search to verify before claiming.".into()
);
}
None
}
}
pub struct HallucinationGuard;
impl HallucinationGuard {
pub fn verify(text: &str, tools_called: &[String]) -> Option<String> {
let has_read = tools_called.iter().any(|t| t == "fs_read" || t == "search");
AntiSimulationGuard::check_code_claim(text, has_read, has_read)
}
}
pub struct SelfCritique;
impl SelfCritique {
pub fn pre_mutation_review(diffs: &[crate::event::FileDiff], spec: Option<&str>) -> String {
let mut review = String::from("## Self-critique (pre-mutation review)\n\n");
if diffs.is_empty() {
review.push_str("No diffs to review.\n");
return review;
}
review.push_str(&format!("Files to change: {}\n", diffs.len()));
for d in diffs {
review.push_str(&format!("- {} (+{} -{})\n", d.file, d.plus, d.minus));
}
review.push_str("\n### Checklist\n");
review.push_str("- [ ] Each change addresses the spec/requirement\n");
review.push_str("- [ ] No unnecessary formatting changes\n");
review.push_str("- [ ] Tests still pass after changes\n");
review.push_str("- [ ] No secrets or credentials exposed\n");
review.push_str("- [ ] Edge cases considered\n");
if let Some(s) = spec {
review.push_str(&format!("\nRefer to spec: {}\n", s));
}
review
}
}
pub struct UncertaintyEstimator;
impl UncertaintyEstimator {
pub fn confidence(has_read_files: bool, has_run_tests: bool, task_complexity: &str) -> f64 {
let mut confidence: f64 = 0.3;
if has_read_files {
confidence += 0.3;
}
if has_run_tests {
confidence += 0.3;
}
match task_complexity {
"trivial" => confidence += 0.1,
"small" => confidence += 0.05,
"medium" => confidence += 0.0,
"hard" => confidence -= 0.1,
"vision" => confidence -= 0.05,
_ => {}
}
confidence.max(0.0).min(1.0)
}
pub fn uncertain_message() -> &'static str {
"I'm not sure about this — let me verify before proceeding."
}
}
pub struct StopAndAsk;
impl StopAndAsk {
pub fn should_ask(
ambiguity_level: f64,
is_irreversible: bool,
autonomy_allows: bool,
constraints_missing: bool,
) -> Option<String> {
if ambiguity_level > 0.7 && !autonomy_allows {
return Some(
"High ambiguity detected. Could you clarify:\n- What exact behavior do you expect?\n- Are there specific constraints I should know?".into()
);
}
if is_irreversible && !autonomy_allows {
return Some(
"This action is irreversible. Before proceeding, please confirm:\n- Is this the expected outcome?\n- Are there any backups or safeguards in place?".into()
);
}
if constraints_missing {
return Some(
"Missing constraints. Could you specify:\n- Target environment/OS?\n- Performance requirements?\n- Compatibility constraints?".into()
);
}
None
}
pub fn ask_single(question: &str) -> String {
format!("❓ {}", question)
}
}
pub struct ReasoningEngine {
pub depth: ReasoningDepth,
pub anti_simulation: bool,
pub hallucination_guard: bool,
pub self_critique: bool,
}
impl Default for ReasoningEngine {
fn default() -> Self {
Self {
depth: ReasoningDepth::Adaptive,
anti_simulation: true,
hallucination_guard: true,
self_critique: true,
}
}
}
impl ReasoningEngine {
pub fn plan_depth(&self, task: &str) -> usize {
let tier = crate::router::TaskTier::from_str(if task.len() > 100 {
"hard"
} else if task.len() > 30 {
"medium"
} else {
"small"
});
match self.depth {
ReasoningDepth::Fast => 1,
ReasoningDepth::Deep => 5,
ReasoningDepth::Adaptive => match tier {
crate::router::TaskTier::Trivial => 1,
crate::router::TaskTier::Small => 2,
crate::router::TaskTier::Medium => 3,
crate::router::TaskTier::Hard => 4,
crate::router::TaskTier::Vision => 3,
},
}
}
pub fn guard_turn(&self, messages: &[Msg], tool_outputs_in_turn: bool) -> Option<String> {
if self.anti_simulation {
AntiSimulationGuard::check(messages, tool_outputs_in_turn)
} else {
None
}
}
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub enum ReasoningEvent {
Planning { steps: Vec<String> },
SelfCritique { review: String },
UncertaintyCheck { message: String },
AskUser { question: String },
FabricationRejected { reason: String },
ClaimRejected { reason: String },
}