Skip to content

fix: move pairing_store and hooks init into GatewayRunner.__init__ - #115

Closed
Ridwannurudeen wants to merge 1 commit into
NousResearch:mainfrom
Ridwannurudeen:fix/110-gateway-runner-hooks-init
Closed

fix: move pairing_store and hooks init into GatewayRunner.__init__#115
Ridwannurudeen wants to merge 1 commit into
NousResearch:mainfrom
Ridwannurudeen:fix/110-gateway-runner-hooks-init

Conversation

@Ridwannurudeen

Copy link
Copy Markdown

Summary

Fixes #110.

PairingStore and HookRegistry were initialised inside _flush_memories_before_reset(), which only runs when a session expires. Since start() calls self.hooks.discover_and_load() unconditionally on every startup, GatewayRunner crashed with:

AttributeError: 'GatewayRunner' object has no attribute 'hooks'

on every fresh gateway start before any session had ever expired.

Root cause

A recent refactor (hooks system added ~Feb 16, follow-up commits Feb 23–27) accidentally left the initialisation block inside the wrong method.

Fix

Move the two blocks to the end of __init__() where they belong, and remove the duplicate from _flush_memories_before_reset(). No logic changes — purely moving 6 lines to the correct scope.

# __init__() — after self._pending_approvals
from gateway.pairing import PairingStore
self.pairing_store = PairingStore()

from gateway.hooks import HookRegistry
self.hooks = HookRegistry()

Test plan

  • hermes gateway start completes without AttributeError
  • hermes gateway status shows the service active
  • Session auto-reset (_flush_memories_before_reset) still works as before — self.hooks and self.pairing_store are now available throughout the object lifetime

…set to __init__

PairingStore and HookRegistry were initialised inside
_flush_memories_before_reset(), which only runs on session expiry.
start() calls self.hooks.discover_and_load() unconditionally, so
GatewayRunner crashed with AttributeError on every fresh start before
any session had expired.

Move the two init blocks to the end of __init__() where they belong,
and remove the duplicate from _flush_memories_before_reset().

Fixes NousResearch#110
@teknium1

Copy link
Copy Markdown
Contributor

Fixed with fec5d59

Thank you

@teknium1 teknium1 closed this Feb 27, 2026
jarvis-stark-ops added a commit to 1Team-Engineering/hermes-agent that referenced this pull request Jun 16, 2026
* feat(mcp): scaffold hermes-tools-mcp stdio server with initialize

NousResearch#109

Minimal stdio JSON-RPC server that responds to MCP 'initialize' with
the expected protocol version + capabilities. Tools surface comes in
follow-up commits. Env-bound to (profile, project) scope.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat(mcp): tools/list returns filtered Hermes tool surface

NousResearch#109

Filters the live Hermes tool registry to the v1 surface defined in
spec §4.1. Includes special hermes_set_task tool for per-turn task
context updates.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat(mcp): tools/call dispatches to Hermes tool registry

NousResearch#109

hermes_set_task updates per-turn task context (task_id, workspace).
All other tools/call requests filter through ALLOWED_TOOLS, then
dispatch via registry.dispatch() which handles both sync and async
tools. Denied tools (kanban_dispatch etc.) return isError=true.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat(kanban): add task_sessions table for relay scope tracking

NousResearch#113

Per spec §4.7 — tracks (profile, project) → tmux session, claude
session_id, FIFO path, MCP config path, project root, scope cwd,
task count, timestamps. Helpers: upsert_session, get_session,
touch_session, mark_compacted, evict_session, list_idle_sessions.

* feat(relay): project derivation + task header builder

NousResearch#114

derive_project maps a kanban workspace to its 'project' key:
  - scratch workspaces -> 'scratch'
  - git workspaces -> basename of git root
  - else raises ProviderError (B11: no silent default)

build_task_header prepends '[task_id=... workspace=...]' to each
user prompt so claude knows the current task without an MCP roundtrip.

* feat(provider): register claude-code-relay ProviderProfile

NousResearch#112

Registers the provider via the existing bundled-plugin pattern under
plugins/model-providers/claude-code-relay/__init__.py. api_mode=
'claude_code_relay' is a new tag for the transport defined in the
next commit. auth_type='none' because the claude binary handles its
own OAuth — the relay just shells out.

