use crate::api::routes::agent::guard_registry::{Guard, GuardContext, GuardId, GuardVerdict};
pub(in crate::api::routes::agent) struct InternalProtocolGuard;
impl Guard for InternalProtocolGuard {
fn id(&self) -> GuardId {
GuardId::InternalProtocol
}
fn is_relevant(&self, _ctx: &GuardContext) -> bool {
true
}
fn evaluate(&self, content: &str, ctx: &GuardContext) -> GuardVerdict {
let lower = content.to_ascii_lowercase();
if !lower.contains("\"tool_call\"")
&& !lower.contains("\"toolcall\"")
&& !lower.contains("unexecuted_streaming_tool_call")
&& !lower.contains("delegated_subagent=")
&& !lower.contains("selected_subagent=")
&& !lower.contains("subtask ")
&& !(lower.contains("{\"name\":")
&& lower.contains("\"params\":")
&& lower.contains("\"content\":"))
{
return GuardVerdict::Pass;
}
let stripped = strip_internal_protocol_metadata(content);
if stripped.is_empty() {
return GuardVerdict::RetryRequested {
reason: format!(
"Response contained only internal protocol metadata with no \
user-facing content. As {}, respond naturally to the user's \
message in your own voice. Do not emit tool call JSON, \
delegation markers, or internal protocol.",
ctx.agent_name
),
};
}
GuardVerdict::Rewritten(stripped)
}
}
fn is_internal_delegation_metadata_line(line: &str) -> bool {
let t = line.trim();
if t.starts_with("delegated_subagent=")
|| t.starts_with("selected_subagent=")
|| t.starts_with("fallback_models=")
|| t.starts_with("notes=")
{
return true;
}
if let Some(rest) = t.strip_prefix("subtask ") {
let mut parts = rest.splitn(2, " -> ");
if let (Some(left), Some(_)) = (parts.next(), parts.next())
&& left.chars().all(|c| c.is_ascii_digit())
{
return true;
}
}
false
}
fn is_internal_orchestration_narrative_line(line: &str) -> bool {
let t = line.trim().to_ascii_lowercase();
t.starts_with("centralized delegation is sensible")
|| t.starts_with("decomposition gate decision")
|| t.starts_with("expected_utility_margin=")
|| t.starts_with("expected utility margin")
|| t.starts_with("delegation decision:")
|| t.starts_with("rationale:")
|| t.starts_with("subtasks:")
}
fn is_internal_tool_protocol_line(line: &str) -> bool {
let t = line.trim().to_ascii_lowercase();
t.contains(r#""tool_call""#)
|| t.starts_with("unexecuted_streaming_tool_call:")
|| t.starts_with("tool_call:")
|| t.starts_with("{\"tool_call\"")
|| t.starts_with("{\"toolcall\"")
|| (t.starts_with('{')
&& t.contains("\"name\":")
&& (t.contains("\"params\":") || t.contains("\"arguments\":")))
}
pub(in crate::api::routes::agent) fn contains_internal_protocol_marker(content: &str) -> bool {
let lower = content.to_ascii_lowercase();
let has_raw_tool_json = lower.contains("{\"name\":")
&& lower.contains("\"params\":")
&& lower.contains("\"content\":");
content.lines().any(|line| {
is_internal_delegation_metadata_line(line)
|| is_internal_orchestration_narrative_line(line)
|| is_internal_tool_protocol_line(line)
}) || has_raw_tool_json
}
fn strip_internal_protocol_metadata(content: &str) -> String {
content
.lines()
.filter(|line| {
!is_internal_delegation_metadata_line(line)
&& !is_internal_orchestration_narrative_line(line)
&& !is_internal_tool_protocol_line(line)
})
.collect::<Vec<_>>()
.join("\n")
.trim()
.to_string()
}