[verified] fix: emit UTC-offset last_active so the app can sort by recency
Session-store updated_at values are naive local datetimes; passing them through without an offset broke Instant.parse() on the client, dropping last_active and leaving the chat list unsorted after restart.
This commit is contained in:
@@ -210,6 +210,16 @@ def _iso(value: Any) -> Optional[str]:
|
||||
try:
|
||||
if isinstance(value, (int, float)):
|
||||
return dt.datetime.fromtimestamp(value, tz=dt.timezone.utc).isoformat()
|
||||
if isinstance(value, dt.datetime):
|
||||
# Session-store timestamps are naive LOCAL datetimes (see
|
||||
# gateway.session_lifecycle._now). The client parses with
|
||||
# Instant.parse(), which requires an offset — a bare
|
||||
# "2026-09-10T05:41:48" fails and last_active is lost, so the
|
||||
# app can no longer sort by recency. Attach the local offset
|
||||
# and normalize to UTC.
|
||||
if value.tzinfo is None:
|
||||
value = value.astimezone()
|
||||
return value.astimezone(dt.timezone.utc).isoformat()
|
||||
return value.isoformat() if value else None
|
||||
except (AttributeError, OSError, OverflowError, TypeError, ValueError):
|
||||
return None
|
||||
|
||||
@@ -12,6 +12,7 @@ by ``conftest.py``).
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
import datetime as dt
|
||||
import json
|
||||
import threading
|
||||
import time
|
||||
@@ -459,6 +460,24 @@ class TestBridge:
|
||||
assert history[0]["ts"] == "2026-09-10T00:26:40.250000+00:00"
|
||||
assert history[1]["ts"] == "2026-09-10T00:26:41.500000+00:00"
|
||||
|
||||
def test_iso_parses_by_instant_for_naive_local_datetimes(self):
|
||||
# gateway session-store timestamps are naive LOCAL datetimes
|
||||
# (gateway.session_lifecycle._now). last_active must come back with a
|
||||
# UTC offset, or the Kotlin client's Instant.parse() fails and the
|
||||
# chat list loses its recency ordering.
|
||||
from pheby import hermes_bridge as hb
|
||||
|
||||
naive = dt.datetime(2026, 9, 10, 5, 41, 48, 401094) # naive local
|
||||
got = hb._iso(naive)
|
||||
assert got is not None
|
||||
assert got.endswith("+00:00")
|
||||
# Java-style Instant.parse equivalent: parse with offset required.
|
||||
parsed = dt.datetime.fromisoformat(got)
|
||||
assert parsed.tzinfo is not None
|
||||
# Aware datetimes pass through normalized to UTC unchanged.
|
||||
aware = dt.datetime(2026, 9, 10, 12, 0, tzinfo=dt.timezone(dt.timedelta(hours=-5)))
|
||||
assert hb._iso(aware) == "2026-09-10T17:00:00+00:00"
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_tool_start_event_is_structured_not_text(self, tmp_path,
|
||||
monkeypatch):
|
||||
|
||||
Reference in New Issue
Block a user