From 252e7e064321b729b6b3ce15da0a5d29fa3165be Mon Sep 17 00:00:00 2001 From: Bryan Helmkamp Date: Sat, 12 Sep 2026 16:26:32 -0600 Subject: [PATCH] Name pebble's stopped failover agent.route.failover.stopped Pebble publishes RouteFailoverStopped when a model error ends a prompt on its route although fallback routes were configured: the error is ineligible or the chain is exhausted. Fabro has no event of its own for that case, so the pebble event is stored as it is under a derived name next to agent.route.failover instead of the generic agent.event. Co-Authored-By: Claude Fable 5.1 --- .../fabro-types/src/run_event/agent.rs | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/lib/foundation/fabro-types/src/run_event/agent.rs b/lib/foundation/fabro-types/src/run_event/agent.rs index 91504f375..0776172f7 100644 --- a/lib/foundation/fabro-types/src/run_event/agent.rs +++ b/lib/foundation/fabro-types/src/run_event/agent.rs @@ -77,6 +77,7 @@ pub fn coding_event_name(event: &CodingEvent) -> &'static str { CodingEvent::LoopDetected => "agent.loop.detected", CodingEvent::ToolRoundsExhausted { .. } => "agent.tool.rounds.exhausted", CodingEvent::RouteFailover { .. } => "agent.route.failover", + CodingEvent::RouteFailoverStopped { .. } => "agent.route.failover.stopped", CodingEvent::McpServerReady { .. } => "agent.mcp.server.ready", CodingEvent::McpServerFailed { .. } => "agent.mcp.server.failed", CodingEvent::McpServerDisconnected { .. } => "agent.mcp.server.disconnected", @@ -125,6 +126,7 @@ pub const CODING_EVENT_NAMES: &[&str] = &[ "agent.loop.detected", "agent.tool.rounds.exhausted", "agent.route.failover", + "agent.route.failover.stopped", "agent.mcp.server.ready", "agent.mcp.server.failed", "agent.mcp.server.disconnected", @@ -280,7 +282,7 @@ pub struct AgentMcpDisconnectedProps { mod tests { use std::time::{Duration, UNIX_EPOCH}; - use pebble_coding_agent::events::TokenUsage; + use pebble_coding_agent::events::{ErrorData, ErrorKind, FailoverStop, TokenUsage}; use serde_json::json; use super::*; @@ -356,4 +358,16 @@ mod tests { assert!(is_coding_event_name("todo.updated")); assert!(!is_coding_event_name("agent.session.activated")); } + + #[test] + fn a_stopped_failover_has_its_own_name() { + let stopped = CodingEvent::RouteFailoverStopped { + route: "anthropic/claude-fable-5".to_string(), + attempt: 2, + reason: FailoverStop::Exhausted, + error: ErrorData::new(ErrorKind::Llm, "overloaded"), + }; + assert_eq!(coding_event_name(&stopped), "agent.route.failover.stopped"); + assert!(is_coding_event_name("agent.route.failover.stopped")); + } }