pub mod model_rotation;
pub mod shared_context;
pub mod transport;
pub const AUTOCHAT_MAX_AGENTS: usize = 8;
pub const AUTOCHAT_DEFAULT_AGENTS: usize = 3;
pub const AUTOCHAT_MAX_ROUNDS: usize = 3;
pub const AUTOCHAT_MAX_DYNAMIC_SPAWNS: usize = 3;
pub const AUTOCHAT_SPAWN_CHECK_MIN_CHARS: usize = 800;
pub const AUTOCHAT_RLM_THRESHOLD_CHARS: usize = 6_000;
pub const AUTOCHAT_RLM_FALLBACK_CHARS: usize = 3_500;
pub const AUTOCHAT_STATUS_MAX_ROUNDS_REACHED: &str = "max_rounds_reached";
pub const AUTOCHAT_NO_PRD_FLAG: &str = "--no-prd";
pub const AUTOCHAT_RLM_HANDOFF_QUERY: &str = "Prepare a concise relay handoff for the next specialist.\n\
Return FINAL(JSON) with this exact shape:\n\
{\"kind\":\"semantic\",\"file\":\"relay_handoff\",\"answer\":\"...\"}\n\
The \"answer\" must include:\n\
1) key conclusions,\n\
2) unresolved risks,\n\
3) one exact next action.\n\
Keep it concise and actionable.";
pub const AUTOCHAT_QUICK_DEMO_TASK: &str = "Self-organize into the right specialties for this task, then relay one concrete implementation plan with clear next handoffs.";
const REQUIRED_RELAY_CAPABILITIES: [&str; 4] =
["relay", "context-handoff", "rlm-aware", "autochat"];
pub fn ensure_required_relay_capabilities(capabilities: &mut Vec<String>) {
for required in REQUIRED_RELAY_CAPABILITIES {
if !capabilities.iter().any(|cap| cap == required) {
capabilities.push(required.to_string());
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ParsedAutochatRequest {
pub agent_count: usize,
pub task: String,
pub bypass_prd: bool,
pub explicit_count: bool,
}
pub fn parse_autochat_request(
rest: &str,
default_agents: usize,
quick_demo_task: &str,
) -> Option<ParsedAutochatRequest> {
let mut bypass_prd = false;
let mut kept_tokens: Vec<&str> = Vec::new();
for token in rest.split_whitespace() {
if token.eq_ignore_ascii_case(AUTOCHAT_NO_PRD_FLAG) {
bypass_prd = true;
} else {
kept_tokens.push(token);
}
}
let normalized = kept_tokens.join(" ");
let explicit_count = normalized
.split_whitespace()
.next()
.and_then(|value| value.parse::<usize>().ok())
.is_some();
let (agent_count, task) = parse_autochat_args(&normalized, default_agents, quick_demo_task)?;
Some(ParsedAutochatRequest {
agent_count,
task: task.to_string(),
bypass_prd,
explicit_count,
})
}
pub fn parse_autochat_args<'a>(
rest: &'a str,
default_agents: usize,
quick_demo_task: &'a str,
) -> Option<(usize, &'a str)> {
let rest = rest.trim();
if rest.is_empty() {
return None;
}
let mut parts = rest.splitn(2, char::is_whitespace);
let first = parts.next().unwrap_or("").trim();
if first.is_empty() {
return None;
}
if let Ok(count) = first.parse::<usize>() {
let task = parts.next().unwrap_or("").trim();
if task.is_empty() {
Some((count, quick_demo_task))
} else {
Some((count, task))
}
} else {
Some((default_agents, rest))
}
}
pub fn normalize_for_convergence(text: &str, max_chars: usize) -> String {
let mut normalized = String::with_capacity(text.len().min(512));
let mut last_was_space = false;
for ch in text.chars() {
if ch.is_ascii_alphanumeric() {
normalized.push(ch.to_ascii_lowercase());
last_was_space = false;
} else if ch.is_whitespace() && !last_was_space {
normalized.push(' ');
last_was_space = true;
}
if normalized.len() >= max_chars {
break;
}
}
normalized.trim().to_string()
}
#[cfg(test)]
mod tests {
use super::{
AUTOCHAT_DEFAULT_AGENTS, AUTOCHAT_NO_PRD_FLAG, AUTOCHAT_QUICK_DEMO_TASK,
ParsedAutochatRequest, ensure_required_relay_capabilities, normalize_for_convergence,
parse_autochat_args, parse_autochat_request,
};
#[test]
fn parse_autochat_args_with_default_count() {
let parsed = parse_autochat_args(
"implement relay checkpointing",
AUTOCHAT_DEFAULT_AGENTS,
AUTOCHAT_QUICK_DEMO_TASK,
);
assert_eq!(
parsed,
Some((AUTOCHAT_DEFAULT_AGENTS, "implement relay checkpointing"))
);
}
#[test]
fn parse_autochat_args_with_count_only_uses_demo_task() {
let parsed = parse_autochat_args("4", AUTOCHAT_DEFAULT_AGENTS, AUTOCHAT_QUICK_DEMO_TASK);
assert_eq!(parsed, Some((4, AUTOCHAT_QUICK_DEMO_TASK)));
}
#[test]
fn normalize_for_convergence_ignores_case_and_symbols() {
let a = normalize_for_convergence("Done! Next Step: Add tests.", 280);
let b = normalize_for_convergence("done next step add tests", 280);
assert_eq!(a, b);
}
#[test]
fn ensure_required_relay_capabilities_adds_missing_caps() {
let mut caps = vec!["planning".to_string(), "relay".to_string()];
ensure_required_relay_capabilities(&mut caps);
assert!(caps.iter().any(|cap| cap == "context-handoff"));
assert!(caps.iter().any(|cap| cap == "rlm-aware"));
assert!(caps.iter().any(|cap| cap == "autochat"));
}
#[test]
fn parse_autochat_request_detects_no_prd_flag() {
let parsed = parse_autochat_request(
&format!("{AUTOCHAT_NO_PRD_FLAG} 4 build relay"),
AUTOCHAT_DEFAULT_AGENTS,
AUTOCHAT_QUICK_DEMO_TASK,
);
assert_eq!(
parsed,
Some(ParsedAutochatRequest {
agent_count: 4,
task: "build relay".to_string(),
bypass_prd: true,
explicit_count: true,
})
);
}
#[test]
fn parse_autochat_request_supports_flag_anywhere() {
let parsed = parse_autochat_request(
"build relay --no-prd now",
AUTOCHAT_DEFAULT_AGENTS,
AUTOCHAT_QUICK_DEMO_TASK,
);
assert_eq!(
parsed,
Some(ParsedAutochatRequest {
agent_count: AUTOCHAT_DEFAULT_AGENTS,
task: "build relay now".to_string(),
bypass_prd: true,
explicit_count: false,
})
);
}
}