97 lines
3.3 KiB
Python
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())
|