hermes - 💡(How to fix) Fix `session_id` not propagated to `get_pre_tool_call_block_message` from its 3 callsites — `pre_tool_call` plugin hooks that scope by session silently no-op

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…

hermes_cli/plugins.get_pre_tool_call_block_message(tool_name, args, task_id="", session_id="", tool_call_id="") accepts a session_id keyword, but all three core callsites pass only task_id and omit session_id. A pre_tool_call plugin that scopes its check on session_id therefore bails on every call and becomes a silent no-op — it appears registered and active, fires, but never does anything. There is no error and no warning.

Error Message

hermes_cli/plugins.get_pre_tool_call_block_message(tool_name, args, task_id="", session_id="", tool_call_id="") accepts a session_id keyword, but all three core callsites pass only task_id and omit session_id. A pre_tool_call plugin that scopes its check on session_id therefore bails on every call and becomes a silent no-op — it appears registered and active, fires, but never does anything. There is no error and no warning.

Root Cause

This plugin silently skips all checks because session_id is never passed from core.

Fix Action

Fix / Workaround

Our workaround (no source patch — we do NOT modify Hermes source)

We previously patched the three callsites locally, but reverted that — every hermes update wipes a source patch. Our current workaround is entirely plugin-side and update-safe: the plugin registers an on_session_start hook that captures agent.session_id directly, plus a state.db last-resort lookup (most recent assistant message) for any site that still doesn't receive it. The plugin now works with or without core propagation, and we carry zero Hermes source modifications. The upstream fix would simply let session-scoped plugins work without that workaround.

Code Example

def get_pre_tool_call_block_message(function_name, function_args, task_id=None, session_id=None):
    if not session_id:
        return None  # bail — no context
    # ... rest of plugin logic

---

--- a/agent/tool_executor.py
+++ b/agent/tool_executor.py
@@ ~129
             block_message = get_pre_tool_call_block_message(
-                function_name, function_args, task_id=effective_task_id or "",
+                function_name, function_args, task_id=effective_task_id or "",
+                session_id=agent.session_id or "",
             )
@@ ~504
             _block_msg = get_pre_tool_call_block_message(
-                function_name, function_args, task_id=effective_task_id or "",
+                function_name, function_args, task_id=effective_task_id or "",
+                session_id=agent.session_id or "",
             )

--- a/agent/agent_runtime_helpers.py
+++ b/agent/agent_runtime_helpers.py
@@ ~1561
             block_message = get_pre_tool_call_block_message(
-                function_name, function_args, task_id=effective_task_id or "",
+                function_name, function_args, task_id=effective_task_id or "",
+                session_id=agent.session_id or "",
             )
RAW_BUFFERClick to expand / collapse

Summary

hermes_cli/plugins.get_pre_tool_call_block_message(tool_name, args, task_id="", session_id="", tool_call_id="") accepts a session_id keyword, but all three core callsites pass only task_id and omit session_id. A pre_tool_call plugin that scopes its check on session_id therefore bails on every call and becomes a silent no-op — it appears registered and active, fires, but never does anything. There is no error and no warning.

Affected callsites (HEAD 69dfcdcc1)

  1. agent/tool_executor.py:129block_message = get_pre_tool_call_block_message(function_name, function_args, task_id=effective_task_id or "")
  2. agent/tool_executor.py:504_block_msg = get_pre_tool_call_block_message(function_name, function_args, task_id=effective_task_id or "")
  3. agent/agent_runtime_helpers.py:1561block_message = get_pre_tool_call_block_message(function_name, function_args, task_id=effective_task_id or "")

All three pass task_id but omit session_id, so the plugin always receives the "" default.

Reproduction

A plugin whose pre_tool_call hook scopes on session context, e.g.:

def get_pre_tool_call_block_message(function_name, function_args, task_id=None, session_id=None):
    if not session_id:
        return None  # bail — no context
    # ... rest of plugin logic

This plugin silently skips all checks because session_id is never passed from core.

Proposed fix

Pass the session id that is already in scope at each callsite (e.g. session_id=agent.session_id or "") — the exact in-scope reference is whatever the maintainers prefer at each site. Illustrative diff:

--- a/agent/tool_executor.py
+++ b/agent/tool_executor.py
@@ ~129
             block_message = get_pre_tool_call_block_message(
-                function_name, function_args, task_id=effective_task_id or "",
+                function_name, function_args, task_id=effective_task_id or "",
+                session_id=agent.session_id or "",
             )
@@ ~504
             _block_msg = get_pre_tool_call_block_message(
-                function_name, function_args, task_id=effective_task_id or "",
+                function_name, function_args, task_id=effective_task_id or "",
+                session_id=agent.session_id or "",
             )

--- a/agent/agent_runtime_helpers.py
+++ b/agent/agent_runtime_helpers.py
@@ ~1561
             block_message = get_pre_tool_call_block_message(
-                function_name, function_args, task_id=effective_task_id or "",
+                function_name, function_args, task_id=effective_task_id or "",
+                session_id=agent.session_id or "",
             )

Impact

Any plugin using a pre_tool_call hook that depends on session_id (first-turn-only injection, session-keyed state, etc.) silently fails. Discovered while building a blueprint-capture plugin meant to record per-session author blueprints — the plugin was registered and its hook fired, but nothing was captured because the session-id gate bailed on every call.

Our workaround (no source patch — we do NOT modify Hermes source)

We previously patched the three callsites locally, but reverted that — every hermes update wipes a source patch. Our current workaround is entirely plugin-side and update-safe: the plugin registers an on_session_start hook that captures agent.session_id directly, plus a state.db last-resort lookup (most recent assistant message) for any site that still doesn't receive it. The plugin now works with or without core propagation, and we carry zero Hermes source modifications. The upstream fix would simply let session-scoped plugins work without that workaround.

Environment

  • Hermes at commit 69dfcdcc1, macOS, Python 3.11
  • Affected: any plugin using a pre_tool_call hook that depends on session_id

Filed by: Barrett Slagle (The Factory)

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

hermes - 💡(How to fix) Fix `session_id` not propagated to `get_pre_tool_call_block_message` from its 3 callsites — `pre_tool_call` plugin hooks that scope by session silently no-op