Skip to content

feat(memory): persistent agent memory system with dashboard UI#606

Draft
kovtcharov wants to merge 4 commits intomainfrom
feature/agent-memory
Draft

feat(memory): persistent agent memory system with dashboard UI#606
kovtcharov wants to merge 4 commits intomainfrom
feature/agent-memory

Conversation

@kovtcharov
Copy link
Collaborator

Summary

  • MemoryStore (src/gaia/agents/base/memory_store.py): SQLite + FTS5 data layer with knowledge CRUD, conversation history, tool call logging, confidence scoring/decay, deduplication (Szymkiewicz-Simpson ≥80%), and temporal awareness (due_at/reminded_at)
  • MemoryMixin (src/gaia/agents/base/memory.py): agent integration layer — 5 LLM-callable tools (remember, recall, update_memory, forget, search_past_conversations), heuristic preference extraction, frozen system-prompt injection + per-turn dynamic context for KV-cache reuse
  • Memory Dashboard REST API (src/gaia/ui/routers/memory.py): 15 FastAPI endpoints — knowledge CRUD, entity/context browsing, tool performance stats, conversation search, FTS rebuild, pruning
  • Memory Dashboard UI (src/gaia/apps/webui/src/components/MemoryDashboard.*): React/TypeScript — stats cards, activity timeline, knowledge browser with FTS search/filter, entity graph, tool performance table, error log, conversation viewer
  • discovery.py: system discovery module (git history, filesystem, installed apps, browser history) for bootstrap onboarding
  • CLI: gaia memory command (stats, search, add, dashboard)
  • Docs: docs/guides/memory.mdx, docs/sdk/sdks/memory.mdx, docs/spec/agent-memory-architecture.md, docs/spec/openjarvis-memory-analysis.md

Reliability hardening

10 rounds of corner-case analysis and fixes:

Fix Detail
Rollback discipline All write paths wrapped in try/except: rollback; raise — prevents Python sqlite3 implicit transactions from being committed out-of-order
Decay idempotency apply_confidence_decay() adds AND updated_at < cutoff so items are not double-decayed on rapid restarts
get_sessions() ordering Replaced MIN(CASE WHEN role='user') with correlated subquery ordered by id ASC
Multi-match extraction _extract_heuristics() uses finditer() instead of search()
args_json truncation log_tool_call() truncates args to 500 chars (matching result_summary/error)
Content truncation remember() and update_memory() tools now actually truncate to 2000 chars before storing

Test plan

  • uv run pytest tests/unit/test_memory_store.py — 372 unit tests for MemoryStore
  • uv run pytest tests/unit/test_memory_mixin.py — MemoryMixin lifecycle, tool registration, truncation
  • uv run pytest tests/unit/test_memory_router.py — REST API endpoint validation
  • gaia memory stats — CLI smoke test
  • gaia chat --ui → Memory Dashboard tab in browser

@github-actions github-actions bot added documentation Documentation changes dependencies Dependency updates agents Agent system changes cli CLI changes tests Test changes electron Electron app changes labels Mar 20, 2026
itomek and others added 3 commits March 21, 2026 16:11
## Summary

- **`gaia init` now installs RAG dependencies** for `chat`, `rag`, and
`all` profiles — adds `pip_extras` field to profile definitions and a
new `_install_pip_extras()` step that detects editable vs package
install, tries `uv pip` first with `pip` fallback
- **Added `self.rag` None guards** to 8 RAG tools in `rag_tools.py` that
were crashing with `'NoneType' object has no attribute 'index_document'`
when RAG deps not installed
- **Widened ChatAgent RAG init exception catch** from `ImportError` to
`Exception` with warning-level logging and debug traceback
- **Updated Agent UI docs** to include `[rag]` in install instructions
(`[ui,rag]`)

## Test plan

- [x] Lint passing (black, isort, pylint, flake8)
- [x] All 1104 unit tests passing
- [ ] `gaia init --profile chat` installs RAG deps automatically
- [ ] Agent UI document indexing works after `pip install -e ".[rag]"`
- [ ] RAG tools return actionable error when deps not installed (instead
of crashing)

🤖 Generated with [Claude Code](https://claude.com/claude-code)
C-1: Guard winreg import and all registry-scanning methods in discovery.py
     so the module loads cleanly on Linux/macOS where winreg is absent.
     Also guard _scan_credential_manager() behind sys.platform check to
     avoid subprocess.CREATE_NO_WINDOW AttributeError on non-Windows.

C-3: Replace direct _lock/_conn access in CLI with two new MemoryStore
     public methods: get_source_counts() and delete_by_source(source).
     delete_by_source() wraps FTS cleanup + DELETE in a single atomic
     transaction with rollback, removing the per-ID loop that could
     leave knowledge/FTS diverged on partial failure.

C-4: Add close_store() to memory router module; call it from FastAPI
     lifespan shutdown so the WAL is checkpointed and the SQLite
     connection is released cleanly on server exit.

M-2: list_knowledge endpoint now excludes sensitive items by default.
     New include_sensitive=false query param (default false) controls
     visibility; sensitive=true still filters to sensitive-only.

M-6: Add append-only comment to conversations FTS trigger block noting
     that an AFTER UPDATE trigger would be required if store_turn()
     ever changes to update existing rows.

Tests: +9 tests (394 total) covering get_source_counts, delete_by_source
       rollback discipline, and all three sensitive filter modes in the router.
- Fix _original_user_input=None fallback bug in _after_process_query
  (getattr default ignored None; switch to `or` to handle init state)
- Extract VALID_CATEGORIES/MAX_CONTENT_LENGTH/MAX_TURN_LENGTH and other
  magic numbers to named module-level constants in memory_store.py
- Import constants in memory.py to eliminate duplicate category sets
  and ensure truncation limits stay in sync across all call sites
- DRY: memory router imports VALID_CATEGORIES from data layer instead
  of redefining its own copy
- Clean up unused imports in test files (F401/F811 flake8 violations)
- 394 unit tests passing, flake8 clean
@kovtcharov kovtcharov force-pushed the feature/agent-memory branch from e0eff31 to 068eead Compare March 21, 2026 23:13
Replace substring `"github.com" in url_lower` with urlparse().hostname
comparison to fix CodeQL CWE-20 "Incomplete URL substring sanitization".
A crafted URL like http://evil.com/github.com could otherwise bypass the
check. Hostname equality/suffix match is unambiguous.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agents Agent system changes cli CLI changes dependencies Dependency updates documentation Documentation changes electron Electron app changes tests Test changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants