feat(memory): add pluggable memory provider interface

Introduces MemoryProvider ABC, MemoryManager orchestrator, and
BuiltinMemoryProvider for the existing MEMORY.md/USER.md system.

Key design decisions:
- Built-in memory is ALWAYS active, never disabled by external providers
- Multiple providers can be active simultaneously
- Prefetch results from all providers are merged per-turn
- Sync fans out to all providers after each turn
- Each provider can expose its own tools

Three registration paths:
1. Built-in (BuiltinMemoryProvider) — always first, not removable
2. First-party (Honcho stays as-is for now, migration in follow-up)
3. Plugin — ctx.register_memory_provider() in plugin system

Files:
- agent/memory_provider.py — ABC with core + optional lifecycle hooks
- agent/memory_manager.py — orchestrator, single integration point
- agent/builtin_memory_provider.py — wraps existing MemoryStore
- hermes_cli/plugins.py — register_memory_provider() + accessor
- run_agent.py — MemoryManager wired alongside existing Honcho code
- tests/agent/test_memory_provider.py — 37 tests

This establishes the interface for all pending memory backend PRs
(#1811 Hindsight, #2732 RetainDB, #2933 Mem0, #3499 Byterover,
#3369 OpenViking, #2351 Holographic, #727 Cognitive) to implement
as plugins rather than one-off integrations.
This commit is contained in:
Teknium
2026-03-29 16:41:22 -07:00
parent 13871e9a8e
commit 1452c81941
7 changed files with 1490 additions and 1 deletions

View File

@@ -152,6 +152,28 @@ class PluginContext:
self._manager._plugin_tool_names.add(name)
logger.debug("Plugin %s registered tool: %s", self.manifest.name, name)
# -- memory provider registration ----------------------------------------
def register_memory_provider(self, provider) -> None:
"""Register a memory provider (must implement MemoryProvider ABC).
The provider will be added to the MemoryManager during agent init.
Providers registered this way are additive — they never disable
the built-in MEMORY.md/USER.md store.
Example plugin __init__.py::
from my_memory_backend import MyMemoryProvider
def register(ctx):
ctx.register_memory_provider(MyMemoryProvider())
"""
self._manager._memory_providers.append(provider)
logger.debug(
"Plugin %s registered memory provider: %s",
self.manifest.name, getattr(provider, "name", "unknown"),
)
# -- hook registration --------------------------------------------------
def register_hook(self, hook_name: str, callback: Callable) -> None:
@@ -183,6 +205,7 @@ class PluginManager:
self._plugins: Dict[str, LoadedPlugin] = {}
self._hooks: Dict[str, List[Callable]] = {}
self._plugin_tool_names: Set[str] = set()
self._memory_providers: List = [] # MemoryProvider instances from plugins
self._discovered: bool = False
# -----------------------------------------------------------------------
@@ -528,3 +551,13 @@ def get_plugin_toolsets() -> List[tuple]:
result.append((ts_key, label, desc))
return result
def get_plugin_memory_providers() -> List:
"""Return MemoryProvider instances registered by plugins.
Called during AIAgent init to add plugin memory providers to
the MemoryManager alongside built-in providers.
"""
manager = get_plugin_manager()
return list(manager._memory_providers)