"""Turn-local presentation of diagnostic-only wakes; execution and controls stay live.""" from contextlib import contextmanager from contextvars import ContextVar _muted_surface: ContextVar[str | None] = ContextVar("muted_notification_surface", default=None) # These are text/media UI events, not approval/clarify/connection requests or outcomes. _FREEFORM_EVENTS = frozenset({ "message.start", "message.delta", "message.interim", "message.complete", "reasoning.delta", "thinking.delta", "status.update", "notification.show", "tool.start", "tool.complete", "tool.generating", "error", "reaction", }) _PRESENTATION_CALLBACKS = ( "stream_delta_callback", "interim_assistant_callback", "reasoning_callback", "tool_progress_callback", "tool_start_callback", "tool_complete_callback", "tool_gen_callback", "reaction_callback", ) def event_presentation_muted(event: str, session_id: str) -> bool: return _muted_surface.get() == session_id and event in _FREEFORM_EVENTS def diagnostic_process_event(event: dict) -> bool: """Early failure/monitor diagnostics, not the explicitly requested final result.""" return bool(event.get("task_failure_notice")) or event.get("type") in { "watch_disabled", "watch_overflow_tripped", "watch_overflow_released", } def notification_config_snapshot(): """Read the owning effective config once per turn (the loader already returns a fresh copy).""" from gateway.warning_notifications import effective_user_config return effective_user_config() @contextmanager def notification_policy_snapshot(agent, platform, config): """Bind one foreground policy for callbacks, including worker threads. ``config`` is read-only.""" missing = object() saved = {key: getattr(agent, key, missing) for key in ("_notification_config", "_notification_platform")} try: agent._notification_config = config agent._notification_platform = platform yield finally: for key, value in saved.items(): if value is missing: delattr(agent, key) else: setattr(agent, key, value) @contextmanager def notification_turn(agent, *, muted: bool, session_id: str = ""): """Freeze the current turn's presentation without touching prompts or tool schemas.""" if not muted: yield return missing = object() keys = (*_PRESENTATION_CALLBACKS, "suppress_status_output", "_mute_notification_reply") saved = {key: getattr(agent, key, missing) for key in keys} token = _muted_surface.set(session_id) try: for key in _PRESENTATION_CALLBACKS: setattr(agent, key, None) agent.suppress_status_output = True agent._mute_notification_reply = True yield finally: for key, value in saved.items(): if value is missing: delattr(agent, key) else: setattr(agent, key, value) _muted_surface.reset(token)