#!/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())