diff --git a/backend/apps/agents/agent_manager.py b/backend/apps/agents/agent_manager.py index 7266b6a4..c6b60aab 100644 --- a/backend/apps/agents/agent_manager.py +++ b/backend/apps/agents/agent_manager.py @@ -46,6 +46,8 @@ class AgentManager(SessionLifecycle, SessionPersistence, Messaging, SessionContr @typechecked def __init__(self): self.sessions: Dict[str, AgentSession] = {} + from backend.apps.agents.core.flight_recorder import set_sessions_provider + set_sessions_provider(lambda: self.sessions) self.tasks: Dict[str, asyncio.Task] = {} # Live mirror of the in-flight streamed assistant text per session, so a stop can persist the partial reply instantly instead of waiting out the multi-second SDK teardown the cancel handler sits behind. self.live_partial: Dict[str, PartialReply] = {} diff --git a/backend/apps/agents/core/flight_recorder.py b/backend/apps/agents/core/flight_recorder.py index e42a8694..7a87f0d1 100644 --- a/backend/apps/agents/core/flight_recorder.py +++ b/backend/apps/agents/core/flight_recorder.py @@ -13,6 +13,14 @@ from typeguard import typechecked P_RING_SIZE = 64 p_lock = threading.Lock() p_rings: Dict[str, deque] = {} +p_sessions_provider = None + + +def set_sessions_provider(provider) -> None: + """agent_manager registers its live sessions dict once so envelopes built anywhere (error + handlers have no manager handle) still carry a real concurrency snapshot.""" + global p_sessions_provider + p_sessions_provider = provider @typechecked @@ -92,7 +100,7 @@ def build_envelope( "phase": phase, "attempts": attempts, "breadcrumbs": breadcrumbs(session_id), - "concurrency": concurrency_snapshot(sessions or {}), + "concurrency": concurrency_snapshot(sessions if sessions is not None else (p_sessions_provider() if p_sessions_provider else {})), } diff --git a/backend/apps/agents/manager/run/TurnRunner.py b/backend/apps/agents/manager/run/TurnRunner.py index 87a2b80a..0b8d2fce 100644 --- a/backend/apps/agents/manager/run/TurnRunner.py +++ b/backend/apps/agents/manager/run/TurnRunner.py @@ -177,6 +177,8 @@ class TurnRunner(AgentManagerProtocol): p_use_persistent = persistent_client_enabled() capacity_retry_attempt = 0 p_router_retry_attempt = 0 + # Baseline crumb so even a first-call failure's envelope names the turn it died in. + flight_recorder.crumb(session_id, "turn-start", model=resolved_model, api=api_type) while True: try: if p_use_persistent: