hermes - ✅(Solved) Fix tool_executor pre_tool_call hook calls omit session_id and tool_call_id (v0.14.0) [2 pull requests, 1 participants]

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…
GitHub stats
NousResearch/hermes-agent#28961Fetched 2026-05-20 04:00:58
View on GitHub
Comments
0
Participants
1
Timeline
7
Reactions
0
Participants
Timeline (top)
labeled ×4cross-referenced ×3

agent/tool_executor.py invokes get_pre_tool_call_block_message from two sites that omit session_id and tool_call_id, even though hermes_cli.plugins.get_pre_tool_call_block_message accepts both kwargs and a third call site in the same file already passes them. Result: any pre_tool_call plugin hook that needs to correlate a tool call to its session can't.

Root Cause

agent/tool_executor.py invokes get_pre_tool_call_block_message from two sites that omit session_id and tool_call_id, even though hermes_cli.plugins.get_pre_tool_call_block_message accepts both kwargs and a third call site in the same file already passes them. Result: any pre_tool_call plugin hook that needs to correlate a tool call to its session can't.

Fix Action

Workaround

We're vendoring a 23-line patch (hermes-tool-executor-session-id.patch) in our Docker build, applied with patch -p0 after Hermes install. Happy to open a PR with the same changes if helpful.

PR fix notes

PR #28982: fix: pass session_id and tool_call_id in pre_tool_call hook

Description (problem / solution / changelog)

Summary

Fixes #28961

The concurrent (execute_tool_calls_concurrent) and sequential (execute_tool_calls_sequential) paths called get_pre_tool_call_block_message without session_id and tool_call_id, unlike the _invoke_tool path which already forwarded both parameters.

This meant plugin hooks receiving pre_tool_call events could not correlate the call back to a specific session or tool invocation.

Changes

  • agent/tool_executor.py — Forward session_id=agent.session_id and tool_call_id=tool_call.id in both the concurrent (L129-131) and sequential (L504-506) execution paths, matching the existing _invoke_tool pattern.
  • tests/run_agent/test_run_agent.py — 2 new tests verifying kwargs propagation through both paths.

Testing

340/340 tests passed (0 regression)

New tests:

  • test_sequential_pre_tool_call_receives_session_id_and_tool_call_id
  • test_concurrent_pre_tool_call_receives_session_id_and_tool_call_id

Changed files

  • agent/tool_executor.py (modified, +4/-0)
  • tests/run_agent/test_run_agent.py (modified, +54/-0)

PR #28988: fix: read max_tokens from custom_providers per-model config

Description (problem / solution / changelog)

Summary

Fixes #28046 — max_tokens configured under custom_providers[].models.<model>.max_tokens was silently ignored, always defaulting to 4096.

The codebase already had get_custom_provider_context_length() for reading per-model context_length from custom_providers, but no equivalent for max_tokens.

Changes

  1. Extract get_custom_provider_model_field() — generic lookup helper that searches custom_providers entries for any per-model field. Replaces the inline logic in get_custom_provider_context_length().

  2. Add get_custom_provider_max_tokens() — thin wrapper over the generic helper, symmetric to get_custom_provider_context_length().

  3. Read max_tokens in agent_init.py — after the existing context_length custom_providers lookup, adds a symmetric block that calls get_custom_provider_max_tokens() when agent.max_tokens is None.

  4. 10 regression tests — full coverage for the new lookup: matching, trailing-slash insensitivity, zero/negative rejection, string coercion, coexistence with context_length, first-match-wins, None inputs.

Backward Compatibility

  • get_custom_provider_context_length() signature unchanged — custom_providers remains a positional arg (3 existing tests pass without modification)
  • New code only runs when agent.max_tokens is None (no override from top-level config or constructor) — pure additive fallback
  • 22/22 tests pass (12 existing context_length + 10 new max_tokens)

Root Cause Pattern

