#![forbid(unsafe_code)]
pub mod anthropic_chat;
pub mod catalog;
pub mod catalog_authority;
pub mod catalog_authority_payload;
pub mod catalog_discovery;
mod continuity;
pub mod gemini_chat;
pub mod http_transport;
#[cfg(any(target_os = "linux", target_os = "macos"))]
pub mod local_credential_store;
#[cfg(not(any(target_os = "linux", target_os = "macos")))]
#[path = "local_credential_store_stub.rs"]
pub mod local_credential_store;
pub mod local_credential_types;
pub mod ollama_chat;
pub mod openai_compat_chat;
pub mod openrouter_chat;
pub mod usage_ledger;
pub use continuity::{
AdapterBoundaryError, AdapterCapabilityDispositions, AdapterDisposition,
BoundFixtureContinuityAdapter, FailureClass, FixtureAdapterKind, FixtureContinuityAdapter,
PreparedProviderDispatch, ProviderAdapter, ProviderFailureEvidence, ProviderRoute,
RawProviderResponse, classify_failure, classify_raw_provider_response,
};
use router_protocol::{
AttemptId, CallId, CanonicalEvent, CanonicalRequest, FunctionTool, HarnessInstanceId,
InterruptionCause, ItemId, OutputItemKind, ProviderCapabilities, PublicError, PublicErrorCode,
ReplayGuard, RequestId, ResponseId, SafeResumeBoundary, SessionId, ToolChoice, TurnId, Usage,
};
use serde_json::{Value, json};
use std::collections::HashMap;
use std::sync::{Arc, Mutex, Weak};
const MAX_PENDING_CALLS: usize = 128;
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub enum FixtureBehavior {
#[default]
Normal,
PreVisibleTransientFailure,
MalformedStreamBeforeVisibility,
MalformedStreamAfterVisibility,
TimeoutBeforeVisibility,
TimeoutAfterVisibility,
CancelledBeforeVisibility,
CancelledAfterVisibility,
PostVisibleFailure,
PostToolFailure,
}
#[derive(Clone, Debug, PartialEq)]
pub struct TransformedFixtureRequest {
pub fixture_wire: &'static str,
pub correlation: String,
pub messages: Vec<Value>,
pub tool_names: Vec<String>,
pub tool_selection: Option<ToolSelectionDiagnostics>,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum ToolSelectionStage {
ToolChoiceNone,
SafeZeroRequired,
ReviewedRequiredPrimitive,
NoReviewedSafeTool,
Ineligible,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum ReviewedSafeCategory {
CollaborationListAgents,
ListMcpResources,
ListMcpResourceTemplates,
GetGoal,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct ToolSelectionDiagnostics {
pub stage: ToolSelectionStage,
pub total_tools: usize,
pub zero_required_candidates: usize,
pub reviewed_safe_matches: usize,
pub reviewed_identity_matches: usize,
pub collaboration_wait_agent_matches: usize,
pub matched_category: Option<ReviewedSafeCategory>,
}
#[derive(Clone, Debug)]
struct CorrelationKey {
harness_instance: HarnessInstanceId,
session_id: SessionId,
turn_id: TurnId,
namespace: Option<String>,
name: String,
}
#[derive(Clone, Debug, PartialEq)]
struct SelectedFixtureTool {
namespace: Option<String>,
name: String,
arguments: Value,
}
#[derive(Default)]
struct ProviderState {
pending: HashMap<CallId, CorrelationKey>,
}
#[derive(Clone, Default)]
pub struct FixtureProvider {
state: Arc<Mutex<ProviderState>>,
}
pub struct PreparedAttempt {
pub attempt_id: AttemptId,
pub transformed: TransformedFixtureRequest,
pub events: Vec<CanonicalEvent>,
pending_guard: Option<PendingCallGuard>,
}
impl PreparedAttempt {
pub fn commit(mut self) {
if let Some(mut guard) = self.pending_guard.take() {
guard.committed = true;
}
}
}
struct PendingCallGuard {
state: Weak<Mutex<ProviderState>>,
call_id: CallId,
committed: bool,
}
impl Drop for PendingCallGuard {
fn drop(&mut self) {
if self.committed {
return;
}
if let Some(state) = self.state.upgrade()
&& let Ok(mut state) = state.lock()
{
state.pending.remove(&self.call_id);
}
}
}
impl FixtureProvider {
pub const CAPABILITIES: ProviderCapabilities = ProviderCapabilities {
images: true,
structured_output: true,
public_reasoning: true,
audio: false,
};
pub fn prepare(
&self,
request: &CanonicalRequest,
request_id: RequestId,
attempt_id: AttemptId,
behavior: FixtureBehavior,
) -> Result<PreparedAttempt, PublicError> {
request.validate().map_err(|error| {
let code = match error {
router_protocol::ValidationError::StreamingRequired => {
PublicErrorCode::UnsupportedWireCombination
}
router_protocol::ValidationError::NonTranslatableCapability => {
PublicErrorCode::UnsupportedCapability
}
router_protocol::ValidationError::InvalidIdentifier(_)
| router_protocol::ValidationError::UnsupportedWireVersion
| router_protocol::ValidationError::InvalidToolDefinition
| router_protocol::ValidationError::InvalidSpendAuthorization => {
PublicErrorCode::InvalidRequest
}
};
PublicError::redacted(code, request_id.clone(), false)
})?;
request
.capabilities
.preflight(Self::CAPABILITIES)
.map_err(|code| PublicError::redacted(code, request_id.clone(), false))?;
let transformed = Self::transform(request);
let response_id = ResponseId::parse(format!("resp-{attempt_id}")).map_err(|_| {
PublicError::redacted(PublicErrorCode::ProviderFailure, request_id.clone(), false)
})?;
if let Some(failure_kind) = pre_visible_failure_kind(behavior) {
let mut replay = ReplayGuard::default();
let forwarded_tool_result = request.function_output().is_some();
if forwarded_tool_result {
replay.mark_tool_result_forwarded();
}
let retryable =
failure_kind.retryable() && replay.authorize_transient_retry().is_some();
let error = if forwarded_tool_result {
PublicError::interrupted(
request_id,
failure_kind.interruption_cause(),
SafeResumeBoundary::ForwardedToolResult,
)
} else {
fixture_failure_error(request_id, failure_kind, retryable)
};
return Ok(PreparedAttempt {
attempt_id,
transformed,
events: vec![CanonicalEvent::ResponseFailed {
response_id,
sequence: 0,
error,
}],
pending_guard: None,
});
}
if let Some((call_id, _output)) = request.function_output() {
return self.prepare_final(
request,
request_id,
attempt_id,
response_id,
transformed,
call_id,
);
}
self.prepare_tool_call(
request,
request_id,
attempt_id,
response_id,
transformed,
behavior,
)
}
fn prepare_tool_call(
&self,
request: &CanonicalRequest,
request_id: RequestId,
attempt_id: AttemptId,
response_id: ResponseId,
mut transformed: TransformedFixtureRequest,
behavior: FixtureBehavior,
) -> Result<PreparedAttempt, PublicError> {
if matches!(request.tool_choice, ToolChoice::None) {
transformed.tool_selection = Some(ToolSelectionDiagnostics {
stage: ToolSelectionStage::ToolChoiceNone,
..tool_selection_diagnostics(&request.tools)
});
return prepare_no_tool_completion(
request_id,
attempt_id,
response_id,
transformed,
behavior,
);
}
let selectable_tools = match &request.tool_choice {
ToolChoice::Named(name) => request
.tools
.iter()
.find(|tool| tool.name == *name)
.map(std::slice::from_ref)
.unwrap_or_default(),
ToolChoice::Auto | ToolChoice::None | ToolChoice::Required => request.tools.as_slice(),
};
let selected_tool = match select_fixture_tool(selectable_tools) {
Ok((selected, diagnostics)) => {
transformed.tool_selection = Some(diagnostics);
selected
}
Err(diagnostics) => {
if matches!(request.tool_choice, ToolChoice::Auto | ToolChoice::None) {
transformed.tool_selection = Some(ToolSelectionDiagnostics {
stage: ToolSelectionStage::NoReviewedSafeTool,
..diagnostics
});
return prepare_no_tool_completion(
request_id,
attempt_id,
response_id,
transformed,
behavior,
);
}
let mut error = PublicError::redacted(
PublicErrorCode::UnsupportedCapability,
request_id,
false,
);
error.details = Some(json!({
"failure_stage": "fixture_tool_selector.ineligible",
"total_tools": diagnostics.total_tools,
"zero_required_candidates": diagnostics.zero_required_candidates,
"reviewed_safe_matches": diagnostics.reviewed_safe_matches,
"reviewed_identity_matches": diagnostics.reviewed_identity_matches,
"collaboration_wait_agent_matches": diagnostics.collaboration_wait_agent_matches
}));
return Err(error);
}
};
let tool_namespace = selected_tool.namespace;
let tool_name = selected_tool.name;
let tool_arguments = selected_tool.arguments;
let encoded_arguments = serde_json::to_string(&tool_arguments).map_err(|_| {
PublicError::redacted(PublicErrorCode::ProviderFailure, request_id.clone(), false)
})?;
let argument_boundary = encoded_arguments
.char_indices()
.map(|(index, _)| index)
.rfind(|index| *index <= encoded_arguments.len() / 2)
.unwrap_or(0);
let (argument_prefix, argument_suffix) = encoded_arguments.split_at(argument_boundary);
let call_id = CallId::parse(format!("call-{attempt_id}")).map_err(|_| {
PublicError::redacted(PublicErrorCode::ProviderFailure, request_id.clone(), false)
})?;
let item_text = ItemId::parse(format!("text-{attempt_id}")).map_err(|_| {
PublicError::redacted(PublicErrorCode::ProviderFailure, request_id.clone(), false)
})?;
let item_call = ItemId::parse(format!("tool-{attempt_id}")).map_err(|_| {
PublicError::redacted(PublicErrorCode::ProviderFailure, request_id.clone(), false)
})?;
let mut events = vec![
CanonicalEvent::ResponseCreated {
response_id: response_id.clone(),
request_id: request_id.clone(),
sequence: 0,
},
CanonicalEvent::ResponseInProgress {
response_id: response_id.clone(),
sequence: 1,
},
CanonicalEvent::OutputItemAdded {
response_id: response_id.clone(),
item_id: item_text.clone(),
sequence: 2,
item_kind: OutputItemKind::Message,
call_id: None,
name: None,
namespace: None,
},
CanonicalEvent::ContentPartAdded {
response_id: response_id.clone(),
item_id: item_text.clone(),
sequence: 3,
},
CanonicalEvent::OutputTextDelta {
response_id: response_id.clone(),
item_id: item_text.clone(),
sequence: 4,
delta: "Checking the fixture. ".to_owned(),
},
CanonicalEvent::OutputTextDone {
response_id: response_id.clone(),
item_id: item_text.clone(),
sequence: 5,
text: "Checking the fixture. ".to_owned(),
},
CanonicalEvent::ContentPartDone {
response_id: response_id.clone(),
item_id: item_text.clone(),
sequence: 6,
},
CanonicalEvent::OutputItemDone {
response_id: response_id.clone(),
item_id: item_text,
sequence: 7,
call_id: None,
},
];
let mut pending_guard = None;
if let Some(cause) = post_visible_failure_cause(behavior) {
events.push(CanonicalEvent::ResponseFailed {
response_id,
sequence: 8,
error: PublicError::interrupted(
request_id,
cause,
SafeResumeBoundary::PublicOutput,
),
});
} else {
{
let mut state = self.state.lock().map_err(|_| {
PublicError::redacted(
PublicErrorCode::RouterUnavailable,
request_id.clone(),
true,
)
})?;
if state.pending.len() >= MAX_PENDING_CALLS {
return Err(PublicError::redacted(
PublicErrorCode::RouterUnavailable,
request_id,
true,
));
}
state.pending.insert(
call_id.clone(),
CorrelationKey {
harness_instance: request.harness_instance.clone(),
session_id: request.session_id.clone(),
turn_id: request.turn_id.clone(),
namespace: tool_namespace.clone(),
name: tool_name.clone(),
},
);
}
pending_guard = Some(PendingCallGuard {
state: Arc::downgrade(&self.state),
call_id: call_id.clone(),
committed: false,
});
events.extend([
CanonicalEvent::OutputItemAdded {
response_id: response_id.clone(),
item_id: item_call.clone(),
sequence: 8,
item_kind: OutputItemKind::FunctionCall,
call_id: Some(call_id.clone()),
name: Some(tool_name),
namespace: tool_namespace,
},
CanonicalEvent::FunctionCallArgumentsDelta {
response_id: response_id.clone(),
item_id: item_call.clone(),
call_id: call_id.clone(),
sequence: 9,
delta: argument_prefix.to_owned(),
},
CanonicalEvent::FunctionCallArgumentsDelta {
response_id: response_id.clone(),
item_id: item_call.clone(),
call_id: call_id.clone(),
sequence: 10,
delta: argument_suffix.to_owned(),
},
CanonicalEvent::FunctionCallArgumentsDone {
response_id: response_id.clone(),
item_id: item_call.clone(),
call_id: call_id.clone(),
sequence: 11,
arguments: tool_arguments,
},
CanonicalEvent::OutputItemDone {
response_id: response_id.clone(),
item_id: item_call,
sequence: 12,
call_id: Some(call_id.clone()),
},
]);
if behavior == FixtureBehavior::PostToolFailure {
events.push(CanonicalEvent::ResponseFailed {
response_id,
sequence: 13,
error: PublicError::interrupted(
request_id,
InterruptionCause::ProviderFailure,
SafeResumeBoundary::ToolCall,
),
});
} else {
events.push(CanonicalEvent::ResponseCompleted {
response_id,
sequence: 13,
usage: Usage {
input_tokens: 17,
output_tokens: 11,
},
});
}
}
Ok(PreparedAttempt {
attempt_id,
transformed,
events,
pending_guard,
})
}
fn prepare_final(
&self,
request: &CanonicalRequest,
request_id: RequestId,
attempt_id: AttemptId,
response_id: ResponseId,
transformed: TransformedFixtureRequest,
call_id: &CallId,
) -> Result<PreparedAttempt, PublicError> {
let mut state = self.state.lock().map_err(|_| {
PublicError::redacted(PublicErrorCode::RouterUnavailable, request_id.clone(), true)
})?;
let expected = state.pending.get(call_id).ok_or_else(|| {
PublicError::redacted(
PublicErrorCode::CorrelationMismatch,
request_id.clone(),
false,
)
})?;
if expected.harness_instance != request.harness_instance
|| expected.session_id != request.session_id
|| expected.turn_id != request.turn_id
{
return Err(PublicError::redacted(
PublicErrorCode::CorrelationMismatch,
request_id,
false,
));
}
if let Some((namespace, name)) = request.function_call(call_id)
&& (namespace != expected.namespace.as_deref() || name != expected.name)
{
return Err(PublicError::redacted(
PublicErrorCode::CorrelationMismatch,
request_id,
false,
));
}
state.pending.remove(call_id);
drop(state);
let item_id = ItemId::parse(format!("final-{attempt_id}")).map_err(|_| {
PublicError::redacted(PublicErrorCode::ProviderFailure, request_id.clone(), false)
})?;
let events = vec![
CanonicalEvent::ResponseCreated {
response_id: response_id.clone(),
request_id,
sequence: 0,
},
CanonicalEvent::ResponseInProgress {
response_id: response_id.clone(),
sequence: 1,
},
CanonicalEvent::OutputItemAdded {
response_id: response_id.clone(),
item_id: item_id.clone(),
sequence: 2,
item_kind: OutputItemKind::Message,
call_id: None,
name: None,
namespace: None,
},
CanonicalEvent::ContentPartAdded {
response_id: response_id.clone(),
item_id: item_id.clone(),
sequence: 3,
},
CanonicalEvent::OutputTextDelta {
response_id: response_id.clone(),
item_id: item_id.clone(),
sequence: 4,
delta: "Fixture result accepted.".to_owned(),
},
CanonicalEvent::OutputTextDone {
response_id: response_id.clone(),
item_id: item_id.clone(),
sequence: 5,
text: "Fixture result accepted.".to_owned(),
},
CanonicalEvent::ContentPartDone {
response_id: response_id.clone(),
item_id: item_id.clone(),
sequence: 6,
},
CanonicalEvent::OutputItemDone {
response_id: response_id.clone(),
item_id,
sequence: 7,
call_id: None,
},
CanonicalEvent::ResponseCompleted {
response_id,
sequence: 8,
usage: Usage {
input_tokens: 9,
output_tokens: 4,
},
},
];
Ok(PreparedAttempt {
attempt_id,
transformed,
events,
pending_guard: None,
})
}
fn transform(request: &CanonicalRequest) -> TransformedFixtureRequest {
TransformedFixtureRequest {
fixture_wire: "fixture.vendor.v2",
correlation: format!("{}:{}", request.session_id, request.turn_id),
messages: request
.input
.iter()
.map(|item| serde_json::to_value(item).unwrap_or(Value::Null))
.collect(),
tool_names: request
.tools
.iter()
.map(|tool| match &tool.namespace {
Some(namespace) => format!("{namespace}.{}", tool.name),
None => tool.name.clone(),
})
.collect(),
tool_selection: None,
}
}
pub fn pending_calls(&self) -> usize {
self.state.lock().map_or(0, |state| state.pending.len())
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
enum FixtureFailureKind {
TransientProvider,
MalformedStream,
Timeout,
Cancelled,
}
impl FixtureFailureKind {
const fn as_str(self) -> &'static str {
match self {
Self::TransientProvider => "transient_provider_failure",
Self::MalformedStream => "malformed_stream",
Self::Timeout => "timeout",
Self::Cancelled => "cancelled",
}
}
const fn retryable(self) -> bool {
matches!(self, Self::TransientProvider | Self::Timeout)
}
const fn interruption_cause(self) -> InterruptionCause {
match self {
Self::TransientProvider => InterruptionCause::ProviderFailure,
Self::MalformedStream => InterruptionCause::MalformedStream,
Self::Timeout => InterruptionCause::Timeout,
Self::Cancelled => InterruptionCause::Cancelled,
}
}
}
fn pre_visible_failure_kind(behavior: FixtureBehavior) -> Option<FixtureFailureKind> {
match behavior {
FixtureBehavior::PreVisibleTransientFailure => Some(FixtureFailureKind::TransientProvider),
FixtureBehavior::MalformedStreamBeforeVisibility => {
Some(FixtureFailureKind::MalformedStream)
}
FixtureBehavior::TimeoutBeforeVisibility => Some(FixtureFailureKind::Timeout),
FixtureBehavior::CancelledBeforeVisibility => Some(FixtureFailureKind::Cancelled),
FixtureBehavior::Normal
| FixtureBehavior::MalformedStreamAfterVisibility
| FixtureBehavior::TimeoutAfterVisibility
| FixtureBehavior::CancelledAfterVisibility
| FixtureBehavior::PostVisibleFailure
| FixtureBehavior::PostToolFailure => None,
}
}
fn post_visible_failure_cause(behavior: FixtureBehavior) -> Option<InterruptionCause> {
match behavior {
FixtureBehavior::PostVisibleFailure => Some(InterruptionCause::ProviderFailure),
FixtureBehavior::MalformedStreamAfterVisibility => Some(InterruptionCause::MalformedStream),
FixtureBehavior::TimeoutAfterVisibility => Some(InterruptionCause::Timeout),
FixtureBehavior::CancelledAfterVisibility => Some(InterruptionCause::Cancelled),
FixtureBehavior::Normal
| FixtureBehavior::PreVisibleTransientFailure
| FixtureBehavior::MalformedStreamBeforeVisibility
| FixtureBehavior::TimeoutBeforeVisibility
| FixtureBehavior::CancelledBeforeVisibility
| FixtureBehavior::PostToolFailure => None,
}
}
fn fixture_failure_error(
request_id: RequestId,
kind: FixtureFailureKind,
retryable: bool,
) -> PublicError {
let mut error = PublicError::redacted(PublicErrorCode::ProviderFailure, request_id, retryable);
error.details = Some(json!({
"fixture_failure": {
"kind": kind.as_str(),
"visibility": "pre_visible",
"retry_eligible": retryable
}
}));
error
}
fn prepare_no_tool_completion(
request_id: RequestId,
attempt_id: AttemptId,
response_id: ResponseId,
transformed: TransformedFixtureRequest,
behavior: FixtureBehavior,
) -> Result<PreparedAttempt, PublicError> {
let item_id = ItemId::parse(format!("no-tool-{attempt_id}")).map_err(|_| {
PublicError::redacted(PublicErrorCode::ProviderFailure, request_id.clone(), false)
})?;
let text = "Bounded fixture completed without a tool.".to_owned();
let mut events = vec![
CanonicalEvent::ResponseCreated {
response_id: response_id.clone(),
request_id: request_id.clone(),
sequence: 0,
},
CanonicalEvent::ResponseInProgress {
response_id: response_id.clone(),
sequence: 1,
},
CanonicalEvent::OutputItemAdded {
response_id: response_id.clone(),
item_id: item_id.clone(),
sequence: 2,
item_kind: OutputItemKind::Message,
call_id: None,
name: None,
namespace: None,
},
CanonicalEvent::ContentPartAdded {
response_id: response_id.clone(),
item_id: item_id.clone(),
sequence: 3,
},
CanonicalEvent::OutputTextDelta {
response_id: response_id.clone(),
item_id: item_id.clone(),
sequence: 4,
delta: text.clone(),
},
CanonicalEvent::OutputTextDone {
response_id: response_id.clone(),
item_id: item_id.clone(),
sequence: 5,
text,
},
CanonicalEvent::ContentPartDone {
response_id: response_id.clone(),
item_id: item_id.clone(),
sequence: 6,
},
CanonicalEvent::OutputItemDone {
response_id: response_id.clone(),
item_id,
sequence: 7,
call_id: None,
},
];
if let Some(cause) = post_visible_failure_cause(behavior) {
events.push(CanonicalEvent::ResponseFailed {
response_id,
sequence: 8,
error: PublicError::interrupted(request_id, cause, SafeResumeBoundary::PublicOutput),
});
} else {
events.push(CanonicalEvent::ResponseCompleted {
response_id,
sequence: 8,
usage: Usage {
input_tokens: 7,
output_tokens: 6,
},
});
}
Ok(PreparedAttempt {
attempt_id,
transformed,
events,
pending_guard: None,
})
}
fn select_fixture_tool(
tools: &[FunctionTool],
) -> Result<(SelectedFixtureTool, ToolSelectionDiagnostics), ToolSelectionDiagnostics> {
let base = tool_selection_diagnostics(tools);
for category in [
ReviewedSafeCategory::CollaborationListAgents,
ReviewedSafeCategory::ListMcpResources,
ReviewedSafeCategory::ListMcpResourceTemplates,
ReviewedSafeCategory::GetGoal,
] {
if let Some(tool) = tools.iter().find(|tool| {
reviewed_safe_category(tool) == Some(category)
&& schema_accepts_empty_object(&tool.parameters)
}) {
return Ok((
SelectedFixtureTool {
namespace: tool.namespace.clone(),
name: tool.name.clone(),
arguments: json!({}),
},
ToolSelectionDiagnostics {
stage: ToolSelectionStage::SafeZeroRequired,
matched_category: Some(category),
..base
},
));
}
}
if let Some((tool, arguments, category)) = tools.iter().find_map(|tool| {
reviewed_required_arguments(tool).map(|(arguments, category)| (tool, arguments, category))
}) {
return Ok((
SelectedFixtureTool {
namespace: tool.namespace.clone(),
name: tool.name.clone(),
arguments,
},
ToolSelectionDiagnostics {
stage: ToolSelectionStage::ReviewedRequiredPrimitive,
matched_category: category,
..base
},
));
}
Err(base)
}
fn tool_selection_diagnostics(tools: &[FunctionTool]) -> ToolSelectionDiagnostics {
let zero_required_candidates = tools
.iter()
.filter(|tool| schema_accepts_empty_object(&tool.parameters))
.count();
let reviewed_safe_matches = tools
.iter()
.filter(|tool| {
reviewed_safe_category(tool).is_some() && schema_accepts_empty_object(&tool.parameters)
})
.count();
let reviewed_identity_matches = tools
.iter()
.filter(|tool| reviewed_safe_category(tool).is_some())
.count();
let collaboration_wait_agent_matches = tools
.iter()
.filter(|tool| {
tool.namespace.as_deref() == Some("collaboration")
&& tool.name == "wait_agent"
&& schema_accepts_empty_object(&tool.parameters)
})
.count();
ToolSelectionDiagnostics {
stage: ToolSelectionStage::Ineligible,
total_tools: tools.len(),
zero_required_candidates,
reviewed_safe_matches,
reviewed_identity_matches,
collaboration_wait_agent_matches,
matched_category: None,
}
}
fn reviewed_safe_category(tool: &FunctionTool) -> Option<ReviewedSafeCategory> {
match (tool.namespace.as_deref(), tool.name.as_str()) {
(Some("collaboration"), "list_agents") => {
Some(ReviewedSafeCategory::CollaborationListAgents)
}
(None, "list_mcp_resources") => Some(ReviewedSafeCategory::ListMcpResources),
(None, "list_mcp_resource_templates") => {
Some(ReviewedSafeCategory::ListMcpResourceTemplates)
}
(None, "get_goal") => Some(ReviewedSafeCategory::GetGoal),
_ => None,
}
}
fn schema_accepts_empty_object(schema: &Value) -> bool {
if let Some(accepted) = schema.as_bool() {
return accepted;
}
let Some(schema) = schema.as_object() else {
return false;
};
if schema.contains_key("$ref") || schema.contains_key("$dynamicRef") {
return false;
}
if let Some(schema_type) = schema.get("type") {
let accepts_object = schema_type.as_str() == Some("object")
|| schema_type
.as_array()
.is_some_and(|types| types.iter().any(|schema_type| schema_type == "object"));
if !accepts_object {
return false;
}
}
if schema
.get("const")
.is_some_and(|constant| constant != &json!({}))
{
return false;
}
if schema
.get("enum")
.and_then(Value::as_array)
.is_some_and(|values| !values.iter().any(|value| value == &json!({})))
{
return false;
}
if schema
.get("required")
.and_then(Value::as_array)
.is_some_and(|required| !required.is_empty())
{
return false;
}
if schema
.get("minProperties")
.and_then(Value::as_u64)
.is_some_and(|minimum| minimum > 0)
{
return false;
}
if schema
.get("allOf")
.and_then(Value::as_array)
.is_some_and(|schemas| !schemas.iter().all(schema_accepts_empty_object))
{
return false;
}
if schema
.get("anyOf")
.and_then(Value::as_array)
.is_some_and(|schemas| !schemas.iter().any(schema_accepts_empty_object))
{
return false;
}
if schema
.get("oneOf")
.and_then(Value::as_array)
.is_some_and(|schemas| {
schemas
.iter()
.filter(|schema| schema_accepts_empty_object(schema))
.count()
!= 1
})
{
return false;
}
if schema.get("not").is_some_and(schema_accepts_empty_object) {
return false;
}
if let Some(condition) = schema.get("if") {
let branch = if schema_accepts_empty_object(condition) {
schema.get("then")
} else {
schema.get("else")
};
if branch.is_some_and(|branch| !schema_accepts_empty_object(branch)) {
return false;
}
}
true
}
fn reviewed_required_arguments(
tool: &FunctionTool,
) -> Option<(Value, Option<ReviewedSafeCategory>)> {
if tool.namespace.as_deref() == Some("collaboration") && tool.name == "list_agents" {
return reviewed_single_string_argument(tool, "path_prefix", "").map(|arguments| {
(
arguments,
Some(ReviewedSafeCategory::CollaborationListAgents),
)
});
}
if tool.namespace.is_none() && tool.name == "get_weather" {
return reviewed_single_string_argument(tool, "city", "San Francisco")
.map(|arguments| (arguments, None));
}
None
}
fn reviewed_single_string_argument(
tool: &FunctionTool,
property_name: &str,
value: &str,
) -> Option<Value> {
let arguments = Value::Object(serde_json::Map::from_iter([(
property_name.to_owned(),
Value::String(value.to_owned()),
)]));
arguments_validate_against_reviewed_schema(&arguments, &tool.parameters, property_name)
.then_some(arguments)
}
fn arguments_validate_against_reviewed_schema(
arguments: &Value,
schema: &Value,
property_name: &str,
) -> bool {
let (Some(arguments), Some(schema)) = (arguments.as_object(), schema.as_object()) else {
return false;
};
if schema.get("type").and_then(Value::as_str) != Some("object") {
return false;
}
let Some(properties) = schema.get("properties").and_then(Value::as_object) else {
return false;
};
let Some(required) = schema.get("required").and_then(Value::as_array) else {
return false;
};
if required.len() != 1 || required[0].as_str() != Some(property_name) {
return false;
}
let Some(property) = properties.get(property_name).and_then(Value::as_object) else {
return false;
};
if property.get("type").and_then(Value::as_str) != Some("string")
|| property.contains_key("pattern")
|| property.contains_key("enum")
|| property.contains_key("const")
|| property
.get("minLength")
.and_then(Value::as_u64)
.is_some_and(|minimum| minimum > 0)
{
return false;
}
arguments.len() == 1 && arguments.get(property_name).is_some_and(Value::is_string)
}
pub fn event_stream_is_retryable(events: &[CanonicalEvent]) -> bool {
let mut guard = ReplayGuard::default();
for event in events {
guard.observe(event.visibility());
if matches!(
event,
CanonicalEvent::ResponseFailed { error, .. } | CanonicalEvent::Error { error, .. }
if !error.retryable
) {
return false;
}
}
guard.authorize_transient_retry().is_some()
}
#[cfg(test)]
mod tests {
use super::*;
use router_protocol::{
CapabilityRequirements, ContentBlock, FunctionTool, HarnessInstanceId, InputItem,
M1_WIRE_VERSION, MessageRole, ReasoningIntent, ToolChoice,
};
fn request(session: &str, turn: &str, output: Option<CallId>) -> CanonicalRequest {
let input = if let Some(call_id) = output {
vec![InputItem::FunctionCallOutput {
call_id,
output: json!({"temperature": 61}),
}]
} else {
vec![InputItem::Message {
role: MessageRole::User,
content: vec![ContentBlock::Text {
text: "synthetic weather request".to_owned(),
}],
}]
};
CanonicalRequest {
wire_version: M1_WIRE_VERSION.to_owned(),
model: "fixture-transformed".to_owned(),
stream: true,
harness_instance: HarnessInstanceId::parse("synthetic-cli").expect("fixture id"),
session_id: SessionId::parse(session).expect("fixture id"),
turn_id: TurnId::parse(turn).expect("fixture id"),
input,
tools: vec![FunctionTool {
namespace: None,
name: "get_weather".to_owned(),
description: "reviewed fixture tool".to_owned(),
parameters: json!({
"type": "object",
"properties": {"city": {"type": "string"}},
"required": ["city"],
"additionalProperties": false
}),
strict: true,
}],
tool_choice: ToolChoice::Auto,
structured_output: None,
reasoning: ReasoningIntent::PublicSummary,
max_output_tokens: Some(64),
max_spend_micro_usd: None,
capabilities: CapabilityRequirements {
images: false,
structured_output: false,
public_reasoning: true,
audio: false,
},
}
}
fn ids(number: u64) -> (RequestId, AttemptId) {
(
RequestId::parse(format!("request-{number}")).expect("fixture id"),
AttemptId::parse(format!("attempt-{number}")).expect("fixture id"),
)
}
#[test]
fn transformed_two_request_tool_loop_preserves_call_id() {
let provider = FixtureProvider::default();
let (request_id, attempt_id) = ids(1);
let first = provider
.prepare(
&request("session-1", "turn-1", None),
request_id,
attempt_id,
FixtureBehavior::Normal,
)
.expect("first request should prepare");
assert_eq!(first.transformed.fixture_wire, "fixture.vendor.v2");
let call_id = first
.events
.iter()
.find_map(|event| match event {
CanonicalEvent::FunctionCallArgumentsDone { call_id, .. } => Some(call_id.clone()),
_ => None,
})
.expect("fixture must emit function call");
first.commit();
let (request_id, attempt_id) = ids(2);
let second = provider
.prepare(
&request("session-1", "turn-1", Some(call_id.clone())),
request_id,
attempt_id,
FixtureBehavior::Normal,
)
.expect("correlated output should prepare");
assert!(matches!(
second.events.last(),
Some(CanonicalEvent::ResponseCompleted { .. })
));
assert_eq!(provider.pending_calls(), 0);
let (request_id, attempt_id) = ids(3);
let mismatch = provider.prepare(
&request("session-2", "turn-1", Some(call_id)),
request_id,
attempt_id,
FixtureBehavior::Normal,
);
match mismatch {
Err(error) => assert_eq!(error.code, PublicErrorCode::CorrelationMismatch),
Ok(_) => panic!("call id reuse must fail"),
}
}
#[test]
fn failures_obey_visibility_replay_boundary() {
let provider = FixtureProvider::default();
for (number, behavior, expected_retryable) in [
(10, FixtureBehavior::PreVisibleTransientFailure, true),
(11, FixtureBehavior::MalformedStreamBeforeVisibility, false),
(12, FixtureBehavior::TimeoutBeforeVisibility, true),
(13, FixtureBehavior::CancelledBeforeVisibility, false),
(14, FixtureBehavior::PostVisibleFailure, false),
(15, FixtureBehavior::MalformedStreamAfterVisibility, false),
(16, FixtureBehavior::TimeoutAfterVisibility, false),
(17, FixtureBehavior::PostToolFailure, false),
(18, FixtureBehavior::CancelledAfterVisibility, false),
] {
let (request_id, attempt_id) = ids(number);
let attempt = provider
.prepare(
&request("session-failure", &format!("turn-{number}"), None),
request_id,
attempt_id,
behavior,
)
.expect("failure fixture should prepare");
assert_eq!(
event_stream_is_retryable(&attempt.events),
expected_retryable
);
}
}
#[test]
fn malformed_timeout_and_cancellation_fixtures_are_terminal_and_explicit() {
let provider = FixtureProvider::default();
for (number, behavior, expected_kind, expected_retryable) in [
(
60,
FixtureBehavior::MalformedStreamBeforeVisibility,
"malformed_stream",
false,
),
(
61,
FixtureBehavior::TimeoutBeforeVisibility,
"timeout",
true,
),
(
62,
FixtureBehavior::CancelledBeforeVisibility,
"cancelled",
false,
),
] {
let (request_id, attempt_id) = ids(number);
let attempt = provider
.prepare(
&request("session-fixture", &format!("turn-{number}"), None),
request_id,
attempt_id,
behavior,
)
.expect("terminal fixture");
let error = match attempt.events.as_slice() {
[CanonicalEvent::ResponseFailed { error, .. }] => error,
_ => panic!("pre-visible fixture must emit one terminal failure"),
};
assert_eq!(
error
.details
.as_ref()
.and_then(|details| { details["fixture_failure"]["kind"].as_str() }),
Some(expected_kind)
);
assert_eq!(error.retryable, expected_retryable);
assert_eq!(
event_stream_is_retryable(&attempt.events),
expected_retryable
);
}
for (number, behavior, expected_cause) in [
(
63,
FixtureBehavior::MalformedStreamAfterVisibility,
"malformed_stream",
),
(64, FixtureBehavior::TimeoutAfterVisibility, "timeout"),
(65, FixtureBehavior::CancelledAfterVisibility, "cancelled"),
] {
let (request_id, attempt_id) = ids(number);
let attempt = provider
.prepare(
&request("session-fixture", &format!("turn-{number}"), None),
request_id,
attempt_id,
behavior,
)
.expect("post-visible terminal fixture");
let error = match attempt.events.last() {
Some(CanonicalEvent::ResponseFailed { error, .. }) => error,
_ => panic!("post-visible fixture must end in failure"),
};
assert_eq!(
error
.details
.as_ref()
.and_then(|details| { details["interruption"]["cause"].as_str() }),
Some(expected_cause)
);
assert!(!error.retryable);
assert!(!event_stream_is_retryable(&attempt.events));
attempt.commit();
assert_eq!(provider.pending_calls(), 0);
}
}
#[test]
fn post_boundary_failures_return_fixed_safe_resume_without_replay() {
let provider = FixtureProvider::default();
for (number, behavior, boundary) in [
(70, FixtureBehavior::PostVisibleFailure, "public_output"),
(71, FixtureBehavior::PostToolFailure, "tool_call"),
] {
let (request_id, attempt_id) = ids(number);
let attempt = provider
.prepare(
&request("session-resume", &format!("turn-{number}"), None),
request_id,
attempt_id,
behavior,
)
.expect("safe-resume fixture");
assert_safe_resume(attempt.events.last(), boundary);
assert!(!event_stream_is_retryable(&attempt.events));
}
let (request_id, attempt_id) = ids(72);
let attempt = provider
.prepare(
&request(
"session-resume",
"turn-forwarded-result",
Some(CallId::parse("call-prior").expect("fixture id")),
),
request_id,
attempt_id,
FixtureBehavior::PreVisibleTransientFailure,
)
.expect("forwarded-result failure fixture");
assert_safe_resume(attempt.events.last(), "forwarded_tool_result");
assert!(!event_stream_is_retryable(&attempt.events));
let (request_id, attempt_id) = ids(73);
let cancelled = provider
.prepare(
&request(
"session-resume",
"turn-forwarded-result-cancelled",
Some(CallId::parse("call-prior-cancelled").expect("fixture id")),
),
request_id,
attempt_id,
FixtureBehavior::CancelledBeforeVisibility,
)
.expect("forwarded-result cancellation fixture");
assert_safe_resume(cancelled.events.last(), "forwarded_tool_result");
assert!(!event_stream_is_retryable(&cancelled.events));
}
fn assert_safe_resume(event: Option<&CanonicalEvent>, expected_boundary: &str) {
let error = match event {
Some(CanonicalEvent::ResponseFailed { error, .. }) => error,
_ => panic!("expected terminal response failure"),
};
let interruption =
&error.details.as_ref().expect("structured interruption")["interruption"];
assert_eq!(interruption["boundary"], expected_boundary);
assert_eq!(interruption["automatic_replay"], false);
assert_eq!(interruption["safe_resume"]["action"], "start_new_turn");
assert_eq!(
interruption["safe_resume"]["repeat_completed_tool_calls"],
false
);
assert!(!error.retryable);
}
#[test]
fn forwarded_tool_result_is_never_marked_retryable() {
let provider = FixtureProvider::default();
let (request_id, attempt_id) = ids(13);
let attempt = provider
.prepare(
&request(
"session-result",
"turn-result",
Some(CallId::parse("call-prior").expect("fixture id")),
),
request_id,
attempt_id,
FixtureBehavior::PreVisibleTransientFailure,
)
.expect("pre-visible failure fixture");
assert!(!event_stream_is_retryable(&attempt.events));
assert!(matches!(
attempt.events.last(),
Some(CanonicalEvent::ResponseFailed { error, .. }) if !error.retryable
));
}
#[test]
fn uncommitted_attempt_releases_pending_correlation() {
let provider = FixtureProvider::default();
let (request_id, attempt_id) = ids(20);
let attempt = provider
.prepare(
&request("session-cancel", "turn-cancel", None),
request_id,
attempt_id,
FixtureBehavior::Normal,
)
.expect("request should prepare");
assert_eq!(provider.pending_calls(), 1);
drop(attempt);
assert_eq!(provider.pending_calls(), 0);
}
#[test]
fn pending_correlation_is_bounded() {
let provider = FixtureProvider::default();
for number in 0..MAX_PENDING_CALLS as u64 {
let (request_id, attempt_id) = ids(1000 + number);
provider
.prepare(
&request("session-cap", &format!("turn-{number}"), None),
request_id,
attempt_id,
FixtureBehavior::Normal,
)
.expect("pending fixture within cap")
.commit();
}
assert_eq!(provider.pending_calls(), MAX_PENDING_CALLS);
let (request_id, attempt_id) = ids(2000);
let error = match provider.prepare(
&request("session-cap", "turn-overflow", None),
request_id,
attempt_id,
FixtureBehavior::Normal,
) {
Err(error) => error,
Ok(_) => panic!("pending correlation cap must fail closed"),
};
assert_eq!(error.code, PublicErrorCode::RouterUnavailable);
assert_eq!(provider.pending_calls(), MAX_PENDING_CALLS);
}
#[test]
fn correlation_is_bound_to_harness_instance() {
let provider = FixtureProvider::default();
let (request_id, attempt_id) = ids(21);
let first = provider
.prepare(
&request("shared-session", "shared-turn", None),
request_id,
attempt_id,
FixtureBehavior::Normal,
)
.expect("initial call");
let call_id = first
.events
.iter()
.find_map(|event| match event {
CanonicalEvent::FunctionCallArgumentsDone { call_id, .. } => Some(call_id.clone()),
_ => None,
})
.expect("fixture call ID");
first.commit();
let mut other_harness = request("shared-session", "shared-turn", Some(call_id));
other_harness.harness_instance =
HarnessInstanceId::parse("synthetic-desktop").expect("fixture id");
let (request_id, attempt_id) = ids(22);
let error = match provider.prepare(
&other_harness,
request_id,
attempt_id,
FixtureBehavior::Normal,
) {
Err(error) => error,
Ok(_) => panic!("cross-harness correlation must fail"),
};
assert_eq!(error.code, PublicErrorCode::CorrelationMismatch);
}
#[test]
fn namespaced_tool_identity_is_emitted_and_checked_during_correlation() {
let provider = FixtureProvider::default();
let mut initial = request("session-namespace", "turn-namespace", None);
initial.tools = vec![FunctionTool {
namespace: Some("collaboration".to_owned()),
name: "list_agents".to_owned(),
description: "fixture namespace tool".to_owned(),
parameters: json!({
"type": "object",
"properties": {"path_prefix": {"type": "string"}},
"required": [],
"additionalProperties": false
}),
strict: true,
}];
initial.tool_choice = ToolChoice::Required;
let (request_id, attempt_id) = ids(30);
let first = provider
.prepare(&initial, request_id, attempt_id, FixtureBehavior::Normal)
.expect("namespaced request");
assert_eq!(first.transformed.tool_names, ["collaboration.list_agents"]);
let (call_id, emitted_namespace, emitted_name) = first
.events
.iter()
.find_map(|event| match event {
CanonicalEvent::OutputItemAdded {
item_kind: OutputItemKind::FunctionCall,
call_id: Some(call_id),
namespace,
name: Some(name),
..
} => Some((call_id.clone(), namespace.clone(), name.clone())),
_ => None,
})
.expect("namespaced function call event");
assert_eq!(emitted_namespace.as_deref(), Some("collaboration"));
assert_eq!(emitted_name, "list_agents");
assert!(first.events.iter().any(|event| matches!(
event,
CanonicalEvent::FunctionCallArgumentsDone { arguments, .. }
if arguments == &json!({})
)));
first.commit();
let mut mismatch = request("session-namespace", "turn-namespace", Some(call_id.clone()));
mismatch.input.insert(
0,
InputItem::FunctionCall {
call_id: call_id.clone(),
namespace: Some("different_namespace".to_owned()),
name: "list_agents".to_owned(),
arguments: json!({}),
},
);
let (request_id, attempt_id) = ids(31);
let error =
match provider.prepare(&mismatch, request_id, attempt_id, FixtureBehavior::Normal) {
Err(error) => error,
Ok(_) => panic!("namespace mismatch must fail"),
};
assert_eq!(error.code, PublicErrorCode::CorrelationMismatch);
let mut followup = request("session-namespace", "turn-namespace", Some(call_id.clone()));
followup.input.insert(
0,
InputItem::FunctionCall {
call_id,
namespace: Some("collaboration".to_owned()),
name: "list_agents".to_owned(),
arguments: json!({}),
},
);
let (request_id, attempt_id) = ids(32);
let second = provider
.prepare(&followup, request_id, attempt_id, FixtureBehavior::Normal)
.expect("matching namespace correlation");
assert!(matches!(
second.events.last(),
Some(CanonicalEvent::ResponseCompleted { .. })
));
}
#[test]
fn required_safe_zero_required_namespaced_tool_wins_over_effectful_tool() {
let provider = FixtureProvider::default();
let mut initial = request("session-safe-selection", "turn-safe-selection", None);
initial.tools = vec![
FunctionTool {
namespace: Some("collaboration".to_owned()),
name: "send_message".to_owned(),
description: "effectful fixture tool".to_owned(),
parameters: json!({
"type": "object",
"properties": {
"target": {"type": "string"},
"message": {"type": "string"}
},
"required": [],
"additionalProperties": false
}),
strict: true,
},
FunctionTool {
namespace: Some("collaboration".to_owned()),
name: "list_agents".to_owned(),
description: "read-only fixture tool".to_owned(),
parameters: json!({
"type": "object",
"properties": {"path_prefix": {"type": "string"}},
"required": [],
"additionalProperties": false
}),
strict: true,
},
];
initial.tool_choice = ToolChoice::Required;
let selected_schema = initial.tools[1].parameters.clone();
let (request_id, attempt_id) = ids(40);
let attempt = provider
.prepare(&initial, request_id, attempt_id, FixtureBehavior::Normal)
.expect("safe tool is eligible");
let (namespace, name) = attempt
.events
.iter()
.find_map(|event| match event {
CanonicalEvent::OutputItemAdded {
item_kind: OutputItemKind::FunctionCall,
namespace,
name: Some(name),
..
} => Some((namespace.clone(), name.clone())),
_ => None,
})
.expect("selected function item");
let arguments = attempt
.events
.iter()
.find_map(|event| match event {
CanonicalEvent::FunctionCallArgumentsDone { arguments, .. } => {
Some(arguments.clone())
}
_ => None,
})
.expect("function arguments");
assert_eq!(namespace.as_deref(), Some("collaboration"));
assert_eq!(name, "list_agents");
assert_eq!(arguments, json!({}));
assert!(schema_accepts_empty_object(&selected_schema));
assert_eq!(
attempt.transformed.tool_selection,
Some(ToolSelectionDiagnostics {
stage: ToolSelectionStage::SafeZeroRequired,
total_tools: 2,
zero_required_candidates: 2,
reviewed_safe_matches: 1,
reviewed_identity_matches: 1,
collaboration_wait_agent_matches: 0,
matched_category: Some(ReviewedSafeCategory::CollaborationListAgents),
})
);
}
#[test]
fn named_choice_cannot_silently_select_a_different_tool() {
let provider = FixtureProvider::default();
let mut initial = request("session-named", "turn-named", None);
initial.tools = vec![
FunctionTool {
namespace: None,
name: "list_mcp_resources".to_owned(),
description: "first safe fixture".to_owned(),
parameters: json!({"type": "object", "required": []}),
strict: true,
},
FunctionTool {
namespace: None,
name: "get_goal".to_owned(),
description: "named safe fixture".to_owned(),
parameters: json!({"type": "object", "required": []}),
strict: true,
},
];
initial.tool_choice = ToolChoice::Named("get_goal".to_owned());
let (request_id, attempt_id) = ids(41);
let attempt = provider
.prepare(&initial, request_id, attempt_id, FixtureBehavior::Normal)
.expect("named safe tool is eligible");
assert!(attempt.events.iter().any(|event| matches!(
event,
CanonicalEvent::OutputItemAdded {
item_kind: OutputItemKind::FunctionCall,
name: Some(name),
..
} if name == "get_goal"
)));
}
#[test]
fn auto_degrades_without_calling_an_effectful_zero_required_tool() {
let provider = FixtureProvider::default();
let mut initial = request("session-effectful", "turn-effectful", None);
initial.tools = vec![FunctionTool {
namespace: Some("collaboration".to_owned()),
name: "send_message".to_owned(),
description: "effectful fixture tool".to_owned(),
parameters: json!({
"type": "object",
"properties": {
"target": {"type": "string"},
"message": {"type": "string"}
},
"required": [],
"additionalProperties": false
}),
strict: true,
}];
let (request_id, attempt_id) = ids(41);
let attempt = provider
.prepare(&initial, request_id, attempt_id, FixtureBehavior::Normal)
.expect("auto may use the reviewed no-tool degradation");
assert!(!attempt.events.iter().any(|event| matches!(
event,
CanonicalEvent::OutputItemAdded {
item_kind: OutputItemKind::FunctionCall,
..
}
)));
assert!(attempt.events.iter().any(|event| matches!(
event,
CanonicalEvent::OutputTextDone { text, .. }
if text == "Bounded fixture completed without a tool."
)));
assert!(matches!(
attempt.events.last(),
Some(CanonicalEvent::ResponseCompleted { .. })
));
assert_eq!(
attempt.transformed.tool_selection,
Some(ToolSelectionDiagnostics {
stage: ToolSelectionStage::NoReviewedSafeTool,
total_tools: 1,
zero_required_candidates: 1,
reviewed_safe_matches: 0,
reviewed_identity_matches: 0,
collaboration_wait_agent_matches: 0,
matched_category: None,
})
);
assert_eq!(provider.pending_calls(), 0);
}
#[test]
fn auto_selects_a_declared_reviewed_tool_and_never_invents_one() {
let provider = FixtureProvider::default();
let initial = request("session-auto", "turn-auto", None);
let (request_id, attempt_id) = ids(73);
let attempt = provider
.prepare(&initial, request_id, attempt_id, FixtureBehavior::Normal)
.expect("declared reviewed tool is eligible for auto");
assert!(attempt.events.iter().any(|event| matches!(
event,
CanonicalEvent::OutputItemAdded {
item_kind: OutputItemKind::FunctionCall,
namespace: None,
name: Some(name),
..
} if name == "get_weather"
)));
assert_eq!(
attempt
.transformed
.tool_selection
.map(|diagnostics| diagnostics.stage),
Some(ToolSelectionStage::ReviewedRequiredPrimitive)
);
let mut empty = request("session-auto", "turn-empty", None);
empty.tools.clear();
let (request_id, attempt_id) = ids(74);
let degraded = provider
.prepare(&empty, request_id, attempt_id, FixtureBehavior::Normal)
.expect("empty auto declaration uses explicit no-tool degradation");
assert!(!degraded.events.iter().any(|event| matches!(
event,
CanonicalEvent::OutputItemAdded {
item_kind: OutputItemKind::FunctionCall,
..
}
)));
assert_eq!(
degraded
.transformed
.tool_selection
.map(|diagnostics| diagnostics.stage),
Some(ToolSelectionStage::NoReviewedSafeTool)
);
}
#[test]
fn none_choice_uses_no_tool_completion() {
let provider = FixtureProvider::default();
let mut initial = request("session-none", "turn-none", None);
initial.tool_choice = ToolChoice::None;
initial.tools = vec![FunctionTool {
namespace: Some("collaboration".to_owned()),
name: "send_message".to_owned(),
description: "effectful fixture tool".to_owned(),
parameters: json!({"type": "object", "properties": {}, "required": []}),
strict: true,
}];
let (request_id, attempt_id) = ids(44);
let attempt = provider
.prepare(&initial, request_id, attempt_id, FixtureBehavior::Normal)
.expect("none choice completes without a tool");
assert!(!attempt.events.iter().any(|event| matches!(
event,
CanonicalEvent::OutputItemAdded {
item_kind: OutputItemKind::FunctionCall,
..
}
)));
assert!(matches!(
attempt.events.last(),
Some(CanonicalEvent::ResponseCompleted { .. })
));
}
#[test]
fn required_choice_fails_when_no_reviewed_safe_tool_is_eligible() {
let provider = FixtureProvider::default();
let mut initial = request("session-required", "turn-required", None);
initial.tool_choice = ToolChoice::Required;
initial.tools = vec![FunctionTool {
namespace: Some("collaboration".to_owned()),
name: "send_message".to_owned(),
description: "effectful fixture tool".to_owned(),
parameters: json!({"type": "object", "properties": {}, "required": []}),
strict: true,
}];
let (request_id, attempt_id) = ids(45);
let error =
match provider.prepare(&initial, request_id, attempt_id, FixtureBehavior::Normal) {
Err(error) => error,
Ok(_) => panic!("required choice must fail closed"),
};
assert_eq!(error.code, PublicErrorCode::UnsupportedCapability);
assert_eq!(
error.details,
Some(json!({
"failure_stage": "fixture_tool_selector.ineligible",
"total_tools": 1,
"zero_required_candidates": 1,
"reviewed_safe_matches": 0,
"reviewed_identity_matches": 0,
"collaboration_wait_agent_matches": 0
}))
);
assert_eq!(
select_fixture_tool(&initial.tools),
Err(ToolSelectionDiagnostics {
stage: ToolSelectionStage::Ineligible,
total_tools: 1,
zero_required_candidates: 1,
reviewed_safe_matches: 0,
reviewed_identity_matches: 0,
collaboration_wait_agent_matches: 0,
matched_category: None,
})
);
}
#[test]
fn reviewed_required_primitive_default_matches_preserved_schema() {
let provider = FixtureProvider::default();
let mut initial = request("session-reviewed", "turn-reviewed", None);
initial.tools = vec![FunctionTool {
namespace: None,
name: "get_weather".to_owned(),
description: "reviewed fixture tool".to_owned(),
parameters: json!({
"type": "object",
"properties": {"city": {"type": "string"}},
"required": ["city"],
"additionalProperties": false
}),
strict: true,
}];
initial.tool_choice = ToolChoice::Required;
let schema = initial.tools[0].parameters.clone();
let (request_id, attempt_id) = ids(42);
let attempt = provider
.prepare(&initial, request_id, attempt_id, FixtureBehavior::Normal)
.expect("reviewed primitive default is eligible");
let arguments = attempt
.events
.iter()
.find_map(|event| match event {
CanonicalEvent::FunctionCallArgumentsDone { arguments, .. } => {
Some(arguments.clone())
}
_ => None,
})
.expect("function arguments");
assert_eq!(arguments, json!({"city": "San Francisco"}));
assert!(arguments_validate_against_reviewed_schema(
&arguments, &schema, "city"
));
initial.tools[0].parameters["properties"]["city"]["type"] =
Value::String("integer".to_owned());
initial.tool_choice = ToolChoice::Auto;
let (request_id, attempt_id) = ids(43);
let attempt = provider
.prepare(&initial, request_id, attempt_id, FixtureBehavior::Normal)
.expect("auto degrades when the primitive schema is unreviewed");
assert!(!attempt.events.iter().any(|event| matches!(
event,
CanonicalEvent::OutputItemAdded {
item_kind: OutputItemKind::FunctionCall,
..
}
)));
assert_eq!(
attempt
.transformed
.tool_selection
.map(|diagnostics| diagnostics.stage),
Some(ToolSelectionStage::NoReviewedSafeTool)
);
}
}