mindforge-memory 6.4.1__tar.gz → 6.4.2__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.1/mindforge_memory.egg-info → mindforge_memory-6.4.2}/PKG-INFO +6 -5
- {mindforge_memory-6.4.1 → mindforge_memory-6.4.2}/README.md +5 -4
- {mindforge_memory-6.4.1 → mindforge_memory-6.4.2}/mindforge/core/query.py +13 -1
- {mindforge_memory-6.4.1 → mindforge_memory-6.4.2}/mindforge/core/storage.py +62 -15
- {mindforge_memory-6.4.1 → mindforge_memory-6.4.2}/mindforge/core/version.py +2 -2
- {mindforge_memory-6.4.1 → mindforge_memory-6.4.2/mindforge_memory.egg-info}/PKG-INFO +6 -5
- {mindforge_memory-6.4.1 → mindforge_memory-6.4.2}/tests/test_v568_fixes.py +1 -1
- {mindforge_memory-6.4.1 → mindforge_memory-6.4.2}/tests/test_v575_release_consistency.py +2 -2
- {mindforge_memory-6.4.1 → mindforge_memory-6.4.2}/tests/test_v612_fixes.py +2 -2
- {mindforge_memory-6.4.1 → mindforge_memory-6.4.2}/LICENSE +0 -0
- {mindforge_memory-6.4.1 → mindforge_memory-6.4.2}/MindForge.py +0 -0
- {mindforge_memory-6.4.1 → mindforge_memory-6.4.2}/mindforge/__init__.py +0 -0
- {mindforge_memory-6.4.1 → mindforge_memory-6.4.2}/mindforge/adapters/__init__.py +0 -0
- {mindforge_memory-6.4.1 → mindforge_memory-6.4.2}/mindforge/adapters/claude_adapter.py +0 -0
- {mindforge_memory-6.4.1 → mindforge_memory-6.4.2}/mindforge/adapters/crewai_memory.py +0 -0
- {mindforge_memory-6.4.1 → mindforge_memory-6.4.2}/mindforge/adapters/generic_api.py +0 -0
- {mindforge_memory-6.4.1 → mindforge_memory-6.4.2}/mindforge/adapters/langgraph_store.py +0 -0
- {mindforge_memory-6.4.1 → mindforge_memory-6.4.2}/mindforge/adapters/openclaw_adapter.py +0 -0
- {mindforge_memory-6.4.1 → mindforge_memory-6.4.2}/mindforge/api/__init__.py +0 -0
- {mindforge_memory-6.4.1 → mindforge_memory-6.4.2}/mindforge/api/server.py +0 -0
- {mindforge_memory-6.4.1 → mindforge_memory-6.4.2}/mindforge/cli/__init__.py +0 -0
- {mindforge_memory-6.4.1 → mindforge_memory-6.4.2}/mindforge/cli/main.py +0 -0
- {mindforge_memory-6.4.1 → mindforge_memory-6.4.2}/mindforge/core/__init__.py +0 -0
- {mindforge_memory-6.4.1 → mindforge_memory-6.4.2}/mindforge/core/embedding.py +0 -0
- {mindforge_memory-6.4.1 → mindforge_memory-6.4.2}/mindforge/core/encryption.py +0 -0
- {mindforge_memory-6.4.1 → mindforge_memory-6.4.2}/mindforge/core/indexer.py +0 -0
- {mindforge_memory-6.4.1 → mindforge_memory-6.4.2}/mindforge/core/mindforge.py +0 -0
- {mindforge_memory-6.4.1 → mindforge_memory-6.4.2}/mindforge/core/paths.py +0 -0
- {mindforge_memory-6.4.1 → mindforge_memory-6.4.2}/mindforge/core/types.py +0 -0
- {mindforge_memory-6.4.1 → mindforge_memory-6.4.2}/mindforge/daemon/__init__.py +0 -0
- {mindforge_memory-6.4.1 → mindforge_memory-6.4.2}/mindforge/daemon/server.py +0 -0
- {mindforge_memory-6.4.1 → mindforge_memory-6.4.2}/mindforge/mcp/__init__.py +0 -0
- {mindforge_memory-6.4.1 → mindforge_memory-6.4.2}/mindforge/mcp/server.py +0 -0
- {mindforge_memory-6.4.1 → mindforge_memory-6.4.2}/mindforge/modules/__init__.py +0 -0
- {mindforge_memory-6.4.1 → mindforge_memory-6.4.2}/mindforge/modules/categorizer.py +0 -0
- {mindforge_memory-6.4.1 → mindforge_memory-6.4.2}/mindforge/modules/conflict_detector.py +0 -0
- {mindforge_memory-6.4.1 → mindforge_memory-6.4.2}/mindforge/modules/connectors.py +0 -0
- {mindforge_memory-6.4.1 → mindforge_memory-6.4.2}/mindforge/modules/context_window.py +0 -0
- {mindforge_memory-6.4.1 → mindforge_memory-6.4.2}/mindforge/modules/event_bus.py +0 -0
- {mindforge_memory-6.4.1 → mindforge_memory-6.4.2}/mindforge/modules/evolution.py +0 -0
- {mindforge_memory-6.4.1 → mindforge_memory-6.4.2}/mindforge/modules/experience.py +0 -0
- {mindforge_memory-6.4.1 → mindforge_memory-6.4.2}/mindforge/modules/federated.py +0 -0
- {mindforge_memory-6.4.1 → mindforge_memory-6.4.2}/mindforge/modules/federated_acl.py +0 -0
- {mindforge_memory-6.4.1 → mindforge_memory-6.4.2}/mindforge/modules/follow_up.py +0 -0
- {mindforge_memory-6.4.1 → mindforge_memory-6.4.2}/mindforge/modules/hybrid_search.py +0 -0
- {mindforge_memory-6.4.1 → mindforge_memory-6.4.2}/mindforge/modules/importer.py +0 -0
- {mindforge_memory-6.4.1 → mindforge_memory-6.4.2}/mindforge/modules/integrator.py +0 -0
- {mindforge_memory-6.4.1 → mindforge_memory-6.4.2}/mindforge/modules/intent_router.py +0 -0
- {mindforge_memory-6.4.1 → mindforge_memory-6.4.2}/mindforge/modules/knowledge_graph.py +0 -0
- {mindforge_memory-6.4.1 → mindforge_memory-6.4.2}/mindforge/modules/memory_compressor.py +0 -0
- {mindforge_memory-6.4.1 → mindforge_memory-6.4.2}/mindforge/modules/memory_decay.py +0 -0
- {mindforge_memory-6.4.1 → mindforge_memory-6.4.2}/mindforge/modules/memory_passport.py +0 -0
- {mindforge_memory-6.4.1 → mindforge_memory-6.4.2}/mindforge/modules/multi_agent.py +0 -0
- {mindforge_memory-6.4.1 → mindforge_memory-6.4.2}/mindforge/modules/multimodal.py +0 -0
- {mindforge_memory-6.4.1 → mindforge_memory-6.4.2}/mindforge/modules/outcome_learning.py +0 -0
- {mindforge_memory-6.4.1 → mindforge_memory-6.4.2}/mindforge/modules/personality.py +0 -0
- {mindforge_memory-6.4.1 → mindforge_memory-6.4.2}/mindforge/modules/principled_forgetting.py +0 -0
- {mindforge_memory-6.4.1 → mindforge_memory-6.4.2}/mindforge/modules/privacy.py +0 -0
- {mindforge_memory-6.4.1 → mindforge_memory-6.4.2}/mindforge/modules/recall.py +0 -0
- {mindforge_memory-6.4.1 → mindforge_memory-6.4.2}/mindforge/modules/session_focus.py +0 -0
- {mindforge_memory-6.4.1 → mindforge_memory-6.4.2}/mindforge/modules/share_conflict.py +0 -0
- {mindforge_memory-6.4.1 → mindforge_memory-6.4.2}/mindforge/modules/skill_extractor.py +0 -0
- {mindforge_memory-6.4.1 → mindforge_memory-6.4.2}/mindforge/modules/skill_pack.py +0 -0
- {mindforge_memory-6.4.1 → mindforge_memory-6.4.2}/mindforge/modules/timeline_viz.py +0 -0
- {mindforge_memory-6.4.1 → mindforge_memory-6.4.2}/mindforge/modules/v6.py +0 -0
- {mindforge_memory-6.4.1 → mindforge_memory-6.4.2}/mindforge_memory.egg-info/SOURCES.txt +0 -0
- {mindforge_memory-6.4.1 → mindforge_memory-6.4.2}/mindforge_memory.egg-info/dependency_links.txt +0 -0
- {mindforge_memory-6.4.1 → mindforge_memory-6.4.2}/mindforge_memory.egg-info/entry_points.txt +0 -0
- {mindforge_memory-6.4.1 → mindforge_memory-6.4.2}/mindforge_memory.egg-info/requires.txt +0 -0
- {mindforge_memory-6.4.1 → mindforge_memory-6.4.2}/mindforge_memory.egg-info/top_level.txt +0 -0
- {mindforge_memory-6.4.1 → mindforge_memory-6.4.2}/pyproject.toml +0 -0
- {mindforge_memory-6.4.1 → mindforge_memory-6.4.2}/setup.cfg +0 -0
- {mindforge_memory-6.4.1 → mindforge_memory-6.4.2}/setup.py +0 -0
- {mindforge_memory-6.4.1 → mindforge_memory-6.4.2}/tests/test_core.py +0 -0
- {mindforge_memory-6.4.1 → mindforge_memory-6.4.2}/tests/test_security_hardening.py +0 -0
- {mindforge_memory-6.4.1 → mindforge_memory-6.4.2}/tests/test_v546_features.py +0 -0
- {mindforge_memory-6.4.1 → mindforge_memory-6.4.2}/tests/test_v547_fixes.py +0 -0
- {mindforge_memory-6.4.1 → mindforge_memory-6.4.2}/tests/test_v548_features.py +0 -0
- {mindforge_memory-6.4.1 → mindforge_memory-6.4.2}/tests/test_v550_features.py +0 -0
- {mindforge_memory-6.4.1 → mindforge_memory-6.4.2}/tests/test_v552_features.py +0 -0
- {mindforge_memory-6.4.1 → mindforge_memory-6.4.2}/tests/test_v554_features.py +0 -0
- {mindforge_memory-6.4.1 → mindforge_memory-6.4.2}/tests/test_v556_features.py +0 -0
- {mindforge_memory-6.4.1 → mindforge_memory-6.4.2}/tests/test_v557_banner_insight_wal.py +0 -0
- {mindforge_memory-6.4.1 → mindforge_memory-6.4.2}/tests/test_v557_security_fixes.py +0 -0
- {mindforge_memory-6.4.1 → mindforge_memory-6.4.2}/tests/test_v557_webhook_sig_export.py +0 -0
- {mindforge_memory-6.4.1 → mindforge_memory-6.4.2}/tests/test_v558_audit_fixes.py +0 -0
- {mindforge_memory-6.4.1 → mindforge_memory-6.4.2}/tests/test_v558_features.py +0 -0
- {mindforge_memory-6.4.1 → mindforge_memory-6.4.2}/tests/test_v560_decay_gc.py +0 -0
- {mindforge_memory-6.4.1 → mindforge_memory-6.4.2}/tests/test_v560_kdf_versioning.py +0 -0
- {mindforge_memory-6.4.1 → mindforge_memory-6.4.2}/tests/test_v560_module_coverage.py +0 -0
- {mindforge_memory-6.4.1 → mindforge_memory-6.4.2}/tests/test_v561_security.py +0 -0
- {mindforge_memory-6.4.1 → mindforge_memory-6.4.2}/tests/test_v562_p2_fixes.py +0 -0
- {mindforge_memory-6.4.1 → mindforge_memory-6.4.2}/tests/test_v563_entry_coverage.py +0 -0
- {mindforge_memory-6.4.1 → mindforge_memory-6.4.2}/tests/test_v563_perf_fixes.py +0 -0
- {mindforge_memory-6.4.1 → mindforge_memory-6.4.2}/tests/test_v565_security.py +0 -0
- {mindforge_memory-6.4.1 → mindforge_memory-6.4.2}/tests/test_v566_encrypted_search.py +0 -0
- {mindforge_memory-6.4.1 → mindforge_memory-6.4.2}/tests/test_v567_security_fixes.py +0 -0
- {mindforge_memory-6.4.1 → mindforge_memory-6.4.2}/tests/test_v569_fts_sync.py +0 -0
- {mindforge_memory-6.4.1 → mindforge_memory-6.4.2}/tests/test_v570_coverage_expansion.py +0 -0
- {mindforge_memory-6.4.1 → mindforge_memory-6.4.2}/tests/test_v570_drama_and_utils.py +0 -0
- {mindforge_memory-6.4.1 → mindforge_memory-6.4.2}/tests/test_v570_encrypted_fallback.py +0 -0
- {mindforge_memory-6.4.1 → mindforge_memory-6.4.2}/tests/test_v571_fixes.py +0 -0
- {mindforge_memory-6.4.1 → mindforge_memory-6.4.2}/tests/test_v572_security.py +0 -0
- {mindforge_memory-6.4.1 → mindforge_memory-6.4.2}/tests/test_v573_encrypted_export.py +0 -0
- {mindforge_memory-6.4.1 → mindforge_memory-6.4.2}/tests/test_v573_security.py +0 -0
- {mindforge_memory-6.4.1 → mindforge_memory-6.4.2}/tests/test_v576_bitemporal.py +0 -0
- {mindforge_memory-6.4.1 → mindforge_memory-6.4.2}/tests/test_v576_gdpr.py +0 -0
- {mindforge_memory-6.4.1 → mindforge_memory-6.4.2}/tests/test_v577_agent_governance.py +0 -0
- {mindforge_memory-6.4.1 → mindforge_memory-6.4.2}/tests/test_v578_embedding.py +0 -0
- {mindforge_memory-6.4.1 → mindforge_memory-6.4.2}/tests/test_v579_graph.py +0 -0
- {mindforge_memory-6.4.1 → mindforge_memory-6.4.2}/tests/test_v580_experience.py +0 -0
- {mindforge_memory-6.4.1 → mindforge_memory-6.4.2}/tests/test_v5812_bugfixes.py +0 -0
- {mindforge_memory-6.4.1 → mindforge_memory-6.4.2}/tests/test_v583_security.py +0 -0
- {mindforge_memory-6.4.1 → mindforge_memory-6.4.2}/tests/test_v584_adapters.py +0 -0
- {mindforge_memory-6.4.1 → mindforge_memory-6.4.2}/tests/test_v585_benchmark.py +0 -0
- {mindforge_memory-6.4.1 → mindforge_memory-6.4.2}/tests/test_v586_federated_mcp.py +0 -0
- {mindforge_memory-6.4.1 → mindforge_memory-6.4.2}/tests/test_v590_features.py +0 -0
- {mindforge_memory-6.4.1 → mindforge_memory-6.4.2}/tests/test_v592_features.py +0 -0
- {mindforge_memory-6.4.1 → mindforge_memory-6.4.2}/tests/test_v593_features.py +0 -0
- {mindforge_memory-6.4.1 → mindforge_memory-6.4.2}/tests/test_v595_features.py +0 -0
- {mindforge_memory-6.4.1 → mindforge_memory-6.4.2}/tests/test_v600_forgetting_followup.py +0 -0
- {mindforge_memory-6.4.1 → mindforge_memory-6.4.2}/tests/test_v600_import_timeline.py +0 -0
- {mindforge_memory-6.4.1 → mindforge_memory-6.4.2}/tests/test_v600_multi_agent_formal.py +0 -0
- {mindforge_memory-6.4.1 → mindforge_memory-6.4.2}/tests/test_v600_outcome_passport.py +0 -0
- {mindforge_memory-6.4.1 → mindforge_memory-6.4.2}/tests/test_v600_skillpack_evolution.py +0 -0
- {mindforge_memory-6.4.1 → mindforge_memory-6.4.2}/tests/test_v611_fixes.py +0 -0
- {mindforge_memory-6.4.1 → mindforge_memory-6.4.2}/tests/test_v612_cascade_fixes.py +0 -0
- {mindforge_memory-6.4.1 → mindforge_memory-6.4.2}/tests/test_v613_integrity.py +0 -0
- {mindforge_memory-6.4.1 → mindforge_memory-6.4.2}/tests/test_v620_growth_governance.py +0 -0
- {mindforge_memory-6.4.1 → mindforge_memory-6.4.2}/tests/test_v630_search_explain.py +0 -0
- {mindforge_memory-6.4.1 → mindforge_memory-6.4.2}/tests/test_v631_scrypt.py +0 -0
- {mindforge_memory-6.4.1 → mindforge_memory-6.4.2}/tests/test_v632_explain_interfaces.py +0 -0
- {mindforge_memory-6.4.1 → mindforge_memory-6.4.2}/tests/test_v635_features.py +0 -0
- {mindforge_memory-6.4.1 → mindforge_memory-6.4.2}/tests/test_v635_integration.py +0 -0
- {mindforge_memory-6.4.1 → mindforge_memory-6.4.2}/tests/test_v636_fixes.py +0 -0
- {mindforge_memory-6.4.1 → mindforge_memory-6.4.2}/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.2
|
|
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.2: no team, no funding, 1,224 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,8 +96,9 @@ MindForge stats
|
|
|
96
96
|
|
|
97
97
|
## What's New
|
|
98
98
|
|
|
99
|
-
v6.1.3 – v6.4.
|
|
99
|
+
v6.1.3 – v6.4.2 shipped in nine days (2026-11-01 → 11-09), guarded by **1,224 tests** (all passing).
|
|
100
100
|
|
|
101
|
+
- **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.
|
|
101
102
|
- **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.
|
|
102
103
|
|
|
103
104
|
- **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.
|
|
@@ -165,7 +166,7 @@ v6.1.3 – v6.4.1 shipped in nine days (2026-11-01 → 11-09), guarded by **1,22
|
|
|
165
166
|
|
|
166
167
|
```
|
|
167
168
|
┌──────────────────────────────────────────────────────┐
|
|
168
|
-
│ MindForge v6.4.
|
|
169
|
+
│ MindForge v6.4.2 │
|
|
169
170
|
│ Cognitive Layer Personality · KnowledgeGraph │
|
|
170
171
|
│ MemoryEvolution · FederatedMemory │
|
|
171
172
|
├──────────────────────────────────────────────────────┤
|
|
@@ -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.2: no team, no funding, 1,224 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,8 +66,9 @@ MindForge stats
|
|
|
66
66
|
|
|
67
67
|
## What's New
|
|
68
68
|
|
|
69
|
-
v6.1.3 – v6.4.
|
|
69
|
+
v6.1.3 – v6.4.2 shipped in nine days (2026-11-01 → 11-09), guarded by **1,224 tests** (all passing).
|
|
70
70
|
|
|
71
|
+
- **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.
|
|
71
72
|
- **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.
|
|
72
73
|
|
|
73
74
|
- **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.
|
|
@@ -135,7 +136,7 @@ v6.1.3 – v6.4.1 shipped in nine days (2026-11-01 → 11-09), guarded by **1,22
|
|
|
135
136
|
|
|
136
137
|
```
|
|
137
138
|
┌──────────────────────────────────────────────────────┐
|
|
138
|
-
│ MindForge v6.4.
|
|
139
|
+
│ MindForge v6.4.2 │
|
|
139
140
|
│ Cognitive Layer Personality · KnowledgeGraph │
|
|
140
141
|
│ MemoryEvolution · FederatedMemory │
|
|
141
142
|
├──────────────────────────────────────────────────────┤
|
|
@@ -380,7 +380,10 @@ class QueryEngine:
|
|
|
380
380
|
|
|
381
381
|
def _cached_get(mid: str) -> Optional[Any]:
|
|
382
382
|
if mid not in _entry_cache:
|
|
383
|
-
|
|
383
|
+
# v6.4.2 perf: 批量检索上下文——首访访问计数走挂账,
|
|
384
|
+
# 由本 search 尾部的 flush_all_access 统一落库。
|
|
385
|
+
_entry_cache[mid] = self.storage.get_memory(
|
|
386
|
+
mid, _batch_access=True)
|
|
384
387
|
return _entry_cache[mid]
|
|
385
388
|
|
|
386
389
|
# 预过滤:按 categories/layers 筛选 score_map,避免非匹配记忆占据排序位
|
|
@@ -470,6 +473,15 @@ class QueryEngine:
|
|
|
470
473
|
except Exception:
|
|
471
474
|
pass
|
|
472
475
|
|
|
476
|
+
# v6.4.2 perf: 本轮 search 命中的首访访问计数 + 挂账的 search 审计
|
|
477
|
+
# 在此统一批量落库(executemany + 单次 commit,含审计 INSERT 合并)。
|
|
478
|
+
# 挂账期上限 = 单次 search 时长,recently_accessed 排序在 search
|
|
479
|
+
# 返回后即正确;同条记忆 60s 节流窗口内重复命中仍免落库(v5.6.3 契约)。
|
|
480
|
+
try:
|
|
481
|
+
self.storage.flush_access()
|
|
482
|
+
except Exception:
|
|
483
|
+
pass
|
|
484
|
+
|
|
473
485
|
elapsed = (time.time() - start) * 1000
|
|
474
486
|
token_estimate = sum(len(c.content) for c in chunks) // 4
|
|
475
487
|
|
|
@@ -694,6 +694,11 @@ class MemoryCache:
|
|
|
694
694
|
|
|
695
695
|
|
|
696
696
|
# v5.6.3 性能修复:记忆读缓存 TTL(秒)。除写路径主动失效外的兜底,
|
|
697
|
+
# v6.4.2 perf: 枚举降级 memo(行反序列化高频路径)。值域天然有界
|
|
698
|
+
# (枚举成员数 × 类数);Enum 单例不可变共享安全;跨模块路径加载的
|
|
699
|
+
# 同名类副本身份不同,作为 dict key 天然隔离不串台。
|
|
700
|
+
_ENUM_DOWNGRADE_MEMO: Dict[type, Dict[Any, Any]] = {}
|
|
701
|
+
|
|
697
702
|
# 任何遗漏的写路径最多造成该窗口内的短时脏读,避免长期脏数据。
|
|
698
703
|
_ENTRY_CACHE_TTL = 30.0
|
|
699
704
|
|
|
@@ -731,6 +736,8 @@ class StorageEngine:
|
|
|
731
736
|
# v6.2.0 增长治理:搜索审计去抖状态(同词 60s 内只落一条)
|
|
732
737
|
self._search_dedup_lock = threading.Lock()
|
|
733
738
|
self._search_dedup: Dict[str, float] = {}
|
|
739
|
+
# v6.4.2 perf: search 审计挂账标记(INSERT 已进事务缓冲未 commit)
|
|
740
|
+
self._search_audit_dirty = False
|
|
734
741
|
self._init_db()
|
|
735
742
|
|
|
736
743
|
def _get_conn(self) -> sqlite3.Connection:
|
|
@@ -1168,7 +1175,23 @@ class StorageEngine:
|
|
|
1168
1175
|
v5.5.4 fix: 兼容双重导入路径导致的枚举类不一致问题。
|
|
1169
1176
|
当 isinstance() 因不同模块路径加载同名枚举而失败时,
|
|
1170
1177
|
通过 .value / .name 属性匹配来识别。
|
|
1178
|
+
|
|
1179
|
+
v6.4.2 perf: str 输入路径先查 memo(命中即免逐值遍历匹配);
|
|
1180
|
+
key 含 default,不假设同一 value 恒定同一 default。
|
|
1171
1181
|
"""
|
|
1182
|
+
if isinstance(value, str):
|
|
1183
|
+
memo = _ENUM_DOWNGRADE_MEMO.setdefault(enum_cls, {})
|
|
1184
|
+
hit = memo.get((value, default))
|
|
1185
|
+
if hit is not None:
|
|
1186
|
+
return hit
|
|
1187
|
+
result = StorageEngine._downgrade_enum_slow(value, enum_cls, default)
|
|
1188
|
+
memo[(value, default)] = result
|
|
1189
|
+
return result
|
|
1190
|
+
return StorageEngine._downgrade_enum_slow(value, enum_cls, default)
|
|
1191
|
+
|
|
1192
|
+
@staticmethod
|
|
1193
|
+
def _downgrade_enum_slow(value: Any, enum_cls: type, default: Any) -> Any:
|
|
1194
|
+
"""未命中 memo 时的逐值匹配内核(原 _downgrade_enum 逻辑)。"""
|
|
1172
1195
|
if isinstance(value, enum_cls):
|
|
1173
1196
|
return value
|
|
1174
1197
|
# 兼容双重导入:isinstance 失败但 value 有枚举属性时,用 .value 匹配
|
|
@@ -1345,7 +1368,8 @@ class StorageEngine:
|
|
|
1345
1368
|
return entry
|
|
1346
1369
|
|
|
1347
1370
|
def get_memory(self, memory_id: str,
|
|
1348
|
-
actor: str = "", session_id: str = ""
|
|
1371
|
+
actor: str = "", session_id: str = "",
|
|
1372
|
+
_batch_access: bool = False) -> Optional[MemoryEntry]:
|
|
1349
1373
|
"""获取记忆
|
|
1350
1374
|
|
|
1351
1375
|
v5.5.2: 自动过期检查——若 expires_at > 0 且已过期,自动移入回收站并返回 None。
|
|
@@ -1433,7 +1457,8 @@ class StorageEngine:
|
|
|
1433
1457
|
return entry
|
|
1434
1458
|
return None
|
|
1435
1459
|
|
|
1436
|
-
self._update_access(entry, actor, session_id
|
|
1460
|
+
self._update_access(entry, actor, session_id,
|
|
1461
|
+
_batch=_batch_access)
|
|
1437
1462
|
return entry
|
|
1438
1463
|
|
|
1439
1464
|
def get_memories_by_ids(self, memory_ids: List[str]) -> List[Optional[MemoryEntry]]:
|
|
@@ -7285,6 +7310,8 @@ class StorageEngine:
|
|
|
7285
7310
|
|
|
7286
7311
|
def _row_to_entry(self, row: sqlite3.Row) -> MemoryEntry:
|
|
7287
7312
|
# v5.3.7 修复:tags/metadata 可能已经是 list/dict(非 JSON 字符串)
|
|
7313
|
+
# v6.4.2 perf: 一次构造列名集合,避免每行 5 次 row.keys() 分配 + 线性 in 扫描
|
|
7314
|
+
_rk = set(row.keys())
|
|
7288
7315
|
raw_tags = row["tags"]
|
|
7289
7316
|
if isinstance(raw_tags, (list, tuple)):
|
|
7290
7317
|
tags_val = list(raw_tags)
|
|
@@ -7324,11 +7351,11 @@ class StorageEngine:
|
|
|
7324
7351
|
consolidation_count=row["consolidation_count"],
|
|
7325
7352
|
forgetting_score=row["forgetting_score"],
|
|
7326
7353
|
strength=row["strength"],
|
|
7327
|
-
starred=bool(row["starred"]) if "starred" in
|
|
7328
|
-
pinned=bool(row["pinned"]) if "pinned" in
|
|
7329
|
-
expires_at=float(row["expires_at"]) if "expires_at" in
|
|
7330
|
-
valid_from=float(row["valid_from"]) if "valid_from" in
|
|
7331
|
-
valid_to=float(row["valid_to"]) if "valid_to" in
|
|
7354
|
+
starred=bool(row["starred"]) if "starred" in _rk else False,
|
|
7355
|
+
pinned=bool(row["pinned"]) if "pinned" in _rk else False,
|
|
7356
|
+
expires_at=float(row["expires_at"]) if "expires_at" in _rk and row["expires_at"] else 0.0,
|
|
7357
|
+
valid_from=float(row["valid_from"]) if "valid_from" in _rk and row["valid_from"] else 0.0,
|
|
7358
|
+
valid_to=float(row["valid_to"]) if "valid_to" in _rk and row["valid_to"] else 0.0,
|
|
7332
7359
|
metadata=meta_val,
|
|
7333
7360
|
encrypted=bool(row["encrypted"]),
|
|
7334
7361
|
ciphertext=row["ciphertext"],
|
|
@@ -7336,7 +7363,8 @@ class StorageEngine:
|
|
|
7336
7363
|
salt=row["salt"],
|
|
7337
7364
|
)
|
|
7338
7365
|
|
|
7339
|
-
def _update_access(self, entry: MemoryEntry, actor: str, session_id: str
|
|
7366
|
+
def _update_access(self, entry: MemoryEntry, actor: str, session_id: str,
|
|
7367
|
+
_batch: bool = False):
|
|
7340
7368
|
"""更新访问计数
|
|
7341
7369
|
|
|
7342
7370
|
v5.1.2:不再每次 get 都写审计日志。
|
|
@@ -7352,7 +7380,16 @@ class StorageEngine:
|
|
|
7352
7380
|
self._access_pending[entry.id] = (
|
|
7353
7381
|
self._access_pending.get(entry.id, 0) + 1)
|
|
7354
7382
|
last = self._access_last_flush.get(entry.id, 0.0)
|
|
7355
|
-
if last
|
|
7383
|
+
if last == 0.0:
|
|
7384
|
+
# v6.4.2 perf: 首访起表。批量检索上下文(_batch=True,如
|
|
7385
|
+
# QueryEngine.search 的结果构建)首访只挂账,由调用方统一
|
|
7386
|
+
# 批量落库(search 尾部 flush_all_access,单次 executemany
|
|
7387
|
+
# + commit);直连 get_memory 首访仍立即落库(v5.6.3 契约)。
|
|
7388
|
+
self._access_last_flush[entry.id] = now
|
|
7389
|
+
if _batch:
|
|
7390
|
+
self._maybe_prune_access_locked(now)
|
|
7391
|
+
return
|
|
7392
|
+
elif now - last < _ACCESS_PERSIST_INTERVAL:
|
|
7356
7393
|
self._maybe_prune_access_locked(now)
|
|
7357
7394
|
return
|
|
7358
7395
|
self._flush_access_locked(entry.id, now)
|
|
@@ -7420,8 +7457,10 @@ class StorageEngine:
|
|
|
7420
7457
|
字典,计数不丢。
|
|
7421
7458
|
"""
|
|
7422
7459
|
with self._access_lock:
|
|
7423
|
-
|
|
7460
|
+
audit_dirty = self._search_audit_dirty
|
|
7461
|
+
if not self._access_pending and not audit_dirty:
|
|
7424
7462
|
return
|
|
7463
|
+
self._search_audit_dirty = False
|
|
7425
7464
|
now = time.time()
|
|
7426
7465
|
pending = self._access_pending
|
|
7427
7466
|
self._access_pending = {}
|
|
@@ -7429,10 +7468,12 @@ class StorageEngine:
|
|
|
7429
7468
|
items = [(inc, now, mid) for mid, inc in pending.items()]
|
|
7430
7469
|
conn = self._get_conn()
|
|
7431
7470
|
try:
|
|
7432
|
-
|
|
7433
|
-
|
|
7434
|
-
|
|
7435
|
-
|
|
7471
|
+
if items:
|
|
7472
|
+
conn.executemany("""
|
|
7473
|
+
UPDATE memories SET access_count = access_count + ?, last_accessed_at = ?
|
|
7474
|
+
WHERE id = ?
|
|
7475
|
+
""", items)
|
|
7476
|
+
# v6.4.2 perf: 挂账的 search 审计 INSERT(若有)随本事务一并提交
|
|
7436
7477
|
conn.commit()
|
|
7437
7478
|
except Exception as e:
|
|
7438
7479
|
with self._access_lock:
|
|
@@ -9248,8 +9289,14 @@ class StorageEngine:
|
|
|
9248
9289
|
self._search_dedup_lock = threading.Lock()
|
|
9249
9290
|
self._search_dedup = {}
|
|
9250
9291
|
try:
|
|
9292
|
+
# v6.4.2 perf: search 审计 INSERT 挂账不 commit,与下一次
|
|
9293
|
+
# flush_access() 的批量 UPDATE 合并成同一次事务提交(IO 慢态
|
|
9294
|
+
# 下 commit ~1ms,search 热路径 100 次 commit → 与挂账合并归零)。
|
|
9295
|
+
# search 审计为非合规数据(v6.2.0 prune 同一分类),崩溃窗口
|
|
9296
|
+
# ≤ 单次 search 时长,可接受;合规类审计不受影响仍即时 commit。
|
|
9297
|
+
self._search_audit_dirty = True
|
|
9251
9298
|
self._add_audit("search", q, "", "", "INTERNAL",
|
|
9252
|
-
{"query": q}, commit=
|
|
9299
|
+
{"query": q}, commit=False)
|
|
9253
9300
|
except Exception as _err:
|
|
9254
9301
|
logger.warning("record_search 审计写入失败: %s", _err)
|
|
9255
9302
|
|
|
@@ -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.2"
|
|
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, 2)
|
|
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.2
|
|
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.2: no team, no funding, 1,224 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,8 +96,9 @@ MindForge stats
|
|
|
96
96
|
|
|
97
97
|
## What's New
|
|
98
98
|
|
|
99
|
-
v6.1.3 – v6.4.
|
|
99
|
+
v6.1.3 – v6.4.2 shipped in nine days (2026-11-01 → 11-09), guarded by **1,224 tests** (all passing).
|
|
100
100
|
|
|
101
|
+
- **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.
|
|
101
102
|
- **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.
|
|
102
103
|
|
|
103
104
|
- **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.
|
|
@@ -165,7 +166,7 @@ v6.1.3 – v6.4.1 shipped in nine days (2026-11-01 → 11-09), guarded by **1,22
|
|
|
165
166
|
|
|
166
167
|
```
|
|
167
168
|
┌──────────────────────────────────────────────────────┐
|
|
168
|
-
│ MindForge v6.4.
|
|
169
|
+
│ MindForge v6.4.2 │
|
|
169
170
|
│ Cognitive Layer Personality · KnowledgeGraph │
|
|
170
171
|
│ MemoryEvolution · FederatedMemory │
|
|
171
172
|
├──────────────────────────────────────────────────────┤
|
|
@@ -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.2")
|
|
44
|
+
self.assertEqual(core_version.VERSION_INFO, (6, 4, 2))
|
|
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.2")
|
|
666
|
+
self.assertEqual(VERSION_INFO, (6, 4, 2))
|
|
667
667
|
|
|
668
668
|
def test_json_round_trip_safety_of_preview(self):
|
|
669
669
|
"""save_evolution 的预览序列化不得抛(不可 json 化的对象走 str 兜底)。"""
|
|
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.1 → mindforge_memory-6.4.2}/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
|
|
File without changes
|
{mindforge_memory-6.4.1 → mindforge_memory-6.4.2}/mindforge_memory.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
{mindforge_memory-6.4.1 → mindforge_memory-6.4.2}/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
|