From 1d85d7af6bf54989b26175b83d88648a3c700e4c Mon Sep 17 00:00:00 2001 From: Pheby Date: Wed, 9 Sep 2026 17:15:20 +0000 Subject: [PATCH] [verified] fix: preserve timestamps in chat history --- plugin/pheby/hermes_bridge.py | 8 +++++--- tests/test_pheby.py | 26 ++++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 3 deletions(-) diff --git a/plugin/pheby/hermes_bridge.py b/plugin/pheby/hermes_bridge.py index fb3340c..79a1119 100644 --- a/plugin/pheby/hermes_bridge.py +++ b/plugin/pheby/hermes_bridge.py @@ -9,6 +9,7 @@ so the module can be imported by unit tests without a Hermes install. from __future__ import annotations import asyncio +import datetime as dt import logging import threading import time @@ -206,8 +207,10 @@ async def list_conversations(server: Any = None) -> List[Dict[str, Any]]: def _iso(value: Any) -> Optional[str]: try: + if isinstance(value, (int, float)): + return dt.datetime.fromtimestamp(value, tz=dt.timezone.utc).isoformat() return value.isoformat() if value else None - except AttributeError: + except (AttributeError, OSError, OverflowError, TypeError, ValueError): return None @@ -254,8 +257,7 @@ async def conversation_history(conversation_id: str, limit: int if isinstance(row.get("_row_id"), (int, str)) else None, "role": role, "text": text, - "ts": row.get("timestamp") if isinstance( - row.get("timestamp"), str) else None, + "ts": _iso(row.get("timestamp")), }) found = True except Exception: diff --git a/tests/test_pheby.py b/tests/test_pheby.py index 13460a9..17fb0bb 100644 --- a/tests/test_pheby.py +++ b/tests/test_pheby.py @@ -432,6 +432,32 @@ class TestAttachments: # Tool events / approvals / clarifications / cancellation (bridge) # ═══════════════════════════════════════════════════════════════════════════ class TestBridge: + @pytest.mark.asyncio + async def test_history_converts_numeric_hermes_timestamps_to_iso(self, monkeypatch): + from pheby import hermes_bridge as hb + + class Store: + def peek_session_id(self, _key): + return "session-1" + + class DB: + def get_messages_as_conversation(self, _session_id, include_row_ids=False): + assert include_row_ids is True + return [ + {"_row_id": 10, "role": "user", "content": "hello", "timestamp": 1_789_000_000.25}, + {"_row_id": 11, "role": "assistant", "content": "reply", "timestamp": 1_789_000_001.5}, + ] + + monkeypatch.setattr(hb, "_session_store", lambda: Store()) + monkeypatch.setattr(hb, "_session_db", lambda: DB()) + + history, found = await hb.conversation_history("conv", 20) + + assert found is True + assert [message["message_id"] for message in history] == ["m10", "m11"] + assert history[0]["ts"] == "2026-09-10T00:26:40.250000+00:00" + assert history[1]["ts"] == "2026-09-10T00:26:41.500000+00:00" + @pytest.mark.asyncio async def test_tool_start_event_is_structured_not_text(self, tmp_path, monkeypatch):