* feat(transport): scope spawn + send turn helpers

NousResearch#112

ScopeContext bundles (profile, project, workspace). ensure_scope
either finds a live tmux or invokes relay-spawn-scope.sh (writing
the MCP config first). send_turn prepends the task header and
relays through relay-send.sh, returning the captured response.
Also added monkeypatch for derive_project_root in spawn test since
tmp_path is not a git repo.

* feat(transport): OpenAI-compat ChatCompletion response shape

NousResearch#112

chat_completion() returns the standard OpenAI ChatCompletion object.
Only the latest user message is forwarded — prior conversation rides
on claude's in-session memory. Usage fields left null; /compact hook
in next task captures /usage and backfills.

* feat(kanban): /compact between-task hook for relay scopes

NousResearch#115

After a task with provider=claude-code-relay completes or blocks,
send /compact to its (profile, project) scope. Per B10, failures
are logged but never block the completion. Records last_compacted_at
on task_sessions.

Also: add tasks.provider column to SCHEMA_SQL + additive ALTER migration;
set conn.row_factory=sqlite3.Row in init_db test-fixture shortcut so
complete_task works correctly in :memory: tests.

Spike 2 (2026-06-15) confirmed /compact returns 'Not enough messages
to compact' on short sessions; that's handled cleanly via the timeout
in relay-send.sh slash-command extension.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat(relay): idle-eviction cron step

NousResearch#116

evict_idle_scopes deletes task_sessions rows whose last_used_at is
older than HERMES_RELAY_IDLE_EVICT_SECS (default 86400 = 24h),
and tears down the tmux session via relay-kill-scope.sh.

Wire into nightly-hermes-maintenance follows separately.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat(relay): boot-check that Hermes Stop hook is installed

NousResearch#109

B3: Stop hook is mandatory for the relay to detect turn-end via FIFO.
Boot-check raises ProviderError with installer path if the hook is
missing — fail loud instead of silently hanging 180s per turn.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(relay): validate ScopeContext.profile + project against B8 regex

Per code review on PR #20 — the shell relay-spawn-scope.sh validates
^[a-zA-Z0-9_-]+$ but Python-side _write_mcp_config writes /tmp/hermes-mcp-{slug}.json
BEFORE invoking the shell. Slugs containing / or .. would have allowed
path traversal out of /tmp.

Add same regex check in ScopeContext.__post_init__. Closes B8 (spec §3).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* fix(relay): chmod 0o600 on /tmp MCP config files

Defense-in-depth: prevent workspace paths in the MCP config from leaking
to other users sharing /tmp. Per code review on PR #20.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* fix(kanban): honor HERMES_RELAY_BIN in /compact hook for consistency

Transport reads HERMES_RELAY_BIN; the kanban_db /compact hook should
too. Per code review on PR #20.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* fix(relay): preserve task_sessions row when relay-kill-scope.sh fails

Per code review on PR #20 — previously evicted the DB row even on kill
failure, orphaning the tmux session with no record to retry. Now check
rc; only evict DB row on rc=0. Failed scopes get retried on next cron tick.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* fix(relay): log WARNING when relay-send exits 4 (Stop-hook timeout)

Per code review on PR #20 — exit 4 means partial response, but callers
had no signal. Add a logger.warning so operators see the partial in logs.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

---------

Co-authored-by: Jarvis <jarvis@Kaipos-Mac-mini.local>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Meraniya pushed a commit to Meraniya/hermes-agent that referenced this pull request Aug 6, 2026
* docs: evaluate Mnemosyne as a memory provider

Adds docs/mnemosyne.md covering mnemosyne-oss/mnemosyne @ v3.15.0
(mnemosyne-hermes 0.5.0) as a candidate tenth external memory provider.

Verdict: opt-in plugin, pinned — not a default, and not a store for
CRM/vault content.

In its favour: 2285 tests pass locally at CI parity in 5m23s; local-first
is real (core/embeddings.py::_is_api_model gates the OpenRouter route
behind an explicit opt-in); Actions are SHA-pinned; MCP SSE refuses a
non-loopback bind without a token; 326 commits in 90 days with a two-day
fix turnaround on NousResearch#491; and the provider implements all four
MemoryProvider abstract methods plus most optional hooks.

