mindforge-memory 6.4.2__tar.gz → 6.4.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {mindforge_memory-6.4.2/mindforge_memory.egg-info → mindforge_memory-6.4.3}/PKG-INFO +7 -6
- {mindforge_memory-6.4.2 → mindforge_memory-6.4.3}/README.md +6 -5
- {mindforge_memory-6.4.2 → mindforge_memory-6.4.3}/mindforge/core/storage.py +9 -0
- {mindforge_memory-6.4.2 → mindforge_memory-6.4.3}/mindforge/core/version.py +2 -2
- {mindforge_memory-6.4.2 → mindforge_memory-6.4.3/mindforge_memory.egg-info}/PKG-INFO +7 -6
- {mindforge_memory-6.4.2 → mindforge_memory-6.4.3}/mindforge_memory.egg-info/SOURCES.txt +1 -0
- {mindforge_memory-6.4.2 → mindforge_memory-6.4.3}/tests/test_v568_fixes.py +1 -1
- {mindforge_memory-6.4.2 → mindforge_memory-6.4.3}/tests/test_v575_release_consistency.py +2 -2
- {mindforge_memory-6.4.2 → mindforge_memory-6.4.3}/tests/test_v612_fixes.py +2 -2
- mindforge_memory-6.4.3/tests/test_v643_fixes.py +99 -0
- {mindforge_memory-6.4.2 → mindforge_memory-6.4.3}/LICENSE +0 -0
- {mindforge_memory-6.4.2 → mindforge_memory-6.4.3}/MindForge.py +0 -0
- {mindforge_memory-6.4.2 → mindforge_memory-6.4.3}/mindforge/__init__.py +0 -0
- {mindforge_memory-6.4.2 → mindforge_memory-6.4.3}/mindforge/adapters/__init__.py +0 -0
- {mindforge_memory-6.4.2 → mindforge_memory-6.4.3}/mindforge/adapters/claude_adapter.py +0 -0
- {mindforge_memory-6.4.2 → mindforge_memory-6.4.3}/mindforge/adapters/crewai_memory.py +0 -0
- {mindforge_memory-6.4.2 → mindforge_memory-6.4.3}/mindforge/adapters/generic_api.py +0 -0
- {mindforge_memory-6.4.2 → mindforge_memory-6.4.3}/mindforge/adapters/langgraph_store.py +0 -0
- {mindforge_memory-6.4.2 → mindforge_memory-6.4.3}/mindforge/adapters/openclaw_adapter.py +0 -0
- {mindforge_memory-6.4.2 → mindforge_memory-6.4.3}/mindforge/api/__init__.py +0 -0
- {mindforge_memory-6.4.2 → mindforge_memory-6.4.3}/mindforge/api/server.py +0 -0
- {mindforge_memory-6.4.2 → mindforge_memory-6.4.3}/mindforge/cli/__init__.py +0 -0
- {mindforge_memory-6.4.2 → mindforge_memory-6.4.3}/mindforge/cli/main.py +0 -0
- {mindforge_memory-6.4.2 → mindforge_memory-6.4.3}/mindforge/core/__init__.py +0 -0
- {mindforge_memory-6.4.2 → mindforge_memory-6.4.3}/mindforge/core/embedding.py +0 -0
- {mindforge_memory-6.4.2 → mindforge_memory-6.4.3}/mindforge/core/encryption.py +0 -0
- {mindforge_memory-6.4.2 → mindforge_memory-6.4.3}/mindforge/core/indexer.py +0 -0
- {mindforge_memory-6.4.2 → mindforge_memory-6.4.3}/mindforge/core/mindforge.py +0 -0
- {mindforge_memory-6.4.2 → mindforge_memory-6.4.3}/mindforge/core/paths.py +0 -0
- {mindforge_memory-6.4.2 → mindforge_memory-6.4.3}/mindforge/core/query.py +0 -0
- {mindforge_memory-6.4.2 → mindforge_memory-6.4.3}/mindforge/core/types.py +0 -0
- {mindforge_memory-6.4.2 → mindforge_memory-6.4.3}/mindforge/daemon/__init__.py +0 -0
- {mindforge_memory-6.4.2 → mindforge_memory-6.4.3}/mindforge/daemon/server.py +0 -0
- {mindforge_memory-6.4.2 → mindforge_memory-6.4.3}/mindforge/mcp/__init__.py +0 -0
- {mindforge_memory-6.4.2 → mindforge_memory-6.4.3}/mindforge/mcp/server.py +0 -0
- {mindforge_memory-6.4.2 → mindforge_memory-6.4.3}/mindforge/modules/__init__.py +0 -0
- {mindforge_memory-6.4.2 → mindforge_memory-6.4.3}/mindforge/modules/categorizer.py +0 -0
- {mindforge_memory-6.4.2 → mindforge_memory-6.4.3}/mindforge/modules/conflict_detector.py +0 -0
- {mindforge_memory-6.4.2 → mindforge_memory-6.4.3}/mindforge/modules/connectors.py +0 -0
- {mindforge_memory-6.4.2 → mindforge_memory-6.4.3}/mindforge/modules/context_window.py +0 -0
- {mindforge_memory-6.4.2 → mindforge_memory-6.4.3}/mindforge/modules/event_bus.py +0 -0
- {mindforge_memory-6.4.2 → mindforge_memory-6.4.3}/mindforge/modules/evolution.py +0 -0
- {mindforge_memory-6.4.2 → mindforge_memory-6.4.3}/mindforge/modules/experience.py +0 -0
- {mindforge_memory-6.4.2 → mindforge_memory-6.4.3}/mindforge/modules/federated.py +0 -0
- {mindforge_memory-6.4.2 → mindforge_memory-6.4.3}/mindforge/modules/federated_acl.py +0 -0
- {mindforge_memory-6.4.2 → mindforge_memory-6.4.3}/mindforge/modules/follow_up.py +0 -0
- {mindforge_memory-6.4.2 → mindforge_memory-6.4.3}/mindforge/modules/hybrid_search.py +0 -0
- {mindforge_memory-6.4.2 → mindforge_memory-6.4.3}/mindforge/modules/importer.py +0 -0
- {mindforge_memory-6.4.2 → mindforge_memory-6.4.3}/mindforge/modules/integrator.py +0 -0
- {mindforge_memory-6.4.2 → mindforge_memory-6.4.3}/mindforge/modules/intent_router.py +0 -0
- {mindforge_memory-6.4.2 → mindforge_memory-6.4.3}/mindforge/modules/knowledge_graph.py +0 -0
- {mindforge_memory-6.4.2 → mindforge_memory-6.4.3}/mindforge/modules/memory_compressor.py +0 -0
- {mindforge_memory-6.4.2 → mindforge_memory-6.4.3}/mindforge/modules/memory_decay.py +0 -0
- {mindforge_memory-6.4.2 → mindforge_memory-6.4.3}/mindforge/modules/memory_passport.py +0 -0
- {mindforge_memory-6.4.2 → mindforge_memory-6.4.3}/mindforge/modules/multi_agent.py +0 -0
- {mindforge_memory-6.4.2 → mindforge_memory-6.4.3}/mindforge/modules/multimodal.py +0 -0
- {mindforge_memory-6.4.2 → mindforge_memory-6.4.3}/mindforge/modules/outcome_learning.py +0 -0
- {mindforge_memory-6.4.2 → mindforge_memory-6.4.3}/mindforge/modules/personality.py +0 -0
- {mindforge_memory-6.4.2 → mindforge_memory-6.4.3}/mindforge/modules/principled_forgetting.py +0 -0
- {mindforge_memory-6.4.2 → mindforge_memory-6.4.3}/mindforge/modules/privacy.py +0 -0
- {mindforge_memory-6.4.2 → mindforge_memory-6.4.3}/mindforge/modules/recall.py +0 -0
- {mindforge_memory-6.4.2 → mindforge_memory-6.4.3}/mindforge/modules/session_focus.py +0 -0
- {mindforge_memory-6.4.2 → mindforge_memory-6.4.3}/mindforge/modules/share_conflict.py +0 -0
- {mindforge_memory-6.4.2 → mindforge_memory-6.4.3}/mindforge/modules/skill_extractor.py +0 -0
- {mindforge_memory-6.4.2 → mindforge_memory-6.4.3}/mindforge/modules/skill_pack.py +0 -0
- {mindforge_memory-6.4.2 → mindforge_memory-6.4.3}/mindforge/modules/timeline_viz.py +0 -0
- {mindforge_memory-6.4.2 → mindforge_memory-6.4.3}/mindforge/modules/v6.py +0 -0
- {mindforge_memory-6.4.2 → mindforge_memory-6.4.3}/mindforge_memory.egg-info/dependency_links.txt +0 -0
- {mindforge_memory-6.4.2 → mindforge_memory-6.4.3}/mindforge_memory.egg-info/entry_points.txt +0 -0
- {mindforge_memory-6.4.2 → mindforge_memory-6.4.3}/mindforge_memory.egg-info/requires.txt +0 -0
- {mindforge_memory-6.4.2 → mindforge_memory-6.4.3}/mindforge_memory.egg-info/top_level.txt +0 -0
- {mindforge_memory-6.4.2 → mindforge_memory-6.4.3}/pyproject.toml +0 -0
- {mindforge_memory-6.4.2 → mindforge_memory-6.4.3}/setup.cfg +0 -0
- {mindforge_memory-6.4.2 → mindforge_memory-6.4.3}/setup.py +0 -0
- {mindforge_memory-6.4.2 → mindforge_memory-6.4.3}/tests/test_core.py +0 -0
- {mindforge_memory-6.4.2 → mindforge_memory-6.4.3}/tests/test_security_hardening.py +0 -0
- {mindforge_memory-6.4.2 → mindforge_memory-6.4.3}/tests/test_v546_features.py +0 -0
- {mindforge_memory-6.4.2 → mindforge_memory-6.4.3}/tests/test_v547_fixes.py +0 -0
- {mindforge_memory-6.4.2 → mindforge_memory-6.4.3}/tests/test_v548_features.py +0 -0
- {mindforge_memory-6.4.2 → mindforge_memory-6.4.3}/tests/test_v550_features.py +0 -0
- {mindforge_memory-6.4.2 → mindforge_memory-6.4.3}/tests/test_v552_features.py +0 -0
- {mindforge_memory-6.4.2 → mindforge_memory-6.4.3}/tests/test_v554_features.py +0 -0
- {mindforge_memory-6.4.2 → mindforge_memory-6.4.3}/tests/test_v556_features.py +0 -0
- {mindforge_memory-6.4.2 → mindforge_memory-6.4.3}/tests/test_v557_banner_insight_wal.py +0 -0
- {mindforge_memory-6.4.2 → mindforge_memory-6.4.3}/tests/test_v557_security_fixes.py +0 -0
- {mindforge_memory-6.4.2 → mindforge_memory-6.4.3}/tests/test_v557_webhook_sig_export.py +0 -0
- {mindforge_memory-6.4.2 → mindforge_memory-6.4.3}/tests/test_v558_audit_fixes.py +0 -0
- {mindforge_memory-6.4.2 → mindforge_memory-6.4.3}/tests/test_v558_features.py +0 -0
- {mindforge_memory-6.4.2 → mindforge_memory-6.4.3}/tests/test_v560_decay_gc.py +0 -0
- {mindforge_memory-6.4.2 → mindforge_memory-6.4.3}/tests/test_v560_kdf_versioning.py +0 -0
- {mindforge_memory-6.4.2 → mindforge_memory-6.4.3}/tests/test_v560_module_coverage.py +0 -0
- {mindforge_memory-6.4.2 → mindforge_memory-6.4.3}/tests/test_v561_security.py +0 -0
- {mindforge_memory-6.4.2 → mindforge_memory-6.4.3}/tests/test_v562_p2_fixes.py +0 -0
- {mindforge_memory-6.4.2 → mindforge_memory-6.4.3}/tests/test_v563_entry_coverage.py +0 -0
- {mindforge_memory-6.4.2 → mindforge_memory-6.4.3}/tests/test_v563_perf_fixes.py +0 -0
- {mindforge_memory-6.4.2 → mindforge_memory-6.4.3}/tests/test_v565_security.py +0 -0
- {mindforge_memory-6.4.2 → mindforge_memory-6.4.3}/tests/test_v566_encrypted_search.py +0 -0
- {mindforge_memory-6.4.2 → mindforge_memory-6.4.3}/tests/test_v567_security_fixes.py +0 -0
- {mindforge_memory-6.4.2 → mindforge_memory-6.4.3}/tests/test_v569_fts_sync.py +0 -0
- {mindforge_memory-6.4.2 → mindforge_memory-6.4.3}/tests/test_v570_coverage_expansion.py +0 -0
- {mindforge_memory-6.4.2 → mindforge_memory-6.4.3}/tests/test_v570_drama_and_utils.py +0 -0
- {mindforge_memory-6.4.2 → mindforge_memory-6.4.3}/tests/test_v570_encrypted_fallback.py +0 -0
- {mindforge_memory-6.4.2 → mindforge_memory-6.4.3}/tests/test_v571_fixes.py +0 -0
- {mindforge_memory-6.4.2 → mindforge_memory-6.4.3}/tests/test_v572_security.py +0 -0
- {mindforge_memory-6.4.2 → mindforge_memory-6.4.3}/tests/test_v573_encrypted_export.py +0 -0
- {mindforge_memory-6.4.2 → mindforge_memory-6.4.3}/tests/test_v573_security.py +0 -0
- {mindforge_memory-6.4.2 → mindforge_memory-6.4.3}/tests/test_v576_bitemporal.py +0 -0
- {mindforge_memory-6.4.2 → mindforge_memory-6.4.3}/tests/test_v576_gdpr.py +0 -0
- {mindforge_memory-6.4.2 → mindforge_memory-6.4.3}/tests/test_v577_agent_governance.py +0 -0
- {mindforge_memory-6.4.2 → mindforge_memory-6.4.3}/tests/test_v578_embedding.py +0 -0
- {mindforge_memory-6.4.2 → mindforge_memory-6.4.3}/tests/test_v579_graph.py +0 -0
- {mindforge_memory-6.4.2 → mindforge_memory-6.4.3}/tests/test_v580_experience.py +0 -0
- {mindforge_memory-6.4.2 → mindforge_memory-6.4.3}/tests/test_v5812_bugfixes.py +0 -0
- {mindforge_memory-6.4.2 → mindforge_memory-6.4.3}/tests/test_v583_security.py +0 -0
- {mindforge_memory-6.4.2 → mindforge_memory-6.4.3}/tests/test_v584_adapters.py +0 -0
- {mindforge_memory-6.4.2 → mindforge_memory-6.4.3}/tests/test_v585_benchmark.py +0 -0
- {mindforge_memory-6.4.2 → mindforge_memory-6.4.3}/tests/test_v586_federated_mcp.py +0 -0
- {mindforge_memory-6.4.2 → mindforge_memory-6.4.3}/tests/test_v590_features.py +0 -0
- {mindforge_memory-6.4.2 → mindforge_memory-6.4.3}/tests/test_v592_features.py +0 -0
- {mindforge_memory-6.4.2 → mindforge_memory-6.4.3}/tests/test_v593_features.py +0 -0
- {mindforge_memory-6.4.2 → mindforge_memory-6.4.3}/tests/test_v595_features.py +0 -0
- {mindforge_memory-6.4.2 → mindforge_memory-6.4.3}/tests/test_v600_forgetting_followup.py +0 -0
- {mindforge_memory-6.4.2 → mindforge_memory-6.4.3}/tests/test_v600_import_timeline.py +0 -0
- {mindforge_memory-6.4.2 → mindforge_memory-6.4.3}/tests/test_v600_multi_agent_formal.py +0 -0
- {mindforge_memory-6.4.2 → mindforge_memory-6.4.3}/tests/test_v600_outcome_passport.py +0 -0
- {mindforge_memory-6.4.2 → mindforge_memory-6.4.3}/tests/test_v600_skillpack_evolution.py +0 -0
- {mindforge_memory-6.4.2 → mindforge_memory-6.4.3}/tests/test_v611_fixes.py +0 -0
- {mindforge_memory-6.4.2 → mindforge_memory-6.4.3}/tests/test_v612_cascade_fixes.py +0 -0
- {mindforge_memory-6.4.2 → mindforge_memory-6.4.3}/tests/test_v613_integrity.py +0 -0
- {mindforge_memory-6.4.2 → mindforge_memory-6.4.3}/tests/test_v620_growth_governance.py +0 -0
- {mindforge_memory-6.4.2 → mindforge_memory-6.4.3}/tests/test_v630_search_explain.py +0 -0
- {mindforge_memory-6.4.2 → mindforge_memory-6.4.3}/tests/test_v631_scrypt.py +0 -0
- {mindforge_memory-6.4.2 → mindforge_memory-6.4.3}/tests/test_v632_explain_interfaces.py +0 -0
- {mindforge_memory-6.4.2 → mindforge_memory-6.4.3}/tests/test_v635_features.py +0 -0
- {mindforge_memory-6.4.2 → mindforge_memory-6.4.3}/tests/test_v635_integration.py +0 -0
- {mindforge_memory-6.4.2 → mindforge_memory-6.4.3}/tests/test_v636_fixes.py +0 -0
- {mindforge_memory-6.4.2 → mindforge_memory-6.4.3}/tests/test_v6_ports.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mindforge-memory
|
|
3
|
-
Version: 6.4.
|
|
3
|
+
Version: 6.4.3
|
|
4
4
|
Summary: AI Agent 终身记忆系统 - 四层记忆架构 · 知识图谱 · 多模态支持
|
|
5
5
|
License: MIT
|
|
6
6
|
Project-URL: Homepage, https://github.com/opok-ops/MindForge
|
|
@@ -36,11 +36,11 @@ Dynamic: license-file
|
|
|
36
36
|
|
|
37
37
|
MindForge gives AI agents a persistent, structured long-term memory: a four-tier lifecycle model, knowledge graphs, hybrid retrieval, end-to-end encryption, and MCP / CLI / REST interfaces. All data stays on your machine.
|
|
38
38
|
|
|
39
|
-
> **The story** — one developer, seven months, from v1.0 (March 2026) to v6.4.
|
|
39
|
+
> **The story** — one developer, seven months, from v1.0 (March 2026) to v6.4.3: no team, no funding, 1,227 passing tests. [Read the journey →](https://opok-ops.github.io/MindForge/#developer)
|
|
40
40
|
|
|
41
41
|
[](https://www.python.org/downloads/)
|
|
42
42
|
[](LICENSE)
|
|
43
|
-
[](https://github.com/opok-ops/MindForge/releases)
|
|
44
44
|
[](https://github.com/opok-ops/MindForge/stargazers)
|
|
45
45
|
[](https://github.com/opok-ops/MindForge/forks)
|
|
46
46
|
[](https://github.com/opok-ops/MindForge/issues)
|
|
@@ -96,9 +96,10 @@ MindForge stats
|
|
|
96
96
|
|
|
97
97
|
## What's New
|
|
98
98
|
|
|
99
|
-
v6.1.3 – v6.4.
|
|
99
|
+
v6.1.3 – v6.4.3 shipped in ten days (2026-11-01 → 11-10), guarded by **1,228 tests** (1,227 passing, 1 environment-skipped).
|
|
100
100
|
|
|
101
|
-
- **v6.4.
|
|
101
|
+
- **v6.4.3 — documentation-code consistency fix** (2026-11-10). Pre-release audit found that v6.4.2's claimed first cut — WAL + `synchronous=NORMAL` — **never landed in code**: `_get_conn()` set only `journal_mode=WAL`, leaving `synchronous` at SQLite's default FULL. The 2.1× search gain was real, but carried entirely by the other four cuts — real numbers, misattributed cause. This release adds `PRAGMA synchronous=NORMAL` on the WAL branch (per-connection, cross-thread; network-FS DELETE fallback stays FULL by design). Same-machine A/B on SSD shows no statistically significant latency delta (search p50 1.8 → 1.8ms — fsync is ~0.1ms-scale here); the benefit targets slow disks and IO-saturated containers, and more importantly makes the documented power-loss semantics (un-checkpointed access counts / search audits may be lost; compliance audits still commit immediately) actually hold. 4 new guard tests in `tests/test_v643_fixes.py`.
|
|
102
|
+
- **v6.4.2 — tens-of-thousands-chars latency: another 2×** (2026-11-09). Same-machine A/B benchmark (400 memories / ~47k chars, 3 runs each): **search p50 6.3→3.0ms (2.1×), p95 11.7→4.2ms (2.8×)**; 24k-char library p50 2.5ms; fast machine state 1.8ms. Five surgical cuts: WAL + `synchronous=NORMAL` (commits skip per-write fsync), enum-downgrade memo, `row.keys()` set reuse, first-visit access-count batching merged into one `flush_access()` transaction per search, and search-audit INSERT folded into the same commit. The v5.6.3 throttle contract and v5.5.4 `recently_accessed` semantics are pinned by 23 contract tests — all green. Compliance-grade audits still commit immediately. *(Note: the `synchronous=NORMAL` cut landed in v6.4.3 — see above.)*
|
|
102
103
|
- **v6.4.1 — million-character millisecond search** (2026-11-09). Scale benchmark (8,000 memories / ~990k chars) exposed a fuzzy-fallback long tail: search p95 881ms. Root causes: SequenceMatcher wasted on mathematically-impossible matches (ratio = 2M/(m+n), M ≤ m, so n ≥ 4m can never pass 0.4) + a ~50μs/entry Python full-scan tax. Fixed with a math-capped prune + SQLite-level candidate pre-filtering: **search p95 881→30.7ms (28×)**, add p50 2.6ms flat at 10k-scale. New `scripts/bench_scale.py` + CI scale guard (3000 memories, millisecond contract) + 4 behavior-level regression tests pin the semantics.
|
|
103
104
|
|
|
104
105
|
- **v6.4.0 — resident daemon & performance guards** (2026-11-08). New `mindforge daemon` subcommand: line-JSON over stdio, engine resident in a long-lived process — per-request p50 2.5ms vs 400–700ms for CLI subprocess spawning (two orders of magnitude; 2026-11-09 quiet-machine re-bench: 2.5ms vs 401ms p50, ~160×). CI now guards latency (engine in-process thresholds) and daemon 13-step E2E alongside the 16-step CLI E2E guard.
|
|
@@ -166,7 +167,7 @@ v6.1.3 – v6.4.2 shipped in nine days (2026-11-01 → 11-09), guarded by **1,22
|
|
|
166
167
|
|
|
167
168
|
```
|
|
168
169
|
┌──────────────────────────────────────────────────────┐
|
|
169
|
-
│ MindForge v6.4.
|
|
170
|
+
│ MindForge v6.4.3 │
|
|
170
171
|
│ Cognitive Layer Personality · KnowledgeGraph │
|
|
171
172
|
│ MemoryEvolution · FederatedMemory │
|
|
172
173
|
├──────────────────────────────────────────────────────┤
|
|
@@ -6,11 +6,11 @@
|
|
|
6
6
|
|
|
7
7
|
MindForge gives AI agents a persistent, structured long-term memory: a four-tier lifecycle model, knowledge graphs, hybrid retrieval, end-to-end encryption, and MCP / CLI / REST interfaces. All data stays on your machine.
|
|
8
8
|
|
|
9
|
-
> **The story** — one developer, seven months, from v1.0 (March 2026) to v6.4.
|
|
9
|
+
> **The story** — one developer, seven months, from v1.0 (March 2026) to v6.4.3: no team, no funding, 1,227 passing tests. [Read the journey →](https://opok-ops.github.io/MindForge/#developer)
|
|
10
10
|
|
|
11
11
|
[](https://www.python.org/downloads/)
|
|
12
12
|
[](LICENSE)
|
|
13
|
-
[](https://github.com/opok-ops/MindForge/releases)
|
|
14
14
|
[](https://github.com/opok-ops/MindForge/stargazers)
|
|
15
15
|
[](https://github.com/opok-ops/MindForge/forks)
|
|
16
16
|
[](https://github.com/opok-ops/MindForge/issues)
|
|
@@ -66,9 +66,10 @@ MindForge stats
|
|
|
66
66
|
|
|
67
67
|
## What's New
|
|
68
68
|
|
|
69
|
-
v6.1.3 – v6.4.
|
|
69
|
+
v6.1.3 – v6.4.3 shipped in ten days (2026-11-01 → 11-10), guarded by **1,228 tests** (1,227 passing, 1 environment-skipped).
|
|
70
70
|
|
|
71
|
-
- **v6.4.
|
|
71
|
+
- **v6.4.3 — documentation-code consistency fix** (2026-11-10). Pre-release audit found that v6.4.2's claimed first cut — WAL + `synchronous=NORMAL` — **never landed in code**: `_get_conn()` set only `journal_mode=WAL`, leaving `synchronous` at SQLite's default FULL. The 2.1× search gain was real, but carried entirely by the other four cuts — real numbers, misattributed cause. This release adds `PRAGMA synchronous=NORMAL` on the WAL branch (per-connection, cross-thread; network-FS DELETE fallback stays FULL by design). Same-machine A/B on SSD shows no statistically significant latency delta (search p50 1.8 → 1.8ms — fsync is ~0.1ms-scale here); the benefit targets slow disks and IO-saturated containers, and more importantly makes the documented power-loss semantics (un-checkpointed access counts / search audits may be lost; compliance audits still commit immediately) actually hold. 4 new guard tests in `tests/test_v643_fixes.py`.
|
|
72
|
+
- **v6.4.2 — tens-of-thousands-chars latency: another 2×** (2026-11-09). Same-machine A/B benchmark (400 memories / ~47k chars, 3 runs each): **search p50 6.3→3.0ms (2.1×), p95 11.7→4.2ms (2.8×)**; 24k-char library p50 2.5ms; fast machine state 1.8ms. Five surgical cuts: WAL + `synchronous=NORMAL` (commits skip per-write fsync), enum-downgrade memo, `row.keys()` set reuse, first-visit access-count batching merged into one `flush_access()` transaction per search, and search-audit INSERT folded into the same commit. The v5.6.3 throttle contract and v5.5.4 `recently_accessed` semantics are pinned by 23 contract tests — all green. Compliance-grade audits still commit immediately. *(Note: the `synchronous=NORMAL` cut landed in v6.4.3 — see above.)*
|
|
72
73
|
- **v6.4.1 — million-character millisecond search** (2026-11-09). Scale benchmark (8,000 memories / ~990k chars) exposed a fuzzy-fallback long tail: search p95 881ms. Root causes: SequenceMatcher wasted on mathematically-impossible matches (ratio = 2M/(m+n), M ≤ m, so n ≥ 4m can never pass 0.4) + a ~50μs/entry Python full-scan tax. Fixed with a math-capped prune + SQLite-level candidate pre-filtering: **search p95 881→30.7ms (28×)**, add p50 2.6ms flat at 10k-scale. New `scripts/bench_scale.py` + CI scale guard (3000 memories, millisecond contract) + 4 behavior-level regression tests pin the semantics.
|
|
73
74
|
|
|
74
75
|
- **v6.4.0 — resident daemon & performance guards** (2026-11-08). New `mindforge daemon` subcommand: line-JSON over stdio, engine resident in a long-lived process — per-request p50 2.5ms vs 400–700ms for CLI subprocess spawning (two orders of magnitude; 2026-11-09 quiet-machine re-bench: 2.5ms vs 401ms p50, ~160×). CI now guards latency (engine in-process thresholds) and daemon 13-step E2E alongside the 16-step CLI E2E guard.
|
|
@@ -136,7 +137,7 @@ v6.1.3 – v6.4.2 shipped in nine days (2026-11-01 → 11-09), guarded by **1,22
|
|
|
136
137
|
|
|
137
138
|
```
|
|
138
139
|
┌──────────────────────────────────────────────────────┐
|
|
139
|
-
│ MindForge v6.4.
|
|
140
|
+
│ MindForge v6.4.3 │
|
|
140
141
|
│ Cognitive Layer Personality · KnowledgeGraph │
|
|
141
142
|
│ MemoryEvolution · FederatedMemory │
|
|
142
143
|
├──────────────────────────────────────────────────────┤
|
|
@@ -757,6 +757,15 @@ class StorageEngine:
|
|
|
757
757
|
conn.execute("PRAGMA journal_mode=DELETE")
|
|
758
758
|
else:
|
|
759
759
|
conn.execute("PRAGMA journal_mode=WAL")
|
|
760
|
+
# v6.4.3 fix: WAL 配套 synchronous=NORMAL(SQLite 官方推荐
|
|
761
|
+
# 组合)。v6.4.2 文档宣称了该优化但代码从未落地——本补丁
|
|
762
|
+
# 将其补齐:WAL+NORMAL 下 commit 免逐次 fsync,挂账批量
|
|
763
|
+
# 落库(search 尾事务)不再付 fsync 代价。
|
|
764
|
+
# 注意:synchronous 是 per-connection 参数,必须在每个
|
|
765
|
+
# 新连接创建时设置,仅在 WAL 分支生效;网络 FS 降级
|
|
766
|
+
# DELETE journal 的分支维持默认 FULL(远程盘耐久性语义
|
|
767
|
+
# 从严,不做削弱)。
|
|
768
|
+
conn.execute("PRAGMA synchronous=NORMAL")
|
|
760
769
|
conn.execute("PRAGMA foreign_keys=ON")
|
|
761
770
|
conn.execute("PRAGMA busy_timeout=5000")
|
|
762
771
|
self._conn_local.conn = conn
|
|
@@ -20,10 +20,10 @@ core/mindforge.py、cli/main.py、mcp/server.py),每处都带一份硬编码
|
|
|
20
20
|
from typing import Tuple
|
|
21
21
|
|
|
22
22
|
#: 当前发布版本(唯一真值)
|
|
23
|
-
__version__: str = "6.4.
|
|
23
|
+
__version__: str = "6.4.3"
|
|
24
24
|
|
|
25
25
|
#: 结构化版本元组,便于比较(如 `VERSION_INFO >= (6, 1, 0)`)
|
|
26
|
-
VERSION_INFO: Tuple[int, int, int] = (6, 4,
|
|
26
|
+
VERSION_INFO: Tuple[int, int, int] = (6, 4, 3)
|
|
27
27
|
|
|
28
28
|
#: 下一个大版本的目标版本号(供 v6 端口预留引用)
|
|
29
29
|
NEXT_MAJOR_TARGET: str = "7.0.0"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mindforge-memory
|
|
3
|
-
Version: 6.4.
|
|
3
|
+
Version: 6.4.3
|
|
4
4
|
Summary: AI Agent 终身记忆系统 - 四层记忆架构 · 知识图谱 · 多模态支持
|
|
5
5
|
License: MIT
|
|
6
6
|
Project-URL: Homepage, https://github.com/opok-ops/MindForge
|
|
@@ -36,11 +36,11 @@ Dynamic: license-file
|
|
|
36
36
|
|
|
37
37
|
MindForge gives AI agents a persistent, structured long-term memory: a four-tier lifecycle model, knowledge graphs, hybrid retrieval, end-to-end encryption, and MCP / CLI / REST interfaces. All data stays on your machine.
|
|
38
38
|
|
|
39
|
-
> **The story** — one developer, seven months, from v1.0 (March 2026) to v6.4.
|
|
39
|
+
> **The story** — one developer, seven months, from v1.0 (March 2026) to v6.4.3: no team, no funding, 1,227 passing tests. [Read the journey →](https://opok-ops.github.io/MindForge/#developer)
|
|
40
40
|
|
|
41
41
|
[](https://www.python.org/downloads/)
|
|
42
42
|
[](LICENSE)
|
|
43
|
-
[](https://github.com/opok-ops/MindForge/releases)
|
|
44
44
|
[](https://github.com/opok-ops/MindForge/stargazers)
|
|
45
45
|
[](https://github.com/opok-ops/MindForge/forks)
|
|
46
46
|
[](https://github.com/opok-ops/MindForge/issues)
|
|
@@ -96,9 +96,10 @@ MindForge stats
|
|
|
96
96
|
|
|
97
97
|
## What's New
|
|
98
98
|
|
|
99
|
-
v6.1.3 – v6.4.
|
|
99
|
+
v6.1.3 – v6.4.3 shipped in ten days (2026-11-01 → 11-10), guarded by **1,228 tests** (1,227 passing, 1 environment-skipped).
|
|
100
100
|
|
|
101
|
-
- **v6.4.
|
|
101
|
+
- **v6.4.3 — documentation-code consistency fix** (2026-11-10). Pre-release audit found that v6.4.2's claimed first cut — WAL + `synchronous=NORMAL` — **never landed in code**: `_get_conn()` set only `journal_mode=WAL`, leaving `synchronous` at SQLite's default FULL. The 2.1× search gain was real, but carried entirely by the other four cuts — real numbers, misattributed cause. This release adds `PRAGMA synchronous=NORMAL` on the WAL branch (per-connection, cross-thread; network-FS DELETE fallback stays FULL by design). Same-machine A/B on SSD shows no statistically significant latency delta (search p50 1.8 → 1.8ms — fsync is ~0.1ms-scale here); the benefit targets slow disks and IO-saturated containers, and more importantly makes the documented power-loss semantics (un-checkpointed access counts / search audits may be lost; compliance audits still commit immediately) actually hold. 4 new guard tests in `tests/test_v643_fixes.py`.
|
|
102
|
+
- **v6.4.2 — tens-of-thousands-chars latency: another 2×** (2026-11-09). Same-machine A/B benchmark (400 memories / ~47k chars, 3 runs each): **search p50 6.3→3.0ms (2.1×), p95 11.7→4.2ms (2.8×)**; 24k-char library p50 2.5ms; fast machine state 1.8ms. Five surgical cuts: WAL + `synchronous=NORMAL` (commits skip per-write fsync), enum-downgrade memo, `row.keys()` set reuse, first-visit access-count batching merged into one `flush_access()` transaction per search, and search-audit INSERT folded into the same commit. The v5.6.3 throttle contract and v5.5.4 `recently_accessed` semantics are pinned by 23 contract tests — all green. Compliance-grade audits still commit immediately. *(Note: the `synchronous=NORMAL` cut landed in v6.4.3 — see above.)*
|
|
102
103
|
- **v6.4.1 — million-character millisecond search** (2026-11-09). Scale benchmark (8,000 memories / ~990k chars) exposed a fuzzy-fallback long tail: search p95 881ms. Root causes: SequenceMatcher wasted on mathematically-impossible matches (ratio = 2M/(m+n), M ≤ m, so n ≥ 4m can never pass 0.4) + a ~50μs/entry Python full-scan tax. Fixed with a math-capped prune + SQLite-level candidate pre-filtering: **search p95 881→30.7ms (28×)**, add p50 2.6ms flat at 10k-scale. New `scripts/bench_scale.py` + CI scale guard (3000 memories, millisecond contract) + 4 behavior-level regression tests pin the semantics.
|
|
103
104
|
|
|
104
105
|
- **v6.4.0 — resident daemon & performance guards** (2026-11-08). New `mindforge daemon` subcommand: line-JSON over stdio, engine resident in a long-lived process — per-request p50 2.5ms vs 400–700ms for CLI subprocess spawning (two orders of magnitude; 2026-11-09 quiet-machine re-bench: 2.5ms vs 401ms p50, ~160×). CI now guards latency (engine in-process thresholds) and daemon 13-step E2E alongside the 16-step CLI E2E guard.
|
|
@@ -166,7 +167,7 @@ v6.1.3 – v6.4.2 shipped in nine days (2026-11-01 → 11-09), guarded by **1,22
|
|
|
166
167
|
|
|
167
168
|
```
|
|
168
169
|
┌──────────────────────────────────────────────────────┐
|
|
169
|
-
│ MindForge v6.4.
|
|
170
|
+
│ MindForge v6.4.3 │
|
|
170
171
|
│ Cognitive Layer Personality · KnowledgeGraph │
|
|
171
172
|
│ MemoryEvolution · FederatedMemory │
|
|
172
173
|
├──────────────────────────────────────────────────────┤
|
|
@@ -40,8 +40,8 @@ class TestVersionTruth(unittest.TestCase):
|
|
|
40
40
|
def test_core_version_matches_release(self):
|
|
41
41
|
# 版本推进时必须一并更新此处的钉值(与 test_v568_fixes 的
|
|
42
42
|
# _EXPECTED_VERSION 同一约定:让「忘记 bump 版本」在测试里显形)。
|
|
43
|
-
self.assertEqual(core_version.__version__, "6.4.
|
|
44
|
-
self.assertEqual(core_version.VERSION_INFO, (6, 4,
|
|
43
|
+
self.assertEqual(core_version.__version__, "6.4.3")
|
|
44
|
+
self.assertEqual(core_version.VERSION_INFO, (6, 4, 3))
|
|
45
45
|
|
|
46
46
|
def test_pyproject_matches_core_version(self):
|
|
47
47
|
# v5.8.13 起 pyproject 不再写死静态版本:
|
|
@@ -662,8 +662,8 @@ class TestVersionConsistency(unittest.TestCase):
|
|
|
662
662
|
def test_version_is_612(self):
|
|
663
663
|
from mindforge.core.version import VERSION_INFO, __version__
|
|
664
664
|
# v6.1.3:版本钉值随发版推进更新(历史方法名保留)
|
|
665
|
-
self.assertEqual(__version__, "6.4.
|
|
666
|
-
self.assertEqual(VERSION_INFO, (6, 4,
|
|
665
|
+
self.assertEqual(__version__, "6.4.3")
|
|
666
|
+
self.assertEqual(VERSION_INFO, (6, 4, 3))
|
|
667
667
|
|
|
668
668
|
def test_json_round_trip_safety_of_preview(self):
|
|
669
669
|
"""save_evolution 的预览序列化不得抛(不可 json 化的对象走 str 兜底)。"""
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
"""
|
|
2
|
+
MindForge v6.4.3 回归测试
|
|
3
|
+
|
|
4
|
+
覆盖测试过程中发现的 1 个文档-代码不一致缺陷(2026-11-10):
|
|
5
|
+
|
|
6
|
+
- Bug-A(P0): v6.4.2 宣称的「WAL + synchronous=NORMAL」五刀第一刀
|
|
7
|
+
从未落地——CHANGELOG/README/website 与 commit message 均宣称该
|
|
8
|
+
组合已上线,但 _get_conn() 仅设置了 journal_mode=WAL,
|
|
9
|
+
synchronous 维持 SQLite 默认 FULL(WAL 下逐 commit fsync)。
|
|
10
|
+
修复:WAL 分支补 PRAGMA synchronous=NORMAL(per-connection,
|
|
11
|
+
每连接创建时设置;网络 FS 降级 DELETE 分支维持 FULL 从严)。
|
|
12
|
+
|
|
13
|
+
守卫目标:让「PRAGMA 只在部分连接生效」「优化宣称了但没落地」
|
|
14
|
+
这两类回归在测试里显形。
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
import os
|
|
18
|
+
import sys
|
|
19
|
+
import tempfile
|
|
20
|
+
import threading
|
|
21
|
+
import unittest
|
|
22
|
+
from pathlib import Path
|
|
23
|
+
|
|
24
|
+
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
|
25
|
+
|
|
26
|
+
from mindforge.core import storage as core_storage
|
|
27
|
+
from mindforge.core.version import __version__
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class _WALCase(unittest.TestCase):
|
|
31
|
+
"""本地盘临时库(走 WAL 分支)公共脚手架。"""
|
|
32
|
+
|
|
33
|
+
def setUp(self):
|
|
34
|
+
self.tmpdir = tempfile.TemporaryDirectory(prefix="mf_v643_fix_")
|
|
35
|
+
self.db = os.path.join(self.tmpdir.name, "memory.db")
|
|
36
|
+
self.storage = core_storage.StorageEngine(
|
|
37
|
+
db_path=self.db, encrypted=False
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
def tearDown(self):
|
|
41
|
+
try:
|
|
42
|
+
self.storage.close()
|
|
43
|
+
finally:
|
|
44
|
+
self.tmpdir.cleanup()
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class TestSynchronousNormalGuard(_WALCase):
|
|
48
|
+
"""Bug-A 守卫:WAL + synchronous=NORMAL 必须真实生效。"""
|
|
49
|
+
|
|
50
|
+
def test_synchronous_normal_on_local_wal(self):
|
|
51
|
+
# SQLite PRAGMA synchronous 返回值:0=OFF, 1=NORMAL, 2=FULL。
|
|
52
|
+
# v6.4.2 缺陷形态即此值恒为 2(FULL)。
|
|
53
|
+
conn = self.storage._get_conn()
|
|
54
|
+
mode = conn.execute("PRAGMA synchronous").fetchone()[0]
|
|
55
|
+
self.assertEqual(
|
|
56
|
+
mode, 1,
|
|
57
|
+
f"synchronous 应为 NORMAL(1),实际 {mode}——"
|
|
58
|
+
"v6.4.3 的 WAL+synchronous=NORMAL 修复未生效或被回退",
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
def test_journal_mode_wal_on_local_disk(self):
|
|
62
|
+
conn = self.storage._get_conn()
|
|
63
|
+
journal = conn.execute("PRAGMA journal_mode").fetchone()[0]
|
|
64
|
+
self.assertEqual(
|
|
65
|
+
str(journal).lower(), "wal",
|
|
66
|
+
f"本地盘 journal_mode 应为 wal,实际 {journal}",
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
def test_synchronous_is_per_connection(self):
|
|
70
|
+
# synchronous 是 per-connection 参数:StorageEngine 用
|
|
71
|
+
# threading.local 维护连接,新线程的连接也必须带上 NORMAL,
|
|
72
|
+
# 否则只保护了首线程(REST/daemon 多线程场景将静默回退 FULL)。
|
|
73
|
+
results = {}
|
|
74
|
+
|
|
75
|
+
def probe():
|
|
76
|
+
try:
|
|
77
|
+
conn = self.storage._get_conn()
|
|
78
|
+
results["mode"] = conn.execute(
|
|
79
|
+
"PRAGMA synchronous"
|
|
80
|
+
).fetchone()[0]
|
|
81
|
+
except Exception as exc: # pragma: no cover - 显形优先
|
|
82
|
+
results["error"] = repr(exc)
|
|
83
|
+
|
|
84
|
+
t = threading.Thread(target=probe)
|
|
85
|
+
t.start()
|
|
86
|
+
t.join(timeout=10)
|
|
87
|
+
self.assertNotIn("error", results, results.get("error", ""))
|
|
88
|
+
self.assertEqual(results.get("mode"), 1)
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
class TestVersionBump(unittest.TestCase):
|
|
92
|
+
"""版本钉值:忘记 bump 时在此显形(与 v568/v575/v612 守卫同一约定)。"""
|
|
93
|
+
|
|
94
|
+
def test_version_is_643(self):
|
|
95
|
+
self.assertEqual(__version__, "6.4.3")
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
if __name__ == "__main__":
|
|
99
|
+
unittest.main()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mindforge_memory-6.4.2 → mindforge_memory-6.4.3}/mindforge/modules/principled_forgetting.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mindforge_memory-6.4.2 → mindforge_memory-6.4.3}/mindforge_memory.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
{mindforge_memory-6.4.2 → mindforge_memory-6.4.3}/mindforge_memory.egg-info/entry_points.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|