use std::collections::HashMap;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum AgentStatus {
Orienting,
Seeding,
Running,
Paused,
Complete,
Failed,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum ModelTier {
Claude,
GLM,
Seed,
DeepSeek,
Hermes,
}
impl ModelTier {
pub fn relative_cost(&self) -> f64 {
match self {
ModelTier::Claude => 50.0, ModelTier::GLM => 5.0, ModelTier::Seed => 0.1, ModelTier::DeepSeek => 0.2, ModelTier::Hermes => 0.15, }
}
pub fn appropriate_for(&self, task: TaskType) -> bool {
match (self, task) {
(ModelTier::Claude, TaskType::Synthesis) => true,
(ModelTier::Claude, TaskType::Critique) => true,
(ModelTier::Claude, TaskType::BigIdea) => true,
(ModelTier::Claude, _) => false,
(ModelTier::GLM, TaskType::Architecture) => true,
(ModelTier::GLM, TaskType::ComplexCode) => true,
(ModelTier::GLM, TaskType::Orchestration) => true,
(ModelTier::GLM, _) => false,
(ModelTier::Seed, TaskType::Discovery) => true,
(ModelTier::Seed, TaskType::Exploration) => true,
(ModelTier::Seed, TaskType::Drafting) => true,
(ModelTier::Seed, TaskType::Variation) => true,
(ModelTier::Seed, _) => false,
(ModelTier::DeepSeek, TaskType::Documentation) => true,
(ModelTier::DeepSeek, TaskType::Research) => true,
(ModelTier::DeepSeek, TaskType::Drafting) => true,
(ModelTier::DeepSeek, _) => false,
(ModelTier::Hermes, TaskType::Adversarial) => true,
(ModelTier::Hermes, TaskType::SecondOpinion) => true,
(ModelTier::Hermes, _) => false,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum TaskType {
Synthesis,
Critique,
BigIdea,
Architecture,
ComplexCode,
Orchestration,
Discovery,
Exploration,
Drafting,
Variation,
Documentation,
Research,
Adversarial,
SecondOpinion,
}
#[derive(Debug, Clone)]
pub struct AgentRoom {
pub room_id: String,
pub role: String,
pub status: AgentStatus,
pub model: ModelTier,
pub task_type: TaskType,
pub generation: u32,
pub seed_iterations: usize,
pub crystallization_score: f64,
pub gated: bool,
pub gate_passed: Option<bool>,
pub created_at: u64,
pub updated_at: u64,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum GateResult {
Approved,
Rejected(String),
NeedsApproval(String),
}
pub struct Lighthouse {
agents: HashMap<String, AgentRoom>,
capacity: HashMap<ModelTier, f64>,
}
impl Default for Lighthouse {
fn default() -> Self {
Self::new()
}
}
impl Lighthouse {
pub fn new() -> Self {
let mut capacity = HashMap::new();
capacity.insert(ModelTier::Claude, 1.0); capacity.insert(ModelTier::GLM, 1.0); capacity.insert(ModelTier::Seed, 1.0); capacity.insert(ModelTier::DeepSeek, 1.0); capacity.insert(ModelTier::Hermes, 1.0);
Lighthouse {
agents: HashMap::new(),
capacity,
}
}
pub fn orient(&mut self, task: &str, task_type: TaskType) -> AgentRoom {
let model = self.cheapest_appropriate(task_type);
let room_id = format!("agent-{}", simple_hash(task));
let agent = AgentRoom {
room_id: room_id.clone(),
role: task.to_string(),
status: AgentStatus::Orienting,
model,
task_type,
generation: 0,
seed_iterations: 0,
crystallization_score: 0.0,
gated: false,
gate_passed: None,
created_at: current_timestamp(),
updated_at: current_timestamp(),
};
self.agents.insert(room_id, agent.clone());
agent
}
pub fn relay(&mut self, room_id: &str, seed_iterations: usize) -> Option<&AgentRoom> {
let agent = self.agents.get_mut(room_id)?;
if seed_iterations > 0 && agent.model != ModelTier::Seed {
agent.status = AgentStatus::Seeding;
agent.seed_iterations = seed_iterations;
} else {
agent.status = AgentStatus::Running;
}
agent.updated_at = current_timestamp();
Some(self.agents.get(room_id)?)
}
pub fn gate(&mut self, room_id: &str, output: &str) -> GateResult {
let agent = match self.agents.get_mut(room_id) {
Some(a) => a,
None => return GateResult::Rejected("Unknown room".to_string()),
};
agent.gated = true;
if contains_credentials(output) {
agent.gate_passed = Some(false);
agent.status = AgentStatus::Failed;
return GateResult::Rejected("Credential leak detected".to_string());
}
if contains_external_action(output) {
agent.gate_passed = Some(false);
return GateResult::NeedsApproval(
"External action requires Casey approval".to_string(),
);
}
if contains_overclaims(output) {
agent.gate_passed = Some(false);
return GateResult::Rejected(
"Overclaim detected — falsify before asserting".to_string(),
);
}
agent.gate_passed = Some(true);
agent.status = AgentStatus::Complete;
agent.updated_at = current_timestamp();
let cost = agent.model.relative_cost() * 0.01;
if let Some(remaining) = self.capacity.get_mut(&agent.model) {
*remaining = (*remaining - cost).max(0.0);
}
GateResult::Approved
}
fn cheapest_appropriate(&self, task_type: TaskType) -> ModelTier {
let tiers = [ModelTier::Seed, ModelTier::Hermes, ModelTier::DeepSeek, ModelTier::GLM, ModelTier::Claude];
for &tier in &tiers {
if tier.appropriate_for(task_type) {
if let Some(cap) = self.capacity.get(&tier) {
if *cap > 0.1 {
return tier;
}
}
}
}
ModelTier::Seed
}
pub fn active_agents(&self) -> Vec<&AgentRoom> {
self.agents.values()
.filter(|a| a.status == AgentStatus::Running || a.status == AgentStatus::Seeding)
.collect()
}
pub fn get_agent(&self, room_id: &str) -> Option<&AgentRoom> {
self.agents.get(room_id)
}
pub fn resource_summary(&self) -> String {
let mut lines = vec!["LIGHTHOUSE RESOURCE STATUS".to_string()];
for (tier, remaining) in &self.capacity {
let bar_len = (*remaining * 20.0) as usize;
let bar: String = "█".repeat(bar_len) + &"░".repeat(20 - bar_len);
lines.push(format!(
" {:?}: [{}] {:.0}% remaining",
tier, bar, remaining * 100.0
));
}
lines.push(format!(" Active agents: {}", self.active_agents().len()));
lines.join("\n")
}
}
fn simple_hash(s: &str) -> String {
use std::fmt::Write;
let mut hash: u64 = 5381;
for b in s.bytes() {
hash = hash.wrapping_mul(33).wrapping_add(b as u64);
}
format!("{:08x}", hash)
}
fn current_timestamp() -> u64 {
std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap_or_default()
.as_secs()
}
fn contains_credentials(s: &str) -> bool {
let lower = s.to_lowercase();
lower.contains("api_key=") || lower.contains("password=") || lower.contains("secret=") ||
lower.contains("token=") || lower.contains("bearer ")
}
fn contains_external_action(s: &str) -> bool {
let markers = ["send_email", "post_tweet", "git push", "npm publish", "deploy"];
markers.iter().any(|m| s.contains(m))
}
fn contains_overclaims(s: &str) -> bool {
let markers = ["proven that", "theorem:", "this proves", "we have proven"];
markers.iter().any(|m| s.to_lowercase().contains(m))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_orient_synthesis_uses_claude() {
let mut lh = Lighthouse::new();
let agent = lh.orient("synthesis paper", TaskType::Synthesis);
assert_eq!(agent.model, ModelTier::Claude);
}
#[test]
fn test_orient_drafting_uses_seed() {
let mut lh = Lighthouse::new();
let agent = lh.orient("draft a readme", TaskType::Drafting);
assert_eq!(agent.model, ModelTier::Seed);
}
#[test]
fn test_orient_adversarial_uses_hermes() {
let mut lh = Lighthouse::new();
let agent = lh.orient("find weak points", TaskType::Adversarial);
assert_eq!(agent.model, ModelTier::Hermes);
}
#[test]
fn test_orient_architecture_uses_glm() {
let mut lh = Lighthouse::new();
let agent = lh.orient("design the system", TaskType::Architecture);
assert_eq!(agent.model, ModelTier::GLM);
}
#[test]
fn test_gate_approves_clean_output() {
let mut lh = Lighthouse::new();
let agent = lh.orient("task", TaskType::Drafting);
let result = lh.gate(&agent.room_id, "Here is a clean result with no issues.");
assert_eq!(result, GateResult::Approved);
}
#[test]
fn test_gate_rejects_credentials() {
let mut lh = Lighthouse::new();
let agent = lh.orient("task", TaskType::Drafting);
let result = lh.gate(&agent.room_id, "The api_key=abc123 is here");
assert!(matches!(result, GateResult::Rejected(_)));
}
#[test]
fn test_gate_needs_approval_for_external() {
let mut lh = Lighthouse::new();
let agent = lh.orient("task", TaskType::Drafting);
let result = lh.gate(&agent.room_id, "Running git push to main");
assert!(matches!(result, GateResult::NeedsApproval(_)));
}
#[test]
fn test_gate_rejects_overclaims() {
let mut lh = Lighthouse::new();
let agent = lh.orient("task", TaskType::Drafting);
let result = lh.gate(&agent.room_id, "We have proven that all lattices are perfect");
assert!(matches!(result, GateResult::Rejected(_)));
}
#[test]
fn test_relay_sets_seeding() {
let mut lh = Lighthouse::new();
let agent = lh.orient("explore", TaskType::Architecture);
let result = lh.relay(&agent.room_id, 50);
assert!(result.is_some());
assert_eq!(result.unwrap().status, AgentStatus::Seeding);
}
#[test]
fn test_resource_summary() {
let mut lh = Lighthouse::new();
let summary = lh.resource_summary();
assert!(summary.contains("Claude"));
assert!(summary.contains("Seed"));
assert!(summary.contains("remaining"));
}
#[test]
fn test_capacity_decreases_after_gate() {
let mut lh = Lighthouse::new();
let initial = *lh.capacity.get(&ModelTier::Seed).unwrap();
let agent = lh.orient("task", TaskType::Drafting);
lh.gate(&agent.room_id, "clean output");
let after = *lh.capacity.get(&ModelTier::Seed).unwrap();
assert!(after < initial);
}
}