Files
hermes-agent/agent/message_metadata.py
kshitijk4poor 4854225903 fix(persistence): version transcript rows by digest, not a row copy
The CAS row snapshot was a full copy of each message's durable payload
riding on the live dict. The rough token estimator priced it (about 2x
estimates -> premature compaction) and it doubled transcript memory.

Replace it with a 16-byte blake2b digest of the repair columns. The
compare now runs in Python against the target row already read inside
the BEGIN IMMEDIATE transaction, followed by a plain UPDATE. Also:
- add _db_row_snapshot to PERSISTENCE_ONLY_MESSAGE_FIELDS so the
  estimator and the outbound request builder both drop it
- derive _REPAIR_COLUMNS/_SYNC_FIELDS from _MESSAGE_WRITE_COLUMNS
- use hermes_state_common._placeholders
- drop the dead resume-path stamp (the SELECT has no token_count, so it
  was always None) and the dead tool name assignment in
  _decoded_repair_row
- keep the digest out of divert JSONL

The kept active-row test now pins estimate stability across a flush and
the survival of a concurrent writer's row. It goes red on the old
prod files and red when the digest compare is removed.
2026-09-27 20:45:44 +05:30

45 lines
1.5 KiB
Python

"""Internal metadata attached to durable conversation messages."""
from __future__ import annotations
from time import time as wall_time
from typing import Any, MutableMapping, Optional, TypeVar
# These fields describe Hermes' durable record and timeline display, not
# provider-visible message content. The request builder strips them from every
# outgoing copy and the token estimator ignores them: one set, so an estimate
# never prices bytes the provider never receives (an edit's inline_diff in
# display_metadata is ~9KB and would trigger premature compaction).
PERSISTENCE_ONLY_MESSAGE_FIELDS = frozenset(
{"timestamp", "display_kind", "display_metadata", "_row_id", "_db_row_snapshot"}
)
_Message = TypeVar("_Message", bound=MutableMapping[str, Any])
def stamp_message_timestamp(
message: _Message,
*,
timestamp: Optional[float] = None,
) -> _Message:
"""Attach a creation timestamp without replacing source-provided time.
Gateway adapters can supply the platform event time; all other callers use
the local wall clock. Returns the same mapping for use at append sites.
"""
if message.get("timestamp") is None:
message["timestamp"] = wall_time() if timestamp is None else timestamp
return message
def append_message(
messages: list[Any],
message: _Message,
*,
timestamp: Optional[float] = None,
) -> _Message:
"""Stamp and append one live transcript message."""
messages.append(stamp_message_timestamp(message, timestamp=timestamp))
return message