- `is_diagnostic_notice()` replaces three drifting copies: the gateway muted every
`credits.*` notice, the TUI and CLI only `warn`/`error`, so `credits.restored` was hidden
on Telegram and shown in the TUI for the same config. Every credit-service notice is an
automatic diagnostic (a "restored" line after a hidden depletion notice is orphan noise).
- `effective_user_config()` is the single fail-open effective-config read; the two extra
`deepcopy`s per foreground turn go (the loader already returns a fresh copy and the
snapshot is read-only).
- `diagnostic_metadata(event)` replaces the repeated
`{"notification_category": "diagnostic"} if event.internal and ... else {}` literal in
gateway/run_turn.py; the gateway-side imports of the resolver are module-level (no cycle:
it imports only gateway.display_config).
- `display.suppress_warning_notifications` is listed with its sibling display keys in
cli-config.yaml.example and the configuration reference; the messaging guide states that a
muted diagnostic wake still runs (and bills) its agent turn.
102 lines
4.3 KiB
Python
102 lines
4.3 KiB
Python
"""Delivery policy for engine diagnostics, never assistant or command responses."""
|
|
|
|
from gateway.display_config import resolve_display_setting
|
|
|
|
|
|
class DiagnosticText(str):
|
|
"""Producer-owned classification on the legacy two-argument status callback.
|
|
|
|
String behavior and event kind stay unchanged for existing plugin renderers.
|
|
Durable carriers must serialize their own category, not this in-memory marker.
|
|
"""
|
|
|
|
|
|
def is_warning_status(event_type: str, message: str) -> bool:
|
|
return event_type == "warn" or isinstance(message, DiagnosticText)
|
|
|
|
|
|
def is_diagnostic_notice(notice) -> bool:
|
|
"""Out-of-band ``AgentNotice`` classification shared by the gateway, TUI and CLI sinks.
|
|
|
|
Credit-service notices are automatic diagnostics at every level (usage bands are ``info``,
|
|
``credits.restored`` is ``success``); everything else is diagnostic only when it warns.
|
|
"""
|
|
return (getattr(notice, "level", None) in {"warn", "error"}
|
|
or str(getattr(notice, "key", "") or "").startswith("credits."))
|
|
|
|
|
|
def diagnostic_metadata(event) -> dict:
|
|
"""``{"notification_category": "diagnostic"}`` for a trusted diagnostic-only wake, else ``{}``.
|
|
|
|
Only internal wakes may classify a turn; human content never does.
|
|
"""
|
|
if getattr(event, "internal", False) and (getattr(event, "metadata", None) or {}).get(
|
|
"notification_category") == "diagnostic":
|
|
return {"notification_category": "diagnostic"}
|
|
return {}
|
|
|
|
|
|
def effective_user_config() -> dict:
|
|
"""The active profile's effective config, or ``{}`` when it cannot be read (presentation fails open)."""
|
|
from hermes_cli.config_effective import load_user_config_effective
|
|
try:
|
|
config = load_user_config_effective()
|
|
except Exception:
|
|
return {}
|
|
return config if isinstance(config, dict) else {}
|
|
|
|
|
|
def diagnostic_turn_muted(display_metadata, platform, user_config=None) -> bool:
|
|
"""One admission rule for every surface: a diagnostic-category wake mutes its turn's
|
|
presentation only when the owning policy hides diagnostics. Human content never mutes."""
|
|
return ((display_metadata or {}).get("notification_category") == "diagnostic"
|
|
and not warning_notifications_enabled(platform, user_config))
|
|
|
|
|
|
def diagnostic_wake_muted(event, user_config=None) -> bool:
|
|
"""Only trusted diagnostic-only wakes can mute a turn, never human content."""
|
|
snapshot = getattr(event, "_notification_reply_muted", None)
|
|
if isinstance(snapshot, bool):
|
|
return snapshot
|
|
return bool(getattr(event, "internal", False)) and diagnostic_turn_muted(
|
|
getattr(event, "metadata", None), event.source.platform, user_config)
|
|
|
|
|
|
def render_notification(render, *, platform, diagnostic=True, user_config=None) -> bool:
|
|
"""Invoke a synchronous UI renderer only when its classified content is visible.
|
|
|
|
Return whether the renderer ran, not whether a transport delivered anything.
|
|
Call only at a presentation sink, never around producer callbacks or persistence.
|
|
The caller supplies the owning scope/turn snapshot; exceptions remain its policy.
|
|
"""
|
|
if diagnostic and not warning_notifications_enabled(platform, user_config):
|
|
return False
|
|
render()
|
|
return True
|
|
|
|
|
|
async def present_notification(present, *, platform, diagnostic=True, user_config=None) -> bool:
|
|
"""Async twin of :func:`render_notification` for lifecycle emitters that await a send.
|
|
|
|
Return whether the presenter ran; its own receipt/exception is the caller's to interpret.
|
|
The caller binds the owning profile scope BEFORE calling (watchers/shutdown fan-outs).
|
|
"""
|
|
if diagnostic and not warning_notifications_enabled(platform, user_config):
|
|
return False
|
|
await present()
|
|
return True
|
|
|
|
|
|
def warning_notifications_enabled(platform, user_config=None) -> bool:
|
|
"""Use the turn snapshot when supplied, otherwise the active profile's effective config.
|
|
|
|
No surface exemption: direct command/API outcomes are not notifications.
|
|
Unknown values never opt in; null inherits via the canonical display resolver.
|
|
"""
|
|
if user_config is None:
|
|
user_config = effective_user_config()
|
|
elif not isinstance(user_config, dict):
|
|
user_config = {}
|
|
platform_key = getattr(platform, "value", platform)
|
|
return not resolve_display_setting(user_config, platform_key, "suppress_warning_notifications", False)
|