Conversation
…are off With memory.memory_enabled and memory.user_profile_enabled both false, agent_init never builds a MemoryStore -- but check_memory_requirements() returned True unconditionally and MEMORY_GUIDANCE was gated only on the tool being present in valid_tool_names. So the tool shipped in every request's schema while answering "Memory is not available" on every call, and the system prompt still told the model to save durable facts there. Gate both on the config flags, using the store predicate for the tool and the already-resolved agent state for the guidance (config is not re-read mid-conversation, so the prompt stays byte-stable). Either flag alone still backs the tool, so only turning both off removes it. This lets a user running a third-party provider (Hindsight, Mem0, ...) turn the built-in files off without paying for the dead surface on every API call. The provider's own tools are unaffected: hiding the built-in tool moves the decision onto the toolset gate, and listing memory under agent.disabled_toolsets remains the only switch that takes those down.
Walks the real resolution chain -- config.yaml on a temp HERMES_HOME -> check_memory_requirements -> get_tool_definitions -- rather than mocking the availability check, since the bug was in how the flags reach the schema. Covers both flags off, either one alone, no config file at all, and a config read that raises (must fail open). Also asserts the external provider's tools survive with the built-in tool gone, so the fix cannot regress into taking Hindsight/Mem0 down with it, while disabled_toolsets keeps its documented "hide everything" meaning. The existing MEMORY_GUIDANCE test built a skip_memory agent whose flags were both false, so it was asserting the old tool-presence-only behavior; it now states its precondition and gains the false-case mirror.
Collaborator
|
Merged via #90559 — your two commits were cherry-picked onto current main with your authorship preserved in git log (merge commit 481bc93). On top of your fix we split MEMORY_GUIDANCE per store: with |
This was referenced Aug 20, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
With both built-in stores off:
agent_initnever builds aMemoryStore(agent/agent_init.pyonly constructs one whenmemory_enabled or user_profile_enabled), yet the surface it backs stays fully wired:check_memory_requirements()returnedTrueunconditionally, so thememorytool shipped in every request's schema. Every call answers"Memory is not available. It may be disabled in config or this environment."MEMORY_GUIDANCEwas gated only on"memory" in agent.valid_tool_names, so the system prompt still told the model to "Save durable facts using the memory tool" — a multi-paragraph block paid for on every API call, instructing the model to use a tool that cannot work.There was no way out via config.
agent.disabled_toolsets: [memory]drops the dead tool and the guidance, butmemory_provider_tools_enabled()returnsFalsefor that same list, so it takes the external provider's tools down with it. Users on a third-party backend (Hindsight, Mem0, Honcho, …) had to keep the built-in dead surface to keep their provider alive.Fix
Two gates, one predicate — "is either built-in store actually enabled":
tools/memory_tool.py—check_memory_requirements()now consults the config flags via a newbuiltin_memory_stores_enabled(). Either flag alone still backs the tool (user_profile_enabled: truekeepstarget="user"working), so only turning both off removes it. Reads throughload_config_readonly()(mtime-cached) and fails open on any read error, so an unreadable config can never strip a working tool.agent/system_prompt.py—MEMORY_GUIDANCEis gated on the already-resolvedagent._memory_enabled/agent._user_profile_enabled, not on a fresh config read. Config is resolved once at init, so the prompt stays byte-stable for the life of the conversation and prompt caching is untouched.getattr(..., True)keeps the rare code paths that build an agent view withoutagent_initon the old behavior.The external provider is deliberately left alone. Hiding the built-in tool flips
memory_tool_presenttoFalseinmemory_provider_tools_enabled(), which moves the decision onto the toolset gate —enabled_toolsetsnamingmemory(orNone, or a bundle resolving to it) still injects the provider's tools.disabled_toolsets: [memory]keeps its documented "hide everything" meaning. No change to that function was needed; the new tests pin the behavior so it cannot silently regress.Docs:
website/docs/user-guide/features/memory.mdnow states what both-flags-off does and how it differs from thedisabled_toolsetsswitch.Test plan
New
tests/agent/test_builtin_memory_disabled_surface.pywalks the real chain —config.yamlon a tempHERMES_HOME→check_memory_requirements→get_tool_definitions— rather than mocking the availability check, since the bug was in how the flags reach the schema:memoryabsent from the schemamemorypresentmemorypresent (no behavior change by default)memory_tool_present=Falsefor["memory", ...]and forNonedisabled_toolsets: ["memory"]still suppresses provider toolsinject_memory_provider_toolsinjects a provider tool with no built-in memory tool in the surfacetests/run_agent/test_run_agent.py: the existingtest_memory_guidance_when_memory_tool_loadedbuilt askip_memory=Trueagent whose flags were both false, so it was asserting the old presence-only behavior; it now states its precondition and gains a false-case mirror plus a user-profile-only case.423 passed, 0 failed.
tests/agent/andtests/cron/are clean. The one unrelated failure I hit locally istests/tools/test_video_generation_tool_surface_matrix.py, which fails on an unmodifiedmaincheckout after2dea073a1c("dispatch image/video FAL strictly on the stored hermes tools selection") and touches nothing in this diff.Related
Narrower than #30814 (which also adds write-time guards) and overlapping the prompt half of #36085 / #45548, neither merged. This PR adds the tool-schema half and the explicit non-regression coverage for the external provider, which is what made the built-in surface unremovable in the first place.