This is the same pattern as #28961 (pre_tool_call missing session_id) and #28984 (Typed Plugin Hook Protocol FR): configuration/state flows through call chains without schema enforcement, so adding a new config field requires manually updating every bridge — and omissions are silent.

Changed files

  • .dev-workflow/code-graph.db (added, +0/-0)
  • agent/agent_init.py (modified, +14/-0)
  • hermes_cli/config.py (modified, +66/-25)
  • tests/hermes_cli/test_custom_provider_max_tokens.py (added, +158/-0)
RAW_BUFFERClick to expand / collapse

Summary

agent/tool_executor.py invokes get_pre_tool_call_block_message from two sites that omit session_id and tool_call_id, even though hermes_cli.plugins.get_pre_tool_call_block_message accepts both kwargs and a third call site in the same file already passes them. Result: any pre_tool_call plugin hook that needs to correlate a tool call to its session can't.

Affected versions

  • v2026.5.7 (hermes-agent==0.14.0)
  • v0.13.x is also affected (same file, same omission)

The two sites

agent/tool_executor.py, around lines 128-131 (concurrent path inside execute_tool_calls_concurrent):

```python from hermes_cli.plugins import get_pre_tool_call_block_message block_message = get_pre_tool_call_block_message( function_name, function_args, task_id=effective_task_id or "", ) ```

And lines 519-521 (sequential dispatch path, same file):

```python from hermes_cli.plugins import get_pre_tool_call_block_message _block_msg = get_pre_tool_call_block_message( function_name, function_args, task_id=effective_task_id or "", ) ```

For comparison, _dispatch_one_tool_call at lines 752-753 / 772-773 — which fires AFTER the pre-call block check, with skip_pre_tool_call_hook=True — DOES pass both:

```python tool_call_id=tool_call.id, session_id=agent.session_id or "", ```

So the data is in scope at both omitting sites; it just isn't forwarded.

Why it matters

hermes_cli/plugins.py:1428 already declares the signature:

```python def get_pre_tool_call_block_message( tool_name: str, args: Optional[Dict[str, Any]], task_id: str = "", session_id: str = "", tool_call_id: str = "", ) -> Optional[str]: ```

A plugin hook that needs to correlate a tool call back to its session — e.g. for per-session policy state — receives session_id=\"\" from these two paths. Anything more sophisticated than "check the JSONL transcript" breaks here, and JSONL itself isn't an option mid-turn since gateway/run.py:7958 (append_to_transcript) only fires at turn-end, after agent_result returns. So a hook firing on tool call N+1 cannot see a skill_view on tool call N within the same turn unless the hook itself wires up a side channel — and that side channel needs session_id to scope per-session.

Proposed fix

Pass both kwargs at both sites, matching the _dispatch_one_tool_call pattern:

```python block_message = get_pre_tool_call_block_message( function_name, function_args, task_id=effective_task_id or "", session_id=agent.session_id or "", tool_call_id=tool_call.id, ) ```

(And the same on the sequential path's _block_msg = ... call.)

Diff is ~6 added lines total.

Workaround

We're vendoring a 23-line patch (hermes-tool-executor-session-id.patch) in our Docker build, applied with patch -p0 after Hermes install. Happy to open a PR with the same changes if helpful.

Repro

Any plugin hook that asserts on the session_id field of its stdin payload will see an empty string under v0.14.0:

```bash cat > /tmp/check_session.sh <<'HOOK' #!/usr/bin/env bash sid="$(jq -r '.session_id // ""' <&0)" [ -z "$sid" ] && { jq -nc --arg m "empty session_id" '{action:"block",message:$m}'; exit 0; } echo '{}' HOOK chmod +x /tmp/check_session.sh ```

Wire it as a pre_tool_call hook → every tool call in the concurrent or sequential paths gets blocked with "empty session_id".

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 - ✅(Solved) Fix tool_executor pre_tool_call hook calls omit session_id and tool_call_id (v0.14.0) [2 pull requests, 1 participants]