fix: hermes tools disable memory should only disable built-in memory - #45548
Sugumaran-Balasubramaniyan wants to merge 5 commits into
Conversation
hermes tools disable memory currently disables the entire memory ecosystem, including external/third-party memory providers. The config alternative (memory_enabled: false) only disables the built-in memory tool but still injects MEMORY_GUIDANCE into the system prompt, confusing the agent. Two fixes: 1. agent/agent_init.py: Change the external memory provider injection gate from requires memory in enabled_toolsets to injects for any non-empty toolset list. External providers are now suppressed only when enabled_toolsets is explicitly empty or memory is in disabled_toolsets (the global nuclear option). 2. agent/system_prompt.py: Gate MEMORY_GUIDANCE on both memory in valid_tool_names AND agent._memory_enabled, so the behavioral prompt is suppressed when the built-in memory store is off. Also updates test fixtures and adds test coverage for the new gates. Closes NousResearch#45422
tonydwb
left a comment
There was a problem hiding this comment.
Code Review Summary
Verdict: Approved
Good fix (Issue #45422). The hermes tools disable memory command was suppressing not just the built-in memory tool but also external memory providers configured via memory.provider in config.yaml. This fix:
- Tights the gate logic: only disables external providers when
disabled_toolsetsincludes "memory" (explicit global opt-out) orenabled_toolsetsis empty (constrained platform) - Allows external providers (e.g., mnemosyne, honcho) to work alongside the rest of the toolset
- Includes comprehensive tests for all gate conditions
Well-scoped fix with good test coverage. No concerns.
Reviewed by Hermes Agent
…r_tools with NousResearch#45422 gate logic Conflict resolution strategy: - agent_init.py: Keep main's refactored call to inject_memory_provider_tools() (removes the inlined block from the original PR) - memory_manager.py: Update memory_provider_tools_enabled() to the NousResearch#45422 gate logic — any non-empty enabled_toolsets allows external memory providers, regardless of whether 'memory' is explicitly named. Add disabled_toolsets parameter for the global nuclear option. - test_memory_provider.py: Use shared inject_memory_provider_tools helper (not inlined mirror), add disabled_toolsets support to the test fixture, keep NousResearch#45422 test cases (test_toolsets_without_memory_still_injects + disabled_toolsets tests)
teknium1
left a comment
There was a problem hiding this comment.
Thanks for addressing two real current-main behaviors: external provider schemas are still blocked by the built-in memory toolset gate in agent/memory_manager.py:82-116, and prompt guidance is still keyed only to the built-in tool in agent/system_prompt.py:219-224.
Problems
- The changed condition in
agent/system_prompt.py:189suppresses guidance when_memory_enabledis false even if_user_profile_enabledis true.agent/agent_init.py:1366-1375initializes that supported built-in store in the user-profile-only case, andtools/memory_tool.py:1082exposes theusertarget. - The new
disabled_toolsetscontract is not preserved by all refreshes:tools/mcp_tool.py:5383calls the helper withoutdisabled_toolsets, andacp_adapter/server.py:1797-1807builds a view without it. A rebuild can therefore re-add provider tools after initial suppression.
Suggested changes
- Preserve guidance for either enabled built-in store and add user-profile-only coverage.
- Thread
disabled_toolsetsthrough MCP and ACP refreshes and test those paths. - The linked #53486 discussion identifies #39531, #53486, and this PR as competing mechanisms; align on the canonical mechanism before salvage.
Automated hermes-sweeper review.
| # Tool-aware behavioral guidance: only inject when the tools are loaded | ||
| tool_guidance = [] | ||
| if "memory" in agent.valid_tool_names: | ||
| if "memory" in agent.valid_tool_names and agent._memory_enabled: |
There was a problem hiding this comment.
_memory_enabled alone is too narrow: when memory_enabled: false but user_profile_enabled: true, agent initialization still creates the built-in store and the memory tool still supports target: user, but this removes its guidance. Preserve guidance for the user-profile-only case and add a regression test.
…le guidance Addressed review feedback from teknium1 on PR NousResearch#45548: 1. system_prompt.py: MEMORY_GUIDANCE now injects when either _memory_enabled OR _user_profile_enabled is true, so user-profile-only configs still get guidance (previously suppressed). 2. memory_manager.py: Fixed duplicate function definition — the new signature with disabled_toolsets was inserted but the old body was left below it. Consolidated into a single clean function. 3. tools/mcp_tool.py: Threaded disabled_toolsets through the MCP refresh path's memory_provider_tools_enabled() call (was missing the second argument, so a rebuild could re-add provider tools after suppression). 4. acp_adapter/server.py: Added disabled_toolsets to the SimpleNamespace tool_view in the ACP /tools command path, so inject_memory_provider_tools can read it when building the tool surface.
Addressed Review FeedbackThanks @teknium1 for the review. All three issues have been fixed: 1. User-profile-only guidance preserved (
|
Resolve conflicts in: - agent/memory_manager.py: Adopt main's memory_provider_tools_enabled with memory_tool_present/resolve_toolset logic, preserving PR NousResearch#45422's expanded docstring documenting the gate logic and disabled_toolsets behavior. - tests/agent/test_memory_provider.py: Sync docstring with actual gate logic. - tools/mcp_tool.py: Use memory_tool_present parameter in MCP refresh path.
…-in tool on the store predicate Rebased onto current main (was 068f587, ~1400 commits behind) and renumbered the config migration from v35 to v38: main had since assigned v35 (background process notifications), v36 (delegation max_iterations 50→250), and v37 (delegation concurrency 3→10). Keeps main's ladder intact and appends the memory rename as the newest slot; DEFAULT_CONFIG._config_version bumped 37 → 38 so the migration actually fires for pre-rename configs. Addresses AI-review feedback on the original submission (comment 1. Migration dead-code risk (version not bumped): fixed — _migrate_to_38 registered and _config_version bumped to 38. Verified end-to-end: a v37 config carrying memory.memory_enabled is rewritten to builtin_enabled and stamped v38. 2. Remaining merged-path readers of the legacy key: verified none — the only runtime consumers read through builtin_memory_enabled() (which reads raw config), the agent._memory_enabled attribute set from it, or the migration itself. 3. Absent read-path alias: added — `hermes config get memory.memory_enabled` now resolves the persisted legacy value pre-migration, then falls back to the canonical memory.builtin_enabled post-migration (mirrors the existing write-path alias in set_config_value). 4. Fail-open asymmetry: documented in builtin_memory_enabled()'s docstring — on config-read errors the tool may advertise while a provider cannot exist, until the config error clears. Original 5-layer scope unchanged (rename + migration/alias, check_fn gating, state-aware tool error, docs fixes, `hermes memory status` provider-aware state line). memory_enabled now scopes to the built-in MEMORY.md/USER.md store; the recommended provider combo (builtin_enabled: false + memory.provider) reads coherently instead of as "memory disabled" (issues NousResearch#60805, NousResearch#32624). Closes NousResearch#60805, NousResearch#32624. References NousResearch#30814, NousResearch#50076, NousResearch#45548, NousResearch#5544.
Summary
hermes tools disable memory currently disables the entire memory ecosystem, including external/third-party memory providers. The config alternative (memory_enabled: false) only disables the built-in memory tool but still injects MEMORY_GUIDANCE into the system prompt, confusing the agent.
Root Cause
Two separate coupling issues:
In agent_init.py, the gate for external memory provider injection checked "memory" in agent.enabled_toolsets. When a user disabled the built-in memory toolset, external providers were also suppressed.
In system_prompt.py, MEMORY_GUIDANCE was gated only on "memory" in valid_tool_names -- which is always true since the built-in memory check_fn returns True even when memory_enabled: false.
Fix
agent/agent_init.py: External memory providers now inject for any non-empty toolset list, regardless of whether the built-in memory toolset is specifically named. Suppressed only when enabled_toolsets is explicitly empty or memory is in disabled_toolsets.
agent/system_prompt.py: MEMORY_GUIDANCE now requires both "memory" in valid_tool_names AND agent._memory_enabled.
Also adds 4 new test cases and updates the mirror test class.
Closes #45422