Memory Hardening
Wraps the built-in _memory plugin with resilience and circuit-breaker protection against FAISS recall timeouts, leaked asyncio tasks, stuck memorize threads, unbounded index caches, and ContextOverflow on memorize/solve. Includes a memory-history clamp (merged from the former _memory_resilience plugin), per-subdir circuit breakers, rate limiting, recall-wait crash guard, and a live WebUI dashboard. Zero source modifications to the official _memory plugin.
README
memory_hardening
Version: 0.6.0 · Plugin ID: memory_hardening · Author: Agent Zero Plugin (memory_hardening) · License: MIT
What it does
Wraps Agent Zero's built-in _memory plugin with resilience, observability, rate-limiting, and circuit-breaker protection against:
- FAISS recall timeouts that freeze the message loop
- Leaked asyncio tasks that pin memory across agents and projects
- Stuck
memorizebackground threads that never report back - Unbounded
Memory.indexglobal cache that leaks across many agents - Thundering-retry patterns after a slow FAISS query
- Coroutine leaks from
asyncio.wait_forcancellations during multi-hour provider outages - Missing
search_memoriesmethod after the upstream_memoryv1.2.0 regression (v0.4.0) - ContextOverflow on memorize / solve from the official
MAX_MSGS_CHARS = 80000budget being too large for a 32k utility model (v0.5.0, merged from_memory_resilience)
Zero source modifications to /a0/plugins/_memory/ -- purely additive extension hooks.
Quick start
The plugin is auto-discovered. To enable it:
touch /a0/usr/plugins/memory_hardening/.toggle-1
That's it. Open Settings -> Agent -> Memory Hardening to see the live dashboard with all features enabled at recommended defaults.
Features by version
| Phase | Version | Date | Feature | Default |
|---|---|---|---|---|
| 1 | 0.1.0 | 2026-07 | Watchdog (recall task tracker) | ON |
| 1 | 0.1.0 | 2026-07 | Circuit breaker (3 failures in 5 min) | ON |
| 1 | 0.1.0 | 2026-07 | Telemetry (latency, success/fail) | ON |
| 1 | 0.1.0 | 2026-07 | Health probe (FAISS + stuck tasks) | ON |
| 1 | 0.1.0 | 2026-07 | Agent notice (one-liner when breaker open) | OFF |
| 2 | 0.2.0 | 2026-07 | Memorize watchdog | ON |
| 2 | 0.2.0 | 2026-07 | Index cache GC (LRU, idle eviction) | ON |
| 2 | 0.2.0 | 2026-07 | Auto-recovery (quarantine + rebuild) | ON |
| 2 | 0.2.0 | 2026-07 | WebUI dashboard | ON |
| 3 | 0.3.0 | 2026-07 | Rate limiter (per-subdir token bucket) | ON |
| 3 | 0.3.0 | 2026-07 | Per-subdir circuit breaker | ON |
| 3 | 0.3.0 | 2026-07 | Adaptive recall interval (3-10) | REMOVED v0.6.0 (never had a working latency source) |
| 3 | 0.3.0 | 2026-07 | Memorize hard cancel | ON |
| 3 | 0.3.0 | 2026-07 | Quarantine (archive old indexes) | OFF |
| 3 | 0.3.0 | 2026-07 | Embedding hot-swap (shadow validation) | OFF |
| 4 | 0.4.0 | 2026-07-19 | Coroutine guard + UI-loop pulse | ON |
| 4 | 0.4.0 | 2026-07-26 | Recall method patch (_memory v1.2.0 regression) |
ON |
| 5 | 0.5.0 | 2026-08-10 | Memory history clamp (merged from _memory_resilience) |
ON |
| 5 | 0.5.2 | 2026-08-26 | Recall-wait TimeoutError guard (wraps RecallWait.execute) |
ON |
What the v0.4.0 recall_patch does
The _memory plugin v1.2.0 added a _safe_recall wrapper but accidentally deleted the search_memories method it calls. Without this patch, every memory recall silently fails with:
AttributeError: 'RecallMemories' object has no attribute 'search_memories'
The recall method patch in v0.4.0:
- Detects the missing method at runtime
- Injects the original
search_memoriesimplementation (copied verbatim from the pre-v1.2.0 backup) viasetattr - Marks the class with
_memory_hardening_patched = Trueto prevent re-patching - Is fully forward-compatible: if a future
_memoryupdate restores the method, the patch becomes a no-op (last_status: already_present) - Can be toggled off from Settings -> Agent -> Memory Hardening if you want to use the upstream version directly
Check the dashboard card Recall Method Patch or query GET /api/plugins/memory_hardening/stats.recall_patch for live status.
What the v0.5.0 history clamp does
The official _memory memorize / solve extensions truncate the chat history to MAX_MSGS_CHARS = 80000 before calling the utility model. That budget is too large for a 32k-context utility model and causes ContextOverflow errors the cascade can't recover from in a single cycle (every candidate returns the same overflow).
The history clamp (merged in v0.5.0 from the former _memory_resilience plugin) hooks util_model_call_before and caps call_data["message"] when the system prompt matches a memory-recall / memorize / solve pattern. The budget is read in this order:
memory_hardening.history_clamp_max_chars_override(if a positive int)_model_fallback.memory_memorize_max_chars(default 50000) — so a single setting governs both the cascade timeout budget and the memory budget50000(fallback default)
The clamp is a safe no-op when the history is already within budget, and it never crashes the utility call (any error leaves call_data unchanged). Check the dashboard card Memory History Clamp or query GET /api/plugins/memory_hardening/stats.history_clamp for live counters.
Telemetry
GET /api/plugins/memory_hardening/stats returns:
{
"telemetry": { "counters": {...}, "latency": {p50, p95, p99} },
"breaker": { "state": "closed|open|half_open" },
"watchdogs": { ... },
"memorize_watchdogs": { ... },
"index_gc": { ... },
"faiss_health": { ... },
"auto_recover": { ... },
"rate_limiter": { ... },
"per_subdir_breaker": { ... },
"memorize_canceller": { ... },
"embedding_swap": { ... },
"coroutine_guard": { "ticks": {...} },
"recall_patch": { "applied": 1, "last_status": "applied", ... },
"history_clamp": { "clamped": 0, "last_budget": 50000, "last_status": "never_run", ... },
"config": { ...all keys... }
}
Configuration
All 52 config keys live in default_config.yaml. Recommended values are loaded on first run. To customize, either edit default_config.yaml or use the WebUI settings panel.
Key toggles (all Phase 1+2+3+4 features default ON unless noted):
hardening_enabled— master kill switchwatchdog_enabled— recall task watchdogbreaker_enabled— global circuit breakertelemetry_enabled— collects metrics (tiny CPU cost)health_probe_enabled— periodic FAISS scanmemorize_watchdog_enabled— background-memorize watchdogindex_gc_enabled—Memory.indexLRU evictionauto_recover_enabled— quarantine + rebuild on FAISS corruptionrate_limiter_enabled— per-subdir token bucketper_subdir_breaker_enabled— one breaker per memory subdirmemorize_hard_cancel_enabled— cooperative cancel + stuck-thread scanquarantine_enabled— auto-archive old indexes (OFF by default)embedding_swap_enabled— shadow validation (OFF by default)coroutine_guard_enabled— leaked-coro cleanuprecall_patch_enabled— restores missingsearch_memories(v0.4.0)history_clamp_enabled— clamp memorize/solve util-model history (v0.5.0)history_clamp_max_chars_override— positive-int override; null reads from_model_fallback.memory_memorize_max_charshistory_clamp_inject_truncation_notice— append a trim notice to the clamped history
For full key listing with safe defaults, see default_config.yaml.
Installation
The plugin ships in /a0/usr/plugins/memory_hardening/. To enable:
# enable
touch /a0/usr/plugins/memory_hardening/.toggle-1
# disable
touch /a0/usr/plugins/memory_hardening/.toggle-0
When enabled, the framework auto-loads:
hooks.py->install()on first load,uninstall()on disable / pre-update- All extension hooks under
extensions/python/ - All API handlers under
api/(e.g.GET /api/plugins/memory_hardening/stats) - The WebUI settings panel from
webui/config.html
WebUI dashboard
Settings -> Agent -> Memory Hardening shows one card per feature:
- Watchdog (active count, reaped, cancelled)
- Circuit Breaker (state, window, threshold, cooldown)
- Per-Subdir Breaker (states per memory subdir)
- Telemetry (success/fail counters, latency p50/p95/p99)
- Health Probe (FAISS index health, stuck task reports)
- Rate Limiter (allowed/throttled per subdir)
- Quarantine (archived indexes)
- Embedding Hot-Swap (active swap, history)
- Coroutine Guard (tick count, last tick age)
- Memorize Watchdog + Memorize Canceller
- Index Cache GC (current size, evicted total)
- Auto-Recovery (rebuild history)
- Recall Method Patch (v0.4.0)
Each card has a checkbox to toggle its feature on/off and an "Advanced" expander to tune thresholds. Click Refresh for instant update; otherwise the dashboard polls the stats endpoint every 60 seconds.
API endpoints
| Route | Method | Purpose |
|---|---|---|
/api/plugins/memory_hardening/stats |
GET | Full telemetry snapshot (52 keys) |
/api/plugins/memory_hardening/reset_breaker |
POST | Force-reset the global circuit breaker |
Example:
curl http://localhost:50001/api/plugins/memory_hardening/stats | jq .recall_patch
Tests
Run from the plugin directory:
cd /a0/usr/plugins/memory_hardening
/opt/venv/bin/python tests/test_helpers.py # 18/18 tests
/opt/venv/bin/python tests/test_extensions.py # 5/5 tests
/opt/venv/bin/python tests/test_history_clamp.py # 29/29 tests
Total: 52/52 tests pass.
Files
memory_hardening/
├── AGENTS.md # developer-facing architecture docs
├── README.md # this file (user-facing overview)
├── USER_MANUAL.md # step-by-step usage guide
├── CHANGELOG.md # version history
├── plugin.yaml # manifest
├── default_config.yaml # all 52 config keys with recommended values
├── hooks.py # install / uninstall lifecycle
├── helpers/ # 17 helpers (circuit_breaker, recall_patch, history_clamp, ...)
├── extensions/ # 16 extension hooks across the lifecycle
├── api/ # stats.py + reset_breaker.py
├── webui/ # config.html dashboard
└── tests/ # test_helpers.py + test_extensions.py + test_history_clamp.py
Compatibility
- Agent Zero framework: v2.5+
_memoryplugin: v0.x, v1.0, v1.1, v1.2 (with v0.4.0 recall_patch), and future v1.3+ (patch becomes no-op)- Embedding models: any (shadow validation in
embedding_swapadds optional zero-downtime model changes) - OS: Linux (primary), macOS (best-effort)
License
MIT -- see LICENSE.
See also
USER_MANUAL.md— practical recipes and step-by-step instructionsCHANGELOG.md— version-by-version changesAGENTS.md— developer-facing architecture and contracts/a0/plugins/_memory/extensions/python/_50_recall_memories.py— the file patched at runtime in v0.4.0/a0/usr/workdir/memory_fix_backups/_50_recall_memories.py.bak— source of the embeddedsearch_memoriesmethod