Conversation
- 22 content pages across Guide, Concepts, and Reference sections - Custom indigo/cyan theme with Lucide icons and Mermaid diagrams - GitHub Actions workflow for GitHub Pages deployment - Live preview: https://mempalace-docs.netlify.app/
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
On Windows, projects containing git-submodule junctions or dev-drive reparse points cause iterdir() to list the entry successfully but Path.is_dir() to raise OSError when it calls stat() internally. Reproducer: any Windows project with a submodule checked out as a junction (e.g. skills/pr-perfect) crashes mempalace init with: OSError: [WinError 448] The path cannot be traversed because it contains an untrusted mount point Fix: wrap every is_dir() call in detect_rooms_from_folders with try/except OSError so the scanner skips inaccessible entries and continues rather than aborting. Covers both the top-level pass and the one-level-deep nested pass. Two new tests mock the OSError on specific paths and verify the function returns correct rooms from the remaining accessible entries.
…ues (#569) * fix: align cmd_compress dict keys with compression_stats() return values * test: align compress test mocks with actual compression_stats() keys * fix: address review — add Total: assertion, move stats key test to test_dialect.py
* refactor: add stage-1 backend abstraction seam Introduce the first upstreamable storage seam for MemPalace without bringing in the PostgreSQL spike or any benchmark artifacts. This change adds a small backend package with: - BaseCollection as the minimal collection contract - ChromaBackend/ChromaCollection as the default implementation It then routes the main runtime collection consumers through that seam: - palace.py - searcher.py - layers.py - palace_graph.py - mcp_server.py - miner.status() Behavioral constraints kept for stage 1: - ChromaDB remains the only backend and the default path - no config/env backend selection yet - no PostgreSQL code - no benchmark or research files - existing tests stay unchanged Important compatibility details: - read paths now call the seam with create=False so they still surface the existing 'no palace found' behavior instead of silently creating empty collections - write paths keep create=True semantics through palace.get_collection() - layers/searcher retain a chromadb module attribute so the existing mock-based tests can keep patching PersistentClient unchanged - ChromaBackend only creates palace directories on create=True, which preserves mocked read-path tests that use fake read-only paths Verification: - python3 -m py_compile mempalace/backends/__init__.py mempalace/backends/base.py mempalace/backends/chroma.py mempalace/palace.py mempalace/searcher.py mempalace/layers.py mempalace/palace_graph.py mempalace/mcp_server.py mempalace/miner.py - pytest -q # 529 passed, 106 deselected * refactor: clean up stage-1 seam compatibility shims Tighten the stage-1 backend abstraction branch after review. This follow-up does three small things: - keep the chromadb compatibility hook in searcher.py and layers.py, but express it through the backends.chroma module so it no longer reads like an accidental unused import - fix the palace_graph.py helper alias to avoid the local name collision flagged by ruff (imported helper vs local _get_collection wrapper) - preserve the existing mock-based test patch points unchanged while keeping the new backend seam intact Why this matters: - the direct form looked like a dead import in review, even though it was intentionally preserving the existing test seam ( and ) - palace_graph.py had a real lint issue ( redefinition) that was small but worth fixing before a public PR Verification: - /opt/homebrew/bin/ruff check mempalace/backends/__init__.py mempalace/backends/base.py mempalace/backends/chroma.py mempalace/palace.py mempalace/searcher.py mempalace/layers.py mempalace/palace_graph.py mempalace/mcp_server.py mempalace/miner.py - pytest -q tests/test_layers.py tests/test_searcher.py - pytest -q # 529 passed, 106 deselected * docs: explain backend shim imports in search paths Add short code comments in searcher.py and layers.py explaining why the module-level `chromadb` alias remains after the stage-1 backend seam refactor. The alias is intentional: it preserves the existing mock patch points used by the current test suite (`mempalace.searcher.chromadb.PersistentClient` and `mempalace.layers.chromadb.PersistentClient`) while the runtime logic now flows through the backend abstraction. This keeps the public PR easier to review because the apparent "unused import" now has an explicit reason next to it. Verification: - /opt/homebrew/bin/ruff check mempalace/searcher.py mempalace/layers.py - pytest -q tests/test_layers.py tests/test_searcher.py * refactor: reuse a default backend instance in palace helper Tighten the stage-1 backend seam by promoting the default Chroma backend adapter to a module-level singleton in `mempalace/palace.py`. This keeps the stage-1 scope unchanged — Chroma is still the only backend wired in this branch — but avoids constructing a fresh `ChromaBackend()` object on every `get_collection()` call. The backend is stateless today, so this is a readability/cleanup change rather than a behavioral one. Why this helps: - makes `palace.get_collection()` read like a real default factory instead of an inline constructor call - keeps the stage-1 branch a little cleaner before opening the public PR - does not widen the backend surface or change any config/runtime behavior Verification: - python3 -m py_compile mempalace/palace.py - pytest -q tests/test_miner.py tests/test_layers.py tests/test_searcher.py - pytest -q # 529 passed, 106 deselected * fix: harden read-only seam behavior and update seam tests Preserve the stage-1 backend abstraction while closing the real read-path regression surfaced in PR review. What changed: - make ChromaBackend.get_collection(create=False) fail fast when the palace directory does not exist instead of letting PersistentClient create it as a side effect - update miner.status() to call get_collection(..., create=False) so status keeps the historical 'No palace found' behavior - remove the temporary chromadb shim aliases from layers.py and searcher.py now that the tests patch the seam directly - add focused tests for the new backends package, including ChromaCollection delegation and ChromaBackend create=True/create=False behavior - retarget layer/searcher tests to patch the backend seam instead of patching chromadb.PersistentClient inside production modules - add a regression test that status() does not create an empty palace when the target path is missing Verification: - ruff check . - uv run pytest -q - uv run pytest -q tests/test_backends.py tests/test_cli.py tests/test_mcp_server.py tests/test_layers.py tests/test_searcher.py tests/test_miner.py Notes: - the separate benchmark/slow/stress layer was started as a soak but not used as the merge gate for this PR branch * refactor: drop duplicate mcp collection cache declaration Remove a redundant `_collection_cache = None` assignment in `mempalace/mcp_server.py` left over after the stage-1 backend seam refactor. This does not change behavior; it only trims review noise in the MCP server module after the read-path hardening pass. Verification: - ruff check mempalace/mcp_server.py - uv run pytest -q tests/test_mcp_server.py --------- Co-authored-by: Sergey Kuznetsov <sergey@iterudit.com>
…g, concurrency safety, and WAL fixes (#647) Addresses findings from security audit (ref #401): inconsistent sanitization across MCP tools, unfiltered argument dispatch allowing audit trail spoofing, SQLite concurrency issues, WAL permission race condition, sensitive content in logs, and internal error leakage to MCP callers. Co-authored-by: Israel Domínguez <isra@MacBook-Pro-de-Israel.local> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…esolves #635) (#667) * feat: MCP reliability — inode detection, WAL rotation, metadata cache, search limits Infrastructure hardening for the MCP server: - Detect palace DB replacement via inode tracking (repair command support) - WAL rotation to prevent unbounded WAL growth - _fetch_all_metadata() + _get_cached_metadata() with 60s TTL for taxonomy/status - _MAX_RESULTS cap (100) with limit clamping [1, _MAX_RESULTS] - max_distance parameter for similarity threshold in search - Handle all notifications/* methods, null arguments, method=None - Remove duplicate _client_cache = None declarations - searcher.py max_distance parameter passthrough Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat: new MCP tools (get/list/update drawer, hook settings, memories filed), export, normalize New MCP tools: - mempalace_get_drawer: fetch single drawer by ID with full content - mempalace_list_drawers: paginated listing with wing/room filter - mempalace_update_drawer: update content/wing/room on existing drawers - mempalace_hook_settings: get/set hook behavior (silent_save, desktop_toast) - mempalace_memories_filed_away: check latest checkpoint status Also includes: - exporter.py: export palace as browsable markdown files - normalize.py: tool_use/tool_result capture for richer transcript mining - layers.py: updated for new tool integration - config.py: hook settings properties (hook_silent_save, hook_desktop_toast) Depends on PR 3 (reliability) for _MAX_RESULTS, _metadata_cache, WAL logging. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: normalize.py handles string messages and Read offset type mismatch Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: params null guard, L2→cosine docs, empty tool_use_map key guard - Handle explicit null in MCP params (request.get("params") or {}) - Fix search tool description: L2 → cosine distance (collection uses hnsw:space=cosine) - Guard against empty string key in tool_use_map from malformed JSONL entries Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: rename ambiguous var 'l' to 'line' (E741 lint) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: address code review findings (5 issues) 1. min_similarity backwards-compat: convert similarity to distance scale (1.0 - similarity) instead of passing raw value as max_distance 2. Restore structured error reporting (error + partial fields) in tool_status, tool_list_wings, tool_list_rooms, tool_get_taxonomy — reverts silent except:pass that dropped #647 security hardening 3. inode cache: remove falsy-zero short-circuit so missing DB file triggers reconnect instead of reusing stale client 4. _fetch_all_metadata: check for empty batch before extending/advancing offset to prevent infinite loop on concurrent deletion 5. KG initialization: only override path when --palace is explicit; default runs use KnowledgeGraph's built-in default path Co-authored-by: jphein <jphein@users.noreply.github.com> --------- Co-authored-by: jp <jp@jphein.com> Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> Co-authored-by: jphein <jphein@users.noreply.github.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
ORT_DISABLE_COREML is not a recognized ONNX Runtime environment variable. ONNX Runtime does not expose a global env var to disable individual execution providers -- providers are selected per session via the providers argument to InferenceSession. Setting it had zero effect. The mitigation was added in df33550 (v3.1.0) with the stated goal of fixing the #74 ARM64 segfault. Two problems: the env var doesn't work as described above, and #74 is a null-pointer crash in chromadb_rust_bindings.abi3.so -- not an ONNX issue, so disabling CoreML would not have fixed it anyway. #521 has since traced the actual macOS arm64 crashes (both mine and search) to the 0.x chromadb hnswlib binding. Filtering CoreMLExecutionProvider at the ONNX layer leaves the hnswlib C++ crash intact, so the real fix is upgrading chromadb to 1.5.4+, which #581 proposes. This PR only removes the misleading no-op and leaves a NOTE pointing at #521 / #581. Closes #397
Note from code review: (1) silent exception swallow on migration failure means caller proceeds with potentially corrupt DB — consider returning a boolean or re-raising in a follow-up. (2) No blob length validation before int.from_bytes — malformed rows could produce wrong seq_id values. Both are edge cases; the fix is still valuable for the common chromadb 0.6→1.5 migration path.
…176) The module-level `ssl._create_default_https_context = ssl._create_unverified_context` disables certificate verification for ALL urllib requests in the process, not just the benchmark's HuggingFace downloads. This silently exposes the benchmark runner to MITM attacks. If a specific environment needs to skip verification (e.g. corporate proxy), users can set `PYTHONHTTPSVERIFY=0` or pass a custom ssl context per-request rather than globally patching the ssl module. Co-authored-by: Tadao <tadao@travisfixes.com>
… mcp_server.py (#449) Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Path("~/foo") does not expand tilde on its own, causing
`mempalace split ~/some/dir` to silently find no files.
Fix by calling .expanduser().resolve() in both places the
path is constructed: cmd_split in cli.py (defensive, at the
CLI boundary) and main() in split_mega_files.py (the root cause).
Co-authored-by: Brooke Whatnall <brookewhatnall@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
…177) The `_load_api_key()` function in longmemeval_bench.py and locomo_bench.py searched for API keys in a fixed path (`~/.config/lu/keys.json`) using personal key names (`anthropic_milla`, `anthropic_claude_code_main`). This leaks internal infrastructure details into the public codebase and trains contributors to store credentials in a non-standard location rather than using the standard ANTHROPIC_API_KEY env var. Simplified to: CLI flag > env var > empty string. Updated help text and HYBRID_MODE.md docs to match. Co-authored-by: Tadao <tadao@travisfixes.com>
…tor docs (#679) The repo moved to the MemPalace org but several docs still point at the old milla-jovovich URLs. Also, CONTRIBUTING.md tells people to PR against main while the actual workflow (per ROADMAP.md) targets develop. Files touched: - CONTRIBUTING.md: clone URL, issues URL, PR target branch - examples/gemini_cli_setup.md: clone URL - integrations/openclaw/SKILL.md: homepage and license URLs
docs: add VitePress documentation site
ci: fix github pages publishing
Agent-Logs-Url: https://github.com/MemPalace/mempalace/sessions/775f2fc4-3051-462e-8586-6d694b55da0d Co-authored-by: igorls <4753812+igorls@users.noreply.github.com>
- query_sanitizer: require matching quote pair in _strip_wrapping_quotes - query_sanitizer: re-check MIN_QUERY_LENGTH after trim in tail_sentence path - migrate: neutral confirmation message accurate for both migrate and repair - cli: os.path.normpath instead of rstrip to handle '/' root edge case
…deletion Harden palace deletion, WAL redaction, and MCP search input handling
Full changelog from git history and merged PRs: - v3.0.0 (2026-04-06): initial public release - v3.1.0 (2026-04-09): 80+ commits, security hardening, Windows compat, tests 20→92 - Unreleased/v3.2.0: 50+ commits, i18n, backend seam, migrate command, more security Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: hash full content in tool_add_drawer drawer ID * style: apply ruff format * style: fix ruff format for CI ruff 0.4.x
#755) '(r)roject' had a duplicate 'r', making it read as '(r)roject' instead of the intended '(r)project'. Small UX fix — no behavior change. Co-authored-by: Arnold Wender <arnold.wender@gmail.com>
…757) When external tools write to the palace database (CLI mining, scripts), the MCP server's cached ChromaDB collection becomes stale — its HNSW index doesn't know about new vectors. Develop already invalidates on inode changes (catches rebuilds) but not on mtime changes (misses in-place writes). This PR: - Adds st_mtime tracking alongside st_ino in _get_client; invalidates the cached client on either change. - Adds the mempalace_reconnect MCP tool for explicit cache flush. Original author: @jphein (#663). Original approval: @Ari4ka. Skips test_missing_db_invalidates_cache on Windows (ChromaDB holds chroma.sqlite3 open).
#677) (#685) * fix: parse Claude.ai privacy export with messages key and sender field (#677) The privacy-export branch in _try_claude_ai_json only checked for the "chat_messages" key, missing exports that use "messages" instead. It also only read the "role" field while real privacy exports use "sender". Both gaps caused the file to fall through to plain-text, producing a single giant drawer. Changes: - Accept "messages" alongside "chat_messages" in the conversation-object guard and inner extraction. - Accept "sender" alongside "role" as the author field. - Fall back to a top-level "text" key when content blocks are empty. - Produce one transcript per conversation instead of concatenating all conversations into a single blob. - Extract shared logic into _collect_claude_messages helper. - Add 6 regression tests covering each variant. * style: apply ruff format to normalize.py * fix: guard against null text field in Claude.ai export parsing item.get("text", "").strip() crashes when "text" is explicitly null in the JSON (legal and observed in some exports). Use (item.get("text") or "").strip() and add a regression test. --------- Co-authored-by: Igor Lins e Silva <4753812+igorls@users.noreply.github.com>
PR #761 bumped pyproject.toml to 3.2.0 but missed three other version strings, causing test_version_consistency to fail on develop CI (macos, linux 3.11, windows). - mempalace/version.py: 3.1.0 → 3.2.0 (unblocks test_version_consistency) - README.md: version badge shield 3.1.0 → 3.2.0 - integrations/openclaw/SKILL.md: 3.1.0 → 3.2.0 - CHANGELOG.md: rename [Unreleased] → [3.2.0] — 2026-04-13, add entries for #685, #690, #707, #716, #734, #755, #757, #761 Verified locally: 689/689 tests pass, ruff clean.
There was a problem hiding this comment.
Pull request overview
Release cut for v3.2.0 (final v3) that synchronizes version metadata across the repo, refreshes release notes/docs, and includes the accumulated develop-branch functionality/bugfix work (docs site, backend seam, mining/search hardening, i18n, migrations/repairs, etc.).
Changes:
- Bump/align version strings and release metadata to 3.2.0 (pyproject,
mempalace/version.py, README badge, OpenClaw skill, changelog). - Add a VitePress documentation site under
website/and a GitHub Pages deployment workflow. - Refactor/read-path hardening across core modules (backend seam usage, search/query sanitization updates, migrate/repair confirmations, mining edge-case guards) plus expanded test coverage.
Reviewed changes
Copilot reviewed 95 out of 105 changed files in this pull request and generated 8 comments.
Show a summary per file
| File | Description |
|---|---|
| website/reference/python-api.md | Adds Python API overview page for docs site. |
| website/reference/modules.md | Adds module map / project structure reference. |
| website/reference/mcp-tools.md | Adds MCP tools parameter reference documentation. |
| website/reference/contributing.md | Adds docs-site contributing page. |
| website/reference/cli.md | Adds CLI command reference for docs site. |
| website/reference/benchmarks.md | Adds benchmark summary page for docs site. |
| website/reference/api-reference.md | Adds parameter-level API reference for docs site. |
| website/public/icons/wrench.svg | Adds docs site icon asset. |
| website/public/icons/shield-check.svg | Adds docs site icon asset. |
| website/public/icons/search.svg | Adds docs site icon asset. |
| website/public/icons/git-merge.svg | Adds docs site icon asset. |
| website/public/icons/file-text.svg | Adds docs site icon asset. |
| website/public/icons/building-2.svg | Adds docs site icon asset. |
| website/package.json | Adds docs site Node/bun tooling manifest. |
| website/index.md | Adds VitePress home page content. |
| website/guide/searching.md | Adds docs guide page (searching). |
| website/guide/openclaw.md | Adds docs guide page (OpenClaw). |
| website/guide/mining.md | Adds docs guide page (mining). |
| website/guide/mcp-integration.md | Adds docs guide page (MCP integration). |
| website/guide/local-models.md | Adds docs guide page (local models). |
| website/guide/hooks.md | Adds docs guide page (hooks). |
| website/guide/getting-started.md | Adds docs getting started guide. |
| website/guide/gemini-cli.md | Adds docs guide page (Gemini CLI). |
| website/guide/configuration.md | Adds docs guide page (configuration). |
| website/guide/claude-code.md | Adds docs guide page (Claude Code). |
| website/concepts/the-palace.md | Adds docs concepts page (palace structure). |
| website/concepts/memory-stack.md | Adds docs concepts page (memory stack). |
| website/concepts/knowledge-graph.md | Adds docs concepts page (knowledge graph). |
| website/concepts/contradiction-detection.md | Adds docs concepts page (planned feature). |
| website/concepts/agents.md | Adds docs concepts page (agent diaries). |
| website/concepts/aaak-dialect.md | Adds docs concepts page (AAAK dialect). |
| website/.vitepress/theme/style.css | Adds custom VitePress styling. |
| website/.vitepress/theme/index.ts | Adds VitePress theme entrypoint. |
| website/.vitepress/config.mts | Adds VitePress site configuration + sidebar/nav. |
| website/.gitignore | Adds docs-site ignores. |
| tests/test_searcher.py | Updates mocking strategy for searcher tests. |
| tests/test_room_detector_local.py | Adds regression tests for Windows OSError/reparse points. |
| tests/test_query_sanitizer.py | Expands sanitize_query tests for new behavior/constants. |
| tests/test_miner.py | Adds dry-run regression test + status behavior test. |
| tests/test_migrate.py | Adds safety/confirmation tests for migrate. |
| tests/test_exporter.py | Adds exporter tests for structure/content/empty export. |
| tests/test_dialect.py | Adds coverage for compression_stats() key set. |
| tests/test_convo_miner.py | Adds tests for sentinel registration to prevent reprocessing. |
| tests/test_convo_miner_unit.py | Adds regression test for long AI response preservation. |
| tests/test_config.py | Adds sanitize_name() Unicode + validation tests. |
| tests/test_cli.py | Adds repair confirmation/db-presence tests; updates compress stats expectations. |
| tests/test_backends.py | Adds tests for backend seam + BLOB seq_id repair. |
| tests/conftest.py | Ensures KnowledgeGraph fixture closes connections. |
| ROADMAP.md | Adds roadmap document (v3 stability/v4 plan). |
| README.md | Updates ASCII-ish architecture diagram + version badge. |
| pyproject.toml | Bumps project version to 3.2.0. |
| MISSION.md | Adds mission narrative document. |
| mempalace/version.py | Updates package __version__ to 3.2.0. |
| mempalace/split_mega_files.py | Expands/resolves --source path handling. |
| mempalace/searcher.py | Uses backend seam collection getter; adds filter builder and distance filtering metadata. |
| mempalace/room_detector_local.py | Adds OSError guards/logging around Windows reparse point failures. |
| mempalace/query_sanitizer.py | Tightens MAX_QUERY_LENGTH and improves tail/quote trimming logic. |
| mempalace/palace.py | Routes collection access via backend seam; tweaks mtime epsilon check. |
| mempalace/palace_graph.py | Uses backend seam collection getter (create=False). |
| mempalace/miner.py | Adjusts process_file() return contract; updates status() to use count-based limit. |
| mempalace/migrate.py | Adds db presence check + destructive action confirmation helpers. |
| mempalace/layers.py | Uses backend seam collection getter; adds L1 scan limit and reuses filter builder. |
| mempalace/knowledge_graph.py | Adds threading lock around SQLite operations for safety. |
| mempalace/instructions/init.md | Updates init instructions to include --yes. |
| mempalace/i18n/zh-TW.json | Adds i18n strings/patterns (Traditional Chinese). |
| mempalace/i18n/zh-CN.json | Adds i18n strings/patterns (Simplified Chinese). |
| mempalace/i18n/test_i18n.py | Adds i18n smoke-test script (non-pytest by default). |
| mempalace/i18n/ko.json | Adds i18n strings/patterns (Korean). |
| mempalace/i18n/ja.json | Adds i18n strings/patterns (Japanese). |
| mempalace/i18n/fr.json | Adds i18n strings/patterns (French). |
| mempalace/i18n/es.json | Adds i18n strings/patterns (Spanish). |
| mempalace/i18n/en.json | Adds i18n strings/patterns (English). |
| mempalace/i18n/de.json | Adds i18n strings/patterns (German). |
| mempalace/i18n/init.py | Adds i18n loader/translator utilities. |
| mempalace/exporter.py | Adds markdown export facility for palace drawers. |
| mempalace/entity_detector.py | Fixes prompt typo in interactive entity classification. |
| mempalace/dialect.py | Adds lang support pulling AAAK/regex from i18n dictionaries. |
| mempalace/convo_miner.py | Adds sentinel registration + long-response chunking behavior. |
| mempalace/config.py | Expands sanitize_name() Unicode support; adds hook settings helpers. |
| mempalace/cli.py | Adds destructive confirmation for repair/migrate; improves path handling; updates compress stats printing. |
| mempalace/backends/chroma.py | Adds Chroma backend adapter + BLOB seq_id repair pre-client init. |
| mempalace/backends/base.py | Defines minimal backend collection interface. |
| mempalace/backends/init.py | Exposes backend interfaces/implementations. |
| mempalace/init.py | Removes ineffective CoreML env workaround; updates commentary. |
| integrations/openclaw/SKILL.md | Updates skill version/homepage links for 3.2.0. |
| examples/gemini_cli_setup.md | Updates repo clone URL. |
| CONTRIBUTING.md | Updates repo clone URL and PR base branch guidance. |
| CLAUDE.md | Adds Claude-focused project documentation (principles, structure, commands). |
| CHANGELOG.md | Finalizes 3.2.0 changelog section and release notes. |
| benchmarks/longmemeval_bench.py | Removes hardcoded credential-path fallback for API keys; clarifies messaging. |
| benchmarks/locomo_bench.py | Removes hardcoded credential-path fallback for API keys. |
| benchmarks/HYBRID_MODE.md | Updates benchmark docs to match API key loading behavior. |
| benchmarks/convomem_bench.py | Removes global SSL verification bypass. |
| .gitignore | Ignores Claude/Codex config directories. |
| .github/workflows/deploy-docs.yml | Adds GitHub Pages deployment workflow for docs site. |
| .github/workflows/ci.yml | Runs CI on both main and develop. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| col = get_collection(palace_path) | ||
| total = col.count() | ||
|
|
||
| if total == 0: | ||
| print(" Palace is empty — nothing to export.") | ||
| return {"wings": 0, "rooms": 0, "drawers": 0} |
There was a problem hiding this comment.
export_palace() calls get_collection(palace_path) with the default create=True, which will create the palace directory/collection when the path doesn’t exist. Exporting should be read-only; consider calling get_collection(..., create=False) and returning a friendly "No palace found"/empty result without materializing a new palace on disk.
| for wing, rooms in batch_grouped.items(): | ||
| safe_wing = _safe_path_component(wing) | ||
| wing_dir = os.path.join(output_dir, safe_wing) | ||
| os.makedirs(wing_dir, exist_ok=True) | ||
|
|
||
| for room, drawers in rooms.items(): | ||
| safe_room = _safe_path_component(room) | ||
| room_path = os.path.join(wing_dir, f"{safe_room}.md") | ||
| key = (wing, room) | ||
| is_new = key not in opened_rooms | ||
|
|
||
| with open(room_path, "a" if not is_new else "w", encoding="utf-8") as f: | ||
| if is_new: | ||
| f.write(f"# {wing} / {room}\n\n") | ||
| opened_rooms.add(key) |
There was a problem hiding this comment.
Room files are written under sanitized paths (safe_wing/safe_room), but opened_rooms uses the unsanitized (wing, room) tuple as the key. If two distinct wing/room names sanitize to the same path, this can cause overwrites or incorrect append/overwrite behavior. Consider keying by room_path (or (safe_wing, safe_room)) to match the actual output files.
| "|------|-------|---------|", | ||
| ] | ||
| for wing, room_count, drawer_count in index_rows: | ||
| index_lines.append(f"| [{wing}]({wing}/) | {room_count} | {drawer_count} |") |
There was a problem hiding this comment.
The export index links use the raw wing value in the URL (([{wing}]({wing}/))), but wing directories are created using the sanitized safe_wing. This can generate broken links (and potentially unsafe paths) for wings with spaces or special characters. Use safe_wing for the link target while keeping the display text as the original wing name.
| index_lines.append(f"| [{wing}]({wing}/) | {room_count} | {drawer_count} |") | |
| safe_wing = _safe_path_component(wing) | |
| index_lines.append(f"| [{wing}]({safe_wing}/) | {room_count} | {drawer_count} |") |
| ```python | ||
| from mempalace.searcher import search_memories | ||
|
|
||
| results = search_memories( | ||
| query="why did we switch to GraphQL", | ||
| wing="myapp", # optional filter | ||
| room="architecture", # optional filter | ||
| n_results=5, | ||
| ) |
There was a problem hiding this comment.
The search_memories() example omits the required palace_path argument. As written, this snippet will raise a TypeError for missing palace_path. Update the example to pass palace_path (positional or keyword) so it matches the actual API.
| ### `search_memories(query, palace_path, wing=None, room=None, n_results=5) → dict` | ||
|
|
||
| Programmatic search returning a dict. Used by the MCP server. | ||
|
|
||
| | Parameter | Type | Default | Description | | ||
| |-----------|------|---------|-------------| | ||
| | `query` | `str` | — | Search query text | | ||
| | `palace_path` | `str` | — | Path to ChromaDB palace directory | | ||
| | `wing` | `str` | `None` | Filter by wing name | | ||
| | `room` | `str` | `None` | Filter by room name | | ||
| | `n_results` | `int` | `5` | Maximum number of results | | ||
|
|
||
| **Returns:** | ||
| ```python | ||
| { | ||
| "query": str, | ||
| "filters": {"wing": str | None, "room": str | None}, | ||
| "results": [ | ||
| { | ||
| "text": str, # verbatim drawer content | ||
| "wing": str, # wing name | ||
| "room": str, # room name | ||
| "source_file": str, # original file basename | ||
| "similarity": float, # 0.0 to 1.0 | ||
| } | ||
| ] | ||
| } | ||
| ``` | ||
|
|
||
| On error: `{"error": str, "hint": str}` | ||
|
|
There was a problem hiding this comment.
api-reference.md documents search_memories(query, palace_path, wing=None, room=None, n_results=5) and a return shape that doesn’t include the newer fields (max_distance parameter, distance per hit, total_before_filter on the response). Please update this reference so it matches mempalace.searcher.search_memories()’s current signature and result schema.
| ## PR Guidelines | ||
|
|
||
| 1. Fork the repo and create a feature branch: `git checkout -b feat/my-thing` | ||
| 2. Write your code | ||
| 3. Add or update tests if applicable | ||
| 4. Run `pytest tests/ -v` — everything must pass | ||
| 5. Commit with clear [conventional commits](https://www.conventionalcommits.org/): | ||
| - `feat: add Notion export format` | ||
| - `fix: handle empty transcript files` | ||
| - `docs: update MCP tool descriptions` | ||
| - `bench: add LoCoMo turn-level metrics` | ||
| 6. Push to your fork and open a PR against `main` | ||
|
|
||
| ## Code Style |
There was a problem hiding this comment.
This guide says to open PRs against main, but the repository contribution workflow targets develop (see the root CONTRIBUTING.md change in this PR). Please update this page to avoid sending contributors to the wrong base branch.
| +------------------------------------------------------------+ | ||
| ¦ WING: Person ¦ | ||
| ¦ ¦ | ||
| ¦ +----------+ +----------+ ¦ | ||
| ¦ ¦ Room A ¦ --hall-- ¦ Room B ¦ ¦ | ||
| ¦ +----------+ +----------+ ¦ | ||
| ¦ ¦ ¦ | ||
| ¦ v ¦ | ||
| ¦ +----------+ +----------+ ¦ | ||
| ¦ ¦ Closet ¦ ---> ¦ Drawer ¦ ¦ | ||
| ¦ +----------+ +----------+ ¦ | ||
| +---------+--------------------------------------------------+ | ||
| ¦ | ||
| tunnel | ||
| │ | ||
| ┌─────────┼──────────────────────────────────────────────────┐ | ||
| │ WING: Project │ | ||
| │ │ │ | ||
| │ ┌────┴─────┐ ──hall── ┌──────────┐ │ | ||
| │ │ Room A │ │ Room C │ │ | ||
| │ └────┬─────┘ └──────────┘ │ | ||
| │ │ │ | ||
| │ ▼ │ | ||
| │ ┌──────────┐ ┌──────────┐ │ | ||
| │ │ Closet │ ───▶ │ Drawer │ │ | ||
| │ └──────────┘ └──────────┘ │ | ||
| └─────────────────────────────────────────────────────────────┘ | ||
| ¦ | ||
| +---------+--------------------------------------------------+ | ||
| ¦ WING: Project ¦ | ||
| ¦ ¦ ¦ | ||
| ¦ +----------+ +----------+ ¦ | ||
| ¦ ¦ Room A ¦ --hall-- ¦ Room C ¦ ¦ | ||
| ¦ +----------+ +----------+ ¦ | ||
| ¦ ¦ ¦ | ||
| ¦ v ¦ | ||
| ¦ +----------+ +----------+ ¦ | ||
| ¦ ¦ Closet ¦ ---> ¦ Drawer ¦ ¦ | ||
| ¦ +----------+ +----------+ ¦ | ||
| +------------------------------------------------------------+ |
There was a problem hiding this comment.
The updated architecture diagram uses the broken-bar character ¦, which is non-ASCII and can render inconsistently across terminals/fonts. If the goal is a portable plain-text diagram (and to avoid encoding issues), consider using standard ASCII | instead.
| def _register_file(collection, source_file: str, wing: str, agent: str): | ||
| """Write a sentinel so file_already_mined() returns True for 0-chunk files. | ||
|
|
||
| Without this, files that normalize to nothing or produce zero chunks are | ||
| re-read and re-processed on every mine run because nothing was written to | ||
| ChromaDB on the first pass. | ||
| """ | ||
| sentinel_id = f"_reg_{hashlib.sha256(source_file.encode()).hexdigest()[:24]}" | ||
| collection.upsert( | ||
| documents=[f"[registry] {source_file}"], | ||
| ids=[sentinel_id], | ||
| metadatas=[ | ||
| { | ||
| "wing": wing, | ||
| "room": "_registry", | ||
| "source_file": source_file, | ||
| "added_by": agent, | ||
| "filed_at": datetime.now().isoformat(), | ||
| "ingest_mode": "registry", | ||
| } | ||
| ], | ||
| ) |
There was a problem hiding this comment.
The registry sentinel is written into the main mempalace_drawers collection as a normal document (documents=["[registry] ..."], room="_registry"). This will inflate drawer counts (status, exports, etc.) and can surface in unfiltered searches. Consider storing sentinels in a separate collection (or adding an explicit filter in search/status/export to exclude ingest_mode="registry").
* fix: disambiguate hook block reasons to name MemPalace explicitly (#666) Replace "your memory system" with explicit MemPalace references and tool names (mempalace_diary_write, mempalace_add_drawer, mempalace_kg_add) in stop and precompact hook block reasons. This prevents Claude Code from misinterpreting the hook as a native auto-memory save instruction. Updated in both Python (hooks_cli.py) and standalone shell scripts. Also fix CONTRIBUTING.md Getting Started to show the fork-first workflow, matching the PR Guidelines section. * fix: remove chromadb <0.7 upper bound — blocks 1.x installs The current constraint `chromadb>=0.5.0,<0.7` forces pip to install chromadb 0.6.x, but palaces created with chromadb 1.x (which is what the mempalace dev environment actually uses — 1.5.7 per uv.lock) have an incompatible SQLite schema. Specifically, chromadb 0.6.x fails with `KeyError: '_type'` when opening a collection written by 1.x. This means a fresh `pip install mempalace` gives users a chromadb version that cannot read palaces created in the maintainer's own environment. The fix removes the upper bound so pip can resolve to the current stable chromadb release. Reproduction: python3 -m venv .venv && source .venv/bin/activate pip install mempalace # installs chromadb 0.6.3 # Try opening a palace created with chromadb 1.x: # -> _get_collection() returns None, tool_status() returns "No palace found" pip install chromadb==1.5.7 # force upgrade # -> tool_status() returns real data (26k drawers in our case) --------- Co-authored-by: z3tz3r0 <kittipan.wang@gmail.com> Co-authored-by: AlyciaBHZ <50111876+AlyciaBHZ@users.noreply.github.com> Co-authored-by: Ben Sigman <1872138+bensig@users.noreply.github.com>
# Conflicts: # CONTRIBUTING.md
sync: merge main into release/3.2.0 (preserve history)
Summary
Cuts v3.2.0 — the final v3 release, giving every user a safe pin target (
pip install 'mempalace<4') before v4 work begins on a separate branch.This PR merges 58 commits accumulated on
developsince v3.1.0 (2026-04-09), plus one commit on this branch finalizing the release metadata.What's in v3.2.0
See CHANGELOG.md for the full list. Highlights:
Packaging
chromadb<0.7upper bound — unblocks installs against chromadb 1.x palaces (fix: remove chromadb <0.7 upper bound — blocks 1.x installs #690)Security
Notable Bug Fixes
messageskey (fix: parse Claude.ai privacy export with messages key and sender field (#677) #685, conversations.json from Claude.ai data export not parsed — mined as single drawer #677)Features
mempalace migrate— cross-ChromaDB-version palace recovery (feat: mempalace migrate — recover palaces from different ChromaDB versions #502)This PR's single commit
PR #761 bumped
pyproject.tomlto 3.2.0 but missed three other version strings, causingtest_version_consistencyto fail on develop CI. This PR adds one commit fixing that:mempalace/version.py: 3.1.0 → 3.2.0 (unblockstest_version_consistency)README.md: version badge shield → 3.2.0integrations/openclaw/SKILL.md: 3.2.0CHANGELOG.md: finalize[Unreleased]→[3.2.0] — 2026-04-13, add entries for fix: parse Claude.ai privacy export with messages key and sender field (#677) #685, fix: remove chromadb <0.7 upper bound — blocks 1.x installs #690, fix: remove 10k drawer cap from status display #707, fix: hash full content in tool_add_drawer drawer ID #716, fix #733: diagram misaligned #734, fix: correct typo in entity_detector interactive classification prompt #755, fix: detect mtime changes in _get_client to prevent stale HNSW index #757, bump version v3.2.0 #761Verification
ruff checkcleanPost-merge steps
v3.2.0onmainpython -m build && twine upload dist/*(Milla's PyPI token)v4branch offdevelopHEAD for v4 work[3.2.0]CHANGELOG section