#!/usr/bin/env python3 """Wake-up Protocol — session start context injection. Generates 300-900 tokens of context when a new Hermes session starts. Loads identity, recent palace context, and fleet status. Refs: Epic #367, Sub-issue #372 """ from __future__ import annotations import json import os import subprocess import time from pathlib import Path # --------------------------------------------------------------------------- # Constants # --------------------------------------------------------------------------- IDENTITY_PATH = Path.home() / ".mempalace" / "identity.txt" MEMPALACE_BIN = "/Library/Frameworks/Python.framework/Versions/3.12/bin/mempalace" FLEET_STATUS_PATH = Path.home() / ".hermes" / "fleet_status.json" WAKEUP_CACHE_PATH = Path.home() / ".hermes" / "last_wakeup.txt" WAKEUP_CACHE_TTL = 300 # 5 minutes — don't regenerate if recent # --------------------------------------------------------------------------- # Helpers # --------------------------------------------------------------------------- def _load_identity() -> str: """Read the agent identity file.""" try: if IDENTITY_PATH.exists(): text = IDENTITY_PATH.read_text(encoding="utf-8").strip() # Cap at ~150 tokens for wake-up brevity words = text.split() if len(words) > 150: text = " ".join(words[:150]) + "..." return text except (OSError, PermissionError): pass return "" def _palace_context() -> str: """Run mempalace wake-up command for recent context. Degrades gracefully.""" try: bin_path = MEMPALACE_BIN if os.path.exists(MEMPALACE_BIN) else "mempalace" result = subprocess.run( [bin_path, "wake-up"], capture_output=True, text=True, timeout=10, ) if result.returncode == 0 and result.stdout.strip(): return result.stdout.strip() except (FileNotFoundError, subprocess.TimeoutExpired, OSError): # ONNX issues (#373) or CLI not available — degrade gracefully pass return "" def fleet_status_summary() -> str: """Read cached fleet status for lightweight session context.""" try: if FLEET_STATUS_PATH.exists(): data = json.loads(FLEET_STATUS_PATH.read_text(encoding="utf-8")) lines = ["## Fleet Status"] if isinstance(data, dict): for agent, status in data.items(): if isinstance(status, dict): state = status.get("state", "unknown") last_seen = status.get("last_seen", "?") lines.append(f" {agent}: {state} (last: {last_seen})") else: lines.append(f" {agent}: {status}") if len(lines) > 1: return "\n".join(lines) except (OSError, json.JSONDecodeError): pass return "" def _check_cache() -> str: """Return cached wake-up if fresh enough.""" try: if WAKEUP_CACHE_PATH.exists(): age = time.time() - WAKEUP_CACHE_PATH.stat().st_mtime if age < WAKEUP_CACHE_TTL: return WAKEUP_CACHE_PATH.read_text(encoding="utf-8").strip() except OSError: pass return "" def _write_cache(content: str) -> None: """Cache the wake-up content.""" try: WAKEUP_CACHE_PATH.parent.mkdir(parents=True, exist_ok=True) WAKEUP_CACHE_PATH.write_text(content, encoding="utf-8") except OSError: pass # --------------------------------------------------------------------------- # Main entry point # --------------------------------------------------------------------------- def palace_wakeup(force: bool = False) -> str: """Generate wake-up context for a new session. ~300-900 tokens. Args: force: If True, bypass the 5-minute cache and regenerate. Returns: Formatted context string suitable for prepending to the system prompt. """ # Check cache first (avoids redundant work on rapid session restarts) if not force: cached = _check_cache() if cached: return cached parts = [] # L0: Identity identity = _load_identity() if identity: parts.append(f"## Identity\n{identity}") # L1: Recent palace context palace = _palace_context() if palace: parts.append(palace) # Fleet status (lightweight) fleet = fleet_status_summary() if fleet: parts.append(fleet) # Timestamp parts.append(f"## Session\nWake-up generated: {time.strftime('%Y-%m-%d %H:%M:%S')}") content = "\n\n".join(parts) # Cache for TTL _write_cache(content) return content # --------------------------------------------------------------------------- # CLI entry point for testing # --------------------------------------------------------------------------- if __name__ == "__main__": print(palace_wakeup(force=True))