Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 18 additions & 25 deletions docs/integrations/hermes.md
Original file line number Diff line number Diff line change
Expand Up @@ -398,29 +398,26 @@
import subprocess
import sys

def _resolve_agent_name() -> str:
"""Extract agent name from Hermes CLI invocation."""
try:
with open("/proc/self/cmdline", "rb") as f:
parts = f.read().split(b"\x00")
for i, part in enumerate(parts):
if part == b"-p" and i + 1 < len(parts):
name = parts[i + 1].decode("utf-8", errors="ignore").strip()
if name:
return name
except Exception:
pass
def _resolve_agent_name(cwd: str = "") -> str:
"""Extract agent name from Hermes hook payload cwd.

The /proc/self/cmdline approach is unreliable — shell hooks run as
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed
child subprocesses whose cmdline is the handler script, not the
gateway. Use the cwd payload field (always set by Hermes) instead.

Falls back to "default" if cwd is empty or resolution fails.
"""

Check failure

Code scanning / CodeCora

Agent name resolution returns deepest dir, not agent name Error documentation

The new _resolve_agent_name iterates [p, *p.parents] starting from the cwd itself (deepest) and returns the first directory whose name isn't "profiles". If the cwd is a subdirectory beneath the agent directory (e.g., /opt/data/profiles/myagent/sessions/123), the function returns 123 (the session ID) instead of myagent (the agent name). The != "profiles" check implies the expected structure is .../profiles/<agent_name>/..., but the iteration order returns the deepest component, not the one immediately following "profiles". This would cause memories to be stored/recalled under the wrong namespace.
if cwd:
p = pathlib.Path(cwd)
for parent in [p, *p.parents]:
if parent.name and parent.name != "profiles":
return parent.name
return "default"

AGENT = _resolve_agent_name()
UTEKE_BIN = pathlib.Path(shutil.which("uteke") or "/opt/data/.cargo/bin/uteke") # adjust to your path

def _recall_uteke(query: str, limit: int = 5) -> list:
if not UTEKE_BIN.exists():
return []
def _recall_uteke(query: str, agent: str, limit: int = 5) -> list:
try:
proc = subprocess.run(
[str(UTEKE_BIN), "recall", "--namespace", AGENT,
["uteke", "recall", "--namespace", agent,
"--limit", str(limit), "--json", query],
capture_output=True, text=True, timeout=15,
)
Expand All @@ -446,12 +443,8 @@
if not isinstance(message, str) or not message.strip() or len(message) < 5:
sys.exit(0)

# Skip cron sessions
session_id = raw.get("session_id", "")
if isinstance(session_id, str) and session_id.startswith("cron_"):
sys.exit(0)

memories = _recall_uteke(message.strip()[:500], limit=5)
agent = _resolve_agent_name(raw.get("cwd", ""))
memories = _recall_uteke(message.strip()[:500], agent, limit=5)
if not memories:
sys.exit(0)

Expand Down