hermes-agent/gateway
Teknium a2c2ec6332
fix(status): make gateway_updated_at a stable RFC3339-or-null contract (#68657)
The /api/status gateway_updated_at field and the gateway /health/detailed
updated_at field passed through whatever gateway_state.json contained,
untyped. All current writers emit RFC3339 via _utc_now_iso(), but legacy
gateways wrote unix epoch floats, and a corrupt or hand-edited state file
can inject numbers or arbitrary garbage — while the frontend types
(web/src/lib/api.ts) declare string | null.

Add normalize_updated_at() in gateway/status.py as the single funnel:
- str: accepted iff datetime.fromisoformat parses (trailing Z tolerated);
  naive timestamps coerced to UTC; canonical isoformat returned
- int/float: treated as unix epoch seconds -> UTC ISO string, with a
  plausibility guard (reject < 2000-01-01, > now+1day, non-finite)
- bool: rejected explicitly (int subclass, but never a timestamp)
- anything else: None

Apply it at both emit sites: the dashboard /api/status handler (covers
both the local read_runtime_status() branch and the remote
/health/detailed cross-container fallback branch) and the gateway API
server's /health/detailed response.

Contract tests: parametrized /api/status normalization (epoch float/int,
garbage string, None, bool, dict, absent key), remote-health numeric and
garbage bodies, dashboard shape test round-trip assertion, direct
normalize_updated_at units (range guards, Z suffix, naive coercion,
non-finite floats), and a write_runtime_status -> read_runtime_status
round-trip proving the writer side stays tz-aware parseable.
2026-07-21 13:12:41 -07:00
..
assets
builtin_hooks
platforms fix(status): make gateway_updated_at a stable RFC3339-or-null contract (#68657) 2026-07-21 13:12:41 -07:00
relay
__init__.py
authz_mixin.py
cgroup_cleanup.py
channel_directory.py
code_skew.py
config.py
cwd_placeholder.py
dead_targets.py
delivery.py
delivery_ledger.py
display_config.py
drain_control.py
hooks.py
kanban_watchers.py
memory_monitor.py
message_timestamps.py
mirror.py
pairing.py
platform_registry.py
profile_routing.py
readiness.py
response_filters.py
restart.py
restart_loop_guard.py
rich_sent_store.py
run.py fix(gateway): reap the replaced gateway's orphaned children on POSIX 2026-07-21 12:40:21 -07:00
runtime_footer.py
scale_to_zero.py
session.py
session_context.py
shutdown_forensics.py
shutdown_watchdog.py
slash_access.py
slash_commands.py
status.py fix(status): make gateway_updated_at a stable RFC3339-or-null contract (#68657) 2026-07-21 13:12:41 -07:00
status_phrases.py
sticker_cache.py
stream_consumer.py
stream_dispatch.py
stream_events.py
systemd_notify.py
turn_lease.py
whatsapp_identity.py