mod cache;
mod context_builder;
mod guard_fallback;
mod guard_retry;
mod inference_pipeline;
mod post_turn;
mod react_loop;
mod tool_prune;
mod types;
#[allow(unused_imports)]
pub(crate) use {
cache::{check_cache, store_in_cache},
context_builder::prepare_inference,
guard_fallback::{deterministic_guard_fallback, is_task_like_turn},
guard_retry::build_retry_request,
inference_pipeline::{execute_inference_pipeline, record_cost},
post_turn::{post_turn_ingest, refine_session_nickname},
react_loop::{run_inference_and_react, sanitize_model_output},
tool_prune::always_include_operational_tools,
types::{
InferenceInput, InferenceOutput, PipelineResult, PreparedContextSnapshot, PreparedInference,
},
};
#[cfg(test)]
mod tests {
use super::{
always_include_operational_tools, build_retry_request, deterministic_guard_fallback,
is_task_like_turn,
};
use crate::api::routes::agent::decomposition::DelegationProvenance;
use crate::api::routes::agent::guard_registry::{GuardContext, GuardId};
use crate::api::routes::agent::intent_registry::Intent;
use roboticus_llm::format::{ToolDefinition, UnifiedMessage, UnifiedRequest};
#[test]
fn operational_tools_remain_visible_to_task_workflow() {
let tools = always_include_operational_tools();
for required in [
"get_memory_stats",
"get_runtime_context",
"list-subagent-roster",
"list-available-skills",
"compose-subagent",
"compose-skill",
"orchestrate-subagents",
] {
assert!(
tools.iter().any(|tool| tool == required),
"missing required always-include tool: {required}"
);
}
}
fn test_prepared_inference() -> super::PreparedInference {
super::PreparedInference {
model: "test-model".to_string(),
request: UnifiedRequest {
model: "test-model".to_string(),
messages: vec![UnifiedMessage {
role: "user".to_string(),
content: "Do the task".to_string(),
parts: None,
}],
max_tokens: Some(512),
temperature: None,
system: None,
quality_target: None,
tools: vec![ToolDefinition {
name: "compose-subagent".to_string(),
description: "compose".to_string(),
parameters: serde_json::json!({"type":"object"}),
}],
},
cache_hash: "cache".to_string(),
system_prompt_hash: "system".to_string(),
query_embedding: None,
intents: vec![Intent::Execution],
previous_assistant: None,
context_snapshot: super::PreparedContextSnapshot {
complexity_level: "medium".to_string(),
token_budget: 0,
system_prompt_tokens: 0,
memory_tokens: 0,
history_tokens: 0,
history_depth: 0,
memory_tiers_json: None,
retrieved_memories_json: None,
},
retrieval_metrics: roboticus_agent::retrieval::RetrievalMetrics::default(),
tool_search_stats: None,
delegated_execution_result: None,
}
}
fn test_guard_context<'a>(tool_results: &'a [(String, String)]) -> GuardContext<'a> {
let provenance = Box::leak(Box::new(DelegationProvenance::default()));
GuardContext {
user_prompt: "Do the task",
intents: &[Intent::Execution],
tool_results,
agent_name: "Duncan",
resolved_model: "test-model",
delegation_provenance: provenance,
previous_assistant: None,
prior_assistant_messages: &[],
semantic_guard_scores: std::collections::HashMap::new(),
subagent_names: vec![],
}
}
#[test]
fn task_deferral_retry_preserves_tools_and_state() {
let prepared = test_prepared_inference();
let tool_results = vec![(
"list-subagent-roster".to_string(),
"{\"subagent_count\":0,\"subagents\":[]}".to_string(),
)];
let ctx = test_guard_context(&tool_results);
let retry = build_retry_request(GuardId::TaskDeferral, &prepared, &ctx);
assert_eq!(retry.tools.len(), prepared.request.tools.len());
let directive = retry.messages.last().expect("retry directive");
assert!(directive.content.contains("Prior tool state:"));
assert!(directive.content.contains("subagent_count"));
assert!(directive.content.contains("compose what is missing"));
}
#[test]
fn output_contract_retry_uses_prior_execution_state() {
let prepared = test_prepared_inference();
let tool_results = vec![(
"list_directory".to_string(),
"FIRMWARE.toml Governance.md Ledger.md".to_string(),
)];
let ctx = test_guard_context(&tool_results);
let retry = build_retry_request(GuardId::OutputContract, &prepared, &ctx);
assert!(retry.tools.is_empty());
let directive = retry.messages.last().expect("retry directive");
assert!(
directive
.content
.contains("exact requested output contract")
);
assert!(directive.content.contains("Prior execution state:"));
assert!(directive.content.contains("no headings"));
}
#[test]
fn low_value_retry_does_not_preserve_tools() {
let prepared = test_prepared_inference();
let empty_results: Vec<(String, String)> = Vec::new();
let ctx = test_guard_context(&empty_results);
let retry = build_retry_request(GuardId::LowValueParroting, &prepared, &ctx);
assert!(retry.tools.is_empty());
}
#[test]
fn internal_protocol_retry_preserves_tools() {
let prepared = test_prepared_inference();
let empty_results: Vec<(String, String)> = Vec::new();
let ctx = test_guard_context(&empty_results);
let retry = build_retry_request(GuardId::InternalProtocol, &prepared, &ctx);
assert_eq!(retry.tools.len(), prepared.request.tools.len());
let directive = retry.messages.last().expect("retry directive");
assert!(directive.content.contains("executable tool call"));
assert!(
directive
.content
.contains("Do not narrate internal protocol")
);
}
#[test]
fn task_like_turn_detects_operational_work() {
assert!(is_task_like_turn(&[Intent::Execution]));
assert!(is_task_like_turn(&[Intent::Delegation]));
assert!(!is_task_like_turn(&[Intent::CapabilitySummary]));
}
#[test]
fn output_contract_guard_fallback_salvages_exact_bullets() {
let rescued = deterministic_guard_fallback(
GuardId::OutputContract,
"## Summary\n- one\n- two\n- three",
&[],
"Return exactly three bullet points.",
"Duncan",
);
assert_eq!(rescued, "- one\n- two\n- three");
}
#[test]
fn internal_protocol_guard_fallback_salvages_delegate_outputs_into_exact_bullets() {
let tool_results = vec![
(
"delegate-subagent".to_string(),
"delegated_subagent=revenue_strategist\nsubtask 1 -> revenue_strategist\n1. Retainer Micro-Agency\n2. Finance Flash Newsletter".to_string(),
),
(
"delegate-subagent".to_string(),
"delegated_subagent=saas_ideator\nsubtask 1 -> saas_ideator\n**Product:** ScopeGuard".to_string(),
),
];
let rescued = deterministic_guard_fallback(
GuardId::InternalProtocol,
"",
&tool_results,
"Return exactly three bullet points: two revenue ideas and one SaaS idea.",
"Duncan",
);
assert_eq!(
rescued,
"- Retainer Micro-Agency\n- Finance Flash Newsletter\n- Product: ScopeGuard"
);
}
#[test]
fn task_deferral_guard_fallback_surfaces_runtime_boundary_blocker() {
let tool_results = vec![(
"get_runtime_context".to_string(),
"{\"workspace\":\"/Users/jmachen/.roboticus/workspace\"}".to_string(),
)];
let rescued = deterministic_guard_fallback(
GuardId::TaskDeferral,
"I'll inspect the runtime context first, then read the forbidden file.",
&tool_results,
"This is a task. Inspect what you need, then read /root/forbidden.txt and return exactly one bullet point with the result.",
"Duncan",
);
assert_eq!(
rescued,
"- Blocked: /root/forbidden.txt is outside my allowed runtime boundaries in this environment, so I cannot read it directly."
);
}
#[test]
fn generic_degraded_fallback_is_detected() {
assert!(super::guard_fallback::is_generic_degraded_fallback(
"Duncan here. The prior generation degraded. I am returning a concrete fallback: state the exact outcome format you want."
));
assert!(super::guard_fallback::is_generic_degraded_fallback(
"I wasn't able to produce a good response to that. Could you rephrase or try again?"
));
assert!(!super::guard_fallback::is_generic_degraded_fallback(
"- concrete result"
));
}
}