Skip to content

fix: hermes tools disable memory should only disable built-in memory - #45548

Open
Sugumaran-Balasubramaniyan wants to merge 5 commits into
NousResearch:mainfrom
Sugumaran-Balasubramaniyan:fix/45422-memory-tool-disable-scope
Open

Sugumaran-Balasubramaniyan wants to merge 5 commits into
NousResearch:mainfrom
Sugumaran-Balasubramaniyan:fix/45422-memory-tool-disable-scope

Conversation

@Sugumaran-Balasubramaniyan

Copy link
Copy Markdown

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:

  1. 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.

  2. 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

  1. 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.

  2. 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

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
@alt-glitch alt-glitch added type/bug Something isn't working P3 Low — cosmetic, nice to have comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint tool/memory Memory tool and memory providers labels Jun 13, 2026

@tonydwb tonydwb left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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_toolsets includes "memory" (explicit global opt-out) or enabled_toolsets is 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 teknium1 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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:189 suppresses guidance when _memory_enabled is false even if _user_profile_enabled is true. agent/agent_init.py:1366-1375 initializes that supported built-in store in the user-profile-only case, and tools/memory_tool.py:1082 exposes the user target.
  • The new disabled_toolsets contract is not preserved by all refreshes: tools/mcp_tool.py:5383 calls the helper without disabled_toolsets, and acp_adapter/server.py:1797-1807 builds 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_toolsets through 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.

Comment thread agent/system_prompt.py Outdated
# 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:

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

_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.

@teknium1 teknium1 added sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform area/memory Memory subsystem: store, providers, sync, background reviews labels Jul 14, 2026
…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.
@Sugumaran-Balasubramaniyan

Copy link
Copy Markdown
Author

Addressed Review Feedback

Thanks @teknium1 for the review. All three issues have been fixed:

1. User-profile-only guidance preserved (system_prompt.py)

MEMORY_GUIDANCE now injects when either _memory_enabled OR _user_profile_enabled is true:

# Before:
if "memory" in agent.valid_tool_names and agent._memory_enabled:

# After:
if "memory" in agent.valid_tool_names and (agent._memory_enabled or getattr(agent, "_user_profile_enabled", False)):

This ensures user-profile-only configs still get the memory tool guidance even when the built-in memory store is disabled.

2. disabled_toolsets threaded through MCP refresh (tools/mcp_tool.py)

The MCP refresh path was calling memory_provider_tools_enabled() without the disabled_toolsets argument, so a rebuild could re-add provider tools after initial suppression. Now passes both arguments:

# Before:
if "memory" in name_set or memory_provider_tools_enabled(getattr(agent, "enabled_toolsets", None)):

# After:
if "memory" in name_set or memory_provider_tools_enabled(getattr(agent, "enabled_toolsets", None), getattr(agent, "disabled_toolsets", None)):

3. disabled_toolsets threaded through ACP refresh (acp_adapter/server.py)

The ACP /tools command path built a SimpleNamespace tool_view without disabled_toolsets, so inject_memory_provider_tools could not read it. Added the field:

tool_view = SimpleNamespace(
    ...
    enabled_toolsets=toolsets,
    disabled_toolsets=getattr(state.agent, "disabled_toolsets", None),  # added
    ...
)

4. Fixed duplicate function definition (agent/memory_manager.py)

The PR diff had accidentally left two definitions of memory_provider_tools_enabled — the new one (incomplete) and the old one. Consolidated into a single clean function with the disabled_toolsets parameter.

Test Results

  • 103 tests in test_memory_provider.py — all pass
  • 410 tests in test_system_prompt.py + test_run_agent.py — all pass

Commit: fbfa979

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.
kiwipaulrob pushed a commit to kiwipaulrob/hermes-agent that referenced this pull request Aug 16, 2026
…-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.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/memory Memory subsystem: store, providers, sync, background reviews comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint P3 Low — cosmetic, nice to have sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades tool/memory Memory tool and memory providers type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: hermes tools disable memory should disable the built-in memory, not the entire memory ecosystem

4 participants