"""Auto-title herdr tabs from the first user message of a Hermes session. Separate from the herdr-agent-state plugin so that `herdr integration install hermes` reinstalls don't clobber it. """ from __future__ import annotations import json import os import random import socket import time _SOURCE = "herdr:hermes:auto-title" _seen_sessions: set[str] = set() def _herdr_env() -> dict[str, str] | None: """Read herdr env vars if running inside a herdr pane.""" if os.environ.get("HERDR_ENV") != "1": return None tab_id = os.environ.get("HERDR_TAB_ID", "").strip() socket_path = os.environ.get("HERDR_SOCKET_PATH", "").strip() if not tab_id or not socket_path: return None return {"tab_id": tab_id, "socket_path": socket_path} def _send(method: str, params: dict) -> None: env = _herdr_env() if env is None: return request = { "id": f"{_SOURCE}:{int(time.time() * 1000)}:{random.randrange(1_000_000):06d}", "method": method, "params": {**params, "tab_id": env["tab_id"]}, } try: client = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) client.settimeout(0.5) client.connect(env["socket_path"]) client.sendall((json.dumps(request) + "\n").encode("utf-8")) try: client.recv(4096) except Exception: pass client.close() except Exception: pass def _session_id(kwargs: dict) -> str | None: value = kwargs.get("session_id") if isinstance(value, str) and value: return value return None def _post_llm_call_title(**kwargs) -> None: """Set the herdr tab title from the first user message of a session.""" session_id = _session_id(kwargs) or "" if not session_id or session_id in _seen_sessions: return user_msg = kwargs.get("user_message", "") if not user_msg or not isinstance(user_msg, str): return title = user_msg.split("\n")[0].strip()[:40] if not title: return _send("tab.rename", {"label": title}) _seen_sessions.add(session_id) def _on_session_start_reset(**kwargs) -> None: """Allow re-titling when a new session starts.""" session_id = _session_id(kwargs) if session_id: _seen_sessions.discard(session_id) def register(ctx): ctx.register_hook("on_session_start", _on_session_start_reset) ctx.register_hook("post_llm_call", _post_llm_call_title)