git.iko.soy/herdr-auto-title
on master help sign in
herdr-auto-title/__init__.py
84 lines 2.5 kB history raw
1"""Auto-title herdr tabs from the first user message of a Hermes session.
3Separate from the herdr-agent-state plugin so that
4`herdr integration install hermes` reinstalls don't clobber it.
5"""
7from __future__ import annotations
9import json
10import os
11import random
12import socket
13import time
15_SOURCE = "herdr:hermes:auto-title"
16_seen_sessions: set[str] = set()
19def _herdr_env() -> dict[str, str] | None:
20 """Read herdr env vars if running inside a herdr pane."""
21 if os.environ.get("HERDR_ENV") != "1":
22 return None
23 tab_id = os.environ.get("HERDR_TAB_ID", "").strip()
24 socket_path = os.environ.get("HERDR_SOCKET_PATH", "").strip()
25 if not tab_id or not socket_path:
26 return None
27 return {"tab_id": tab_id, "socket_path": socket_path}
30def _send(method: str, params: dict) -> None:
31 env = _herdr_env()
32 if env is None:
33 return
34 request = {
35 "id": f"{_SOURCE}:{int(time.time() * 1000)}:{random.randrange(1_000_000):06d}",
36 "method": method,
37 "params": {**params, "tab_id": env["tab_id"]},
38 }
39 try:
40 client = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
41 client.settimeout(0.5)
42 client.connect(env["socket_path"])
43 client.sendall((json.dumps(request) + "\n").encode("utf-8"))
44 try:
45 client.recv(4096)
46 except Exception:
47 pass
48 client.close()
49 except Exception:
50 pass
53def _session_id(kwargs: dict) -> str | None:
54 value = kwargs.get("session_id")
55 if isinstance(value, str) and value:
56 return value
57 return None
60def _post_llm_call_title(**kwargs) -> None:
61 """Set the herdr tab title from the first user message of a session."""
62 session_id = _session_id(kwargs) or ""
63 if not session_id or session_id in _seen_sessions:
64 return
65 user_msg = kwargs.get("user_message", "")
66 if not user_msg or not isinstance(user_msg, str):
67 return
68 title = user_msg.split("\n")[0].strip()[:40]
69 if not title:
70 return
71 _send("tab.rename", {"label": title})
72 _seen_sessions.add(session_id)
75def _on_session_start_reset(**kwargs) -> None:
76 """Allow re-titling when a new session starts."""
77 session_id = _session_id(kwargs)
78 if session_id:
79 _seen_sessions.discard(session_id)
82def register(ctx):
83 ctx.register_hook("on_session_start", _on_session_start_reset)
84 ctx.register_hook("post_llm_call", _post_llm_call_title)