Against it:

- CI sets MNEMOSYNE_NO_EMBEDDINGS=1, so the hybrid vector path — the whole
  value proposition — is never exercised. Every green test runs degraded.
- Degradation is silent (NousResearch#474, NousResearch#518); reproduced locally at 1/3 recall on
  trivially easy queries with the model unreachable.
- ALL_TOOL_SCHEMAS exposes 40 tools / ~6.5k tokens per request against a
  README claiming 23, and mnemosyne_triple_end is advertised over MCP with
  no _TOOL_HANDLERS entry, so calling it raises ValueError.
- Two divergent in-tree copies of the provider reached by different install
  routes (hermes_memory_provider/ 3778 lines vs mnemosyne_hermes/ 2882).
- NousResearch#487 (gateway RSS jump) and NousResearch#537 (hermes update drops the provider) land
  on us directly.
- Open correctness bugs NousResearch#507/NousResearch#506/NousResearch#524/NousResearch#525/NousResearch#523, and NousResearch#482 with 50 of 106
  config keys silently ignored.
- Benchmark claims do not survive their own README: BEAM Recall@10 = 20% at
  every scale, end-to-end 65.2% against Hindsight's 73.4% in their table.

Governance noted: the CLA pre-authorises relicensing under any future
license, bus factor is ~2, and SECURITY.md points at the retired tracker
while describing a confidential-issue feature GitHub does not have.

Pilot conditions are listed in the doc. Documentation only.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NxqJBGm7soA7QefpRBSDAu

* fix(nix): refresh stale npm lockfile hashes for tui and web

The fetchNpmDeps hashes in nix/tui.nix and nix/web.nix no longer matched
the committed ui-tui/package-lock.json and web/package-lock.json, so the
`nix` job failed on every PR with "Nix build failed due to stale npm
lockfile hash".

Drift is pre-existing on main, not introduced here: both nix files and
both lockfiles are byte-identical between main and this branch, so the
same check fails on main. Fixed here because it blocks this PR and the
fix is mechanical — the replacement hashes are the values nix itself
reported in the job log.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NxqJBGm7soA7QefpRBSDAu

---------

Co-authored-by: Claude <noreply@anthropic.com>
mansonyu2000 pushed a commit to mansonyu2000/hermes-agent that referenced this pull request Aug 10, 2026
brainstorm: agent-coding-workflow Skill → 5-phase plan
tdd: pending (MIM tests in todo)
verified:
- zentao task list --execution=28 → 13 tasks created (IDs 32-44)
- zentao story NousResearch#115 created under product NousResearch#10
- ZENTAO_DB_HOST=192.168.3.23 required for MySQL connection

Added:
- Zentao: 13 MIM tasks (F1-F3 + security) under execution NousResearch#28
- Zentao: story NousResearch#115 (MIM聊天V1) under product NousResearch#10
- tasks/mim-chat-v1-plan.md + tasks/mim-chat-v1-todo.md (双向关联zentao_id)
- bin/sync-to-zentao.py (解析todo.md → 更新zentao任务状态)
- docs/templates/: spec/todo/acceptance 模板
- docs/guides/dev-setup.md: 开发环境搭建指南
- docs/governance/CONTRIBUTING-WinPeek.md: WinPeek贡献指南
- docs/{reference-library,test,code,deploy}/README.md: 目录占位
- .claude/commands/: /onboard /status /handoff (本地斜杠命令)

Changed:
- CLAUDE-module.md: 新增 🚀新会话启动引导段落
- tasks/README.md: MIM状态→🟢进行中, 新增Zentao追踪表
- docs/README.md: 新增工作流目录 + 模板引用
- .zentao/mapping.json: 新增MIM任务映射

Fixed:
- tools/winpeek_tools.py: _handle_mim_update_profile IDOR — 要求password认证
  (security review finding: unauthorized profile update)

Co-Authored-By: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] hermes-gateway.service fails to start: 'GatewayRunner' object has no attribute 'hooks'

2 participants