atlas-iac/services/hermes/scripts/patch_web_session_activity.py

97 lines
3.3 KiB
Python

#!/usr/bin/env python3
"""Merge display-only worker activity into dashboard session transcripts."""
from __future__ import annotations
import argparse
from pathlib import Path
HELPER_MARKER = '''@app.get("/api/sessions/{session_id}/messages")
async def get_session_messages(session_id: str, profile: Optional[str] = None):
'''
HELPER_REPLACEMENT = '''def _run_activity_messages(session_id: str) -> List[Dict[str, Any]]:
"""Read a bounded activity journal without treating it as agent history."""
digest = hashlib.sha256(session_id.encode("utf-8")).hexdigest()
root = get_hermes_home() / "run-activity"
if root.is_symlink():
return []
paths = (
root / f"{digest}.previous.jsonl",
root / f"{digest}.jsonl",
)
entries: List[Dict[str, Any]] = []
for path in paths:
try:
if path.is_symlink() or not path.is_file():
continue
if path.stat().st_size > 600_000:
continue
for line in path.read_text(encoding="utf-8").splitlines():
item = json.loads(line)
if not isinstance(item, dict):
continue
content = item.get("content")
timestamp = item.get("timestamp")
if not isinstance(content, str) or not isinstance(
timestamp, (int, float)
):
continue
entries.append(
{
"role": "assistant",
"content": content[:1_000],
"timestamp": float(timestamp),
"observed": True,
}
)
except (OSError, UnicodeError, ValueError, json.JSONDecodeError):
continue
return entries[-1_000:]
@app.get("/api/sessions/{session_id}/messages")
async def get_session_messages(session_id: str, profile: Optional[str] = None):
'''
MESSAGES_BEFORE = ''' messages = db.get_messages(sid)
return {"session_id": sid, "messages": messages}
'''
MESSAGES_AFTER = ''' messages = [
*db.get_messages(sid),
*_run_activity_messages(sid),
]
messages.sort(key=lambda item: float(item.get("timestamp") or 0.0))
return {"session_id": sid, "messages": messages}
'''
def patch(source: Path, destination: Path) -> None:
"""Apply the activity projection and fail closed on upstream drift."""
content = source.read_text(encoding="utf-8")
if HELPER_MARKER not in content:
raise RuntimeError("Hermes dashboard activity helper context changed")
if MESSAGES_BEFORE not in content:
raise RuntimeError("Hermes dashboard messages context changed")
content = content.replace(HELPER_MARKER, HELPER_REPLACEMENT, 1)
destination.parent.mkdir(parents=True, exist_ok=True)
destination.write_text(
content.replace(MESSAGES_BEFORE, MESSAGES_AFTER, 1),
encoding="utf-8",
)
def main() -> int:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("source", type=Path)
parser.add_argument("destination", type=Path)
args = parser.parse_args()
patch(args.source, args.destination)
return 0
if __name__ == "__main__":
raise SystemExit(main())