mnemo-engine 0.4.0__tar.gz → 0.4.1__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.
- {mnemo_engine-0.4.0 → mnemo_engine-0.4.1}/PKG-INFO +12 -1
- {mnemo_engine-0.4.0 → mnemo_engine-0.4.1}/README.md +11 -0
- {mnemo_engine-0.4.0 → mnemo_engine-0.4.1}/mnemo/Cargo.lock +101 -4
- {mnemo_engine-0.4.0 → mnemo_engine-0.4.1}/mnemo/Cargo.toml +22 -2
- mnemo_engine-0.4.1/mnemo/deny.toml +131 -0
- mnemo_engine-0.4.1/mnemo/src/lib.rs +158 -0
- {mnemo_engine-0.4.0 → mnemo_engine-0.4.1}/mnemo/src/mcp.rs +11 -0
- {mnemo_engine-0.4.0 → mnemo_engine-0.4.1}/mnemo/src/pager.rs +72 -0
- {mnemo_engine-0.4.0 → mnemo_engine-0.4.1}/mnemo/src/wal.rs +25 -4
- mnemo_engine-0.4.1/mnemo/tests/proptest_store.proptest-regressions +8 -0
- mnemo_engine-0.4.1/mnemo/tests/proptest_store.rs +526 -0
- {mnemo_engine-0.4.0 → mnemo_engine-0.4.1}/mnemo-python/Cargo.lock +4 -4
- {mnemo_engine-0.4.0 → mnemo_engine-0.4.1}/mnemo-python/Cargo.toml +1 -1
- {mnemo_engine-0.4.0 → mnemo_engine-0.4.1}/mnemo-python/README.md +11 -0
- {mnemo_engine-0.4.0 → mnemo_engine-0.4.1}/mnemo-python/src/lib.rs +165 -5
- {mnemo_engine-0.4.0 → mnemo_engine-0.4.1}/mnemo-python/test_mnemo.py +171 -0
- {mnemo_engine-0.4.0 → mnemo_engine-0.4.1}/pyproject.toml +1 -1
- mnemo_engine-0.4.0/mnemo/src/lib.rs +0 -80
- {mnemo_engine-0.4.0 → mnemo_engine-0.4.1}/mnemo/.gitignore +0 -0
- {mnemo_engine-0.4.0 → mnemo_engine-0.4.1}/mnemo/README.md +0 -0
- {mnemo_engine-0.4.0 → mnemo_engine-0.4.1}/mnemo/examples/quickstart.rs +0 -0
- {mnemo_engine-0.4.0 → mnemo_engine-0.4.1}/mnemo/examples/session.rs +0 -0
- {mnemo_engine-0.4.0 → mnemo_engine-0.4.1}/mnemo/src/bin/mnemo.rs +0 -0
- {mnemo_engine-0.4.0 → mnemo_engine-0.4.1}/mnemo/src/cache.rs +0 -0
- {mnemo_engine-0.4.0 → mnemo_engine-0.4.1}/mnemo/src/crypto.rs +0 -0
- {mnemo_engine-0.4.0 → mnemo_engine-0.4.1}/mnemo/src/error.rs +0 -0
- {mnemo_engine-0.4.0 → mnemo_engine-0.4.1}/mnemo/src/format.rs +0 -0
- {mnemo_engine-0.4.0 → mnemo_engine-0.4.1}/mnemo/src/index.rs +0 -0
- {mnemo_engine-0.4.0 → mnemo_engine-0.4.1}/mnemo/src/memory.rs +0 -0
- {mnemo_engine-0.4.0 → mnemo_engine-0.4.1}/mnemo/src/result_cache.rs +0 -0
- {mnemo_engine-0.4.0 → mnemo_engine-0.4.1}/mnemo/src/session.rs +0 -0
- {mnemo_engine-0.4.0 → mnemo_engine-0.4.1}/mnemo/src/store.rs +0 -0
- {mnemo_engine-0.4.0 → mnemo_engine-0.4.1}/mnemo/tests/cli_smoke.rs +0 -0
- {mnemo_engine-0.4.0 → mnemo_engine-0.4.1}/mnemo/tests/integration.rs +0 -0
- {mnemo_engine-0.4.0 → mnemo_engine-0.4.1}/mnemo-python/.gitignore +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mnemo-engine
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.1
|
|
4
4
|
Classifier: Programming Language :: Rust
|
|
5
5
|
Classifier: Programming Language :: Python :: 3
|
|
6
6
|
Classifier: License :: OSI Approved :: Apache Software License
|
|
@@ -155,11 +155,22 @@ its own agent.
|
|
|
155
155
|
| `cache_stats(namespace=None)` | Result-cache stats: `{entries, bytes, hits, misses, hit_rate, evictions}` |
|
|
156
156
|
| `cache_put_semantic(namespace, key, vector, value, model, content_type="text", ttl_secs=None)` | Semantic cache put (Phase 10.2) — vector must match db dimensions |
|
|
157
157
|
| `cache_get_semantic(namespace, query, model, threshold=0.97)` | Top-1 cosine over the namespace's vectored entries whose `model` matches |
|
|
158
|
+
| `set_cache_flush_policy(policy, max_dirty=None, max_age_secs=None)` | `policy="strict"` (default; every put durable on return) or `"batched"` (engine auto-flushes at `max_dirty` puts or `max_age_secs` seconds). Under `batched`, `cache_put` and friends no longer force an eager flush — call `db.flush()` yourself if you need it before a specific boundary |
|
|
159
|
+
| `set_cache_budget(namespace, max_entries, max_bytes)` | Per-namespace LRU cap (defaults 10 000 entries / 64 MiB). On the next `cache_put` the LRU is evicted until both limits are satisfied |
|
|
158
160
|
| `set_max_snapshots(max)` | Override the snapshot-manifest retention cap (default 256; `0` disables) |
|
|
161
|
+
| `memories()` | Enumerate every live (non-deleted, non-expired) memory as a list of dicts |
|
|
162
|
+
| `dimensions()` | Embedding dimensionality this database was created with (also on `stats()["dimensions"]`) |
|
|
163
|
+
| `rekey(new_passphrase, fast=False)` | Change the passphrase. Re-derives the KEK and re-wraps the DEK; encrypted pages are not rewritten (O(1) in db size). `fast=True` uses the cheap-Argon2 test params — omit for production |
|
|
159
164
|
| `stats()` | Summary statistics |
|
|
160
165
|
| `export_encrypted(dest)` | Copy the (already-encrypted) file elsewhere |
|
|
161
166
|
| `len(db)` | Live memory count |
|
|
162
167
|
|
|
168
|
+
Plus one module-level function:
|
|
169
|
+
|
|
170
|
+
| Function | Purpose |
|
|
171
|
+
|---|---|
|
|
172
|
+
| `mnemo.compact_file(path, passphrase)` | Rewrite the file out-of-place, dropping tombstoned and expired memories and reclaiming stale pages. Caller's own handle must be closed first. Returns `{"before": int, "after": int}` |
|
|
173
|
+
|
|
163
174
|
`Session` methods: `add_turn(turn)`, `recall(query, top_k=10, memory_types=None)`,
|
|
164
175
|
`close()`, `discard()`, `id()`, `agent()`, `turn_ids()`, `turn_count()`; also a
|
|
165
176
|
context manager (exiting consolidates).
|
|
@@ -141,11 +141,22 @@ its own agent.
|
|
|
141
141
|
| `cache_stats(namespace=None)` | Result-cache stats: `{entries, bytes, hits, misses, hit_rate, evictions}` |
|
|
142
142
|
| `cache_put_semantic(namespace, key, vector, value, model, content_type="text", ttl_secs=None)` | Semantic cache put (Phase 10.2) — vector must match db dimensions |
|
|
143
143
|
| `cache_get_semantic(namespace, query, model, threshold=0.97)` | Top-1 cosine over the namespace's vectored entries whose `model` matches |
|
|
144
|
+
| `set_cache_flush_policy(policy, max_dirty=None, max_age_secs=None)` | `policy="strict"` (default; every put durable on return) or `"batched"` (engine auto-flushes at `max_dirty` puts or `max_age_secs` seconds). Under `batched`, `cache_put` and friends no longer force an eager flush — call `db.flush()` yourself if you need it before a specific boundary |
|
|
145
|
+
| `set_cache_budget(namespace, max_entries, max_bytes)` | Per-namespace LRU cap (defaults 10 000 entries / 64 MiB). On the next `cache_put` the LRU is evicted until both limits are satisfied |
|
|
144
146
|
| `set_max_snapshots(max)` | Override the snapshot-manifest retention cap (default 256; `0` disables) |
|
|
147
|
+
| `memories()` | Enumerate every live (non-deleted, non-expired) memory as a list of dicts |
|
|
148
|
+
| `dimensions()` | Embedding dimensionality this database was created with (also on `stats()["dimensions"]`) |
|
|
149
|
+
| `rekey(new_passphrase, fast=False)` | Change the passphrase. Re-derives the KEK and re-wraps the DEK; encrypted pages are not rewritten (O(1) in db size). `fast=True` uses the cheap-Argon2 test params — omit for production |
|
|
145
150
|
| `stats()` | Summary statistics |
|
|
146
151
|
| `export_encrypted(dest)` | Copy the (already-encrypted) file elsewhere |
|
|
147
152
|
| `len(db)` | Live memory count |
|
|
148
153
|
|
|
154
|
+
Plus one module-level function:
|
|
155
|
+
|
|
156
|
+
| Function | Purpose |
|
|
157
|
+
|---|---|
|
|
158
|
+
| `mnemo.compact_file(path, passphrase)` | Rewrite the file out-of-place, dropping tombstoned and expired memories and reclaiming stale pages. Caller's own handle must be closed first. Returns `{"before": int, "after": int}` |
|
|
159
|
+
|
|
149
160
|
`Session` methods: `add_turn(turn)`, `recall(query, top_k=10, memory_types=None)`,
|
|
150
161
|
`close()`, `discard()`, `id()`, `agent()`, `turn_ids()`, `turn_count()`; also a
|
|
151
162
|
context manager (exiting consolidates).
|
|
@@ -120,6 +120,21 @@ version = "1.6.0"
|
|
|
120
120
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
121
121
|
checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b"
|
|
122
122
|
|
|
123
|
+
[[package]]
|
|
124
|
+
name = "bit-set"
|
|
125
|
+
version = "0.5.3"
|
|
126
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
127
|
+
checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1"
|
|
128
|
+
dependencies = [
|
|
129
|
+
"bit-vec",
|
|
130
|
+
]
|
|
131
|
+
|
|
132
|
+
[[package]]
|
|
133
|
+
name = "bit-vec"
|
|
134
|
+
version = "0.6.3"
|
|
135
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
136
|
+
checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb"
|
|
137
|
+
|
|
123
138
|
[[package]]
|
|
124
139
|
name = "bitflags"
|
|
125
140
|
version = "2.11.1"
|
|
@@ -293,6 +308,12 @@ version = "2.4.1"
|
|
|
293
308
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
294
309
|
checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6"
|
|
295
310
|
|
|
311
|
+
[[package]]
|
|
312
|
+
name = "fnv"
|
|
313
|
+
version = "1.0.7"
|
|
314
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
315
|
+
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
|
316
|
+
|
|
296
317
|
[[package]]
|
|
297
318
|
name = "fs4"
|
|
298
319
|
version = "0.9.1"
|
|
@@ -399,6 +420,12 @@ dependencies = [
|
|
|
399
420
|
"wasm-bindgen",
|
|
400
421
|
]
|
|
401
422
|
|
|
423
|
+
[[package]]
|
|
424
|
+
name = "lazy_static"
|
|
425
|
+
version = "1.5.0"
|
|
426
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
427
|
+
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
|
428
|
+
|
|
402
429
|
[[package]]
|
|
403
430
|
name = "libc"
|
|
404
431
|
version = "0.2.186"
|
|
@@ -415,6 +442,12 @@ dependencies = [
|
|
|
415
442
|
"windows-link",
|
|
416
443
|
]
|
|
417
444
|
|
|
445
|
+
[[package]]
|
|
446
|
+
name = "libm"
|
|
447
|
+
version = "0.2.16"
|
|
448
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
449
|
+
checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981"
|
|
450
|
+
|
|
418
451
|
[[package]]
|
|
419
452
|
name = "linux-raw-sys"
|
|
420
453
|
version = "0.4.15"
|
|
@@ -429,13 +462,14 @@ checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79"
|
|
|
429
462
|
|
|
430
463
|
[[package]]
|
|
431
464
|
name = "mnemo-engine"
|
|
432
|
-
version = "0.4.
|
|
465
|
+
version = "0.4.1"
|
|
433
466
|
dependencies = [
|
|
434
467
|
"aes-gcm",
|
|
435
468
|
"argon2",
|
|
436
469
|
"base64ct",
|
|
437
470
|
"clap",
|
|
438
471
|
"fs4",
|
|
472
|
+
"proptest",
|
|
439
473
|
"rand",
|
|
440
474
|
"rmp",
|
|
441
475
|
"rmp-serde",
|
|
@@ -451,7 +485,7 @@ dependencies = [
|
|
|
451
485
|
|
|
452
486
|
[[package]]
|
|
453
487
|
name = "mnemo-node"
|
|
454
|
-
version = "0.4.
|
|
488
|
+
version = "0.4.1"
|
|
455
489
|
dependencies = [
|
|
456
490
|
"mnemo-engine",
|
|
457
491
|
"napi",
|
|
@@ -523,6 +557,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
|
523
557
|
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
|
|
524
558
|
dependencies = [
|
|
525
559
|
"autocfg",
|
|
560
|
+
"libm",
|
|
526
561
|
]
|
|
527
562
|
|
|
528
563
|
[[package]]
|
|
@@ -596,6 +631,32 @@ dependencies = [
|
|
|
596
631
|
"unicode-ident",
|
|
597
632
|
]
|
|
598
633
|
|
|
634
|
+
[[package]]
|
|
635
|
+
name = "proptest"
|
|
636
|
+
version = "1.5.0"
|
|
637
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
638
|
+
checksum = "b4c2511913b88df1637da85cc8d96ec8e43a3f8bb8ccb71ee1ac240d6f3df58d"
|
|
639
|
+
dependencies = [
|
|
640
|
+
"bit-set",
|
|
641
|
+
"bit-vec",
|
|
642
|
+
"bitflags",
|
|
643
|
+
"lazy_static",
|
|
644
|
+
"num-traits",
|
|
645
|
+
"rand",
|
|
646
|
+
"rand_chacha",
|
|
647
|
+
"rand_xorshift",
|
|
648
|
+
"regex-syntax",
|
|
649
|
+
"rusty-fork",
|
|
650
|
+
"tempfile",
|
|
651
|
+
"unarray",
|
|
652
|
+
]
|
|
653
|
+
|
|
654
|
+
[[package]]
|
|
655
|
+
name = "quick-error"
|
|
656
|
+
version = "1.2.3"
|
|
657
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
658
|
+
checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
|
|
659
|
+
|
|
599
660
|
[[package]]
|
|
600
661
|
name = "quote"
|
|
601
662
|
version = "1.0.45"
|
|
@@ -607,9 +668,9 @@ dependencies = [
|
|
|
607
668
|
|
|
608
669
|
[[package]]
|
|
609
670
|
name = "rand"
|
|
610
|
-
version = "0.8.
|
|
671
|
+
version = "0.8.6"
|
|
611
672
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
612
|
-
checksum = "
|
|
673
|
+
checksum = "5ca0ecfa931c29007047d1bc58e623ab12e5590e8c7cc53200d5202b69266d8a"
|
|
613
674
|
dependencies = [
|
|
614
675
|
"libc",
|
|
615
676
|
"rand_chacha",
|
|
@@ -635,6 +696,15 @@ dependencies = [
|
|
|
635
696
|
"getrandom",
|
|
636
697
|
]
|
|
637
698
|
|
|
699
|
+
[[package]]
|
|
700
|
+
name = "rand_xorshift"
|
|
701
|
+
version = "0.3.0"
|
|
702
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
703
|
+
checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f"
|
|
704
|
+
dependencies = [
|
|
705
|
+
"rand_core",
|
|
706
|
+
]
|
|
707
|
+
|
|
638
708
|
[[package]]
|
|
639
709
|
name = "regex"
|
|
640
710
|
version = "1.12.3"
|
|
@@ -726,6 +796,18 @@ version = "1.0.22"
|
|
|
726
796
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
727
797
|
checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
|
|
728
798
|
|
|
799
|
+
[[package]]
|
|
800
|
+
name = "rusty-fork"
|
|
801
|
+
version = "0.3.1"
|
|
802
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
803
|
+
checksum = "cc6bf79ff24e648f6da1f8d1f011e9cac26491b619e6b9280f2b47f1774e6ee2"
|
|
804
|
+
dependencies = [
|
|
805
|
+
"fnv",
|
|
806
|
+
"quick-error",
|
|
807
|
+
"tempfile",
|
|
808
|
+
"wait-timeout",
|
|
809
|
+
]
|
|
810
|
+
|
|
729
811
|
[[package]]
|
|
730
812
|
name = "ryu"
|
|
731
813
|
version = "1.0.23"
|
|
@@ -860,6 +942,12 @@ dependencies = [
|
|
|
860
942
|
"web-time",
|
|
861
943
|
]
|
|
862
944
|
|
|
945
|
+
[[package]]
|
|
946
|
+
name = "unarray"
|
|
947
|
+
version = "0.1.4"
|
|
948
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
949
|
+
checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94"
|
|
950
|
+
|
|
863
951
|
[[package]]
|
|
864
952
|
name = "unicode-ident"
|
|
865
953
|
version = "1.0.24"
|
|
@@ -894,6 +982,15 @@ version = "0.9.5"
|
|
|
894
982
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
895
983
|
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
|
|
896
984
|
|
|
985
|
+
[[package]]
|
|
986
|
+
name = "wait-timeout"
|
|
987
|
+
version = "0.2.1"
|
|
988
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
989
|
+
checksum = "09ac3b126d3914f9849036f826e054cbabdc8519970b8998ddaf3b5bd3c65f11"
|
|
990
|
+
dependencies = [
|
|
991
|
+
"libc",
|
|
992
|
+
]
|
|
993
|
+
|
|
897
994
|
[[package]]
|
|
898
995
|
name = "wasi"
|
|
899
996
|
version = "0.11.1+wasi-snapshot-preview1"
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
# below stay `mnemo`, so consumers still write `use mnemo::...` and invoke
|
|
7
7
|
# `mnemo` on the command line after `cargo add mnemo-engine`.
|
|
8
8
|
name = "mnemo-engine"
|
|
9
|
-
version = "0.4.
|
|
9
|
+
version = "0.4.1"
|
|
10
10
|
edition = "2021"
|
|
11
11
|
rust-version = "1.75"
|
|
12
12
|
description = "Encrypted, single-file, portable agent memory engine with multi-signal recall, sessions, snapshots, and an IVF+PQ index"
|
|
@@ -55,7 +55,14 @@ base64ct = "=1.6.0"
|
|
|
55
55
|
ulid = { version = "=1.1.3", features = ["serde"] }
|
|
56
56
|
thiserror = "=1.0.64"
|
|
57
57
|
zeroize = "=1.8.1"
|
|
58
|
-
|
|
58
|
+
# Pinned `=0.8.6` (patch bump from 0.8.5) to close RUSTSEC-2026-0097,
|
|
59
|
+
# an unsoundness that only triggers under a very narrow config —
|
|
60
|
+
# custom `log::Log` impl reentrantly calling `rand::rng()` on
|
|
61
|
+
# `ThreadRng` mid-reseed — which mnemo does not do. The engine
|
|
62
|
+
# never touches `log`, and no CLI code paths reach `ThreadRng` from
|
|
63
|
+
# a logger. Upgrading anyway is the least-surprising thing to do
|
|
64
|
+
# and satisfies the `advisories` check without an ignore.
|
|
65
|
+
rand = "=0.8.6"
|
|
59
66
|
clap = { version = "=4.4.18", features = ["derive"] }
|
|
60
67
|
# Used by the `mnemo` CLI binary only — `passphrase()` calls it when stdin
|
|
61
68
|
# is a TTY to read the passphrase without echo. The library never touches
|
|
@@ -77,6 +84,19 @@ sha2 = "=0.10.8"
|
|
|
77
84
|
|
|
78
85
|
[dev-dependencies]
|
|
79
86
|
tempfile = "=3.10.1"
|
|
87
|
+
# Property-based testing (Phase 1.3). Pinned exactly; `=1.5.0`'s MSRV
|
|
88
|
+
# is Rust 1.65, well under our 1.75 floor. Kept as a dev-dep only —
|
|
89
|
+
# it does not appear in the shipped library dependency set.
|
|
90
|
+
proptest = "=1.5.0"
|
|
91
|
+
|
|
92
|
+
[features]
|
|
93
|
+
# When enabled, exposes crash-injection hooks used by the Phase 1.3
|
|
94
|
+
# proptest under `mnemo::__failpoints::*`. Always enabled during
|
|
95
|
+
# `cargo test` via `#[cfg(test)]`; the feature is here so an external
|
|
96
|
+
# fault-injection consumer (or a future integration test in a sibling
|
|
97
|
+
# crate) can opt into the same hooks without touching this crate's
|
|
98
|
+
# tests. Not for production use — never enable in a release build.
|
|
99
|
+
failpoints = []
|
|
80
100
|
|
|
81
101
|
[profile.release]
|
|
82
102
|
lto = true
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
# cargo-deny configuration (Phase 1.4).
|
|
2
|
+
#
|
|
3
|
+
# Runs in CI on every PR via `cargo deny check` in `mnemo/`. Three
|
|
4
|
+
# checks matter here:
|
|
5
|
+
# 1. `advisories` — cross-references the RustSec advisory DB and
|
|
6
|
+
# fails on any unpatched vulnerability affecting a dep in the
|
|
7
|
+
# lockfile.
|
|
8
|
+
# 2. `licenses` — allowlist of OSI-approved licenses compatible
|
|
9
|
+
# with the Apache-2.0 the mnemo crate itself is published under.
|
|
10
|
+
# Anything unlisted requires an explicit `exceptions` entry.
|
|
11
|
+
# 3. `bans` — currently informational (`multiple-versions = "warn"`)
|
|
12
|
+
# to catch transitive duplication without blocking merges while
|
|
13
|
+
# we enumerate the necessary skip set. Follow-up PR will
|
|
14
|
+
# tighten to `deny` once CI has surfaced the real exception
|
|
15
|
+
# list.
|
|
16
|
+
#
|
|
17
|
+
# Run locally with:
|
|
18
|
+
# cd mnemo && cargo deny check
|
|
19
|
+
#
|
|
20
|
+
# For just one check:
|
|
21
|
+
# cargo deny check advisories
|
|
22
|
+
# cargo deny check licenses
|
|
23
|
+
# cargo deny check bans
|
|
24
|
+
|
|
25
|
+
# ---------------------------------------------------------------------
|
|
26
|
+
# Graph configuration — what crates the checks apply to.
|
|
27
|
+
# ---------------------------------------------------------------------
|
|
28
|
+
|
|
29
|
+
[graph]
|
|
30
|
+
# Empty `targets` means "check all crates for all target triples the
|
|
31
|
+
# lockfile references." Since mnemo compiles on Linux/macOS/Windows
|
|
32
|
+
# x86_64+aarch64, restricting to a single target would hide license /
|
|
33
|
+
# advisory hits on platform-specific transitives (e.g. `windows-sys`
|
|
34
|
+
# on Windows only).
|
|
35
|
+
targets = []
|
|
36
|
+
all-features = false
|
|
37
|
+
no-default-features = false
|
|
38
|
+
|
|
39
|
+
[output]
|
|
40
|
+
# Group violations by feature depth; keeps the diagnostic readable
|
|
41
|
+
# on the multi-page transitive graph typical of a Rust crate.
|
|
42
|
+
feature-depth = 1
|
|
43
|
+
|
|
44
|
+
# ---------------------------------------------------------------------
|
|
45
|
+
# Advisory DB — RustSec vulnerability + yanked-crate check.
|
|
46
|
+
# ---------------------------------------------------------------------
|
|
47
|
+
|
|
48
|
+
[advisories]
|
|
49
|
+
version = 2
|
|
50
|
+
db-path = "~/.cargo/advisory-db"
|
|
51
|
+
db-urls = ["https://github.com/rustsec/advisory-db"]
|
|
52
|
+
# `yanked` fires when Cargo.lock pins a version the author later
|
|
53
|
+
# yanked from crates.io. `warn` first so we notice; upgrade to
|
|
54
|
+
# `deny` once we've picked up a clean baseline.
|
|
55
|
+
yanked = "warn"
|
|
56
|
+
# Time-boxed / justified exceptions to the advisory check. Every
|
|
57
|
+
# entry must carry a `reason` — a future worker reading this file
|
|
58
|
+
# should be able to tell why the ignore was granted without leaving
|
|
59
|
+
# the config.
|
|
60
|
+
ignore = [
|
|
61
|
+
# `paste 1.0.15` — dtolnay archived the repo (RUSTSEC-2024-0436,
|
|
62
|
+
# "unmaintained"). NOT a vulnerability. It is pulled transitively
|
|
63
|
+
# by `rmp` / `rmp-serde` (our MessagePack layer) which have no
|
|
64
|
+
# migration path today. The advisory itself suggests `pastey` as
|
|
65
|
+
# a drop-in fork; that migration lives in `rmp`, not here. Revisit
|
|
66
|
+
# when `rmp-serde` ships a release that swaps to `pastey`.
|
|
67
|
+
{ id = "RUSTSEC-2024-0436", reason = "paste (unmaintained) is a transitive dep of rmp-serde; no upstream migration path yet — track via rmp-serde issue tracker" },
|
|
68
|
+
]
|
|
69
|
+
|
|
70
|
+
# ---------------------------------------------------------------------
|
|
71
|
+
# Licenses — SPDX allowlist. Anything not listed → build fails.
|
|
72
|
+
# ---------------------------------------------------------------------
|
|
73
|
+
|
|
74
|
+
[licenses]
|
|
75
|
+
version = 2
|
|
76
|
+
# Kept minimal — the mnemo dep graph in practice only ships crates
|
|
77
|
+
# under Apache-2.0, MIT, BSD-3-Clause, and Unicode-3.0 (the newer
|
|
78
|
+
# SPDX variant `unicode-ident` uses). Apache-2.0 WITH LLVM-exception
|
|
79
|
+
# covers the `rustix` family. Adding an unused allow is not a security
|
|
80
|
+
# hole but it's noise — cargo-deny prints an "unmatched license
|
|
81
|
+
# allowance" warning for each, and drift in the allowlist becomes
|
|
82
|
+
# hard to notice. Extend this list only when a new dep actually
|
|
83
|
+
# ships under something not already here.
|
|
84
|
+
allow = [
|
|
85
|
+
"Apache-2.0",
|
|
86
|
+
"Apache-2.0 WITH LLVM-exception",
|
|
87
|
+
"MIT",
|
|
88
|
+
"BSD-2-Clause",
|
|
89
|
+
"BSD-3-Clause",
|
|
90
|
+
"Unicode-3.0",
|
|
91
|
+
]
|
|
92
|
+
# Threshold for guessing a license from a LICENSE file's text when
|
|
93
|
+
# the crate omits SPDX metadata. Default 0.8 is conservative — lower
|
|
94
|
+
# it only if a specific crate needs a `[[licenses.clarify]]` entry.
|
|
95
|
+
confidence-threshold = 0.8
|
|
96
|
+
# Per-crate license overrides go here if a crate ships an unusual
|
|
97
|
+
# LICENSE mix cargo-deny can't parse. Empty at PR 9 landing;
|
|
98
|
+
# expand as CI surfaces cases.
|
|
99
|
+
exceptions = []
|
|
100
|
+
|
|
101
|
+
# ---------------------------------------------------------------------
|
|
102
|
+
# Bans — duplicate versions, wildcard deps, per-crate allow/deny.
|
|
103
|
+
# ---------------------------------------------------------------------
|
|
104
|
+
|
|
105
|
+
[bans]
|
|
106
|
+
# `warn` for now: transitive graphs commonly duplicate `syn 1/2`,
|
|
107
|
+
# `regex-syntax 0.6/0.8`, `bitflags 1/2`, `windows-sys 0.4x/0.5x`
|
|
108
|
+
# during migrations. First green CI run will list the actual
|
|
109
|
+
# duplicates; follow-up PR flips to `deny` with the enumerated
|
|
110
|
+
# `skip` entries.
|
|
111
|
+
multiple-versions = "warn"
|
|
112
|
+
wildcards = "allow"
|
|
113
|
+
highlight = "all"
|
|
114
|
+
workspace-default-features = "allow"
|
|
115
|
+
external-default-features = "allow"
|
|
116
|
+
allow = []
|
|
117
|
+
deny = []
|
|
118
|
+
skip = []
|
|
119
|
+
skip-tree = []
|
|
120
|
+
|
|
121
|
+
# ---------------------------------------------------------------------
|
|
122
|
+
# Sources — where crates may come from.
|
|
123
|
+
# ---------------------------------------------------------------------
|
|
124
|
+
|
|
125
|
+
[sources]
|
|
126
|
+
unknown-registry = "warn"
|
|
127
|
+
unknown-git = "warn"
|
|
128
|
+
# Only crates.io. No git deps in mnemo (all pinned to `=x.y.z` per
|
|
129
|
+
# AGENTS.md), so this stays tight.
|
|
130
|
+
allow-registry = ["https://github.com/rust-lang/crates.io-index"]
|
|
131
|
+
allow-git = []
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
//! # Mnemo
|
|
2
|
+
//!
|
|
3
|
+
//! Mnemo is an **encrypted, single-file, portable agent-memory engine**.
|
|
4
|
+
//!
|
|
5
|
+
//! A whole memory store — vectors, content, metadata, and the multi-signal
|
|
6
|
+
//! recall machinery an agent needs — lives in one file you can copy, back up,
|
|
7
|
+
//! or hand to another process. The file is encrypted at rest with a two-tier
|
|
8
|
+
//! key hierarchy: an Argon2id key-encryption key (KEK) derived from a
|
|
9
|
+
//! passphrase wraps a random data-encryption key (DEK), and the DEK encrypts
|
|
10
|
+
//! every page with AES-256-GCM.
|
|
11
|
+
//!
|
|
12
|
+
//! ## Quick start
|
|
13
|
+
//!
|
|
14
|
+
//! ```no_run
|
|
15
|
+
//! use mnemo::{Mnemo, MnemoConfig, Memory, MemoryType, RecallRequest};
|
|
16
|
+
//!
|
|
17
|
+
//! # fn main() -> mnemo::Result<()> {
|
|
18
|
+
//! let cfg = MnemoConfig { dimensions: 3, ..Default::default() };
|
|
19
|
+
//! let mut db = Mnemo::create("agent.mnemo", "correct horse battery", cfg)?;
|
|
20
|
+
//!
|
|
21
|
+
//! db.remember(
|
|
22
|
+
//! Memory::new("the user prefers dark mode", MemoryType::Semantic, vec![0.1, 0.2, 0.9])
|
|
23
|
+
//! .with_agent("assistant-1")
|
|
24
|
+
//! .with_importance(0.8),
|
|
25
|
+
//! )?;
|
|
26
|
+
//! db.flush()?;
|
|
27
|
+
//!
|
|
28
|
+
//! let hits = db.recall(&RecallRequest::new(vec![0.1, 0.2, 0.9]).top_k(5))?;
|
|
29
|
+
//! for h in hits {
|
|
30
|
+
//! println!("{:.3} {}", h.score, h.memory.content);
|
|
31
|
+
//! }
|
|
32
|
+
//! # Ok(())
|
|
33
|
+
//! # }
|
|
34
|
+
//! ```
|
|
35
|
+
//!
|
|
36
|
+
//! ## What is and is not built
|
|
37
|
+
//!
|
|
38
|
+
//! This crate implements a real, tested core: the encrypted single-file
|
|
39
|
+
//! storage engine, the crypto layer, the agent-memory model, multi-signal
|
|
40
|
+
//! recall, an IVF+PQ approximate-nearest-neighbour index, a write-ahead log,
|
|
41
|
+
//! snapshot-based point-in-time recovery, a bounded LRU page cache, and the
|
|
42
|
+
//! [`Session`] conversation wrapper. Exact brute-force search remains
|
|
43
|
+
//! available as the ground-truth baseline; a built index makes
|
|
44
|
+
//! [`Mnemo::recall`] sub-linear. Each [`Mnemo::flush`] is one atomic,
|
|
45
|
+
//! WAL-committed transaction and a restorable snapshot — [`Mnemo::restore_to`]
|
|
46
|
+
//! rewinds the database to any past transaction. Python and TypeScript
|
|
47
|
+
//! language bindings are the one documented roadmap item — see the README.
|
|
48
|
+
|
|
49
|
+
#![forbid(unsafe_code)]
|
|
50
|
+
#![warn(missing_docs)]
|
|
51
|
+
|
|
52
|
+
mod cache;
|
|
53
|
+
mod crypto;
|
|
54
|
+
mod error;
|
|
55
|
+
mod format;
|
|
56
|
+
mod index;
|
|
57
|
+
pub mod mcp;
|
|
58
|
+
mod memory;
|
|
59
|
+
mod pager;
|
|
60
|
+
mod result_cache;
|
|
61
|
+
mod session;
|
|
62
|
+
mod store;
|
|
63
|
+
mod wal;
|
|
64
|
+
|
|
65
|
+
pub use crypto::KdfParams;
|
|
66
|
+
pub use error::{MnemoError, Result};
|
|
67
|
+
pub use index::{IndexConfig, IndexInfo};
|
|
68
|
+
pub use memory::{Memory, MemoryType, Metric, Scope, ScoreWeights};
|
|
69
|
+
pub use result_cache::{
|
|
70
|
+
CacheBudget, CacheFlushPolicy, CachePutOpts, CacheStats, CachedValue, SemanticCachePutOpts,
|
|
71
|
+
DEFAULT_BATCH_MAX_AGE, DEFAULT_BATCH_MAX_DIRTY, DEFAULT_MAX_BYTES, DEFAULT_MAX_ENTRIES,
|
|
72
|
+
DEFAULT_SEMANTIC_THRESHOLD,
|
|
73
|
+
};
|
|
74
|
+
pub use session::{Role, Session, Turn};
|
|
75
|
+
pub use store::{
|
|
76
|
+
CompactReport, Mnemo, MnemoConfig, RecallRequest, RecallResult, SnapshotInfo, Stats,
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
/// Re-export of [`ulid::Ulid`], the identifier type used for memories.
|
|
80
|
+
pub use ulid::Ulid;
|
|
81
|
+
|
|
82
|
+
/// Test-only crash-injection surface used by the Phase 1.3 proptest
|
|
83
|
+
/// (`mnemo/tests/proptest_store.rs`). Compiled in under `cfg(test)`
|
|
84
|
+
/// and the `failpoints` feature only; absent from release builds.
|
|
85
|
+
///
|
|
86
|
+
/// **Not part of the stable public API.** The double-underscore
|
|
87
|
+
/// convention makes the "internal" contract obvious at the call site.
|
|
88
|
+
#[cfg(any(test, feature = "failpoints"))]
|
|
89
|
+
#[doc(hidden)]
|
|
90
|
+
pub mod __failpoints {
|
|
91
|
+
/// Arm the pager and WAL commit path to fail on the Nth
|
|
92
|
+
/// subsequent write. `n = 0` fails the very next write; `n = -1`
|
|
93
|
+
/// (or any negative) disables. One-shot — disarms on trigger.
|
|
94
|
+
///
|
|
95
|
+
/// Every write is checked before the byte hits the file, so a
|
|
96
|
+
/// triggered failure is guaranteed torn-BEFORE, never
|
|
97
|
+
/// torn-AFTER — critical for the "either pre-flush or post-flush"
|
|
98
|
+
/// invariant the property test asserts.
|
|
99
|
+
pub fn set_writes_until_fail(n: i64) {
|
|
100
|
+
crate::pager::failpoints::set_writes_until_fail(n)
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/// Internal parse surfaces exposed for cargo-fuzz targets (Phase 1.2).
|
|
105
|
+
///
|
|
106
|
+
/// **Not part of the stable public API.** Each function feeds an
|
|
107
|
+
/// arbitrary byte slice into a parse/decode path that a hostile
|
|
108
|
+
/// `.mnemo` file (or a hostile MCP client) can send us, and returns
|
|
109
|
+
/// `Result<()>` so a fuzz target only asserts "no panic." Marked
|
|
110
|
+
/// `#[doc(hidden)]` so `cargo doc` doesn't advertise it; do not import
|
|
111
|
+
/// from application code.
|
|
112
|
+
#[doc(hidden)]
|
|
113
|
+
pub mod __fuzz {
|
|
114
|
+
/// Parse a `.mnemo` header page (page 0, plaintext) from arbitrary
|
|
115
|
+
/// bytes. This is the pre-passphrase attack surface — a hostile
|
|
116
|
+
/// file hits it before any DEK exists.
|
|
117
|
+
pub fn parse_header(bytes: &[u8]) -> crate::Result<()> {
|
|
118
|
+
crate::format::Header::from_page(bytes).map(|_| ())
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/// Replay the WAL scan over an arbitrary byte region. Delegates
|
|
122
|
+
/// to [`crate::wal::recover_bytes`]; a corrupt / truncated /
|
|
123
|
+
/// adversarial region must return `Err` or `Ok(None/Some(_))`,
|
|
124
|
+
/// never panic.
|
|
125
|
+
pub fn wal_recover_bytes(region: &[u8], wal_seq: u64) -> crate::Result<()> {
|
|
126
|
+
crate::wal::recover_bytes(region, wal_seq).map(|_| ())
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/// Decode a `Memory` record body from arbitrary bytes.
|
|
130
|
+
pub fn decode_memory(bytes: &[u8]) -> crate::Result<()> {
|
|
131
|
+
rmp_serde::from_slice::<crate::Memory>(bytes)
|
|
132
|
+
.map(|_| ())
|
|
133
|
+
.map_err(|e| crate::MnemoError::Serialize(e.to_string()))
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/// Decode a `CacheEntry` record body from arbitrary bytes. Type is
|
|
137
|
+
/// `pub(crate)`, so this wrapper hides it behind a unit return.
|
|
138
|
+
pub fn decode_cache_entry(bytes: &[u8]) -> crate::Result<()> {
|
|
139
|
+
rmp_serde::from_slice::<crate::result_cache::CacheEntry>(bytes)
|
|
140
|
+
.map(|_| ())
|
|
141
|
+
.map_err(|e| crate::MnemoError::Serialize(e.to_string()))
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/// Decode a cache-directory page-run body (a `Vec<CacheDirectoryEntry>`)
|
|
145
|
+
/// from arbitrary bytes.
|
|
146
|
+
pub fn decode_cache_directory(bytes: &[u8]) -> crate::Result<()> {
|
|
147
|
+
rmp_serde::from_slice::<Vec<crate::result_cache::CacheDirectoryEntry>>(bytes)
|
|
148
|
+
.map(|_| ())
|
|
149
|
+
.map_err(|e| crate::MnemoError::Serialize(e.to_string()))
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/// Parse a single MCP stdio JSON-RPC request line. Malformed
|
|
153
|
+
/// input returns `Err(String)`; the fuzz target only asserts
|
|
154
|
+
/// no panic and that the server would not die on this line.
|
|
155
|
+
pub fn parse_mcp_request_line(line: &str) -> Result<(), String> {
|
|
156
|
+
crate::mcp::__fuzz_parse_request_line(line)
|
|
157
|
+
}
|
|
158
|
+
}
|
|
@@ -60,6 +60,17 @@ const PROTOCOL_VERSION: &str = "2025-06-18";
|
|
|
60
60
|
/// Environment variable that supplies the database passphrase.
|
|
61
61
|
const PASSPHRASE_ENV: &str = "MNEMO_PASSPHRASE";
|
|
62
62
|
|
|
63
|
+
/// Fuzz-target helper: parse a single JSON-RPC line as a `Request` and
|
|
64
|
+
/// discard. Panic-free — malformed input returns `Err(String)`, valid
|
|
65
|
+
/// requests return `Ok(())`. Exposed only via `crate::__fuzz`; not part
|
|
66
|
+
/// of the stable public API.
|
|
67
|
+
#[doc(hidden)]
|
|
68
|
+
pub fn __fuzz_parse_request_line(line: &str) -> std::result::Result<(), String> {
|
|
69
|
+
serde_json::from_str::<Request>(line)
|
|
70
|
+
.map(|_| ())
|
|
71
|
+
.map_err(|e| e.to_string())
|
|
72
|
+
}
|
|
73
|
+
|
|
63
74
|
// --- JSON-RPC 2.0 wire types ---------------------------------------------
|
|
64
75
|
|
|
65
76
|
/// Incoming JSON-RPC 2.0 request or notification (a notification is a
|