aboutsummaryrefslogtreecommitdiff
path: root/src/integration/assets/hermes/__init__.py
blob: 03d15a555f21c17bc41962160947ba78f9e42354 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
"""Hermes plugin installed by Herdr to report agent lifecycle state."""

# HERDR_INTEGRATION_ID=hermes
# HERDR_INTEGRATION_VERSION=1

from __future__ import annotations

import json
import os
import random
import socket
import time

_SOURCE = "herdr:hermes"
_AGENT = "hermes"


def _base_params() -> tuple[str, str] | None:
    if os.environ.get("HERDR_ENV") != "1":
        return None
    pane_id = os.environ.get("HERDR_PANE_ID", "").strip()
    socket_path = os.environ.get("HERDR_SOCKET_PATH", "").strip()
    if not pane_id or not socket_path:
        return None
    return pane_id, socket_path


def _send(method: str, params: dict) -> None:
    base = _base_params()
    if base is None:
        return
    pane_id, socket_path = base
    params = {
        "pane_id": pane_id,
        "source": _SOURCE,
        "agent": _AGENT,
        "seq": time.time_ns(),
        **params,
    }
    request = {
        "id": f"{_SOURCE}:{int(time.time() * 1000)}:{random.randrange(1_000_000):06d}",
        "method": method,
        "params": params,
    }
    try:
        client = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
        client.settimeout(0.5)
        client.connect(socket_path)
        client.sendall((json.dumps(request) + "\n").encode("utf-8"))
        try:
            client.recv(4096)
        except Exception:
            pass
        client.close()
    except Exception:
        pass


def _report(state: str) -> None:
    _send("pane.report_agent", {"state": state})


def _release() -> None:
    _send("pane.release_agent", {})


def _working(**kwargs) -> None:
    del kwargs
    _report("working")


def _blocked(**kwargs) -> None:
    del kwargs
    _report("blocked")


def _idle(**kwargs) -> None:
    del kwargs
    _report("idle")


def _finalize(**kwargs) -> None:
    del kwargs
    _release()


def register(ctx):
    ctx.register_hook("on_session_start", _idle)
    ctx.register_hook("pre_llm_call", _working)
    ctx.register_hook("pre_api_request", _working)
    ctx.register_hook("pre_tool_call", _working)
    ctx.register_hook("post_tool_call", _working)
    ctx.register_hook("pre_approval_request", _blocked)
    ctx.register_hook("post_approval_response", _working)
    ctx.register_hook("post_llm_call", _idle)
    ctx.register_hook("on_session_end", _idle)
    ctx.register_hook("on_session_finalize", _finalize)